Skip to content

Commit 93b27c2

Browse files
authored
ci: added presubmits and continuous tests (#4)
* ci: added presubmits and continuous tests * add trigger to repo settings * chore: rename test file according to conventions (#6)
1 parent 8d94f9f commit 93b27c2

File tree

5 files changed

+249
-1
lines changed

5 files changed

+249
-1
lines changed

.github/sync-repo-settings.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ branchProtectionRules:
3030
- "conventionalcommits.org"
3131
- "header-check"
3232
# Add required status checks like presubmit tests
33+
- "core-js-sdk-pr (toolbox-testing-438616)"
3334
requiredApprovingReviewCount: 1
3435
requiresCodeOwnerReviews: true
3536
requiresStrictStatusChecks: true
3637

3738
# Set team access
3839
permissionRules:
3940
- team: senseai-eco
40-
permission: admin
41+
permission: admin
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Cloud Build Failure Reporter
16+
17+
on:
18+
workflow_call:
19+
inputs:
20+
trigger_names:
21+
required: true
22+
type: string
23+
workflow_dispatch:
24+
inputs:
25+
trigger_names:
26+
description: 'Cloud Build trigger names separated by comma.'
27+
required: true
28+
default: ''
29+
30+
jobs:
31+
report:
32+
33+
permissions:
34+
issues: 'write'
35+
checks: 'read'
36+
37+
runs-on: 'ubuntu-latest'
38+
39+
steps:
40+
- uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea' # v7
41+
with:
42+
script: |-
43+
// parse test names
44+
const testNameSubstring = '${{ inputs.trigger_names }}';
45+
const testNameFound = new Map(); //keeps track of whether each test is found
46+
testNameSubstring.split(',').forEach(testName => {
47+
testNameFound.set(testName, false);
48+
});
49+
50+
// label for all issues opened by reporter
51+
const periodicLabel = 'periodic-failure';
52+
53+
// check if any reporter opened any issues previously
54+
const prevIssues = await github.paginate(github.rest.issues.listForRepo, {
55+
...context.repo,
56+
state: 'open',
57+
creator: 'github-actions[bot]',
58+
labels: [periodicLabel]
59+
});
60+
61+
// createOrCommentIssue creates a new issue or comments on an existing issue.
62+
const createOrCommentIssue = async function (title, txt) {
63+
if (prevIssues.length < 1) {
64+
console.log('no previous issues found, creating one');
65+
await github.rest.issues.create({
66+
...context.repo,
67+
title: title,
68+
body: txt,
69+
labels: [periodicLabel]
70+
});
71+
return;
72+
}
73+
// only comment on issue related to the current test
74+
for (const prevIssue of prevIssues) {
75+
if (prevIssue.title.includes(title)){
76+
console.log(
77+
`found previous issue ${prevIssue.html_url}, adding comment`
78+
);
79+
80+
await github.rest.issues.createComment({
81+
...context.repo,
82+
issue_number: prevIssue.number,
83+
body: txt
84+
});
85+
return;
86+
}
87+
}
88+
};
89+
90+
// updateIssues comments on any existing issues. No-op if no issue exists.
91+
const updateIssues = async function (checkName, txt) {
92+
if (prevIssues.length < 1) {
93+
console.log('no previous issues found.');
94+
return;
95+
}
96+
// only comment on issue related to the current test
97+
for (const prevIssue of prevIssues) {
98+
if (prevIssue.title.includes(checkName)){
99+
console.log(`found previous issue ${prevIssue.html_url}, adding comment`);
100+
await github.rest.issues.createComment({
101+
...context.repo,
102+
issue_number: prevIssue.number,
103+
body: txt
104+
});
105+
}
106+
}
107+
};
108+
109+
// Find status of check runs.
110+
// We will find check runs for each commit and then filter for the periodic.
111+
// Checks API only allows for ref and if we use main there could be edge cases where
112+
// the check run happened on a SHA that is different from head.
113+
const commits = await github.paginate(github.rest.repos.listCommits, {
114+
...context.repo
115+
});
116+
117+
const relevantChecks = new Map();
118+
for (const commit of commits) {
119+
console.log(
120+
`checking runs at ${commit.html_url}: ${commit.commit.message}`
121+
);
122+
const checks = await github.rest.checks.listForRef({
123+
...context.repo,
124+
ref: commit.sha
125+
});
126+
127+
// Iterate through each check and find matching names
128+
for (const check of checks.data.check_runs) {
129+
console.log(`Handling test name ${check.name}`);
130+
for (const testName of testNameFound.keys()) {
131+
if (testNameFound.get(testName) === true){
132+
//skip if a check is already found for this name
133+
continue;
134+
}
135+
if (check.name.includes(testName)) {
136+
relevantChecks.set(check, commit);
137+
testNameFound.set(testName, true);
138+
}
139+
}
140+
}
141+
// Break out of the loop early if all tests are found
142+
const allTestsFound = Array.from(testNameFound.values()).every(value => value === true);
143+
if (allTestsFound){
144+
break;
145+
}
146+
}
147+
148+
// Handle each relevant check
149+
relevantChecks.forEach((commit, check) => {
150+
if (
151+
check.status === 'completed' &&
152+
check.conclusion === 'success'
153+
) {
154+
updateIssues(
155+
check.name,
156+
`[Tests are passing](${check.html_url}) for commit [${commit.sha}](${commit.html_url}).`
157+
);
158+
} else if (check.status === 'in_progress') {
159+
console.log(
160+
`Check is pending ${check.html_url} for ${commit.html_url}. Retry again later.`
161+
);
162+
} else {
163+
createOrCommentIssue(
164+
`Cloud Build Failure Reporter: ${check.name} failed`,
165+
`Cloud Build Failure Reporter found test failure for [**${check.name}** ](${check.html_url}) at [${commit.sha}](${commit.html_url}). Please fix the error and then close the issue after the **${check.name}** test passes.`
166+
);
167+
}
168+
});
169+
170+
// no periodic checks found across all commits, report it
171+
const noTestFound = Array.from(testNameFound.values()).every(value => value === false);
172+
if (noTestFound){
173+
createOrCommentIssue(
174+
'Missing periodic tests: ${{ inputs.trigger_names }}',
175+
`No periodic test is found for triggers: ${{ inputs.trigger_names }}. Last checked from ${
176+
commits[0].html_url
177+
} to ${commits[commits.length - 1].html_url}.`
178+
);
179+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: Schedule Reporter
16+
17+
on:
18+
schedule:
19+
- cron: '0 6 * * *' # Runs at 6 AM every morning
20+
21+
jobs:
22+
run_reporter:
23+
permissions:
24+
issues: 'write'
25+
checks: 'read'
26+
contents: 'read'
27+
uses: ./.github/workflows/cloud_build_failure_reporter.yml
28+
with:
29+
# TODO: Create triggers
30+
trigger_names: "core-js-sdk-test-on-merge"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
steps:
16+
- id: Install package
17+
name: 'node:${_VERSION}'
18+
entrypoint: /bin/bash
19+
dir: packages/toolbox-core
20+
args:
21+
- -c
22+
- npm ci
23+
- id: Run tests
24+
name: 'node:${_VERSION}'
25+
entrypoint: /bin/bash
26+
dir: packages/toolbox-core
27+
env:
28+
- TOOLBOX_URL=$_TOOLBOX_URL
29+
- TOOLBOX_VERSION=$_TOOLBOX_VERSION
30+
- GOOGLE_CLOUD_PROJECT=$PROJECT_ID
31+
args:
32+
- '-c'
33+
- npm test
34+
options:
35+
logging: CLOUD_LOGGING_ONLY
36+
substitutions:
37+
_VERSION: '20.0.0'
38+
_TOOLBOX_VERSION: '0.5.0'

0 commit comments

Comments
 (0)