mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-21 15:35:00 +03:00
Added Dev Container setup (#21279)
no issue - Dev Containers let you work on Ghost in a consistent, isolated environment with all the necessary development dependencies pre-installed. VSCode (or Cursor) can effectively run _inside_ the container, providing a local quality development environment while working in a well-defined, isolated environment. - For now the default setup only works with "Clone repository in Container Volume" or "Clone PR in Container Volume" — this allows for a super quick and simple setup. We can also introduce another configuration to allow opening an existing local checkout in a Dev Container, but that's not quite ready yet. - This PR also added the `yarn clean:hard` command which: deletes all node_modules, cleans the yarn cache, and cleans the NX cache. This will be necessary for opening a local checkout in a Dev Container. - To learn more about Dev Containers, read this guide from VSCode: https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories --------- Co-authored-by: Joe Grigg <joe@ghost.org> Co-authored-by: Steve Larson <9larsons@gmail.com>
This commit is contained in:
parent
3f9fd2ad3f
commit
af0f26c75f
64
.devcontainer/.docker/Dockerfile
Normal file
64
.devcontainer/.docker/Dockerfile
Normal file
@ -0,0 +1,64 @@
|
||||
ARG NODE_VERSION=20.15.1
|
||||
## Base Image used for all targets
|
||||
FROM node:$NODE_VERSION-bullseye-slim AS base
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
curl \
|
||||
jq \
|
||||
libjemalloc2 \
|
||||
python3 \
|
||||
tar
|
||||
|
||||
# Base DevContainer: for use in a Dev Container where your local code is mounted into the container
|
||||
### Adding code and installing dependencies gets overridden by your local code/dependencies, so this is done in onCreateCommand
|
||||
FROM base AS base-devcontainer
|
||||
# Install Stripe CLI, zsh, playwright
|
||||
RUN curl -s https://packages.stripe.dev/api/security/keypair/stripe-cli-gpg/public | gpg --dearmor | tee /usr/share/keyrings/stripe.gpg && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/stripe.gpg] https://packages.stripe.dev/stripe-cli-debian-local stable main" | tee -a /etc/apt/sources.list.d/stripe.list && \
|
||||
apt update && \
|
||||
apt install -y \
|
||||
git \
|
||||
stripe \
|
||||
zsh \
|
||||
default-mysql-client && \
|
||||
npx -y playwright@1.46.1 install --with-deps
|
||||
|
||||
ENV NX_DAEMON=true
|
||||
ENV YARN_CACHE_FOLDER=/workspaces/ghost/.yarncache
|
||||
|
||||
EXPOSE 2368
|
||||
EXPOSE 4200
|
||||
EXPOSE 4173
|
||||
EXPOSE 41730
|
||||
EXPOSE 4175
|
||||
EXPOSE 4176
|
||||
EXPOSE 4177
|
||||
EXPOSE 4178
|
||||
EXPOSE 6174
|
||||
EXPOSE 7173
|
||||
EXPOSE 7174
|
||||
|
||||
|
||||
# Full Devcontainer Stage: Add the code and install dependencies
|
||||
### This is a full devcontainer with all the code and dependencies installed
|
||||
### Useful in an environment like Github Codespaces where you don't mount your local code into the container
|
||||
FROM base-devcontainer AS full-devcontainer
|
||||
WORKDIR /workspaces/ghost
|
||||
COPY ../../ .
|
||||
RUN yarn install --frozen-lockfile --prefer-offline --cache-folder $YARN_CACHE_FOLDER
|
||||
|
||||
# Development Stage: alternative entrypoint for development with some caching optimizations
|
||||
FROM base-devcontainer AS development
|
||||
|
||||
WORKDIR /workspaces/ghost
|
||||
|
||||
COPY ../../ .
|
||||
|
||||
RUN yarn install --frozen-lockfile --prefer-offline --cache-folder $YARN_CACHE_FOLDER && \
|
||||
cp -r .yarncache .yarncachecopy && \
|
||||
rm -Rf .yarncachecopy/.tmp && \
|
||||
yarn cache clean
|
||||
|
||||
ENTRYPOINT ["./.devcontainer/.docker/development.entrypoint.sh"]
|
||||
CMD ["yarn", "dev"]
|
13
.devcontainer/.docker/base-devcontainer.compose.yml
Normal file
13
.devcontainer/.docker/base-devcontainer.compose.yml
Normal file
@ -0,0 +1,13 @@
|
||||
# For use in a Dev Container where your local code is mounted into the container
|
||||
name: ghost-devcontainer
|
||||
services:
|
||||
ghost:
|
||||
image: ghost-base-devcontainer
|
||||
build:
|
||||
target: base-devcontainer
|
||||
command: ["sleep", "infinity"]
|
||||
environment:
|
||||
- DEVCONTAINER=true
|
||||
- DISPLAY=host.docker.internal:0
|
||||
volumes:
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
47
.devcontainer/.docker/base.compose.yml
Normal file
47
.devcontainer/.docker/base.compose.yml
Normal file
@ -0,0 +1,47 @@
|
||||
# Base container and services for running Ghost
|
||||
## Intended to be extended by another compose file
|
||||
## e.g. docker compose -f base.compose.yml -f development.compose.yml up
|
||||
## Does not include development dependencies, Ghost code, or any other dependencies
|
||||
name: ghost-base
|
||||
services:
|
||||
ghost:
|
||||
image: ghost-base
|
||||
build:
|
||||
context: ../../
|
||||
dockerfile: .devcontainer/.docker/Dockerfile
|
||||
target: base
|
||||
pull_policy: never
|
||||
tty: true
|
||||
depends_on:
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
mysql:
|
||||
image: mysql:8.0.35
|
||||
# We'll need to look into how we can further fine tune the memory usage/performance here
|
||||
command: --innodb-buffer-pool-size=1G --innodb-log-buffer-size=500M --innodb-change-buffer-max-size=50 --innodb-flush-log-at-trx_commit=0 --innodb-flush-method=O_DIRECT
|
||||
ports:
|
||||
- "3306"
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: ghost
|
||||
restart: always
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
healthcheck:
|
||||
test: "mysql -uroot -proot ghost -e 'select 1'"
|
||||
interval: 1s
|
||||
retries: 120
|
||||
redis:
|
||||
image: redis:7.0
|
||||
ports:
|
||||
- "6379"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
|
||||
interval: 1s
|
||||
retries: 120
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
38
.devcontainer/.docker/development.compose.yml
Normal file
38
.devcontainer/.docker/development.compose.yml
Normal file
@ -0,0 +1,38 @@
|
||||
# Development container with Ghost code and dependencies pre-installed
|
||||
## Watches your local filesystem and syncs changes to the container
|
||||
## Intended for use with raw docker compose commands
|
||||
name: ghost-development
|
||||
services:
|
||||
ghost:
|
||||
image: ghost-development
|
||||
build:
|
||||
target: development
|
||||
command: ["yarn", "dev"]
|
||||
volumes:
|
||||
- ../../.yarncache:/workspaces/ghost/.yarncache
|
||||
develop:
|
||||
watch:
|
||||
- path: ../../
|
||||
action: sync
|
||||
target: /workspaces/ghost
|
||||
ignore:
|
||||
- node_modules/
|
||||
- .yarncache/
|
||||
- path: yarn.lock
|
||||
action: rebuild
|
||||
ports:
|
||||
- 2368:2368
|
||||
- 4200:4200
|
||||
- 4173:4173
|
||||
- 41730:41730
|
||||
- 4175:4175
|
||||
- 4176:4176
|
||||
- 4177:4177
|
||||
- 4178:4178
|
||||
- 6174:6174
|
||||
- 7173:7173
|
||||
- 7174:7174
|
||||
- 9174:9174
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-}
|
||||
- APP_FLAGS=${APP_FLAGS:-}
|
7
.devcontainer/.docker/development.entrypoint.sh
Executable file
7
.devcontainer/.docker/development.entrypoint.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$(ls -A ".yarncache")" ]; then
|
||||
cp -r /workspaces/ghost/.yarncachecopy/* /workspaces/ghost/.yarncache/
|
||||
fi
|
||||
|
||||
exec "$@"
|
26
.devcontainer/.docker/full-devcontainer.compose.yml
Normal file
26
.devcontainer/.docker/full-devcontainer.compose.yml
Normal file
@ -0,0 +1,26 @@
|
||||
# Dev Container with all Ghost code and dependencies pre-installed
|
||||
name: ghost-full-devcontainer
|
||||
services:
|
||||
ghost:
|
||||
image: ghost-full-devcontainer
|
||||
build:
|
||||
target: full-devcontainer
|
||||
command: ["sleep", "infinity"]
|
||||
environment:
|
||||
- DEVCONTAINER=true
|
||||
- DISPLAY=host.docker.internal:0
|
||||
volumes:
|
||||
- /tmp/.X11-unix:/tmp/.X11-unix
|
||||
ports:
|
||||
- 2368:2368
|
||||
- 4200:4200
|
||||
- 4173:4173
|
||||
- 41730:41730
|
||||
- 4175:4175
|
||||
- 4176:4176
|
||||
- 4177:4177
|
||||
- 4178:4178
|
||||
- 6174:6174
|
||||
- 7173:7173
|
||||
- 7174:7174
|
||||
- 9174:9174
|
24
.devcontainer/README.md
Normal file
24
.devcontainer/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Dev Container Setup
|
||||
|
||||
## devcontainer.json
|
||||
This file contains the configuration for the dev container. It is used to define the setup of the container, including things like port bindings, environment variables, and other dev container specific features.
|
||||
|
||||
It points to a docker compose file in the `.devcontainer/.docker` directory, which in turn relies on a Dockerfile in the same directory.
|
||||
|
||||
## Dockerfile
|
||||
The Dockerfile in this directory uses a multi-stage build to allow for multiple types of builds without duplicating code and ensuring maximum consistency. The following targets are available:
|
||||
- `base`: The bare minimum base image used to build and run Ghost. Includes the operating system, node, and some build dependencies, but does not include any Ghost code or dependencies.
|
||||
- `base-devcontainer`: everything from `base`, plus additional development dependencies like the stripe-cli and playwright. No code or node dependencies.
|
||||
- `full-devcontainer`: everything from `base-devcontainer`, plus Ghost's code and all node dependencies
|
||||
- `development`: an alternative to `full-devcontainer` intended for manual development e.g. with docker compose. Add Ghost's code and installs dependencies with some optimizations for the yarn cache
|
||||
|
||||
## Docker Compose
|
||||
Similarly, the docker compose configuration relies on merging compose files to create the final configuration. The `base.compose.yml` file contains the bare minimum configuration, and can be extended by specifying additional services or modifying the existing ones by supplying additional compose files. For example, to run the `development.compose.yml` file, you would use the following command:
|
||||
|
||||
```
|
||||
docker compose -f .devcontainer/docker/base.compose.yml -f .devcontainer/docker/development.compose.yml up
|
||||
```
|
||||
|
||||
There is an alias `yarn compose` script in the top level `package.json` which points to the appropriate `compose.yml` files for local development.
|
||||
|
||||
This setup gives us the flexibility to create multiple different docker compose configurations, while ensuring a base level of consistency across configurations.
|
92
.devcontainer/createLocalConfig.js
Normal file
92
.devcontainer/createLocalConfig.js
Normal file
@ -0,0 +1,92 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
// Reads the config.local.json file and updates it with environments variables for devcontainer setup
|
||||
const configBasePath = path.join(__dirname, '..', 'ghost', 'core');
|
||||
const configFile = path.join(configBasePath, 'config.local.json');
|
||||
let originalConfig = {};
|
||||
if (fs.existsSync(configFile)) {
|
||||
try {
|
||||
// Backup the user's config.local.json file just in case
|
||||
// This won't be used by Ghost but can be useful to switch back to local development
|
||||
const backupFile = path.join(configBasePath, 'config.local-backup.json');
|
||||
fs.copyFileSync(configFile, backupFile);
|
||||
|
||||
// Read the current config.local.json file into memory
|
||||
const fileContent = fs.readFileSync(configFile, 'utf8');
|
||||
originalConfig = JSON.parse(fileContent);
|
||||
} catch (error) {
|
||||
console.error('Error reading or parsing config file:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log('Config file does not exist. Creating a new one.');
|
||||
}
|
||||
|
||||
let newConfig = {};
|
||||
// Change the url if we're in a codespace
|
||||
if (process.env.CODESPACES === 'true') {
|
||||
assert.ok(process.env.CODESPACE_NAME, 'CODESPACE_NAME is not defined');
|
||||
assert.ok(process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN, 'GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN is not defined');
|
||||
const url = `https://${process.env.CODESPACE_NAME}-2368.${process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}`;
|
||||
newConfig.url = url;
|
||||
}
|
||||
|
||||
newConfig.database = {
|
||||
client: 'mysql2',
|
||||
connection: {
|
||||
host: 'mysql',
|
||||
user: 'root',
|
||||
password: 'root',
|
||||
database: 'ghost'
|
||||
}
|
||||
}
|
||||
newConfig.adapters = {
|
||||
Redis: {
|
||||
host: 'redis',
|
||||
port: 6379
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only update the mail settings if they aren't already set
|
||||
if (!originalConfig.mail && process.env.MAILGUN_SMTP_PASS && process.env.MAILGUN_SMTP_USER) {
|
||||
newConfig.mail = {
|
||||
transport: 'SMTP',
|
||||
options: {
|
||||
service: 'Mailgun',
|
||||
host: 'smtp.mailgun.org',
|
||||
secure: true,
|
||||
port: 465,
|
||||
auth: {
|
||||
user: process.env.MAILGUN_SMTP_USER,
|
||||
pass: process.env.MAILGUN_SMTP_PASS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only update the bulk email settings if they aren't already set
|
||||
if (!originalConfig.bulkEmail && process.env.MAILGUN_API_KEY && process.env.MAILGUN_DOMAIN) {
|
||||
newConfig.bulkEmail = {
|
||||
mailgun: {
|
||||
baseUrl: 'https://api.mailgun.net/v3',
|
||||
apiKey: process.env.MAILGUN_API_KEY,
|
||||
domain: process.env.MAILGUN_DOMAIN,
|
||||
tag: 'bulk-email'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the original config with the new config
|
||||
const config = {...originalConfig, ...newConfig};
|
||||
|
||||
// Write the updated config.local.json file
|
||||
try {
|
||||
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
||||
console.log('Config file updated successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error writing config file:', error);
|
||||
process.exit(1);
|
||||
}
|
139
.devcontainer/devcontainer.json
Normal file
139
.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,139 @@
|
||||
{
|
||||
"name": "Ghost Local DevContainer",
|
||||
"dockerComposeFile": ["./.docker/base.compose.yml", "./.docker/base-devcontainer.compose.yml"],
|
||||
"service": "ghost",
|
||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||
"shutdownAction": "stopCompose",
|
||||
"onCreateCommand": ["./.devcontainer/onCreateCommand.sh"],
|
||||
"updateContentCommand": ["git", "submodule", "update", "--init", "--recursive"],
|
||||
"postCreateCommand": ["yarn", "knex-migrator", "init"],
|
||||
"remoteEnv": {
|
||||
"STRIPE_SECRET_KEY": "${localEnv:STRIPE_SECRET_KEY}",
|
||||
"STRIPE_API_KEY": "${localEnv:STRIPE_SECRET_KEY}",
|
||||
"STRIPE_PUBLISHABLE_KEY": "${localEnv:STRIPE_PUBLISHABLE_KEY}",
|
||||
"STRIPE_ACCOUNT_ID": "${localEnv:STRIPE_ACCOUNT_ID}",
|
||||
"MAILGUN_SMTP_USER": "${localEnv:MAILGUN_SMTP_USER}",
|
||||
"MAILGUN_SMTP_PASS": "${localEnv:MAILGUN_SMTP_PASS}",
|
||||
"MAILGUN_API_KEY": "${localEnv:MAILGUN_API_KEY}",
|
||||
"MAILGUN_DOMAIN": "${localEnv:MAILGUN_DOMAIN}"
|
||||
},
|
||||
"forwardPorts": [2368,4200],
|
||||
"portsAttributes": {
|
||||
"80": {
|
||||
"onAutoForward": "ignore"
|
||||
},
|
||||
"2368": {
|
||||
"label": "Ghost"
|
||||
},
|
||||
"2369": {
|
||||
"label": "Ghost (Test Server)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"2370": {
|
||||
"label": "Ghost (Test Server)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"2371": {
|
||||
"label": "Ghost (Test Server)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"2372": {
|
||||
"label": "Ghost (Test Server)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"2373": {
|
||||
"label": "Ghost (Test Server)",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"4200": {
|
||||
"label": "Admin",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"4201": {
|
||||
"label": "Admin Live Reload",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"4175": {
|
||||
"label": "Portal",
|
||||
},
|
||||
"4176": {
|
||||
"label": "Portal (HTTPS)",
|
||||
"protocol": "https"
|
||||
},
|
||||
"4177": {
|
||||
"label": "Announcement Bar"
|
||||
},
|
||||
"4178": {
|
||||
"label": "Search"
|
||||
},
|
||||
"4173": {
|
||||
"label": "Lexical"
|
||||
},
|
||||
"41730": {
|
||||
"label": "Lexical (HTTPS)",
|
||||
"protocol": "https"
|
||||
},
|
||||
"6174": {
|
||||
"label": "Signup Form",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"7173": {
|
||||
"label": "Comments"
|
||||
},
|
||||
"7174": {
|
||||
"label": "Comments (HTTPS)",
|
||||
"protocol": "https"
|
||||
},
|
||||
"9174": {
|
||||
"label": "Prometheus Metrics Exporter",
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"5173": {
|
||||
"onAutoForward": "silent"
|
||||
},
|
||||
"5368": {
|
||||
"onAutoForward": "silent"
|
||||
}
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||
"terminal.integrated.profiles.linux": { "zsh": { "path": "/bin/zsh" } }
|
||||
},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker"
|
||||
]
|
||||
}
|
||||
},
|
||||
"secrets": {
|
||||
"STRIPE_SECRET_KEY": {
|
||||
"description": "Your Stripe account's test secret API key",
|
||||
"documentationUrl": "https://dashboard.stripe.com/test/apikeys"
|
||||
},
|
||||
"STRIPE_PUBLISHABLE_KEY": {
|
||||
"description": "Your Stripe account's test publishable key",
|
||||
"documentationUrl": "https://dashboard.stripe.com/test/apikeys"
|
||||
},
|
||||
"STRIPE_ACCOUNT_ID": {
|
||||
"description": "Your Stripe Account ID",
|
||||
"documentationUrl": "https://dashboard.stripe.com/settings/account"
|
||||
},
|
||||
"MAILGUN_SMTP_USER": {
|
||||
"description": "Your Mailgun account's SMTP username, e.g. postmaster@sandbox1234567890.mailgun.org. You can find this in the Mailgun dashboard under Sending -> Domains -> Select your domain -> SMTP.",
|
||||
"documentationUrl": "https://app.mailgun.com/mg/sending/domains"
|
||||
},
|
||||
"MAILGUN_SMTP_PASS": {
|
||||
"description": "Your Mailgun account's SMTP password",
|
||||
"documentationUrl": "https://app.mailgun.com/mg/sending/domains"
|
||||
},
|
||||
"MAILGUN_API_KEY": {
|
||||
"description": "Your Mailgun account's API key",
|
||||
"documentationUrl": ""
|
||||
},
|
||||
"MAILGUN_DOMAIN": {
|
||||
"description": "Your Mailgun account's domain, e.g. sandbox1234567890.mailgun.org",
|
||||
"documentationUrl": ""
|
||||
}
|
||||
}
|
||||
}
|
18
.devcontainer/onCreateCommand.sh
Executable file
18
.devcontainer/onCreateCommand.sh
Executable file
@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
echo "Setting up local config file..."
|
||||
node .devcontainer/createLocalConfig.js
|
||||
|
||||
echo "Cleaning up any previous installs..."
|
||||
yarn clean:hard
|
||||
|
||||
echo "Installing dependencies..."
|
||||
yarn install
|
||||
|
||||
echo "Updating git submodules..."
|
||||
git submodule update --init --recursive
|
||||
|
||||
echo "Building typescript packages..."
|
||||
yarn nx run-many -t build:ts
|
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules/
|
||||
|
||||
.nxcache
|
||||
.nx/cache
|
||||
.nx/workspace-data
|
||||
|
||||
**/*.log
|
26
.github/scripts/clean.sh
vendored
Executable file
26
.github/scripts/clean.sh
vendored
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Clean yarn cache
|
||||
echo "Cleaning yarn cache..."
|
||||
if [ "$DEVCONTAINER" = "true" ]; then
|
||||
# In devcontainer, these directories are mounted from the host so we can't delete them — only their contents
|
||||
rm -rf .yarncache/* .yarncachecopy/*
|
||||
else
|
||||
yarn cache clean
|
||||
fi
|
||||
|
||||
# Reset Nx
|
||||
echo "Resetting NX cache..."
|
||||
rm -rf .nxcache .nx
|
||||
|
||||
# Recursively delete all node_modules directories
|
||||
echo "Deleting all node_modules directories..."
|
||||
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
|
||||
|
||||
echo "Deleting all build artifacts..."
|
||||
find ./ghost -type d -name "build" -exec rm -rf '{}' +
|
||||
find ./ghost -type f -name "tsconfig.tsbuildinfo" -delete
|
||||
|
||||
echo "Cleanup complete!"
|
5
.github/scripts/setup.js
vendored
5
.github/scripts/setup.js
vendored
@ -36,6 +36,11 @@ async function runAndStream(command, args, options) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.DEVCONTAINER === 'true') {
|
||||
console.log(chalk.yellow(`Devcontainer detected, skipping setup`));
|
||||
return;
|
||||
}
|
||||
|
||||
const coreFolder = path.join(__dirname, '../../ghost/core');
|
||||
const rootFolder = path.join(__dirname, '../..');
|
||||
const config = require('../../ghost/core/core/shared/config/loader').loadNconf({
|
||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -128,6 +128,7 @@ Caddyfile
|
||||
|
||||
# Playwright state with cookies it keeps across tests
|
||||
/ghost/core/playwright-state.json
|
||||
/ghost/core/playwright-report
|
||||
|
||||
# Admin
|
||||
/ghost/admin/dist
|
||||
@ -174,3 +175,7 @@ tsconfig.tsbuildinfo
|
||||
.tinyb
|
||||
.venv
|
||||
.diff_tmp
|
||||
|
||||
# Docker Yarn Cache
|
||||
.yarncache
|
||||
.yarncachecopy
|
||||
|
@ -17,6 +17,7 @@ export default defineConfig((config) => {
|
||||
'process.env.NODE_ENV': JSON.stringify(config.mode)
|
||||
},
|
||||
preview: {
|
||||
host: '0.0.0.0',
|
||||
port: 4177
|
||||
},
|
||||
plugins: [
|
||||
|
@ -20,6 +20,7 @@ export default (function viteConfig() {
|
||||
'process.env.VITEST_SEGFAULT_RETRY': 3
|
||||
},
|
||||
preview: {
|
||||
host: '0.0.0.0',
|
||||
port: 7173
|
||||
},
|
||||
server: {
|
||||
|
@ -21,6 +21,7 @@ export default defineConfig((config) => {
|
||||
REACT_APP_VERSION: JSON.stringify(process.env.npm_package_version)
|
||||
},
|
||||
preview: {
|
||||
host: '0.0.0.0',
|
||||
port: 4175
|
||||
},
|
||||
server: {
|
||||
|
@ -20,6 +20,7 @@ export default (function viteConfig() {
|
||||
'process.env.VITEST_SEGFAULT_RETRY': 3
|
||||
},
|
||||
preview: {
|
||||
host: '0.0.0.0',
|
||||
port: 6174
|
||||
},
|
||||
build: {
|
||||
|
@ -17,6 +17,7 @@ export default defineConfig((config) => {
|
||||
'process.env.NODE_ENV': JSON.stringify(config.mode)
|
||||
},
|
||||
preview: {
|
||||
host: '0.0.0.0',
|
||||
port: 4178
|
||||
},
|
||||
plugins: [
|
||||
|
3
ghost/core/nodemon.json
Normal file
3
ghost/core/nodemon.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignore": ["*.test.js", "**/content/public/**", "**/core/built/**", ".yarncache/**"]
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"archive": "npm pack",
|
||||
"dev": "node --watch index.js",
|
||||
"dev": "nodemon index.js",
|
||||
"build:assets": "postcss core/frontend/public/ghost.css --no-map --use cssnano -o core/frontend/public/ghost.min.css",
|
||||
"test": "yarn test:unit",
|
||||
"test:base": "mocha --reporter dot --require=./test/utils/overrides.js --exit --trace-warnings --recursive --extension=test.js",
|
||||
@ -250,6 +250,7 @@
|
||||
"mocha-slow-test-reporter": "0.1.2",
|
||||
"mock-knex": "TryGhost/mock-knex#d8b93b1c20d4820323477f2c60db016ab3e73192",
|
||||
"nock": "13.3.3",
|
||||
"nodemon": "^3.1.7",
|
||||
"papaparse": "5.3.2",
|
||||
"parse-prometheus-text-format": "1.1.1",
|
||||
"postcss": "8.4.39",
|
||||
@ -319,17 +320,20 @@
|
||||
},
|
||||
"test:browser": {
|
||||
"dependsOn": [
|
||||
"^build:ts"
|
||||
"^build:ts",
|
||||
"ghost-admin:build"
|
||||
]
|
||||
},
|
||||
"test:browser:admin": {
|
||||
"dependsOn": [
|
||||
"^build:ts"
|
||||
"^build:ts",
|
||||
"ghost-admin:build"
|
||||
]
|
||||
},
|
||||
"test:browser:portal": {
|
||||
"dependsOn": [
|
||||
"^build:ts"
|
||||
"^build:ts",
|
||||
"ghost-admin:build"
|
||||
]
|
||||
},
|
||||
"test:e2e": {
|
||||
|
@ -1,4 +1,24 @@
|
||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||
const os = require('os');
|
||||
|
||||
const getWorkerCount = () => {
|
||||
if (process.env.CI) {
|
||||
return '100%';
|
||||
}
|
||||
if (process.env.PLAYWRIGHT_SLOWMO) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let cpuCount;
|
||||
try {
|
||||
cpuCount = os.cpus().length;
|
||||
} catch (err) {
|
||||
cpuCount = 1;
|
||||
}
|
||||
// Stripe limits to 5 new accounts per second
|
||||
// If we go higher than 5, we'll get rate limited and tests will fail
|
||||
return Math.min(5, cpuCount - 1);
|
||||
};
|
||||
|
||||
const config = {
|
||||
timeout: 75 * 1000,
|
||||
@ -7,7 +27,7 @@ const config = {
|
||||
},
|
||||
// save trace on fail
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? '100%' : (process.env.PLAYWRIGHT_SLOWMO ? 1 : undefined),
|
||||
workers: getWorkerCount(),
|
||||
reporter: process.env.CI ? [['list', {printSteps: true}], ['html']] : [['list', {printSteps: true}]],
|
||||
use: {
|
||||
trace: 'retain-on-failure',
|
||||
|
@ -363,12 +363,13 @@ test.describe('Publishing', () => {
|
||||
await sharedPage.goto('/ghost');
|
||||
await createPostDraft(sharedPage, postData);
|
||||
|
||||
const editorUrl = await sharedPage.url();
|
||||
|
||||
// Schedule the post to publish asap (by setting it to 00:00, it will get auto corrected to the minimum time possible - 5 seconds in the future)
|
||||
await publishPost(sharedPage, {time: '00:00', type: 'publish+send'});
|
||||
await closePublishFlow(sharedPage);
|
||||
await checkPostStatus(sharedPage, 'Scheduled', 'Scheduled to be published and sent'); // Member count can differ, hence not included here
|
||||
await checkPostStatus(sharedPage, 'Scheduled', 'in a few seconds'); // Extra test for suffix on hover
|
||||
const editorUrl = await sharedPage.url();
|
||||
|
||||
// Go to the homepage and check if the post is not yet visible there
|
||||
await checkPostNotPublished(sharedPage, postData);
|
||||
@ -394,11 +395,11 @@ test.describe('Publishing', () => {
|
||||
await sharedPage.goto('/ghost');
|
||||
await createPostDraft(sharedPage, postData);
|
||||
|
||||
const editorUrl = await sharedPage.url();
|
||||
// Schedule the post to publish asap (by setting it to 00:00, it will get auto corrected to the minimum time possible - 5 seconds in the future)
|
||||
await publishPost(sharedPage, {time: '00:00'});
|
||||
await closePublishFlow(sharedPage);
|
||||
await checkPostStatus(sharedPage, 'Scheduled', 'Scheduled to be published in a few seconds');
|
||||
const editorUrl = await sharedPage.url();
|
||||
|
||||
// Check not published yet
|
||||
await checkPostNotPublished(sharedPage, postData);
|
||||
@ -425,12 +426,12 @@ test.describe('Publishing', () => {
|
||||
|
||||
await sharedPage.goto('/ghost');
|
||||
await createPostDraft(sharedPage, postData);
|
||||
const editorUrl = await sharedPage.url();
|
||||
|
||||
// Schedule the post to publish asap (by setting it to 00:00, it will get auto corrected to the minimum time possible - 5 seconds in the future)
|
||||
await publishPost(sharedPage, {type: 'send', time: '00:00'});
|
||||
await closePublishFlow(sharedPage);
|
||||
await checkPostStatus(sharedPage, 'Scheduled', 'Scheduled to be sent in a few seconds');
|
||||
const editorUrl = await sharedPage.url();
|
||||
|
||||
// Check not published yet
|
||||
await checkPostNotPublished(sharedPage, postData);
|
||||
|
@ -23,6 +23,7 @@
|
||||
"archive": "nx run ghost:archive",
|
||||
"build": "nx run-many -t build",
|
||||
"build:clean": "nx reset && rimraf -g 'ghost/*/build' && rimraf -g 'ghost/*/tsconfig.tsbuildinfo'",
|
||||
"clean:hard": "./.github/scripts/clean.sh",
|
||||
"dev:debug": "DEBUG_COLORS=true DEBUG=@tryghost*,ghost:* yarn dev",
|
||||
"dev:admin": "node .github/scripts/dev.js --admin",
|
||||
"dev:ghost": "node .github/scripts/dev.js --ghost",
|
||||
@ -34,6 +35,8 @@
|
||||
"reset:data:empty": "cd ghost/core && node index.js generate-data --clear-database --quantities members:0,posts:0 --seed 123",
|
||||
"reset:data:xxl": "cd ghost/core && node index.js generate-data --clear-database --quantities members:2000000,posts:0,emails:0,members_stripe_customers:0,members_login_events:0,members_status_events:0 --seed 123",
|
||||
"docker:reset": "docker-compose -f .github/scripts/docker-compose.yml down -v && docker-compose -f .github/scripts/docker-compose.yml up -d --wait",
|
||||
"docker:down": "docker-compose -f .github/scripts/docker-compose.yml down",
|
||||
"compose": "docker compose -f .devcontainer/.docker/base.compose.yml -f .devcontainer/.docker/development.compose.yml",
|
||||
"lint": "nx run-many -t lint",
|
||||
"test": "nx run-many -t test",
|
||||
"test:unit": "nx run-many -t test:unit",
|
||||
|
62
yarn.lock
62
yarn.lock
@ -12484,6 +12484,21 @@ chokidar@^2.1.8:
|
||||
optionalDependencies:
|
||||
fsevents "^1.2.7"
|
||||
|
||||
chokidar@^3.5.2:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
dependencies:
|
||||
anymatch "~3.1.2"
|
||||
braces "~3.0.2"
|
||||
glob-parent "~5.1.2"
|
||||
is-binary-path "~2.1.0"
|
||||
is-glob "~4.0.1"
|
||||
normalize-path "~3.0.0"
|
||||
readdirp "~3.6.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
chownr@^1.1.1:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
|
||||
@ -13875,7 +13890,7 @@ debug@3.2.7, debug@^3.0.1, debug@^3.1.0, debug@^3.2.6, debug@^3.2.7:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@~4.3.1, debug@~4.3.2, debug@~4.3.6:
|
||||
debug@4, debug@^4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@~4.3.1, debug@~4.3.2, debug@~4.3.6:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
@ -19407,6 +19422,11 @@ iferr@^0.1.5:
|
||||
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
|
||||
integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==
|
||||
|
||||
ignore-by-default@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
|
||||
|
||||
ignore@5.3.2, ignore@^5.0.4, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
@ -24033,6 +24053,22 @@ nodemailer@6.9.15, nodemailer@^6.6.3:
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.15.tgz#57b79dc522be27e0e47ac16cc860aa0673e62e04"
|
||||
integrity sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==
|
||||
|
||||
nodemon@^3.1.7:
|
||||
version "3.1.7"
|
||||
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.7.tgz#07cb1f455f8bece6a499e0d72b5e029485521a54"
|
||||
integrity sha512-hLj7fuMow6f0lbB0cD14Lz2xNjwsyruH251Pk4t/yIitCFJbmY1myuLlHm/q06aST4jg6EgAh74PIBBrRqpVAQ==
|
||||
dependencies:
|
||||
chokidar "^3.5.2"
|
||||
debug "^4"
|
||||
ignore-by-default "^1.0.1"
|
||||
minimatch "^3.1.2"
|
||||
pstree.remy "^1.1.8"
|
||||
semver "^7.5.3"
|
||||
simple-update-notifier "^2.0.0"
|
||||
supports-color "^5.5.0"
|
||||
touch "^3.1.0"
|
||||
undefsafe "^2.0.5"
|
||||
|
||||
nopt@^3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
|
||||
@ -26560,6 +26596,11 @@ psl@^1.1.28, psl@^1.1.33:
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
||||
|
||||
pstree.remy@^1.1.8:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
|
||||
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
|
||||
|
||||
public-encrypt@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
|
||||
@ -28401,6 +28442,13 @@ simple-swizzle@^0.2.2:
|
||||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
simple-update-notifier@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb"
|
||||
integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==
|
||||
dependencies:
|
||||
semver "^7.5.3"
|
||||
|
||||
sinon-chai@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-4.0.0.tgz#77d59d9f4a833f0d3a88249b4637acc72656fdfa"
|
||||
@ -29491,7 +29539,7 @@ supports-color@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==
|
||||
|
||||
supports-color@^5.3.0:
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
@ -30177,6 +30225,11 @@ totalist@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd"
|
||||
integrity sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==
|
||||
|
||||
touch@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694"
|
||||
integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==
|
||||
|
||||
tough-cookie@4.1.4, tough-cookie@^4.0.0, tough-cookie@^4.1.4:
|
||||
version "4.1.4"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36"
|
||||
@ -30545,6 +30598,11 @@ unc-path-regex@^0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
|
||||
integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==
|
||||
|
||||
undefsafe@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
||||
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
||||
|
||||
underscore.string@^3.2.2, underscore.string@~3.3.4:
|
||||
version "3.3.6"
|
||||
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.6.tgz#ad8cf23d7423cb3b53b898476117588f4e2f9159"
|
||||
|
Loading…
Reference in New Issue
Block a user