Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ jobs:
# itself per-app — instead we emit the matrix here and it skips when
# the result is `[]`. The publish job's own is_main / event gates keep
# tags and PRs from publishing regardless of what this lists.
PUBLISH_PUBLIC_APPS_MATRIX=$(node scripts/build-public-apps-matrix.cjs "$AFFECTED_PROJECTS")
PUBLISH_PUBLIC_APPS_MATRIX=$(node scripts/build-public-apps-matrix.js "$AFFECTED_PROJECTS")
echo "publish_public_apps_matrix=$PUBLISH_PUBLIC_APPS_MATRIX" >> "$GITHUB_OUTPUT"

outputs:
Expand Down Expand Up @@ -290,11 +290,24 @@ jobs:
- name: Fetch main branch
run: git fetch --no-tags origin main

- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
env:
FORCE_COLOR: 0
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

# The script's only runtime dep is semver, so install @internal/scripts
# alone rather than the whole workspace — ~0.5s and a single package.
- name: Install scripts dependencies
run: pnpm install --frozen-lockfile --filter @internal/scripts --prod --ignore-scripts

- name: Check app version bump
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_COMPARE_SHA: ${{ github.event.pull_request.head.sha }}
run: node scripts/check-app-version-bump.cjs
run: node scripts/check-app-version-bump.js

job_migration_integrity_check:
name: Check migration integrity
Expand Down Expand Up @@ -2127,7 +2140,7 @@ jobs:
run: |
PREV_TAG="${{ steps.prev_tag.outputs.tag }}"
if [ -n "$PREV_TAG" ]; then
node scripts/lib/release-notes.cjs "$PREV_TAG" "${GITHUB_REF_NAME}" > /tmp/release-notes.md
node scripts/lib/release-notes.js "$PREV_TAG" "${GITHUB_REF_NAME}" > /tmp/release-notes.md
else
echo "This release contains fixes for minor bugs and issues reported by Ghost users." > /tmp/release-notes.md
fi
Expand Down
2 changes: 1 addition & 1 deletion apps/admin-toolbar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test": "pnpm test:unit",
"test:unit": "pnpm run build && vitest run",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/announcement-bar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"test:unit": "pnpm test:ci",
"lint": "eslint src test --cache",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"browserslist": {
Expand Down
2 changes: 1 addition & 1 deletion apps/comments-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"lint:code": "eslint src --cache",
"lint:types": "pnpm test:types",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"browserslist": {
Expand Down
2 changes: 1 addition & 1 deletion apps/portal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"lint:types": "tsc --noEmit",
"lint": "pnpm run '/^lint:/'",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"browserslist": {
Expand Down
2 changes: 1 addition & 1 deletion apps/signup-form/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/sodo-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"test": "vitest run",
"lint": "eslint src test --cache",
"preship": "pnpm lint",
"ship": "node ../../scripts/release-apps.cjs",
"ship": "node ../../scripts/release-apps.js",
"prepublishOnly": "pnpm build"
},
"browserslist": {
Expand Down
9 changes: 6 additions & 3 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ Write it as ESM (`.js` — the package is `type: module`) and parse arguments wi
`semver` is already there. Keep it cheap: everything here lands in every dev's
`pnpm install`, so prefer `node:` built-ins.

Put testable logic in `lib/` and cover it in `test/` — tests are plain
`node --test`, discovered automatically. Note this package is *not* part of the
root Vitest watcher (`pnpm test:watch`), which only covers Vitest-based projects.
Put logic shared by several scripts in `lib/`. A script that is its own
entrypoint can just export the parts worth testing and guard the CLI path with
`import.meta.main` — see `build-public-apps-matrix.js`. Either way, cover it in
`test/`: tests are plain `node --test`, discovered automatically. Note this
package is *not* part of the root Vitest watcher (`pnpm test:watch`), which only
covers Vitest-based projects.

## The `.cjs` files

Expand Down
60 changes: 0 additions & 60 deletions scripts/build-public-apps-matrix.cjs

This file was deleted.

98 changes: 98 additions & 0 deletions scripts/build-public-apps-matrix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Builds the publish matrix for public UMD apps, filtered to the apps affected
// in the current run. The output (stdout, compact JSON array) feeds the
// publish_public_apps job's `strategy.matrix.include` via fromJSON.
//
// Matrix context isn't available in a job-level `if:`, so the affected gate
// can't live on the publish job — instead we compute the set here in job_setup
// (mirroring the affected_playwright_projects dynamic matrix) and the job skips
// itself when the result is `[]`.
//
// public-apps.json says *which* apps are public (apps/ also holds admin, shade
// and friends, which are not) and maps each to its defaults.json key. The URLs
// to purge come from defaults.json itself — see cdnPathsFor.

import {PUBLIC_APPS, readDefaults} from './lib/public-apps.js';

const DEFAULTS = await readDefaults();

// defaults.json pins each app to a major.minor and interpolates it into the
// URLs as {version}. The publish job knows the version it just released, so we
// hand back CURRENT_MINOR for it to substitute.
const VERSION_PLACEHOLDER = '{version}';

/**
* The jsDelivr URLs to purge for one app, read off its defaults.json entry.
*
* defaults.json is what Ghost core resolves at render time, so it is by
* definition the set of assets visitors fetch. Deriving from it means the purge
* list cannot silently fall behind: an app that starts shipping a stylesheet
* has to be declared here or the feature breaks visibly. Every string field
* holding a URL counts — `url` for all of them, plus `styles` for sodo-search.
*
* @param {Record<string, string>} configEntry - a defaults.json app entry
* @returns {string[]}
*/
export function cdnPathsFor(configEntry) {
return Object.values(configEntry)
.filter(value => typeof value === 'string' && value.startsWith('https://'))
.map(url => url.replaceAll(VERSION_PLACEHOLDER, 'CURRENT_MINOR'));
}

/**
* @param {string[]} affectedProjects - nx project names affected in this run
* @returns {Array<{package_name: string, package_path: string, cdn_paths: string}>}
* matrix entries with cdn_paths flattened to the newline-delimited string the
* publish job's purge step expects.
*/
export function buildMatrix(affectedProjects) {
const affected = new Set(affectedProjects);
return PUBLIC_APPS
.filter(app => affected.has(app.packageName))
.map((app) => {
const configEntry = DEFAULTS[app.configKey];

// Both of these would otherwise degrade into an empty purge list,
// which reads as success and leaves jsDelivr serving the old bundle.
if (!configEntry) {
throw new Error(`public-apps.json maps ${app.packageName} to configKey "${app.configKey}", which defaults.json does not define`);
}

const cdnPaths = cdnPathsFor(configEntry);
if (!cdnPaths.length) {
throw new Error(`defaults.json entry "${app.configKey}" (${app.packageName}) has no CDN URLs to purge`);
}

return {
package_name: app.packageName,
package_path: app.path,
cdn_paths: cdnPaths.join('\n')
};
});
}

function main() {
const raw = process.argv[2] || '[]';

let affectedProjects;
try {
affectedProjects = JSON.parse(raw);
} catch (error) {
throw new Error(`Invalid affected-projects JSON: ${error.message}`);
}

if (!Array.isArray(affectedProjects)) {
throw new Error('affected-projects argument must be a JSON array');
}

// Stdout is the contract — the workflow captures this into a job output.
process.stdout.write(JSON.stringify(buildMatrix(affectedProjects)));
}

if (import.meta.main) {
try {
main();
} catch (error) {
console.error(error.message);
process.exit(1);
}
}
Loading
Loading