diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fffe7021525..a452cb2157c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1137,12 +1137,14 @@ jobs: run: npm install -g ghost-cli@latest # Test against the same tarball that npm-publish ships, not a parallel rebuild. + # Ghost-CLI still requires the package/ prefix; switch this to + # ghost-release-tarball once it accepts a prefix-free archive too. - name: Download npm tarball uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: ghost-npm-tarball - - run: mv ghost-*.tgz ghost.tgz + - run: mv ghost-*-npm.tgz ghost.tgz - name: Verify packaged package.json run: tar -xOf ghost.tgz package/package.json | jq -e '.packageManager' >/dev/null @@ -1192,6 +1194,37 @@ jobs: env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + job_stripe_fixtures: + runs-on: ubuntu-latest + needs: [job_setup] + if: needs.job_setup.outputs.is_tag == 'true' || needs.job_setup.outputs.affected_projects_str != '' + name: Stripe fixture checks + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - 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 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # Asserts the fake Stripe server against responses captured from Stripe, and + # that it refuses the requests Stripe refuses. Needs no Ghost, no Docker and + # no browser, so it does not belong in the e2e matrix that waits on the image. + - name: Check Stripe fixtures + run: pnpm --filter @tryghost/e2e test:fixtures + + - uses: tryghost/actions/actions/slack-build@e7a401946f91165a6426290705f501a377ec1533 # main + if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' + with: + status: ${{ job.status }} + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + job_build_admin: name: Build Admin needs: [job_setup] @@ -1342,11 +1375,24 @@ jobs: # dependsOn would rebuild admin/tsc/assets we already have). run: pnpm --filter ghost run archive + # pack.mjs emits the same tree in two layouts. The prefix-free tarball is + # the release asset; the -npm one carries the package/ prefix npm and + # today's Ghost-CLI need, and goes away with the npm publish in 7.0. + - name: Upload release tarball + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: ghost-release-tarball + path: | + ghost/core/ghost-*.tgz + !ghost/core/ghost-*-npm.tgz + retention-days: 7 + if-no-files-found: error + - name: Upload npm tarball uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: ghost-npm-tarball - path: ghost/core/ghost-*.tgz + path: ghost/core/ghost-*-npm.tgz retention-days: 7 if-no-files-found: error @@ -1773,6 +1819,152 @@ jobs: path: docker-image-e2e.tar.gz retention-days: 1 + # `Inspect image size and layers` above only runs on the artifact path, where + # the image is loaded into the local daemon. On the push path nothing is + # loaded, so size comes from the registry manifest instead — and is compared + # against the image CI built for the base commit, which is what makes a + # dependency's cost visible on the PR that adds it. + # + # Last in the job on purpose: these steps are informational, and job_docker + # gates the e2e lane and the release lane. They never delay the e2e image, and + # on tag runs they never strand a half-published release. + - name: Report image size + if: steps.strategy.outputs.should-push == 'true' + continue-on-error: ${{ startsWith(github.ref, 'refs/tags/v') }} + shell: bash + env: + CORE_IMAGE: ${{ steps.strategy.outputs.image-core-name }} + FULL_IMAGE: ${{ steps.strategy.outputs.image-full-name }} + CORE_TAGS: ${{ steps.meta-core.outputs.tags }} + FULL_TAGS: ${{ steps.meta-full.outputs.tags }} + BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }} + run: | + set -uo pipefail + + # Sum of compressed layer sizes — what a pull actually costs. buildx + # attaches a provenance manifest, so a tag resolves to an index: pick the + # real platform manifest out of it before summing. + layer_bytes() { # + local image="$1" raw digest + raw=$(docker buildx imagetools inspect "${image}:$2" --raw 2>/dev/null) || return 1 + if jq -e 'has("manifests")' <<< "$raw" > /dev/null 2>&1; then + digest=$(jq -r 'first(.manifests[] | select((.platform.architecture // "unknown") != "unknown") | .digest) // empty' <<< "$raw") + [ -n "$digest" ] || return 1 + raw=$(docker buildx imagetools inspect "${image}@${digest}" --raw 2>/dev/null) || return 1 + fi + jq -e '[.layers[].size] | add' <<< "$raw" 2>/dev/null + } + + mib() { awk -v b="$1" 'BEGIN {printf "%.1f MiB", b / 1048576}'; } + delta() { awk -v b="$1" 'BEGIN {printf "%s%.1f MiB", (b < 0 ? "-" : "+"), (b < 0 ? -b : b) / 1048576}'; } + + # Every main build tags `sha-` (metadata-action type=sha), so the + # PR base commit / previous main commit is an exact like-for-like baseline. + # The zero SHA is what a branch's first push reports as `before`. + BASE_TAG="" + case "$BASE_SHA" in + ""|0000000*) ;; + *) BASE_TAG="sha-${BASE_SHA:0:7}" ;; + esac + + { + echo "## Docker image size" + echo "" + echo "Compressed layer totals from the registry manifest." + echo "" + echo "| Image | This build | \`${BASE_TAG:-no baseline}\` | Delta |" + echo "|---|---|---|---|" + } >> "$GITHUB_STEP_SUMMARY" + + report() { #