Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions .github/workflows/devcontainer-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@ name: Devcontainer image
# Builds the dev container base image used by .devcontainer/devcontainer.json
# (VS Code Dev Containers + GitHub Codespaces) and publishes it to GHCR.
#
# Currently triggered only by manual dispatch — auto-triggers on
# pull_request and push were removed pending a one-time TryGhost org-admin
# bootstrap of the ghost-devcontainer package on GHCR. Until then,
# GITHUB_TOKEN can't create the package on first push (`denied:
# permission_denied: write_package`), and we don't want every PR's CI to
# show a noisy failed check.
#
# Once the package shell exists at github.com/orgs/TryGhost/packages and
# is linked to this repo with Actions write access, restore the
# `pull_request:` and `push:` triggers in a tiny follow-up PR so the
# image rebuilds automatically on changes to docker/ghost-dev/** and
# pnpm-install inputs.
# Triggers only on push to main (path-filtered) and manual dispatch. PRs
# don't trigger this workflow — devcontainer.json references the :latest
# tag, so pre-publishing per-PR images would just produce unused tags.
# A broken Dockerfile change in a PR will surface when the merge to main
# fires this workflow; the previously-good :latest stays in place until
# the next successful run.

on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'docker/ghost-dev/**'
- '.github/workflows/devcontainer-build.yml'
- '.github/scripts/**'
- '.github/hooks/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'pnpm-workspace.yaml'
- '.npmrc'
- 'ghost/core/package.json'
- 'ghost/i18n/package.json'
- 'ghost/parse-email-address/package.json'

permissions:
contents: read
Expand All @@ -27,14 +35,17 @@ jobs:
publish:
name: Build & push
runs-on: ubuntu-latest
if: github.repository == 'TryGhost/Ghost'
if: github.repository == 'TryGhost/Ghost' && github.ref == 'refs/heads/main'
concurrency:
group: devcontainer-image-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4

Expand All @@ -50,6 +61,7 @@ jobs:
with:
context: .
file: docker/ghost-dev/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
ghcr.io/tryghost/ghost-devcontainer:latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ yarn-error.log*

