mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-19 06:07:40 +00:00
Merge pull request #14199 from keymanapp/maint/resources/14172-pr-build-status-2
maint(resources): add pr-build-status GitHub Action to summarize build status 🤖
This commit is contained in:
commit
3be1bd942d
17 changed files with 12045 additions and 10 deletions
183
.github/workflows/pr-build-status.yml
vendored
Normal file
183
.github/workflows/pr-build-status.yml
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
name: Keyman Build Summary
|
||||
on:
|
||||
# Temporary for testing:
|
||||
push:
|
||||
branches:
|
||||
- "maint/resources/14172-pr-build-status-2"
|
||||
|
||||
check_run:
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
run_pr_build_status:
|
||||
name: Summarize build status checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR build status
|
||||
id: run_pr_build_status_script
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// This code is copied out of resources/build/pr-build-status/pr-build-status.mjs
|
||||
// where it is tested. It is copied inline here in order to avoid requiring the
|
||||
// repository to be checked out, which dramatically reduces the run time of the
|
||||
// check.
|
||||
//
|
||||
// Note: we don't currently look at check runs, only statuses
|
||||
//
|
||||
// Verify the following statuses:
|
||||
// 'user_testing'
|
||||
// 'API Verification' (github-actions[bot])
|
||||
//
|
||||
// At least 1 of the following statuses must be found:
|
||||
// 'Test*' (keyman-server), e.g. 'Test Build (Keyman)'
|
||||
// 'Ubuntu Packaging' (github-actions[bot])
|
||||
//
|
||||
// Ignore the following statuses:
|
||||
// check/web/file-size
|
||||
//
|
||||
|
||||
function reduceStatuses(statuses) {
|
||||
const filtered_statuses = statuses.reduce((o, status) => {
|
||||
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
|
||||
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
|
||||
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};;
|
||||
} else if(status.context == 'API Verification') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'check', state: status.state};
|
||||
} else if(status.context == 'Ubuntu Packaging') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
|
||||
} else if(status.context == 'check/web/file-size') {
|
||||
// Ignore check/web/file-size -- we won't block automerge for this at this point
|
||||
} else {
|
||||
// We fail with an 'unknown status' response if we get a new status check
|
||||
// so we can be sure we are not skipping known status checks
|
||||
o[status.context] = {type: 'unknown', state: status.state};
|
||||
}
|
||||
return o;
|
||||
|
||||
}, {});
|
||||
return filtered_statuses;
|
||||
}
|
||||
|
||||
//
|
||||
// Given the collection of status checks we care about, return
|
||||
// an aggregate status -- error, failed, pending, or success,
|
||||
// and a summary description
|
||||
//
|
||||
function calculateFinalStatus(filtered_statuses) {
|
||||
const counts = {};
|
||||
let hasBuilds = false;
|
||||
for(const context of Object.keys(filtered_statuses)) {
|
||||
const { state, type } = filtered_statuses[context];
|
||||
if(type == 'unknown') {
|
||||
// We special-case for unknown status checks, and never permit them
|
||||
return [
|
||||
'error', `An unknown context ${context} was found, cannot calculate build status.`
|
||||
];
|
||||
}
|
||||
if(type == 'build') {
|
||||
hasBuilds = true;
|
||||
}
|
||||
counts[state] = counts[state] ? counts[state] + 1 : 1;
|
||||
}
|
||||
|
||||
// If we do not have any statuses yet, we wait
|
||||
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
|
||||
return ['pending', 'Checks have not yet been triggered ⌛'];
|
||||
}
|
||||
|
||||
const state =
|
||||
counts.error ? 'error' :
|
||||
counts.failed ? 'failed' :
|
||||
counts.pending ? 'pending' :
|
||||
'success';
|
||||
|
||||
let description = '';
|
||||
function appendDescription(count, state) {
|
||||
if(!count) return;
|
||||
if(description != '') description += '; ';
|
||||
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
|
||||
}
|
||||
appendDescription(counts.error, 'in an error state ❌');
|
||||
appendDescription(counts.failed, 'failed ❌');
|
||||
appendDescription(counts.pending, 'pending ⌛');
|
||||
appendDescription(counts.success, 'completed successfully ✅');
|
||||
|
||||
return [ state, description ];
|
||||
}
|
||||
|
||||
async function getCommitStatuses(github, owner, repo, sha) {
|
||||
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
return statuses;
|
||||
}
|
||||
|
||||
async function getCommitCheckRuns(github, owner, repo, sha) {
|
||||
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
return statuses;
|
||||
}
|
||||
|
||||
function calculateCheckResult(statuses) {
|
||||
if(!Array.isArray(statuses)) {
|
||||
return ['error', 'Failed to retrieve status checks from GitHub ❌'];
|
||||
}
|
||||
|
||||
const filtered_statuses = reduceStatuses(statuses);
|
||||
|
||||
const result = calculateFinalStatus(filtered_statuses);
|
||||
return result;
|
||||
}
|
||||
|
||||
async function test(github, owner, repo, sha) {
|
||||
// Get statuses from sha
|
||||
const statuses = await getCommitStatuses(github, owner, repo, sha);
|
||||
return calculateCheckResult(statuses);
|
||||
}
|
||||
|
||||
async function createCheck(github, owner, repo, sha) {
|
||||
const check = await github.rest.checks.create({
|
||||
owner,
|
||||
repo,
|
||||
head_sha: sha,
|
||||
name: 'Build Outcome',
|
||||
status: 'in_progress',
|
||||
});
|
||||
return check.data.id;
|
||||
}
|
||||
|
||||
async function updateCheck(github, owner, repo, checkRunId, status, description) {
|
||||
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
|
||||
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
|
||||
|
||||
await github.rest.checks.update({
|
||||
owner,
|
||||
repo,
|
||||
check_run_id: checkRunId,
|
||||
status: checkStatus,
|
||||
conclusion,
|
||||
output: {
|
||||
title: description,
|
||||
summary: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { owner, repo } = context.repo;
|
||||
const sha = context.payload?.check_suite?.sha || context.sha;
|
||||
const checkRunId = await createCheck(github, owner, repo, sha);
|
||||
const res = await test(github, owner, repo, sha);
|
||||
await updateCheck(github, owner, repo, checkRunId, res[0], res[1]);
|
||||
16
resources/build/pr-build-status/build.sh
Executable file
16
resources/build/pr-build-status/build.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../../resources/build/builder.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
builder_describe "Test the pr-build-status.yml GHA" test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
# TODO: consider generating the .yml from here?
|
||||
|
||||
builder_run_action test npm test
|
||||
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
[
|
||||
{
|
||||
"id": 44090590145,
|
||||
"name": "build",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRAEDwQ",
|
||||
"head_sha": "11a558e926370253db37026c95b7ff5a6aa1e8fc",
|
||||
"external_id": "389dd74c-044c-5be6-a27d-4b4819c2ff55",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090590145",
|
||||
"html_url": "https://github.com/keymanapp/keyman/actions/runs/15648814574/job/44090590145",
|
||||
"details_url": "https://github.com/keymanapp/keyman/actions/runs/15648814574/job/44090590145",
|
||||
"status": "completed",
|
||||
"conclusion": "skipped",
|
||||
"started_at": "2025-06-14T05:23:18Z",
|
||||
"completed_at": "2025-06-14T05:23:18Z",
|
||||
"output": {
|
||||
"title": null,
|
||||
"summary": null,
|
||||
"text": null,
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090590145/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40118324763
|
||||
},
|
||||
"app": {
|
||||
"id": 15368,
|
||||
"client_id": "Iv1.05c79e9ad1f6bdfa",
|
||||
"slug": "github-actions",
|
||||
"node_id": "MDM6QXBwMTUzNjg=",
|
||||
"owner": {
|
||||
"login": "github",
|
||||
"id": 9919,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github",
|
||||
"html_url": "https://github.com/github",
|
||||
"followers_url": "https://api.github.com/users/github/followers",
|
||||
"following_url": "https://api.github.com/users/github/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github/orgs",
|
||||
"repos_url": "https://api.github.com/users/github/repos",
|
||||
"events_url": "https://api.github.com/users/github/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitHub Actions",
|
||||
"description": "Automate your workflow from idea to production",
|
||||
"external_url": "https://help.github.com/en/actions",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"created_at": "2018-07-30T09:30:17Z",
|
||||
"updated_at": "2025-03-07T16:35:00Z",
|
||||
"permissions": {
|
||||
"actions": "write",
|
||||
"administration": "read",
|
||||
"attestations": "write",
|
||||
"checks": "write",
|
||||
"contents": "write",
|
||||
"deployments": "write",
|
||||
"discussions": "write",
|
||||
"issues": "write",
|
||||
"merge_queues": "write",
|
||||
"metadata": "read",
|
||||
"models": "read",
|
||||
"packages": "write",
|
||||
"pages": "write",
|
||||
"pull_requests": "write",
|
||||
"repository_hooks": "write",
|
||||
"repository_projects": "write",
|
||||
"security_events": "write",
|
||||
"statuses": "write",
|
||||
"vulnerability_alerts": "read"
|
||||
},
|
||||
"events": [
|
||||
"branch_protection_rule",
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"create",
|
||||
"delete",
|
||||
"deployment",
|
||||
"deployment_status",
|
||||
"discussion",
|
||||
"discussion_comment",
|
||||
"fork",
|
||||
"gollum",
|
||||
"issues",
|
||||
"issue_comment",
|
||||
"label",
|
||||
"merge_group",
|
||||
"milestone",
|
||||
"page_build",
|
||||
"project",
|
||||
"project_card",
|
||||
"project_column",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"registry_package",
|
||||
"release",
|
||||
"repository",
|
||||
"repository_dispatch",
|
||||
"status",
|
||||
"watch",
|
||||
"workflow_dispatch",
|
||||
"workflow_run"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44090590129,
|
||||
"name": "triage",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRAEDsQ",
|
||||
"head_sha": "11a558e926370253db37026c95b7ff5a6aa1e8fc",
|
||||
"external_id": "d467db85-960e-541c-a8c1-5f09c33904f5",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090590129",
|
||||
"html_url": "https://github.com/keymanapp/keyman/actions/runs/15648814560/job/44090590129",
|
||||
"details_url": "https://github.com/keymanapp/keyman/actions/runs/15648814560/job/44090590129",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-14T05:23:21Z",
|
||||
"completed_at": "2025-06-14T05:23:29Z",
|
||||
"output": {
|
||||
"title": null,
|
||||
"summary": null,
|
||||
"text": null,
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090590129/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40118324747
|
||||
},
|
||||
"app": {
|
||||
"id": 15368,
|
||||
"client_id": "Iv1.05c79e9ad1f6bdfa",
|
||||
"slug": "github-actions",
|
||||
"node_id": "MDM6QXBwMTUzNjg=",
|
||||
"owner": {
|
||||
"login": "github",
|
||||
"id": 9919,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github",
|
||||
"html_url": "https://github.com/github",
|
||||
"followers_url": "https://api.github.com/users/github/followers",
|
||||
"following_url": "https://api.github.com/users/github/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github/orgs",
|
||||
"repos_url": "https://api.github.com/users/github/repos",
|
||||
"events_url": "https://api.github.com/users/github/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitHub Actions",
|
||||
"description": "Automate your workflow from idea to production",
|
||||
"external_url": "https://help.github.com/en/actions",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"created_at": "2018-07-30T09:30:17Z",
|
||||
"updated_at": "2025-03-07T16:35:00Z",
|
||||
"permissions": {
|
||||
"actions": "write",
|
||||
"administration": "read",
|
||||
"attestations": "write",
|
||||
"checks": "write",
|
||||
"contents": "write",
|
||||
"deployments": "write",
|
||||
"discussions": "write",
|
||||
"issues": "write",
|
||||
"merge_queues": "write",
|
||||
"metadata": "read",
|
||||
"models": "read",
|
||||
"packages": "write",
|
||||
"pages": "write",
|
||||
"pull_requests": "write",
|
||||
"repository_hooks": "write",
|
||||
"repository_projects": "write",
|
||||
"security_events": "write",
|
||||
"statuses": "write",
|
||||
"vulnerability_alerts": "read"
|
||||
},
|
||||
"events": [
|
||||
"branch_protection_rule",
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"create",
|
||||
"delete",
|
||||
"deployment",
|
||||
"deployment_status",
|
||||
"discussion",
|
||||
"discussion_comment",
|
||||
"fork",
|
||||
"gollum",
|
||||
"issues",
|
||||
"issue_comment",
|
||||
"label",
|
||||
"merge_group",
|
||||
"milestone",
|
||||
"page_build",
|
||||
"project",
|
||||
"project_card",
|
||||
"project_column",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"registry_package",
|
||||
"release",
|
||||
"repository",
|
||||
"repository_dispatch",
|
||||
"status",
|
||||
"watch",
|
||||
"workflow_dispatch",
|
||||
"workflow_run"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44090589932,
|
||||
"name": "GitGuardian Security Checks",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRAEC7A",
|
||||
"head_sha": "11a558e926370253db37026c95b7ff5a6aa1e8fc",
|
||||
"external_id": "",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090589932",
|
||||
"html_url": "https://github.com/keymanapp/keyman/runs/44090589932",
|
||||
"details_url": "https://dashboard.gitguardian.com",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-14T05:23:17Z",
|
||||
"completed_at": "2025-06-14T05:23:20Z",
|
||||
"output": {
|
||||
"title": "No secrets detected ✅",
|
||||
"summary": "**1** commit was scanned without uncovering any secrets.\n",
|
||||
"text": "Commit scanned: **1**\n\n\n\n- Pull request #14196: `maint/developer/support-buildLevel` 👉 `maint/common/pr-build-bot`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&utm_source=github&utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/>\n",
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44090589932/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40118324322
|
||||
},
|
||||
"app": {
|
||||
"id": 46505,
|
||||
"client_id": "Iv1.ec3c001966b4cc5a",
|
||||
"slug": "gitguardian",
|
||||
"node_id": "MDM6QXBwNDY1MDU=",
|
||||
"owner": {
|
||||
"login": "GitGuardian",
|
||||
"id": 27360172,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/GitGuardian",
|
||||
"html_url": "https://github.com/GitGuardian",
|
||||
"followers_url": "https://api.github.com/users/GitGuardian/followers",
|
||||
"following_url": "https://api.github.com/users/GitGuardian/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/GitGuardian/orgs",
|
||||
"repos_url": "https://api.github.com/users/GitGuardian/repos",
|
||||
"events_url": "https://api.github.com/users/GitGuardian/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/GitGuardian/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitGuardian",
|
||||
"description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).",
|
||||
"external_url": "https://dashboard.gitguardian.com",
|
||||
"html_url": "https://github.com/apps/gitguardian",
|
||||
"created_at": "2019-11-12T15:44:31Z",
|
||||
"updated_at": "2023-07-18T09:06:22Z",
|
||||
"permissions": {
|
||||
"checks": "write",
|
||||
"contents": "read",
|
||||
"emails": "read",
|
||||
"issues": "write",
|
||||
"members": "read",
|
||||
"metadata": "read",
|
||||
"organization_hooks": "write",
|
||||
"pull_requests": "write"
|
||||
},
|
||||
"events": [
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"commit_comment",
|
||||
"create",
|
||||
"delete",
|
||||
"organization",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"repository"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
[
|
||||
{
|
||||
"id": 44134134750,
|
||||
"name": "triage",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRplz3g",
|
||||
"head_sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"external_id": "aece869f-5587-5930-867f-8649375f3d24",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44134134750",
|
||||
"html_url": "https://github.com/keymanapp/keyman/actions/runs/15667765443/job/44134134750",
|
||||
"details_url": "https://github.com/keymanapp/keyman/actions/runs/15667765443/job/44134134750",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-15T21:54:36Z",
|
||||
"completed_at": "2025-06-15T21:54:41Z",
|
||||
"output": {
|
||||
"title": null,
|
||||
"summary": null,
|
||||
"text": null,
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44134134750/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40159371625
|
||||
},
|
||||
"app": {
|
||||
"id": 15368,
|
||||
"client_id": "Iv1.05c79e9ad1f6bdfa",
|
||||
"slug": "github-actions",
|
||||
"node_id": "MDM6QXBwMTUzNjg=",
|
||||
"owner": {
|
||||
"login": "github",
|
||||
"id": 9919,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github",
|
||||
"html_url": "https://github.com/github",
|
||||
"followers_url": "https://api.github.com/users/github/followers",
|
||||
"following_url": "https://api.github.com/users/github/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github/orgs",
|
||||
"repos_url": "https://api.github.com/users/github/repos",
|
||||
"events_url": "https://api.github.com/users/github/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitHub Actions",
|
||||
"description": "Automate your workflow from idea to production",
|
||||
"external_url": "https://help.github.com/en/actions",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"created_at": "2018-07-30T09:30:17Z",
|
||||
"updated_at": "2025-03-07T16:35:00Z",
|
||||
"permissions": {
|
||||
"actions": "write",
|
||||
"administration": "read",
|
||||
"attestations": "write",
|
||||
"checks": "write",
|
||||
"contents": "write",
|
||||
"deployments": "write",
|
||||
"discussions": "write",
|
||||
"issues": "write",
|
||||
"merge_queues": "write",
|
||||
"metadata": "read",
|
||||
"models": "read",
|
||||
"packages": "write",
|
||||
"pages": "write",
|
||||
"pull_requests": "write",
|
||||
"repository_hooks": "write",
|
||||
"repository_projects": "write",
|
||||
"security_events": "write",
|
||||
"statuses": "write",
|
||||
"vulnerability_alerts": "read"
|
||||
},
|
||||
"events": [
|
||||
"branch_protection_rule",
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"create",
|
||||
"delete",
|
||||
"deployment",
|
||||
"deployment_status",
|
||||
"discussion",
|
||||
"discussion_comment",
|
||||
"fork",
|
||||
"gollum",
|
||||
"issues",
|
||||
"issue_comment",
|
||||
"label",
|
||||
"merge_group",
|
||||
"milestone",
|
||||
"page_build",
|
||||
"project",
|
||||
"project_card",
|
||||
"project_column",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"registry_package",
|
||||
"release",
|
||||
"repository",
|
||||
"repository_dispatch",
|
||||
"status",
|
||||
"watch",
|
||||
"workflow_dispatch",
|
||||
"workflow_run"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44134134737,
|
||||
"name": "GitGuardian Security Checks",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRplz0Q",
|
||||
"head_sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"external_id": "",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44134134737",
|
||||
"html_url": "https://github.com/keymanapp/keyman/runs/44134134737",
|
||||
"details_url": "https://dashboard.gitguardian.com",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-15T21:54:32Z",
|
||||
"completed_at": "2025-06-15T21:54:44Z",
|
||||
"output": {
|
||||
"title": "No secrets detected ✅",
|
||||
"summary": "**5** commits were scanned without uncovering any secrets.\n",
|
||||
"text": "Commits scanned: **5**\n\n\n\n- Pull request #14196: `maint/developer/support-buildLevel` 👉 `maint/common/pr-build-bot`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&utm_source=github&utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/>\n",
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44134134737/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40159371205
|
||||
},
|
||||
"app": {
|
||||
"id": 46505,
|
||||
"client_id": "Iv1.ec3c001966b4cc5a",
|
||||
"slug": "gitguardian",
|
||||
"node_id": "MDM6QXBwNDY1MDU=",
|
||||
"owner": {
|
||||
"login": "GitGuardian",
|
||||
"id": 27360172,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/GitGuardian",
|
||||
"html_url": "https://github.com/GitGuardian",
|
||||
"followers_url": "https://api.github.com/users/GitGuardian/followers",
|
||||
"following_url": "https://api.github.com/users/GitGuardian/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/GitGuardian/orgs",
|
||||
"repos_url": "https://api.github.com/users/GitGuardian/repos",
|
||||
"events_url": "https://api.github.com/users/GitGuardian/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/GitGuardian/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitGuardian",
|
||||
"description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).",
|
||||
"external_url": "https://dashboard.gitguardian.com",
|
||||
"html_url": "https://github.com/apps/gitguardian",
|
||||
"created_at": "2019-11-12T15:44:31Z",
|
||||
"updated_at": "2023-07-18T09:06:22Z",
|
||||
"permissions": {
|
||||
"checks": "write",
|
||||
"contents": "read",
|
||||
"emails": "read",
|
||||
"issues": "write",
|
||||
"members": "read",
|
||||
"metadata": "read",
|
||||
"organization_hooks": "write",
|
||||
"pull_requests": "write"
|
||||
},
|
||||
"events": [
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"commit_comment",
|
||||
"create",
|
||||
"delete",
|
||||
"organization",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"repository"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,246 @@
|
|||
[
|
||||
{
|
||||
"id": 44131811673,
|
||||
"name": "GitGuardian Security Checks",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRnYBWQ",
|
||||
"head_sha": "870d37f56b489ef5067b4352a06505369d9038e0",
|
||||
"external_id": "",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44131811673",
|
||||
"html_url": "https://github.com/keymanapp/keyman/runs/44131811673",
|
||||
"details_url": "https://dashboard.gitguardian.com",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-15T19:40:57Z",
|
||||
"completed_at": "2025-06-15T19:41:28Z",
|
||||
"output": {
|
||||
"title": "No secrets detected ✅",
|
||||
"summary": "**2** commits were scanned without uncovering any secrets.\n",
|
||||
"text": "Commits scanned: **2**\n\n\n\n- Pull request #14196: `maint/developer/support-buildLevel` 👉 `maint/common/pr-build-bot`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&utm_source=github&utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/>\n",
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44131811673/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40157100674
|
||||
},
|
||||
"app": {
|
||||
"id": 46505,
|
||||
"client_id": "Iv1.ec3c001966b4cc5a",
|
||||
"slug": "gitguardian",
|
||||
"node_id": "MDM6QXBwNDY1MDU=",
|
||||
"owner": {
|
||||
"login": "GitGuardian",
|
||||
"id": 27360172,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/GitGuardian",
|
||||
"html_url": "https://github.com/GitGuardian",
|
||||
"followers_url": "https://api.github.com/users/GitGuardian/followers",
|
||||
"following_url": "https://api.github.com/users/GitGuardian/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/GitGuardian/orgs",
|
||||
"repos_url": "https://api.github.com/users/GitGuardian/repos",
|
||||
"events_url": "https://api.github.com/users/GitGuardian/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/GitGuardian/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitGuardian",
|
||||
"description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).",
|
||||
"external_url": "https://dashboard.gitguardian.com",
|
||||
"html_url": "https://github.com/apps/gitguardian",
|
||||
"created_at": "2019-11-12T15:44:31Z",
|
||||
"updated_at": "2023-07-18T09:06:22Z",
|
||||
"permissions": {
|
||||
"checks": "write",
|
||||
"contents": "read",
|
||||
"emails": "read",
|
||||
"issues": "write",
|
||||
"members": "read",
|
||||
"metadata": "read",
|
||||
"organization_hooks": "write",
|
||||
"pull_requests": "write"
|
||||
},
|
||||
"events": [
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"commit_comment",
|
||||
"create",
|
||||
"delete",
|
||||
"organization",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"repository"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44131810574,
|
||||
"name": "triage",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKRnX9Dg",
|
||||
"head_sha": "870d37f56b489ef5067b4352a06505369d9038e0",
|
||||
"external_id": "3d143feb-b3d7-59b2-ae1b-fe551167c2b4",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44131810574",
|
||||
"html_url": "https://github.com/keymanapp/keyman/actions/runs/15666730139/job/44131810574",
|
||||
"details_url": "https://github.com/keymanapp/keyman/actions/runs/15666730139/job/44131810574",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-15T19:40:57Z",
|
||||
"completed_at": "2025-06-15T19:41:04Z",
|
||||
"output": {
|
||||
"title": null,
|
||||
"summary": null,
|
||||
"text": null,
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44131810574/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40157101634
|
||||
},
|
||||
"app": {
|
||||
"id": 15368,
|
||||
"client_id": "Iv1.05c79e9ad1f6bdfa",
|
||||
"slug": "github-actions",
|
||||
"node_id": "MDM6QXBwMTUzNjg=",
|
||||
"owner": {
|
||||
"login": "github",
|
||||
"id": 9919,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github",
|
||||
"html_url": "https://github.com/github",
|
||||
"followers_url": "https://api.github.com/users/github/followers",
|
||||
"following_url": "https://api.github.com/users/github/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github/orgs",
|
||||
"repos_url": "https://api.github.com/users/github/repos",
|
||||
"events_url": "https://api.github.com/users/github/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitHub Actions",
|
||||
"description": "Automate your workflow from idea to production",
|
||||
"external_url": "https://help.github.com/en/actions",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"created_at": "2018-07-30T09:30:17Z",
|
||||
"updated_at": "2025-03-07T16:35:00Z",
|
||||
"permissions": {
|
||||
"actions": "write",
|
||||
"administration": "read",
|
||||
"attestations": "write",
|
||||
"checks": "write",
|
||||
"contents": "write",
|
||||
"deployments": "write",
|
||||
"discussions": "write",
|
||||
"issues": "write",
|
||||
"merge_queues": "write",
|
||||
"metadata": "read",
|
||||
"models": "read",
|
||||
"packages": "write",
|
||||
"pages": "write",
|
||||
"pull_requests": "write",
|
||||
"repository_hooks": "write",
|
||||
"repository_projects": "write",
|
||||
"security_events": "write",
|
||||
"statuses": "write",
|
||||
"vulnerability_alerts": "read"
|
||||
},
|
||||
"events": [
|
||||
"branch_protection_rule",
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"create",
|
||||
"delete",
|
||||
"deployment",
|
||||
"deployment_status",
|
||||
"discussion",
|
||||
"discussion_comment",
|
||||
"fork",
|
||||
"gollum",
|
||||
"issues",
|
||||
"issue_comment",
|
||||
"label",
|
||||
"merge_group",
|
||||
"milestone",
|
||||
"page_build",
|
||||
"project",
|
||||
"project_card",
|
||||
"project_column",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"registry_package",
|
||||
"release",
|
||||
"repository",
|
||||
"repository_dispatch",
|
||||
"status",
|
||||
"watch",
|
||||
"workflow_dispatch",
|
||||
"workflow_run"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14196",
|
||||
"id": 2591938321,
|
||||
"number": 14196,
|
||||
"head": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/common/pr-build-bot",
|
||||
"sha": "81c941313bcccd8405364731ce61672588285d5d",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,246 @@
|
|||
[
|
||||
{
|
||||
"id": 44207248981,
|
||||
"name": "triage",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKSvUWVQ",
|
||||
"head_sha": "8fd5ccc9026b85d6a780d8bcf862380e7a89aefb",
|
||||
"external_id": "08ccda57-e306-5823-a607-291b5482a6f2",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44207248981",
|
||||
"html_url": "https://github.com/keymanapp/keyman/actions/runs/15691412642/job/44207248981",
|
||||
"details_url": "https://github.com/keymanapp/keyman/actions/runs/15691412642/job/44207248981",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-16T20:38:08Z",
|
||||
"completed_at": "2025-06-16T20:38:12Z",
|
||||
"output": {
|
||||
"title": null,
|
||||
"summary": null,
|
||||
"text": null,
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44207248981/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40219154773
|
||||
},
|
||||
"app": {
|
||||
"id": 15368,
|
||||
"client_id": "Iv1.05c79e9ad1f6bdfa",
|
||||
"slug": "github-actions",
|
||||
"node_id": "MDM6QXBwMTUzNjg=",
|
||||
"owner": {
|
||||
"login": "github",
|
||||
"id": 9919,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/github",
|
||||
"html_url": "https://github.com/github",
|
||||
"followers_url": "https://api.github.com/users/github/followers",
|
||||
"following_url": "https://api.github.com/users/github/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/github/orgs",
|
||||
"repos_url": "https://api.github.com/users/github/repos",
|
||||
"events_url": "https://api.github.com/users/github/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/github/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitHub Actions",
|
||||
"description": "Automate your workflow from idea to production",
|
||||
"external_url": "https://help.github.com/en/actions",
|
||||
"html_url": "https://github.com/apps/github-actions",
|
||||
"created_at": "2018-07-30T09:30:17Z",
|
||||
"updated_at": "2025-03-07T16:35:00Z",
|
||||
"permissions": {
|
||||
"actions": "write",
|
||||
"administration": "read",
|
||||
"attestations": "write",
|
||||
"checks": "write",
|
||||
"contents": "write",
|
||||
"deployments": "write",
|
||||
"discussions": "write",
|
||||
"issues": "write",
|
||||
"merge_queues": "write",
|
||||
"metadata": "read",
|
||||
"models": "read",
|
||||
"packages": "write",
|
||||
"pages": "write",
|
||||
"pull_requests": "write",
|
||||
"repository_hooks": "write",
|
||||
"repository_projects": "write",
|
||||
"security_events": "write",
|
||||
"statuses": "write",
|
||||
"vulnerability_alerts": "read"
|
||||
},
|
||||
"events": [
|
||||
"branch_protection_rule",
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"create",
|
||||
"delete",
|
||||
"deployment",
|
||||
"deployment_status",
|
||||
"discussion",
|
||||
"discussion_comment",
|
||||
"fork",
|
||||
"gollum",
|
||||
"issues",
|
||||
"issue_comment",
|
||||
"label",
|
||||
"merge_group",
|
||||
"milestone",
|
||||
"page_build",
|
||||
"project",
|
||||
"project_card",
|
||||
"project_column",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"registry_package",
|
||||
"release",
|
||||
"repository",
|
||||
"repository_dispatch",
|
||||
"status",
|
||||
"watch",
|
||||
"workflow_dispatch",
|
||||
"workflow_run"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14199",
|
||||
"id": 2594414200,
|
||||
"number": 14199,
|
||||
"head": {
|
||||
"ref": "maint/resources/14172-pr-build-status-2",
|
||||
"sha": "c0d28233302cbc22c7d7625dfba5930006ad7d0b",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 44207248005,
|
||||
"name": "GitGuardian Security Checks",
|
||||
"node_id": "CR_kwDOAY2xT88AAAAKSvUShQ",
|
||||
"head_sha": "8fd5ccc9026b85d6a780d8bcf862380e7a89aefb",
|
||||
"external_id": "",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44207248005",
|
||||
"html_url": "https://github.com/keymanapp/keyman/runs/44207248005",
|
||||
"details_url": "https://dashboard.gitguardian.com",
|
||||
"status": "completed",
|
||||
"conclusion": "success",
|
||||
"started_at": "2025-06-16T20:38:03Z",
|
||||
"completed_at": "2025-06-16T20:38:06Z",
|
||||
"output": {
|
||||
"title": "No secrets detected ✅",
|
||||
"summary": "**8** commits were scanned without uncovering any secrets.\n",
|
||||
"text": "Commits scanned: **8**\n\n\n\n- Pull request #14199: `maint/resources/14172-pr-build-status-2` 👉 `maint/developer/support-buildLevel`\n \n\n🦉 [GitGuardian](https://dashboard.gitguardian.com/auth/login/?utm_medium=checkruns&utm_source=github&utm_campaign=cr1) detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.<br/>\n",
|
||||
"annotations_count": 0,
|
||||
"annotations_url": "https://api.github.com/repos/keymanapp/keyman/check-runs/44207248005/annotations"
|
||||
},
|
||||
"check_suite": {
|
||||
"id": 40219152500
|
||||
},
|
||||
"app": {
|
||||
"id": 46505,
|
||||
"client_id": "Iv1.ec3c001966b4cc5a",
|
||||
"slug": "gitguardian",
|
||||
"node_id": "MDM6QXBwNDY1MDU=",
|
||||
"owner": {
|
||||
"login": "GitGuardian",
|
||||
"id": 27360172,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjI3MzYwMTcy",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/27360172?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/GitGuardian",
|
||||
"html_url": "https://github.com/GitGuardian",
|
||||
"followers_url": "https://api.github.com/users/GitGuardian/followers",
|
||||
"following_url": "https://api.github.com/users/GitGuardian/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/GitGuardian/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/GitGuardian/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/GitGuardian/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/GitGuardian/orgs",
|
||||
"repos_url": "https://api.github.com/users/GitGuardian/repos",
|
||||
"events_url": "https://api.github.com/users/GitGuardian/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/GitGuardian/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"name": "GitGuardian",
|
||||
"description": "# 🦉 What is GitGuardian?\r\n\r\nGitGuardian Secrets Detection detects and fixes vulnerabilities in source code at every step of the software development lifecycle, covering 350+ types of secrets like API keys, database connection strings, private keys, certificates, and more. The platform’s policy engine enables security teams to monitor and enforce rules across their VCS, DevOps tools, and infrastructure-as-code configurations. Our automated remediation playbooks and collaboration features bring security and development teams together to resolve incidents fast and in full.\r\n\r\n## 1. Scan your codebase for 350+ types of secrets\r\nGitGuardian scans your GitHub repositories and raises alerts only for critical secrets, such as API keys or other credentials. At scale, GitGuardian’s detection algorithm has been battle-tested on over three years of activity in all public GitHub repositories – totaling over 1 billion scanned commits!\r\n\r\n## 2. Quickly remediate your hard-coded secrets\r\nIf you ever experience a leak involving a credential, we have a complete remediation guide used by 100k+ developers each year. We’ll show you how to revoke the secret and remove it from your git history.\r\n\r\n## 3. Prevent secrets from reaching GitHub\r\nInstall ggshield, the GitGuardian CLI, and add secrets detection to your local development workflow using pre-commit and pre-push git hooks integrations.\r\n\r\n# 🙋♂️ FAQ\r\n\r\n**What is your pricing?**\r\nGitGuardian is free for teams under 25 developers and offers a 30-day trial for larger teams.\r\n\r\n**How can I be sure that GitGuardian won’t raise too many false positives?**\r\nWe have scanned billions of commits, sent millions of alerts since 2018, and integrated each feedback to improve our algorithm. Our alerts currently receive 91% “true positive” feedback from developers.\r\n\r\n**My repositories are private; why should I install automated secret detection?**\r\nImagine if there were a plain text file with all your credit card numbers inside, you wouldn’t put this file inside your company’s git repository. Secrets are just as sensitive and should be handled with special care.\r\n\r\n**Is GitGuardian available to be installed on-premise?**\r\nYes, you can contact one of our security specialists to look over the possibility of installing GitGuardian on-premise on your repositories.\r\n\r\n# ⚒️ Installation notes \r\n\r\nYou should install GitGuardian directly through your [GitGuardian](https://dashboard.gitguardian.com/) workspace on the Integration settings page. \r\n\r\nSo that you know, your GitHub organization or GitHub account can only be associated with a single GitGuardian workspace.\r\n\r\n# 👋 Support\r\n\r\nIf you experience any difficulties or have any questions, please reach out to us by email ([support@gitguardian.com](mailto:support@gitguardian.com)).",
|
||||
"external_url": "https://dashboard.gitguardian.com",
|
||||
"html_url": "https://github.com/apps/gitguardian",
|
||||
"created_at": "2019-11-12T15:44:31Z",
|
||||
"updated_at": "2023-07-18T09:06:22Z",
|
||||
"permissions": {
|
||||
"checks": "write",
|
||||
"contents": "read",
|
||||
"emails": "read",
|
||||
"issues": "write",
|
||||
"members": "read",
|
||||
"metadata": "read",
|
||||
"organization_hooks": "write",
|
||||
"pull_requests": "write"
|
||||
},
|
||||
"events": [
|
||||
"check_run",
|
||||
"check_suite",
|
||||
"commit_comment",
|
||||
"create",
|
||||
"delete",
|
||||
"organization",
|
||||
"public",
|
||||
"pull_request",
|
||||
"pull_request_review",
|
||||
"pull_request_review_comment",
|
||||
"push",
|
||||
"repository"
|
||||
]
|
||||
},
|
||||
"pull_requests": [
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14199",
|
||||
"id": 2594414200,
|
||||
"number": 14199,
|
||||
"head": {
|
||||
"ref": "maint/resources/14172-pr-build-status-2",
|
||||
"sha": "c0d28233302cbc22c7d7625dfba5930006ad7d0b",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"ref": "maint/developer/support-buildLevel",
|
||||
"sha": "825f7d2df0069fa56a8f2a534b42a49ee1f21ca1",
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"name": "keyman"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
[
|
||||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/statuses/8fd5ccc9026b85d6a780d8bcf862380e7a89aefb",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4",
|
||||
"id": 37011551277,
|
||||
"node_id": "SC_kwDOAY2xT88AAAAIng90LQ",
|
||||
"state": "success",
|
||||
"description": "User tests are not required",
|
||||
"target_url": "https://github.com/keymanapp/keyman/pull/14199#issuecomment-2975084272",
|
||||
"context": "user_testing",
|
||||
"created_at": "2025-06-16T20:38:04Z",
|
||||
"updated_at": "2025-06-16T20:38:04Z",
|
||||
"creator": {
|
||||
"login": "keymanapp-test-bot[bot]",
|
||||
"id": 89363325,
|
||||
"node_id": "MDM6Qm90ODkzNjMzMjU=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/in/133554?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D",
|
||||
"html_url": "https://github.com/apps/keymanapp-test-bot",
|
||||
"followers_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/followers",
|
||||
"following_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/orgs",
|
||||
"repos_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/repos",
|
||||
"events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keymanapp-test-bot%5Bbot%5D/received_events",
|
||||
"type": "Bot",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
}
|
||||
}
|
||||
]
|
||||
1528
resources/build/pr-build-status/package-lock.json
generated
Normal file
1528
resources/build/pr-build-status/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
20
resources/build/pr-build-status/package.json
Normal file
20
resources/build/pr-build-status/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"description": "CHeck pull request build status - development area",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.9.1",
|
||||
"@actions/github": "^6.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^11.2.2",
|
||||
"mocha-teamcity-reporter": "^4.0.0",
|
||||
"chai": "^5.1.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "pr-build-status.mjs",
|
||||
"name": "@keymanapp/pr-build-status",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "mocha pr-build-status.tests.mjs"
|
||||
}
|
||||
}
|
||||
188
resources/build/pr-build-status/pr-build-status.mjs
Normal file
188
resources/build/pr-build-status/pr-build-status.mjs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*
|
||||
* Clone of the pr-build-status.yml file for unit testing and development. The
|
||||
* indented section of this file below is copied verbatim into
|
||||
* .github/workflows/pr-build-status.yml, and allows us to develop the YAML
|
||||
* script without introducing a dependency on the repository.
|
||||
*/
|
||||
|
||||
// Copy the indented section into the step for pr-build-status.mjs.
|
||||
|
||||
// START OF CLONED SECTION
|
||||
|
||||
// This code is copied out of resources/build/pr-build-status/pr-build-status.mjs
|
||||
// where it is tested. It is copied inline here in order to avoid requiring the
|
||||
// repository to be checked out, which dramatically reduces the run time of the
|
||||
// check.
|
||||
//
|
||||
// Note: we don't currently look at check runs, only statuses
|
||||
//
|
||||
// Verify the following statuses:
|
||||
// 'user_testing'
|
||||
// 'API Verification' (github-actions[bot])
|
||||
//
|
||||
// At least 1 of the following statuses must be found:
|
||||
// 'Test*' (keyman-server), e.g. 'Test Build (Keyman)'
|
||||
// 'Ubuntu Packaging' (github-actions[bot])
|
||||
//
|
||||
// Ignore the following statuses:
|
||||
// check/web/file-size
|
||||
//
|
||||
|
||||
function reduceStatuses(statuses) {
|
||||
const filtered_statuses = statuses.reduce((o, status) => {
|
||||
if(status.creator?.login == 'keyman-server' && status.context.startsWith('Test')) {
|
||||
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
|
||||
} else if(status.creator?.login == 'keymanapp-test-bot[bot]' && status.context == 'user_testing') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'user-test', state: status.state};;
|
||||
} else if(status.context == 'API Verification') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'check', state: status.state};
|
||||
} else if(status.context == 'Ubuntu Packaging') {
|
||||
if(!o[status.context]) o[status.context] = {type: 'build', state: status.state};
|
||||
} else if(status.context == 'check/web/file-size') {
|
||||
// Ignore check/web/file-size -- we won't block automerge for this at this point
|
||||
} else {
|
||||
// We fail with an 'unknown status' response if we get a new status check
|
||||
// so we can be sure we are not skipping known status checks
|
||||
o[status.context] = {type: 'unknown', state: status.state};
|
||||
}
|
||||
return o;
|
||||
|
||||
}, {});
|
||||
return filtered_statuses;
|
||||
}
|
||||
|
||||
//
|
||||
// Given the collection of status checks we care about, return
|
||||
// an aggregate status -- error, failed, pending, or success,
|
||||
// and a summary description
|
||||
//
|
||||
function calculateFinalStatus(filtered_statuses) {
|
||||
const counts = {};
|
||||
let hasBuilds = false;
|
||||
for(const context of Object.keys(filtered_statuses)) {
|
||||
const { state, type } = filtered_statuses[context];
|
||||
if(type == 'unknown') {
|
||||
// We special-case for unknown status checks, and never permit them
|
||||
return [
|
||||
'error', `An unknown context ${context} was found, cannot calculate build status.`
|
||||
];
|
||||
}
|
||||
if(type == 'build') {
|
||||
hasBuilds = true;
|
||||
}
|
||||
counts[state] = counts[state] ? counts[state] + 1 : 1;
|
||||
}
|
||||
|
||||
// If we do not have any statuses yet, we wait
|
||||
if(Object.keys(filtered_statuses).length == 0 || !hasBuilds) {
|
||||
return ['pending', 'Checks have not yet been triggered ⌛'];
|
||||
}
|
||||
|
||||
const state =
|
||||
counts.error ? 'error' :
|
||||
counts.failed ? 'failed' :
|
||||
counts.pending ? 'pending' :
|
||||
'success';
|
||||
|
||||
let description = ''; //;
|
||||
function appendDescription(count, state) {
|
||||
if(!count) return;
|
||||
if(description != '') description += '; ';
|
||||
description += `${count} check${count == 1 ? '' : 's'} ${state}`;
|
||||
}
|
||||
appendDescription(counts.error, 'in an error state ❌');
|
||||
appendDescription(counts.failed, 'failed ❌');
|
||||
appendDescription(counts.pending, 'pending ⌛');
|
||||
appendDescription(counts.success, 'completed successfully ✅');
|
||||
|
||||
return [ state, description ];
|
||||
}
|
||||
|
||||
async function getCommitStatuses(github, owner, repo, sha) {
|
||||
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/statuses', {
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
return statuses;
|
||||
}
|
||||
|
||||
async function getCommitCheckRuns(github, owner, repo, sha) {
|
||||
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{sha}/check-runs', {
|
||||
owner,
|
||||
repo,
|
||||
sha,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
});
|
||||
return statuses;
|
||||
}
|
||||
|
||||
function calculateCheckResult(statuses) {
|
||||
if(!Array.isArray(statuses)) {
|
||||
return ['error', 'Failed to retrieve status checks from GitHub ❌'];
|
||||
}
|
||||
|
||||
const filtered_statuses = reduceStatuses(statuses);
|
||||
|
||||
const result = calculateFinalStatus(filtered_statuses);
|
||||
return result;
|
||||
}
|
||||
|
||||
async function test(github, owner, repo, sha) {
|
||||
// Get statuses from sha
|
||||
const statuses = await getCommitStatuses(github, owner, repo, sha);
|
||||
return calculateCheckResult(statuses);
|
||||
}
|
||||
|
||||
async function createCheck(github, owner, repo, sha) {
|
||||
const check = await github.rest.checks.create({
|
||||
owner,
|
||||
repo,
|
||||
head_sha: sha,
|
||||
name: 'Build Outcome',
|
||||
status: 'in_progress',
|
||||
});
|
||||
return check.data.id;
|
||||
}
|
||||
|
||||
async function updateCheck(github, owner, repo, checkRunId, status, description) {
|
||||
// To ensure that
|
||||
const checkStatus = status == 'pending' ? 'in_progress' : 'completed';
|
||||
const conclusion = checkStatus == 'in_progress' ? undefined : (status == 'success' ? 'success' : 'failure');
|
||||
|
||||
await github.rest.checks.update({
|
||||
owner,
|
||||
repo,
|
||||
check_run_id: checkRunId,
|
||||
status: checkStatus,
|
||||
conclusion,
|
||||
output: {
|
||||
title: description,
|
||||
summary: ''
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// END OF CLONED SECTION
|
||||
// THIS SECTION MUST BE INCLUDED, UNCOMMENTED IN THE .yml
|
||||
|
||||
// const { owner, repo } = context.repo;
|
||||
// const sha = context.payload?.check_suite?.sha || context.sha;
|
||||
// const checkRunId = await createCheck(github, owner, repo, sha);
|
||||
// const res = await test(github, owner, repo, sha);
|
||||
// await updateCheck(github, owner, repo, checkRunId, res[0], res[1]);
|
||||
|
||||
// END OF COMMENTED SECTION
|
||||
|
||||
// Following code is used only for unit testing; do not include in the .yml
|
||||
|
||||
export const unitTestEndpoints = {
|
||||
getCommitStatuses, calculateCheckResult, getCommitCheckRuns
|
||||
};
|
||||
44
resources/build/pr-build-status/pr-build-status.tests.mjs
Normal file
44
resources/build/pr-build-status/pr-build-status.tests.mjs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import * as fs from 'node:fs';
|
||||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { unitTestEndpoints } from './pr-build-status.mjs';
|
||||
|
||||
// debug only
|
||||
import { getOctokit } from '@actions/github';
|
||||
import * as process from 'node:process';
|
||||
|
||||
const commits = {
|
||||
'825f7d2df0069fa56a8f2a534b42a49ee1f21ca1': [ 'success', '24 checks completed successfully ✅' ],
|
||||
'870d37f56b489ef5067b4352a06505369d9038e0': [ 'error', '6 checks in an error state ❌; 17 checks completed successfully ✅' ],
|
||||
'11a558e926370253db37026c95b7ff5a6aa1e8fc': [ 'success', '22 checks completed successfully ✅' ],
|
||||
'8fd5ccc9026b85d6a780d8bcf862380e7a89aefb': [ 'pending', 'Checks have not yet been triggered ⌛' ],
|
||||
'153683cfb007c6066c9dcf71d25afa4c66efa17f': [ 'pending', 'Checks have not yet been triggered ⌛' ],
|
||||
};
|
||||
|
||||
// When adding new SHAs to test, run this to collect data from GitHub
|
||||
const debug = false;
|
||||
if(debug) {
|
||||
|
||||
async function writeTestFile(sha) {
|
||||
fs.writeFileSync('fixtures/' + sha + '-statuses.json',
|
||||
JSON.stringify(await unitTestEndpoints.getCommitStatuses(octokit, 'keymanapp', 'keyman', sha), null, 2), 'utf-8');
|
||||
fs.writeFileSync('fixtures/' + sha + '-check-runs.json',
|
||||
JSON.stringify(await unitTestEndpoints.getCommitCheckRuns(octokit, 'keymanapp', 'keyman', sha), null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
const octokit = getOctokit(process.env['GITHUB_TOKEN']);
|
||||
for(const sha of Object.keys(commits)) await writeTestFile(sha);
|
||||
}
|
||||
|
||||
describe('pr-build-status', function () {
|
||||
for(const sha of Object.keys(commits)) {
|
||||
it(`should return '${commits[sha][1]}' for sha ${sha}`, function () {
|
||||
const json = JSON.parse(fs.readFileSync(`fixtures/${sha}-statuses.json`,'utf-8'));
|
||||
const status = unitTestEndpoints.calculateCheckResult(json);
|
||||
assert.isNotNull(status);
|
||||
assert.isArray(status);
|
||||
assert.deepStrictEqual(status, commits[sha]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -65,6 +65,8 @@ function triggerTestBuilds() {
|
|||
local -n platforms=$1
|
||||
local branch="$2"
|
||||
|
||||
local found_build=false
|
||||
|
||||
# Cancel any already running builds for this branch
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: cancel current builds for $branch"
|
||||
|
|
@ -83,6 +85,7 @@ function triggerTestBuilds() {
|
|||
eval test_builds='(${'bc_test_$platform'[@]})'
|
||||
for test_build in "${test_builds[@]}"; do
|
||||
if [[ $test_build == "" ]]; then continue; fi
|
||||
found_build=true
|
||||
if [ "${test_build:(-7)}" == "_GitHub" ]; then
|
||||
local job=${test_build%_GitHub}
|
||||
|
||||
|
|
@ -102,6 +105,26 @@ function triggerTestBuilds() {
|
|||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ $found_build == false ]]; then
|
||||
postSkippedBuildsStatusResult
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Add a 'Test Build (Keyman)' successful status check to the commit
|
||||
#
|
||||
function postSkippedBuildsStatusResult() {
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: write successful status check 'Skipping since no platform builds necessary'"
|
||||
else
|
||||
curl --silent --write-out '\n' \
|
||||
--request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data '{"state":"success","description":"Skipping since no platform builds necessary","context":"Test Build (Keyman)"}' \
|
||||
"https://api.github.com/repos/keymanapp/keyman/statuses/${BUILD_VCS_NUMBER}"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -241,16 +264,7 @@ if (( ${#build_platforms[@]} > 0)); then
|
|||
triggerTestBuilds build_platforms $PRNUM
|
||||
else
|
||||
builder_echo heading "No builds to start"
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: write successful status check 'Skipping since no platform builds necessary'"
|
||||
else
|
||||
curl --silent --write-out '\n' \
|
||||
--request POST \
|
||||
--header "Accept: application/vnd.github+json" \
|
||||
--header "Authorization: token $GITHUB_TOKEN" \
|
||||
--data '{"state":"success","description":"Skipping since no platform builds necessary","context":"Test Build (Keyman)"}' \
|
||||
"https://api.github.com/repos/keymanapp/keyman/statuses/${BUILD_VCS_NUMBER}"
|
||||
fi
|
||||
postSkippedBuildsStatusResult
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue