Closes the gap between "what tag did I deploy" and "what is actually running", and gives the schema a history that can be reasoned about across releases. Migrations - Baseline the existing schema as 0000_init (migrate diff --from-empty). The schema had only ever been applied with `prisma db push`, so no history existed and schema state was disconnected from app version. Existing databases must be baselined once with `migrate resolve --applied 0000_init`; the workflows print this remedy on P3005. - Run `prisma migrate deploy` as a deploy STEP, not the container CMD — as a CMD, N replicas would race each other applying the same migration. Version reporting - GET /version on the API reports the APP_VERSION / GIT_SHA / BUILD_DATE that build.yml already baked into both images but nothing ever read. - The web footer shows the web build and flags an api/web mismatch. The two cannot drift at build time (one matrix run) but can at deploy time. - Both deploy workflows now fail if the running API does not report the tag that was dispatched — a stack naming a tag is not proof of what is running. - scripts/set-version.mjs stamps every package.json, which had all sat at 0.1.0 while real releases shipped as v1.x. Pre-migrate backup - deploy/scripts/pre-migrate-backup.mjs dumps the database from INSIDE the still-running old API container over Portainer's Docker API, so the file lands in the volume the Operaciones restore screen reads. A dump taken on the CI runner would be unreachable by the only restore path we have. Verifies the artefact with `gzip -t` before letting the migration proceed. galactus - deploy/galactus/*.compose.yml: standalone-Docker ports of the Swarm stacks. Plain compose silently ignores `deploy:`, so restart_policy becomes `restart: unless-stopped` — without it nothing returns after a host reboot. - .gitea/workflows/deploy-galactus.yml drives endpoint 3 with its own secrets. Fixes - deploy.yml passed `endpoint_id` and `pull_image` to cssnr/portainer-stack-deploy-action, which has no such inputs (they are `endpoint` and `pull`). The endpoint was silently never set. docs/DEPLOY_AND_MIGRATIONS.md documents expand/contract as the rule for schema changes: Prisma has no down-migrations, so a code rollback never rolls the schema back, and restoring the replication master from a dump diverges every replica. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
61 lines
2.1 KiB
JavaScript
61 lines
2.1 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Stamp one version across every package.json in the workspace.
|
|
*
|
|
* The git tag is what actually drives the image tags (docker/metadata-action in
|
|
* .gitea/workflows/build.yml reads the tag, not any package.json). This script
|
|
* exists so the checked-in manifests stop lying: they all sat at 0.1.0 while
|
|
* real releases went out as v1.x, which makes a checkout impossible to place
|
|
* against a running container.
|
|
*
|
|
* Usage:
|
|
* node scripts/set-version.mjs 1.2.0
|
|
* pnpm version:set 1.2.0
|
|
*
|
|
* Then, as one release commit:
|
|
* git commit -am "chore(release): v1.2.0"
|
|
* git tag v1.2.0 && git push origin master v1.2.0
|
|
*
|
|
* Note the tag carries the leading `v` but the deploy workflow's `tag` input
|
|
* does NOT — metadata-action's {{version}} strips it, so the published image is
|
|
* `1.2.0`. Dispatch `1.2.0`, tag `v1.2.0`.
|
|
*/
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const REPO = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
|
const MANIFESTS = [
|
|
"package.json",
|
|
"apps/api/package.json",
|
|
"apps/web/package.json",
|
|
"packages/database/package.json",
|
|
];
|
|
|
|
const version = process.argv[2];
|
|
if (!version) {
|
|
console.error("usage: node scripts/set-version.mjs <x.y.z>");
|
|
process.exit(1);
|
|
}
|
|
// Plain semver only — a leading `v` here would end up in the image tag and in
|
|
// every manifest, which is not what any consumer expects.
|
|
if (!/^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(version)) {
|
|
console.error(`invalid version: ${version} (expected x.y.z, no leading "v")`);
|
|
process.exit(1);
|
|
}
|
|
|
|
for (const rel of MANIFESTS) {
|
|
const file = join(REPO, rel);
|
|
const raw = readFileSync(file, "utf8");
|
|
const pkg = JSON.parse(raw);
|
|
const previous = pkg.version;
|
|
pkg.version = version;
|
|
// Match the 2-space + trailing-newline shape the files already have so the
|
|
// release commit is a one-line diff per manifest.
|
|
writeFileSync(file, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
console.log(`${rel}: ${previous} -> ${version}`);
|
|
}
|
|
|
|
console.log(`\nnext: git commit -am "chore(release): v${version}" && git tag v${version}`);
|