Skip to content

Commit cd3f08b

Browse files
authored
Trigger AWS Lambda tests on label (getsentry#2538)
Our AWS Lambda test suite currently doesn't run properly on external contributor PRs because it needs access to repo secrets, which it currently doesn't have. This PR adds a label to grant access to the secrets, which is invalidated upon any new code changes.
1 parent c0f4a9d commit cd3f08b

File tree

6 files changed

+157
-1
lines changed

6 files changed

+157
-1
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import json
4+
import os
5+
from urllib.parse import quote
6+
from urllib.request import Request, urlopen
7+
8+
LABEL = "Trigger: tests using secrets"
9+
10+
11+
def _has_write(repo_id: int, username: str, *, token: str) -> bool:
12+
req = Request(
13+
f"https://api.github.com/repositories/{repo_id}/collaborators/{username}/permission",
14+
headers={"Authorization": f"token {token}"},
15+
)
16+
contents = json.load(urlopen(req, timeout=10))
17+
18+
return contents["permission"] in {"admin", "write"}
19+
20+
21+
def _remove_label(repo_id: int, pr: int, label: str, *, token: str) -> None:
22+
quoted_label = quote(label)
23+
req = Request(
24+
f"https://api.github.com/repositories/{repo_id}/issues/{pr}/labels/{quoted_label}",
25+
method="DELETE",
26+
headers={"Authorization": f"token {token}"},
27+
)
28+
urlopen(req)
29+
30+
31+
def main() -> int:
32+
parser = argparse.ArgumentParser()
33+
parser.add_argument("--repo-id", type=int, required=True)
34+
parser.add_argument("--pr", type=int, required=True)
35+
parser.add_argument("--event", required=True)
36+
parser.add_argument("--username", required=True)
37+
parser.add_argument("--label-names", type=json.loads, required=True)
38+
args = parser.parse_args()
39+
40+
token = os.environ["GITHUB_TOKEN"]
41+
42+
write_permission = _has_write(args.repo_id, args.username, token=token)
43+
44+
if (
45+
not write_permission
46+
# `reopened` is included here due to close => push => reopen
47+
and args.event in {"synchronize", "reopened"}
48+
and LABEL in args.label_names
49+
):
50+
print(f"Invalidating label [{LABEL}] due to code change...")
51+
_remove_label(args.repo_id, args.pr, LABEL, token=token)
52+
args.label_names.remove(LABEL)
53+
54+
if write_permission or LABEL in args.label_names:
55+
print("Permissions passed!")
56+
print(f"- has write permission: {write_permission}")
57+
print(f"- has [{LABEL}] label: {LABEL in args.label_names}")
58+
return 0
59+
else:
60+
print("Permissions failed!")
61+
print(f"- has write permission: {write_permission}")
62+
print(f"- has [{LABEL}] label: {LABEL in args.label_names}")
63+
print(f"- args.label_names: {args.label_names}")
64+
print(
65+
f"Please have a collaborator add the [{LABEL}] label once they "
66+
f"have reviewed the code to trigger tests."
67+
)
68+
return 1
69+
70+
71+
if __name__ == "__main__":
72+
raise SystemExit(main())

.github/workflows/test-integration-aws_lambda.yml

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,49 @@ on:
44
branches:
55
- master
66
- release/**
7-
pull_request:
7+
# XXX: We are using `pull_request_target` instead of `pull_request` because we want
8+
# this to run on forks with access to the secrets necessary to run the test suite.
9+
# Prefer to use `pull_request` when possible.
10+
pull_request_target:
11+
types: [labeled, opened, reopened, synchronize]
812
# Cancel in progress workflows on pull_requests.
913
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
1014
concurrency:
1115
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
1216
cancel-in-progress: true
1317
permissions:
1418
contents: read
19+
# `write` is needed to remove the `Trigger: tests using secrets` label
20+
pull-requests: write
1521
env:
1622
SENTRY_PYTHON_TEST_AWS_ACCESS_KEY_ID: ${{ secrets.SENTRY_PYTHON_TEST_AWS_ACCESS_KEY_ID }}
1723
SENTRY_PYTHON_TEST_AWS_SECRET_ACCESS_KEY: ${{ secrets.SENTRY_PYTHON_TEST_AWS_SECRET_ACCESS_KEY }}
1824
BUILD_CACHE_KEY: ${{ github.sha }}
1925
CACHED_BUILD_PATHS: |
2026
${{ github.workspace }}/dist-serverless
2127
jobs:
28+
check-permissions:
29+
name: permissions check
30+
runs-on: ubuntu-20.04
31+
steps:
32+
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
33+
with:
34+
persist-credentials: false
35+
- name: permissions
36+
run: |
37+
python3 -uS .github/workflows/scripts/trigger_tests_on_label.py \
38+
--repo-id ${{ github.event.repository.id }} \
39+
--pr ${{ github.event.number }} \
40+
--event ${{ github.event.action }} \
41+
--username "$ARG_USERNAME" \
42+
--label-names "$ARG_LABEL_NAMES"
43+
env:
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
# these can contain special characters
46+
ARG_USERNAME: ${{ github.event.pull_request.user.login }}
47+
ARG_LABEL_NAMES: ${{ toJSON(github.event.pull_request.labels.*.name) }}
2248
test-pinned:
49+
needs: check-permissions
2350
timeout-minutes: 30
2451
name: aws_lambda pinned, python ${{ matrix.python-version }}, ${{ matrix.os }}
2552
runs-on: ${{ matrix.os }}
@@ -34,6 +61,8 @@ jobs:
3461
os: [ubuntu-20.04]
3562
steps:
3663
- uses: actions/checkout@v4
64+
with:
65+
ref: ${{ github.event.pull_request.head.sha || github.ref }}
3766
- uses: actions/setup-python@v4
3867
with:
3968
python-version: ${{ matrix.python-version }}

scripts/split-tox-gh-actions/split-tox-gh-actions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
"aws_lambda",
4242
]
4343

44+
FRAMEWORKS_NEEDING_GITHUB_SECRETS = [
45+
"aws_lambda",
46+
]
47+
4448
ENV = Environment(
4549
loader=FileSystemLoader(TEMPLATE_DIR),
4650
)
@@ -152,6 +156,7 @@ def render_template(framework, py_versions_pinned, py_versions_latest):
152156
"needs_aws_credentials": framework in FRAMEWORKS_NEEDING_AWS,
153157
"needs_clickhouse": framework in FRAMEWORKS_NEEDING_CLICKHOUSE,
154158
"needs_postgres": framework in FRAMEWORKS_NEEDING_POSTGRES,
159+
"needs_github_secrets": framework in FRAMEWORKS_NEEDING_GITHUB_SECRETS,
155160
"py_versions": {
156161
# formatted for including in the matrix
157162
"pinned": [f'"{v}"' for v in py_versions_pinned if v != "2.7"],

scripts/split-tox-gh-actions/templates/base.jinja

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ on:
66
- master
77
- release/**
88

9+
{% if needs_github_secrets %}
10+
# XXX: We are using `pull_request_target` instead of `pull_request` because we want
11+
# this to run on forks with access to the secrets necessary to run the test suite.
12+
# Prefer to use `pull_request` when possible.
13+
pull_request_target:
14+
types: [labeled, opened, reopened, synchronize]
15+
{% else %}
916
pull_request:
17+
{% endif %}
1018

1119
# Cancel in progress workflows on pull_requests.
1220
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-a-fallback-value
@@ -16,6 +24,10 @@ concurrency:
1624

1725
permissions:
1826
contents: read
27+
{% if needs_github_secrets %}
28+
# `write` is needed to remove the `Trigger: tests using secrets` label
29+
pull-requests: write
30+
{% endif %}
1931

2032
env:
2133
{% if needs_aws_credentials %}
@@ -29,6 +41,10 @@ env:
2941
{% raw %}${{ github.workspace }}/dist-serverless{% endraw %}
3042

3143
jobs:
44+
{% if needs_github_secrets %}
45+
{% include "check_permissions.jinja" %}
46+
{% endif %}
47+
3248
{% if py_versions.pinned %}
3349
{% with category="pinned", versions=py_versions.pinned %}
3450
{% include "test.jinja" %}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
check-permissions:
2+
name: permissions check
3+
runs-on: ubuntu-20.04
4+
steps:
5+
- uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0
6+
with:
7+
persist-credentials: false
8+
9+
- name: permissions
10+
run: |
11+
{% raw %}
12+
python3 -uS .github/workflows/scripts/trigger_tests_on_label.py \
13+
--repo-id ${{ github.event.repository.id }} \
14+
--pr ${{ github.event.number }} \
15+
--event ${{ github.event.action }} \
16+
--username "$ARG_USERNAME" \
17+
--label-names "$ARG_LABEL_NAMES"
18+
{% endraw %}
19+
env:
20+
{% raw %}
21+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22+
# these can contain special characters
23+
ARG_USERNAME: ${{ github.event.pull_request.user.login }}
24+
ARG_LABEL_NAMES: ${{ toJSON(github.event.pull_request.labels.*.name) }}
25+
{% endraw %}

scripts/split-tox-gh-actions/templates/test.jinja

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
test-{{ category }}:
2+
{% if needs_github_secrets %}
3+
needs: check-permissions
4+
{% endif %}
25
timeout-minutes: 30
36
{% if category == "py27" %}
47
name: {{ framework }} {{ category }}, python 2.7
@@ -41,6 +44,12 @@
4144

4245
steps:
4346
- uses: actions/checkout@v4
47+
{% if needs_github_secrets %}
48+
{% raw %}
49+
with:
50+
ref: ${{ github.event.pull_request.head.sha || github.ref }}
51+
{% endraw %}
52+
{% endif %}
4453
{% if category != "py27" %}
4554
- uses: actions/setup-python@v4
4655
with:

0 commit comments

Comments
 (0)