Skip to content

Commit b300842

Browse files
author
anita-steiner
committed
create trigger workflow
0 parents  commit b300842

File tree

4 files changed

+379
-0
lines changed

4 files changed

+379
-0
lines changed

Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
FROM alpine:3.16.0
3+
4+
RUN apk update && \
5+
apk --no-cache add curl jq coreutils
6+
7+
COPY entrypoint.sh /entrypoint.sh
8+
9+
ENTRYPOINT ["sh", "/entrypoint.sh"]

README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Trigger Workflow and Wait
2+
3+
Github Action for trigger a workflow from another workflow. The action then waits for a response.
4+
see: https://github.com/datavisyn/trigger-workflow-and-wait/blob/master/README.md
5+
6+
see also https://github.com/convictional/trigger-workflow-and-wait
7+
8+
**When would you use it?**
9+
10+
When deploying an app you may need to deploy additional services, this Github Action helps with that.
11+
12+
13+
## Arguments
14+
15+
| Argument Name | Required | Default | Description |
16+
| --------------------- | ---------- | ----------- | --------------------- |
17+
| `owner` | True | N/A | The owner of the repository where the workflow is contained. |
18+
| `repo` | True | N/A | The repository where the workflow is contained. |
19+
| `github_token` | True | N/A | The Github access token with access to the repository. Its recommended you put it under secrets. |
20+
| `workflow_file_name` | True | N/A | The reference point. For example, you could use main.yml. |
21+
| `github_user` | False | N/A | The name of the github user whose access token is being used to trigger the workflow. |
22+
| `ref` | False | main | The reference of the workflow run. The reference can be a branch, tag, or a commit SHA. |
23+
| `wait_interval` | False | 10 | The number of seconds delay between checking for result of run. |
24+
| `client_payload` | False | `{}` | Payload to pass to the workflow, must be a JSON string |
25+
| `propagate_failure` | False | `true` | Fail current job if downstream job fails. |
26+
| `trigger_workflow` | False | `true` | Trigger the specified workflow. |
27+
| `wait_workflow` | False | `true` | Wait for workflow to finish. |
28+
29+
30+
## Example
31+
32+
### Simple
33+
34+
```yaml
35+
- uses: ./.github/actions/trigger-workflow-and-wait
36+
with:
37+
owner: datavisyn
38+
repo: myrepo
39+
github_token: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}
40+
```
41+
42+
### All Options
43+
44+
```yaml
45+
- uses: ./.github/actions/trigger-workflow-and-wait
46+
with:
47+
owner: datavisyn
48+
repo: myrepo
49+
github_token: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}
50+
github_user: github-user
51+
workflow_file_name: main.yml
52+
ref: release-branch
53+
wait_interval: 10
54+
client_payload: '{}'
55+
propagate_failure: false
56+
trigger_workflow: true
57+
wait_workflow: true
58+
```
59+
60+
61+
## Testing
62+
63+
You can test out the action locally by cloning the repository to your computer. You can run:
64+
65+
```shell
66+
INPUT_OWNER="datavisyn" \
67+
INPUT_REPO="myrepo" \
68+
INPUT_GITHUB_TOKEN="<REDACTED>" \
69+
INPUT_GITHUB_USER="github-user" \
70+
INPUT_WORKFLOW_FILE_NAME="main.yml" \
71+
INPUT_REF="release-branch" \
72+
INPUT_WAIT_INTERVAL=10 \
73+
INPUT_CLIENT_PAYLOAD='{}' \
74+
INPUT_PROPAGATE_FAILURE=false \
75+
INPUT_TRIGGER_WORKFLOW=true \
76+
INPUT_WAIT_WORKFLOW=true \
77+
busybox sh entrypoint.sh
78+
```
79+
80+
You will have to create a Github Personal access token. You can create a test workflow to be executed. In a repository, add a new `main.yml` to `.github/workflows/`. The workflow will be:
81+
82+
```shell
83+
name: Main
84+
on:
85+
workflow_dispatch
86+
jobs:
87+
build:
88+
runs-on: ubuntu-latest
89+
steps:
90+
- uses: actions/checkout@master
91+
- name: Pause for 25 seconds
92+
run: |
93+
sleep 25
94+
```
95+
96+
For testing a failure case, just add this line after the sleep:
97+
98+
```yaml
99+
...
100+
- name: Pause for 25 seconds
101+
run: |
102+
sleep 25
103+
echo "For testing failure"
104+
exit 1
105+
```
106+
107+
## Potential Issues
108+
109+
### Changes
110+

action.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: 'Trigger Workflow'
2+
description: 'Triggers another workflows and wait'
3+
inputs:
4+
owner:
5+
description: "The owner of the repository where the workflow is contained."
6+
required: true
7+
repo:
8+
description: "The repository where the workflow is contained."
9+
required: true
10+
github_token:
11+
description: "The Github access token with access to the repository. It is recommended you put this token under secrets."
12+
required: true
13+
github_user:
14+
description: "The name of the github user whose access token is being used to trigger the workflow."
15+
required: false
16+
ref:
17+
description: 'The reference of the workflow run. The reference can be a branch, tag, or a commit SHA. Default: main'
18+
required: false
19+
wait_interval:
20+
description: "The number of seconds delay between checking for result of run."
21+
required: false
22+
workflow_file_name:
23+
description: "The reference point. For example, you could use main.yml."
24+
required: true
25+
client_payload:
26+
description: 'Payload to pass to the workflow, must be a JSON string'
27+
required: false
28+
propagate_failure:
29+
description: 'Fail current job if downstream job fails. default: true'
30+
required: false
31+
trigger_workflow:
32+
description: 'Trigger the specified workflow. default: true'
33+
required: false
34+
wait_workflow:
35+
description: 'Wait for workflow to finish. default: true'
36+
required: false
37+
outputs:
38+
workflow_id:
39+
description: The ID of the workflow that was triggered by this action
40+
workflow_url:
41+
description: The URL of the workflow that was triggered by this action
42+
runs:
43+
using: 'docker'
44+
image: 'Dockerfile'

