-
-
Notifications
You must be signed in to change notification settings - Fork 282
249 lines (221 loc) · 9.13 KB
/
staging.yml
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
name: Create Dev Staging Environment
on:
workflow_dispatch:
inputs:
PR_number:
description: 'Pull request number'
required: true
jobs:
create:
name: 'Create staging and deploy'
defaults:
run:
shell: bash
runs-on: ubuntu-latest
steps:
# ======================================================
# It's important to check that the PR number
# provided as input is valid and belongs to
# the repository.
#
# This will also return the PR's branch as an output
# which can be fetched in next steps via:
# ${{ steps.verify_pr_number.outputs.result }}
# ======================================================
- name: Verify Pull Request Number
uses: actions/github-script@v5
id: verify_pr_number
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
const response = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ github.event.inputs.PR_number }}
});
// Check if the pull request is open
if (response.data.number !== ${{ github.event.inputs.PR_number }}) {
throw new Error('Pull request is not open or number is not valid!');
} else {
console.log("PR ref: " + response.data.head.ref);
return response.data.head.ref;
}
# ======================================================
# Checkout the branch infra and the repository
# ======================================================
- uses: actions/checkout@v2
name: 'Checkout repository and infra branch'
with:
ref: infra
# ======================================================
# Terraform setup
#
# - secrets.TERRAFORM_API_TOKEN: is the Terraform
# Cloud API Token.
# ======================================================
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.0.11
cli_config_credentials_token: ${{ secrets.TERRAFORM_API_TOKEN }}
# ======================================================
# We need to create a new Terraform resource file and for
# this we can use the `create_staging_resource.sh` and passing
# the PR number as an argument.
#
# The script returns a JSON string of the format:
# {
# "resource_file": "extra_staging_'${resource_id}'.tf",
# "terraform_expected_output": "staging_dns_'${resource_id}'"
# }
#
# We use jq to pull out the value of `terraform_expected_output`
# as we will need it later to fetch the hostname of the
# staging server
# ======================================================
- name: 'Create staging environment resource file'
id: create_resource_file
working-directory: infra/instances/staging/
run: |
OUTPUT="$(./create_staging_resource.sh PR_${{ github.event.inputs.PR_number }} | jq -r .terraform_expected_output)"
echo "::set-output name=STAGING_RESOURCE_NAME::$OUTPUT"
- name: Terraform Init and Validate
id: init
working-directory: infra/instances/staging/
run: |
terraform init
terraform validate -no-color
- name: Terraform Plan
id: plan
run: terraform plan -no-color
working-directory: infra/instances/staging/
continue-on-error: true
# ======================================================
# Once Terraform Plan is completed we need to update
# the PR with the results of the plan
# ======================================================
- name: Update Pull Request
uses: actions/github-script@v5
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${process.env.PLAN}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: ${{ github.event.inputs.PR_number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
- name: Terraform Plan Status
if: steps.plan.outcome == 'failure'
run: exit 1
- name: Terraform Apply
id: apply
working-directory: infra/instances/staging/
run: terraform apply -auto-approve
- name: Terraform Output
id: apply_output
working-directory: infra/instances/staging/
run: terraform output -raw -no-color ${{ steps.create_resource_file.outputs.STAGING_RESOURCE_NAME }}
# ======================================================
# If everything goes well and the Terraform Plan was
# executed successfully, and the resources were created
# we need to commit the new resource file and push it
# to the infra branch.
#
# If we don't do this, everytime this workflow runs
# it will destroy the resources created by previous
# runs. We need to persist the state in the repository
# for this to work.
#
# Here we use a neat little trick:
# git commit -m "Add terraform resource files" && \
# git push || echo "Nothing to commit"
#
# git push will fail with a non-zero exit if there
# are no changes to commit and this will cause the workflow
# to fail. We don't want that. We can use the `|| echo`
# to print "Nothing to commit" to the console if
# git push fails.
# ======================================================
- name: Commit terraform resource files to the PR
working-directory: infra/instances/staging/
run: |
git config --global user.name '${{ github.actor }}'
git config --global user.email '${{ github.actor}}@users.noreply.github.com'
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
git add ./\*.tf
git commit -m "Add terraform resource files" && \
git push || echo "Nothing to commit"
# ======================================================
# Checkout the PR branch so that we can deploy it
# ======================================================
- uses: actions/checkout@v2
name: 'Checkout PR branch'
with:
ref: ${{ steps.verify_pr_number.outputs.result }}
# ======================================================
# We sync the files in this directory to the staging
# server. We use the `rsync` command to do this.
# ======================================================
- uses: burnett01/rsync-deployments@23a557dceb19f9bb960ef40cf75cab5e9b37ec1f
name: 'Deploy to staging'
with:
switches: -avzr --delete
path: ./web
remote_path: /var/app
remote_host: ${{ steps.apply_output.outputs.stdout }}
remote_user: ${{ secrets.REMOTE_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
# ======================================================
# Once we have the new files synced to the staging server
# we need to restart the staging server.
# ======================================================
- uses: JimCronqvist/action-ssh@7737f1192ddd8376686e9d6354dea44592c942bf
name: Execute SSH commmands on remote server
with:
hosts: '${{ secrets.REMOTE_USER }}@${{ steps.apply_output.outputs.stdout }}'
privateKey: ${{ secrets.SSH_PRIVATE_KEY }}
command: |
cd /var/app/web
npm ci
pm2 start /var/app/web/bin/www || pm2 restart /var/app/web/bin/www
sudo service nginx restart
# ======================================================
# When we're done, we need to update the PR one more time
# with the results of the apply.
# ======================================================
- name: Update status
uses: actions/github-script@v5
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `#### Staging server created
> PR #${{ github.event.inputs.PR_number }} has been deployed successfully
URL: http://${{ steps.apply_output.outputs.stdout }}`;
github.rest.issues.createComment({
issue_number: ${{ github.event.inputs.PR_number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
# ======================================================
# tmate is a nice little utility that allows us to
# ssh to the staging server and execute commands
# on the server in case any of the steps above fail.
#
# Otherwise this step will not be executed.
# ======================================================
- name: Setup tmate session
if: ${{ failure() }}
uses: mxschmitt/action-tmate@v3