mirror of
https://github.com/nocobase/nocobase.git
synced 2026-10-16 15:07:08 +08:00
* chore: move NocoBase Studio back to its private repository Remove packages/apps/studio and packages/tools/studio-cli, which are now maintained in nocobase/studio, together with everything in the monorepo that only existed for them: the fixed changeset group and their pending and prerelease changeset entries, the Studio release image workflow and its call from the beta release, the Studio test shards of the release workflows, the Studio OpenAPI and database-matrix targets, and the script tests covering the image and the CLI brand. * docs(app-installer): describe --app with a generic published application Replace the NocoBase Studio examples in the installer's help, flag description, usage error and README, and in the deployment Skill, with a generic scoped package, and use a neutral fixture name in its tests. * chore: rerun checks on the updated CI
205 lines
6.8 KiB
JavaScript
205 lines
6.8 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import test from 'node:test';
|
|
|
|
const workflow = (name) =>
|
|
readFileSync(
|
|
new URL(`../../.github/workflows/v3-${name}.yml`, import.meta.url),
|
|
'utf8',
|
|
);
|
|
|
|
const triggerBranches = (source, event) => {
|
|
const match = source.match(
|
|
new RegExp(`^ ${event}:\\n branches:\\n((?: - .+\\n)+)`, 'mu'),
|
|
);
|
|
assert.ok(match, `${event} must declare branch filters`);
|
|
return match[1]
|
|
.trimEnd()
|
|
.split('\n')
|
|
.map((line) => line.replace(/^ - /u, '').replace(/^['"]|['"]$/gu, ''));
|
|
};
|
|
|
|
const matchesSingleSegmentGlob = (pattern, branch) => {
|
|
const expression = pattern
|
|
.split('*')
|
|
.map((part) => part.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&'))
|
|
.join('[^/]*');
|
|
return new RegExp(`^${expression}$`, 'u').test(branch);
|
|
};
|
|
|
|
test('stacked v3 targets receive PR checks without widening direct-branch triggers', () => {
|
|
const changesets = workflow('changeset-check');
|
|
const guard = workflow('guard-main');
|
|
|
|
assert.deepEqual(triggerBranches(changesets, 'pull_request'), [
|
|
'v3-main',
|
|
'v3-develop',
|
|
'*/v3-*',
|
|
]);
|
|
assert.deepEqual(triggerBranches(guard, 'pull_request'), ['v3-main']);
|
|
|
|
const workflowPattern = '*/v3-*';
|
|
const botPattern = /^[^/]+\/v3-[^/]+$/u;
|
|
for (const branch of [
|
|
'feat/v3-app-installer',
|
|
'fix/v3-stacked-pr-routing',
|
|
'docs/v3-release-notes',
|
|
'feat/app-installer',
|
|
'v3-develop',
|
|
'agent/PM-42',
|
|
'feat/v3-routing/extra',
|
|
]) {
|
|
assert.equal(
|
|
matchesSingleSegmentGlob(workflowPattern, branch),
|
|
botPattern.test(branch),
|
|
branch,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('OSS releases write only the migrated repository and v3 branches', () => {
|
|
for (const name of [
|
|
'release-beta',
|
|
'release-stable',
|
|
'merge-beta-to-stable',
|
|
]) {
|
|
const source = workflow(name);
|
|
assert.match(source, /repositories: nocobase\n/u);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/repositories: nocobase3\n|github\.com\/nocobase\/nocobase3\.git/u,
|
|
);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/--base (?:main|develop)\b|origin\/(?:main|develop)\b|git push origin (?:main|develop)\b/u,
|
|
);
|
|
assert.match(source, /RELEASE_RESULT_FEISHU_WEBHOOK_URL/u);
|
|
assert.doesNotMatch(source, /gh pr merge[^\n]*--merge/u);
|
|
assert.match(source, /gh pr merge[^\n]*--squash/u);
|
|
}
|
|
});
|
|
|
|
test('OSS releases publish through public npm with the existing token', () => {
|
|
for (const name of ['release-beta', 'release-stable']) {
|
|
const source = workflow(name);
|
|
assert.match(source, /registry-url: https:\/\/registry\.npmjs\.org/u);
|
|
assert.match(source, /NODE_AUTH_TOKEN: \$\{\{ secrets\.NPM_TOKEN \}\}/u);
|
|
assert.doesNotMatch(source, /npm\.nocobase\.ai|PRO_NPM_TOKEN/u);
|
|
}
|
|
|
|
const promotion = workflow('merge-beta-to-stable');
|
|
assert.match(promotion, /REGISTRY_URL: https:\/\/registry\.npmjs\.org/u);
|
|
assert.doesNotMatch(promotion, /npm\.nocobase\.ai/u);
|
|
});
|
|
|
|
test('the release smoke registry keeps scope isolation over public npm', () => {
|
|
const source = readFileSync(
|
|
new URL('../../.github/verdaccio/config.yaml', import.meta.url),
|
|
'utf8',
|
|
);
|
|
assert.match(source, /nocobase:\n url: https:\/\/registry\.npmjs\.org\//u);
|
|
assert.match(source, /'@nocobase\/\*':[\s\S]*?proxy: nocobase/u);
|
|
assert.doesNotMatch(source, /npm\.nocobase\.ai/u);
|
|
});
|
|
|
|
test('v3 GitHub Releases never take the shared Latest marker', () => {
|
|
const source = workflow('github-release');
|
|
assert.match(
|
|
source,
|
|
/release-beta\/\*\) FLAGS=\(--prerelease --latest=false\)/u,
|
|
);
|
|
assert.match(source, /release\/\*\)\s+FLAGS=\(--latest=false\)/u);
|
|
});
|
|
|
|
test('site deployment uses the existing publish triggers without a migration gate', () => {
|
|
for (const name of ['docs', 'ui-library']) {
|
|
const source = workflow(name);
|
|
assert.doesNotMatch(source, /V3_ASSET_DEPLOY_ENABLED/u);
|
|
assert.match(
|
|
source,
|
|
/github\.event_name == 'push' && github\.ref == 'refs\/heads\/v3-develop'/u,
|
|
);
|
|
assert.match(
|
|
source,
|
|
/github\.event_name == 'workflow_dispatch' && inputs\.publish/u,
|
|
);
|
|
assert.deepEqual(triggerBranches(source, 'pull_request'), [
|
|
'v3-develop',
|
|
'v3-main',
|
|
'*/v3-*',
|
|
]);
|
|
assert.deepEqual(triggerBranches(source, 'push'), ['v3-develop']);
|
|
assert.match(source, /publish:[\s\S]*?default: false/u);
|
|
}
|
|
const docs = workflow('docs');
|
|
for (const name of ['OSS_BUCKET', 'OSS_REGION', 'CDN_DOMAIN']) {
|
|
assert.ok(docs.includes(`secrets.V3_DOCS_ALI_${name}`));
|
|
assert.ok(!docs.includes(`secrets.DOCS_ALI_${name}`));
|
|
}
|
|
for (const name of ['docs', 'ui-library']) {
|
|
for (const key of ['ID', 'SECRET']) {
|
|
assert.ok(
|
|
workflow(name).includes(`secrets.DOCS_ALI_OSS_ACCESS_KEY_${key}`),
|
|
);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('independent Pro releases keep their own repository and branches', () => {
|
|
for (const name of [
|
|
'pro-release-beta',
|
|
'pro-release-stable',
|
|
'pro-promote-to-stable',
|
|
]) {
|
|
const source = workflow(name);
|
|
assert.match(source, /repositories: nocobase3-pro/u);
|
|
assert.doesNotMatch(source, /--base v3-|git push origin v3-/u);
|
|
assert.match(source, /secrets\.PRO_NPM_REGISTRY/u);
|
|
assert.match(source, /Commercial packages must not use public npm/u);
|
|
assert.doesNotMatch(source, /npm\.nocobase\.ai/u);
|
|
}
|
|
});
|
|
|
|
test('installer smoke covers the Default archive', () => {
|
|
const smoke = workflow('app-installer-smoke');
|
|
assert.match(
|
|
smoke,
|
|
/pnpm --filter @nocobase\/app-template-default build --tar/u,
|
|
);
|
|
assert.match(
|
|
smoke,
|
|
/smoke-app-installer\.mjs --archive packages\/templates\/app-template-default\/storage\/exports\/dist\.tar\.gz/u,
|
|
);
|
|
assert.doesNotMatch(smoke, /app-template-hub|--source|schedule:/u);
|
|
for (const name of ['release-beta', 'release-stable']) {
|
|
assert.match(workflow(name), /template: \[default, examples\]/u);
|
|
assert.doesNotMatch(workflow(name), /template: \[[^\n]*\bhub\b/u);
|
|
}
|
|
});
|
|
|
|
test('Pro publishing passes the private registry and token independently', () => {
|
|
for (const name of ['pro-release-beta', 'pro-release-stable']) {
|
|
const source = workflow(name);
|
|
assert.match(
|
|
source,
|
|
/registry-url: \$\{\{ secrets\.PRO_NPM_REGISTRY \}\}/u,
|
|
);
|
|
assert.match(source, /--registry "\$PRO_NPM_REGISTRY"/u);
|
|
assert.match(
|
|
source,
|
|
/NODE_AUTH_TOKEN: \$\{\{ secrets\.PRO_NPM_TOKEN \}\}/u,
|
|
);
|
|
assert.doesNotMatch(
|
|
source,
|
|
/registry-url: https:\/\/registry\.npmjs\.org|--registry https:\/\/registry\.npmjs\.org/u,
|
|
);
|
|
}
|
|
});
|
|
|
|
test('Pro workflows retain the branch arguments supported by their source-owned sync script', () => {
|
|
assert.match(workflow('pro-release-beta'), /ARGS=\(--branch develop\)/u);
|
|
for (const name of ['pro-release-stable', 'pro-promote-to-stable']) {
|
|
assert.match(workflow(name), /ARGS=\(--branch main\)/u);
|
|
}
|
|
});
|