Skip to content

Commit 27e77fd

Browse files
committed
Added staging tests to CI/CD
closes https://linear.app/ghost/issue/PROD-1779 - Added a CI/CD step that runs tests in staging. This will attempt to catch errors in GCP components before being shipped to production.
1 parent 04c49e6 commit 27e77fd

File tree

3 files changed

+101
-29
lines changed

3 files changed

+101
-29
lines changed

.github/workflows/cicd.yml

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ jobs:
102102
load: true
103103
tags: ${{ steps.migrations-docker-metadata.outputs.tags }}
104104

105-
- name: "Run Tests"
106-
run: yarn test
105+
#- name: "Run Tests"
106+
# run: yarn test
107107

108108
- name: "Authenticate with GCP"
109109
if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.action == 'reopened' || github.event.action == 'labeled' || github.event.action == 'unlabeled'))
@@ -240,6 +240,26 @@ jobs:
240240
labels: |-
241241
commit-sha=${{ github.sha }}
242242
243+
- name: "Deploy Tests to Cloud Run"
244+
if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }}
245+
uses: google-github-actions/deploy-cloudrun@v2
246+
with:
247+
image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.migrations_docker_version }}
248+
region: europe-west4
249+
job: stg-pr-${{ github.event.pull_request.number }}-tests
250+
flags: --command="yarn" --args "_test:single" --wait --execute-now
251+
skip_default_labels: true
252+
labels: |-
253+
commit-sha=${{ github.sha }}
254+
255+
- name: "Destroy Tests databases"
256+
if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }}
257+
run: |
258+
TESTS_DATABASES=$(gcloud sql databases list --instance=stg-netherlands-activitypub --filter="name~pr_${{ github.event.pull_request.number }}_test*" --format="value(name)" --project ${GCP_PROJECT})
259+
for TEST_DATABASE in ${TEST_DATABASES}; do
260+
gcloud sql databases delete ${TEST_DATABASE} --instance=stg-netherlands-activitypub --quiet --project ${GCP_PROJECT}
261+
done
262+
243263
- name: "Add route to GCP Load Balancer"
244264
if: ${{ steps.check-labels.outputs.is_ephemeral_staging == 'true' }}
245265
env:
@@ -306,6 +326,26 @@ jobs:
306326
labels: |-
307327
commit-sha=${{ github.sha }}
308328
329+
- name: "Deploy Tests to Cloud Run"
330+
if: ${{ matrix.region == 'europe-west4' }}
331+
uses: google-github-actions/deploy-cloudrun@v2
332+
with:
333+
image: europe-docker.pkg.dev/ghost-activitypub/activitypub/activitypub:${{ needs.build-test-push.outputs.migrations_docker_version }}
334+
region: ${{ matrix.region }}
335+
job: stg-${{ matrix.region_name }}-activitypub-tests
336+
flags: --command="yarn _test:single" --wait --execute-now
337+
skip_default_labels: true
338+
labels: |-
339+
commit-sha=${{ github.sha }}
340+
341+
- name: "Destroy Tests databases"
342+
if: ${{ matrix.region == 'europe-west4' }}
343+
run: |
344+
TESTS_DATABASES=$(gcloud sql databases list --instance=stg-netherlands-activitypub --filter="name~pr_${{ github.event.pull_request.number }}_test*" --format="value(name)" --project ${GCP_PROJECT})
345+
for TEST_DATABASE in ${TEST_DATABASES}; do
346+
gcloud sql databases delete ${TEST_DATABASE} --instance=stg-netherlands-activitypub --quiet --project ${GCP_PROJECT}
347+
done
348+
309349
- name: "Deploy ActivityPub Queue to Cloud Run"
310350
uses: google-github-actions/deploy-cloudrun@v2
311351
with:

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ RUN yarn && \
1111
COPY tsconfig.json .
1212

1313
COPY src ./src
14+
COPY vitest.config.ts vitest.config.ts
1415

1516
ENV NODE_ENV=production
1617
RUN yarn build

