fix(ci): isolate staging Actions settings under STG prefix
Map staging Secrets and Variables exclusively from STG names, validate explicit API and SSO settings, and pass them to the staging Worker. Preserve production settings and document the variable registration and Secret migration. Validation: source checks and 113 tests pass. Gitea STG Variables were created and verified; Secret registration remains with the user.
This commit is contained in:
1 parent
319a8afaab
commit
0d9b1fd703
5 files changed
+176
-36
No files matched your search
@@ -0,0 +1,72 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mkdtempSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { stagingSettings } from '../scripts/staging-settings.mjs';
|
||||
|
||||
const fixture = () => ({
|
||||
AUTH_CLIENT_ID: 'staging-client', AUTH_ISSUER: 'https://identity.staging.example/oidc/',
|
||||
ABC_API_BASE_URL: 'https://feedback.staging.example/',
|
||||
ABC_PROJECT_ID: '01900000-0000-7000-8000-000000000001',
|
||||
ABC_CHANNEL_ID: '01900000-0000-7000-8000-000000000002',
|
||||
ORG_CONTEXT_ENABLED: 'false',
|
||||
PROD_AUTH_CLIENT_ID: 'production-client', PROD_ABC_API_BASE_URL: 'https://production.example'
|
||||
});
|
||||
|
||||
test('staging uses its mapped API and SSO settings independently of production values', () => {
|
||||
const settings = stagingSettings(fixture());
|
||||
assert.equal(settings.vars.AUTH_CLIENT_ID, 'staging-client');
|
||||
assert.equal(settings.vars.AUTH_AUTHORIZE_URL, 'https://identity.staging.example/oidc/oauth2/auth');
|
||||
assert.equal(settings.vars.AUTH_TOKEN_URL, 'https://identity.staging.example/oidc/oauth2/token');
|
||||
assert.equal(settings.vars.AUTH_USERINFO_URL, 'https://identity.staging.example/oidc/userinfo');
|
||||
assert.equal(settings.vars.ABC_API_BASE_URL, 'https://feedback.staging.example');
|
||||
assert.equal(settings.vars.PROD_AUTH_CLIENT_ID, undefined);
|
||||
});
|
||||
|
||||
test('missing staging settings cannot silently fall back to a different environment', () => {
|
||||
for (const name of ['AUTH_CLIENT_ID', 'AUTH_ISSUER', 'ABC_API_BASE_URL', 'ABC_PROJECT_ID', 'ABC_CHANNEL_ID', 'ORG_CONTEXT_ENABLED']) {
|
||||
const env = fixture();
|
||||
delete env[name];
|
||||
assert.throws(() => stagingSettings(env), new RegExp('STG_' + name));
|
||||
}
|
||||
assert.throws(() => stagingSettings({ ...fixture(), ABC_CHANNEL_ID: 'not-a-uuid' }), /STG_ABC_CHANNEL_ID/);
|
||||
assert.throws(() => stagingSettings({ ...fixture(), AUTH_ISSUER: 'http://identity.example' }), /STG_AUTH_ISSUER/);
|
||||
assert.throws(() => stagingSettings({ ...fixture(), ABC_API_BASE_URL: 'https://api.example/api' }), /STG_ABC_API_BASE_URL/);
|
||||
});
|
||||
|
||||
test('staging organization credentials remain separate from public Worker variables', () => {
|
||||
const settings = stagingSettings({ ...fixture(), ORG_CONTEXT_ENABLED: 'true',
|
||||
ORG_CONTEXT_TENANT_IDS: '["038326b6-954a-48a7-a85f-efd83f62b82a"]',
|
||||
ORG_CONTEXT_KEY_ID: 'fixture-key', ORG_CONTEXT_KEY_SECRET: 'fixture-secret'
|
||||
});
|
||||
assert.equal(settings.secrets.ORG_CONTEXT_KEY_SECRET, 'fixture-secret');
|
||||
assert.equal(settings.vars.ORG_CONTEXT_KEY_SECRET, undefined);
|
||||
});
|
||||
|
||||
test('staging validation never deploys and dry-run pins the staging Worker and config', () => {
|
||||
const directory = mkdtempSync(join(tmpdir(), 'qa-staging-test-'));
|
||||
const capture = join(directory, 'commands.jsonl');
|
||||
writeFileSync(join(directory, 'npx'), `#!/usr/bin/env node
|
||||
require('node:fs').appendFileSync(process.env.QA_TEST_CAPTURE, JSON.stringify(process.argv.slice(2)) + '\\n');
|
||||
`, { mode: 0o700 });
|
||||
const script = fileURLToPath(new URL('../scripts/deploy-worker.mjs', import.meta.url));
|
||||
const env = { ...process.env, ...fixture(), PATH: directory + ':' + process.env.PATH, QA_TEST_CAPTURE: capture };
|
||||
try {
|
||||
const validation = spawnSync(process.execPath, [script, '--validate-only'], { env, encoding: 'utf8' });
|
||||
assert.equal(validation.status, 0, validation.stderr);
|
||||
assert.equal(existsSync(capture), false);
|
||||
const dryRun = spawnSync(process.execPath, [script, '--dry-run'], { env, encoding: 'utf8' });
|
||||
assert.equal(dryRun.status, 0, dryRun.stderr);
|
||||
const commands = readFileSync(capture, 'utf8').trim().split('\n').map(JSON.parse);
|
||||
assert.equal(commands.length, 1);
|
||||
const args = commands[0];
|
||||
assert.ok(args.includes('--dry-run'));
|
||||
assert.ok(args.includes('baron-qa-gateway-test'));
|
||||
assert.ok(args.includes('wrangler.toml'));
|
||||
assert.ok(args.includes('ABC_API_BASE_URL:https://feedback.staging.example'));
|
||||
assert.ok(!args.some(value => value.includes('production.example') || value === 'secret'));
|
||||
} finally { rmSync(directory, { recursive: true, force: true }); }
|
||||
});
|
||||
Reference in new issue
Block a user