entrypoint.sh

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
usage_docs() {
5+
echo ""
6+
echo "You can use this Github Action with:"
7+
echo "- uses: datavisyn/trigger-workflow-and-wait"
8+
echo " with:"
9+
echo " owner: datavisyn"
10+
echo " repo: myrepo"
11+
echo " github_token: \${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}"
12+
echo " workflow_file_name: main.yaml"
13+
}
14+
GITHUB_API_URL="${API_URL:-https://api.github.com}"
15+
GITHUB_SERVER_URL="${SERVER_URL:-https://github.com}"
16+
17+
validate_args() {
18+
wait_interval=10 # Waits for 10 seconds
19+
if [ "${INPUT_WAIT_INTERVAL}" ]
20+
then
21+
wait_interval=${INPUT_WAIT_INTERVAL}
22+
fi
23+
24+
propagate_failure=true
25+
if [ -n "${INPUT_PROPAGATE_FAILURE}" ]
26+
then
27+
propagate_failure=${INPUT_PROPAGATE_FAILURE}
28+
fi
29+
30+
trigger_workflow=true
31+
if [ -n "${INPUT_TRIGGER_WORKFLOW}" ]
32+
then
33+
trigger_workflow=${INPUT_TRIGGER_WORKFLOW}
34+
fi
35+
36+
wait_workflow=true
37+
if [ -n "${INPUT_WAIT_WORKFLOW}" ]
38+
then
39+
wait_workflow=${INPUT_WAIT_WORKFLOW}
40+
fi
41+
42+
if [ -z "${INPUT_OWNER}" ]
43+
then
44+
echo "Error: Owner is a required argument."
45+
usage_docs
46+
exit 1
47+
fi
48+
49+
if [ -z "${INPUT_REPO}" ]
50+
then
51+
echo "Error: Repo is a required argument."
52+
usage_docs
53+
exit 1
54+
fi
55+
56+
if [ -z "${INPUT_GITHUB_TOKEN}" ]
57+
then
58+
echo "Error: Github token is required. You can head over settings and"
59+
echo "under developer, you can create a personal access tokens. The"
60+
echo "token requires repo access."
61+
usage_docs
62+
exit 1
63+
fi
64+
65+
if [ -z "${INPUT_WORKFLOW_FILE_NAME}" ]
66+
then
67+
echo "Error: Workflow File Name is required"
68+
usage_docs
69+
exit 1
70+
fi
71+
72+
client_payload=$(echo '{}' | jq -c)
73+
if [ "${INPUT_CLIENT_PAYLOAD}" ]
74+
then
75+
client_payload=$(echo "${INPUT_CLIENT_PAYLOAD}" | jq -c)
76+
fi
77+
78+
ref="main"
79+
if [ "$INPUT_REF" ]
80+
then
81+
ref="${INPUT_REF}"
82+
fi
83+
}
84+
85+
lets_wait() {
86+
echo "Sleeping for ${wait_interval} seconds"
87+
sleep $wait_interval
88+
}
89+
90+
api() {
91+
path=$1; shift
92+
if response=$(curl --fail-with-body -sSL \
93+
"${GITHUB_API_URL}/repos/${INPUT_OWNER}/${INPUT_REPO}/actions/$path" \
94+
-H "Authorization: Bearer ${INPUT_GITHUB_TOKEN}" \
95+
-H 'Accept: application/vnd.github.v3+json' \
96+
-H 'Content-Type: application/json' \
97+
"$@")
98+
then
99+
echo "$response"
100+
else
101+
echo >&2 "api failed:"
102+
echo >&2 "path: $path"
103+
echo >&2 "response: $response"
104+
exit 1
105+
fi
106+
}
107+
108+
lets_wait() {
109+
local interval=${1:-$wait_interval}
110+
echo >&2 "Sleeping for $interval seconds"
111+
sleep "$interval"
112+
}
113+
114+
# Return the ids of the most recent workflow runs, optionally filtered by user
115+
get_workflow_runs() {
116+
since=${1:?}
117+
118+
query="event=workflow_dispatch&created=>=$since${INPUT_GITHUB_USER+&actor=}${INPUT_GITHUB_USER}&per_page=100"
119+
120+
echo "Getting workflow runs using query: ${query}" >&2
121+
122+
api "workflows/${INPUT_WORKFLOW_FILE_NAME}/runs?${query}" |
123+
jq -r '.workflow_runs[].id' |
124+
sort # Sort to ensure repeatable order, and lexicographically for compatibility with join
125+
}
126+
127+
trigger_workflow() {
128+
START_TIME=$(date +%s)
129+
SINCE=$(date -u -Iseconds -d "@$((START_TIME - 120))") # Two minutes ago, to overcome clock skew
130+
131+
OLD_RUNS=$(get_workflow_runs "$SINCE")
132+
133+
echo >&2 "Triggering workflow:"
134+
echo >&2 " workflows/${INPUT_WORKFLOW_FILE_NAME}/dispatches"
135+
echo >&2 " {\"ref\":\"${ref}\",\"inputs\":${client_payload}}"
136+
137+
api "workflows/${INPUT_WORKFLOW_FILE_NAME}/dispatches" \
138+
--data "{\"ref\":\"${ref}\",\"inputs\":${client_payload}}"
139+
140+
NEW_RUNS=$OLD_RUNS
141+
while [ "$NEW_RUNS" = "$OLD_RUNS" ]
142+
do
143+
lets_wait
144+
NEW_RUNS=$(get_workflow_runs "$SINCE")
145+
done
146+
147+
# Return new run ids
148+
join -v2 <(echo "$OLD_RUNS") <(echo "$NEW_RUNS")
149+
}
150+
151+
wait_for_workflow_to_finish() {
152+
last_workflow_id=${1:?}
153+
last_workflow_url="${GITHUB_SERVER_URL}/${INPUT_OWNER}/${INPUT_REPO}/actions/runs/${last_workflow_id}"
154+
155+
echo "Waiting for workflow to finish:"
156+
echo "The workflow id is [${last_workflow_id}]."
157+
echo "The workflow logs can be found at ${last_workflow_url}"
158+
echo "::set-output name=workflow_id::${last_workflow_id}"
159+
echo "::set-output name=workflow_url::${last_workflow_url}"
160+
echo ""
161+
162+
conclusion=null
163+
status=
164+
165+
while [[ "${conclusion}" == "null" && "${status}" != "completed" ]]
166+
do
167+
lets_wait
168+
169+
workflow=$(api "runs/$last_workflow_id")
170+
conclusion=$(echo "${workflow}" | jq -r '.conclusion')
171+
status=$(echo "${workflow}" | jq -r '.status')
172+
173+
echo "Checking conclusion [${conclusion}]"
174+
echo "Checking status [${status}]"
175+
done
176+
177+
if [[ "${conclusion}" == "success" && "${status}" == "completed" ]]
178+
then
179+
echo "Yes, success"
180+
else
181+
# Alternative "failure"
182+
echo "Conclusion is not success, it's [${conclusion}]."
183+
184+
if [ "${propagate_failure}" = true ]
185+
then
186+
echo "Propagating failure to upstream job"
187+
exit 1
188+
fi
189+
fi
190+
}
191+
192+
main() {
193+
validate_args
194+
195+
if [ "${trigger_workflow}" = true ]
196+
then
197+
run_ids=$(trigger_workflow)
198+
else
199+
echo "Skipping triggering the workflow."
200+
fi
201+
202+
if [ "${wait_workflow}" = true ]
203+
then
204+
for run_id in $run_ids
205+
do
206+
wait_for_workflow_to_finish "$run_id"
207+
done
208+
else
209+
echo "Skipping waiting for workflow."
210+
fi
211+
}
212+
echo ${GITHUB_API_URL}
213+
echo ${INPUT_OWNER}
214+
echo ${INPUT_REPO}
215+
216+
main

0 commit comments

Comments
 (0)