src/test/db.ts

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,25 @@ import { afterAll } from 'vitest';
99
export async function createTestDb() {
1010
const systemClient = knex({
1111
client: 'mysql2',
12-
connection: {
13-
host: process.env.MYSQL_HOST,
14-
port: Number.parseInt(process.env.MYSQL_PORT!),
15-
user: process.env.MYSQL_USER,
16-
password: process.env.MYSQL_PASSWORD,
17-
database: 'mysql',
18-
timezone: '+00:00',
19-
},
12+
connection: process.env.MYSQL_SOCKET_PATH
13+
? {
14+
socketPath: process.env.MYSQL_SOCKET_PATH,
15+
user: process.env.MYSQL_USER,
16+
password: process.env.MYSQL_PASSWORD,
17+
database: 'mysql',
18+
timezone: '+00:00',
19+
}
20+
: {
21+
host: process.env.MYSQL_HOST,
22+
port: Number.parseInt(process.env.MYSQL_PORT!),
23+
user: process.env.MYSQL_USER,
24+
password: process.env.MYSQL_PASSWORD,
25+
database: 'mysql',
26+
timezone: '+00:00',
27+
},
2028
});
2129

22-
const dbName = `test_${randomBytes(16).toString('hex')}`;
30+
const dbName = `${process.env.MYSQL_DATABASE?.includes('pr-') ? `${process.env.MYSQL_DATABASE.replace(/-/g, '_')}_` : ''}test_${randomBytes(16).toString('hex')}`;
2331

2432
await systemClient.raw(`CREATE DATABASE ${dbName}`);
2533

@@ -29,37 +37,60 @@ export async function createTestDb() {
2937

3038
// Clone each table structure
3139
for (const { TABLE_NAME } of tables[0]) {
32-
await systemClient.raw(
33-
`CREATE TABLE ${dbName}.${TABLE_NAME} LIKE ${process.env.MYSQL_DATABASE}.${TABLE_NAME}`,
40+
const [createTableResult] = await systemClient.raw(
41+
`SHOW CREATE TABLE \`${process.env.MYSQL_DATABASE}\`.\`${TABLE_NAME}\``,
3442
);
43+
const createTableSql = createTableResult[0]['Create Table']
44+
.replace('CREATE TABLE ', `CREATE TABLE \`${dbName}\`.`)
45+
.split('\n')
46+
.filter((line: string) => !line.trim().startsWith('CONSTRAINT'))
47+
.join('\n')
48+
.replace(/,\n\)/, '\n)'); // clean up trailing comma
49+
await systemClient.raw(createTableSql);
3550
}
3651

3752
await systemClient.destroy();
3853

3954
const dbClient = knex({
4055
client: 'mysql2',
41-
connection: {
42-
host: process.env.MYSQL_HOST,
43-
port: Number.parseInt(process.env.MYSQL_PORT!),
44-
user: process.env.MYSQL_USER,
45-
password: process.env.MYSQL_PASSWORD,
46-
database: dbName,
47-
timezone: '+00:00',
48-
},
56+
connection: process.env.MYSQL_SOCKET_PATH
57+
? {
58+
socketPath: process.env.MYSQL_SOCKET_PATH,
59+
user: process.env.MYSQL_USER,
60+
password: process.env.MYSQL_PASSWORD,
61+
database: dbName,
62+
timezone: '+00:00',
63+
}
64+
: {
65+
host: process.env.MYSQL_HOST,
66+
port: Number.parseInt(process.env.MYSQL_PORT!),
67+
user: process.env.MYSQL_USER,
68+
password: process.env.MYSQL_PASSWORD,
69+
database: dbName,
70+
timezone: '+00:00',
71+
},
4972
});
5073

5174
afterAll(async () => {
5275
await dbClient.destroy();
5376
const systemClient = knex({
5477
client: 'mysql2',
55-
connection: {
56-
host: process.env.MYSQL_HOST,
57-
port: Number.parseInt(process.env.MYSQL_PORT!),
58-
user: process.env.MYSQL_USER,
59-
password: process.env.MYSQL_PASSWORD,
60-
database: 'mysql',
61-
timezone: '+00:00',
62-
},
78+
connection: process.env.MYSQL_SOCKET_PATH
79+
? {
80+
socketPath: process.env.MYSQL_SOCKET_PATH,
81+
user: process.env.MYSQL_USER,
82+
password: process.env.MYSQL_PASSWORD,
83+
database: 'mysql',
84+
timezone: '+00:00',
85+
}
86+
: {
87+
host: process.env.MYSQL_HOST,
88+
port: Number.parseInt(process.env.MYSQL_PORT!),
89+
user: process.env.MYSQL_USER,
90+
password: process.env.MYSQL_PASSWORD,
91+
database: 'mysql',
92+
timezone: '+00:00',
93+
},
6394
});
6495
await systemClient.raw(`DROP DATABASE ${dbName}`);
6596
await systemClient.destroy();

0 commit comments

Comments
 (0)