@abgov/nx-oc 12.14.1 → 12.15.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/nx-oc",
3
- "version": "12.14.1",
3
+ "version": "12.15.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "src/index.js",
6
6
  "description": "Government of Alberta - Nx plugin for OpenShift.",
@@ -0,0 +1,62 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ // minimatch is always present (nx depends on it); used only in this test.
4
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
5
+ import minimatch = require('minimatch');
6
+
7
+ /**
8
+ * Guards the source -> published-package boundary.
9
+ *
10
+ * The build copies non-source files (generator templates, schemas, static
11
+ * assets) into the package via the build target's `assets` globs. Any such file
12
+ * NOT matched by a glob is silently dropped from the published package, so a
13
+ * consumer's generate produces output referencing a file that was never
14
+ * shipped. This is exactly how `Dockerfile__tmpl__` was lost: the rename from
15
+ * `Dockerfile.template` (dotted) to `Dockerfile__tmpl__` (dotless) stopped it
16
+ * matching `**\/*.!(ts)`.
17
+ *
18
+ * Unit tests of the generators can't catch this — they resolve templates from
19
+ * the source tree, never the packaged output. This asserts the invariant
20
+ * directly: every non-TypeScript file under src is covered by an asset glob.
21
+ */
22
+ describe('build assets packaging', () => {
23
+ const srcRoot = __dirname;
24
+ const projectRoot = path.join(srcRoot, '..');
25
+ const repoRoot = path.join(projectRoot, '..', '..');
26
+
27
+ function listFiles(dir: string): string[] {
28
+ return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
29
+ const full = path.join(dir, entry.name);
30
+ return entry.isDirectory() ? listFiles(full) : [full];
31
+ });
32
+ }
33
+
34
+ it('matches every non-TypeScript src file with an asset glob', () => {
35
+ const project = JSON.parse(
36
+ fs.readFileSync(path.join(projectRoot, 'project.json'), 'utf-8')
37
+ );
38
+ const assets: unknown[] = project.targets.build.options.assets ?? [];
39
+
40
+ // Globs whose input is this package's src directory.
41
+ const srcGlobs = assets
42
+ .filter(
43
+ (a): a is { input: string; glob: string } =>
44
+ typeof a === 'object' &&
45
+ a !== null &&
46
+ 'input' in a &&
47
+ path.resolve(repoRoot, (a as { input: string }).input) === srcRoot
48
+ )
49
+ .map((a) => a.glob);
50
+
51
+ // Every non-source file under src must ship (tsc only emits .ts -> .js).
52
+ const mustShip = listFiles(srcRoot)
53
+ .filter((f) => !/\.tsx?$/.test(f))
54
+ .map((f) => path.relative(srcRoot, f));
55
+
56
+ const unmatched = mustShip.filter(
57
+ (rel) => !srcGlobs.some((glob) => minimatch(rel, glob, { dot: true }))
58
+ );
59
+
60
+ expect(unmatched).toEqual([]);
61
+ });
62
+ });
@@ -31,7 +31,13 @@ function normalizeOptions(host, options) {
31
31
  }
32
32
  function addFiles(host, options) {
33
33
  var _a, _b;
34
- const templateOptions = Object.assign(Object.assign(Object.assign({}, options), options.adsp), { sourceRepositoryUrl: (0, git_utils_1.getGitRemoteUrl)(), database: (_a = options.database) !== null && _a !== void 0 ? _a : 'none', sandbox: (_b = options.sandbox) !== null && _b !== void 0 ? _b : false, tmpl: '' });
34
+ const templateOptions = Object.assign(Object.assign(Object.assign({}, options), options.adsp), {
35
+ // Clean https URL for the image's source label (never the raw remote, which
36
+ // may be an SSH URL or carry a trailing newline).
37
+ sourceRepositoryUrl: (() => {
38
+ const repo = (0, git_utils_1.getGitHubRepo)((0, git_utils_1.getGitRemoteUrl)());
39
+ return repo ? `https://github.com/${repo}` : '';
40
+ })(), database: (_a = options.database) !== null && _a !== void 0 ? _a : 'none', sandbox: (_b = options.sandbox) !== null && _b !== void 0 ? _b : false, tmpl: '' });
35
41
  (0, devkit_1.generateFiles)(host, path.join(__dirname, `${options.appType}-files`), `./.openshift/${options.projectName}`, templateOptions);
36
42
  }
