Repository navigation
166 lines (149 loc) · 6.78 KB
/
Copy pathdeploy.yml
File metadata and controls
166 lines (149 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
name: deploy
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
# The run asks GitHub for a short lived token and trades it for the deploy role. Without this the
# request for the token is refused and the whole job cannot reach the account at all.
id-token: write
concurrency:
group: deploy
cancel-in-progress: false
jobs:
deploy:
name: plan and apply
runs-on: ubuntu-latest
env:
AWS_REGION: ${{ vars.AWS_REGION }}
AWS_DEFAULT_REGION: ${{ vars.AWS_REGION }}
steps:
- uses: actions/checkout@v4
# The account is reached through a role this repository is trusted to assume, rather than
# through a key stored here. A key has to be rotated by a person and works from anywhere it
# leaks to; this credential lasts the length of the run and only this repository can ask for
# it. The role carries only the actions the infrastructure below needs.
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.AWS_DEPLOY_ROLE }}
aws-region: ${{ vars.AWS_REGION }}
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
- run: npm ci
- name: build the bundle the function runs
run: npm run build
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.10.5"
# The state has to outlive any one run, so it lives in a bucket. The bucket is made here
# because nothing else can make it: it is what holds the record of everything else.
- name: make the state bucket if it is not there
id: state
run: |
set -euo pipefail
account="$(aws sts get-caller-identity --query Account --output text)"
bucket="transcript-tfstate-${account}-${AWS_REGION}"
if aws s3api head-bucket --bucket "$bucket" 2>/dev/null; then
echo "the state bucket is already there"
else
aws s3api create-bucket --bucket "$bucket" --region "$AWS_REGION" \
--create-bucket-configuration LocationConstraint="$AWS_REGION"
aws s3api put-bucket-versioning --bucket "$bucket" \
--versioning-configuration Status=Enabled
aws s3api put-public-access-block --bucket "$bucket" \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws s3api put-bucket-encryption --bucket "$bucket" \
--server-side-encryption-configuration '{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}'
echo "the state bucket was made"
fi
echo "bucket=$bucket" >> "$GITHUB_OUTPUT"
- name: init
run: |
set -euo pipefail
terraform init -input=false \
-backend-config="bucket=${{ steps.state.outputs.bucket }}" \
-backend-config="region=${AWS_REGION}"
working-directory: infra
- name: plan
run: terraform plan -input=false -var="region=${AWS_REGION}" -out=tfplan
working-directory: infra
- name: apply
run: terraform apply -input=false -auto-approve tfplan
working-directory: infra
- name: say the address
run: |
set -euo pipefail
address="$(terraform output -raw address)"
example="$(terraform output -raw example)"
{
echo "## The page is serving"
echo ""
echo "Open it: $address"
echo ""
echo "One video: $example"
} >> "$GITHUB_STEP_SUMMARY"
echo "address=$address"
working-directory: infra
# A public function url needs a grant for lambda:InvokeFunction as well as one for
# lambda:InvokeFunctionUrl, and without it every path answers 403 while the function itself
# still runs. The distribution can serve an answer it cached earlier, so the origin is asked
# directly here. Anything other than 200 fails the run.
- name: read the function url the distribution reads from
run: |
set -euo pipefail
origin="$(terraform output -raw origin)"
code=""
for attempt in 1 2 3 4 5 6; do
code=$(curl -s -o /tmp/origin.html -w '%{http_code}' --max-time 90 "$origin" || true)
if [ "$code" = "200" ]; then break; fi
echo "attempt $attempt answered $code, the grant may still be settling"
sleep 10
done
echo "the function url answered $code"
test "$code" = "200"
working-directory: infra
# The deploy serving at all is a different question from the product working, so they are
# asked separately. This one only proves the distribution reaches the function.
- name: read the front page
run: |
set -euo pipefail
address="$(terraform output -raw address)"
code=$(curl -s -o /tmp/front.html -w '%{http_code}' --max-time 90 "$address/" || true)
echo "the front page answered $code with $(wc -c < /tmp/front.html) bytes"
test "$code" = "200"
grep -q "Read a video as text" /tmp/front.html
working-directory: infra
# Green means the steps ran. It says nothing about whether a reader gets a transcript, so one
# is asked for here. A failure names what the platform did rather than the status alone,
# because a bare 404 told nobody anything for a day. The page states the cause in its title.
- name: read a transcript from the page that is now serving
run: |
set -euo pipefail
address="$(terraform output -raw address)"
for attempt in 1 2 3 4 5 6 7 8; do
code=$(curl -s -o /tmp/page.html -w '%{http_code}' --max-time 90 "$address/videos?id=gyN9lV9QgyA" || true)
if [ "$code" = "200" ]; then break; fi
echo "attempt $attempt answered $code, the distribution may still be spreading"
sleep 30
done
bytes=$(wc -c < /tmp/page.html)
said=$(sed -n "s/.*<title>\\(.*\\)<\\/title>.*/\\1/p" /tmp/page.html | head -1)
echo "the page answered $code with $bytes bytes, and said: $said"
if [ "$code" != "200" ]; then
{
echo "## The page is serving, and it cannot read a transcript"
echo ""
echo "It answered $code and said: $said"
echo ""
echo "The cause is in the function log, on the watch_page_unreadable line."
} >> "$GITHUB_STEP_SUMMARY"
echo "::error::the page answered $code and said: $said"
exit 1
fi
test "$bytes" -gt 5000
grep -q "Transcript" /tmp/page.html
echo "the page carries text"
working-directory: infra