Skip to content

Commit 79c7509

Browse files
authored
ci: run tests without permission checks for origin pull requests (#553)
1 parent 3e1da4d commit 79c7509

3 files changed

Lines changed: 280 additions & 206 deletions

File tree

.github/maintainers_guide.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,29 @@ Information on setting up and configuring mocked events can be found in [`.githu
1818

1919
### Testing
2020

21-
When testing locally, ensure at least linting and unit tests pass by running `npm test`.
22-
Additionally, sending a PR is highly recommended with every change as there are several GitHub
23-
Actions jobs that execute what are effectively integration tests for this GitHub Action.
21+
Expected behaviors are confirmed with both unit tests and integration tests. Our unit tests run fast without secrets, while integration tests use webhooks and tokens for sending data to Slack across various techniques.
2422

25-
#### Checks on PRs
23+
#### Unit tests
2624

27-
Actions that run the integration tests on PRs from a fork will require approval before running.
28-
These checks use stored secrets so the changes should be reviewed before approving the workflow to
29-
avoid accidentally leaking tokens!
25+
Run the following scripts to confirm tests pass before opening a PR:
26+
27+
```sh
28+
$ npm test # Unit tests
29+
$ npm run lint # Lint and format
30+
$ npm run check # Typecheck
31+
```
32+
33+
The `test.yml` workflow runs these scripts for pull requests and changes to the `main` branch.
34+
35+
#### Integration tests
36+
37+
The `integration.yml` workflow uses this action in interactions with Slack using secrets saved to the `staging` environment.
38+
39+
A PR from a forked branch will fail this workflow until a maintainer reviews the code and [dispatches](https://github.com/slackapi/slack-github-action/actions/workflows/integration.yml) a test run that points to the most recent commit using the following format:
40+
41+
```
42+
pull/<NUMBER>/head
43+
```
3044

3145
### Releasing
3246

.github/workflows/integration.yml

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
name: Integration tests
2+
on:
3+
pull_request:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
inputs:
9+
ref:
10+
description: "The branch, tag, or SHA to checkout"
11+
required: true
12+
13+
jobs:
14+
integration:
15+
name: Run integration tests
16+
runs-on: ubuntu-latest
17+
environment: staging
18+
permissions:
19+
contents: read
20+
steps:
21+
- name: "check: require maintainer approval"
22+
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
23+
run: |
24+
echo "::error::Integration tests from forked branches require maintainer approval."
25+
echo "::notice::Dispatch a test run at ${{ github.server_url }}/${{ github.repository }}/actions/workflows/integration.yml with ref 'pull/${{ github.event.pull_request.number }}/head'"
26+
exit 1
27+
28+
- name: "build: checkout the latest changes"
29+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
30+
with:
31+
persist-credentials: false
32+
ref: ${{ inputs.ref || github.event.pull_request.head.sha || github.sha }}
33+
34+
- name: "build: setup the node runtime"
35+
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
36+
with:
37+
cache: npm
38+
cache-dependency-path: package-lock.json
39+
node-version-file: .nvmrc
40+
41+
- name: "build: install the required dependencies"
42+
run: npm ci
43+
44+
- name: "build: package code for distribution"
45+
run: npm run build
46+
47+
- name: "pretest(inputs): save the push event trigger commit URL"
48+
if: github.event_name == 'push'
49+
id: push
50+
run: |
51+
echo "url=$URL" >> "$GITHUB_OUTPUT"
52+
env:
53+
URL: ${{ github.event.head_commit.url }}
54+
55+
- name: "pretest(inputs): save the pull request event trigger URL"
56+
if: github.event_name == 'pull_request'
57+
id: pull_request
58+
run: |
59+
echo "url=$URL" >> "$GITHUB_OUTPUT"
60+
env:
61+
URL: ${{ github.event.pull_request.html_url }}
62+
63+
- name: "pretest(inputs): save the workflow dispatch event trigger commit URL"
64+
if: github.event_name == 'workflow_dispatch'
65+
id: workflow_dispatch
66+
run: |
67+
echo "url=https://github.com/${GITHUB_REPOSITORY}/commit/${INPUT_REF}" >> "$GITHUB_OUTPUT"
68+
env:
69+
INPUT_REF: ${{ inputs.ref }}
70+
71+
- name: "integration(wfb): send a payload to workflow builder via webhook trigger"
72+
id: wfb
73+
uses: ./
74+
with:
75+
errors: true
76+
webhook: ${{ secrets.SLACK_WEBHOOK_TRIGGER }}
77+
webhook-type: webhook-trigger
78+
payload: |
79+
author: ${{ github.event.sender.login }}
80+
channel_id: ${{ secrets.SLACK_CHANNEL_ID }}
81+
event_url: ${{ steps.push.outputs.url || steps.pull_request.outputs.url || steps.workflow_dispatch.outputs.url }}
82+
repo_name: ${{ github.event.repository.full_name }}
83+
status: ${{ job.status }}
84+
85+
- name: "integration(wfb): confirm a payload was sent"
86+
run: test -n "$WFB_OUTPUT_TIME"
87+
env:
88+
WFB_OUTPUT_TIME: ${{ steps.wfb.outputs.time }}
89+
90+
- name: "integration(botToken): post a message to channel"
91+
id: message
92+
uses: ./
93+
with:
94+
errors: true
95+
method: chat.postMessage
96+
token: ${{ secrets.SLACK_BOT_TOKEN }}
97+
payload: |
98+
channel: ${{ secrets.SLACK_CHANNEL_ID }}
99+
text: ":checkered_flag: Action happens at <https://github.com/${{ github.repository }}>"
100+
101+
- name: "integration(method): confirm a message was posted"
102+
run: test -n "$MESSAGE_OUTPUT_TS"
103+
env:
104+
MESSAGE_OUTPUT_TS: ${{ steps.message.outputs.ts }}
105+
106+
- name: "integration(method): post a message with blocks"
107+
id: blocks
108+
uses: ./
109+
with:
110+
errors: true
111+
method: chat.postMessage
112+
token: ${{ secrets.SLACK_BOT_TOKEN }}
113+
payload: |
114+
channel: ${{ secrets.SLACK_CHANNEL_ID }}
115+
text: ":eyes: Event received..."
116+
attachments:
117+
- color: "dbab09"
118+
fields:
119+
- title: "Status"
120+
short: true
121+
value: "Processing"
122+
123+
- name: "integration(method): confirm the blocks were posted"
124+
run: test -n "$BLOCKS_OUTPUT_TS"
125+
env:
126+
BLOCKS_OUTPUT_TS: ${{ steps.blocks.outputs.ts }}
127+
128+
- name: "integration(method): post a threaded message"
129+
id: timer
130+
uses: ./
131+
with:
132+
errors: true
133+
method: chat.postMessage
134+
token: ${{ secrets.SLACK_BOT_TOKEN }}
135+
payload: |
136+
channel: ${{ secrets.SLACK_CHANNEL_ID }}
137+
text: "Started at `${{ steps.blocks.outputs.time }}`"
138+
thread_ts: "${{ steps.blocks.outputs.ts }}"
139+
140+
- name: "integration(incoming): confirm the thread started"
141+
run: test -n "$TIMER_OUTPUT_TIME"
142+
env:
143+
TIMER_OUTPUT_TIME: ${{ steps.timer.outputs.time }}
144+
145+
- name: "integration(method): wait to mock event processing"
146+
run: sleep 3
147+
148+
- name: "integration(method): update the original message"
149+
id: finished
150+
uses: ./
151+
with:
152+
errors: true
153+
method: chat.update
154+
token: ${{ secrets.SLACK_BOT_TOKEN }}
155+
payload: |
156+
channel: ${{ secrets.SLACK_CHANNEL_ID }}
157+
ts: "${{ steps.blocks.outputs.ts }}"
158+
text: ":gear: Event processed!"
159+
attachments:
160+
- color: "28a745"
161+
fields:
162+
- title: "Status"
163+
short: true
164+
value: "Completed"
165+
166+
- name: "integration(method): post another threaded message"
167+
id: done
168+
uses: ./
169+
with:
170+
errors: true
171+
method: chat.postMessage
172+
token: ${{ secrets.SLACK_BOT_TOKEN }}
173+
payload: |
174+
channel: ${{ steps.blocks.outputs.channel_id }}
175+
text: "Finished at `${{ steps.finished.outputs.time }}`"
176+
thread_ts: "${{ steps.timer.outputs.thread_ts }}"
177+
178+
- name: "integration(method): post a file into a channel"
179+
id: file
180+
uses: ./
181+
with:
182+
errors: true
183+
method: files.uploadV2
184+
token: ${{ secrets.SLACK_BOT_TOKEN }}
185+
payload: |
186+
channel_id: ${{ secrets.SLACK_CHANNEL_ID }}
187+
initial_comment: ":robot_face: The codes exists here"
188+
file: .github/workflows/integration.yml
189+
filename: integration.yml
190+
191+
- name: "integration(method): react to the completed update message"
192+
uses: ./
193+
with:
194+
errors: true
195+
method: reactions.add
196+
token: ${{ secrets.SLACK_BOT_TOKEN }}
197+
payload: |
198+
channel: ${{ secrets.SLACK_CHANNEL_ID }}
199+
timestamp: ${{ steps.blocks.outputs.ts }}
200+
name: "tada"
201+
202+
- name: "integration(method): confirm the thread ended"
203+
run: test -n "$DONE_OUTPUT_TIME"
204+
env:
205+
DONE_OUTPUT_TIME: ${{ steps.done.outputs.time }}
206+
207+
- name: "integration(incoming): post a message via incoming webhook"
208+
id: incoming
209+
uses: ./
210+
with:
211+
errors: true
212+
webhook: ${{ secrets.SLACK_INCOMING_WEBHOOK }}
213+
webhook-type: incoming-webhook
214+
payload: |
215+
text: "Incoming webhook test for slack send"
216+
blocks:
217+
- type: section
218+
text:
219+
type: plain_text
220+
text: ":link: A message was received via incoming webhook"
221+
emoji: true
222+
223+
- name: "integration(incoming): confirm a webhook was posted"
224+
run: test -n "$INCOMING_WEBHOOK_OUTPUT_TIME"
225+
env:
226+
INCOMING_WEBHOOK_OUTPUT_TIME: ${{ steps.incoming.outputs.time }}
227+
228+
- name: "integration(incoming): reveal contents of the github payload"
229+
run: echo "$JSON"
230+
env:
231+
JSON: ${{ toJSON(github) }}
232+
233+
- name: "integration(incoming): post a message via payload file"
234+
id: payload_file
235+
uses: ./
236+
with:
237+
errors: true
238+
payload-file-path: ./.github/resources/.slack/incoming-webhook.json
239+
payload-templated: true
240+
webhook: ${{ secrets.SLACK_INCOMING_WEBHOOK }}
241+
webhook-type: incoming-webhook
242+
env:
243+
JOB_STATUS: ${{ job.status }}
244+
ATTACHMENT_COLOR: ${{ (job.status == 'success' && 'good') || (job.status == 'failure' && 'danger') || 'warning' }}
245+
246+
- name: "integration(incoming): confirm a payload file was posted"
247+
run: test -n "$PAYLOAD_FILE_OUTPUT_TIME"
248+
env:
249+
PAYLOAD_FILE_OUTPUT_TIME: ${{ steps.payload_file.outputs.time }}
250+
251+
- name: "chore(health): check up on recent changes to the health score"
252+
uses: slackapi/slack-health-score@d58a419f15cdaff97e9aa7f09f95772830ab66f7 # v0.1.1
253+
with:
254+
codecov_token: ${{ secrets.CODECOV_API_TOKEN }}
255+
github_token: ${{ secrets.GITHUB_TOKEN }}
256+
extension: js
257+
include: src

0 commit comments

Comments
 (0)