@abgov/nx-oc 12.14.1 → 12.15.0
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 +1 -1
- package/src/adsp/environments.js +3 -14
- package/src/build-assets.spec.ts +62 -0
- package/src/generators/deployment/deployment.js +7 -1
- package/src/generators/deployment/deployment.js.map +1 -1
- package/src/generators/deployment/dotnet-files/Dockerfile__tmpl__ +4 -0
- package/src/generators/deployment/frontend-files/Dockerfile__tmpl__ +4 -1
- package/src/generators/deployment/node-files/Dockerfile__tmpl__ +4 -1
- package/src/generators/sandbox/sandbox.js +74 -21
- package/src/generators/sandbox/sandbox.js.map +1 -1
- package/src/generators/sandbox/sandbox.spec.ts +48 -12
- package/src/generators/sandbox/schema.d.ts +7 -0
- package/src/generators/sandbox/schema.json +5 -0
- package/src/generators/sandbox/build-files/__projectName__/sandbox-build.yml__tmpl__ +0 -32
package/package.json
CHANGED
package/src/adsp/environments.js
CHANGED
|
@@ -1,19 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var _a, _b, _c, _d, _e, _f;
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.environments = void 0;
|
|
5
4
|
exports.environments = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
},
|
|
10
|
-
test: {
|
|
11
|
-
accessServiceUrl: (_c = process.env.ADSP_E2E_ACCESS_URL) !== null && _c !== void 0 ? _c : 'https://access-uat.alberta.ca',
|
|
12
|
-
directoryServiceUrl: (_d = process.env.ADSP_E2E_DIRECTORY_URL) !== null && _d !== void 0 ? _d : 'https://directory-service.adsp-uat.alberta.ca',
|
|
13
|
-
},
|
|
14
|
-
prod: {
|
|
15
|
-
accessServiceUrl: (_e = process.env.ADSP_E2E_ACCESS_URL) !== null && _e !== void 0 ? _e : 'https://access.alberta.ca',
|
|
16
|
-
directoryServiceUrl: (_f = process.env.ADSP_E2E_DIRECTORY_URL) !== null && _f !== void 0 ? _f : 'https://directory-service.adsp.alberta.ca',
|
|
17
|
-
},
|
|
5
|
+
dev: { accessServiceUrl: "http://localhost:46205", directoryServiceUrl: "http://localhost:46205" },
|
|
6
|
+
test: { accessServiceUrl: "http://localhost:46205", directoryServiceUrl: "http://localhost:46205" },
|
|
7
|
+
prod: { accessServiceUrl: "http://localhost:46205", directoryServiceUrl: "http://localhost:46205" },
|
|
18
8
|
};
|
|
19
|
-
//# sourceMappingURL=environments.js.map
|
|
@@ -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), {
|
|
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":";;
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/deployment/deployment.ts"],"names":[],"mappings":"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"}
|
|
@@ -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), {
|
|
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,26 @@ 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
|
-
//
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
//
|
|
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(`
|
|
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
|
+
commands.push(`oc import-image ${projectName}:sandbox --confirm -n ${sandboxProject}`);
|
|
109
158
|
commands.push(`oc process -f .openshift/${projectName}/${projectName}.yml -p PROJECT=${sandboxProject} | oc apply -f -`);
|
|
110
159
|
commands.push(`oc rollout restart deployment/${projectName} -n ${sandboxProject}`);
|
|
111
160
|
commands.push(`oc rollout status deployment/${projectName} -n ${sandboxProject} --timeout=180s`);
|
|
@@ -119,7 +168,12 @@ function addSandboxTarget(host, options) {
|
|
|
119
168
|
executor: 'nx:run-commands',
|
|
120
169
|
options: {
|
|
121
170
|
commands: [
|
|
122
|
-
`oc delete all,configmap,
|
|
171
|
+
`oc delete all,configmap,is -l app=${projectName} -n ${sandboxProject} --ignore-not-found`,
|
|
172
|
+
`oc delete imagestream ${projectName} -n ${sandboxProject} --ignore-not-found`,
|
|
173
|
+
// Remove the sandbox package (needs delete:packages). Best-effort:
|
|
174
|
+
// sandbox packages are the unlinked ones and can also be pruned org-wide
|
|
175
|
+
// via `select(.repository == null)`.
|
|
176
|
+
`gh api --method DELETE /orgs/${registryOrg}/packages/container/${imageName} 2>/dev/null || true`,
|
|
123
177
|
],
|
|
124
178
|
},
|
|
125
179
|
} });
|
|
@@ -156,7 +210,6 @@ function default_1(host, options) {
|
|
|
156
210
|
return;
|
|
157
211
|
}
|
|
158
212
|
addManifestFiles(host, normalizedOptions);
|
|
159
|
-
addBuildFiles(host, normalizedOptions);
|
|
160
213
|
addDatabaseFiles(host, normalizedOptions);
|
|
161
214
|
addSandboxTarget(host, normalizedOptions);
|
|
162
215
|
yield registerSandboxRedirectUri(normalizedOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/generators/sandbox/sandbox.ts"],"names":[],"mappings":";;
|
|
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,36 @@ 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.
|
|
100
113
|
expect(
|
|
101
|
-
cmds.some((c) =>
|
|
114
|
+
cmds.some((c) =>
|
|
115
|
+
c.includes(`oc tag ${imageRef} test:sandbox --reference-policy=local`)
|
|
116
|
+
)
|
|
102
117
|
).toBeTruthy();
|
|
103
|
-
expect(
|
|
104
|
-
|
|
118
|
+
expect(
|
|
119
|
+
cmds.some((c) => c.includes('oc import-image test:sandbox --confirm'))
|
|
120
|
+
).toBeTruthy();
|
|
121
|
+
// Per-deploy pull secret from the gh session (no stored PAT).
|
|
122
|
+
expect(
|
|
123
|
+
cmds.some((c) => c.includes('oc create secret docker-registry ghcr-pull'))
|
|
124
|
+
).toBeTruthy();
|
|
125
|
+
|
|
126
|
+
// The in-cluster BuildConfig flow is gone.
|
|
127
|
+
expect(cmds.some((c) => c.includes('oc start-build'))).toBeFalsy();
|
|
128
|
+
expect(host.exists('.openshift/test/sandbox-build.yml')).toBeFalsy();
|
|
129
|
+
|
|
105
130
|
// A node service's ADSP client secret is mirrored from .env into an
|
|
106
131
|
// OpenShift Secret the deployment reads.
|
|
107
132
|
expect(
|
|
@@ -111,15 +136,20 @@ describe('Sandbox Generator', () => {
|
|
|
111
136
|
c.includes('CLIENT_SECRET')
|
|
112
137
|
)
|
|
113
138
|
).toBeTruthy();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('persists the resolved registry to nx.json for reuse', async () => {
|
|
142
|
+
const host = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
|
143
|
+
addNodeProject(host);
|
|
144
|
+
|
|
145
|
+
await generator(host, options);
|
|
114
146
|
|
|
115
|
-
|
|
116
|
-
expect(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.
|
|
120
|
-
|
|
121
|
-
expect(buildManifest).toContain('kind: ImageStream');
|
|
122
|
-
expect(buildManifest).toContain('test:sandbox');
|
|
147
|
+
const nxJson = readNxJson(host);
|
|
148
|
+
expect(
|
|
149
|
+
(nxJson.generators as Record<string, { registry?: string }>)[
|
|
150
|
+
'@abgov/nx-oc:sandbox'
|
|
151
|
+
].registry
|
|
152
|
+
).toBe('ghcr.io/test-org');
|
|
123
153
|
});
|
|
124
154
|
|
|
125
155
|
it('adds sandbox-teardown nx target', async () => {
|
|
@@ -136,6 +166,12 @@ describe('Sandbox Generator', () => {
|
|
|
136
166
|
expect(cmds.some((c) => c.includes('test-sandbox'))).toBeTruthy();
|
|
137
167
|
expect(cmds.some((c) => c.includes('-l app=test'))).toBeTruthy();
|
|
138
168
|
expect(cmds.some((c) => c.includes('all,configmap'))).toBeTruthy();
|
|
169
|
+
// Teardown also removes the sandbox package (best-effort).
|
|
170
|
+
expect(
|
|
171
|
+
cmds.some((c) =>
|
|
172
|
+
c.includes('packages/container/test-sandbox-test')
|
|
173
|
+
)
|
|
174
|
+
).toBeTruthy();
|
|
139
175
|
});
|
|
140
176
|
|
|
141
177
|
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
|