Skip to content

Commit

Permalink
create trigger workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
anita-steiner committed Jun 29, 2022
0 parents commit b300842
Show file tree
Hide file tree
Showing 4 changed files with 379 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

FROM alpine:3.16.0

RUN apk update && \
apk --no-cache add curl jq coreutils

COPY entrypoint.sh /entrypoint.sh

ENTRYPOINT ["sh", "/entrypoint.sh"]
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Trigger Workflow and Wait

Github Action for trigger a workflow from another workflow. The action then waits for a response.
see: https://github.com/datavisyn/trigger-workflow-and-wait/blob/master/README.md

see also https://github.com/convictional/trigger-workflow-and-wait

**When would you use it?**

When deploying an app you may need to deploy additional services, this Github Action helps with that.


## Arguments

| Argument Name | Required | Default | Description |
| --------------------- | ---------- | ----------- | --------------------- |
| `owner` | True | N/A | The owner of the repository where the workflow is contained. |
| `repo` | True | N/A | The repository where the workflow is contained. |
| `github_token` | True | N/A | The Github access token with access to the repository. Its recommended you put it under secrets. |
| `workflow_file_name` | True | N/A | The reference point. For example, you could use main.yml. |
| `github_user` | False | N/A | The name of the github user whose access token is being used to trigger the workflow. |
| `ref` | False | main | The reference of the workflow run. The reference can be a branch, tag, or a commit SHA. |
| `wait_interval` | False | 10 | The number of seconds delay between checking for result of run. |
| `client_payload` | False | `{}` | Payload to pass to the workflow, must be a JSON string |
| `propagate_failure` | False | `true` | Fail current job if downstream job fails. |
| `trigger_workflow` | False | `true` | Trigger the specified workflow. |
| `wait_workflow` | False | `true` | Wait for workflow to finish. |


## Example

### Simple

```yaml
- uses: ./.github/actions/trigger-workflow-and-wait
with:
owner: datavisyn
repo: myrepo
github_token: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}
```

### All Options

```yaml
- uses: ./.github/actions/trigger-workflow-and-wait
with:
owner: datavisyn
repo: myrepo
github_token: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}
github_user: github-user
workflow_file_name: main.yml
ref: release-branch
wait_interval: 10
client_payload: '{}'
propagate_failure: false
trigger_workflow: true
wait_workflow: true
```


## Testing

You can test out the action locally by cloning the repository to your computer. You can run:

```shell
INPUT_OWNER="datavisyn" \
INPUT_REPO="myrepo" \
INPUT_GITHUB_TOKEN="<REDACTED>" \
INPUT_GITHUB_USER="github-user" \
INPUT_WORKFLOW_FILE_NAME="main.yml" \
INPUT_REF="release-branch" \
INPUT_WAIT_INTERVAL=10 \
INPUT_CLIENT_PAYLOAD='{}' \
INPUT_PROPAGATE_FAILURE=false \
INPUT_TRIGGER_WORKFLOW=true \
INPUT_WAIT_WORKFLOW=true \
busybox sh entrypoint.sh
```

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:

```shell
name: Main
on:
workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Pause for 25 seconds
run: |
sleep 25
```

For testing a failure case, just add this line after the sleep:

```yaml
...
- name: Pause for 25 seconds
run: |
sleep 25
echo "For testing failure"
exit 1
```

## Potential Issues

### Changes

44 changes: 44 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: 'Trigger Workflow'
description: 'Triggers another workflows and wait'
inputs:
owner:
description: "The owner of the repository where the workflow is contained."
required: true
repo:
description: "The repository where the workflow is contained."
required: true
github_token:
description: "The Github access token with access to the repository. It is recommended you put this token under secrets."
required: true
github_user:
description: "The name of the github user whose access token is being used to trigger the workflow."
required: false
ref:
description: 'The reference of the workflow run. The reference can be a branch, tag, or a commit SHA. Default: main'
required: false
wait_interval:
description: "The number of seconds delay between checking for result of run."
required: false
workflow_file_name:
description: "The reference point. For example, you could use main.yml."
required: true
client_payload:
description: 'Payload to pass to the workflow, must be a JSON string'
required: false
propagate_failure:
description: 'Fail current job if downstream job fails. default: true'
required: false
trigger_workflow:
description: 'Trigger the specified workflow. default: true'
required: false
wait_workflow:
description: 'Wait for workflow to finish. default: true'
required: false
outputs:
workflow_id:
description: The ID of the workflow that was triggered by this action
workflow_url:
description: The URL of the workflow that was triggered by this action
runs:
using: 'docker'
image: 'Dockerfile'
216 changes: 216 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
#!/usr/bin/env bash
set -e

usage_docs() {
echo ""
echo "You can use this Github Action with:"
echo "- uses: datavisyn/trigger-workflow-and-wait"
echo " with:"
echo " owner: datavisyn"
echo " repo: myrepo"
echo " github_token: \${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN }}"
echo " workflow_file_name: main.yaml"
}
GITHUB_API_URL="${API_URL:-https://api.github.com}"
GITHUB_SERVER_URL="${SERVER_URL:-https://github.com}"

