meadow-integration 1.0.38 → 1.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,85 @@
1
+ # Publish a container image to GitHub Container Registry on every
2
+ # version tag push (e.g. `v1.2.3`). Pulls the version straight from
3
+ # the tag — keep `package.json` `version` in sync with the tag for
4
+ # clarity, but the tag is what drives the image label.
5
+ #
6
+ # Why GHCR (not Docker Hub):
7
+ # - Free for public images (no rate limits on anonymous pulls)
8
+ # - Scoped to the same `stevenvelozo` namespace as the source repo
9
+ # - One step to wire: GITHUB_TOKEN already has `packages: write`
10
+ #
11
+ # Image lands at:
12
+ # ghcr.io/stevenvelozo/meadow-integration:<version>
13
+ # ghcr.io/stevenvelozo/meadow-integration:latest (only on the
14
+ # highest stable tag)
15
+ #
16
+ # Manual run: `Actions → Publish container image → Run workflow`,
17
+ # pass an explicit tag for ad-hoc rebuilds without bumping a release.
18
+
19
+ name: Publish container image
20
+
21
+ on:
22
+ push:
23
+ tags:
24
+ - 'v*.*.*' # v1.2.3, v1.2.3-beta.1, etc.
25
+ workflow_dispatch:
26
+ inputs:
27
+ tag:
28
+ description: 'Tag to apply (e.g. dev or 1.2.3-test). `latest` is reserved for stable tag pushes.'
29
+ required: true
30
+ default: 'dev'
31
+
32
+ permissions:
33
+ contents: read
34
+ packages: write # required to push to ghcr.io under the repo owner
35
+
36
+ jobs:
37
+ build-and-push:
38
+ runs-on: ubuntu-latest
39
+
40
+ steps:
41
+ - name: Checkout
42
+ uses: actions/checkout@v4
43
+
44
+ - name: Set up QEMU (multi-arch support)
45
+ uses: docker/setup-qemu-action@v3
46
+
47
+ - name: Set up Docker Buildx
48
+ uses: docker/setup-buildx-action@v3
49
+
50
+ - name: Log in to GHCR
51
+ uses: docker/login-action@v3
52
+ with:
53
+ registry: ghcr.io
54
+ username: ${{ github.actor }}
55
+ password: ${{ secrets.GITHUB_TOKEN }}
56
+
57
+ # Derives image tags from the git ref:
58
+ # v1.2.3 → :1.2.3 + :1.2 + :1 + :latest (semver patterns)
59
+ # v1.2.3-beta → :1.2.3-beta only (pre-release, no `latest`)
60
+ # workflow_dispatch → :<inputs.tag> only
61
+ - name: Compute image tags
62
+ id: meta
63
+ uses: docker/metadata-action@v5
64
+ with:
65
+ images: ghcr.io/${{ github.repository_owner }}/meadow-integration
66
+ tags: |
67
+ type=semver,pattern={{version}}
68
+ type=semver,pattern={{major}}.{{minor}}
69
+ type=semver,pattern={{major}}
70
+ type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
71
+
72
+ # linux/amd64 + linux/arm64 covers the common cases (CI hosts,
73
+ # Apple Silicon dev boxes, and most cloud runners). Add more
74
+ # platforms as deployment targets emerge.
75
+ - name: Build and push
76
+ uses: docker/build-push-action@v5
77
+ with:
78
+ context: .
79
+ file: ./Dockerfile
80
+ platforms: linux/amd64,linux/arm64
81
+ push: true
82
+ tags: ${{ steps.meta.outputs.tags }}
83
+ labels: ${{ steps.meta.outputs.labels }}
84
+ cache-from: type=gha
85
+ cache-to: type=gha,mode=max
@@ -0,0 +1,357 @@
1
+ # Building and Publishing
2
+
3
+ How to ship `meadow-integration` to npm and to GitHub Container Registry
4
+ (GHCR), and how to consume the published image. This doc follows the
5
+ same template as the other dockerized retold tools — the structure is
6
+ identical across modules, only the module-specific details (image name,
7
+ env vars, lifecycle shape) differ.
8
+
9
+ `meadow-integration` is a **one-shot job** rather than a long-running
10
+ service: the container runs the data-clone command, exits with a status
11
+ code, and is meant to be invoked once per sync rather than left running.
12
+ That shape difference shows up in the [Module-specific notes](#module-specific-notes)
13
+ section below.
14
+
15
+ ---
16
+
17
+ ## TL;DR
18
+
19
+ ```bash
20
+ # npm-only release (the default — most common case)
21
+ npm run release:patch
22
+
23
+ # npm release that ALSO rebuilds the GHCR image
24
+ npm run release:patch:image
25
+ ```
26
+
27
+ The default release is npm-only. Docker images are deliberate, opt-in
28
+ artifacts because each multi-arch build burns several minutes of CI
29
+ time. Use `:image` (or set `BUILD_DOCKER=1`) when you actually want a
30
+ new image — typically when runtime code, dependencies, env-var contract,
31
+ or the Dockerfile changed. For a doc-only fix or internal-only patch,
32
+ the plain `release:patch` ships to npm and skips the image rebuild.
33
+
34
+ ---
35
+
36
+ ## Prerequisites (one-time setup)
37
+
38
+ - **npm login** — `npm whoami` should print your username. If not,
39
+ `npm login`.
40
+ - **Git remote configured** — `git remote get-url origin` should print
41
+ the GitHub HTTPS or SSH URL. If not, `git remote add origin
42
+ git@github.com:stevenvelozo/meadow-integration.git`.
43
+ - **Push access to the repo** — required so `postversion` /
44
+ `postpublish` can push the tag. The GHCR workflow runs under
45
+ `GITHUB_TOKEN` so no extra registry auth needed for image pushing.
46
+ - **Docker** (only if you want to test the image locally before tag) —
47
+ `docker version` should respond.
48
+
49
+ ---
50
+
51
+ ## Ecosystem convention: lockfiles are gitignored
52
+
53
+ `package-lock.json` is in this repo's `.gitignore` (Quackage convention
54
+ shared across the retold ecosystem). That has two consequences for the
55
+ Dockerfile and the publish pipeline that are worth knowing:
56
+
57
+ - **The Dockerfile uses `npm install`, not `npm ci`.** `npm ci` requires
58
+ `package-lock.json` to be present in the build context, and CI runners
59
+ check out only what's in git. Switching to `npm ci` will fail every
60
+ GHCR build with `EUSAGE: The npm ci command can only install with an
61
+ existing package-lock.json`.
62
+ - **Builds resolve dep ranges fresh each time.** The tradeoff vs. a
63
+ pinned `npm ci` build is reproducibility — two builds of the same git
64
+ SHA can pick up different transitive versions if anything in the
65
+ range bumps. Acceptable for retold modules because the upstream
66
+ ranges are owned by the same author; for stricter reproducibility,
67
+ the alternative is to commit the lockfile (and revert the
68
+ ecosystem-wide convention here).
69
+
70
+ If you see `npm ci` errors in the GHCR workflow logs, the fix is always
71
+ the same: change `RUN npm ci` to `RUN npm install` in the Dockerfile.
72
+
73
+ ---
74
+
75
+ ## Releasing
76
+
77
+ ### Two flavors of release
78
+
79
+ | Command | npm registry | GHCR image rebuild |
80
+ |--------------------------------------|--------------|--------------------|
81
+ | `npm run release:patch` | yes | no |
82
+ | `npm run release:patch:image` | yes | yes |
83
+ | `npm run release:minor` | yes | no |
84
+ | `npm run release:minor:image` | yes | yes |
85
+ | `npm run release:major` | yes | no |
86
+ | `npm run release:major:image` | yes | yes |
87
+
88
+ The non-`:image` variants are the default because most patch releases
89
+ don't change runtime behavior; the `:image` variants tell the pipeline
90
+ "this release does change runtime — build me a new image."
91
+
92
+ ### What `release:patch` does (no docker)
93
+
94
+ 1. **`npm version patch`** — bumps `package.json`, creates a commit
95
+ (`1.0.39`), creates a local tag `v1.0.39`.
96
+ 2. **`postversion`** hook fires — `git push` pushes the commit. The
97
+ tag stays local (intentional — no tag push means no GHCR trigger).
98
+ 3. **`npm publish`** runs — `prepublishOnly` runs `npm test` first as
99
+ the gate. If tests fail, publish aborts.
100
+ 4. **`postpublish`** hook fires — checks `BUILD_DOCKER`; unset, so it
101
+ does nothing.
102
+
103
+ End state: npm has the new version, git has the bump commit, the
104
+ `v1.0.39` tag exists locally only, no GHCR build was triggered.
105
+
106
+ ### What `release:patch:image` does (with docker)
107
+
108
+ Same as above, except `npm publish` runs with `BUILD_DOCKER=1` in the
109
+ environment. The `postpublish` hook sees the flag and pushes the
110
+ `v1.0.39` tag to the remote, which fires the GHCR workflow.
111
+
112
+ ### Promoting a previous npm release to docker later
113
+
114
+ If you released `v1.0.39` to npm only, then later decide you do want a
115
+ docker image for it:
116
+
117
+ ```bash
118
+ git push origin v1.0.39 # pushes the local tag → GHCR fires
119
+ ```
120
+
121
+ The local tag is still sitting there from the original `npm version`
122
+ step. Pushing it triggers the workflow without touching npm.
123
+
124
+ ### Direct CLI publish (also works)
125
+
126
+ ```bash
127
+ # already-bumped, want to publish to npm only (default):
128
+ npm publish
129
+
130
+ # already-bumped, want to publish to npm AND build docker:
131
+ npm run publish:docker
132
+ # or equivalently:
133
+ BUILD_DOCKER=1 npm publish
134
+ ```
135
+
136
+ ### From `retold-manager` TUI
137
+
138
+ - `[!]` Publish — npm only. Existing key, behavior unchanged.
139
+ - `[D]` Publish with docker image — npm + GHCR build. New key.
140
+
141
+ ---
142
+
143
+ ## The chain
144
+
145
+ The lifecycle hooks all live in `package.json`. Default path
146
+ (`BUILD_DOCKER` unset):
147
+
148
+ ```
149
+ npm publish
150
+
151
+ prepublishOnly: npm test ← test gate
152
+ ↓ (passes)
153
+ publish to npm registry
154
+ ↓ (succeeds)
155
+ postpublish: BUILD_DOCKER unset → no-op ← image NOT triggered
156
+ ```
157
+
158
+ Docker-included path (`BUILD_DOCKER=1`):
159
+
160
+ ```
161
+ BUILD_DOCKER=1 npm publish (or: npm run publish:docker)
162
+
163
+ prepublishOnly: npm test ← test gate
164
+ ↓ (passes)
165
+ publish to npm registry
166
+ ↓ (succeeds)
167
+ postpublish: BUILD_DOCKER=1 → tag + push ← image trigger
168
+ git tag v<version> ← creates if not present
169
+ git push origin v<version>
170
+ ↓ (tag arrives at GitHub)
171
+ .github/workflows/publish-image.yml fires:
172
+ - docker buildx build --platform linux/amd64,linux/arm64
173
+ - docker push ghcr.io/stevenvelozo/meadow-integration:<version>
174
+ - tags: <version>, <major>.<minor>, <major>, latest
175
+ ```
176
+
177
+ The `release:patch` (no docker) and `release:patch:image` (docker)
178
+ scripts both wrap this with a preceding `npm version patch` so you
179
+ don't have to bump separately.
180
+
181
+ ---
182
+
183
+ ## Verifying a release
184
+
185
+ After `release:patch` completes:
186
+
187
+ 1. **npm**: `npm view meadow-integration version` should print the new
188
+ version (may take ~30s for the registry to update).
189
+ 2. **GHCR workflow**: visit
190
+ `https://github.com/stevenvelozo/meadow-integration/actions` and
191
+ confirm the "Publish container image" run succeeded.
192
+ 3. **Image**: `docker pull ghcr.io/stevenvelozo/meadow-integration:<version>`
193
+ should succeed. The image is also tagged as `latest`, `<major>`, and
194
+ `<major>.<minor>`.
195
+ 4. **Smoke test** (one-shot — runs the bundled BookStore default schema
196
+ against a non-existent API; expect it to fail at the data-fetch step,
197
+ which proves the binary started correctly):
198
+ ```bash
199
+ docker run --rm ghcr.io/stevenvelozo/meadow-integration:latest
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Recovery patterns
205
+
206
+ ### Tests fail during `prepublishOnly`
207
+
208
+ Publish aborts cleanly — npm registry is untouched, no tag is pushed.
209
+ Fix the test, then re-run `npm publish` (the version is already bumped
210
+ from the earlier `npm version` step, so don't re-bump).
211
+
212
+ ### `npm publish` succeeded but GHCR build didn't start
213
+
214
+ Usually means the tag push failed silently (network blip during
215
+ `postpublish`). Verify:
216
+
217
+ ```bash
218
+ git tag --list 'v*' | tail -5 # is the tag local?
219
+ git ls-remote --tags origin | tail -5 # is the tag on the remote?
220
+ ```
221
+
222
+ If local but not remote, push manually:
223
+ ```bash
224
+ git push origin v<version>
225
+ ```
226
+
227
+ The GHCR workflow triggers on tag push, so this re-fires the build with
228
+ no other side effects.
229
+
230
+ ### GHCR build failed
231
+
232
+ Check the workflow logs in the Actions tab. Common failures: Dockerfile
233
+ issue, dependency that doesn't install on the build platform, GHCR
234
+ permission issue (rare; `GITHUB_TOKEN` should always have
235
+ `packages: write`). Re-run the workflow from the Actions UI after
236
+ fixing — no need to bump the npm version.
237
+
238
+ ### Need to re-publish at a different commit
239
+
240
+ The version-tag-to-commit binding is sticky. To re-publish `v1.0.39`
241
+ pointing at a different commit:
242
+
243
+ ```bash
244
+ # remove old tag locally and remotely
245
+ git tag -d v1.0.39
246
+ git push origin :refs/tags/v1.0.39
247
+
248
+ # unpublish from npm if within the 72h window
249
+ npm unpublish meadow-integration@1.0.39
250
+
251
+ # then re-run release at the new commit
252
+ npm run release:patch
253
+ ```
254
+
255
+ `npm unpublish` is rate-limited and discouraged in general — better
256
+ practice is to bump to a new patch version and ship that.
257
+
258
+ ---
259
+
260
+ ## Versioning conventions
261
+
262
+ Standard semver:
263
+ - **patch** (`1.0.38` → `1.0.39`) — bug fixes, internal cleanup, anything
264
+ that doesn't change the public CLI/API or behavior contract.
265
+ - **minor** (`1.0.38` → `1.1.0`) — additive features, new env vars, new
266
+ CLI subcommands, new bundled schemas. Existing consumers continue to
267
+ work.
268
+ - **major** (`1.0.38` → `2.0.0`) — breaking changes (env var renamed,
269
+ CLI flag removed, default behavior changed). Bump and document in
270
+ CHANGELOG.
271
+
272
+ GHCR images are tagged with all three tiers (`<version>`,
273
+ `<major>.<minor>`, `<major>`, `latest`), so consumers can pin at
274
+ whatever stability level fits.
275
+
276
+ ---
277
+
278
+ ## Image consumption
279
+
280
+ ### Pull and run (one-shot)
281
+
282
+ ```bash
283
+ docker pull ghcr.io/stevenvelozo/meadow-integration:latest
284
+
285
+ # Single sync run; container exits when done
286
+ docker run --rm \
287
+ -e MEADOW_INTEGRATION_API_SERVER=http://your-meadow:8080/1.0/ \
288
+ -e MEADOW_INTEGRATION_API_USERNAME=admin \
289
+ -e MEADOW_INTEGRATION_API_PASSWORD_FILE=/run/secrets/api-pass \
290
+ -e MEADOW_INTEGRATION_DB_HOST=mysql.local \
291
+ -e MEADOW_INTEGRATION_DB_USERNAME=root \
292
+ -e MEADOW_INTEGRATION_DB_PASSWORD_FILE=/run/secrets/db-pass \
293
+ -e MEADOW_INTEGRATION_DB_NAME=mydatabase \
294
+ -e MEADOW_INTEGRATION_SCHEMA_PATH=/schemas/my-schema.json \
295
+ -v $(pwd)/schemas:/schemas:ro \
296
+ ghcr.io/stevenvelozo/meadow-integration:latest
297
+ ```
298
+
299
+ ### Configuration via env vars
300
+
301
+ All `MEADOW_INTEGRATION_*` env vars are read at startup. CLI flags
302
+ override env vars; env vars override JSON config; JSON config overrides
303
+ built-in defaults.
304
+
305
+ | Variable | Purpose |
306
+ |---------------------------------------|----------------------------------------------------|
307
+ | `MEADOW_INTEGRATION_API_SERVER` | Source Meadow API URL |
308
+ | `MEADOW_INTEGRATION_API_USERNAME` | API username |
309
+ | `MEADOW_INTEGRATION_API_PASSWORD` | API password |
310
+ | `MEADOW_INTEGRATION_DB_PROVIDER` | `MySQL` or `MSSQL` (default `MySQL`) |
311
+ | `MEADOW_INTEGRATION_DB_HOST` | Destination DB host |
312
+ | `MEADOW_INTEGRATION_DB_PORT` | Destination DB port |
313
+ | `MEADOW_INTEGRATION_DB_USERNAME` | DB user |
314
+ | `MEADOW_INTEGRATION_DB_PASSWORD` | DB password |
315
+ | `MEADOW_INTEGRATION_DB_NAME` | Destination database name |
316
+ | `MEADOW_INTEGRATION_SCHEMA_PATH` | Path to Meadow extended schema JSON |
317
+
318
+ Any secret-bearing var also accepts `<NAME>_FILE` pointing at a file
319
+ whose contents become the value (mysql/postgres convention). Use this
320
+ for docker secret + k8s Secret mounts:
321
+
322
+ ```bash
323
+ -e MEADOW_INTEGRATION_DB_PASSWORD_FILE=/run/secrets/db-pass
324
+ ```
325
+
326
+ ### Volumes
327
+
328
+ - Mount your schema file(s) (read-only is fine) and point
329
+ `MEADOW_INTEGRATION_SCHEMA_PATH` at the in-container path.
330
+
331
+ ### Default schema
332
+
333
+ The image ships with a sample BookStore schema at
334
+ `/service_root/schema/default.json`. If `MEADOW_INTEGRATION_SCHEMA_PATH`
335
+ is unset (or empty), the data-clone command falls back to it. Useful
336
+ for smoke-testing the container shape before wiring your real schema.
337
+
338
+ ### No healthcheck
339
+
340
+ This image deliberately does not declare a `HEALTHCHECK` because the
341
+ process is a one-shot job, not a daemon. Compose / k8s should treat
342
+ non-zero exit as failure (which is the default).
343
+
344
+ ---
345
+
346
+ ## Module-specific notes
347
+
348
+ - **One-shot job shape**: this image runs to completion and exits.
349
+ Restart policy in compose / k8s should be `no` or `OnFailure`,
350
+ never `unless-stopped`. Wrap in a Kubernetes Job or compose
351
+ `restart: "no"` to express "run once and stop".
352
+ - **No exposed port**: the data-clone command doesn't accept inbound
353
+ connections. It pulls from the source API and pushes to the
354
+ destination DB.
355
+ - **Exit code is the contract**: 0 = clone completed, non-zero = error.
356
+ Operators should chain on exit code (cron, k8s Job retries, compose
357
+ `depends_on: condition: service_completed_successfully`).
package/Dockerfile CHANGED
@@ -12,6 +12,7 @@ RUN cd /service_root && npm install --omit=dev
12
12
 
13
13
  ADD source /service_root/source
14
14
  ADD scripts /service_root/scripts
15
+ ADD schema /service_root/schema
15
16
 
16
17
  WORKDIR /service_root
17
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meadow-integration",
3
- "version": "1.0.38",
3
+ "version": "1.0.39",
4
4
  "description": "Meadow Data Integration",
5
5
  "retoldBeacon": {
6
6
  "displayName": "Meadow Integration",
@@ -43,14 +43,24 @@
43
43
  "start": "node source/cli/Meadow-Integration-CLI-Run.js",
44
44
  "tests": "npx quack test -g",
45
45
  "coverage": "npx quack coverage",
46
- "build": "npx quack build"
46
+ "build": "npx quack build",
47
+ "prepublishOnly": "npm test",
48
+ "postversion": "npx quack release postversion",
49
+ "postpublish": "npx quack release postpublish",
50
+ "publish:docker": "npx quack release publish --image",
51
+ "release:patch": "npx quack release patch",
52
+ "release:minor": "npx quack release minor",
53
+ "release:major": "npx quack release major",
54
+ "release:patch:image": "npx quack release patch --image",
55
+ "release:minor:image": "npx quack release minor --image",
56
+ "release:major:image": "npx quack release major --image"
47
57
  },
48
58
  "author": "steven velozo <steven@velozo.com>",
49
59
  "license": "MIT",
50
60
  "devDependencies": {
51
61
  "meadow-connection-sqlite": "^1.0.19",
52
62
  "pict-docuserve": "^0.1.5",
53
- "quackage": "^1.1.2"
63
+ "quackage": "^1.2.2"
54
64
  },
55
65
  "mocha": {
56
66
  "diff": true,
@@ -75,9 +85,9 @@
75
85
  "fable-serviceproviderbase": "^3.0.19",
76
86
  "fast-xml-parser": "^4.4.1",
77
87
  "meadow": "^2.0.37",
78
- "meadow-connection-mssql": "^1.0.22",
79
- "meadow-connection-mysql": "^1.0.18",
80
- "orator": "^6.1.0",
88
+ "meadow-connection-mssql": "^1.0.23",
89
+ "meadow-connection-mysql": "^1.0.19",
90
+ "orator": "^6.1.1",
81
91
  "orator-serviceserver-restify": "^2.0.10",
82
92
  "pict-section-flow": "^0.0.17",
83
93
  "pict-service-commandlineutility": "^1.0.19",