Conversation
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…Ghost#1378) ref TryGhost#1324 Apparently the previous fix did not work, but it's not totally clear why. There seems to be an issue using `always()` and `failure()` together. According to the official GitHub docs, `failure()` should return true `when any ancestor job fails,` but in practice (as documented in https://github.com/orgs/community/discussions/80788), it always returns false even when dependent jobs fail. Have changed condition to explicitly check on the results of the previous jobs
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
closes https://linear.app/ghost/issue/PROD-2665 - Context: ActivityPub uses identity tokens provided by Ghost to authenticate and authorize requests. Identity tokens are JWT signed with RS256 and verifiable by a public key. Ghost exposes the public key as a JSON Web Key Set (JWKS) on <site_url>/ghost/.well-known/jwks.json. To avoid fetching the public key on each ActivityPub request, we cache it in Redis. - Problem: we currently don't have any invalidation mechanism for the cached public key. If the key is rotated after e.g. a site migration, users experience a HTTP 403 authorization error and are unable to access the API/use the app - Solution: if the JWT verification fails against a cached public key, we now retry by fetching a new key from Ghost and save the new key into the cache --------- Co-authored-by: Sag <guptazy@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ref https://linear.app/ghost/issue/BER-2908/add-dummy-api-endpoint-to-start-vertical-integration - in the context of introducing the global feed, extracted the mapping from a feed result to a post DTO, so that it can reused
ref https://linear.app/ghost/issue/BER-2911 Updated the `post.created` event handler so that newly created posts from Ghost publishers get added to a "global feed". This implementation is a prototype to get a feel for how this feature would work in the client
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
no ref Since we upgrade to the latest version of Biome we have been seeing lint warnings being reported on the usage of `Number.parseInt` as a new rule was added as part of the `recommended` ruleset
no ref Removed some of the vendor specific agent files now that `agents.md` seems to be the most widely supported (see https://agents.md/)
closes https://linear.app/ghost/issue/BER-2908 - in this first slice, the global feed returns all long-form posts (Articles) from all users, sorted by reverse-chron
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…st#1398) no refs When a new site is created it triggers a follow request to the Ghost Explore mastodon account which ends up making 2 outbound requests (webfinger lookup + follow activity). We do not want to do these live requests whilst testing so we have stubbed them out using wiremock
ref https://linear.app/ghost/issue/BER-2413 Refactored the follow flow so that follow / unfollow for internal accounts does not get federated as we can handle all of the operations required without needing to out to the network
…flicts (TryGhost#1406) no issue - used port 444 for caddy-testing instead of the default HTTPS port (443), to avoid conflicts during local dev
…st#1405) ref https://linear.app/ghost/issue/BER-2935 - this is the second iteration of the global feed experiment and the code is not final - used integration tests to test the new behaviour and removed cucumber tests for now, as we're going to either drop the experiment or re-write the logic if we expand further on the experiment
ref https://linear.app/ghost/issue/BER-2935/discovery-feed-from-top-publishers - we only want to show original posts in the global feed, not reposts
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ref https://linear.app/ghost/issue/BER-2904 - The current code did an N+1 query, for each follower it queried the database. This generated high load on the database when requests per second spiked.
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ref https://linear.app/ghost/issue/BER-2943 - added new API endpoint: GET /feed/discover/:slug that returns a feed of posts on a given topic, sorted by recently published at - this will replace the current GET /feed/global endpoint and related logic (a follow-up PR will clean up that code)
closes https://linear.app/ghost/issue/BER-2948 - The team uses Sentry and this is costing us 1,000$ a month.
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…st#1617) ref https://linear.app/ghost/issue/BER-3241 - We log the 410 and that should be enough to determine the request failed.
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Refactor `PostCreatedEvent` to implement `SerializableEvent` to comply with ADR-0011 for serializable domain events. Linear Issue: [BER-3174](https://linear.app/ghost/issue/BER-3174/refactor-postcreatedevent-to-implement-serializableevent-adr-0011) --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
no issue - The `yarn dev` command only runs the ActivityPub service (without nginx), versus `yarn dev:standalone` runs ActivityPub + nginx - As Ghost now runs a nginx service in development, we don't need to run it in ActivityPub
ref https://linear.app/ghost/issue/ONC-1521 Added sanitization to post title field
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
no ref When setting up the app from fresh via `yarn dev` (no previous docker images, no built `dist` dir), it was possible for an issue to occur where the container tries to load `app.ts` before it is built, resulting in the error: ``` activitypub-1 | yarn run v1.22.22 activitypub-1 | $ concurrently "yarn build --watch" "node --inspect=0.0.0.0:9229 --watch dist/app.js" activitypub-1 | [1] Debugger listening on ws://0.0.0.0:9229/99b125ec-4ba2-4fe8-9c14-e94ffa86b922 activitypub-1 | [1] For help, see: https://nodejs.org/en/docs/inspector activitypub-1 | [1] node:internal/modules/cjs/loader:1386 activitypub-1 | [1] throw err; activitypub-1 | [1] ^ activitypub-1 | [1] activitypub-1 | [1] Error: Cannot find module '/opt/activitypub/dist/app.js' activitypub-1 | [1] at Function._resolveFilename (node:internal/modules/cjs/loader:1383:15) activitypub-1 | [1] at defaultResolveImpl (node:internal/modules/cjs/loader:1025:19) activitypub-1 | [1] at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1030:22) activitypub-1 | [1] at Function._load (node:internal/modules/cjs/loader:1192:37) activitypub-1 | [1] at TracingChannel.traceSync (node:diagnostics_channel:328:14) activitypub-1 | [1] at wrapModuleLoad (node:internal/modules/cjs/loader:237:24) activitypub-1 | [1] at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:171:5) activitypub-1 | [1] at node:internal/main/run_main_module:36:49 { activitypub-1 | [1] code: 'MODULE_NOT_FOUND', activitypub-1 | [1] requireStack: [] activitypub-1 | [1] } activitypub-1 | [1] activitypub-1 | [1] Node.js v22.22.0 activitypub-1 | [1] Failed running 'dist/app.js'. Waiting for file changes before restarting... activitypub-1 | [1] node --inspect=0.0.0.0:9229 --watch dist/app.js exited with code 0 activitypub-1 | [0] $ esbuild src/app.ts --sourcemap --platform=neutral --bundle --packages=external --outfile=dist/app.js --watch activitypub-1 | [0] [watch] build finished, watching for changes... ``` This changeset fixes the issue + also updates the debugger setup so that sourcemaps are correctly loaded and debugging can be easily stared from vscode flavoured IDE's
…1636) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Refactor PostDeletedEvent to implement SerializableEvent and use primitive data to comply with ADR-0011 and ensure event data persistence after post deletion. The PostDeletedEvent previously contained the full Post entity, which is problematic for deletion events as the post may no longer exist in the database when the event is processed. This change ensures all necessary data for federation and other consumers is explicitly stored as primitive, serializable fields within the event itself. --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…1640) ref https://linear.app/ghost/issue/BER-3163 Misc clean up from https://linear.app/ghost/issue/BER-3163: - add missing account.updated test - clean up event name resolution - clean up event tests - reorder getName location - cleanup event registration
…ryGhost#1646) ref https://linear.app/ghost/issue/BER-3241 - We already see a 410 on the logs.
| if: steps.changes.outputs.populate-explore-json == 'true' | ||
| uses: google-github-actions/auth@v2 | ||
| if: steps.changes.outputs.reconcile-account-topics == 'true' | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| - name: Authenticate with GCP | ||
| if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} | ||
| uses: google-github-actions/auth@v2 | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| - name: Deploy Migrations to Cloud Run | ||
| if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }} | ||
| uses: google-github-actions/deploy-cloudrun@v2 | ||
| uses: google-github-actions/deploy-cloudrun@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| steps: | ||
| - name: Authenticate with GCP | ||
| uses: google-github-actions/auth@v2 | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| - name: Deploy Migrations to Cloud Run | ||
| if: ${{ matrix.region == 'europe-west4' }} | ||
| uses: google-github-actions/deploy-cloudrun@v2 | ||
| uses: google-github-actions/deploy-cloudrun@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
|
|
||
| - name: "Authenticate with GCP" | ||
| uses: google-github-actions/auth@v2 | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| - name: "Authenticate with GCP" | ||
| if: ${{ steps.check-closed-prs.outputs.destroy_prs != '' }} | ||
| uses: google-github-actions/auth@v2 | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
|
|
||
| - name: Authenticate with GCP | ||
| uses: google-github-actions/auth@v2 | ||
| uses: google-github-actions/auth@v3 |
Check warning
Code scanning / CodeQL
Unpinned tag for a non-immutable Action in workflow Medium
| name: Vitest | ||
| runs-on: ubuntu-latest | ||
| if: ${{ !inputs.skip-tests }} | ||
| steps: | ||
| - name: Check skip-tests | ||
| id: check-skip-tests | ||
| run: echo "skip_tests=${{ inputs.skip-tests }}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Checkout | ||
| if: steps.check-skip-tests.outputs.skip_tests != 'true' | ||
| uses: actions/checkout@v4 | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Download ActivityPub image | ||
| if: steps.check-skip-tests.outputs.skip_tests != 'true' | ||
| uses: actions/download-artifact@v4 | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: activitypub-amd64 | ||
| path: /tmp | ||
|
|
||
| - name: Download Migrations image | ||
| if: steps.check-skip-tests.outputs.skip_tests != 'true' | ||
| uses: actions/download-artifact@v4 | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: activitypub-migrations-amd64 | ||
| path: /tmp | ||
|
|
||
| - name: Load Docker images | ||
| if: steps.check-skip-tests.outputs.skip_tests != 'true' | ||
| run: | | ||
| docker load < /tmp/activitypub-amd64.tar | ||
| docker load < /tmp/activitypub-migrations-amd64.tar | ||
|
|
||
| - name: Run Tests | ||
| if: steps.check-skip-tests.outputs.skip_tests != 'true' | ||
| run: yarn test | ||
| run: docker compose run --rm migrate-testing up && yarn test:all | ||
|
|
||
| e2e: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium test
| name: E2E | ||
| runs-on: ubuntu-latest | ||
| if: ${{ !inputs.skip-tests }} | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Download ActivityPub image | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: activitypub-amd64 | ||
| path: /tmp | ||
|
|
||
| - name: Download Migrations image | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: activitypub-migrations-amd64 | ||
| path: /tmp | ||
|
|
||
| - name: Load Docker images | ||
| run: | | ||
| docker load < /tmp/activitypub-amd64.tar | ||
| docker load < /tmp/activitypub-migrations-amd64.tar | ||
|
|
||
| - name: Run E2E Tests | ||
| run: yarn test:cucumber |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium test
No description provided.