validate_args() {
wait_interval=10 # Waits for 10 seconds
if [ "${INPUT_WAIT_INTERVAL}" ]
then
wait_interval=${INPUT_WAIT_INTERVAL}
fi

propagate_failure=true
if [ -n "${INPUT_PROPAGATE_FAILURE}" ]
then
propagate_failure=${INPUT_PROPAGATE_FAILURE}
fi

trigger_workflow=true
if [ -n "${INPUT_TRIGGER_WORKFLOW}" ]
then
trigger_workflow=${INPUT_TRIGGER_WORKFLOW}
fi

wait_workflow=true
if [ -n "${INPUT_WAIT_WORKFLOW}" ]
then
wait_workflow=${INPUT_WAIT_WORKFLOW}
fi

if [ -z "${INPUT_OWNER}" ]
then
echo "Error: Owner is a required argument."
usage_docs
exit 1
fi

if [ -z "${INPUT_REPO}" ]
then
echo "Error: Repo is a required argument."
usage_docs
exit 1
fi

if [ -z "${INPUT_GITHUB_TOKEN}" ]
then
echo "Error: Github token is required. You can head over settings and"
echo "under developer, you can create a personal access tokens. The"
echo "token requires repo access."
usage_docs
exit 1
fi

if [ -z "${INPUT_WORKFLOW_FILE_NAME}" ]
then
echo "Error: Workflow File Name is required"
usage_docs
exit 1
fi

client_payload=$(echo '{}' | jq -c)
if [ "${INPUT_CLIENT_PAYLOAD}" ]
then
client_payload=$(echo "${INPUT_CLIENT_PAYLOAD}" | jq -c)
fi

ref="main"
if [ "$INPUT_REF" ]
then
ref="${INPUT_REF}"
fi
}

lets_wait() {
echo "Sleeping for ${wait_interval} seconds"
sleep $wait_interval
}

api() {
path=$1; shift
if response=$(curl --fail-with-body -sSL \
"${GITHUB_API_URL}/repos/${INPUT_OWNER}/${INPUT_REPO}/actions/$path" \
-H "Authorization: Bearer ${INPUT_GITHUB_TOKEN}" \
-H 'Accept: application/vnd.github.v3+json' \
-H 'Content-Type: application/json' \
"$@")
then
echo "$response"
else
echo >&2 "api failed:"
echo >&2 "path: $path"
echo >&2 "response: $response"
exit 1
fi
}

lets_wait() {
local interval=${1:-$wait_interval}
echo >&2 "Sleeping for $interval seconds"
sleep "$interval"
}

# Return the ids of the most recent workflow runs, optionally filtered by user
get_workflow_runs() {
since=${1:?}

query="event=workflow_dispatch&created=>=$since${INPUT_GITHUB_USER+&actor=}${INPUT_GITHUB_USER}&per_page=100"

echo "Getting workflow runs using query: ${query}" >&2

api "workflows/${INPUT_WORKFLOW_FILE_NAME}/runs?${query}" |
jq -r '.workflow_runs[].id' |
sort # Sort to ensure repeatable order, and lexicographically for compatibility with join
}

trigger_workflow() {
START_TIME=$(date +%s)
SINCE=$(date -u -Iseconds -d "@$((START_TIME - 120))") # Two minutes ago, to overcome clock skew

OLD_RUNS=$(get_workflow_runs "$SINCE")

echo >&2 "Triggering workflow:"
echo >&2 " workflows/${INPUT_WORKFLOW_FILE_NAME}/dispatches"
echo >&2 " {\"ref\":\"${ref}\",\"inputs\":${client_payload}}"

api "workflows/${INPUT_WORKFLOW_FILE_NAME}/dispatches" \
--data "{\"ref\":\"${ref}\",\"inputs\":${client_payload}}"

NEW_RUNS=$OLD_RUNS
while [ "$NEW_RUNS" = "$OLD_RUNS" ]
do
lets_wait
NEW_RUNS=$(get_workflow_runs "$SINCE")
done

# Return new run ids
join -v2 <(echo "$OLD_RUNS") <(echo "$NEW_RUNS")
}

wait_for_workflow_to_finish() {
last_workflow_id=${1:?}
last_workflow_url="${GITHUB_SERVER_URL}/${INPUT_OWNER}/${INPUT_REPO}/actions/runs/${last_workflow_id}"

echo "Waiting for workflow to finish:"
echo "The workflow id is [${last_workflow_id}]."
echo "The workflow logs can be found at ${last_workflow_url}"
echo "::set-output name=workflow_id::${last_workflow_id}"
echo "::set-output name=workflow_url::${last_workflow_url}"
echo ""

conclusion=null
status=

while [[ "${conclusion}" == "null" && "${status}" != "completed" ]]
do
lets_wait

workflow=$(api "runs/$last_workflow_id")
conclusion=$(echo "${workflow}" | jq -r '.conclusion')
status=$(echo "${workflow}" | jq -r '.status')

echo "Checking conclusion [${conclusion}]"
echo "Checking status [${status}]"
done

if [[ "${conclusion}" == "success" && "${status}" == "completed" ]]
then
echo "Yes, success"
else
# Alternative "failure"
echo "Conclusion is not success, it's [${conclusion}]."

if [ "${propagate_failure}" = true ]
then
echo "Propagating failure to upstream job"
exit 1
fi
fi
}

main() {
validate_args

if [ "${trigger_workflow}" = true ]
then
run_ids=$(trigger_workflow)
else
echo "Skipping triggering the workflow."
fi

if [ "${wait_workflow}" = true ]
then
for run_id in $run_ids
do
wait_for_workflow_to_finish "$run_id"
done
else
echo "Skipping waiting for workflow."
fi
}
echo ${GITHUB_API_URL}
echo ${INPUT_OWNER}
echo ${INPUT_REPO}

main

0 comments on commit b300842

Please sign in to comment.