Skip to content

Commit baa6516

Browse files
committed
ci: add affected module workflows and scoped image publishing
1 parent 893b2d4 commit baa6516

12 files changed

Lines changed: 772 additions & 143 deletions

File tree

.github/workflows/ci.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches-ignore:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
detect:
16+
runs-on: ubuntu-latest
17+
outputs:
18+
node_packages_json: ${{ steps.affected.outputs.node_packages_json }}
19+
go_packages_json: ${{ steps.affected.outputs.go_packages_json }}
20+
has_node: ${{ steps.affected.outputs.has_node }}
21+
has_go: ${{ steps.affected.outputs.has_go }}
22+
has_code: ${{ steps.affected.outputs.has_code }}
23+
steps:
24+
- uses: actions/checkout@v6
25+
with:
26+
fetch-depth: 0
27+
28+
- uses: actions/setup-node@v6
29+
with:
30+
node-version: 24
31+
32+
- name: Detect affected modules
33+
id: affected
34+
run: |
35+
if [ "${{ github.event_name }}" = "pull_request" ]; then
36+
node scripts/ci/affected-modules.mjs --base "${{ github.event.pull_request.base.sha }}" --head "${{ github.event.pull_request.head.sha }}"
37+
else
38+
node scripts/ci/affected-modules.mjs --base "${{ github.event.before }}" --head "${{ github.sha }}"
39+
fi
40+
41+
node-checks:
42+
needs: detect
43+
if: needs.detect.outputs.has_node == 'true'
44+
runs-on: ubuntu-latest
45+
strategy:
46+
fail-fast: false
47+
matrix: ${{ fromJson(needs.detect.outputs.node_packages_json) }}
48+
steps:
49+
- uses: actions/checkout@v6
50+
51+
- uses: pnpm/action-setup@v5
52+
53+
- uses: actions/setup-node@v6
54+
with:
55+
node-version: 24
56+
cache: pnpm
57+
58+
- name: Install dependencies
59+
run: pnpm install --frozen-lockfile
60+
61+
- name: Prepare env files
62+
run: |
63+
cp .env.example .env.dev
64+
cp .env.example .env.prod
65+
cp .env.example .env.test
66+
sed -i 's/NODE_ENV=development/NODE_ENV=test/' .env.test
67+
sed -i 's/NODE_ENV=development/NODE_ENV=production/' .env.prod
68+
69+
- name: Check format
70+
run: pnpm --filter "${{ matrix.package }}" --if-present run format:check
71+
72+
- name: Lint
73+
run: pnpm --filter "${{ matrix.package }}" --if-present run lint
74+
75+
- name: Typecheck
76+
run: pnpm --filter "${{ matrix.package }}" --if-present run typecheck
77+
78+
- name: Test
79+
run: pnpm env:test -- "pnpm --filter '${{ matrix.package }}' --if-present run test"
80+
81+
- name: Build
82+
run: pnpm env:prod -- "pnpm --filter '${{ matrix.package }}...' --if-present run build"
83+
84+
go-checks:
85+
needs: detect
86+
if: needs.detect.outputs.has_go == 'true'
87+
runs-on: ubuntu-latest
88+
strategy:
89+
fail-fast: false
90+
matrix: ${{ fromJson(needs.detect.outputs.go_packages_json) }}
91+
steps:
92+
- uses: actions/checkout@v6
93+
94+
- uses: pnpm/action-setup@v5
95+
96+
- uses: actions/setup-node@v6
97+
with:
98+
node-version: 24
99+
100+
- uses: actions/setup-go@v6
101+
with:
102+
go-version-file: ${{ matrix.path }}/go.mod
103+
cache-dependency-path: ${{ matrix.path }}/go.sum
104+
105+
- name: Check format
106+
run: pnpm --filter "${{ matrix.package }}" run format:check
107+
108+
- name: Lint
109+
run: pnpm --filter "${{ matrix.package }}" run lint
110+
111+
- name: Test
112+
run: pnpm --filter "${{ matrix.package }}" run test
113+
114+
- name: Build
115+
run: pnpm --filter "${{ matrix.package }}" run build

.github/workflows/main.yml

Lines changed: 151 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,78 @@ on:
77

88
permissions:
99
contents: read
10+
pull-requests: read
1011

1112
env:
1213
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
1314
IMAGE_TAG: ${{ github.sha }}
1415

1516
jobs:
16-
verify-build-publish:
17+
detect:
1718
runs-on: ubuntu-latest
19+
outputs:
20+
node_packages_json: ${{ steps.affected.outputs.node_packages_json }}
21+
go_packages_json: ${{ steps.affected.outputs.go_packages_json }}
22+
docker_images_json: ${{ steps.affected.outputs.docker_images_json }}
23+
has_node: ${{ steps.affected.outputs.has_node }}
24+
has_go: ${{ steps.affected.outputs.has_go }}
25+
has_code: ${{ steps.affected.outputs.has_code }}
26+
has_docker: ${{ steps.affected.outputs.has_docker }}
27+
deploy_cloudflare: ${{ steps.affected.outputs.deploy_cloudflare }}
28+
notify_deployer: ${{ steps.affected.outputs.notify_deployer }}
29+
needs_db_migrate: ${{ steps.affected.outputs.needs_db_migrate }}
30+
needs_infra_sync: ${{ steps.affected.outputs.needs_infra_sync }}
31+
needs_deployer_update: ${{ steps.affected.outputs.needs_deployer_update }}
32+
needs_env_manual_review: ${{ steps.affected.outputs.needs_env_manual_review }}
33+
changed_files: ${{ steps.affected.outputs.changed_files }}
34+
is_pr_merge: ${{ steps.pr.outputs.is_pr_merge }}
1835
steps:
1936
- uses: actions/checkout@v6
2037
with:
2138
fetch-depth: 0
2239

23-
- uses: pnpm/action-setup@v6
40+
- uses: actions/setup-node@v6
41+
with:
42+
node-version: 24
43+
44+
- name: Detect affected modules
45+
id: affected
46+
run: node scripts/ci/affected-modules.mjs --base "${{ github.event.before }}" --head "${{ github.sha }}"
47+
48+
- name: Detect associated pull request
49+
id: pr
50+
env:
51+
GITHUB_TOKEN: ${{ github.token }}
52+
run: |
53+
node <<'NODE' >> "$GITHUB_OUTPUT"
54+
const response = await fetch(`${process.env.GITHUB_API_URL}/repos/${process.env.GITHUB_REPOSITORY}/commits/${process.env.GITHUB_SHA}/pulls`, {
55+
headers: {
56+
accept: 'application/vnd.github+json',
57+
authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
58+
'x-github-api-version': '2022-11-28',
59+
},
60+
});
61+
const pulls = response.ok ? await response.json() : [];
62+
console.log(`is_pr_merge=${Array.isArray(pulls) && pulls.length > 0}`);
63+
NODE
64+
65+
node-checks:
66+
needs: detect
67+
if: needs.detect.outputs.has_node == 'true' && needs.detect.outputs.is_pr_merge != 'true'
68+
runs-on: ubuntu-latest
69+
strategy:
70+
fail-fast: false
71+
matrix: ${{ fromJson(needs.detect.outputs.node_packages_json) }}
72+
steps:
73+
- uses: actions/checkout@v6
74+
75+
- uses: pnpm/action-setup@v5
2476

2577
- uses: actions/setup-node@v6
2678
with:
2779
node-version: 24.15.0
2880
cache: pnpm
2981

30-
- uses: actions/setup-go@v6
31-
with:
32-
go-version-file: apps/worker/go.mod
33-
cache-dependency-path: |
34-
apps/worker/go.sum
35-
apps/deployer/go.sum
36-
3782
- name: Install dependencies
3883
run: pnpm install --frozen-lockfile
3984

@@ -46,22 +91,69 @@ jobs:
4691
sed -i 's/NODE_ENV=development/NODE_ENV=production/' .env.prod
4792
4893
- name: Check format
49-
run: pnpm run format:check
94+
run: pnpm --filter "${{ matrix.package }}" --if-present run format:check
5095

5196
- name: Lint
52-
run: pnpm run lint
97+
run: pnpm --filter "${{ matrix.package }}" --if-present run lint
5398

5499
- name: Typecheck
55-
run: pnpm run typecheck
100+
run: pnpm --filter "${{ matrix.package }}" --if-present run typecheck
56101

57102
- name: Test
58-
run: pnpm run test
103+
run: pnpm env:test -- "pnpm --filter '${{ matrix.package }}' --if-present run test"
104+
105+
- name: Build
106+
run: pnpm env:prod -- "pnpm --filter '${{ matrix.package }}...' --if-present run build"
107+
108+
go-checks:
109+
needs: detect
110+
if: needs.detect.outputs.has_go == 'true' && needs.detect.outputs.is_pr_merge != 'true'
111+
runs-on: ubuntu-latest
112+
strategy:
113+
fail-fast: false
114+
matrix: ${{ fromJson(needs.detect.outputs.go_packages_json) }}
115+
steps:
116+
- uses: actions/checkout@v6
117+
118+
- uses: pnpm/action-setup@v5
119+
120+
- uses: actions/setup-node@v6
121+
with:
122+
node-version: 24
123+
124+
- uses: actions/setup-go@v6
125+
with:
126+
go-version-file: ${{ matrix.path }}/go.mod
127+
cache-dependency-path: ${{ matrix.path }}/go.sum
128+
129+
- name: Check format
130+
run: pnpm --filter "${{ matrix.package }}" run format:check
131+
132+
- name: Lint
133+
run: pnpm --filter "${{ matrix.package }}" run lint
134+
135+
- name: Test
136+
run: pnpm --filter "${{ matrix.package }}" run test
137+
138+
- name: Build
139+
run: pnpm --filter "${{ matrix.package }}" run build
59140

60-
- name: Build apps
61-
run: pnpm run build
141+
publish-images:
142+
needs:
143+
- detect
144+
- node-checks
145+
- go-checks
146+
if: always() && needs.detect.outputs.has_docker == 'true' && needs.detect.result == 'success' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
147+
runs-on: ubuntu-latest
148+
strategy:
149+
fail-fast: false
150+
matrix: ${{ fromJson(needs.detect.outputs.docker_images_json) }}
151+
steps:
152+
- uses: actions/checkout@v6
62153

63-
- name: Build Cloudflare worker
64-
run: pnpm run build:cf
154+
- uses: actions/setup-node@v6
155+
with:
156+
node-version: 24
65157

66158
- uses: docker/setup-buildx-action@v4
67159

@@ -70,32 +162,53 @@ jobs:
70162
username: ${{ secrets.DOCKERHUB_USERNAME }}
71163
password: ${{ secrets.DOCKERHUB_TOKEN }}
72164

73-
- name: Build and push API image
165+
- name: Build and push ${{ matrix.id }} image
74166
uses: docker/build-push-action@v7
75167
with:
76168
context: .
77-
file: apps/api/Dockerfile
169+
file: ${{ matrix.dockerfile }}
78170
push: true
79171
tags: |
80-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-api:${{ env.IMAGE_TAG }}
81-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-api:main
172+
docker.io/${{ env.DOCKERHUB_USERNAME }}/${{ matrix.image }}:${{ env.IMAGE_TAG }}
173+
docker.io/${{ env.DOCKERHUB_USERNAME }}/${{ matrix.image }}:main
174+
175+
- name: Prune ${{ matrix.id }} image history
176+
env:
177+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
178+
DOCKERHUB_REPOSITORY: ${{ matrix.image }}
179+
DOCKERHUB_KEEP_TAGS: "10"
180+
run: node scripts/ci/prune-dockerhub-tags.mjs
181+
182+
deploy-cloudflare:
183+
needs:
184+
- detect
185+
- node-checks
186+
- go-checks
187+
if: always() && needs.detect.outputs.deploy_cloudflare == 'true' && needs.detect.result == 'success' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
188+
runs-on: ubuntu-latest
189+
env:
190+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
191+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
192+
steps:
193+
- uses: actions/checkout@v6
82194

83-
- name: Build and push web image
84-
uses: docker/build-push-action@v7
85-
with:
86-
context: .
87-
file: apps/web/Dockerfile
88-
push: true
89-
tags: |
90-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-web:${{ env.IMAGE_TAG }}
91-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-web:main
195+
- uses: pnpm/action-setup@v5
92196

93-
- name: Build and push worker image
94-
uses: docker/build-push-action@v7
197+
- uses: actions/setup-node@v6
95198
with:
96-
context: .
97-
file: apps/worker/Dockerfile
98-
push: true
99-
tags: |
100-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-worker:${{ env.IMAGE_TAG }}
101-
docker.io/${{ env.DOCKERHUB_USERNAME }}/zhblogs-worker:main
199+
node-version: 24
200+
cache: pnpm
201+
202+
- name: Install dependencies
203+
run: pnpm install --frozen-lockfile
204+
205+
- name: Prepare env files
206+
run: |
207+
cp .env.example .env.dev
208+
cp .env.example .env.prod
209+
cp .env.example .env.test
210+
sed -i 's/NODE_ENV=development/NODE_ENV=test/' .env.test
211+
sed -i 's/NODE_ENV=development/NODE_ENV=production/' .env.prod
212+
213+
- name: Deploy Cloudflare worker
214+
run: pnpm env:prod -- "CLOUDFLARE_INCLUDE_PROCESS_ENV=true pnpm -F @zhblogs/cloudflare run deploy"

0 commit comments

Comments
 (0)