@abgov/nx-oc 13.0.0-beta.1 → 13.0.0-beta.3
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/executors.json +5 -0
- package/migrations.json +10 -0
- package/package.json +4 -1
- package/src/adsp/adsp-utils.d.ts +10 -0
- package/src/adsp/adsp-utils.js +48 -0
- package/src/adsp/adsp-utils.js.map +1 -1
- package/src/build-assets.spec.ts +62 -0
- package/src/executors/sandbox/sandbox.d.ts +5 -0
- package/src/executors/sandbox/sandbox.js +183 -0
- package/src/executors/sandbox/sandbox.js.map +1 -0
- package/src/executors/sandbox/sandbox.spec.ts +186 -0
- package/src/executors/sandbox/schema.d.ts +12 -0
- package/src/executors/sandbox/schema.json +50 -0
- package/src/generators/deployment/deployment.js +11 -25
- package/src/generators/deployment/deployment.js.map +1 -1
- package/src/generators/deployment/deployment.spec.ts +5 -2
- package/src/generators/deployment/dotnet-files/Dockerfile__tmpl__ +16 -0
- package/src/generators/deployment/frontend-files/Dockerfile__tmpl__ +10 -0
- package/src/generators/deployment/node-files/Dockerfile__tmpl__ +10 -0
- package/src/generators/deployment/node-files/__projectName__.yml__tmpl__ +12 -1
- package/src/generators/deployment/schema.d.ts +1 -0
- package/src/generators/sandbox/database-files/sandbox-postgres.yml__tmpl__ +3 -1
- package/src/generators/sandbox/files/SANDBOX.md__tmpl__ +91 -0
- package/src/generators/sandbox/sandbox.js +109 -56
- package/src/generators/sandbox/sandbox.js.map +1 -1
- package/src/generators/sandbox/sandbox.spec.ts +110 -13
- package/src/generators/sandbox/schema.d.ts +10 -0
- package/src/generators/sandbox/schema.json +13 -0
- package/src/migrations/convert-sandbox-target/convert-sandbox-target.d.ts +2 -0
- package/src/migrations/convert-sandbox-target/convert-sandbox-target.js +54 -0
- package/src/migrations/convert-sandbox-target/convert-sandbox-target.js.map +1 -0
- package/src/migrations/convert-sandbox-target/convert-sandbox-target.spec.ts +111 -0
- package/src/utils/app-type.d.ts +18 -0
- package/src/utils/app-type.js +43 -0
- package/src/utils/app-type.js.map +1 -0
- package/src/utils/git-utils.js +4 -2
- package/src/utils/git-utils.js.map +1 -1
- package/src/utils/oc-utils.d.ts +1 -0
- package/src/utils/oc-utils.js +21 -0
- package/src/utils/oc-utils.js.map +1 -1
- package/src/generators/deployment/dotnet-files/Dockerfile.template +0 -10
- package/src/generators/deployment/frontend-files/Dockerfile.template +0 -8
- package/src/generators/deployment/node-files/Dockerfile.template +0 -8
package/executors.json
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
"implementation": "./src/executors/apply/apply",
|
|
6
6
|
"schema": "./src/executors/apply/schema.json",
|
|
7
7
|
"description": "Executor that applies deployment template to OpenShift."
|
|
8
|
+
},
|
|
9
|
+
"sandbox": {
|
|
10
|
+
"implementation": "./src/executors/sandbox/sandbox",
|
|
11
|
+
"schema": "./src/executors/sandbox/schema.json",
|
|
12
|
+
"description": "Build locally with podman, push to the registry, import into the sandbox namespace, and roll out."
|
|
8
13
|
}
|
|
9
14
|
}
|
|
10
15
|
}
|
package/migrations.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"generators": {
|
|
4
|
+
"convert-sandbox-target-to-executor": {
|
|
5
|
+
"version": "13.0.0-beta.3",
|
|
6
|
+
"description": "Convert generated sandbox `nx:run-commands` targets to the @abgov/nx-oc:sandbox executor.",
|
|
7
|
+
"implementation": "./src/migrations/convert-sandbox-target/convert-sandbox-target"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abgov/nx-oc",
|
|
3
|
-
"version": "13.0.0-beta.
|
|
3
|
+
"version": "13.0.0-beta.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"description": "Government of Alberta - Nx plugin for OpenShift.",
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"generators": "./generators.json",
|
|
25
25
|
"executors": "./executors.json",
|
|
26
|
+
"nx-migrations": {
|
|
27
|
+
"migrations": "./migrations.json"
|
|
28
|
+
},
|
|
26
29
|
"scripts": {},
|
|
27
30
|
"types": "./src/index.d.ts",
|
|
28
31
|
"type": "commonjs"
|
package/src/adsp/adsp-utils.d.ts
CHANGED
|
@@ -15,3 +15,13 @@ export declare function getAdspConfiguration(_host: Tree, options: {
|
|
|
15
15
|
tenantRealm?: string;
|
|
16
16
|
}): Promise<AdspConfiguration>;
|
|
17
17
|
export declare function isAdspOptions(options: unknown): options is AdspOptions;
|
|
18
|
+
/**
|
|
19
|
+
* Registers redirect URIs (and matching post-logout URIs) on an existing public
|
|
20
|
+
* client so a deployed app can complete browser sign-in/out against its route.
|
|
21
|
+
*
|
|
22
|
+
* Idempotent and best-effort: no-ops without a token, skips if the client isn't
|
|
23
|
+
* found, and logs (never throws) on failure so it can't break generation. The
|
|
24
|
+
* client's `webOrigins` is intentionally left as-is — the default `["+"]`
|
|
25
|
+
* already allows CORS from any redirect-URI origin.
|
|
26
|
+
*/
|
|
27
|
+
export declare function addClientRedirectUris(accessServiceUrl: string, realm: string, clientId: string, redirectUris: string[], accessToken: string | undefined): Promise<void>;
|
package/src/adsp/adsp-utils.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.getServiceUrls = getServiceUrls;
|
|
|
6
6
|
exports.selectTenant = selectTenant;
|
|
7
7
|
exports.getAdspConfiguration = getAdspConfiguration;
|
|
8
8
|
exports.isAdspOptions = isAdspOptions;
|
|
9
|
+
exports.addClientRedirectUris = addClientRedirectUris;
|
|
9
10
|
const tslib_1 = require("tslib");
|
|
10
11
|
const devkit_1 = require("@nx/devkit");
|
|
11
12
|
const axios_1 = require("axios");
|
|
@@ -162,4 +163,51 @@ function isAdspOptions(options) {
|
|
|
162
163
|
var _a;
|
|
163
164
|
return !!((_a = options === null || options === void 0 ? void 0 : options.adsp) === null || _a === void 0 ? void 0 : _a.tenantRealm);
|
|
164
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Registers redirect URIs (and matching post-logout URIs) on an existing public
|
|
168
|
+
* client so a deployed app can complete browser sign-in/out against its route.
|
|
169
|
+
*
|
|
170
|
+
* Idempotent and best-effort: no-ops without a token, skips if the client isn't
|
|
171
|
+
* found, and logs (never throws) on failure so it can't break generation. The
|
|
172
|
+
* client's `webOrigins` is intentionally left as-is — the default `["+"]`
|
|
173
|
+
* already allows CORS from any redirect-URI origin.
|
|
174
|
+
*/
|
|
175
|
+
function addClientRedirectUris(accessServiceUrl, realm, clientId, redirectUris, accessToken) {
|
|
176
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
177
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
178
|
+
if (!accessToken || redirectUris.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
const base = new URL(`/auth/admin/realms/${realm}/clients`, accessServiceUrl)
|
|
181
|
+
.href;
|
|
182
|
+
const authHeader = { Authorization: `Bearer ${accessToken}` };
|
|
183
|
+
const union = (existing, additions) => Array.from(new Set([...(existing !== null && existing !== void 0 ? existing : []), ...additions]));
|
|
184
|
+
try {
|
|
185
|
+
const { data: clients } = yield axios_1.default.get(base, {
|
|
186
|
+
params: { clientId },
|
|
187
|
+
headers: authHeader,
|
|
188
|
+
});
|
|
189
|
+
const client = clients === null || clients === void 0 ? void 0 : clients[0];
|
|
190
|
+
if (!client) {
|
|
191
|
+
console.log(`[nx-oc] Public client '${clientId}' not found in realm '${realm}' — skipping redirect URI update.`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const existingPostLogout = ((_b = (_a = client.attributes) === null || _a === void 0 ? void 0 : _a['post.logout.redirect.uris']) !== null && _b !== void 0 ? _b : '')
|
|
195
|
+
.split('##')
|
|
196
|
+
.filter(Boolean);
|
|
197
|
+
const nextRedirects = union(client.redirectUris, redirectUris);
|
|
198
|
+
const nextPostLogout = union(existingPostLogout, redirectUris).join('##');
|
|
199
|
+
if (nextRedirects.length === ((_d = (_c = client.redirectUris) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) &&
|
|
200
|
+
nextPostLogout === ((_f = (_e = client.attributes) === null || _e === void 0 ? void 0 : _e['post.logout.redirect.uris']) !== null && _f !== void 0 ? _f : '')) {
|
|
201
|
+
console.log(`[nx-oc] Client '${clientId}' already allows ${redirectUris.join(', ')}.`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
yield axios_1.default.put(`${base}/${client.id}`, Object.assign(Object.assign({}, client), { redirectUris: nextRedirects, attributes: Object.assign(Object.assign({}, client.attributes), { 'post.logout.redirect.uris': nextPostLogout }) }), { headers: authHeader });
|
|
205
|
+
console.log(`[nx-oc] Registered redirect URI(s) on client '${clientId}': ${redirectUris.join(', ')}`);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
const detail = (_j = (_h = (_g = err === null || err === void 0 ? void 0 : err.response) === null || _g === void 0 ? void 0 : _g.status) !== null && _h !== void 0 ? _h : err === null || err === void 0 ? void 0 : err.message) !== null && _j !== void 0 ? _j : err;
|
|
209
|
+
console.log(`[nx-oc] Could not update redirect URIs for '${clientId}': ${detail}`);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
165
213
|
//# sourceMappingURL=adsp-utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adsp-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/adsp/adsp-utils.ts"],"names":[],"mappings":";;AAeA,sCAOC;AAED,gCAqFC;AAED,wCAYC;AAED,oCAoBC;AAED,oDAgDC;AAED,sCAEC;;
|
|
1
|
+
{"version":3,"file":"adsp-utils.js","sourceRoot":"","sources":["../../../../../packages/nx-oc/src/adsp/adsp-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = runExecutor;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const app_type_1 = require("../../utils/app-type");
|
|
8
|
+
const oc_utils_1 = require("../../utils/oc-utils");
|
|
9
|
+
const PROXY_TAG_PREFIX = 'adsp:proxy-service:';
|
|
10
|
+
// Fail fast, with an actionable message, before the (slow) production build —
|
|
11
|
+
// rather than a raw "command not found" partway through.
|
|
12
|
+
function requireTool(tool, hint) {
|
|
13
|
+
try {
|
|
14
|
+
(0, child_process_1.execSync)(`command -v ${tool}`, { stdio: 'ignore', shell: '/bin/bash' });
|
|
15
|
+
}
|
|
16
|
+
catch (_a) {
|
|
17
|
+
throw new Error(`'${tool}' is required but was not found on PATH. ${hint}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// The pull secret + registry login read the gh session token, so gh must be
|
|
21
|
+
// installed AND authenticated. Checked up front so a missing/expired login
|
|
22
|
+
// fails before the slow build rather than at the push/import step.
|
|
23
|
+
function requireGhAuth() {
|
|
24
|
+
requireTool('gh', 'Install the GitHub CLI (https://cli.github.com).');
|
|
25
|
+
try {
|
|
26
|
+
(0, child_process_1.execSync)('gh auth status', { stdio: 'ignore', shell: '/bin/bash' });
|
|
27
|
+
}
|
|
28
|
+
catch (_a) {
|
|
29
|
+
throw new Error('gh is installed but not authenticated. Run `gh auth login` as an account with write:packages on the registry org (check/switch with `gh auth status` / `gh auth switch`).');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// podman must be installed and its machine reachable. `podman info` fails when
|
|
33
|
+
// the machine is stopped, so it catches that before the build.
|
|
34
|
+
function requirePodman() {
|
|
35
|
+
requireTool('podman', "Install it (macOS: 'brew install podman').");
|
|
36
|
+
try {
|
|
37
|
+
(0, child_process_1.execSync)('podman info', { stdio: 'ignore', shell: '/bin/bash' });
|
|
38
|
+
}
|
|
39
|
+
catch (_a) {
|
|
40
|
+
throw new Error("podman is installed but not responding — start the machine (macOS: 'podman machine start').");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function run(label, cmd, cwd) {
|
|
44
|
+
devkit_1.logger.info(`\n▸ ${label}`);
|
|
45
|
+
// Safe to echo: secrets are shell substitutions ($(gh auth token), $(grep …
|
|
46
|
+
// .env)), so the literal command never contains a resolved secret.
|
|
47
|
+
devkit_1.logger.info(` $ ${cmd}`);
|
|
48
|
+
(0, child_process_1.execSync)(cmd, { stdio: 'inherit', cwd, shell: '/bin/bash' });
|
|
49
|
+
}
|
|
50
|
+
// oc tag triggers an async imagestream reconcile, so a back-to-back
|
|
51
|
+
// import-image can 409 ("object has been modified"). Retry until it settles.
|
|
52
|
+
function importWithRetry(imageStreamTag, namespace, cwd, retries) {
|
|
53
|
+
for (let attempt = 1;; attempt++) {
|
|
54
|
+
try {
|
|
55
|
+
run(`Import image (attempt ${attempt}/${retries})`, `oc import-image ${imageStreamTag} --confirm -n ${namespace}`, cwd);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (attempt >= retries) {
|
|
60
|
+
throw new Error(`oc import-image failed after ${retries} attempts: ${err.message}`);
|
|
61
|
+
}
|
|
62
|
+
devkit_1.logger.warn(` import-image failed (likely the oc tag reconcile race); retrying in 3s`);
|
|
63
|
+
(0, child_process_1.execSync)('sleep 3', { stdio: 'ignore' });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function runExecutor(options, context) {
|
|
68
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
var _a, _b, _c, _d;
|
|
70
|
+
const projectName = context.projectName;
|
|
71
|
+
const project = projectName && ((_a = context.projectsConfigurations) === null || _a === void 0 ? void 0 : _a.projects[projectName]);
|
|
72
|
+
if (!projectName || !project) {
|
|
73
|
+
devkit_1.logger.error('[nx-oc:sandbox] Could not resolve the target project.');
|
|
74
|
+
return { success: false };
|
|
75
|
+
}
|
|
76
|
+
const { sandboxProject, registry, imageTag = 'sandbox', skipBuild = false, skipPush = false, importRetries = 5, } = options;
|
|
77
|
+
if (!sandboxProject) {
|
|
78
|
+
devkit_1.logger.error('[nx-oc:sandbox] "sandboxProject" is required.');
|
|
79
|
+
return { success: false };
|
|
80
|
+
}
|
|
81
|
+
if (!registry) {
|
|
82
|
+
devkit_1.logger.error('[nx-oc:sandbox] "registry" is required.');
|
|
83
|
+
return { success: false };
|
|
84
|
+
}
|
|
85
|
+
const appType = (_b = options.appType) !== null && _b !== void 0 ? _b : (0, app_type_1.detectApplicationType)(project);
|
|
86
|
+
if (!appType) {
|
|
87
|
+
devkit_1.logger.error(`[nx-oc:sandbox] Could not determine the application type for ${projectName}.`);
|
|
88
|
+
return { success: false };
|
|
89
|
+
}
|
|
90
|
+
const database = (_c = options.database) !== null && _c !== void 0 ? _c : 'none';
|
|
91
|
+
// Container registries (GHCR) require lowercase paths. Prefix the image with
|
|
92
|
+
// the (per-user) namespace so images from different experimenters never
|
|
93
|
+
// collide on GHCR's org-global package names.
|
|
94
|
+
const reg = registry.toLowerCase();
|
|
95
|
+
const registryHost = reg.split('/')[0];
|
|
96
|
+
const imageName = `${sandboxProject}-${projectName}`.toLowerCase();
|
|
97
|
+
const imageRef = `${reg}/${imageName}:${imageTag}`;
|
|
98
|
+
const projectRoot = project.root;
|
|
99
|
+
const cwd = context.root;
|
|
100
|
+
try {
|
|
101
|
+
// ---- preflight: check prerequisites before the expensive build ----
|
|
102
|
+
(0, oc_utils_1.ensureOcLogin)();
|
|
103
|
+
// The pull secret + import always need gh (session token to reach GHCR).
|
|
104
|
+
requireGhAuth();
|
|
105
|
+
if (!skipBuild || !skipPush) {
|
|
106
|
+
requirePodman();
|
|
107
|
+
}
|
|
108
|
+
// ---- service client secret (node services authenticate to ADSP) ----
|
|
109
|
+
// Upserted from the current .env so re-runs pick up a rotated secret.
|
|
110
|
+
if (appType === 'node') {
|
|
111
|
+
run('Upsert CLIENT_SECRET', `oc create secret generic ${projectName}-secrets ` +
|
|
112
|
+
`--from-literal=CLIENT_SECRET="$(grep -E '^CLIENT_SECRET=' ${projectRoot}/.env 2>/dev/null | cut -d= -f2-)" ` +
|
|
113
|
+
`-n ${sandboxProject} --dry-run=client -o yaml | oc apply -f -`, cwd);
|
|
114
|
+
}
|
|
115
|
+
// ---- shared database provisioning ----
|
|
116
|
+
if (database === 'postgres') {
|
|
117
|
+
run('Ensure Postgres credentials', `oc get secret sandbox-postgres-creds -n ${sandboxProject} 2>/dev/null || ` +
|
|
118
|
+
`oc create secret generic sandbox-postgres-creds ` +
|
|
119
|
+
`--from-literal=POSTGRESQL_ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=') ` +
|
|
120
|
+
`-n ${sandboxProject}`, cwd);
|
|
121
|
+
run('Deploy shared Postgres', `oc apply -f .openshift/sandbox/sandbox-postgres.yml -n ${sandboxProject}`, cwd);
|
|
122
|
+
// The shared Postgres only creates the admin database; each app needs its
|
|
123
|
+
// own <app>_sandbox database created before its migrate init container
|
|
124
|
+
// runs. Wait for Postgres, then create the database idempotently.
|
|
125
|
+
const dbName = `${projectName}_sandbox`;
|
|
126
|
+
run('Create app database', `oc rollout status deployment/sandbox-postgres -n ${sandboxProject} --timeout=180s && ` +
|
|
127
|
+
`oc exec -n ${sandboxProject} deployment/sandbox-postgres -- ` +
|
|
128
|
+
`bash -lc "psql -U postgres -tc \\"SELECT 1 FROM pg_database WHERE datname='${dbName}'\\" ` +
|
|
129
|
+
`| grep -q 1 || createdb -U postgres ${dbName}"`, cwd);
|
|
130
|
+
}
|
|
131
|
+
else if (database === 'mongo') {
|
|
132
|
+
run('Ensure MongoDB credentials', `oc get secret sandbox-mongodb-creds -n ${sandboxProject} 2>/dev/null || ` +
|
|
133
|
+
`oc create secret generic sandbox-mongodb-creds ` +
|
|
134
|
+
`--from-literal=MONGODB_ADMIN_PASSWORD=$(openssl rand -base64 24 | tr -d '/+=') ` +
|
|
135
|
+
`-n ${sandboxProject}`, cwd);
|
|
136
|
+
run('Deploy shared MongoDB', `oc apply -f .openshift/sandbox/sandbox-mongodb.yml -n ${sandboxProject}`, cwd);
|
|
137
|
+
}
|
|
138
|
+
// ---- paired backend Services (so a frontend's nginx resolves proxy_pass
|
|
139
|
+
// upstreams at startup). Only the Service is needed for DNS. Idempotent. ----
|
|
140
|
+
const proxyServices = ((_d = project.tags) !== null && _d !== void 0 ? _d : [])
|
|
141
|
+
.filter((tag) => tag.startsWith(PROXY_TAG_PREFIX))
|
|
142
|
+
.map((tag) => {
|
|
143
|
+
const value = tag.slice(PROXY_TAG_PREFIX.length);
|
|
144
|
+
const lastColon = value.lastIndexOf(':');
|
|
145
|
+
return { name: value.slice(0, lastColon), port: value.slice(lastColon + 1) };
|
|
146
|
+
});
|
|
147
|
+
for (const { name, port } of proxyServices) {
|
|
148
|
+
run(`Ensure paired Service ${name}`, `oc get service ${name} -n ${sandboxProject} >/dev/null 2>&1 || ` +
|
|
149
|
+
`oc create service clusterip ${name} --tcp=${port}:${port} -n ${sandboxProject}`, cwd);
|
|
150
|
+
}
|
|
151
|
+
// ---- build locally + push to the registry ----
|
|
152
|
+
if (!skipBuild) {
|
|
153
|
+
run('Build', `npx nx build ${projectName} --configuration production`, cwd);
|
|
154
|
+
run('Podman build', `podman build --platform=linux/amd64 -f .openshift/${projectName}/Dockerfile -t ${imageRef} .`, cwd);
|
|
155
|
+
}
|
|
156
|
+
if (!skipPush) {
|
|
157
|
+
// gh supplies the token so no PAT is stored; the same session token backs
|
|
158
|
+
// the pull secret below.
|
|
159
|
+
run('Registry login', `gh auth token | podman login ${registryHost} -u "$(gh api user -q .login)" --password-stdin`, cwd);
|
|
160
|
+
run('Push image', `podman push ${imageRef}`, cwd);
|
|
161
|
+
}
|
|
162
|
+
// ---- import into the namespace imagestream + roll out ----
|
|
163
|
+
// Per-deploy pull secret from the gh session (no long-lived PAT needed).
|
|
164
|
+
run('Upsert pull secret', `oc create secret docker-registry ghcr-pull ` +
|
|
165
|
+
`--docker-server=${registryHost} --docker-username="$(gh api user -q .login)" --docker-password="$(gh auth token)" ` +
|
|
166
|
+
`-n ${sandboxProject} --dry-run=client -o yaml | oc apply -f -`, cwd);
|
|
167
|
+
// oc tag sets/repoints the imagestream tag with reference-policy=local so
|
|
168
|
+
// pods pull in-cluster; import --confirm then pulls the current manifest.
|
|
169
|
+
run('Tag imagestream', `oc tag ${imageRef} ${projectName}:${imageTag} --reference-policy=local -n ${sandboxProject}`, cwd);
|
|
170
|
+
importWithRetry(`${projectName}:${imageTag}`, sandboxProject, cwd, importRetries);
|
|
171
|
+
run('Apply manifest', `oc process -f .openshift/${projectName}/${projectName}.yml -p PROJECT=${sandboxProject} | oc apply -f -`, cwd);
|
|
172
|
+
run('Restart rollout', `oc rollout restart deployment/${projectName} -n ${sandboxProject}`, cwd);
|
|
173
|
+
run('Wait for rollout', `oc rollout status deployment/${projectName} -n ${sandboxProject} --timeout=180s`, cwd);
|
|
174
|
+
devkit_1.logger.info(`\n✓ Sandbox deploy complete for ${projectName}.`);
|
|
175
|
+
return { success: true };
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
devkit_1.logger.error(`\n[nx-oc:sandbox] ${err.message}`);
|
|
179
|
+
return { success: false };
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../../../../packages/nx-oc/src/executors/sandbox/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
import runExecutor from './sandbox';
|
|
3
|
+
import { SandboxExecutorSchema } from './schema';
|
|
4
|
+
|
|
5
|
+
jest.mock('child_process', () => ({
|
|
6
|
+
...jest.requireActual('child_process'),
|
|
7
|
+
execSync: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
jest.mock('../../utils/oc-utils', () => ({ ensureOcLogin: jest.fn() }));
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
12
|
+
const { execSync } = require('child_process') as { execSync: jest.Mock };
|
|
13
|
+
|
|
14
|
+
const IMAGE_REF = 'ghcr.io/test-org/test-sandbox-test:sandbox';
|
|
15
|
+
|
|
16
|
+
function context(tags: string[] = []): ExecutorContext {
|
|
17
|
+
return {
|
|
18
|
+
root: '/ws',
|
|
19
|
+
cwd: '/ws',
|
|
20
|
+
isVerbose: false,
|
|
21
|
+
projectName: 'test',
|
|
22
|
+
projectsConfigurations: {
|
|
23
|
+
version: 2,
|
|
24
|
+
projects: {
|
|
25
|
+
test: { root: 'apps/test', tags },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
} as unknown as ExecutorContext;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const baseOptions: SandboxExecutorSchema = {
|
|
32
|
+
sandboxProject: 'test-sandbox',
|
|
33
|
+
registry: 'ghcr.io/test-org',
|
|
34
|
+
appType: 'node',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// The commands passed to execSync, in call order.
|
|
38
|
+
function commands(): string[] {
|
|
39
|
+
return execSync.mock.calls.map((c) => c[0] as string);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
execSync.mockReset();
|
|
44
|
+
execSync.mockImplementation(() => Buffer.from(''));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('sandbox executor', () => {
|
|
48
|
+
it('builds, pushes, tags, imports, and rolls out in order', async () => {
|
|
49
|
+
const result = await runExecutor(baseOptions, context());
|
|
50
|
+
expect(result.success).toBe(true);
|
|
51
|
+
|
|
52
|
+
const cmds = commands();
|
|
53
|
+
const has = (s: string) => cmds.some((c) => c.includes(s));
|
|
54
|
+
expect(has('nx build test --configuration production')).toBe(true);
|
|
55
|
+
expect(
|
|
56
|
+
has(
|
|
57
|
+
`podman build --platform=linux/amd64 -f .openshift/test/Dockerfile -t ${IMAGE_REF} .`
|
|
58
|
+
)
|
|
59
|
+
).toBe(true);
|
|
60
|
+
expect(has(`podman push ${IMAGE_REF}`)).toBe(true);
|
|
61
|
+
expect(
|
|
62
|
+
has(`oc tag ${IMAGE_REF} test:sandbox --reference-policy=local -n test-sandbox`)
|
|
63
|
+
).toBe(true);
|
|
64
|
+
expect(has('oc import-image test:sandbox --confirm -n test-sandbox')).toBe(true);
|
|
65
|
+
expect(has('oc create secret docker-registry ghcr-pull')).toBe(true);
|
|
66
|
+
expect(has('oc rollout restart deployment/test -n test-sandbox')).toBe(true);
|
|
67
|
+
expect(has('oc rollout status deployment/test -n test-sandbox')).toBe(true);
|
|
68
|
+
|
|
69
|
+
// ordering: build precedes push precedes tag precedes import precedes rollout
|
|
70
|
+
const idx = (s: string) => cmds.findIndex((c) => c.includes(s));
|
|
71
|
+
expect(idx('podman build')).toBeLessThan(idx('podman push'));
|
|
72
|
+
expect(idx('podman push')).toBeLessThan(idx('oc tag'));
|
|
73
|
+
expect(idx('oc tag')).toBeLessThan(idx('oc import-image'));
|
|
74
|
+
expect(idx('oc import-image')).toBeLessThan(idx('oc rollout restart'));
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('mirrors CLIENT_SECRET for a node service only', async () => {
|
|
78
|
+
await runExecutor(baseOptions, context());
|
|
79
|
+
expect(
|
|
80
|
+
commands().some(
|
|
81
|
+
(c) => c.includes('oc create secret generic test-secrets') && c.includes('CLIENT_SECRET')
|
|
82
|
+
)
|
|
83
|
+
).toBe(true);
|
|
84
|
+
|
|
85
|
+
execSync.mockClear();
|
|
86
|
+
await runExecutor({ ...baseOptions, appType: 'frontend' }, context());
|
|
87
|
+
expect(commands().some((c) => c.includes('test-secrets'))).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('fails fast with an actionable message when podman is missing', async () => {
|
|
91
|
+
execSync.mockImplementation((cmd: string) => {
|
|
92
|
+
if (cmd.startsWith('command -v podman')) throw new Error('not found');
|
|
93
|
+
return Buffer.from('');
|
|
94
|
+
});
|
|
95
|
+
const result = await runExecutor(baseOptions, context());
|
|
96
|
+
expect(result.success).toBe(false);
|
|
97
|
+
// did not proceed to the build
|
|
98
|
+
expect(commands().some((c) => c.includes('podman build'))).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('fails fast when the podman machine is not running', async () => {
|
|
102
|
+
execSync.mockImplementation((cmd: string) => {
|
|
103
|
+
if (cmd === 'podman info') throw new Error('cannot connect');
|
|
104
|
+
return Buffer.from('');
|
|
105
|
+
});
|
|
106
|
+
const result = await runExecutor(baseOptions, context());
|
|
107
|
+
expect(result.success).toBe(false);
|
|
108
|
+
expect(commands().some((c) => c.includes('podman build'))).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('fails fast when gh is installed but not authenticated (before the build)', async () => {
|
|
112
|
+
execSync.mockImplementation((cmd: string) => {
|
|
113
|
+
if (cmd === 'gh auth status') throw new Error('not logged in');
|
|
114
|
+
return Buffer.from('');
|
|
115
|
+
});
|
|
116
|
+
const result = await runExecutor(baseOptions, context());
|
|
117
|
+
expect(result.success).toBe(false);
|
|
118
|
+
// gh checked up front — no build/push happened
|
|
119
|
+
expect(commands().some((c) => c.includes('nx build test'))).toBe(false);
|
|
120
|
+
expect(commands().some((c) => c.includes('podman build'))).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('retries oc import-image on the tag-reconcile race', async () => {
|
|
124
|
+
let importAttempts = 0;
|
|
125
|
+
execSync.mockImplementation((cmd: string) => {
|
|
126
|
+
if (cmd.includes('oc import-image')) {
|
|
127
|
+
importAttempts++;
|
|
128
|
+
if (importAttempts < 3) throw new Error('409 the object has been modified');
|
|
129
|
+
}
|
|
130
|
+
return Buffer.from('');
|
|
131
|
+
});
|
|
132
|
+
const result = await runExecutor(baseOptions, context());
|
|
133
|
+
expect(result.success).toBe(true);
|
|
134
|
+
expect(importAttempts).toBe(3);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('gives up after importRetries and fails', async () => {
|
|
138
|
+
execSync.mockImplementation((cmd: string) => {
|
|
139
|
+
if (cmd.includes('oc import-image')) throw new Error('409');
|
|
140
|
+
return Buffer.from('');
|
|
141
|
+
});
|
|
142
|
+
const result = await runExecutor({ ...baseOptions, importRetries: 2 }, context());
|
|
143
|
+
expect(result.success).toBe(false);
|
|
144
|
+
const importCalls = commands().filter((c) => c.includes('oc import-image'));
|
|
145
|
+
expect(importCalls.length).toBe(2);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('skipBuild/skipPush reuse the existing image', async () => {
|
|
149
|
+
await runExecutor({ ...baseOptions, skipBuild: true, skipPush: true }, context());
|
|
150
|
+
const cmds = commands();
|
|
151
|
+
expect(cmds.some((c) => c.includes('podman build'))).toBe(false);
|
|
152
|
+
expect(cmds.some((c) => c.includes('nx build test'))).toBe(false);
|
|
153
|
+
expect(cmds.some((c) => c.includes('podman push'))).toBe(false);
|
|
154
|
+
// still imports + rolls out
|
|
155
|
+
expect(cmds.some((c) => c.includes('oc import-image'))).toBe(true);
|
|
156
|
+
expect(cmds.some((c) => c.includes('oc rollout restart'))).toBe(true);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('provisions Postgres and the per-app database before rollout', async () => {
|
|
160
|
+
await runExecutor({ ...baseOptions, database: 'postgres' }, context());
|
|
161
|
+
const cmds = commands();
|
|
162
|
+
expect(cmds.some((c) => c.includes('sandbox-postgres-creds'))).toBe(true);
|
|
163
|
+
expect(cmds.some((c) => c.includes('sandbox-postgres.yml'))).toBe(true);
|
|
164
|
+
expect(cmds.some((c) => c.includes('createdb -U postgres test_sandbox'))).toBe(true);
|
|
165
|
+
const createDbIdx = cmds.findIndex((c) => c.includes('createdb'));
|
|
166
|
+
const rolloutIdx = cmds.findIndex((c) => c.includes('rollout status deployment/test'));
|
|
167
|
+
expect(createDbIdx).toBeGreaterThanOrEqual(0);
|
|
168
|
+
expect(createDbIdx).toBeLessThan(rolloutIdx);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('ensures paired backend Services from proxy-service tags (idempotent)', async () => {
|
|
172
|
+
await runExecutor(
|
|
173
|
+
{ ...baseOptions, appType: 'frontend' },
|
|
174
|
+
context(['adsp:proxy-service:test-service:3333'])
|
|
175
|
+
);
|
|
176
|
+
const guard = commands().find((c) => c.includes('oc get service test-service'));
|
|
177
|
+
expect(guard).toBeTruthy();
|
|
178
|
+
expect(guard).toContain('||');
|
|
179
|
+
expect(guard).toContain('oc create service clusterip test-service --tcp=3333:3333');
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('adds no paired-service guard without proxy-service tags', async () => {
|
|
183
|
+
await runExecutor(baseOptions, context());
|
|
184
|
+
expect(commands().some((c) => c.includes('oc get service'))).toBe(false);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ApplicationType, DatabaseType } from '../../generators/deployment/schema';
|
|
2
|
+
|
|
3
|
+
export interface SandboxExecutorSchema {
|
|
4
|
+
sandboxProject: string;
|
|
5
|
+
registry: string;
|
|
6
|
+
database?: DatabaseType;
|
|
7
|
+
appType?: ApplicationType;
|
|
8
|
+
imageTag?: string;
|
|
9
|
+
skipBuild?: boolean;
|
|
10
|
+
skipPush?: boolean;
|
|
11
|
+
importRetries?: number;
|
|
12
|
+
}
|