Skip to content

Commit 67f67a6

Browse files
authored
Merge pull request #2 from SebastiaanZ/release/v0.3.0
Release version v0.3.0 that makes it easier to use the action
2 parents ac398ce + 6dc926c commit 67f67a6

File tree

4 files changed

+148
-42
lines changed

4 files changed

+148
-42
lines changed

README.md

Lines changed: 127 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,6 @@ jobs:
5656
webhook_id: '1234567890' # Has to be provided as a string
5757
webhook_token: ${{ secrets.webhook_token }}
5858

59-
# Information about the current workflow
60-
workflow_name: ${{ github.workflow }}
61-
run_id: ${{ github.run_id }}
62-
run_number: ${{ github.run_number }}
63-
status: ${{ job.status }}
64-
actor: ${{ github.actor }}
65-
repository: ${{ github.repository }}
66-
ref: ${{ github.ref }}
67-
sha: ${{ github.sha }}
68-
6959
# Optional arguments for PR-related events
7060
# Note: There's no harm in including these lines for non-PR
7161
# events, as non-existing paths in objects will evaluate to
@@ -75,33 +65,31 @@ jobs:
7565
pr_number: ${{ github.event.pull_request.number }}
7666
pr_title: ${{ github.event.pull_request.title }}
7767
pr_source: ${{ github.event.pull_request.head.label }}
78-
# Or simply provide the raw Pull Request payload in JSON format
79-
pull_request_payload: ${{ toJson(github.event.pull_request) }}
8068
```
8169
8270
### Command specification
8371
84-
**Note:** The example step above contains the typical way of providing the arguments.
72+
**Note:** The default values assume that the workflow you want to report the status of is also the workflow that is running this action. If this is not possible (e.g., because you don't have access to secrets in a `pull_request`-triggered workflow), you could use a `workflow_run` triggered workflow that reports the status of the workflow that triggered it. See the recipes section below for an example.
8573

86-
| Argument | Description | Required |
74+
| Argument | Description | Default |
8775
| --- | --- | :---: |
88-
| webhook_id | ID of the Discord webhook (use a string) | yes |
89-
| webhook_token | Token of the Discord webhook | yes |
90-
| workflow_name | Name of the workflow | yes |
91-
| run_id | Run ID of the workflow | yes |
92-
| run_number | Run number of the workflow | yes |
93-
| status | Status for the embed; one of ["succes", "failure", "cancelled"] | yes |
94-
| actor | Actor who requested the workflow | yes |
95-
| repository | Repository; has to be in form `owner/repo` | yes |
96-
| ref | Branch or tag ref that triggered the workflow run | yes |
97-
| sha | Full commit SHA that triggered the workflow run. | yes |
98-
| pr_author_login | **Login** of the Pull Request author | no¹ |
99-
| pr_number | Pull Request number | no¹ |
100-
| pr_title | Title of the Pull Request | no¹ |
101-
| pr_source | Source branch for the Pull Request | no¹ |
102-
| pull_request_payload | PR payload in JSON format² | no³ |
103-
| debug | set to "true" to turn on debug logging | no |
104-
| dry_run | set to "true" to not send the webhook request | no |
76+
| status | Status for the embed; one of ["succes", "failure", "cancelled"] | (required) |
77+
| webhook_id | ID of the Discord webhook (use a string) | (required) |
78+
| webhook_token | Token of the Discord webhook | (required) |
79+
| workflow_name | Name of the workflow | github.workflow |
80+
| run_id | Run ID of the workflow | github.run_id |
81+
| run_number | Run number of the workflow | github.run_number |
82+
| actor | Actor who requested the workflow | github.actor |
83+
| repository | Repository; has to be in form `owner/repo` | github.repository |
84+
| ref | Branch or tag ref that triggered the workflow run | github.ref |
85+
| sha | Full commit SHA that triggered the workflow run. | github.sha |
86+
| pr_author_login | **Login** of the Pull Request author | (optional)¹ |
87+
| pr_number | Pull Request number | (optional)¹ |
88+
| pr_title | Title of the Pull Request | (optional)¹ |
89+
| pr_source | Source branch for the Pull Request | (optional)¹ |
90+
| debug | set to "true" to turn on debug logging | false |
91+
| dry_run | set to "true" to not send the webhook request | false |
92+
| pull_request_payload | PR payload in JSON format² **(deprecated)** | (deprecated)³ |
10593

10694
1) The Action will determine whether to send an embed tailored towards a Pull Request Check Run or towards a general workflow run based on the presence of non-null values for the four pull request arguments. This means that you either have to provide **all** of them or **none** of them.
10795

@@ -113,4 +101,111 @@ jobs:
113101

114102
## Recipes
115103

116-
To be added.
104+
### Reporting the status of a `pull_request`-triggered workflow
105+
106+
One complication with `pull_request`-triggered workflows is that your secrets won't be available if the workflow is triggered for a pull request made from a fork. As you'd typically provide the webhook token as a secret, this makes using this action in such a workflow slightly more complicated.
107+
108+
However, GitHub has provided an additional workflow trigger specifically for this situation: [`workflow_run`](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#workflow_run). You can use this event to start a workflow whenever another workflow is being run or has just finished. As workflows triggered by `workflow_run` always run in the base repository, it has full access to your secrets.
109+
110+
To give your `workflow_run`-triggered workflow access to all the information we need to build a Pull Request status embed, you'll need to share some details from the original workflow in some way. One way to do that is by uploading an artifact. To do that, add these two steps to the end of your `pull_request`-triggered workflow:
111+
112+
```yaml
113+
name: Lint & Test
114+
115+
on:
116+
pull_request:
117+
118+
119+
jobs:
120+
lint-test:
121+
runs-on: ubuntu-latest
122+
123+
steps:
124+
# Your regular steps here
125+
126+
# -------------------------------------------------------------------------------
127+
128+
# Prepare the Pull Request Payload artifact. If this fails, we
129+
# we fail silently using the `continue-on-error` option. It's
130+
# nice if this succeeds, but if it fails for any reason, it
131+
# does not mean that our lint-test checks failed.
132+
- name: Prepare Pull Request Payload artifact
133+
id: prepare-artifact
134+
if: always() && github.event_name == 'pull_request'
135+
continue-on-error: true
136+
run: cat $GITHUB_EVENT_PATH | jq '.pull_request' > pull_request_payload.json
137+
138+
# This only makes sense if the previous step succeeded. To
139+
# get the original outcome of the previous step before the
140+
# `continue-on-error` conclusion is applied, we use the
141+
# `.outcome` value. This step also fails silently.
142+
- name: Upload a Build Artifact
143+
if: always() && steps.prepare-artifact.outcome == 'success'
144+
continue-on-error: true
145+
uses: actions/upload-artifact@v2
146+
with:
147+
name: pull-request-payload
148+
path: pull_request_payload.json
149+
```
150+
151+
Then, add a new workflow that is triggered whenever the workflow above is run:
152+
153+
```yaml
154+
name: Status Embed
155+
156+
on:
157+
workflow_run:
158+
workflows:
159+
- Lint & Test
160+
types:
161+
- completed
162+
163+
jobs:
164+
status_embed:
165+
name: Send Status Embed to Discord
166+
runs-on: ubuntu-latest
167+
168+
steps:
169+
# Process the artifact uploaded in the `pull_request`-triggered workflow:
170+
- name: Get Pull Request Information
171+
id: pr_info
172+
if: github.event.workflow_run.event == 'pull_request'
173+
run: |
174+
curl -s -H "Authorization: token $GITHUB_TOKEN" ${{ github.event.workflow_run.artifacts_url }} > artifacts.json
175+
DOWNLOAD_URL=$(cat artifacts.json | jq -r '.artifacts[] | select(.name == "pull-request-payload") | .archive_download_url')
176+
[ -z "$DOWNLOAD_URL" ] && exit 1
177+
wget --quiet --header="Authorization: token $GITHUB_TOKEN" -O pull_request_payload.zip $DOWNLOAD_URL || exit 2
178+
unzip -p pull_request_payload.zip > pull_request_payload.json
179+
[ -s pull_request_payload.json ] || exit 3
180+
echo "::set-output name=pr_author_login::$(jq -r '.user.login // empty' pull_request_payload.json)"
181+
echo "::set-output name=pr_number::$(jq -r '.number // empty' pull_request_payload.json)"
182+
echo "::set-output name=pr_title::$(jq -r '.title // empty' pull_request_payload.json)"
183+
echo "::set-output name=pr_source::$(jq -r '.head.label // empty' pull_request_payload.json)"
184+
env:
185+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
186+
187+
# Send an informational status embed to Discord instead of the
188+
# standard embeds that Discord sends. This embed will contain
189+
# more information and we can fine tune when we actually want
190+
# to send an embed.
191+
- name: GitHub Actions Status Embed for Discord
192+
uses: SebastiaanZ/github-status-embed-for-discord@v0.2.1
193+
with:
194+
# Webhook token
195+
webhook_id: '1234567'
196+
webhook_token: ${{ secrets.webhook_token }}
197+
198+
# We need to provide the information of the workflow that
199+
# triggered this workflow instead of this workflow.
200+
workflow_name: ${{ github.event.workflow_run.name }}
201+
run_id: ${{ github.event.workflow_run.id }}
202+
run_number: ${{ github.event.workflow_run.run_number }}
203+
status: ${{ github.event.workflow_run.conclusion }}
204+
sha: ${{ github.event.workflow_run.head_sha }}
205+
206+
# Now we can use the information extracted in the previous step:
207+
pr_author_login: ${{ steps.pr_info.outputs.pr_author_login }}
208+
pr_number: ${{ steps.pr_info.outputs.pr_number }}
209+
pr_title: ${{ steps.pr_info.outputs.pr_title }}
210+
pr_source: ${{ steps.pr_info.outputs.pr_source }}
211+
```

action.yaml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,42 @@ description: Send an enhanced GitHub Actions status embed to a Discord webhook
55
inputs:
66
workflow_name:
77
description: 'name of the workflow'
8-
required: true
8+
required: false
9+
default: ${{ github.workflow }}
910

1011
run_id:
1112
description: 'run id of this workflow run'
12-
required: true
13+
required: false
14+
default: ${{ github.run_id }}
1315

1416
run_number:
1517
description: 'number of this workflow run'
16-
required: true
18+
required: false
19+
default: ${{ github.run_number }}
1720

1821
status:
1922
description: 'results status communicated with this embed'
2023
required: true
2124

2225
repository:
2326
description: 'GitHub repository name, including owner'
24-
required: true
27+
required: false
28+
default: ${{ github.repository }}
2529

2630
actor:
2731
description: 'actor that initiated the workflow run'
28-
required: true
32+
required: false
33+
default: ${{ github.actor }}
2934

3035
ref:
3136
description: 'The branch or tag that triggered the workflow'
32-
required: true
37+
required: false
38+
default: ${{ github.ref }}
3339

3440
sha:
3541
description: 'sha of the commit that triggered the workflow'
36-
required: true
42+
required: false
43+
default: ${{ github.sha }}
3744

3845
webhook_id:
3946
description: 'ID of the Discord webhook that is targeted'
@@ -64,12 +71,12 @@ inputs:
6471
required: false
6572

6673
debug:
67-
description: 'Pull Request in jSON payload form'
74+
description: 'Output debug logging as annotations'
6875
required: false
6976
default: 'false'
7077

7178
dry_run:
72-
description: 'Pull Request in jSON payload form'
79+
description: 'Do not send a request to the webhook endpoint'
7380
required: false
7481
default: 'false'
7582

github_status_embed/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
workflow = Workflow.from_arguments(arguments)
6565
webhook = Webhook.from_arguments(arguments)
6666
if arguments.get("pull_request_payload", False):
67+
log.warning("The use of `pull_request_payload` is deprecated and will be removed in v1.0.0")
6768
pull_request = PullRequest.from_payload(arguments)
6869
else:
6970
pull_request = PullRequest.from_arguments(arguments)

github_status_embed/types.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import collections
34
import dataclasses
45
import enum
56
import json
@@ -71,6 +72,8 @@ def from_arguments(cls, arguments: typing.Dict[str, str]) -> typing.Optional[Typ
7172
value = _type[value.upper()]
7273
else:
7374
value = _type(value)
75+
if isinstance(value, collections.Sized) and len(value) == 0:
76+
raise ValueError
7477
except (ValueError, KeyError):
7578
raise InvalidArgument(f"invalid value for `{attribute}`: {value}") from None
7679
else:
@@ -206,7 +209,7 @@ class PullRequest(TypedDataclass, optional=True):
206209
def from_payload(cls, arguments: typing.Dict[str, str]) -> typing.Optional[PullRequest]:
207210
"""Create a Pull Request instance from Pull Request Payload JSON."""
208211
# Safe load the JSON Payload provided as a command line argument.
209-
raw_payload = arguments.pop('pull_request_payload')
212+
raw_payload = arguments.pop('pull_request_payload').replace("\\", "\\\\")
210213
log.debug(f"Attempting to parse PR Payload JSON: {raw_payload!r}.")
211214
try:
212215
payload = json.loads(raw_payload)

0 commit comments

Comments
 (0)