Skip to content

Commit cff49e0

Browse files
Merge pull request #1 from commercetools/johnsonogwuru/dockerize
chore: dockerize application
2 parents b305000 + 1a1b928 commit cff49e0

14 files changed

Lines changed: 400 additions & 78 deletions

File tree

.dockerignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Credentials and local config — must never be baked into the image.
2+
config.yml
3+
4+
# Go build cache and test artifacts
5+
*.test
6+
*.out
7+
8+
# Git history
9+
.git
10+
.gitignore
11+
12+
# Editor/IDE
13+
.idea/
14+
.vscode/
15+
*.swp
16+
*.swo
17+
18+
# OS
19+
.DS_Store

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
name: Build & Test
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v6
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v6
19+
with:
20+
go-version-file: go.mod
21+
22+
- name: Download dependencies
23+
run: go mod download
24+
25+
- name: Verify dependencies
26+
run: go mod verify
27+
28+
- name: Vet
29+
run: go vet ./...
30+
31+
- name: Test
32+
run: go test -race -coverprofile=coverage.out ./...
33+
34+
- name: Build
35+
run: go build -o /dev/null .

Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# --- build stage ---
2+
FROM golang:1.21-alpine AS builder
3+
4+
WORKDIR /build
5+
6+
# Download dependencies first (layer-cached until go.mod/go.sum change).
7+
COPY go.mod go.sum ./
8+
RUN go mod download
9+
10+
COPY . .
11+
12+
RUN CGO_ENABLED=0 GOOS=linux go build \
13+
-ldflags="-s -w" \
14+
-o checkout-data-sync .
15+
16+
# --- runtime stage ---
17+
FROM alpine:3.19
18+
19+
# ca-certificates is required for TLS connections to the commercetools APIs.
20+
RUN apk --no-cache add ca-certificates
21+
22+
WORKDIR /app
23+
24+
COPY --from=builder /build/checkout-data-sync .
25+
26+
# Mount your config.yml at /app/config.yml via a volume (see README).
27+
# Credentials must never be baked into the image.
28+
ENTRYPOINT ["./checkout-data-sync"]
29+
CMD ["--config", "config.yml"]

