From 817ec23cf883b465d1bd64f14db28e8f6c9af9a4 Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Fri, 10 Apr 2026 17:19:27 -0500 Subject: [PATCH 1/3] Fixed dependency-inspector to use pnpm instead of yarn (#27342) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://github.com/TryGhost/Ghost/commit/f186b6adeb58412915012ac49c078a5fc96797c2 - Switched `displayAuditSummary()` from `yarn audit` to `pnpm audit` and updated the JSON parsing — pnpm returns a single object with `metadata.vulnerabilities` instead of yarn's newline-delimited JSON - Fixed `parsePnpmOutdatedOutput()` to use `info.wanted` instead of `info.current`, which pnpm's `outdated --json` does not include These were missed during the pnpm migration in #27017. --- .github/scripts/dependency-inspector.js | 55 ++++++++++++------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/.github/scripts/dependency-inspector.js b/.github/scripts/dependency-inspector.js index 27945305047..2a9f9c84b2e 100755 --- a/.github/scripts/dependency-inspector.js +++ b/.github/scripts/dependency-inspector.js @@ -12,7 +12,10 @@ const { execSync } = require('child_process'); * [packageName, current, wanted, latest, dependencyType] tuples. * * pnpm's JSON output is an object keyed by package name: - * { "pkg": { "current": "1.0.0", "wanted": "1.0.1", "latest": "2.0.0", "dependencyType": "dependencies" } } + * { "pkg": { "wanted": "1.0.1", "latest": "2.0.0", "dependencyType": "dependencies" } } + * + * pnpm's JSON output does not include a "current" field — "wanted" + * represents the lockfile-resolved version, so we use it as current. */ function parsePnpmOutdatedOutput(stdout) { if (!stdout || !stdout.trim()) { @@ -22,7 +25,7 @@ function parsePnpmOutdatedOutput(stdout) { const data = JSON.parse(stdout); return Object.entries(data).map(([name, info]) => [ name, - info.current, + info.wanted, info.wanted, info.latest, info.dependencyType @@ -632,7 +635,7 @@ With a severity flag, shows all packages with that update type. } /** - * Run yarn audit and display a vulnerability summary + * Run pnpm audit and display a vulnerability summary */ displayAuditSummary() { console.log('🔒 SECURITY AUDIT:\n'); @@ -640,40 +643,36 @@ With a severity flag, shows all packages with that update type. try { let stdout = ''; try { - stdout = execSync('yarn audit --json 2>/dev/null', { + stdout = execSync('pnpm audit --json', { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }); } catch (error) { - // yarn audit exits with non-zero when vulnerabilities are found + // pnpm audit exits with non-zero when vulnerabilities are found stdout = error.stdout || ''; } - // Find the auditSummary line - const lines = stdout.trim().split('\n'); - for (const line of lines) { - try { - const data = JSON.parse(line); - if (data.type === 'auditSummary' && data.data && data.data.vulnerabilities) { - const v = data.data.vulnerabilities; - const total = v.info + v.low + v.moderate + v.high + v.critical; - console.log(` Total vulnerabilities: ${total}`); - console.log(` 🔴 Critical: ${v.critical}`); - console.log(` 🟠 High: ${v.high}`); - console.log(` 🟡 Moderate: ${v.moderate}`); - console.log(` đŸŸĸ Low: ${v.low}`); - if (v.info > 0) { - console.log(` â„šī¸ Info: ${v.info}`); - } - console.log(` Total dependencies scanned: ${data.data.totalDependencies}\n`); - return; - } - } catch (e) { - // Skip non-JSON lines - } + if (!stdout || !stdout.trim()) { + console.log(' âš ī¸ Could not parse audit summary\n'); + return; } - console.log(' âš ī¸ Could not parse audit summary\n'); + const data = JSON.parse(stdout); + if (data.metadata && data.metadata.vulnerabilities) { + const v = data.metadata.vulnerabilities; + const total = v.info + v.low + v.moderate + v.high + v.critical; + console.log(` Total vulnerabilities: ${total}`); + console.log(` 🔴 Critical: ${v.critical}`); + console.log(` 🟠 High: ${v.high}`); + console.log(` 🟡 Moderate: ${v.moderate}`); + console.log(` đŸŸĸ Low: ${v.low}`); + if (v.info > 0) { + console.log(` â„šī¸ Info: ${v.info}`); + } + console.log(` Total dependencies scanned: ${data.metadata.totalDependencies}\n`); + } else { + console.log(' âš ī¸ Could not parse audit summary\n'); + } } catch (error) { console.log(` âš ī¸ Audit failed: ${error.message}\n`); } From 32d423087ba4a2375da81888e105f7cb453329ce Mon Sep 17 00:00:00 2001 From: Steve Larson <9larsons@gmail.com> Date: Fri, 10 Apr 2026 17:28:55 -0500 Subject: [PATCH 2/3] Skipped Tinybird tests job on forks (#27344) no ref This task requires a secret that's not available on forks, and we should skip it like we do other jobs. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84d121f9ef2..72967317d19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -754,7 +754,7 @@ jobs: - name: Test project run: tb test run - name: Trigger and watch traffic analytics infra Tinybird workflow - if: github.repository == 'TryGhost/Ghost' + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository env: GH_TOKEN: ${{ secrets.TRAFFIC_ANALYTICS_GITHUB_TOKEN }} uses: ./.github/actions/dispatch-workflow From 8eb0a3e7b755908535bd107156ed3c9abd39b50a Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Fri, 10 Apr 2026 18:53:32 -0400 Subject: [PATCH 3/3] Improve performance of core acceptance/legacy CI jobs (#27341) no issue This removes the explicit TS package build step in favor of nx automatic build triggers. Because of the way this command was invoked, all of the frontend packages were being built on every CI run of acceptance/legacy tests, which adds about 3-4 minutes per job. The only package ghost/core depends on is parse-email-address, and so by leveraging Nx's automatic dependent target running, we can ensure that only the necessary packages are built. Leveraging Nx now also means that if more lecal packages with build targets are added as ghost/core deps in the future, the jobs will continue to work as designed. This is a repeat of #27293 which was partially reverted during the pnpm cutover) --- .github/workflows/ci.yml | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72967317d19..a505a787aa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -452,9 +452,6 @@ jobs: env: DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} - - name: Build TS packages - run: pnpm nx run-many -t build --exclude=ghost-admin - - name: Set timezone (non-UTC) uses: szenius/set-timezone@1f9716b0f7120e344f0c62bb7b1ee98819aefd42 # v2.0 with: @@ -472,12 +469,10 @@ jobs: echo "database__connection__password=root" >> $GITHUB_ENV - name: E2E tests - working-directory: ghost/core - run: pnpm test:ci:e2e + run: pnpm nx run ghost:test:ci:e2e - name: Integration tests - working-directory: ghost/core - run: pnpm test:ci:integration + run: pnpm nx run ghost:test:ci:integration - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 if: matrix.node == env.NODE_VERSION && contains(matrix.env.DB, 'mysql') @@ -543,9 +538,6 @@ jobs: env: DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} - - name: Build TS packages - run: pnpm nx run-many -t build --exclude=ghost-admin - - name: Set env vars (SQLite) if: contains(matrix.env.DB, 'sqlite') run: echo "database__connection__filename=/dev/shm/ghost-test.db" >> $GITHUB_ENV @@ -558,8 +550,7 @@ jobs: echo "database__connection__password=root" >> $GITHUB_ENV - name: Legacy tests - working-directory: ghost/core - run: pnpm test:ci:legacy + run: pnpm nx run ghost:test:ci:legacy - uses: tryghost/actions/actions/slack-build@0cbdcbeb9030f46b109d5e6e44c14933026d8ca5 # main if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main'