forked from formsort/action-heroku-deploy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
action.yml
185 lines (176 loc) · 8.28 KB
/
action.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
name: "SHFT Heroku Deploy"
description: "Deploy to Heroku"
inputs:
github_repo:
description: "GitHub repo name with owner (eg. shftco/shftco)"
required: true
github_token:
description: "GITHUB_TOKEN"
required: true
heroku_api_key:
description: "API Key for the Heroku account that will perform the deploy"
required: true
heroku_app_name:
description: "Name of the Heroku app to be deployed"
required: true
heroku_app_version:
description: "The new version of the app to be deployed. Will fall back to `inputs.ref` if not provided."
required: false
ref:
description: "Git ref to deploy to Heroku"
required: true
default: ""
only_promotion:
description: "Only allow promoting from a pipeline (rather than a direct build)"
required: true
default: "false"
outputs:
source_tarball:
description: The URL of the source tarball used to create the build
value: ${{ steps.source_blob.outputs.link }}
build:
description: "The JSON blob of the build created by Heroku"
value: ${{ steps.create_build.outputs.build }}
build_id:
description: "The id of the build created by Heroku"
value: ${{ steps.create_build.outputs.id }}
build_log:
description: "The logs for the build created by Heroku"
value: ${{ steps.build_logs.outputs.text }}
release:
description: "The JSON blob of the release created by Heroku"
value: ${{ steps.get_release.outputs.release }}
release_id:
description: "The id of the release created by Heroku"
value: ${{ steps.get_release.outputs.id }}
runs:
using: "composite"
steps:
- id: pipeline_check
name: Check if app is part of a pipeline_check
continue-on-error: ${{ inputs.only_promotion == 'false' }}
shell: bash
run: |
# Try to determine if the app is part of a pipeline *and* it is in production stage
# If the app is NOT part of any pipelines, this endpoint will return 404, causing `curl`
# to exit with a failure code (see the `-f` flag) and will fail the rest of the step.
# This is why we have `continue-on-error` set to true above.
pipeline_couplings=$(\
curl -fsS 'https://api.heroku.com/apps/${{ inputs.heroku_app_name }}/pipeline-couplings' \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
)
if [[ $(echo "$pipeline_couplings" | jq -rc .stage) != 'production' ]]; then
exit;
fi
pipeline_id=$(echo "$pipeline_couplings" | jq -rc .pipeline.id)
# Get all the apps in the pipeline our app is associated with
all_pipeline_couplings=$(\
curl -fsS "https://api.heroku.com/pipelines/$pipeline_id/pipeline-couplings" \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
)
# Get the app id for the app designated as "staging" from the pipeline. Remember, we expected the
# app we are deploying to be the `production` app and we can only promote to that from `staging`.
staging_app_id=$(echo "$all_pipeline_couplings" | jq -rc '.[] | select(.stage == "staging").app.id')
# Get the latest builds on pipeline to see if there's a successful build associated with the
# staging app we just found above AND the SHA we are trying to deploy for.
latest_pipeline_builds=$(\
curl -fsS "https://api.heroku.com/pipelines/$pipeline_id/latest-builds" \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
)
applicable_build_id=$(\
echo "$latest_pipeline_builds" | \
jq -rc '.[] | select(.app.id == "'"$staging_app_id"'" and .status == "succeeded" and .source_blob.version == "${{ inputs.ref }}").id')
# IF we have a build on staging with the SHA we are trying to deploy across latest builds,
# set the outputs so the next steps will try to promote instead of doing a full build & release
if [[ -n "$applicable_build_id" ]]; then
echo "pipeline_id=$pipeline_id" >> $GITHUB_OUTPUT
echo "source_app_id=$staging_app_id" >> $GITHUB_OUTPUT
target_app_id=$(echo "$pipeline_couplings" | jq -rc .app.id)
echo "target_app_id=$target_app_id" >> $GITHUB_OUTPUT
fi
- id: source_blob
name: Get link to source blob
if: >
inputs.only_promotion == 'false' && !steps.pipeline_check.outputs.pipeline_id
shell: bash
run: |
echo "link=$(\
curl -fsS 'https://api.github.com/repos/${{ inputs.github_repo }}/tarball/${{ inputs.ref }}' \
-H 'Authorization: token ${{ inputs.github_token }}' -D - -o /dev/null \
| awk -v RS='\r\n' -v OFS='' -F'location: ' '$2 {print $2}' \
)" >> $GITHUB_OUTPUT
- id: create_build
name: Create Heroku build
if: >-
inputs.only_promotion == 'false' && !steps.pipeline_check.outputs.pipeline_id
shell: bash
run: |
heroku_build=$(curl -fsS -X POST 'https://api.heroku.com/apps/${{ inputs.heroku_app_name }}/builds' \
-d '{"source_blob":{"url":"${{ steps.source_blob.outputs.link }}", "version": "${{ inputs.heroku_app_version || inputs.ref }}"}}' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
| jq -rc . \
)
echo "build=$heroku_build" >> $GITHUB_OUTPUT
build_id=$(echo "$heroku_build" | jq -rc .id)
echo "id=$build_id" >> $GITHUB_OUTPUT
- id: build_logs
name: Heroku build logs
if: steps.create_build.outputs.build
shell: bash
run: |
# Wait till the build finishes
curl -fsSN $(echo '${{ steps.create_build.outputs.build }}' | jq -cr .output_stream_url) | tee build_log.txt
build_log=$(cat build_log.txt)
build_log="${build_log//'%'/'%25'}"
build_log="${build_log//$'\n'/'%0A'}"
build_log="${build_log//$'\r'/'%0D'}"
echo "text=$build_log" >> $GITHUB_OUTPUT
- id: promote
name: Promote
if: steps.pipeline_check.outputs.pipeline_id
shell: bash
run: |
heroku_promotion=$(curl -fsS -X POST 'https://api.heroku.com/pipeline-promotions' \
-d '{"pipeline":{"id":"${{ steps.pipeline_check.outputs.pipeline_id }}"}, "source":{"app":{"id": "${{ steps.pipeline_check.outputs.source_app_id }}"}}, "targets":[{"app":{"id": "${{ steps.pipeline_check.outputs.target_app_id }}"}}]}' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
| jq -rc . \
)
echo "promotion=$heroku_promotion" >> $GITHUB_OUTPUT
promotion_id=$(echo "$heroku_promotion" | jq -rc .id)
echo "promotion_id=$promotion_id" >> $GITHUB_OUTPUT
- id: get_release
name: Get Heroku release
shell: bash
run: |
promotion_id='${{ steps.promote.outputs.promotion_id }}'
if [[ -n "$promotion_id" ]]; then
sleep 10; # Give Heroku some time to kick off the release
release_id=$(curl -fsS "https://api.heroku.com/pipeline-promotions/$promotion_id/promotion-targets" \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
| jq -rc '.[0].release.id' \
)
else
release_id=$(curl -fsS 'https://api.heroku.com/apps/${{ inputs.heroku_app_name }}/builds/${{ steps.create_build.outputs.id }}' \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
| jq -rc .release.id \
)
fi
echo "id=$release_id" >> $GITHUB_OUTPUT
heroku_release=$(curl -fsS "https://api.heroku.com/apps/${{ inputs.heroku_app_name }}/releases/$release_id" \
-H 'Authorization: Bearer ${{ inputs.heroku_api_key }}' \
-H 'Accept: application/vnd.heroku+json; version=3' \
| jq -rc . \
)
echo "release=$heroku_release" >> $GITHUB_OUTPUT
branding:
color: green
icon: upload-cloud