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:
Marc Durdin 2025-06-24 06:07:30 +07:00 committed by GitHub
commit 3be1bd942d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 12045 additions and 10 deletions

183
.github/workflows/pr-build-status.yml vendored Normal file
View 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]);

View 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

View file

@ -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&amp;utm_source=github&amp;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 platforms 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, GitGuardians 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. Well 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 wont 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 wouldnt put this file inside your companys 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"
}
}
}
]
}
]

View file

@ -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&amp;utm_source=github&amp;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 platforms 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, GitGuardians 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. Well 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 wont 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 wouldnt put this file inside your companys 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"
}
}
}
]
}
]

View file

@ -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&amp;utm_source=github&amp;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 platforms 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, GitGuardians 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. Well 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 wont 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 wouldnt put this file inside your companys 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"
}
}
}
]
}
]

View file

@ -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&amp;utm_source=github&amp;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 platforms 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, GitGuardians 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. Well 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 wont 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 wouldnt put this file inside your companys 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"
}
}
}
]
}
]

View file

@ -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
}
}
]

File diff suppressed because it is too large Load diff

View 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"
}
}

View 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
};

View 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]);
});
}
});

View file

@ -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