Added missing Polish translations #30489
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, labeled, unlabeled] | |
| push: | |
| # Ref: GHA Filter pattern syntax: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#filter-pattern-cheat-sheet | |
| # Run on pushes to main, release branches, and previous/future major version branches | |
| branches: | |
| - main | |
| - 'v[0-9]+.*' # Matches any release branch, e.g. v6.0.3, v12.1.0 | |
| - '[0-9]+.x' # Matches any major version branch, e.g. 5.x, 23.x | |
| env: | |
| FORCE_COLOR: 1 | |
| HEAD_COMMIT: ${{ github.sha }} | |
| CACHED_DEPENDENCY_PATHS: | | |
| ${{ github.workspace }}/node_modules | |
| ${{ github.workspace }}/apps/*/node_modules | |
| ${{ github.workspace }}/ghost/*/node_modules | |
| ${{ github.workspace }}/e2e/node_modules | |
| ~/.cache/ms-playwright/ | |
| NX_REJECT_UNKNOWN_LOCAL_CACHE: 0 | |
| NODE_VERSION: 22.18.0 | |
| concurrency: | |
| group: ${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| job_setup: | |
| name: Setup | |
| runs-on: ubuntu-latest | |
| # Having certain parts of our huge CI workflow run on a specific label is an anti-pattern. | |
| # Long term, we want to get away from this by having all of our tests run on every PR, and have the staging deploy either be automatic or a different workflow | |
| # This logic skips the entire workflow if triggered by a label that doesn't contain "test" or "deploy" - there's nothing new to do in that case | |
| if: | | |
| github.event_name != 'pull_request' || | |
| (github.event.action == 'labeled' && (contains(github.event.label.name, 'test') || contains(github.event.label.name, 'deploy'))) || | |
| (github.event.action == 'unlabeled' && (contains(github.event.label.name, 'test') || contains(github.event.label.name, 'deploy'))) || | |
| (github.event.action != 'labeled' && github.event.action != 'unlabeled') | |
| timeout-minutes: 15 | |
| env: | |
| IS_MAIN: ${{ github.ref == 'refs/heads/main' }} | |
| IS_DEVELOPMENT: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/5.x' || github.ref == 'refs/heads/6.x' }} | |
| IS_SIX: ${{ github.ref == 'refs/heads/6.x' }} | |
| IS_SIX_PR: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref == '6.x' }} | |
| permissions: | |
| pull-requests: read | |
| steps: | |
| - name: Checkout current commit | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ env.HEAD_COMMIT }} | |
| fetch-depth: 2 | |
| - name: Output GitHub context | |
| if: env.RUNNER_DEBUG == '1' | |
| run: | | |
| echo "GITHUB_EVENT_NAME: ${{ github.event_name }}" | |
| echo "GITHUB_CONTEXT: ${{ toJson(github.event) }}" | |
| - name: Get metadata (push) | |
| if: github.event_name == 'push' | |
| run: | | |
| NUMBER_OF_COMMITS=$(printf "%s\n" '${{ toJson(github.event.commits.*.id) }}' | jq length) | |
| echo "There are $NUMBER_OF_COMMITS commits in this push." | |
| echo "BASE_COMMIT=$(git rev-parse HEAD~$NUMBER_OF_COMMITS)" >> $GITHUB_ENV | |
| - name: Get metadata (pull_request) | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| BASE_COMMIT=$(curl --location --request GET 'https://api.github.com/repos/TryGhost/Ghost/pulls/${{ github.event.pull_request.number }}' --header 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' | jq -r .base.sha) | |
| echo "Setting BASE_COMMIT to $BASE_COMMIT" | |
| echo "BASE_COMMIT=$BASE_COMMIT" >> $GITHUB_ENV | |
| - name: Check user org membership | |
| id: check_user_org_membership | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| echo "Looking up: ${{ github.triggering_actor }}" | |
| ENCODED_USERNAME=$(printf '%s' '${{ github.triggering_actor }}' | jq -sRr @uri) | |
| LOOKUP_USER=$(curl --write-out "%{http_code}" --silent --output /dev/null --location "https://api.github.com/orgs/tryghost/members/$ENCODED_USERNAME" --header "Authorization: Bearer ${{ secrets.CANARY_DOCKER_BUILD }}") | |
| if [ "$LOOKUP_USER" == "204" ]; then | |
| echo "User is in the org" | |
| echo "is_member=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "User is not in the org" | |
| echo "is_member=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Determine added packages | |
| uses: dorny/[email protected] | |
| id: added | |
| with: | |
| filters: | | |
| new-package: | |
| - added: 'ghost/**/package.json' | |
| - name: Determine changed packages | |
| uses: AurorNZ/[email protected] | |
| id: changed | |
| with: | |
| filters: | | |
| shared: &shared | |
| - '.github/**' | |
| - 'package.json' | |
| - 'yarn.lock' | |
| core: | |
| - *shared | |
| - 'ghost/**' | |
| - '!ghost/admin/**' | |
| - '!ghost/core/core/server/data/tinybird/**' | |
| admin: | |
| - *shared | |
| - 'ghost/admin/**' | |
| admin-x-settings: | |
| - *shared | |
| - 'apps/admin-x-settings/**' | |
| - 'apps/admin-x-design-system/**' | |
| - 'apps/admin-x-framework/**' | |
| - 'apps/shade/**' | |
| activitypub: | |
| - *shared | |
| - 'apps/shade/**' | |
| - 'apps/admin-x-framework/**' | |
| - 'apps/activitypub/**' | |
| announcement-bar: | |
| - *shared | |
| - 'apps/announcement-bar/**' | |
| comments-ui: | |
| - *shared | |
| - 'apps/comments-ui/**' | |
| portal: | |
| - *shared | |
| - 'apps/portal/**' | |
| signup-form: | |
| - *shared | |
| - 'apps/signup-form/**' | |
| sodo-search: | |
| - *shared | |
| - 'apps/sodo-search/**' | |
| tinybird: | |
| - 'ghost/core/core/server/data/tinybird/**' | |
| - '!ghost/core/core/server/data/tinybird/**/*.md' | |
| any-code: | |
| - '!**/*.md' | |
| - name: Compute lockfile hash | |
| run: echo "hash=lockfile-${{ hashFiles('yarn.lock') }}" >> "$GITHUB_ENV" | |
| - name: Compute dependency cache key | |
| run: echo "cachekey=dep-cache-${{ hashFiles('yarn.lock') }}-${{ env.HEAD_COMMIT }}" >> "$GITHUB_ENV" | |
| - name: Compute dependency cache restore key | |
| run: | | |
| EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | |
| echo "DEPENDENCY_CACHE_RESTORE_KEYS<<$EOF" >> "$GITHUB_ENV" | |
| echo "dep-cache-${{ env.hash }}-${{ github.sha }}" >> "$GITHUB_ENV" | |
| echo "dep-cache-${{ env.hash }}" >> "$GITHUB_ENV" | |
| echo "dep-cache" >> "$GITHUB_ENV" | |
| echo "$EOF" >> "$GITHUB_ENV" | |
| - name: Nx cache | |
| uses: actions/cache@v4 | |
| id: cache_nx | |
| with: | |
| path: .nxcache | |
| key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT }} | |
| restore-keys: | | |
| nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT }} | |
| nx-Linux-${{ github.ref }} | |
| nx-Linux | |
| - name: Check dependency cache | |
| uses: actions/cache@v4 | |
| id: cache_dependencies | |
| with: | |
| path: ${{ env.CACHED_DEPENDENCY_PATHS }} | |
| key: ${{ env.cachekey }} | |
| restore-keys: ${{ env.IS_DEVELOPMENT == 'false' && env.DEPENDENCY_CACHE_RESTORE_KEYS || 'dep-never-restore'}} | |
| - name: Define Node test matrix | |
| id: node_matrix | |
| run: | | |
| echo 'matrix=["22.18.0"]' >> $GITHUB_OUTPUT | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install dependencies | |
| run: bash .github/scripts/install-deps.sh | |
| outputs: | |
| changed_admin: ${{ steps.changed.outputs.admin }} | |
| changed_core: ${{ steps.changed.outputs.core }} | |
| changed_admin_x_settings: ${{ steps.changed.outputs.admin-x-settings }} | |
| changed_activitypub: ${{ steps.changed.outputs.activitypub }} | |
| changed_announcement_bar: ${{ steps.changed.outputs.announcement-bar }} | |
| changed_comments_ui: ${{ steps.changed.outputs.comments-ui }} | |
| changed_portal: ${{ steps.changed.outputs.portal }} | |
| changed_signup_form: ${{ steps.changed.outputs.signup-form }} | |
| changed_sodo_search: ${{ steps.changed.outputs.sodo-search }} | |
| changed_tinybird: ${{ steps.changed.outputs.tinybird }} | |
| changed_any_code: ${{ steps.changed.outputs.any-code }} | |
| changed_new_package: ${{ steps.added.outputs.new-package }} | |
| base_commit: ${{ env.BASE_COMMIT }} | |
| is_main: ${{ env.IS_MAIN }} | |
| is_development: ${{ env.IS_DEVELOPMENT }} | |
| is_six: ${{ env.IS_SIX }} | |
| is_six_pr: ${{ env.IS_SIX_PR }} | |
| member_is_in_org: ${{ steps.check_user_org_membership.outputs.is_member }} | |
| has_browser_tests_label: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'browser-tests') }} | |
| dependency_cache_key: ${{ env.cachekey }} | |
| node_version: ${{ env.NODE_VERSION }} | |
| node_test_matrix: ${{ steps.node_matrix.outputs.matrix }} | |
| job_lint: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_any_code == 'true' | |
| name: Lint | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1000 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - uses: actions/cache@v4 | |
| with: | |
| path: ghost/**/.eslintcache | |
| key: eslint-cache | |
| - run: yarn nx affected -t lint --base=${{ needs.job_setup.outputs.BASE_COMMIT }} | |
| - uses: tryghost/actions/actions/slack-build@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_i18n: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| name: i18n | |
| if: | | |
| needs.job_setup.outputs.changed_comments_ui == 'true' | |
| || needs.job_setup.outputs.changed_signup_form == 'true' | |
| || needs.job_setup.outputs.changed_sodo_search == 'true' | |
| || needs.job_setup.outputs.changed_portal == 'true' | |
| || needs.job_setup.outputs.changed_core == 'true' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Run i18n tests | |
| run: yarn nx run @tryghost/i18n:test | |
| job_admin-tests: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_admin == 'true' | |
| name: Admin tests - Chrome | |
| env: | |
| MOZ_HEADLESS: 1 | |
| JOBS: 1 | |
| CI: true | |
| COVERAGE: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - run: yarn nx run ghost-admin:test | |
| env: | |
| BROWSER: Chrome | |
| # Merge coverage reports and upload | |
| - name: Merge Admin test coverage | |
| run: yarn ember coverage-merge | |
| working-directory: ghost/admin | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: admin-coverage | |
| path: ghost/*/coverage/cobertura-coverage.xml | |
| - uses: tryghost/actions/actions/slack-build@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_browser-tests: | |
| name: Browser tests | |
| timeout-minutes: 60 | |
| runs-on: | |
| labels: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_any_code == 'true' && (needs.job_setup.outputs.is_six_pr == 'true' || needs.job_setup.outputs.is_development == 'true' || needs.job_setup.outputs.has_browser_tests_label == 'true') | |
| concurrency: | |
| group: ${{ github.workflow }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Check secrets | |
| if: github.event_name == 'pull_request' | |
| env: | |
| STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }} | |
| run: | | |
| if [ -z "${{ env.STRIPE_PUBLISHABLE_KEY }}" ]; then | |
| echo "Stripe Publishable Key is not available. Did you open this PR from a fork?" | |
| echo "If so, please re-open the PR from a branch in the upstream TryGhost/Ghost repository." | |
| exit 1 | |
| fi | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: yarn | |
| - name: Install Stripe-CLI | |
| run: | | |
| export VERSION=1.13.5 | |
| wget "https://github.com/stripe/stripe-cli/releases/download/v$VERSION/stripe_${VERSION}_linux_x86_64.tar.gz" | |
| tar -zxvf "stripe_${VERSION}_linux_x86_64.tar.gz" | |
| mv stripe /usr/local/bin | |
| stripe -v | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Run migrations | |
| working-directory: ghost/core | |
| run: yarn knex-migrator init | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - name: Build Admin | |
| run: yarn nx run ghost-admin:build:dev | |
| - name: Run Playwright tests locally | |
| run: yarn test:browser | |
| env: | |
| CI: true | |
| STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }} | |
| STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }} | |
| - uses: tryghost/actions/actions/slack-build@main | |
| if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| with: | |
| status: ${{ job.status }} | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: browser-tests-playwright-report | |
| path: ghost/core/playwright-report | |
| retention-days: 30 | |
| job_perf-tests: | |
| runs-on: | |
| labels: ubuntu-latest-4-cores | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_core == 'true' && needs.job_setup.outputs.is_development == 'true' | |
| name: Performance tests | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Install hyperfine | |
| run: | | |
| export HYPERFINE_VERSION=1.18.0 | |
| wget https://github.com/sharkdp/hyperfine/releases/download/v$HYPERFINE_VERSION/hyperfine-v$HYPERFINE_VERSION-x86_64-unknown-linux-gnu.tar.gz | |
| tar -zxvf hyperfine-v$HYPERFINE_VERSION-x86_64-unknown-linux-gnu.tar.gz | |
| mv hyperfine-v$HYPERFINE_VERSION-x86_64-unknown-linux-gnu/hyperfine /usr/local/bin | |
| chmod +x /usr/local/bin/hyperfine | |
| - name: Build TS code | |
| run: yarn nx run-many -t build:tsc | |
| - name: Run hyperfine on boot | |
| working-directory: ghost/core | |
| run: hyperfine --show-output --warmup 3 'GHOST_CI_SHUTDOWN_AFTER_BOOT=1 node index.js' --export-json boot-perf.json | |
| - name: Convert data | |
| working-directory: ghost/core | |
| run: | | |
| jq '[{ name: "Boot time", unit: "s", value: .results[0].median, range: ((.results[0].max - .results[0].min) | tostring) }]' < boot-perf.json > boot-perf-formatted.json | |
| - name: Run analysis | |
| uses: benchmark-action/[email protected] | |
| with: | |
| tool: 'customSmallerIsBetter' | |
| output-file-path: ghost/core/boot-perf-formatted.json | |
| benchmark-data-dir-path: "" | |
| gh-repository: github.com/TryGhost/Ghost-Benchmarks | |
| github-token: ${{ secrets.CANARY_DOCKER_BUILD }} | |
| auto-push: true | |
| job_unit-tests: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_any_code == 'true' | |
| strategy: | |
| matrix: | |
| node: ${{ fromJSON(needs.job_setup.outputs.node_test_matrix) }} | |
| name: Unit tests (Node ${{ matrix.node }}) | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1000 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Set timezone (non-UTC) | |
| uses: szenius/[email protected] | |
| with: | |
| timezoneLinux: "America/New_York" | |
| # Ensure all built packages are built before running tests (admin is built in job_setup) | |
| - name: Build remaining assets | |
| run: yarn nx run-many -t build --exclude=ghost-admin | |
| - run: yarn nx affected -t test:unit --base=${{ needs.job_setup.outputs.BASE_COMMIT }} | |
| - uses: actions/upload-artifact@v4 | |
| if: matrix.node == env.NODE_VERSION | |
| with: | |
| name: unit-coverage | |
| path: ghost/*/coverage/cobertura-coverage.xml | |
| - uses: tryghost/actions/actions/slack-build@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_acceptance-tests: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_core == 'true' | |
| strategy: | |
| matrix: | |
| node: ${{ fromJSON(needs.job_setup.outputs.node_test_matrix) }} | |
| env: | |
| - DB: mysql8 | |
| NODE_ENV: testing-mysql | |
| include: | |
| - node: ${{ needs.job_setup.outputs.node_version }} | |
| env: | |
| DB: sqlite3 | |
| NODE_ENV: testing | |
| env: | |
| DB: ${{ matrix.env.DB }} | |
| NODE_ENV: ${{ matrix.env.NODE_ENV }} | |
| name: Acceptance tests (Node ${{ matrix.node }}, ${{ matrix.env.DB }}) | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| - name: Shutdown MySQL | |
| run: sudo service mysql stop | |
| if: matrix.env.DB == 'mysql8' | |
| - name: Login to Docker Hub | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - uses: tryghost/mysql-action@main | |
| if: matrix.env.DB == 'mysql8' | |
| timeout-minutes: 3 | |
| with: | |
| authentication plugin: 'caching_sha2_password' | |
| mysql version: '8.0' | |
| mysql database: 'ghost_testing' | |
| mysql root password: 'root' | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Set timezone (non-UTC) | |
| uses: szenius/[email protected] | |
| with: | |
| timezoneLinux: "America/New_York" | |
| - name: Set env vars (SQLite) | |
| if: contains(matrix.env.DB, 'sqlite') | |
| run: echo "database__connection__filename=/dev/shm/ghost-test.db" >> $GITHUB_ENV | |
| - name: Set env vars (MySQL) | |
| if: contains(matrix.env.DB, 'mysql') | |
| run: echo "database__connection__password=root" >> $GITHUB_ENV | |
| - name: E2E tests | |
| working-directory: ghost/core | |
| run: yarn test:ci:e2e | |
| - name: Integration tests | |
| working-directory: ghost/core | |
| run: yarn test:ci:integration | |
| - uses: actions/upload-artifact@v4 | |
| if: matrix.node == env.NODE_VERSION && contains(matrix.env.DB, 'mysql') | |
| with: | |
| name: e2e-coverage | |
| path: | | |
| ghost/*/coverage-e2e/cobertura-coverage.xml | |
| ghost/*/coverage-integration/cobertura-coverage.xml | |
| - uses: tryghost/actions/actions/slack-build@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_legacy-tests: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_core == 'true' | |
| strategy: | |
| matrix: | |
| include: | |
| - node: ${{ needs.job_setup.outputs.node_version }} | |
| env: | |
| DB: mysql8 | |
| NODE_ENV: testing-mysql | |
| - node: ${{ needs.job_setup.outputs.node_version }} | |
| env: | |
| DB: sqlite3 | |
| NODE_ENV: testing | |
| env: | |
| DB: ${{ matrix.env.DB }} | |
| NODE_ENV: ${{ matrix.env.NODE_ENV }} | |
| name: Legacy tests (Node ${{ matrix.node }}, ${{ matrix.env.DB }}) | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| - name: Shutdown MySQL | |
| run: sudo service mysql stop | |
| if: matrix.env.DB == 'mysql8' | |
| - uses: tryghost/mysql-action@main | |
| if: matrix.env.DB == 'mysql8' | |
| timeout-minutes: 3 | |
| with: | |
| authentication plugin: 'caching_sha2_password' | |
| mysql version: '8.0' | |
| mysql database: 'ghost_testing' | |
| mysql root password: 'root' | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Set env vars (SQLite) | |
| if: contains(matrix.env.DB, 'sqlite') | |
| run: echo "database__connection__filename=/dev/shm/ghost-test.db" >> $GITHUB_ENV | |
| - name: Set env vars (MySQL) | |
| if: contains(matrix.env.DB, 'mysql') | |
| run: echo "database__connection__password=root" >> $GITHUB_ENV | |
| - name: Legacy tests | |
| working-directory: ghost/core | |
| run: yarn test:ci:legacy | |
| - uses: tryghost/actions/actions/slack-build@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_admin_x_settings: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_admin_x_settings == 'true' | |
| name: Admin-X Settings tests | |
| env: | |
| CI: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - run: yarn nx run @tryghost/admin-x-settings:test:acceptance | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: admin-x-settings-playwright-report | |
| path: apps/admin-x-settings/playwright-report | |
| retention-days: 30 | |
| - uses: tryghost/actions/actions/slack-build@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_activitypub: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_activitypub == 'true' | |
| name: ActivityPub tests | |
| env: | |
| CI: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - run: yarn nx run @tryghost/activitypub:test:acceptance | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: activitypub-playwright-report | |
| path: apps/activitypub/playwright-report | |
| retention-days: 30 | |
| - uses: tryghost/actions/actions/slack-build@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_comments_ui: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_comments_ui == 'true' | |
| name: Comments-UI tests | |
| env: | |
| CI: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - run: yarn nx run @tryghost/comments-ui:test | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: comments-ui-playwright-report | |
| path: apps/comments-ui/playwright-report | |
| retention-days: 30 | |
| - uses: tryghost/actions/actions/slack-build@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_signup_form: | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_signup_form == 'true' | |
| name: Signup-form tests | |
| env: | |
| CI: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - run: yarn nx run @tryghost/signup-form:test:e2e | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: signup-form-playwright-report | |
| path: apps/signup-form/playwright-report | |
| retention-days: 30 | |
| - uses: tryghost/actions/actions/slack-build@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_tinybird-tests: | |
| name: Tinybird Tests | |
| runs-on: ubuntu-latest | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_tinybird == 'true' | |
| defaults: | |
| run: | |
| working-directory: ghost/core/core/server/data/tinybird | |
| services: | |
| tinybird: | |
| image: tinybirdco/tinybird-local:latest | |
| ports: | |
| - 7181:7181 | |
| env: | |
| TINYBIRD_HOST: ${{ secrets.TINYBIRD_HOST }} | |
| TINYBIRD_TOKEN: ${{ secrets.TINYBIRD_TOKEN }} | |
| TINYBIRD_HOST_STAGING: ${{ secrets.TINYBIRD_HOST_STAGING }} | |
| TINYBIRD_TOKEN_STAGING: ${{ secrets.TINYBIRD_TOKEN_STAGING }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Tinybird CLI | |
| run: curl -fsSL https://tinybird.co/install.sh | sh | |
| - name: Build project | |
| run: tb build | |
| - name: Test project | |
| run: tb test run | |
| - name: Deployment check - Staging | |
| run: tb --cloud --host ${{ env.TINYBIRD_HOST_STAGING }} --token ${{ env.TINYBIRD_TOKEN_STAGING }} deploy --check | |
| - name: Deployment check - Production | |
| run: tb --cloud --host ${{ env.TINYBIRD_HOST }} --token ${{ env.TINYBIRD_TOKEN }} deploy --check | |
| job_ghost-cli: | |
| name: Ghost-CLI tests | |
| needs: [job_setup] | |
| if: needs.job_setup.outputs.changed_core == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| submodules: true | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Install npm v8 # downgrade from v10 to v8 due to https://github.com/npm/cli/issues/6738 | |
| run: npm install -g npm@8 | |
| - name: Install Ghost-CLI | |
| run: npm install -g ghost-cli@latest | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - run: node .github/scripts/bump-version.js canary | |
| - run: yarn archive | |
| - run: mv ghost-*.tgz ghost.tgz | |
| working-directory: ghost/core | |
| - name: Switch back to Node v16.14.0 (for v4) | |
| uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: '16.14.0' | |
| - name: Install latest v4 | |
| run: | | |
| DIR=$(mktemp -d) | |
| echo "V4_DIR=$DIR" >> $GITHUB_ENV | |
| ghost install v4 --local -d $DIR | |
| - name: Switch back to ${{ env.NODE_VERSION }} | |
| uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Update from v4 | |
| run: | | |
| ghost update -f -d $V4_DIR --archive $(pwd)/ghost/core/ghost.tgz | |
| - name: Save Ghost CLI Debug Logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ghost-cli-debug-logs | |
| path: /home/runner/.ghost/logs/ | |
| - name: Clean Install | |
| run: | | |
| DIR=$(mktemp -d) | |
| ghost install local -d $DIR --archive $(pwd)/ghost/core/ghost.tgz | |
| - name: Latest Release | |
| run: | | |
| DIR=$(mktemp -d) | |
| ghost install local -d $DIR | |
| ghost update -d $DIR --archive $(pwd)/ghost/core/ghost.tgz | |
| - name: Print debug logs | |
| if: failure() | |
| run: | | |
| [ -f ~/.ghost/logs/*.log ] && cat ~/.ghost/logs/*.log | |
| - uses: tryghost/actions/actions/slack-build@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_docker_build: | |
| name: Build & Push Docker Image | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Determine build strategy | |
| id: strategy | |
| run: | | |
| IS_FORK_PR="false" | |
| if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then | |
| IS_FORK_PR="true" | |
| fi | |
| IMAGE_NAME="ghcr.io/${{ github.repository_owner }}/ghost-development" | |
| if [ "$IS_FORK_PR" = "true" ]; then | |
| IMAGE_NAME="ghcr.io/${{ github.event.pull_request.head.repo.owner.login }}/ghost-development" | |
| fi | |
| CACHE_KEY=$(echo $IMAGE_NAME | tr '[:upper:]' '[:lower:]') | |
| echo "is-fork-pr=$IS_FORK_PR" >> $GITHUB_OUTPUT | |
| echo "should-push=$( [ "$IS_FORK_PR" = "false" ] && echo "true" || echo "false" )" >> $GITHUB_OUTPUT | |
| echo "should-load=$( [ "$IS_FORK_PR" = "true" ] && echo "true" || echo "false" )" >> $GITHUB_OUTPUT | |
| echo "image-name=$IMAGE_NAME" >> $GITHUB_OUTPUT | |
| echo "cache-key=$CACHE_KEY" >> $GITHUB_OUTPUT | |
| echo "Build Strategy: " | |
| echo " Is fork PR: $IS_FORK_PR" | |
| echo " Should push: $( [ "$IS_FORK_PR" = "false" ] && echo "true" || echo "false" )" | |
| echo " Should load: $( [ "$IS_FORK_PR" = "true" ] && echo "true" || echo "false" )" | |
| echo " Image name: $IMAGE_NAME" | |
| echo " Cache key: $CACHE_KEY" | |
| - name: Docker meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ steps.strategy.outputs.image-name }} | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=sha | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| labels: | | |
| org.opencontainers.image.title=Ghost Development | |
| org.opencontainers.image.description=Ghost development build | |
| org.opencontainers.image.vendor=TryGhost | |
| maintainer=TryGhost | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v6 | |
| id: build | |
| with: | |
| context: . | |
| file: Dockerfile | |
| push: ${{ steps.strategy.outputs.should-push }} | |
| load: ${{ steps.strategy.outputs.should-load }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # On PRs: use both main cache and PR-specific cache | |
| # On main: only use main cache | |
| cache-from: | | |
| type=registry,ref=${{ steps.strategy.outputs.cache-key }}:cache-main | |
| ${{ github.event_name == 'pull_request' && format('type=registry,ref={0}:cache-pr-{1}', steps.strategy.outputs.cache-key, github.event.pull_request.number) || '' }} | |
| # Only export cache if we can push (not on fork PRs) | |
| cache-to: ${{ steps.strategy.outputs.should-push == 'true' && format('type=registry,ref={0}:cache-{1},mode=max', steps.strategy.outputs.cache-key, github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'main') || '' }} | |
| - name: Save image as artifact (fork PR) | |
| if: steps.strategy.outputs.is-fork-pr == 'true' | |
| run: | | |
| # Get the first tag from the multi-line tags output | |
| IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
| echo "Saving image: $IMAGE_TAG" | |
| docker save "$IMAGE_TAG" | gzip > docker-image.tar.gz | |
| echo "Image saved as docker-image.tar.gz" | |
| ls -lh docker-image.tar.gz | |
| - name: Upload image artifact (fork PR) | |
| if: steps.strategy.outputs.is-fork-pr == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: docker-image | |
| path: docker-image.tar.gz | |
| retention-days: 1 | |
| outputs: | |
| image-tags: ${{ steps.meta.outputs.tags }} | |
| image-digest: ${{ steps.build.outputs.digest }} | |
| is-fork: ${{ steps.strategy.outputs.is-fork-pr }} | |
| job_inspect_image: | |
| name: Inspect Docker Image | |
| needs: job_docker_build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| sparse-checkout: | | |
| .github/actions/load-docker-image | |
| - name: Load Docker Image | |
| id: load | |
| uses: ./.github/actions/load-docker-image | |
| with: | |
| is-fork: ${{ needs.job_docker_build.outputs.is-fork }} | |
| image-tags: ${{ needs.job_docker_build.outputs.image-tags }} | |
| - name: Inspect image size and layers | |
| shell: bash | |
| run: | | |
| IMAGE_TAG="${{ steps.load.outputs.image-tag }}" | |
| echo "Analyzing Docker image: $IMAGE_TAG" | |
| # Get the image size in bytes | |
| IMAGE_SIZE_BYTES=$(docker inspect "$IMAGE_TAG" --format='{{.Size}}') | |
| # Convert to human readable format | |
| IMAGE_SIZE_MB=$(( IMAGE_SIZE_BYTES / 1024 / 1024 )) | |
| IMAGE_SIZE_GB=$(echo "scale=2; $IMAGE_SIZE_BYTES / 1024 / 1024 / 1024" | bc) | |
| # Format size display based on magnitude | |
| if [ $IMAGE_SIZE_MB -ge 1024 ]; then | |
| IMAGE_SIZE_DISPLAY="${IMAGE_SIZE_GB} GB" | |
| else | |
| IMAGE_SIZE_DISPLAY="${IMAGE_SIZE_MB} MB" | |
| fi | |
| echo "Image size: ${IMAGE_SIZE_DISPLAY}" | |
| # Write to GitHub Step Summary | |
| { | |
| echo "# Docker Image Analysis" | |
| echo "" | |
| echo "**Image:** \`$IMAGE_TAG\`" | |
| echo "" | |
| echo "**Total Size:** ${IMAGE_SIZE_DISPLAY}" | |
| echo "" | |
| echo "## Image Layers" | |
| echo "" | |
| echo "| Size | Layer |" | |
| echo "|------|-------|" | |
| # Get all layers (including 0B ones) | |
| docker history "$IMAGE_TAG" --format "{{.Size}}@@@{{.CreatedBy}}" --no-trunc | \ | |
| while IFS='@@@' read -r size cmd; do | |
| # Clean up the command for display | |
| cmd_clean=$(echo "$cmd" | sed 's/^\/bin\/sh -c //' | sed 's/^#(nop) //' | sed 's/^@@//' | sed 's/|/\\|/g' | cut -c1-80) | |
| if [ ${#cmd} -gt 80 ]; then | |
| cmd_clean="${cmd_clean}..." | |
| fi | |
| echo "| $size | \`${cmd_clean}\` |" | |
| done | |
| } >> $GITHUB_STEP_SUMMARY | |
| job_e2e_tests: | |
| name: ${{matrix.shell == 'react' && '[Optional] ' || ''}}E2E Tests (${{ matrix.shell == 'react' && 'React' || 'Ember' }} ${{ matrix.shardIndex }}/${{ matrix.shardTotal }}) | |
| runs-on: ubuntu-latest | |
| needs: [job_docker_build, job_setup] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shardIndex: [1, 2, 3, 4] | |
| shardTotal: [4] | |
| shell: [ember, react] | |
| continue-on-error: ${{ matrix.shell == 'react' }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # TODO: Build tb-cli in a separate workflow and pull from GHCR instead of building here | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build Tinybird CLI Image | |
| uses: docker/build-push-action@v6 | |
| id: build | |
| with: | |
| context: . | |
| file: docker/tb-cli/Dockerfile | |
| push: false | |
| load: true | |
| tags: ghost-tb-cli | |
| - name: Load Image | |
| uses: ./.github/actions/load-docker-image | |
| id: load | |
| with: | |
| is-fork: ${{ needs.job_docker_build.outputs.is-fork }} | |
| image-tags: ${{ needs.job_docker_build.outputs.image-tags }} | |
| - name: Setup Docker Registry Mirrors | |
| uses: ./.github/actions/setup-docker-registry-mirrors | |
| - name: Pull images | |
| env: | |
| GHOST_IMAGE_TAG: ${{ steps.load.outputs.image-tag }} | |
| run: docker compose -f e2e/compose.yml pull | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Setup Playwright | |
| uses: ./.github/actions/setup-playwright | |
| - name: Run e2e tests | |
| env: | |
| GHOST_IMAGE_TAG: ${{ steps.load.outputs.image-tag }} | |
| USE_REACT_SHELL: ${{ matrix.shell == 'react' && 'true' || '' }} | |
| TEST_WORKERS_COUNT: 1 | |
| run: yarn test:e2e --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} | |
| - name: Upload blob report to GitHub Actions Artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: blob-report-${{ matrix.shell }}-${{ matrix.shardIndex }} | |
| path: e2e/blob-report | |
| retention-days: 1 | |
| - name: Upload test results artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-${{ matrix.shell }}-${{ matrix.shardIndex }} | |
| path: e2e/test-results | |
| retention-days: 7 | |
| - uses: tryghost/actions/actions/slack-build@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_merge_e2e_reports: | |
| name: Merge ${{ matrix.shell == 'react' && 'React' || 'Ember' }} Reports | |
| if: always() | |
| needs: [job_e2e_tests, job_setup] | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shell: [ember, react] | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Download blob reports from GitHub Actions Artifacts | |
| uses: actions/download-artifact@v4 | |
| continue-on-error: true | |
| with: | |
| path: e2e/all-blob-reports | |
| pattern: blob-report-${{ matrix.shell }}-* | |
| merge-multiple: true | |
| - name: Check for blob reports | |
| id: check | |
| run: | | |
| if [ -d "e2e/all-blob-reports" ] && [ -n "$(ls -A e2e/all-blob-reports 2>/dev/null)" ]; then | |
| echo "has_reports=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_reports=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Download test results from GitHub Actions Artifacts | |
| if: steps.check.outputs.has_reports == 'true' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: e2e/all-test-results | |
| pattern: test-results-${{ matrix.shell }}-* | |
| merge-multiple: true | |
| - name: Merge into HTML Report | |
| if: steps.check.outputs.has_reports == 'true' | |
| run: npx playwright merge-reports --reporter html ./all-blob-reports | |
| working-directory: e2e | |
| - name: Upload HTML report | |
| if: steps.check.outputs.has_reports == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report-${{ matrix.shell }} | |
| path: e2e/playwright-report | |
| retention-days: 14 | |
| - name: Upload merged test results | |
| if: steps.check.outputs.has_reports == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results-${{ matrix.shell }} | |
| path: e2e/all-test-results | |
| retention-days: 7 | |
| - name: View Test Report command | |
| if: steps.check.outputs.has_reports == 'true' | |
| run: | | |
| echo -e "::notice::To view the ${{ matrix.shell == 'react' && 'React' || 'Ember' }} Playwright report locally, run:\n\nREPORT_DIR=\$(mktemp -d) && gh run download ${{ github.run_id }} -n playwright-report-${{ matrix.shell }} -D \"\$REPORT_DIR\" && npx playwright show-report \"\$REPORT_DIR\"" | |
| - name: Comment on PR with test report command | |
| if: github.event_name == 'pull_request' && steps.check.outputs.has_reports == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh pr comment ${{ github.event.pull_request.number }} --body "## ${{ matrix.shell == 'react' && 'React' || 'Ember' }} E2E Tests Failed | |
| To view the Playwright test report locally, run: | |
| \`\`\`bash | |
| REPORT_DIR=\$(mktemp -d) && gh run download ${{ github.run_id }} -n playwright-report-${{ matrix.shell }} -D \"\$REPORT_DIR\" && npx playwright show-report \"\$REPORT_DIR\" | |
| \`\`\`" | |
| job_coverage: | |
| name: Coverage | |
| needs: [ | |
| job_admin-tests, | |
| job_acceptance-tests, | |
| job_unit-tests | |
| ] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Restore Admin coverage | |
| if: contains(needs.job_admin-tests.result, 'success') | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: admin-coverage | |
| - name: Move coverage | |
| if: contains(needs.job_admin-tests.result, 'success') | |
| run: | | |
| rsync -av --remove-source-files admin/* ghost/admin | |
| - name: Upload Admin test coverage | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| flags: admin-tests | |
| - name: Restore E2E coverage | |
| if: contains(needs.job_acceptance-tests.result, 'success') | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: e2e-coverage | |
| - name: Move coverage | |
| if: contains(needs.job_acceptance-tests.result, 'success') | |
| run: | | |
| rsync -av --remove-source-files core/* ghost/core | |
| - name: Upload E2E test coverage | |
| if: contains(needs.job_acceptance-tests.result, 'success') | |
| uses: codecov/codecov-action@v5 | |
| with: | |
| flags: e2e-tests | |
| job_required_tests: | |
| name: All required tests passed or skipped | |
| needs: | |
| [ | |
| job_setup, | |
| job_lint, | |
| job_i18n, | |
| job_ghost-cli, | |
| job_admin-tests, | |
| job_unit-tests, | |
| job_acceptance-tests, | |
| job_legacy-tests, | |
| job_browser-tests, | |
| job_admin_x_settings, | |
| job_activitypub, | |
| job_comments_ui, | |
| job_signup_form, | |
| job_tinybird-tests, | |
| job_e2e_tests | |
| ] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Output needs | |
| run: echo "${{ toJson(needs) }}" | |
| - name: Check if any required jobs failed or been cancelled | |
| if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') | |
| run: | | |
| echo "One of the dependent jobs have failed or been cancelled. You may need to re-run it." && exit 1 | |
| canary: | |
| needs: [ | |
| job_setup, | |
| job_required_tests | |
| ] | |
| name: Canary | |
| runs-on: ubuntu-latest | |
| if: | | |
| always() | |
| && needs.job_setup.result == 'success' | |
| && needs.job_required_tests.result == 'success' | |
| && ( | |
| needs.job_setup.outputs.is_main == 'true' | |
| || needs.job_setup.outputs.is_six == 'true' | |
| || ( | |
| github.event_name == 'pull_request' | |
| && ( | |
| needs.job_setup.outputs.member_is_in_org == 'true' | |
| || github.event.pull_request.user.login == 'renovate[bot]' | |
| ) | |
| && contains(github.event.pull_request.labels.*.name, 'deploy-to-staging') | |
| ) | |
| ) | |
| steps: | |
| - name: Output needs (for debugging) | |
| run: echo "${{ toJson(needs) }}" | |
| - name: Compute branch name (push) | |
| if: github.event_name == 'push' | |
| run: echo "branch_name=${{ github.ref_name }}" >> $GITHUB_ENV | |
| - name: Compute branch name (pull_request) | |
| if: github.event_name == 'pull_request' | |
| run: echo "branch_name=${{ github.ref }}" >> $GITHUB_ENV | |
| - name: Set deployment parameters | |
| run: | | |
| if [[ "${{ needs.job_setup.outputs.is_main }}" == "true" ]]; then | |
| echo "deploy_version=canary" >> $GITHUB_ENV | |
| else | |
| echo "deploy_version=six" >> $GITHUB_ENV | |
| fi | |
| - name: Invoke build | |
| uses: aurelien-baudet/workflow-dispatch@v4 | |
| with: | |
| token: ${{ secrets.CANARY_DOCKER_BUILD }} | |
| workflow: .github/workflows/deploy.yml | |
| ref: 'refs/heads/main' # this is the ref of Ghost-Moya | |
| repo: TryGhost/Ghost-Moya | |
| # env.branch_name is the branch of Ghost itself | |
| inputs: '{"version":"${{ env.deploy_version }}","environment":"staging","version_extra":"${{ env.branch_name }}"}' | |
| wait-for-completion-timeout: 25m | |
| wait-for-completion-interval: 30s | |
| publish_packages: | |
| needs: [ | |
| job_setup, | |
| job_lint, | |
| job_unit-tests | |
| ] | |
| name: Publish ${{ matrix.package_name }} | |
| runs-on: ubuntu-latest | |
| if: always() && needs.job_setup.result == 'success' && needs.job_lint.result == 'success' && needs.job_unit-tests.result == 'success' && needs.job_setup.outputs.is_main == 'true' | |
| permissions: | |
| id-token: write | |
| strategy: | |
| matrix: | |
| include: | |
| - package_name: '@tryghost/activitypub' | |
| package_path: 'apps/activitypub' | |
| cdn_paths: 'https://cdn.jsdelivr.net/ghost/activitypub@CURRENT_MAJOR/dist/activitypub.js' | |
| - package_name: '@tryghost/portal' | |
| package_path: 'apps/portal' | |
| cdn_paths: 'https://cdn.jsdelivr.net/ghost/portal@~CURRENT_MINOR/umd/portal.min.js' | |
| - package_name: '@tryghost/sodo-search' | |
| package_path: 'apps/sodo-search' | |
| cdn_paths: | | |
| https://cdn.jsdelivr.net/ghost/sodo-search@~CURRENT_MINOR/umd/sodo-search.min.js | |
| https://cdn.jsdelivr.net/ghost/sodo-search@~CURRENT_MINOR/umd/main.css | |
| - package_name: '@tryghost/comments-ui' | |
| package_path: 'apps/comments-ui' | |
| cdn_paths: 'https://cdn.jsdelivr.net/ghost/comments-ui@~CURRENT_MINOR/umd/comments-ui.min.js' | |
| - package_name: '@tryghost/signup-form' | |
| package_path: 'apps/signup-form' | |
| cdn_paths: 'https://cdn.jsdelivr.net/ghost/signup-form@~CURRENT_MINOR/umd/signup-form.min.js' | |
| - package_name: '@tryghost/announcement-bar' | |
| package_path: 'apps/announcement-bar' | |
| cdn_paths: 'https://cdn.jsdelivr.net/ghost/announcement-bar@~CURRENT_MINOR/umd/announcement-bar.min.js' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Check if version changed | |
| id: version_check | |
| working-directory: ${{ matrix.package_path }} | |
| run: | | |
| CURRENT_VERSION=$(cat package.json | jq -r .version) | |
| PUBLISHED_VERSION=$(npm show ${{ matrix.package_name }} version || echo "0.0.0") | |
| echo "Current version: $CURRENT_VERSION" | |
| echo "Published version: $PUBLISHED_VERSION" | |
| CURRENT_MINOR=$(cat package.json | jq -r .version | awk -F. '{print $1"."$2}') | |
| echo "current_minor=$CURRENT_MINOR" >> $GITHUB_OUTPUT | |
| CURRENT_MAJOR=$(cat package.json | jq -r .version | awk -F. '{print $1}') | |
| echo "current_major=$CURRENT_MAJOR" >> $GITHUB_OUTPUT | |
| if [ "$CURRENT_VERSION" = "$PUBLISHED_VERSION" ]; then | |
| echo "Version is unchanged." | |
| echo "version_changed=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "Version has changed." | |
| echo "version_changed=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build the package | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| run: yarn run nx build ${{ matrix.package_name }} | |
| - name: Configure .npmrc | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| run: | | |
| echo "@tryghost:registry=https://registry.npmjs.org/" >> ~/.npmrc | |
| # TODO: Check we can remove this once we update Node to v24 | |
| - name: Install v11 of NPM # We need this to install packages via OIDC. | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| run: npm install -g npm@11 | |
| - name: Publish to npm | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| working-directory: ${{ matrix.package_path }} | |
| run: npm publish --access public | |
| - name: Replace version placeholders in cdn-paths | |
| id: cdn_paths | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| run: | | |
| cdn_paths="${{ matrix.cdn_paths }}" | |
| echo "cdn_paths<<EOF" >> $GITHUB_OUTPUT | |
| echo "$cdn_paths" | sed -e 's/CURRENT_MINOR/${{ steps.version_check.outputs.current_minor }}/g' -e 's/CURRENT_MAJOR/${{ steps.version_check.outputs.current_major }}/g' >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Print cdn_paths | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| run: echo "${{ steps.cdn_paths.outputs.cdn_paths }}" | |
| - name: Wait before purging jsDelivr cache | |
| if: steps.version_check.outputs.version_changed == 'true' && matrix.package_name == '@tryghost/activitypub' | |
| run: | | |
| echo "Purging jsDelivr cache immediately after publishing a new version on NPM is unreliable. Waiting 1 minute before purging cache..." | |
| sleep 60 | |
| - name: Purge jsDelivr cache | |
| if: steps.version_check.outputs.version_changed == 'true' | |
| uses: gacts/purge-jsdelivr-cache@v1 | |
| with: | |
| url: ${{ steps.cdn_paths.outputs.cdn_paths }} | |
| deploy_tinybird_staging: | |
| name: Deploy Tinybird - Staging | |
| runs-on: ubuntu-latest | |
| needs: [ | |
| job_setup, | |
| job_tinybird-tests | |
| ] | |
| if: always() && github.event_name == 'push' && needs.job_setup.outputs.changed_tinybird == 'true' && needs.job_setup.result == 'success' && needs.job_tinybird-tests.result == 'success' && needs.job_setup.outputs.is_main == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Deploy to Staging | |
| uses: ./.github/actions/deploy-tinybird | |
| with: | |
| host: ${{ secrets.TINYBIRD_HOST_STAGING }} | |
| token: ${{ secrets.TINYBIRD_TOKEN_STAGING }} | |
| workspace: 'Staging' | |
| slack-webhook: ${{ secrets.ANALYTICS_SLACK_WEBHOOK_URL }} | |
| deploy_tinybird_production: | |
| name: Deploy Tinybird - Production | |
| runs-on: ubuntu-latest | |
| needs: [ | |
| job_setup, | |
| job_tinybird-tests, | |
| deploy_tinybird_staging | |
| ] | |
| if: always() && github.event_name == 'push' && needs.job_setup.outputs.changed_tinybird == 'true' && needs.job_setup.result == 'success' && needs.job_tinybird-tests.result == 'success' && needs.deploy_tinybird_staging.result == 'success' && needs.job_setup.outputs.is_main == 'true' | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Deploy to Production | |
| uses: ./.github/actions/deploy-tinybird | |
| with: | |
| host: ${{ secrets.TINYBIRD_HOST }} | |
| token: ${{ secrets.TINYBIRD_TOKEN }} | |
| workspace: 'Production' | |
| slack-webhook: ${{ secrets.ANALYTICS_SLACK_WEBHOOK_URL }} | |
| deploy_admin: | |
| needs: [ | |
| job_setup, | |
| job_required_tests | |
| ] | |
| name: Deploy Admin | |
| runs-on: ubuntu-latest | |
| if: | | |
| always() | |
| && needs.job_setup.result == 'success' | |
| && needs.job_setup.outputs.is_main == 'true' | |
| && needs.job_required_tests.result == 'success' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| env: | |
| FORCE_COLOR: 0 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Restore caches | |
| uses: ./.github/actions/restore-cache | |
| env: | |
| DEPENDENCY_CACHE_KEY: ${{ needs.job_setup.outputs.dependency_cache_key }} | |
| - name: Build Admin | |
| env: | |
| GHOST_CDN_URL: https://assets.ghost.io/admin-forward/ | |
| run: yarn nx run admin:build | |
| - name: Upload build artifact | |
| id: upload-artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: admin-build | |
| path: apps/admin/dist | |
| - name: Deploy Admin | |
| uses: aurelien-baudet/workflow-dispatch@v4 | |
| with: | |
| token: ${{ secrets.CANARY_DOCKER_BUILD }} | |
| workflow: .github/workflows/deploy-admin.yml | |
| ref: 'refs/heads/main' # this is the ref of Ghost-Moya | |
| repo: TryGhost/Ghost-Moya | |
| inputs: '{"admin_build_artifact_id":"${{ steps.upload-artifacts.outputs.artifact-id }}", "admin_build_artifact_run_id":"${{ github.run_id }}"}' | |
| wait-for-completion-timeout: 25m | |
| wait-for-completion-interval: 30s |