Skip to content

Commit 54da3ca

Browse files
authored
docs build and deploy to aws (#57)
New build workflow doesn't affect any existing configurations until defined explicitly
1 parent 3ba18dc commit 54da3ca

File tree

1 file changed

+362
-0
lines changed

1 file changed

+362
-0
lines changed
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
name: Docs Build Push AWS
2+
3+
on:
4+
workflow_call:
5+
secrets:
6+
AZURE_VAULT_CLIENT_ID:
7+
required: true
8+
AZURE_VAULT_SUBSCRIPTION_ID:
9+
required: true
10+
AZURE_VAULT_TENANT_ID:
11+
required: true
12+
DOCS_VAULTNAME:
13+
required: true
14+
inputs:
15+
environment:
16+
description: "This will be appended to the baseURL for for production builds. For example, main docs will be `/` where agent would be `/nginx-agent`"
17+
required: false
18+
default: preview
19+
type: string
20+
production_url_path:
21+
description: "This will be appended to the baseURL for for production builds. For example, main docs will be `/` where agent would be `/nginx-agent`"
22+
required: true
23+
type: string
24+
preview_url_path:
25+
description: "Appended to the baseURL for PR preview builds"
26+
required: true
27+
type: string
28+
docs_source_path:
29+
description: "Directory of built docs files. Hugo default would be `./public/`"
30+
required: true
31+
type: string
32+
docs_build_path:
33+
description: "Directory where hugo or sphinx build command should be run from"
34+
required: false
35+
type: string
36+
default: ./
37+
doc_type:
38+
type: string
39+
description: "Type of source docs. Currently supports 'hugo' and 'sphinx'"
40+
default: hugo
41+
force_hugo_theme_version:
42+
type: string
43+
description: "Overrides default of latest hugo theme. Useful for testing pre-release versions. Must start with 'v' before version."
44+
default: ""
45+
auto_deploy_branch:
46+
type: string
47+
description: "A branch specified here will autodeploy to the environment specified in auto_deploy_env. An on.push event for this branch must be specified in caller."
48+
auto_deploy_env:
49+
type: string
50+
description: "Env to which auto_deploy_branch will be deployed to. Preview is not supported."
51+
node_dep:
52+
type: boolean
53+
description: "Defines should we run npm ci or not"
54+
outputs:
55+
PREVIEW_URL:
56+
description: String representing the URL to preview the build
57+
value: ${{ jobs.build.outputs.PREVIEW_URL }}
58+
59+
permissions:
60+
id-token: write
61+
contents: read
62+
63+
env:
64+
GO_VERISON: "1.21" # Go version used for `hugo mod get`
65+
HUGO_VERSION: "0.147.8" # Hugo version used for building docs
66+
THEME_MODULE: "github.com/nginxinc/nginx-hugo-theme/v2" # Name of source repo for module. For example; github.com/nginxinc/nginx-hugo-theme
67+
68+
PR_NUMBER: ${{github.event.pull_request.number}}
69+
70+
# environments
71+
DOMAIN_PREVIEW: "internal-beta-docs.nginx.com"
72+
DOMAIN_DEV: "docs-dev.nginx.com"
73+
DOMAIN_STAGING: "docs-staging.nginx.com"
74+
DOMAIN_PROD: "docs.nginx.com"
75+
DOMAIN_UNIT: "unit.nginx.org"
76+
77+
jobs:
78+
checks:
79+
name: Checks and variables
80+
runs-on: ubuntu-24.04
81+
permissions:
82+
contents: read
83+
outputs:
84+
forked_workflow: ${{ steps.vars.outputs.forked_workflow }}
85+
steps:
86+
- name: Checkout Repository
87+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
88+
89+
- name: Set Variables
90+
id: vars
91+
run: |
92+
echo "forked_workflow=${{ (github.event.pull_request && github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name) || !(startsWith(github.repository, 'nginx/') || startsWith(github.repository, 'nginxinc/')) }}" >> $GITHUB_OUTPUT
93+
- name: Output variables
94+
run: |
95+
echo forked_workflow: ${{ steps.vars.outputs.forked_workflow }}
96+
build:
97+
needs: [checks]
98+
if: ${{ needs.checks.outputs.forked_workflow == 'false' }}
99+
runs-on: ubuntu-24.04
100+
# environment is needed for azure key vault federated credentials
101+
environment: preview
102+
outputs:
103+
PREVIEW_URL: ${{ steps.summary.outputs.PREVIEW_URL }}
104+
env:
105+
# Remapping of inputs to envs
106+
PRODUCTION_URL_PATH: ${{inputs.production_url_path}}
107+
PREVIEW_URL_PATH: ${{inputs.preview_url_path}}
108+
DOCS_SOURCE_PATH: ${{inputs.docs_source_path}}
109+
EVENT_ACTION: ${{github.event.action}}
110+
DEPLOYMENT_ENV: ${{inputs.environment}}
111+
THEME_VERSION: ${{inputs.force_hugo_theme_version}}
112+
AUTO_DEPLOY_BRANCH: ${{inputs.auto_deploy_branch}}
113+
AUTO_DEPLOY_ENV: ${{inputs.auto_deploy_env}}
114+
115+
concurrency:
116+
group: ${{ github.workflow }}-${{ github.ref }}
117+
steps:
118+
- name: Check and setup auto-deploy
119+
# Auto deploy should only trigger when the auto_deploy_branch match the current ref.
120+
# We also check if `environment` has already been set, otherwise this flow could cause
121+
# manual triggers to deploy to `auto_deploy_env` instead of the one specified in the trigger.
122+
if: (inputs.auto_deploy_branch == github.ref_name) && inputs.environment == ''
123+
run: |
124+
echo "Auto deploy branch ($AUTO_DEPLOY_BRANCH) matches current branch. Attempting autodeploy to $AUTO_DEPLOY_ENV."
125+
echo "DEPLOYMENT_ENV=${AUTO_DEPLOY_ENV}" >> $GITHUB_ENV
126+
127+
- name: Validate environment inputs
128+
run: |
129+
if [[ -z "${DEPLOYMENT_ENV}" ]]; then
130+
# for use in this step
131+
export DEPLOYMENT_ENV="preview"
132+
133+
# for use in subsequent steps
134+
echo "DEPLOYMENT_ENV=preview" >> "$GITHUB_ENV"
135+
fi
136+
137+
if [[ ! "${DEPLOYMENT_ENV}" =~ ^(dev|staging|prod|preview)$ ]]; then
138+
echo "::error::Invalid environment input: ${DEPLOYMENT_ENV}. Must be one of dev, staging, prod, preview"
139+
exit 1
140+
fi
141+
142+
if [[ "${DEPLOYMENT_ENV}" == "preview" && -z "${PR_NUMBER}" ]]; then
143+
echo "PR_NUMBER=${GITHUB_SHA::7}" >> "$GITHUB_ENV"
144+
echo "::notice::PR_NUMBER set to short commit hash: ${GITHUB_SHA::7}"
145+
fi
146+
147+
if [[ -z "${GITHUB_SHA}" ]]; then
148+
echo "::error::GITHUB_SHA not set. This shouldn't happen!"
149+
exit 1
150+
fi
151+
152+
- name: Set deployment domain
153+
id: deployment
154+
run: |
155+
case ${DEPLOYMENT_ENV}/${{github.repository}} in
156+
dev/*)
157+
DOMAIN="${DOMAIN_DEV}"
158+
;;
159+
staging/*)
160+
DOMAIN="${DOMAIN_STAGING}"
161+
;;
162+
prod/nginx/unit-docs)
163+
DOMAIN="${DOMAIN_UNIT}"
164+
;;
165+
prod/*)
166+
DOMAIN="${DOMAIN_PROD}"
167+
;;
168+
preview/*)
169+
DOMAIN="${DOMAIN_PREVIEW}"
170+
;;
171+
esac
172+
173+
echo "DEPLOYMENT_DOMAIN=${DOMAIN}" >> $GITHUB_ENV
174+
175+
- name: Azure login
176+
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
177+
with:
178+
client-id: ${{ secrets.AZURE_VAULT_CLIENT_ID }}
179+
tenant-id: ${{ secrets.AZURE_VAULT_TENANT_ID }}
180+
subscription-id: ${{ secrets.AZURE_VAULT_SUBSCRIPTION_ID }}
181+
182+
- name: Retrieve secrets from Keyvault
183+
id: keyvault
184+
uses: azure/cli@089eac9d8cc39f5d003e94f8b65efc51076c9cbd # v2.1.0
185+
with:
186+
inlineScript: |
187+
secrets_get=(AWSAccount AWSRoleName AWSS3Bucket)
188+
for secret_get in ${secrets_get[@]}
189+
do
190+
value=$(az keyvault secret show --name $secret_get --vault-name '${{ secrets.DOCS_VAULTNAME }}' --query value --output tsv)
191+
echo "::add-mask::$value"
192+
echo "$secret_get=$value" >> $GITHUB_ENV
193+
done
194+
195+
- name: Configure AWS credentials via OIDC (assume role)
196+
uses: aws-actions/configure-aws-credentials@v4
197+
with:
198+
role-to-assume: arn:aws:iam::${{ env.AWSAccount }}:role/${{ env.AWSRoleName }}
199+
aws-region: eu-central-1
200+
201+
- name: Checkout docs content
202+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.7.1
203+
with:
204+
fetch-depth: 0 # This is required for hugo Lastmod to function properly
205+
206+
- name: Get latest hugo theme
207+
if: inputs.doc_type == 'hugo' && inputs.force_hugo_theme_version == ''
208+
run: |
209+
RESPONSE=$(curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/nginxinc/nginx-hugo-theme/releases/latest)
210+
echo $RESPONSE
211+
echo "THEME_VERSION=$(echo $RESPONSE | jq -r ".tag_name")" >> "$GITHUB_ENV"
212+
213+
### Hugo builds
214+
215+
- name: Setup Go
216+
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
217+
if: inputs.doc_type == 'hugo'
218+
with:
219+
go-version: ${{env.GO_VERISON}}
220+
cache: false
221+
222+
- name: Setup Hugo
223+
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0
224+
if: inputs.doc_type == 'hugo'
225+
with:
226+
hugo-version: ${{env.HUGO_VERSION}}
227+
extended: true
228+
229+
- name: Add hugo build info
230+
if: inputs.doc_type == 'hugo'
231+
run: |
232+
timestamp=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
233+
cat <<EOF > buildInfo.json
234+
{
235+
"nginxHugoThemeVersion": "$THEME_MODULE@$THEME_VERSION",
236+
"buildDate": "$timestamp"
237+
}
238+
EOF
239+
mkdir -p ${{inputs.docs_build_path}}/static/
240+
cp buildInfo.json ${{inputs.docs_build_path}}/static/
241+
242+
- name: Setup node deps
243+
if: inputs.node_dep == true
244+
run: npm ci
245+
246+
- name: Build Hugo for PR preview
247+
if: inputs.doc_type == 'hugo' && (github.event.action == 'synchronize' || github.event.action == 'opened' || env.DEPLOYMENT_ENV == 'preview')
248+
working-directory: ${{inputs.docs_build_path}}
249+
run: |
250+
hugo mod get -v "$THEME_MODULE@$THEME_VERSION"
251+
hugo --gc -e production --baseURL="https://${DEPLOYMENT_DOMAIN}${PREVIEW_URL_PATH}/${PR_NUMBER}"
252+
253+
- name: Build Hugo for environment
254+
working-directory: ${{inputs.docs_build_path}}
255+
if: inputs.doc_type == 'hugo' && env.DEPLOYMENT_ENV != 'preview'
256+
run: |
257+
hugo mod get "$THEME_MODULE@$THEME_VERSION"
258+
hugo --gc -e production --baseURL="https://${DEPLOYMENT_DOMAIN}${PRODUCTION_URL_PATH}"
259+
260+
### Sphinx builds
261+
262+
- name: Setup Sphinx
263+
uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
264+
if: inputs.doc_type == 'sphinx'
265+
with:
266+
python-version: "3.9"
267+
268+
- name: Install python dependencies
269+
if: inputs.doc_type == 'sphinx'
270+
run: pip install -r requirements.txt
271+
272+
- name: Setup Gnupg directories
273+
if: inputs.doc_type == 'sphinx'
274+
run: |
275+
mkdir -p /home/runner/.gnupg
276+
chmod 700 /home/runner/.gnupg
277+
278+
- name: Build Sphinx for PR preview and production
279+
working-directory: ${{inputs.docs_build_path}}
280+
if: inputs.doc_type == 'sphinx'
281+
run: |
282+
make deploy
283+
284+
### AWS upload
285+
286+
- name: Upload to S3
287+
if: github.event.action == 'synchronize' || github.event.action == 'opened' || env.DEPLOYMENT_ENV == 'preview'
288+
run: |
289+
cd ${{inputs.docs_build_path}}
290+
aws s3 sync $DOCS_SOURCE_PATH s3://${AWSS3Bucket}/dev/${{github.repository}}/previews/${PR_NUMBER} --delete
291+
292+
- name: Upload to S3
293+
if: env.DEPLOYMENT_ENV != 'preview'
294+
run: |
295+
cd ${{inputs.docs_build_path}}
296+
aws s3 sync $DOCS_SOURCE_PATH s3://${AWSS3Bucket}/${DEPLOYMENT_ENV}/${{github.repository}}/latest/ --delete
297+
298+
- name: Azure logout
299+
run: |
300+
az logout
301+
if: always()
302+
303+
### PR preview comment
304+
305+
- name: PR preview comment
306+
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
307+
if: env.DEPLOYMENT_ENV == 'preview' && (github.event.action == 'synchronize' || github.event.action == 'opened')
308+
with:
309+
script: |
310+
// If a comment already exists, skip
311+
const { data: comments } = await github.rest.issues.listComments({
312+
issue_number: context.issue.number,
313+
owner: context.repo.owner,
314+
repo: context.repo.repo,
315+
});
316+
317+
const existingComment = comments.find(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Deploy Preview'));
318+
319+
if (existingComment) {
320+
core.info('Preview comment already exists. Skipping...');
321+
return;
322+
}
323+
324+
const previewHostname = process.env.DOMAIN_PREVIEW;
325+
const previewUrlPath = process.env.PREVIEW_URL_PATH;
326+
const prNumber = context.payload.pull_request.number;
327+
328+
const previewUrl = `https://${previewHostname}${previewUrlPath}/${prNumber}/`;
329+
330+
const body = `### <span aria-hidden="true">✅</span> Deploy Preview will be available once build job completes!
331+
332+
| Name | Link |
333+
|:-:|------------------------|
334+
|<span aria-hidden="true">😎</span> Deploy Preview | [${previewUrl}](${previewUrl}) |
335+
---`;
336+
337+
await github.rest.issues.createComment({
338+
issue_number: context.issue.number,
339+
owner: context.repo.owner,
340+
repo: context.repo.repo,
341+
body: body,
342+
});
343+
344+
- name: Summary
345+
# TODO(dani): Extract this into a reusable Markdown template for comments and summaries?
346+
id: summary
347+
run: |
348+
echo "### Deployment Summary" >> $GITHUB_STEP_SUMMARY
349+
echo "" >> $GITHUB_STEP_SUMMARY
350+
echo "| Task | Result |" >> $GITHUB_STEP_SUMMARY
351+
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
352+
echo "| Deployment environment | \`${DEPLOYMENT_ENV}\`|" >> $GITHUB_STEP_SUMMARY
353+
if [[ "${DEPLOYMENT_ENV}" == "preview" ]]; then
354+
echo "| Preview URL | [https://${DOMAIN_PREVIEW}${PREVIEW_URL_PATH}/${PR_NUMBER}/](https://${DOMAIN_PREVIEW}${PREVIEW_URL_PATH}/${PR_NUMBER}/) |" >> $GITHUB_STEP_SUMMARY
355+
echo "PREVIEW_URL=https://${DOMAIN_PREVIEW}${PREVIEW_URL_PATH}/${PR_NUMBER}/" >> $GITHUB_OUTPUT
356+
else
357+
echo "| Production URL | [https://${DEPLOYMENT_DOMAIN}${PRODUCTION_URL_PATH}](https://${DEPLOYMENT_DOMAIN}${PRODUCTION_URL_PATH}) |" >> $GITHUB_STEP_SUMMARY
358+
echo "PREVIEW_URL=https://${DEPLOYMENT_DOMAIN}${PRODUCTION_URL_PATH}" >> $GITHUB_OUTPUT
359+
fi
360+
361+
362+
# TODO(dani): Add more details to the summary

0 commit comments

Comments
 (0)