# Runtime data
pids
.ghost-dev
*.pid
*.seed
*.pid.lock
Expand Down
2 changes: 1 addition & 1 deletion apps/announcement-bar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"react-dom": "17.0.2"
},
"scripts": {
"dev": "concurrently \"vite preview -l silent\" \"pnpm build:watch\"",
"dev": "concurrently --kill-others --names preview,build \"vite preview -l silent\" \"pnpm build:watch\"",
"build": "vite build",
"build:watch": "vite build --watch",
"test": "vitest run",
Expand Down
9 changes: 4 additions & 5 deletions apps/comments-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryghost/comments-ui",
"version": "1.4.16",
"version": "1.4.17",
"license": "MIT",
"repository": "https://github.com/TryGhost/Ghost",
"author": "Ghost Foundation",
Expand All @@ -15,7 +15,7 @@
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "concurrently \"pnpm preview --host -l silent\" \"pnpm build:watch\"",
"dev": "concurrently --kill-others --names preview,build \"pnpm preview --host -l silent\" \"pnpm build:watch\"",
"dev:test": "vite build && vite preview --port 7175",
"build": "vite build",
"build:watch": "vite build --watch",
Expand Down Expand Up @@ -69,16 +69,15 @@
"@vitejs/plugin-react": "catalog:",
"@vitest/coverage-v8": "catalog:",
"autoprefixer": "10.4.21",
"bson-objectid": "2.0.4",
"bson-objectid": "catalog:",
"concurrently": "catalog:",
"eslint": "catalog:",
"eslint-plugin-i18next": "6.1.4",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-refresh": "catalog:",
"eslint-plugin-tailwindcss": "3.18.2",
"jsdom": "catalog:",
"moment": "2.30.1",
"postcss": "catalog:",
"postcss-import": "16.1.1",
"sinon": "21.1.1",
"tailwindcss": "3.4.18",
"vite": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion apps/portal/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "concurrently \"pnpm preview -l silent\" \"pnpm build:watch\"",
"dev": "concurrently --kill-others --names preview,build \"pnpm preview -l silent\" \"pnpm build:watch\"",
"build": "vite build",
"build:watch": "vite build --watch",
"preview": "vite preview",
Expand Down
8 changes: 3 additions & 5 deletions apps/signup-form/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tryghost/signup-form",
"version": "0.3.23",
"version": "0.3.25",
"license": "MIT",
"repository": "https://github.com/TryGhost/Ghost",
"author": "Ghost Foundation",
Expand All @@ -14,8 +14,8 @@
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "concurrently \"vite --port 6173\" \"vite preview -l silent\" \"vite build --watch\"",
"preview": "concurrently \"vite preview -l silent\" \"vite build --watch\"",
"dev": "concurrently --kill-others --names dev,preview,build \"vite --port 6173\" \"vite preview -l silent\" \"vite build --watch\"",
"preview": "concurrently --kill-others --names preview,build \"vite preview -l silent\" \"vite build --watch\"",
"dev:test": "vite build && vite preview --port 6175",
"build": "tsc && vite build",
"lint": "pnpm run lint:js",
Expand Down Expand Up @@ -48,8 +48,6 @@
"autoprefixer": "10.4.21",
"concurrently": "catalog:",
"eslint": "catalog:",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-refresh": "catalog:",
"eslint-plugin-tailwindcss": "3.18.2",
"jsdom": "catalog:",
"postcss": "catalog:",
Expand Down
1 change: 0 additions & 1 deletion apps/signup-form/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export default (function viteConfig() {
test: {
globals: true, // required for @testing-library/jest-dom extensions
environment: 'jsdom',
setupFiles: './test/test-setup.js',
include: ['./test/unit/*'],
testTimeout: process.env.TIMEOUT ? parseInt(process.env.TIMEOUT) : 10000,
...(process.env.CI && { // https://github.com/vitest-dev/vitest/issues/1674
Expand Down
2 changes: 1 addition & 1 deletion apps/sodo-search/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"react-dom": "17.0.2"
},
"scripts": {
"dev": "concurrently \"vite preview -l silent\" \"pnpm build:watch\" \"pnpm tailwind\"",
"dev": "concurrently --kill-others --names preview,build,tailwind \"vite preview -l silent\" \"pnpm build:watch\" \"pnpm tailwind\"",
"build": "vite build && pnpm tailwind:base",
"build:watch": "vite build --watch",
"tailwind": "pnpm tailwind:base --watch ",
Expand Down
2 changes: 1 addition & 1 deletion ghost/admin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ghost-admin",
"version": "6.39.1-rc.0",
"version": "6.40.0-rc.0",
"description": "Ember.js admin client for Ghost",
"author": "Ghost Foundation",
"homepage": "http://ghost.org",
Expand Down
12 changes: 6 additions & 6 deletions ghost/core/core/server/api/endpoints/automated-emails.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const messages = {
};

// NOTE: This file is in a transitionary state. The `automated_emails` database table was split into
// `welcome_email_automations` (automation metadata: status, name, slug) and
// `automations` (automation metadata: status, name, slug) and
// `welcome_email_automated_emails` (email content: subject, lexical, sender fields). This controller
// acts as a facade that joins/splits data between those two models while preserving the original
// `automated_emails` API shape externally.
Expand Down Expand Up @@ -51,7 +51,7 @@ const controller = {
],
permissions: true,
async query(frame) {
const result = await models.WelcomeEmailAutomation.findPage({
const result = await models.Automation.findPage({
...frame.options,
withRelated: ['welcomeEmailAutomatedEmail']
});
Expand All @@ -75,7 +75,7 @@ const controller = {
],
permissions: true,
async query(frame) {
const model = await models.WelcomeEmailAutomation.findOne(frame.data, {
const model = await models.Automation.findOne(frame.data, {
...frame.options,
withRelated: ['welcomeEmailAutomatedEmail']
});
Expand All @@ -102,7 +102,7 @@ const controller = {
const automationData = _.pick(data, AUTOMATION_FIELDS);

return models.Base.transaction(async (transacting) => {
const automation = await models.WelcomeEmailAutomation.add(automationData, {...frame.options, transacting});
const automation = await models.Automation.add(automationData, {...frame.options, transacting});
const email = await models.WelcomeEmailAutomatedEmail.add(
{
...emailData,
Expand Down Expand Up @@ -139,7 +139,7 @@ const controller = {
const automationData = _.pick(data, AUTOMATION_FIELDS);

return models.Base.transaction(async (transacting) => {
let automation = await models.WelcomeEmailAutomation.findOne({id: frame.options.id}, {
let automation = await models.Automation.findOne({id: frame.options.id}, {
transacting,
withRelated: ['welcomeEmailAutomatedEmail']
});
Expand All @@ -159,7 +159,7 @@ const controller = {
}

if (Object.keys(automationData).length > 0) {
automation = await models.WelcomeEmailAutomation.edit(automationData, {
automation = await models.Automation.edit(automationData, {
...frame.options,
transacting
});
Expand Down
2 changes: 1 addition & 1 deletion ghost/core/core/server/data/exporter/table-lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const BACKUP_TABLES = [
'recommendation_subscribe_events',
'outbox',
'gifts',
'welcome_email_automations',
'automations',
'welcome_email_automation_runs',
'welcome_email_automated_emails'
];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const logging = require('@tryghost/logging');
const {createNonTransactionalMigration} = require('../../utils');

const FROM_TABLE = 'welcome_email_automations';
const TO_TABLE = 'automations';

module.exports = createNonTransactionalMigration(
async function up(connection) {
const fromExists = await connection.schema.hasTable(FROM_TABLE);
const toExists = await connection.schema.hasTable(TO_TABLE);

if (toExists) {
logging.warn(`Skipping renaming table: ${TO_TABLE} already exists`);
return;
}

if (!fromExists) {
logging.warn(`Skipping renaming table: ${FROM_TABLE} does not exist`);
return;
}

logging.info(`Renaming table: ${FROM_TABLE} -> ${TO_TABLE}`);
await connection.schema.renameTable(FROM_TABLE, TO_TABLE);
},
async function down(connection) {
const fromExists = await connection.schema.hasTable(FROM_TABLE);
const toExists = await connection.schema.hasTable(TO_TABLE);

if (fromExists) {
logging.warn(`Skipping renaming table: ${FROM_TABLE} already exists`);
return;
}

if (!toExists) {
logging.warn(`Skipping renaming table: ${TO_TABLE} does not exist`);
return;
}

logging.info(`Renaming table: ${TO_TABLE} -> ${FROM_TABLE}`);
await connection.schema.renameTable(TO_TABLE, FROM_TABLE);
}
);
6 changes: 3 additions & 3 deletions ghost/core/core/server/data/schema/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -1171,7 +1171,7 @@ module.exports = {
created_at: {type: 'dateTime', nullable: false},
updated_at: {type: 'dateTime', nullable: true}
},
welcome_email_automations: {
automations: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
status: {type: 'string', maxlength: 50, nullable: false, defaultTo: 'inactive', validations: {isIn: [['active', 'inactive']]}},
name: {type: 'string', maxlength: 191, nullable: false, unique: true},
Expand All @@ -1181,7 +1181,7 @@ module.exports = {
},
welcome_email_automated_emails: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
welcome_email_automation_id: {type: 'string', maxlength: 24, nullable: false, references: 'welcome_email_automations.id', constraintName: 'weae_automation_id_foreign', cascadeDelete: true},
welcome_email_automation_id: {type: 'string', maxlength: 24, nullable: false, references: 'automations.id', constraintName: 'weae_automation_id_foreign', cascadeDelete: true},
next_welcome_email_automated_email_id: {type: 'string', maxlength: 24, nullable: true, references: 'welcome_email_automated_emails.id', constraintName: 'weae_next_email_id_foreign', cascadeDelete: false},
delay_days: {type: 'integer', nullable: false, unsigned: true},
subject: {type: 'string', maxlength: 300, nullable: false},
Expand All @@ -1195,7 +1195,7 @@ module.exports = {
},
welcome_email_automation_runs: {
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
welcome_email_automation_id: {type: 'string', maxlength: 24, nullable: false, references: 'welcome_email_automations.id', constraintName: 'wear_automation_id_foreign', cascadeDelete: true},
welcome_email_automation_id: {type: 'string', maxlength: 24, nullable: false, references: 'automations.id', constraintName: 'wear_automation_id_foreign', cascadeDelete: true},
member_id: {type: 'string', maxlength: 24, nullable: false, references: 'members.id', constraintName: 'wear_member_id_foreign', cascadeDelete: true},
next_welcome_email_automated_email_id: {type: 'string', maxlength: 24, nullable: true, references: 'welcome_email_automated_emails.id', constraintName: 'wear_next_email_id_foreign', cascadeDelete: false},
ready_at: {type: 'dateTime', nullable: true},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const {MEMBER_WELCOME_EMAIL_SLUGS} = require('../services/member-welcome-emails/

const MEMBER_WELCOME_EMAIL_SLUG_SET = new Set(Object.values(MEMBER_WELCOME_EMAIL_SLUGS));

const WelcomeEmailAutomation = ghostBookshelf.Model.extend({
tableName: 'welcome_email_automations',
const Automation = ghostBookshelf.Model.extend({
tableName: 'automations',

defaults() {
return {
Expand Down Expand Up @@ -57,5 +57,5 @@ const WelcomeEmailAutomation = ghostBookshelf.Model.extend({
});

module.exports = {
WelcomeEmailAutomation: ghostBookshelf.model('WelcomeEmailAutomation', WelcomeEmailAutomation)
Automation: ghostBookshelf.model('Automation', Automation)
};
4 changes: 2 additions & 2 deletions ghost/core/core/server/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {Action} = require('./action');
const {ApiKey, ApiKeys} = require('./api-key');
const {Author, Authors} = require('./author');
const {AutomatedEmailRecipient, AutomatedEmailRecipients} = require('./automated-email-recipient');
const {Automation} = require('./automation');
const {Benefit, Benefits} = require('./benefit');
const {CollectionPost} = require('./collection-post');
const {Collection} = require('./collection');
Expand Down Expand Up @@ -75,7 +76,6 @@ const {User, Users} = require('./user');
const {Webhook, Webhooks} = require('./webhook');
const {WelcomeEmailAutomatedEmail} = require('./welcome-email-automated-email');
const {WelcomeEmailAutomationRun} = require('./welcome-email-automation-run');
const {WelcomeEmailAutomation} = require('./welcome-email-automation');

// enable event listeners
require('./base/listeners');
Expand All @@ -91,6 +91,7 @@ exports.Author = Author;
exports.Authors = Authors;
exports.AutomatedEmailRecipient = AutomatedEmailRecipient;
exports.AutomatedEmailRecipients = AutomatedEmailRecipients;
exports.Automation = Automation;
exports.Benefit = Benefit;
exports.Benefits = Benefits;
exports.CollectionPost = CollectionPost;
Expand Down Expand Up @@ -188,7 +189,6 @@ exports.Webhook = Webhook;
exports.Webhooks = Webhooks;
exports.WelcomeEmailAutomatedEmail = WelcomeEmailAutomatedEmail;
exports.WelcomeEmailAutomationRun = WelcomeEmailAutomationRun;
exports.WelcomeEmailAutomation = WelcomeEmailAutomation;

function init() {
// `init` used to be a necessary call, but now it's unnecessary.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const WelcomeEmailAutomatedEmail = ghostBookshelf.Model.extend({
return this.belongsTo('EmailDesignSetting', 'email_design_setting_id', 'id');
},

welcomeEmailAutomation() {
return this.belongsTo('WelcomeEmailAutomation', 'welcome_email_automation_id', 'id');
automation() {
return this.belongsTo('Automation', 'welcome_email_automation_id', 'id');
},

nextWelcomeEmailAutomatedEmail() {
Expand Down
4 changes: 2 additions & 2 deletions ghost/core/core/server/models/welcome-email-automation-run.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const WelcomeEmailAutomationRun = ghostBookshelf.Model.extend({
};
},

welcomeEmailAutomation() {
return this.belongsTo('WelcomeEmailAutomation', 'welcome_email_automation_id', 'id');
automation() {
return this.belongsTo('Automation', 'welcome_email_automation_id', 'id');
},

member() {
Expand Down
Loading
Loading