37
43
  function default_1(host, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/deployment/deployment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/deployment/deployment.ts"],"names":[],"mappings":";;AAuEA,4BAqCC;;AA5GD,uCAOoB;AACpB,6BAA6B;AAC7B,6BAA6B;AAC7B,uDAA2D;AAC3D,qDAAuE;AACvE,mDAAiF;AAEjF,qCAAkD;AAElD,MAAM,iBAAiB,GAAG,6BAA6B,CAAC;AAExD,SAAe,gBAAgB,CAC7B,IAAU,EACV,OAAe;;;QAEf,MAAM,WAAW,GAAG,IAAA,cAAK,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;QAEpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvD,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,cAAc,GAAG,CAAA,MAAA,MAAA,KAAK,CAAC,CAAC,CAAC,0CAAE,QAAQ,0CAAE,SAAS,KAAI,EAAE,CAAC;QAE3D,MAAM,SAAS,GAAG,yBAAyB,CAAC;QAC5C,MAAM,aAAa,GAAG,MAAA,MAAA,KAAK,CAAC,CAAC,CAAC,0CAAE,QAAQ,0CACpC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EACjE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QAE7C,MAAM,MAAM,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAC3D,MAAM,OAAO,GAAG,MAAA,OAAO,CAAC,OAAO,mCAAI,IAAA,gCAAqB,EAAC,MAAM,CAAC,CAAC;QAEjE,MAAM,IAAI,GAAG,MAAM,IAAA,2BAAoB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEvD,uCACK,OAAO,KACV,OAAO;YACP,IAAI;YACJ,WAAW;YACX,cAAc;YACd,aAAa,EACb,eAAe,EAAE,IAAA,6BAAkB,EAAC,MAAM,CAAC,IAC3C;IACJ,CAAC;CAAA;AAED,SAAS,QAAQ,CAAC,IAAU,EAAE,OAAyB;;IACrD,MAAM,eAAe,iDAChB,OAAO,GACP,OAAO,CAAC,IAAI;QACf,4EAA4E;QAC5E,kDAAkD;QAClD,mBAAmB,EAAE,CAAC,GAAG,EAAE;YACzB,MAAM,IAAI,GAAG,IAAA,yBAAa,EAAC,IAAA,2BAAe,GAAE,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,CAAC,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,CAAC,CAAC,EAAE,EACJ,QAAQ,EAAE,MAAA,OAAO,CAAC,QAAQ,mCAAI,MAAM,EACpC,OAAO,EAAE,MAAA,OAAO,CAAC,OAAO,mCAAI,KAAK,EACjC,IAAI,EAAE,EAAE,GACT,CAAC;IACF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,QAAQ,CAAC,EAChD,gBAAgB,OAAO,CAAC,WAAW,EAAE,EACrC,eAAe,CAChB,CAAC;AACJ,CAAC;AAED,mBAA+B,IAAU,EAAE,OAAe;;QACxD,MAAM,MAAM,GAAG,IAAA,iCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,WAAW,KAAK,aAAa,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;YACvD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CACT,qEAAqE,CACtE,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChE,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QAED,MAAM,CAAC,OAAO,mCACT,MAAM,CAAC,OAAO,KACjB,YAAY,EAAE;gBACZ,QAAQ,EAAE,oBAAoB;gBAC9B,OAAO,EAAE;oBACP,SAAS,EAAE,iBAAiB,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE;;wBAAC,OAAA,CAAC;4BAC9D,OAAO;4BACP,GAAG,EAAE,MAAA,4BAAI,CAAC,CAAC,CAAC,0CAAE,WAAW,EAAE;yBAC5B,CAAC,CAAA;qBAAA,CAAC;iBACJ;aACF,GACF,CAAC;QAEF,IAAA,mCAA0B,EAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAE1D,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;QAClC,MAAM,IAAA,oBAAW,EAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CAAA"}
@@ -5,6 +5,10 @@ COPY . .
5
5
  RUN dotnet publish ${PROJECT} -c Release -o /app/publish
6
6
 
7
7
  FROM registry.access.redhat.com/ubi8/dotnet-80-runtime
8
+ <% if (sourceRepositoryUrl) { %>
9
+ # Provenance: associate the image with its source repo (shown in GHCR).
10
+ LABEL org.opencontainers.image.source=<%= sourceRepositoryUrl %>
11
+ <% } %>
8
12
  WORKDIR /app
9
13
  COPY --from=build /app/publish .
10
14
  ENV ASPNETCORE_URLS=http://+:5000
@@ -1,5 +1,8 @@
1
1
  FROM registry.access.redhat.com/ubi9/nginx-120
2
-
2
+ <% if (sourceRepositoryUrl) { %>
3
+ # Provenance: associate the image with its source repo (shown in GHCR).
4
+ LABEL org.opencontainers.image.source=<%= sourceRepositoryUrl %>
5
+ <% } %>
3
6
  # Build output path mirrors the workspace layout (from the project's build target).
4
7
  COPY ./<%= buildOutputPath %>/nginx.conf "${NGINX_CONF_PATH}"
5
8
  COPY ./<%= buildOutputPath %> .
@@ -1,5 +1,8 @@
1
1
  FROM registry.access.redhat.com/ubi9/nodejs-24
2
-
2
+ <% if (sourceRepositoryUrl) { %>
3
+ # Provenance: associate the image with its source repo (shown in GHCR).
4
+ LABEL org.opencontainers.image.source=<%= sourceRepositoryUrl %>
5
+ <% } %>
3
6
  # Build output path mirrors the workspace layout (from the project's build target).
4
7
  COPY ./<%= buildOutputPath %> .
5
8
  COPY ./node_modules ./node_modules
@@ -8,6 +8,43 @@ const adsp_1 = require("../../adsp");
8
8
  const git_utils_1 = require("../../utils/git-utils");
9
9
  const oc_utils_1 = require("../../utils/oc-utils");
10
10
  const app_type_1 = require("../../utils/app-type");
11
+ const SANDBOX_GENERATOR = '@abgov/nx-oc:sandbox';
12
+ // Resolves the sandbox container registry once per workspace and persists it to
13
+ // nx.json so subsequent sandbox generations reuse it without re-prompting:
14
+ // --registry flag → nx.json → derived from git remote (ghcr.io/<org>) → prompt.
15
+ function resolveRegistry(host, registry, remoteUrl) {
16
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
17
+ var _a, _b, _c;
18
+ if (registry)
19
+ return persistRegistry(host, registry);
20
+ const stored = (_c = (_b = (_a = (0, devkit_1.readNxJson)(host)) === null || _a === void 0 ? void 0 : _a.generators) === null || _b === void 0 ? void 0 : _b[SANDBOX_GENERATOR]) === null || _c === void 0 ? void 0 : _c.registry;
21
+ if (stored)
22
+ return stored;
23
+ const derived = (0, git_utils_1.deriveRegistryFromRemote)(remoteUrl);
24
+ if (derived) {
25
+ console.log(`\n✓ Sandbox registry: ${derived.toLowerCase()} (derived from git remote)\n`);
26
+ return persistRegistry(host, derived);
27
+ }
28
+ const { prompt } = yield Promise.resolve().then(() => require('enquirer'));
29
+ const { registry: answered } = yield prompt({
30
+ type: 'input',
31
+ name: 'registry',
32
+ message: 'What container registry should sandbox images be published to (e.g., ghcr.io/my-org)?',
33
+ });
34
+ return persistRegistry(host, answered);
35
+ });
36
+ }
37
+ // Container registries (GHCR) require lowercase paths, so normalize on store.
38
+ function persistRegistry(host, registry) {
39
+ var _a, _b, _c;
40
+ const value = registry.toLowerCase();
41
+ const nxJson = (_a = (0, devkit_1.readNxJson)(host)) !== null && _a !== void 0 ? _a : {};
42
+ const generators = ((_b = nxJson.generators) !== null && _b !== void 0 ? _b : {});
43
+ generators[SANDBOX_GENERATOR] = Object.assign(Object.assign({}, ((_c = generators[SANDBOX_GENERATOR]) !== null && _c !== void 0 ? _c : {})), { registry: value });
44
+ nxJson.generators = generators;
45
+ (0, devkit_1.updateNxJson)(host, nxJson);
46
+ return value;
47
+ }
11
48
  function normalizeOptions(host, options) {
12
49
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
13
50
  var _a, _b;
@@ -15,14 +52,22 @@ function normalizeOptions(host, options) {
15
52
  const config = (0, devkit_1.readProjectConfiguration)(host, projectName);
16
53
  const appType = (_a = options.appType) !== null && _a !== void 0 ? _a : (0, app_type_1.detectApplicationType)(config);
17
54
  const adsp = yield (0, adsp_1.getAdspConfiguration)(host, Object.assign(Object.assign({}, options), { env: (_b = options.env) !== null && _b !== void 0 ? _b : 'dev' }));
55
+ const remoteUrl = (0, git_utils_1.getGitRemoteUrl)();
56
+ const registry = (yield resolveRegistry(host, options.registry, remoteUrl)).toLowerCase();
57
+ // Prefix the image with the (per-user) sandbox namespace so images from
58
+ // different experimenters never collide on GHCR's org-global package names.
59
+ const imageName = `${options.sandboxProject}-${projectName}`.toLowerCase();
60
+ const repoSlug = (0, git_utils_1.getGitHubRepo)(remoteUrl);
18
61
  return Object.assign(Object.assign({}, options), { appType,
19
62
  adsp,
20
- projectName, buildOutputPath: (0, app_type_1.getBuildOutputPath)(config) });
63
+ projectName, buildOutputPath: (0, app_type_1.getBuildOutputPath)(config), registry, registryHost: registry.split('/')[0], registryOrg: registry.split('/').slice(1).join('/'), imageName, imageRef: `${registry}/${imageName}:sandbox`, sourceRepositoryUrl: repoSlug ? `https://github.com/${repoSlug}` : undefined });
21
64
  });
