Skip to content

Commit 0ec3bed

Browse files
authored
System tests pipeline on GitHub Actions (#11331)
Signed-off-by: Jakub Stejskal <xstejs24@gmail.com>
1 parent 6523740 commit 0ec3bed

30 files changed

Lines changed: 2214 additions & 9 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Add Comment
2+
description: "Posts a comment on the pull-request"
3+
4+
inputs:
5+
commentMessage:
6+
description: "Message that will be put as a comment"
7+
required: true
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Add comment
13+
id: comment-and-check
14+
uses: actions/github-script@v7
15+
env:
16+
MESSAGE: ${{ inputs.commentMessage }}
17+
with:
18+
script: |
19+
const {owner, repo} = context.repo;
20+
21+
const msg = process.env.MESSAGE
22+
let sha = undefined
23+
let prNumber = undefined;
24+
25+
//------------------------------------------------------------------
26+
// 1) Work out PR number & HEAD SHA
27+
//------------------------------------------------------------------
28+
if (context.payload.pull_request) {
29+
prNumber = context.payload.pull_request.number;
30+
sha ||= context.payload.pull_request.head.sha;
31+
} else if (context.payload.issue?.pull_request) {
32+
prNumber = context.payload.issue.number;
33+
if (!sha) {
34+
const pr = await github.request(
35+
context.payload.issue.pull_request.url);
36+
sha = pr.data.head.sha;
37+
}
38+
} else {
39+
sha ||= context.sha;
40+
}
41+
42+
core.info(`Going to put a comment to PR “${prNumber}” (“${sha}”) - ”${msg}”`);
43+
44+
//------------------------------------------------------------------
45+
// 2) Add / update PR comment
46+
//------------------------------------------------------------------
47+
if (prNumber) {
48+
await github.rest.issues.createComment({
49+
owner, repo, issue_number: prNumber, body: msg
50+
});
51+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: "Build Strimzi jars"
2+
description: "Build and archive Strimzi jars"
3+
4+
inputs:
5+
mvnArgs:
6+
description: "Maven arguments for the build"
7+
required: false
8+
default: ""
9+
runnerArch:
10+
description: "Architecture of GitHub runner"
11+
required: false
12+
default: "arm64"
13+
14+
runs:
15+
using: "composite"
16+
steps:
17+
- name: Install yq
18+
uses: ./.github/actions/install-yq
19+
with:
20+
architecture: ${{ inputs.runnerArch }}
21+
- name: Install Shellcheck
22+
uses: ./.github/actions/install-shellcheck
23+
with:
24+
architecture: ${{ inputs.runnerArch }}
25+
- name: Install Helm
26+
uses: ./.github/actions/install-helm
27+
- uses: actions/setup-java@v5
28+
with:
29+
distribution: "temurin"
30+
java-version: "17"
31+
cache: 'maven'
32+
33+
# Caches for Maven and Kafka
34+
- name: Cache local Maven repository
35+
uses: actions/cache@v4
36+
with:
37+
path: ~/.m2/repository
38+
key: "maven-cache | **/pom.xml"
39+
restore-keys: |
40+
maven | ${{ github.job }}
41+
maven
42+
- name: Cache Kafka binaries
43+
uses: actions/cache@v4
44+
with:
45+
path: docker-images/artifacts/binaries/kafka/archives
46+
key: '"kafka-binaries" | kafka-versions.yaml'
47+
48+
# Building the artifacts
49+
- name: Build artifacts
50+
shell: bash
51+
run: |
52+
make java_install
53+
env:
54+
MVN_ARGS: ${{ inputs.mvnArgs }}
55+
56+
- name: Create tarball with artifact
57+
shell: bash
58+
run: "tar -cvpf strimzi-binaries.tar ./docker-images/artifacts/binaries"
59+
60+
- name: Upload containers artifact
61+
uses: actions/upload-artifact@v4
62+
with:
63+
name: strimzi-binaries.tar
64+
path: strimzi-binaries.tar
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
name: Add check & status
2+
description: "Sets a commit/issue status on the given (or auto-detected) SHA."
3+
4+
inputs:
5+
checkState:
6+
description: "Check state: error, failure, pending, or success"
7+
default: success
8+
checkName:
9+
description: "Status-line “context” label (appears in the PR UI)"
10+
default: custom/check
11+
checkDescription:
12+
description: "Short description that shows next to the status"
13+
default: ''
14+
sha:
15+
description: "Commit SHA to attach the status to (auto-detected if empty)"
16+
required: false
17+
18+
runs:
19+
using: composite
20+
steps:
21+
- name: Add issue/commit check
22+
id: comment-and-check
23+
# Run only if action wasn't triggered by comment or manually
24+
if: github.event_name == 'workflow_dispatch' || github.event_name == 'issue_comment'
25+
uses: actions/github-script@v7
26+
env:
27+
CHECK_STATE: ${{ inputs.checkState }}
28+
CHECK_NAME: ${{ inputs.checkName }}
29+
CHECK_DESCRIPTION: ${{ inputs.checkDescription }}
30+
SHA: ${{ inputs.sha }}
31+
with:
32+
script: |
33+
const {owner, repo} = context.repo;
34+
35+
const checkState = process.env.CHECK_STATE
36+
const checkName = process.env.CHECK_NAME
37+
const desc = process.env.CHECK_DESCRIPTION
38+
let sha = process.env.SHA
39+
let prNumber = undefined;
40+
let isPr = undefined;
41+
42+
//------------------------------------------------------------------
43+
// 1) Work out PR number & HEAD SHA
44+
//------------------------------------------------------------------
45+
if (context.payload.pull_request) {
46+
isPr = true;
47+
prNumber = context.payload.pull_request.number;
48+
sha ||= context.payload.pull_request.head.sha;
49+
} else if (context.payload.issue?.pull_request) {
50+
isPr = true;
51+
prNumber = context.payload.issue.number;
52+
if (!sha) {
53+
const pr = await github.request(
54+
context.payload.issue.pull_request.url);
55+
sha = pr.data.head.sha;
56+
}
57+
} else {
58+
isPr = false;
59+
sha ||= context.sha;
60+
}
61+
62+
core.info(`Going to work with the following sha: “${sha}”.`);
63+
64+
if (sha) {
65+
//------------------------------------------------------------------
66+
// 2) Map state → conclusion
67+
//------------------------------------------------------------------
68+
const conclusionToState = {
69+
success: 'success',
70+
failure: 'failure',
71+
pending: 'in_progress',
72+
error: 'neutral',
73+
skipped: 'failure'
74+
};
75+
const conclusion = conclusionToState[checkState] ?? 'neutral';
76+
77+
//------------------------------------------------------------------
78+
// 3) Create NEW run if none is currently in progress
79+
//------------------------------------------------------------------
80+
const list = await github.rest.checks.listForRef({ owner, repo, ref: sha });
81+
82+
let run = list.data.check_runs
83+
.find(cr => cr.name === checkName && cr.status !== 'completed');
84+
85+
if (!run) {
86+
core.info(`Creating a new check-run ”${checkName}”`);
87+
run = (await github.rest.checks.create({
88+
owner, repo,
89+
name: checkName,
90+
head_sha: sha,
91+
status: 'in_progress',
92+
output: {
93+
title: checkName,
94+
summary: [
95+
desc,
96+
'',
97+
`[See the full workflow run](${process.env.GITHUB_SERVER_URL}/` +
98+
`${process.env.GITHUB_REPOSITORY}/actions/runs/` +
99+
`${process.env.GITHUB_RUN_ID})`
100+
].join('\n')
101+
},
102+
details_url:
103+
`${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` +
104+
`/actions/runs/${process.env.GITHUB_RUN_ID}`
105+
})).data;
106+
core.info(`Created new run: ${run.id} (status=${run.status})`)
107+
} else {
108+
core.info(`Re-using existing run #${run.id} (status=${run.status})`);
109+
}
110+
111+
//------------------------------------------------------------------
112+
// 4) Finish the run when we have a final state
113+
//------------------------------------------------------------------
114+
core.info(`Conclusion for ”${checkName}” is ”${conclusion} (state=${checkState})”`);
115+
116+
if (conclusion !== 'in_progress') {
117+
await github.rest.checks.update({
118+
owner, repo, check_run_id: run.id,
119+
status: 'completed',
120+
name: checkName,
121+
conclusion,
122+
output: {
123+
title: checkName,
124+
summary: [
125+
desc,
126+
'',
127+
`[See the full workflow run](${process.env.GITHUB_SERVER_URL}/` +
128+
`${process.env.GITHUB_REPOSITORY}/actions/runs/` +
129+
`${process.env.GITHUB_RUN_ID})`
130+
].join('\n')
131+
},
132+
details_url:
133+
`${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` +
134+
`/actions/runs/${process.env.GITHUB_RUN_ID}`
135+
});
136+
}
137+
138+
//------------------------------------------------------------------
139+
// 5) Set commit status as well
140+
//------------------------------------------------------------------
141+
if (!isPr) {
142+
const stateForStatusApi =
143+
checkState === 'pending' ? 'pending' :
144+
checkState === 'failure' ? 'failure' :
145+
checkState === 'error' ? 'error' : 'success';
146+
147+
core.info(`Updating commit status “${checkName}”=“${stateForStatusApi}” for sha: “${sha}”.`);
148+
149+
await github.rest.repos.createCommitStatus({
150+
owner, repo,
151+
sha,
152+
state: stateForStatusApi,
153+
context: checkName,
154+
description: desc,
155+
target_url:
156+
`${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}` +
157+
`/actions/runs/${process.env.GITHUB_RUN_ID}`
158+
});
159+
} else {
160+
core.info(`Not going to create commit status for sha: “${sha}”.`);
161+
}
162+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Verify users rights
2+
description: "Fails the workflow run if the triggering user lacks write permission or is not in the given organisation team."
3+
4+
inputs:
5+
message:
6+
description: "Message that will be shown as comment in case of access denial"
7+
default: "⚠️ You don’t have permission to run this workflow. Please ask a maintainer to trigger it for you."
8+
team:
9+
description: "Name of the team that contains trusted contributors"
10+
default: "contributors"
11+
12+
runs:
13+
using: composite
14+
steps:
15+
- name: Check repository permission / team membership
16+
uses: actions/github-script@v7
17+
env:
18+
TEAM: ${{ inputs.team }}
19+
MESSAGE: ${{ inputs.message }}
20+
with:
21+
script: |
22+
const {owner, repo} = context.repo;
23+
const actor = process.env.GITHUB_ACTOR;
24+
const team = process.env.TEAM
25+
const denialMessage = process.env.MESSAGE
26+
27+
//------------------------------------------------------------------
28+
// 1) Check collaborator permission (works for user *and* org repos)
29+
//------------------------------------------------------------------
30+
const perm = (await github.rest.repos.getCollaboratorPermissionLevel({
31+
owner, repo, username: actor
32+
})).data.permission; // admin | maintain | write | triage | read | none
33+
34+
if (['admin', 'maintain', 'write'].includes(perm)) {
35+
core.info(`${actor} has ${perm} permission → authorised ✅`);
36+
return;
37+
}
38+
39+
//------------------------------------------------------------------
40+
// 2) Repo is under an organisation? Then allow specific team members
41+
//------------------------------------------------------------------
42+
if (context.payload.repository.owner.type === 'Organization') {
43+
try {
44+
await github.rest.teams.getMembershipForUserInOrg({
45+
org: owner,
46+
team_slug: team,
47+
username: actor
48+
});
49+
core.info(`${actor} is in org team “${team}” → authorised ✅`);
50+
return;
51+
} catch (_) {
52+
core.info(`${actor} is not in team “${team}”.`);
53+
}
54+
}
55+
56+
let orgMember = true;
57+
try {
58+
await github.rest.orgs.getMembershipForUser({org: owner, username: actor});
59+
core.info(`${actor} is in org” → authorised ✅`);
60+
return;
61+
} catch (_) {
62+
core.info(`${actor} is not in org “${owner}”.`);
63+
}
64+
65+
//------------------------------------------------------------------
66+
// 3) Post PR comment if applicable, then fail
67+
//------------------------------------------------------------------
68+
if (context.payload.pull_request || context.payload.issue?.pull_request) {
69+
const issueNumber = (context.payload.pull_request?.number)
70+
?? (context.payload.issue.number);
71+
await github.rest.issues.createComment({
72+
owner, repo,
73+
issue_number: issueNumber,
74+
body: denialMessage
75+
});
76+
core.info(`Refusal comment posted to PR #${issueNumber}`);
77+
}
78+
79+
core.setFailed(`${actor} is not authorised ❌`);

0 commit comments

Comments
 (0)