README.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
# checkout-data-sync
2+
3+
[![CI](https://github.com/commercetools/checkout-data-sync/actions/workflows/ci.yml/badge.svg)](https://github.com/commercetools/checkout-data-sync/actions)
4+
[![Docker Pulls](https://img.shields.io/docker/pulls/commercetools/checkout-data-sync)](https://hub.docker.com/r/commercetools/checkout-data-sync)
5+
6+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
7+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
8+
9+
- [What is this?](#what-is-this)
10+
- [Prerequisites](#prerequisites)
11+
- [Usage](#usage)
12+
- [Running the Docker Image](#running-the-docker-image)
13+
- [Build](#build)
14+
- [Run](#run)
15+
- [Examples](#examples)
16+
- [Sync Behaviour](#sync-behaviour)
17+
- [Applications](#applications)
18+
- [Payment Integrations](#payment-integrations)
19+
- [Scopes](#scopes)
20+
21+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
22+
23+
### What is this?
24+
25+
A Dockerized CLI application which migrates [commercetools Checkout](https://docs.commercetools.com/checkout) resources between projects — for example when moving between cloud providers or regions.
26+
27+
The following resource types are supported, synced in this order:
28+
29+
- **Applications**
30+
- **Payment Integrations**
31+
32+
Applications are synced first because Payment Integrations reference them by ID. The tool builds a `sourceAppID → targetAppID` map during the application phase and uses it to rewrite references before touching payment integrations.
33+
34+
### Prerequisites
35+
36+
- Docker (to run the image) or Go 1.21+ (to build from source)
37+
- A commercetools API client with Checkout permissions on both source and target projects
38+
- A `config.yml` file — copy the example and fill in your credentials:
39+
40+
```bash
41+
cp config.example.yml config.yml
42+
```
43+
44+
```yaml
45+
source:
46+
project_key: "source-project-key"
47+
client_id: "sourceClientId"
48+
client_secret: "sourceClientSecret"
49+
# OAuth token endpoint (Composable Commerce), not the Checkout API host.
50+
auth_url: "https://auth.europe-west1.gcp.commercetools.com/oauth/token"
51+
checkout_api_url: "https://checkout.europe-west1.gcp.commercetools.com"
52+
# Optional: space-separated OAuth scopes. Omit to use the API client's default scopes.
53+
scopes: "manage_project"
54+
55+
target:
56+
project_key: "target-project-key"
57+
client_id: "targetClientId"
58+
client_secret: "targetClientSecret"
59+
auth_url: "https://auth.eu-central-1.aws.commercetools.com/oauth/token"
60+
checkout_api_url: "https://checkout.eu-central-1.aws.commercetools.com"
61+
scopes: ""
62+
63+
# Map source connector deployment UUIDs to target deployment UUIDs.
64+
# Required for every Payment Integration that has a connectorDeployment.
65+
# Connector deployment IDs are environment-specific and will not resolve
66+
# across cloud providers without an explicit mapping.
67+
deployment_mapping:
68+
"source-deployment-uuid": "target-deployment-uuid"
69+
```
70+
71+
> **Note:** `auth_url` and `checkout_api_url` must not have a trailing slash.
72+
73+
- The following fields are **required** to be set on resources that will be synced:
74+
75+
| Resource | Required Fields |
76+
|---|---|
77+
| Application | `key` |
78+
| Payment Integration | `key` (derived from source ID + name if absent — see [Sync Behaviour](#payment-integrations)) |
79+
80+
### Usage
81+
82+
```
83+
usage: checkout-data-sync
84+
-c, --config <path> Path to config file (default: config.yml)
85+
-f, --full Execute the migration. Omit to perform a dry-run
86+
instead (shows what would be created or updated,
87+
without making any changes).
88+
```
89+
90+
By default the tool runs in **dry-run** mode: it compares source and target resources and prints a plan without writing anything. Pass `-f` to execute the migration.
91+
92+
#### Running the Docker Image
93+
94+
##### Build
95+
96+
```bash
97+
docker build -t checkout-data-sync .
98+
```
99+
100+
##### Run
101+
102+
Mount your `config.yml` at `/app/config.yml` via a volume — credentials must never be baked into the image.
103+
104+
```bash
105+
docker run --rm \
106+
-v $(pwd)/config.yml:/app/config.yml \
107+
checkout-data-sync
108+
```
109+
110+
### Examples
111+
112+
- To perform a dry-run (default):
113+
```bash
114+
docker run --rm -v $(pwd)/config.yml:/app/config.yml checkout-data-sync
115+
```
116+
117+
Example output:
118+
```
119+
=== DRY RUN — pass -f to execute ===
120+
121+
--- Applications ---
122+
Found 2 application(s) in source project "source-project-key"
123+
124+
[CREATE] application "my-application"
125+
[SKIP] application "existing-app" — already up to date
126+
127+
Applications: 1 to create, 0 to update, 1 skipped/errors
128+
129+
--- Payment Integrations ---
130+
Found 3 payment integration(s) in source project "source-project-key"
131+
132+
[CREATE] payment integration "credit-card-via-adyen"
133+
[UPDATE] payment integration "paypal" (target id: abc123, version: 2)
134+
• action: setStatus
135+
[SKIP] payment integration "apple-pay" — already up to date
136+
137+
Payment integrations: 1 to create, 1 to update, 1 skipped/errors
138+
```
139+
140+
- To execute the migration:
141+
```bash
142+
docker run --rm -v $(pwd)/config.yml:/app/config.yml checkout-data-sync -f
143+
```
144+
145+
- To use a config file at a custom path:
146+
```bash
147+
docker run --rm \
148+
-v $(pwd)/staging.yml:/app/staging.yml \
149+
checkout-data-sync -c /app/staging.yml -f
150+
```
151+
152+
- To build and run without Docker:
153+
```bash
154+
go build -o checkout-data-sync .
155+
./checkout-data-sync -c config.yml # dry-run
156+
./checkout-data-sync -c config.yml -f # execute
157+
```
158+
159+
### Sync Behaviour
160+
161+
#### Applications
162+
163+
Applications are matched between source and target by their `key`.
164+
165+
| Condition | Action |
166+
|---|---|
167+
| Key not found in target | `CREATE` |
168+
| Key found, no fields differ | `SKIP` |
169+
| Key found, fields differ | `UPDATE` |
170+
171+
The following update actions are used: `setName`, `setStatus`, `setDescription`, `setApplicationLogo`, `setCountries`, `setAllowedOrigins`, `setPaymentsConfiguration`, `setDiscountsConfiguration`.
172+
173+
Agreements within an application are synced by `name`:
174+
175+
| Condition | Action |
176+
|---|---|
177+
| Agreement name not in target | `addAgreement` |
178+
| Agreement name not in source | `removeAgreement` |
179+
| Agreement exists but differs | `setAgreementName` / `setAgreementType` / `setAgreementStatus` / `setAgreementText` |
180+
181+
#### Payment Integrations
182+
183+
Payment integrations are matched against target resources using a two-step lookup:
184+
185+
1. **Exact key match** — looks up the source key in the target index.
186+
2. **Name fallback** — if no key match, looks up by `name`. When a name match is found with a different key, the target key is considered stale and a `setKey` action is prepended to the update. This handles key renames in the source project.
187+
188+
> **Note:** If multiple target payment integrations share the same name, the name index entry is removed to prevent ambiguous matching. A missing key match in that case results in a new resource being created.
189+
190+
| Condition | Action |
191+
|---|---|
192+
| No match by key or name | `CREATE` |
193+
| Key match, no fields differ | `SKIP` |
194+
| Key match, fields differ | `UPDATE` |
195+
| Name match, key differs | `UPDATE` with `setKey` prepended |
196+
197+
The following update actions are used: `setKey`, `setName`, `setStatus`, `setComponentType`, `setPredicate`, `setDisplayInfo`, `setSortingInfo`, `setAutomatedReversalConfiguration`, `setConnectorDeployment`.
198+
199+
**Key derivation for keyless integrations**
200+
201+
When a payment integration has no `key`, one is derived from the combination of its source **ID** and **name**: `{sanitised-id}-{sanitised-name}`. Using the source ID as a prefix guarantees that two integrations sharing the same display name receive distinct keys.
202+
203+
**Connector deployment mapping**
204+
205+
Connector deployment IDs are environment-specific. Any payment integration that has a `connectorDeployment` **must** have its source deployment UUID listed in `deployment_mapping`. Without a mapping entry the integration is skipped with a clear error:
206+
207+
```
208+
payment integration "credit-card-via-adyen": connectorDeployment "src-uuid" has no entry
209+
in deployment_mapping — add it under deployment_mapping in config.yml and retry
210+
```
211+
212+
**Error handling**
213+
214+
Errors on individual resources are non-fatal. The tool logs each failure, continues processing the remaining resources, and exits with a non-zero status when any error occurred. This means a single bad resource never blocks the rest of the migration.
215+
216+
## Scopes
217+
218+
For least-privilege access, use the following scopes instead of `manage_project`:
219+
220+
| Operation | Source scope | Target scope |
221+
|---|---|---|
222+
| Read applications | `view_checkout_applications:{projectKey}` ||
223+
| Write applications || `manage_checkout_applications:{projectKey}` |
224+
| Read payment integrations | `view_checkout_payment_integrations:{projectKey}` ||
225+
| Write payment integrations || `manage_checkout_payment_integrations:{projectKey}` |
226+
227+
____
228+
229+
#### Maintained by: [ogwurujohnson](https://github.com/ogwurujohnson)

cmd/root.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,27 @@ import (
66

77
"github.com/spf13/cobra"
88

9-
"github.com/commercetools/checkout-cloud-sync/internal/config"
10-
"github.com/commercetools/checkout-cloud-sync/internal/sync"
9+
"github.com/commercetools/checkout-data-sync/internal/config"
10+
"github.com/commercetools/checkout-data-sync/internal/sync"
1111
)
1212

1313
var (
1414
configFile string
15-
full bool
15+
full bool
1616
)
1717

1818
var rootCmd = &cobra.Command{
19-
Use: "checkout-cloud-sync",
19+
Use: "checkout-data-sync",
2020
Short: "Migrate commercetools Checkout resources between cloud providers",
21-
Long: `checkout-cloud-sync migrates commercetools Checkout resources
21+
Long: `checkout-data-sync migrates commercetools Checkout resources
2222
(applications and payment-integrations) from a source project to a target project.
2323
2424
Without -f, runs in dry-run mode and prints what would be migrated.
2525
With -f, performs the actual migration of both applications and payment-integrations.
2626
2727
Example:
28-
checkout-cloud-sync -c config.yml # dry-run
29-
checkout-cloud-sync -c config.yml -f # execute migration`,
28+
checkout-data-sync -c config.yml # dry-run
29+
checkout-data-sync -c config.yml -f # execute migration`,
3030
SilenceUsage: true,
3131
RunE: func(cmd *cobra.Command, args []string) error {
3232
cfg, err := config.Load(configFile)

config.example.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ target:
2020
# deployment_mapping:
2121
# "source-deployment-uuid": "target-deployment-uuid"
2222
deployment_mapping:
23-
"7da2cbeb-d00d-40d3-ab29-5f23f1bdcb21": "5e20cfe4-e849-4abe-b600-c8f569519138"
23+
"source-deployment-uuid": "target-deployment-uuid"

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module github.com/commercetools/checkout-cloud-sync
1+
module github.com/commercetools/checkout-data-sync
22

33
go 1.21
44

internal/client/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
"strings"
1414
"time"
1515

16-
"github.com/commercetools/checkout-cloud-sync/internal/config"
16+
"github.com/commercetools/checkout-data-sync/internal/config"
1717
)
1818

1919
// Client is an authenticated Checkout API client for a single project.

internal/client/client_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"net/http/httptest"
88
"testing"
99

10-
"github.com/commercetools/checkout-cloud-sync/internal/client"
11-
"github.com/commercetools/checkout-cloud-sync/internal/config"
10+
"github.com/commercetools/checkout-data-sync/internal/client"
11+
"github.com/commercetools/checkout-data-sync/internal/config"
1212
)
1313

1414
// tokenHandler always returns a valid token response.

internal/config/config_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"path/filepath"
66
"testing"
77

8-
"github.com/commercetools/checkout-cloud-sync/internal/config"
8+
"github.com/commercetools/checkout-data-sync/internal/config"
99
)
1010

1111
const validConfig = `

0 commit comments

Comments
 (0)