22
65
  }
23
66
  function addManifestFiles(host, options) {
24
- var _a;
25
- const templateOptions = Object.assign(Object.assign(Object.assign({}, options), options.adsp), { sourceRepositoryUrl: (0, git_utils_1.getGitRemoteUrl)(), database: (_a = options.database) !== null && _a !== void 0 ? _a : 'none', sandbox: true, ocInfraProject: options.sandboxProject, tmpl: '' });
67
+ var _a, _b;
68
+ const templateOptions = Object.assign(Object.assign(Object.assign({}, options), options.adsp), {
69
+ // Clean https URL for the image's source label (provenance / UI connect).
70
+ sourceRepositoryUrl: (_a = options.sourceRepositoryUrl) !== null && _a !== void 0 ? _a : '', database: (_b = options.database) !== null && _b !== void 0 ? _b : 'none', sandbox: true, ocInfraProject: options.sandboxProject, tmpl: '' });
26
71
  (0, devkit_1.generateFiles)(host, path.join(__dirname, `../deployment/${options.appType}-files`), `./.openshift/${options.projectName}`, templateOptions);
27
72
  }
28
73
  function addDatabaseFiles(host, options) {
@@ -30,20 +75,10 @@ function addDatabaseFiles(host, options) {
30
75
  return;
31
76
  (0, devkit_1.generateFiles)(host, path.join(__dirname, 'database-files'), './.openshift/sandbox', { database: options.database, tmpl: '' });
32
77
  }
33
- // In-cluster build infrastructure (ImageStream + binary Docker BuildConfig).
34
- // The sandbox builds inside the cluster and pushes to the internal registry,
35
- // so it needs no GitHub repo/CI, no externally-exposed registry route, and no
36
- // local container runtime.
37
- function addBuildFiles(host, options) {
38
- (0, devkit_1.generateFiles)(host, path.join(__dirname, 'build-files'), './.openshift', {
39
- projectName: options.projectName,
40
- tmpl: '',
41
- });
42
- }
43
78
  function addSandboxTarget(host, options) {
44
79
  var _a;
45
80
  const config = (0, devkit_1.readProjectConfiguration)(host, options.project);
46
- const { projectName, sandboxProject, database, appType } = options;
81
+ const { projectName, sandboxProject, database, appType, imageRef, registryHost, registryOrg, imageName, } = options;
47
82
  const commands = [];
48
83
  // ADSP services authenticate with the access service using a confidential
49
84
  // Keycloak client secret. The express-service generator writes it to the
@@ -100,12 +135,29 @@ function addSandboxTarget(host, options) {
100
135
  commands.push(`oc get service ${name} -n ${sandboxProject} >/dev/null 2>&1 || ` +
101
136
  `oc create service clusterip ${name} --tcp=${port}:${port} -n ${sandboxProject}`);
102
137
  }
103
- // Ensure the in-cluster build infrastructure (ImageStream + BuildConfig).
104
- commands.push(`oc apply -f .openshift/${projectName}/sandbox-build.yml -n ${sandboxProject}`);
105
- // Build locally, then build the image in-cluster from the uploaded context
106
- // and push to the internal registry no external registry route required.
138
+ // Build the image locally and push to the container registry, then import it
139
+ // into the namespace's imagestream. reference-policy=local mirrors it into the
140
+ // internal registry so pods pull in-cluster (no per-pod pull secret, no node
141
+ // egress to the registry). This replaces the in-cluster BuildConfig: no
142
+ // full-workspace upload, and local layer caching makes iteration fast.
107
143
  commands.push(`npx nx build ${projectName} --configuration production`);
108
- commands.push(`oc start-build ${projectName} --from-dir=. --follow --wait -n ${sandboxProject}`);
144
+ commands.push(`podman build --platform=linux/amd64 -f .openshift/${projectName}/Dockerfile -t ${imageRef} .`);
145
+ // Prereq: the publishing account is logged in with write:packages. gh supplies
146
+ // the token so no PAT is stored; the same session token backs the pull secret.
147
+ commands.push(`gh auth token | podman login ${registryHost} -u "$(gh api user -q .login)" --password-stdin`);
148
+ commands.push(`podman push ${imageRef}`);
149
+ // Per-deploy pull secret from the gh session (sandbox images are re-imported
150
+ // every run, so a session token is sufficient — no long-lived PAT needed).
151
+ commands.push(`oc create secret docker-registry ghcr-pull ` +
152
+ `--docker-server=${registryHost} --docker-username="$(gh api user -q .login)" --docker-password="$(gh auth token)" ` +
153
+ `-n ${sandboxProject} --dry-run=client -o yaml | oc apply -f -`);
154
+ // oc tag sets/repoints the imagestream tag (import-image refuses to change an
155
+ // existing tag's source); import --confirm then pulls the manifest.
156
+ commands.push(`oc tag ${imageRef} ${projectName}:sandbox --reference-policy=local -n ${sandboxProject}`);
157
+ // oc tag triggers an async imagestream reconcile, so a back-to-back
158
+ // import-image can 409 ("object has been modified"). Retry until it settles.
159
+ commands.push(`n=0; until oc import-image ${projectName}:sandbox --confirm -n ${sandboxProject}; do ` +
160
+ `n=$((n+1)); [ $n -ge 5 ] && exit 1; sleep 3; done`);
109
161
  commands.push(`oc process -f .openshift/${projectName}/${projectName}.yml -p PROJECT=${sandboxProject} | oc apply -f -`);
110
162
  commands.push(`oc rollout restart deployment/${projectName} -n ${sandboxProject}`);
111
163
  commands.push(`oc rollout status deployment/${projectName} -n ${sandboxProject} --timeout=180s`);
@@ -119,7 +171,12 @@ function addSandboxTarget(host, options) {
119
171
  executor: 'nx:run-commands',
120
172
  options: {
121
173
  commands: [
122
- `oc delete all,configmap,bc,is -l app=${projectName} -n ${sandboxProject} --ignore-not-found`,
174
+ `oc delete all,configmap,is -l app=${projectName} -n ${sandboxProject} --ignore-not-found`,
175
+ `oc delete imagestream ${projectName} -n ${sandboxProject} --ignore-not-found`,
176
+ // Remove the sandbox package (needs delete:packages). Best-effort:
177
+ // sandbox packages are the unlinked ones and can also be pruned org-wide
178
+ // via `select(.repository == null)`.
179
+ `gh api --method DELETE /orgs/${registryOrg}/packages/container/${imageName} 2>/dev/null || true`,
123
180
  ],
124
181
  },
125
182
  } });
