Skip to content

[pull] main from TryGhost:main#1063

Merged
pull[bot] merged 6 commits into
code:mainfrom
TryGhost:main
Apr 14, 2026
Merged

[pull] main from TryGhost:main#1063
pull[bot] merged 6 commits into
code:mainfrom
TryGhost:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Apr 14, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

mike182uk and others added 6 commits April 14, 2026 16:17
ref https://linear.app/ghost/issue/BER-3474

When a gift is refunded, the member is downgrade to `free` and thier
product access is removed
closes https://linear.app/ghost/issue/NY-1190

*I recommend [reviewing this
commit-by-commit](https://github.com/TryGhost/Ghost/pull/27364/commits).*

This change should have no user impact. It replaces the current welcome
email system with one powered by the new automations rails.

We've discussed the design for this change at some length. I hope this
is a straightforward implementation of that design. Here's the prompt I
gave to a coding agent:

<details>
<summary><strong>📝 LLM prompt</strong></summary>

I'm working on migrating member welcome emails to our new automations
data model. See the attached design document below for background.

I've already created the relevant tables/models for this, but most
things are still running with the old logic. I want to run member
welcome emails on the new data model.

This change should make two significant changes:

1. **When a member signs up…**
1. Dispatch a `MemberCreatedEvent`. **This code already exists and
doesn't need to be added.**
2. Add a new listener for `MemberCreatedEvent`. You should decide where
this listener goes, but it should be set up on app boot (directly or
indirectly); consider adding it to an existing part of the system that's
already listening for domain events. Create a new
`WelcomeEmailAutomationRun` and save it to the database.
* In
`core/server/services/members/members-api/repositories/member-repository.js`,
remove `_Outbox` interactions.
      * Fields:
* `welcomeEmailAutomationId`: either the free or paid automation,
depending on how the user signed up
        * `memberId`: the new/updated member's ID
* `nextWelcomeEmailAutomatedEmailId`: the first welcome email's ID. You
can assume it's the only one where the automation ID matches
        * `readyAt`: right now (we may change this in a followup patch)
        * `stepStartedAt`: `null`
        * `stepAttempts`: `0`
        * `exitReason`: `null`
   3. Start an automations poll (see below).
2. Create a function that does an **automations poll**. Ideally, it
should be stateless, but if state is needed, it can be part of a `class`
service that's initialized on startup. When an automations poll
executes…
   1. In a single transaction…
1. Select all `WelcomeEmailAutomationRun`s that we should start (see
`design_document` below for a sketch of a database query, which should
use Knex/Bookshelf as much as possible, not raw SQL). Also join the
`WelcomeEmailAutomation` and `WelcomeEmailAutomatedEmail`.
      2. Update each of these runs
         * `stepStartedAt` should be "now"
         * `stepAttempts` should be increased by 1
3. Ideally, you'd be able to do the SELECT and UPDATE in a single
database operation. If this is possible (make sure this works with
SQLite and MySQL), do that. If not, use a transaction.
   2. For each `WelcomeEmailAutomationRun`…
1. If `stepAttempts` is larger than `MAX_ATTEMPTS` (which should be
hard-coded to 10, but the hard-coded value could change in the future),
mark the job totally failed (see below).
2. Attempt to send the member welcome email. *This should replace the
existing Outbox handler*, which is at least partially defined in
`core/server/services/outbox/handlers/member-created.js`.
* We shouldn't delete the existing outbox code because it could still
have stuff in it. But we will eventually stop running it. (It's possible
we'll change the outbox code slightly, but we shouldn't change its
functionality.)
3. If it fails, increment `stepAttempts`, update `readyAt` to give some
delay, and enqueue another automation poll. (See `design_document` below
for a sketch of a database query, which should use Knex/Bookshelf as
much as possible.)
4. If it succeeds, mark the run as completed. (See `design_document`
below for a sketch of a database query, which should use Knex/Bookshelf
as much as possible.)

This patch isn't meant to implement the full design. It's a step in the
right direction. This patch should NOT:

* Handle multiple emails per automation. Assume there's always exactly
one
* Interact with the scheduler in any way
* Bail out of the automation run if the member no longer exists
* Bail out of the automation run if the member has unsubscribed
* Bail out of the automation run if the member's status has changed
(e.g., going from free to paid)

This change should have no user impact. It should only change the
plumbing of how member welcome emails are sent.

```
<design_document>
{DESIGN DOCUMENT AS MARKDOWN}
</design_document>
```
</details>

This implemented a great first draft. I made _many_ changes to its
output.

---------

Co-authored-by: Troy Ciesco <tmciesco@gmail.com>
ref https://linear.app/ghost/issue/ONC-1624

When fetching events, if we end the job on a particular second, we
increment the timestamp to start at the **next** second. This means we
drop any remaining events on that second, although they could/should be
picked up by the missing events job that runs on a delay.
no ref

In the gift repository we decided to have a dedicated `update` method
instead of a generic `save` method that also fell back to creating
Husky v9 introduced an `sh -e` wrapper around the hooks, which ignores
the `#!/bin/bash` shebang in our existing hooks, and executes them in sh
instead. We have some bash-specific syntax in our existing hooks, which
now fail.

This moves the bash-based hook logic behind POSIX entrypoints so Git can
execute the repo hooks correctly.

---

Alternatively, we could rewrite our existing hooks to be POSIX
compatible, but this seems to be the simplest step to properly migrate
to v9.
no ref

We had two tests:

1. Verify that an automation run is created when a member signs up
2. Verify that the "ready at" time is correct when a member signs up

This should just be one test.

This is a test-only change.
@pull pull Bot locked and limited conversation to collaborators Apr 14, 2026
@pull pull Bot added the ⤵️ pull label Apr 14, 2026
@pull pull Bot merged commit 649a4a1 into code:main Apr 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants