Skip to content

Commit f4f6a6a

Browse files
committed
ops: draft CD workflow
1 parent aba6f8c commit f4f6a6a

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed

.github/workflows/cd.yaml

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
name: 'ticketing CD'
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
workflow_conclusion:
7+
description: 'Status of workflow trigerring s1seven CD'
8+
required: false
9+
default: 'success'
10+
type: string
11+
tag:
12+
description: 'Release tag, semver formatted prefixed with "v"'
13+
required: false
14+
default: ''
15+
type: string
16+
environment:
17+
description: 'Deployment environment'
18+
required: false
19+
type: string
20+
default: ''
21+
22+
workflow_dispatch:
23+
inputs:
24+
workflow_conclusion:
25+
description: 'Status of workflow trigerring s1seven CD'
26+
required: false
27+
default: 'success'
28+
tag:
29+
description: 'Release tag, semver formatted prefixed with "v"'
30+
required: false
31+
default: ''
32+
environment:
33+
description: 'Deployment environment'
34+
required: false
35+
type: choice
36+
default: ''
37+
options:
38+
- development
39+
- staging
40+
- production
41+
42+
concurrency:
43+
group: ${{ github.workflow }}-${{ github.ref }}-cd
44+
cancel-in-progress: true
45+
46+
env:
47+
ACTIONS_STEP_DEBUG: ${{ vars.ACTIONS_STEP_DEBUG }}
48+
NODE_VERSION: ${{ vars.NODE_VERSION || '18.x' }}
49+
STEP_SETUP_PROJECT: 'Setup node, checkout and install project dependencies'
50+
BUILD_FOLDER: dist
51+
BUILD_ARTIFACTS: build
52+
CI_WORKFLOW: ci.yaml
53+
54+
jobs:
55+
init:
56+
runs-on: ubuntu-latest
57+
if: |
58+
github.event.workflow_run.conclusion == 'success' ||
59+
github.event.inputs.workflow_conclusion == 'success'
60+
61+
steps:
62+
- uses: actions/checkout@v3
63+
with:
64+
fetch-depth: 0
65+
66+
- name: Check that tag matches a valid release
67+
id: check-tag
68+
env:
69+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70+
run: |
71+
if [[ -z "${{ inputs.tag }}" || "${{ inputs.tag }}" == "" ]]; then
72+
echo "is-valid=false" >> $GITHUB_OUTPUT
73+
elif [[ $(git tag -l "${{ inputs.tag }}") =~ "${{ inputs.tag }}" ]]; then
74+
echo "is-valid=true" >> $GITHUB_OUTPUT
75+
else
76+
echo "is-valid=false" >> $GITHUB_OUTPUT
77+
fi
78+
79+
- name: Get branch names
80+
id: branch-name
81+
uses: tj-actions/branch-names@v6
82+
83+
- name: Generate tag
84+
id: current-tag
85+
run: |
86+
if [[ "${{ steps.check-tag.outputs.is-valid }}" == "true" ]]; then
87+
echo "value=${{ inputs.tag }}" >> $GITHUB_OUTPUT
88+
elif [[ "${{ steps.branch-name.outputs.is_tag }}" == "true" ]]; then
89+
echo "value=${{ steps.branch-name.outputs.tag }}" >> $GITHUB_OUTPUT
90+
elif [[ -z "${{ inputs.tag }}" || "${{ inputs.tag }}" == "" ]]; then
91+
echo "value=$(echo nx_successful_ci_run__$(echo ${{ github.run_id }})__$(date +"%Y-%m-%d-%H%M")-UTC)" >> $GITHUB_OUTPUT
92+
else
93+
# an invalid tag was passed to the workflow
94+
exit 1
95+
fi
96+
97+
- name: Check if source event is 'release'
98+
uses: haya14busa/action-cond@v1
99+
id: is-release
100+
with:
101+
cond: ${{ startsWith(steps.current-tag.outputs.value, 'v') }}
102+
if_true: 'true'
103+
if_false: 'false'
104+
105+
- name: Get Git ref
106+
uses: haya14busa/action-cond@v1
107+
id: git-ref
108+
with:
109+
cond: ${{ steps.is-release.outputs.value == 'true' || steps.branch-name.outputs.is_tag == 'true' }}
110+
if_true: ${{ steps.current-tag.outputs.value }}
111+
if_false: ${{ steps.branch-name.outputs.current_branch }}
112+
113+
- name: ${{ env.STEP_SETUP_PROJECT }}
114+
id: setup
115+
uses: ./.github/actions/checkout-and-yarn
116+
with:
117+
fetch-depth: 0
118+
fetch-ref: ${{ steps.git-ref.outputs.value }}
119+
node-version: ${{ env.NODE_VERSION }}
120+
token: ${{ secrets.GITHUB_TOKEN }}
121+
122+
- name: Get reference workflow
123+
uses: haya14busa/action-cond@v1
124+
id: ref-workflow
125+
with:
126+
cond: ${{ github.event_name == 'workflow_dispatch' }}
127+
if_true: ${{ env.CD_WORKFLOW }}
128+
if_false: ${{ env.CI_WORKFLOW }}
129+
130+
- name: Derive appropriate SHAs for base and head for `nx affected` commands
131+
id: set-shas
132+
uses: nrwl/nx-set-shas@v3
133+
with:
134+
# solves issue where no projects are affected after merging to main
135+
workflow-id: ${{ steps.ref-workflow.outputs.value }}
136+
137+
- name: Check if any project was affected
138+
id: check-projects
139+
run: |
140+
if [[ $(npx nx affected:apps --exclude=workspace --plain) == "" && \
141+
$(npx nx affected:libs --exclude=workspace --plain) == "" ]]; then
142+
echo "affected=false" >> $GITHUB_OUTPUT
143+
else
144+
echo "affected=true" >> $GITHUB_OUTPUT
145+
fi
146+
147+
- name: Find depth of NX_BASE
148+
id: commit-depth
149+
run: echo "depth=$(git rev-list HEAD ^${{ env.NX_BASE }} --count)" >> $GITHUB_OUTPUT
150+
151+
- name: List apps (affected or all if release)
152+
id: check-apps
153+
env:
154+
NX_BASE: ${{ steps.set-shas.outputs.base }}
155+
NX_HEAD: ${{ steps.set-shas.outputs.head }}
156+
run: |
157+
if [[ "${{ steps.is-release.outputs.value }}" == "true" ]]; then
158+
echo "affected=$(yarn ts-node-tools tools/nx/get-projects-cli.ts -t app)" >> $GITHUB_OUTPUT
159+
else
160+
echo "affected=$(yarn affected:apps | node -e "
161+
const input = require('fs').readFileSync(0).toString().trim();
162+
const result = JSON.stringify(input === '' ? [] : input.split(','));
163+
console.log(result);
164+
")" >> $GITHUB_OUTPUT
165+
fi
166+
167+
- name: Define environment to deploy
168+
id: deploy
169+
run: |
170+
if [[ "${{ inputs.environment }}" != "" ]]; then
171+
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
172+
elif [[ "${{ steps.is-release.outputs.value }}" == "true" ]]; then
173+
echo "environment=staging" >> $GITHUB_OUTPUT
174+
else
175+
echo "environment=development" >> $GITHUB_OUTPUT
176+
fi
177+
178+
- name: Show outputs
179+
run: |
180+
echo "current-branch: ${{ steps.branch-name.outputs.current_branch }}"
181+
echo "projects-affected: ${{ steps.check-projects.outputs.affected }}"
182+
echo "deploy-config: ${{ steps.deploy.outputs.environment }}"
183+
echo "is-release: ${{ steps.is-release.outputs.value }}"
184+
echo "tag: ${{ steps.current-tag.outputs.value }}"
185+
echo "git-ref: ${{ steps.git-ref.outputs.value }}"
186+
echo "ref-workflow: ${{ steps.ref-workflow.outputs.value }}"
187+
188+
- name: Create job summary
189+
run: |
190+
echo "## CD initialized! :rocket:" >> $GITHUB_STEP_SUMMARY
191+
echo "- deploy config: ${{ steps.deploy.outputs.environment }}" >> $GITHUB_STEP_SUMMARY
192+
echo "- base commit SHA: ${{ steps.set-shas.outputs.base }}" >> $GITHUB_STEP_SUMMARY
193+
echo "- head commit SHA: ${{ steps.set-shas.outputs.head }}" >> $GITHUB_STEP_SUMMARY
194+
echo "- current branch: ${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_STEP_SUMMARY
195+
echo "- tag: ${{ steps.current-tag.outputs.value }}" >> $GITHUB_STEP_SUMMARY
196+
echo "- git ref: ${{ steps.git-ref.outputs.value }}" >> $GITHUB_STEP_SUMMARY
197+
echo "- reference workflow: ${{ steps.ref-workflow.outputs.value }}" >> $GITHUB_STEP_SUMMARY
198+
echo "- is release ?: ${{ steps.is-release.outputs.value }}" >> $GITHUB_STEP_SUMMARY
199+
echo "- affected apps: ${{ steps.check-apps.outputs.affected }}" >> $GITHUB_STEP_SUMMARY
200+
201+
outputs:
202+
current-branch: ${{ steps.branch-name.outputs.current_branch }}
203+
tag: ${{ steps.current-tag.outputs.value }}
204+
git-ref: ${{ steps.git-ref.outputs.value }}
205+
ref-workflow: ${{ steps.ref-workflow.outputs.value }}
206+
base: ${{ steps.set-shas.outputs.base }}
207+
head: ${{ steps.set-shas.outputs.head }}
208+
base-depth: ${{ steps.commit-depth.outputs.depth}}
209+
deploy-config: ${{ steps.deploy.outputs.environment }}
210+
is-release: ${{ steps.is-release.outputs.value }}
211+
projects-affected: ${{ steps.check-projects.outputs.affected }}
212+
affected-apps: ${{ steps.check-apps.outputs.affected }}
213+
214+
docker:
215+
needs: [init]
216+
runs-on: ubuntu-latest
217+
if: |
218+
needs.init.outputs.projects-affected == 'true' &&
219+
needs.init.outputs.is-release == 'false'
220+
timeout-minutes: 20
221+
# TODO: disable once docker build is stable again
222+
continue-on-error: true
223+
env:
224+
NX_BASE: ${{ needs.init.outputs.base }}
225+
NX_HEAD: ${{ needs.init.outputs.head }}
226+
227+
steps:
228+
- uses: actions/checkout@v3
229+
with:
230+
ref: ${{ needs.init.outputs.git-ref }}
231+
232+
- name: ${{ env.STEP_SETUP_PROJECT }}
233+
uses: ./.github/actions/checkout-and-yarn
234+
id: setup
235+
with:
236+
fetch-depth: 0
237+
fetch-ref: ${{ needs.init.outputs.git-ref }}
238+
node-version: ${{ env.NODE_VERSION }}
239+
token: ${{ secrets.GITHUB_TOKEN }}
240+
241+
# When building images in CI runner
242+
- name: Retrieve build output from external workflow
243+
if: github.event_name == 'workflow_dispatch'
244+
uses: dawidd6/action-download-artifact@v2
245+
with:
246+
github_token: ${{ secrets.GITHUB_TOKEN }}
247+
branch: ${{ needs.init.outputs.current-branch }}
248+
workflow: ${{ env.CI_WORKFLOW }}
249+
name: ${{ env.BUILD_ARTIFACTS }}
250+
path: ${{ env.BUILD_FOLDER }}
251+
if_no_artifact_found: warn
252+
253+
- name: Check build outputs presence
254+
uses: xSAVIKx/artifact-exists-action@v0
255+
id: check-build-artifact
256+
with:
257+
name: ${{ env.BUILD_ARTIFACTS }}
258+
259+
- name: Retrieve build output from current workflow
260+
if: github.event_name != 'workflow_dispatch' && steps.check-build-artifact.outputs.exists == 'true'
261+
uses: actions/download-artifact@v3
262+
with:
263+
name: ${{ env.BUILD_ARTIFACTS }}
264+
path: ${{ env.BUILD_FOLDER }}
265+
266+
- name: Check build artifact existence
267+
id: check-build
268+
uses: andstor/file-existence-action@v2
269+
with:
270+
files: ${{ env.BUILD_FOLDER }}
271+
272+
- if: steps.check-build.outputs.files_exists != 'true'
273+
run: exit 1
274+
275+
- name: Login to GitHub Container Registry
276+
uses: docker/login-action@v2
277+
with:
278+
registry: ghcr.io
279+
username: ${{ github.actor }}
280+
password: ${{ secrets.GITHUB_TOKEN }}
281+
282+
- name: Set up QEMU
283+
uses: docker/setup-qemu-action@v3
284+
with:
285+
platforms: arm64
286+
287+
- name: Set up Docker Buildx
288+
uses: docker/setup-buildx-action@v3
289+
with:
290+
platforms: linux/amd64,linux/arm64
291+
use: true
292+
293+
- name: Run Docker build
294+
run: yarn nx run-many --target=docker-build-remote --projects=$(yarn get:apps) --parallel=4 --verbose
295+
env:
296+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
297+
298+
# - name: Cancel workflow on failure
299+
# if: failure()
300+
# uses: andymckay/cancel-action@0.3

commitlint.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ const typeEnumDescription = {
6868
title: 'Continuous Integrations',
6969
emoji: '🏭',
7070
},
71+
ops: {
72+
description:
73+
'Changes to our CI / CD configuration files and scripts (example scopes: GH Actions, Heroku, Docker...)',
74+
title: 'DevOps',
75+
emoji: '🏭',
76+
},
7177
chore: {
7278
description: "Other changes that don't modify src or test files",
7379
title: 'Chores',

0 commit comments

Comments
 (0)