@@ -156,7 +213,6 @@ function default_1(host, options) {
156
213
  return;
157
214
  }
158
215
  addManifestFiles(host, normalizedOptions);
159
- addBuildFiles(host, normalizedOptions);
160
216
  addDatabaseFiles(host, normalizedOptions);
161
217
  addSandboxTarget(host, normalizedOptions);
162
218
  yield registerSandboxRedirectUri(normalizedOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/sandbox/sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/sandbox/sandbox.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  addProjectConfiguration,
3
+ readNxJson,
3
4
  readProjectConfiguration,
4
5
  } from '@nx/devkit';
5
6
  import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
@@ -26,6 +27,7 @@ describe('Sandbox Generator', () => {
26
27
  const options: Schema = {
27
28
  project: 'test',
28
29
  sandboxProject: 'test-sandbox',
30
+ registry: 'ghcr.io/test-org',
29
31
  };
30
32
 
31
33
  function addNodeProject(host) {
@@ -95,13 +97,41 @@ describe('Sandbox Generator', () => {
95
97
  const cmds: string[] = config.targets['sandbox'].options.commands;
96
98
  expect(cmds.some((c) => c.includes('oc rollout restart'))).toBeTruthy();
97
99
  expect(cmds.some((c) => c.includes('oc rollout status'))).toBeTruthy();
98
- // In-cluster binary build (no local podman / external registry route).
99
100
  expect(cmds.some((c) => c.includes('nx build test'))).toBeTruthy();
101
+
102
+ // Local podman build + push to the prescribed registry, with the image name
103
+ // prefixed by the (per-user) sandbox namespace to avoid GHCR's org-global
104
+ // name collisions.
105
+ const imageRef = 'ghcr.io/test-org/test-sandbox-test:sandbox';
106
+ expect(
107
+ cmds.some((c) => c.includes('podman build') && c.includes(imageRef))
108
+ ).toBeTruthy();
109
+ expect(cmds.some((c) => c.includes(`podman push ${imageRef}`))).toBeTruthy();
110
+ expect(cmds.some((c) => c.includes('podman login ghcr.io'))).toBeTruthy();
111
+
112
+ // Import into the namespace imagestream; pods pull from the internal registry.
113
+ expect(
114
+ cmds.some((c) =>
115
+ c.includes(`oc tag ${imageRef} test:sandbox --reference-policy=local`)
116
+ )
117
+ ).toBeTruthy();
118
+ // import-image is retried to absorb the 409 from oc tag's async reconcile.
119
+ expect(
120
+ cmds.some(
121
+ (c) =>
122
+ c.includes('oc import-image test:sandbox --confirm') &&
123
+ c.includes('until')
124
+ )
125
+ ).toBeTruthy();
126
+ // Per-deploy pull secret from the gh session (no stored PAT).
100
127
  expect(
101
- cmds.some((c) => c.includes('oc start-build test --from-dir=.'))
128
+ cmds.some((c) => c.includes('oc create secret docker-registry ghcr-pull'))
102
129
  ).toBeTruthy();
103
- expect(cmds.some((c) => c.includes('sandbox-build.yml'))).toBeTruthy();
104
- expect(cmds.some((c) => c.includes('podman'))).toBeFalsy();
130
+
131
+ // The in-cluster BuildConfig flow is gone.
132
+ expect(cmds.some((c) => c.includes('oc start-build'))).toBeFalsy();
133
+ expect(host.exists('.openshift/test/sandbox-build.yml')).toBeFalsy();
134
+
105
135
  // A node service's ADSP client secret is mirrored from .env into an
106
136
  // OpenShift Secret the deployment reads.
107
137
  expect(
@@ -111,15 +141,20 @@ describe('Sandbox Generator', () => {
111
141
  c.includes('CLIENT_SECRET')
112
142
  )
113
143
  ).toBeTruthy();
144
+ });
145
+
146
+ it('persists the resolved registry to nx.json for reuse', async () => {
147
+ const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
148
+ addNodeProject(host);
114
149
 
115
- // The ImageStream + BuildConfig manifest is generated.
116
- expect(host.exists('.openshift/test/sandbox-build.yml')).toBeTruthy();
117
- const buildManifest = host
118
- .read('.openshift/test/sandbox-build.yml')
119
- .toString();
120
- expect(buildManifest).toContain('kind: BuildConfig');
121
- expect(buildManifest).toContain('kind: ImageStream');
122
- expect(buildManifest).toContain('test:sandbox');
150
+ await generator(host, options);
151
+
152
+ const nxJson = readNxJson(host);
153
+ expect(
154
+ (nxJson.generators as Record<string, { registry?: string }>)[
155
+ '@abgov/nx-oc:sandbox'
156
+ ].registry
157
+ ).toBe('ghcr.io/test-org');
123
158
  });
124
159
 
125
160
  it('adds sandbox-teardown nx target', async () => {
@@ -136,6 +171,12 @@ describe('Sandbox Generator', () => {
136
171
  expect(cmds.some((c) => c.includes('test-sandbox'))).toBeTruthy();
137
172
  expect(cmds.some((c) => c.includes('-l app=test'))).toBeTruthy();
138
173
  expect(cmds.some((c) => c.includes('all,configmap'))).toBeTruthy();
174
+ // Teardown also removes the sandbox package (best-effort).
175
+ expect(
176
+ cmds.some((c) =>
177
+ c.includes('packages/container/test-sandbox-test')
178
+ )
179
+ ).toBeTruthy();
139
180
  });
140
181
 
141
182
  it('generates shared postgres manifest with secret-backed DATABASE_URL', async () => {
@@ -11,6 +11,7 @@ export interface Schema {
11
11
  accessToken?: string;
12
12
  tenant?: string;
13
13
  tenantRealm?: string;
14
+ registry?: string;
14
15
  }
15
16
 
16
17
  export interface NormalizedSchema extends Schema {
@@ -18,4 +19,10 @@ export interface NormalizedSchema extends Schema {
18
19
  appType: ApplicationType;
19
20
  adsp: AdspConfiguration;
20
21
  buildOutputPath: string;
22
+ registry: string;
23
+ registryHost: string;
24
+ registryOrg: string;
25
+ imageName: string;
26
+ imageRef: string;
27
+ sourceRepositoryUrl?: string;
21
28
  }
@@ -51,6 +51,11 @@
51
51
  "tenantRealm": {
52
52
  "type": "string",
53
53
  "description": "Keycloak realm UUID. Optional when --tenant is provided; overrides the realm looked up from the tenant service."
54
+ },
55
+ "registry": {
56
+ "type": "string",
57
+ "description": "Container registry to publish the sandbox image to (e.g., ghcr.io/my-org). Defaults to the value stored in nx.json, then derived from the git remote. Persisted to nx.json on first use.",
58
+ "alias": "r"
54
59
  }
55
60
  },
56
61
  "required": ["project", "sandboxProject"],
@@ -1,32 +0,0 @@
1
- # Sandbox in-cluster build infrastructure for <%= projectName %>.
2
- # A binary Docker BuildConfig uploads the local build context (oc start-build
3
- # --from-dir), builds the image inside the cluster, and pushes it to the
4
- # ImageStream over the internal registry. This needs no GitHub repo/CI, no
5
- # externally-exposed registry route, and no local container runtime — unlike
6
- # the GitHub Actions + GHCR flow used for real environment deployments.
7
- apiVersion: image.openshift.io/v1
8
- kind: ImageStream
9
- metadata:
10
- name: <%= projectName %>
11
- labels:
12
- app: <%= projectName %>
13
- ---
14
- apiVersion: build.openshift.io/v1
15
- kind: BuildConfig
16
- metadata:
17
- name: <%= projectName %>
18
- labels:
19
- app: <%= projectName %>
20
- spec:
21
- runPolicy: Serial
22
- source:
23
- type: Binary
24
- binary: {}
25
- strategy:
26
- type: Docker
27
- dockerStrategy:
28
- dockerfilePath: .openshift/<%= projectName %>/Dockerfile
29
- output:
30
- to:
31
- kind: ImageStreamTag
32
- name: <%= projectName %>:sandbox