mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-19 14:17:40 +00:00
Merge branch 'master' of https://github.com/keymanapp/keyman into flick-longpress
This commit is contained in:
commit
d0add3fa4e
80 changed files with 13727 additions and 214 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]);
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.67 alpha 2025-06-22
|
||||
|
||||
* chore(developer): ldml: additional line numbers (#14104)
|
||||
|
||||
## 19.0.66 alpha 2025-06-16
|
||||
|
||||
* fix(windows): add values wucUpdateAvailable and wucNotChecked to TRemoteUpdateCheckResult enum (#14123)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.67
|
||||
19.0.68
|
||||
|
|
@ -393,7 +393,7 @@ export class UnicodeSetItem extends VarsItem {
|
|||
super(id, value, sections, x);
|
||||
const needRanges = sections.usetparser.sizeUnicodeSet(value);
|
||||
if (needRanges >= 0) {
|
||||
this.unicodeSet = sections.usetparser.parseUnicodeSet(value, needRanges);
|
||||
this.unicodeSet = sections.usetparser.parseUnicodeSet(value, needRanges, x);
|
||||
} // otherwise: error (was recorded via callback)
|
||||
}
|
||||
unicodeSet?: UnicodeSet;
|
||||
|
|
|
|||
|
|
@ -392,4 +392,4 @@ export class NFDAnalyzer extends StringAnalyzer {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* Keyman is copyright (C) SIL Global. MIT License.
|
||||
*/
|
||||
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageDef as def, CompilerMessageSpec as m } from './compiler-interfaces.js';
|
||||
import { CompilerErrorNamespace, CompilerErrorSeverity, CompilerMessageDef as def, CompilerMessageSpec as m, CompilerMessageObjectSpec as mx } from './compiler-interfaces.js';
|
||||
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
||||
import { ObjectWithMetadata } from './symbol-utils.js';
|
||||
|
||||
const DeveloperUtilsErrMask = CompilerErrorNamespace.DeveloperUtils;
|
||||
// const SevInfo = CompilerErrorSeverity.Info | DeveloperUtilsErrMask;
|
||||
|
|
@ -14,8 +15,10 @@ const SevError = CompilerErrorSeverity.Error | DeveloperUtilsErrMask;
|
|||
export class DeveloperUtilsMessages {
|
||||
// structured Ajv validation error
|
||||
static ERROR_SchemaValidationError = SevError | 0x0001;
|
||||
static Error_SchemaValidationError = (o:{instancePath:string, keyword:string, message: string, params: string}) => m(this.ERROR_SchemaValidationError,
|
||||
`Error validating LDML XML file: ${def(o.instancePath)}: ${def(o.keyword)}: ${def(o.message)} ${def(o.params)}`);
|
||||
static Error_SchemaValidationError = (o:{instancePath:string, keyword:string, message: string, params: string}, x?: ObjectWithMetadata) => mx(
|
||||
this.ERROR_SchemaValidationError, x,
|
||||
`Error validating LDML XML file: ${def(o.instancePath)}: ${def(o.keyword)}: ${def(o.message)} ${def(o.params)}`,
|
||||
);
|
||||
|
||||
static ERROR_ImportInvalidBase = SevError | 0x0002;
|
||||
static Error_ImportInvalidBase = (o: { base: string, path: string, subtag: string }) =>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ export { UrlSubpathCompilerCallback } from './utils/UrlSubpathCompilerCallback.j
|
|||
|
||||
export { DeveloperUtilsMessages } from './developer-utils-messages.js';
|
||||
export * as SourceFilenamePatterns from './source-filename-patterns.js';
|
||||
export { KeymanXMLType, KeymanXMLWriter, KeymanXMLReader, KeymanXMLMetadata, XML_FILENAME_SYMBOL } from './xml-utils.js';
|
||||
export { KeymanXMLType, KeymanXMLWriter, KeymanXMLReader, KeymanXMLMetadata, XML_FILENAME_SYMBOL, withOffset } from './xml-utils.js';
|
||||
export { SymbolUtils, ObjectWithMetadata } from './symbol-utils.js';
|
||||
export * as LineUtils from './line-utils.js';
|
||||
export * as GitHubUrls from './github-urls.js';
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { constants } from '@keymanapp/ldml-keyboard-constants';
|
|||
import { LDMLKeyboardTestDataXMLSourceFile, LKTTest, LKTTests } from './ldml-keyboard-testdata-xml.js';
|
||||
import boxXmlArray = util.boxXmlArray;
|
||||
import { LineFinderEventResolver } from '../../line-utils.js';
|
||||
import { XML_FILENAME_SYMBOL, KeymanXMLReader } from '../../xml-utils.js';
|
||||
import { XML_FILENAME_SYMBOL, KeymanXMLReader, findInstanceObject } from '../../xml-utils.js';
|
||||
|
||||
interface NameAndProps {
|
||||
'$'?: any; // content
|
||||
|
|
@ -310,12 +310,13 @@ export class LDMLKeyboardXMLSourceFileReader implements EventResolver {
|
|||
public validate(source: LDMLKeyboardXMLSourceFile | LDMLKeyboardTestDataXMLSourceFile): boolean {
|
||||
if(!SchemaValidators.default.ldmlKeyboard3(source)) {
|
||||
for (const err of (<any>SchemaValidators.default.ldmlKeyboard3).errors) {
|
||||
const context = findInstanceObject(source, err?.instancePath?.split('/'));
|
||||
this.callbacks.reportMessage(DeveloperUtilsMessages.Error_SchemaValidationError({
|
||||
instancePath: err.instancePath,
|
||||
keyword: err.keyword,
|
||||
message: err.message || 'Unknown AJV Error', // docs say 'message' is optional if 'messages:false' in options
|
||||
params: Object.entries(err.params || {}).sort().map(([k,v])=>`${k}="${v}"`).join(' '),
|
||||
}));
|
||||
}, context));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,3 +360,40 @@ export class KeymanXMLWriter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* traverse an AJV instancePath and map to an object if possible
|
||||
* @param source object tree root (contains the root object)
|
||||
* @param path ajv split instancePath, such as '/keyboard3/layers/0'.split('/')
|
||||
* @returns undefined if the path was not present, null if path went to something that wasn't an object, otherwise the compileContext object is returned.
|
||||
*/
|
||||
export function findInstanceObject(source: any, path: string[]) : any {
|
||||
if(!path || !source || path.length == 0) {
|
||||
return source;
|
||||
} else if(path[0] == '') {
|
||||
return findInstanceObject(source, path.slice(1));
|
||||
} else if(Array.isArray(source) || typeof source == 'object') {
|
||||
const child = source[path[0]];
|
||||
if (child == undefined) return child; // nothing here
|
||||
if (!child || typeof child == 'string') {
|
||||
return source; // return the *parent* object if the child is empty (could be a property)
|
||||
}
|
||||
return findInstanceObject(child, path.slice(1));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object simulating an XML object with an offset number
|
||||
* For use in calling message functions
|
||||
* @param c number for the offset setting
|
||||
* @param x if set, this object will be used as the base object instead of {}
|
||||
*/
|
||||
export function withOffset(c: number, x?: any) : KeymanXMLMetadata {
|
||||
// set metadata on an empty object
|
||||
const o = Object.assign({}, x);
|
||||
KeymanXMLReader.setMetaData(o, {
|
||||
startIndex: c
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { testReaderCases } from '../helpers/reader-callback-test.js';
|
|||
import CLDRScanToVkey = Constants.CLDRScanToVkey;
|
||||
import CLDRScanToKeyMap = Constants.CLDRScanToKeyMap;
|
||||
import USVirtualKeyCodes = Constants.USVirtualKeyCodes;
|
||||
import { KeymanXMLReader, XML_FILENAME_SYMBOL } from '../../src/xml-utils.js';
|
||||
import { KeymanXMLReader, withOffset, XML_FILENAME_SYMBOL } from '../../src/xml-utils.js';
|
||||
|
||||
function pluckKeysFromKeybag(keys: LKKey[], ids: string[]) {
|
||||
return keys.filter(({id}) => ids.indexOf(id) !== -1);
|
||||
|
|
@ -25,7 +25,7 @@ describe('ldml keyboard xml reader tests', function () {
|
|||
keyword: 'required',
|
||||
message: `must have required property 'info'`,
|
||||
params: 'missingProperty="info"',
|
||||
})],
|
||||
}, withOffset(39))],
|
||||
},
|
||||
{
|
||||
subpath: 'invalid-conforms-to.xml',
|
||||
|
|
@ -34,7 +34,7 @@ describe('ldml keyboard xml reader tests', function () {
|
|||
keyword: 'enum',
|
||||
message: `must be equal to one of the allowed values`,
|
||||
params: 'allowedValues="45,46"', // this has to be kept in sync with the DTD
|
||||
})],
|
||||
}, withOffset(39))],
|
||||
},
|
||||
{
|
||||
subpath: 'import-minimal.xml',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { env } from 'node:process';
|
|||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
|
||||
|
||||
import { KeymanXMLType, KeymanXMLReader, KeymanXMLWriter } from '../src/xml-utils.js';
|
||||
import { KeymanXMLType, KeymanXMLReader, KeymanXMLWriter, findInstanceObject } from '../src/xml-utils.js';
|
||||
import { LineFinder } from '../src/line-utils.js';
|
||||
import { makePathToFixture } from './helpers/index.js';
|
||||
import { SymbolUtils } from '../src/symbol-utils.js';
|
||||
|
|
@ -198,5 +198,36 @@ describe(`XML Reader line number test`, () => {
|
|||
LineFinder.offsetToLineColumn(
|
||||
getMetaData(actual.keyboard3.transforms).startIndex, lines), { line: 8, column: 2 });
|
||||
});
|
||||
describe('findInstanceObject test', () => {
|
||||
const path0 = '/keyboard3/layers/0';
|
||||
const TARGET = Symbol("Looking for this!");
|
||||
it(`Should be able to parse ${path0}`, () => {
|
||||
const o = {
|
||||
keyboard3: {
|
||||
layers: [
|
||||
TARGET,
|
||||
]
|
||||
}
|
||||
};
|
||||
assert.equal(findInstanceObject(o, path0.split('/')), TARGET);
|
||||
});
|
||||
// path to property
|
||||
const path1 = '/keyboard3/conformsTo';
|
||||
it(`Should be able to parse ${path1}`, () => {
|
||||
const keyboard3 = { conformsTo: "1234"};
|
||||
const o = {
|
||||
keyboard3,
|
||||
};
|
||||
assert.equal(findInstanceObject(o, path1.split('/')), keyboard3);
|
||||
});
|
||||
const path2 = '/keyboard3/bad/path';
|
||||
it(`Should be able to handle ${path2}`, () => {
|
||||
const keyboard3 = { conformsTo: "1234"};
|
||||
const o = {
|
||||
keyboard3,
|
||||
};
|
||||
assert.equal(findInstanceObject(o, path2.split('/')), undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ function do_clean() {
|
|||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
function do_publish() {
|
||||
verify-program-signatures
|
||||
builder_if_release_build_level verify-program-signatures
|
||||
verify-node-installer-version
|
||||
|
||||
"$KEYMAN_ROOT/common/windows/cef-checkout.sh"
|
||||
|
|
@ -80,7 +80,7 @@ function do_publish() {
|
|||
|
||||
copy-kmdev
|
||||
|
||||
verify-installer-signatures
|
||||
builder_if_release_build_level verify-installer-signatures
|
||||
}
|
||||
|
||||
function do_test() {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function do_build() {
|
|||
build_version.res
|
||||
vs_msbuild kmanalyze.vcxproj //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
cp "$WIN32_TARGET_PATH/kmanalyze.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmanalyze.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export class LayrCompiler extends SectionCompiler {
|
|||
if (totalLayerCount === 0) { // TODO-LDML: does not validate touch layers yet
|
||||
// no layers seen anywhere
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(this.keyboard3?.layers[0]));
|
||||
this.callbacks.reportMessage(LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(this.keyboard3));
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export class VarsCompiler extends SectionCompiler {
|
|||
// Sets
|
||||
for (const e of variables.set) {
|
||||
const { id, value } = e;
|
||||
if(!this.validateIdentifier(id)) {
|
||||
if(!this.validateIdentifier(id, e)) {
|
||||
valid = false;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -124,7 +124,7 @@ export class VarsCompiler extends SectionCompiler {
|
|||
// UnicodeSets
|
||||
for (const e of variables.uset) {
|
||||
const { id, value } = e;
|
||||
if(!this.validateIdentifier(id)) {
|
||||
if(!this.validateIdentifier(id, e)) {
|
||||
valid = false;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -291,7 +291,7 @@ export class VarsCompiler extends SectionCompiler {
|
|||
let { value } = e;
|
||||
// fix any variables
|
||||
value = result.substituteStrings(value, sections);
|
||||
result.strings.push(new StringVarItem(id, value, sections));
|
||||
result.strings.push(new StringVarItem(id, value, sections, e));
|
||||
}
|
||||
addSet(result: Vars, e: LDMLKeyboard.LKSet, sections: DependencySections): void {
|
||||
const { id } = e;
|
||||
|
|
@ -306,14 +306,14 @@ export class VarsCompiler extends SectionCompiler {
|
|||
// this is not 'forMatch', all variables are to be assumed as string literals, not regex
|
||||
// content.
|
||||
const cookedItems: string[] = rawItems.map(v => result.substituteMarkerString(v, false));
|
||||
result.sets.push(new SetVarItem(id, cookedItems, sections));
|
||||
result.sets.push(new SetVarItem(id, cookedItems, sections, e));
|
||||
}
|
||||
addUnicodeSet(result: Vars, e: LDMLKeyboard.LKUSet, sections: DependencySections): void {
|
||||
const { id } = e;
|
||||
let { value } = e;
|
||||
value = result.substituteStrings(value, sections);
|
||||
value = result.substituteUnicodeSets(value, sections);
|
||||
result.usets.push(new UnicodeSetItem(id, value, sections, sections.usetparser));
|
||||
result.usets.push(new UnicodeSetItem(id, value, sections, sections.usetparser, e));
|
||||
}
|
||||
// routines for using/substituting variables have been moved to the Vars class and its
|
||||
// properties
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import 'mocha';
|
||||
import {assert} from 'chai';
|
||||
import { DispCompiler } from '../src/compiler/disp.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture, testCompilationCases, withOffset } from './helpers/index.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture, testCompilationCases } from './helpers/index.js';
|
||||
import { KMXPlus } from '@keymanapp/common-types';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
|
||||
import Disp = KMXPlus.Disp;
|
||||
import { withOffset } from '@keymanapp/developer-utils';
|
||||
|
||||
describe('disp', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<key id="one" output="1" />
|
||||
</keys>
|
||||
|
||||
<layers formId="touch">
|
||||
<layers formId="touch" minDeviceWidth="123">
|
||||
<!-- INVALID: need at least one layer. -->
|
||||
</layers>
|
||||
</keyboard3>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import * as path from 'path';
|
|||
import { fileURLToPath } from 'url';
|
||||
import { SectionCompiler, SectionCompilerNew } from '../../src/compiler/section-compiler.js';
|
||||
import { util, KMXPlus, LdmlKeyboardTypes } from '@keymanapp/common-types';
|
||||
import { CompilerEvent, compilerEventFormat, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboard, KeymanXMLMetadata, KeymanXMLReader, CompilerError } from "@keymanapp/developer-utils";
|
||||
import { CompilerEvent, compilerEventFormat, CompilerCallbacks, LDMLKeyboardXMLSourceFileReader, LDMLKeyboardTestDataXMLSourceFile, LDMLKeyboard, CompilerError } from "@keymanapp/developer-utils";
|
||||
import { LdmlKeyboardCompiler } from '../../src/main.js'; // make sure main.js compiles
|
||||
import { assert } from 'chai';
|
||||
import { KMXPlusMetadataCompiler } from '../../src/compiler/metadata-compiler.js';
|
||||
|
|
@ -358,17 +358,3 @@ export function hex_str(s?: string) : string {
|
|||
return [...s].map(ch => dontEscape.test(ch) ? ch : util.escapeRegexChar(ch)).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an object simulating an XML object with an offset number
|
||||
* For use in calling message functions
|
||||
* @param c number for the offset setting
|
||||
* @param x if set, this object will be used as the base object instead of {}
|
||||
*/
|
||||
export function withOffset(c: number, x?: any) : KeymanXMLMetadata {
|
||||
// set metadata on an empty object
|
||||
const o = Object.assign({}, x);
|
||||
KeymanXMLReader.setMetaData(o, {
|
||||
startIndex: c
|
||||
});
|
||||
return o;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { KeysCompiler } from '../src/compiler/keys.js';
|
||||
import { assertCodePoints, compilerTestCallbacks, loadSectionFixture, testCompilationCases, withOffset } from './helpers/index.js';
|
||||
import { assertCodePoints, compilerTestCallbacks, loadSectionFixture, testCompilationCases } from './helpers/index.js';
|
||||
import { KMXPlus, Constants, LdmlKeyboardTypes } from '@keymanapp/common-types';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
||||
|
|
@ -9,7 +9,7 @@ import { MetaCompiler } from '../src/compiler/meta.js';
|
|||
const keysDependencies = [ ...BASIC_DEPENDENCIES, MetaCompiler ];
|
||||
import Keys = KMXPlus.Keys;
|
||||
import { BASIC_DEPENDENCIES } from '../src/compiler/empty-compiler.js';
|
||||
import { LDMLKeyboard } from '@keymanapp/developer-utils';
|
||||
import { LDMLKeyboard, withOffset } from '@keymanapp/developer-utils';
|
||||
const K = Constants.USVirtualKeyCodes;
|
||||
|
||||
describe('keys', function () {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ import 'mocha';
|
|||
import { assert } from 'chai';
|
||||
import { LayrCompiler } from '../src/compiler/layr.js';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
import { compilerTestCallbacks, testCompilationCases, withOffset } from './helpers/index.js';
|
||||
import { compilerTestCallbacks, testCompilationCases } from './helpers/index.js';
|
||||
import { KMXPlus } from '@keymanapp/common-types';
|
||||
import { constants } from '@keymanapp/ldml-keyboard-constants';
|
||||
|
||||
import Layr = KMXPlus.Layr;
|
||||
import LayrRow = KMXPlus.LayrRow;
|
||||
import { withOffset } from '@keymanapp/developer-utils';
|
||||
|
||||
function allKeysOk(row : LayrRow, str : string, msg? : string) {
|
||||
const split = str.split(' ');
|
||||
|
|
@ -107,13 +108,13 @@ describe('layr', function () {
|
|||
{
|
||||
// missing layer element
|
||||
subpath: 'sections/layr/invalid-missing-layer.xml',
|
||||
errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(258))],
|
||||
errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(40))],
|
||||
retainOffsetInMessages: true,
|
||||
},
|
||||
{
|
||||
// missing layer element
|
||||
subpath: 'sections/layr/invalid-missing-layer2.xml',
|
||||
errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement()],
|
||||
errors: [LdmlCompilerMessages.Error_MustBeAtLeastOneLayerElement(withOffset(40))],
|
||||
retainOffsetInMessages: true,
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import 'mocha';
|
||||
import { assert } from 'chai';
|
||||
import { LocaCompiler } from '../src/compiler/loca.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture, withOffset } from './helpers/index.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js';
|
||||
import { KMXPlus } from '@keymanapp/common-types';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
|
||||
import Loca = KMXPlus.Loca;
|
||||
import { withOffset } from '@keymanapp/developer-utils';
|
||||
|
||||
describe('loca', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import 'mocha';
|
|||
import {expect} from 'chai';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
import { verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
|
||||
import { CompilerErrorNamespace, CompilerEvent } from '@keymanapp/developer-utils';
|
||||
import { withOffset } from './helpers/index.js';
|
||||
import { CompilerErrorNamespace, CompilerEvent, withOffset } from '@keymanapp/developer-utils';
|
||||
|
||||
describe('LdmlCompilerMessages', function () {
|
||||
it('should have a valid LdmlCompilerMessages object', function() {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import 'mocha';
|
||||
import {assert} from 'chai';
|
||||
import { MetaCompiler } from '../src/compiler/meta.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture, withOffset } from './helpers/index.js';
|
||||
import { compilerTestCallbacks, loadSectionFixture } from './helpers/index.js';
|
||||
import { KMXPlus } from '@keymanapp/common-types';
|
||||
import { LdmlCompilerMessages } from '../src/compiler/ldml-compiler-messages.js';
|
||||
|
||||
import KeyboardSettings = KMXPlus.KeyboardSettings;
|
||||
import Meta = KMXPlus.Meta;
|
||||
import { LDMLKeyboard } from '@keymanapp/developer-utils';
|
||||
import { LDMLKeyboard, withOffset } from '@keymanapp/developer-utils';
|
||||
|
||||
describe('meta', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
|
|
|||
|
|
@ -84,11 +84,14 @@ function do_bundle() {
|
|||
mkdir -p build/dist
|
||||
node build-bundler.js
|
||||
|
||||
sentry-cli sourcemaps inject \
|
||||
--org keyman \
|
||||
--project keyman-developer \
|
||||
--release "$KEYMAN_VERSION_GIT_TAG" \
|
||||
build/dist/ "${SOURCEMAP_PATHS[@]}"
|
||||
if builder_is_ci_build && builder_is_ci_build_level_release; then
|
||||
# Only inject sourcemaps for release builds
|
||||
sentry-cli sourcemaps inject \
|
||||
--org keyman \
|
||||
--project keyman-developer \
|
||||
--release "$KEYMAN_VERSION_GIT_TAG" \
|
||||
build/dist/ "${SOURCEMAP_PATHS[@]}"
|
||||
fi
|
||||
|
||||
# Manually copy over kmcmplib module
|
||||
cp ../kmc-kmn/build/src/import/kmcmplib/wasm-host.wasm build/dist/
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconvert.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
|
||||
rm -rf "$DEVELOPER_PROGRAM/projects/templates"
|
||||
mkdir -p "$DEVELOPER_PROGRAM/projects/templates"
|
||||
|
|
|
|||
|
|
@ -32,9 +32,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
if [[ -f "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" ]]; then
|
||||
cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
fi
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdbrowserhost.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ function do_build() {
|
|||
build_version.res
|
||||
vs_msbuild kmdecomp.sln //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
cp "$WIN32_TARGET_PATH/kmdecomp.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmdecomp.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -32,11 +32,11 @@ function do_build() {
|
|||
build_version.res
|
||||
vs_msbuild imsample.sln //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
cp "$WIN32_TARGET_PATH/imsample.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/imsample.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
|
||||
vs_msbuild imsample.sln //t:Build "//p:Platform=x64"
|
||||
cp "$X64_TARGET" "$DEVELOPER_PROGRAM"
|
||||
cp "$X64_TARGET_PATH/imsample.x64.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/imsample.x64.pdb" "$DEVELOPER_DEBUGPATH"
|
||||
}
|
||||
|
||||
# TODO
|
||||
|
|
|
|||
|
|
@ -33,13 +33,8 @@ function do_build() {
|
|||
sentrytool_delphiprep "$WIN32_TARGET" setup.dpr
|
||||
tds2dbg "$WIN32_TARGET"
|
||||
cp "$WIN32_TARGET" "$DEVELOPER_PROGRAM"
|
||||
if [[ -f "$WIN32_TARGET_PATH/setup.dbg" ]]; then
|
||||
cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg"
|
||||
fi
|
||||
rm -f "$WIN32_TARGET_PATH/devsetup.dbg"
|
||||
if [[ -f "$WIN32_TARGET_PATH/setup.dbg" ]]; then
|
||||
mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg"
|
||||
fi
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$DEVELOPER_DEBUGPATH/devsetup.dbg"
|
||||
builder_if_release_build_level mv "$WIN32_TARGET_PATH/setup.dbg" "$WIN32_TARGET_PATH/devsetup.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -76,11 +76,10 @@ function do_build() {
|
|||
cp kmlmp.cmd "$DEVELOPER_PROGRAM"
|
||||
cp kmc.cmd "$DEVELOPER_PROGRAM"
|
||||
cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$DEVELOPER_PROGRAM"
|
||||
if [[ -f "$WIN32_TARGET_PATH/tike.dbg" ]]; then
|
||||
cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
fi
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/tike.dbg" "$DEVELOPER_DEBUGPATH"
|
||||
|
||||
cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_DLL" "$WIN32_TARGET_PATH"
|
||||
cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH"
|
||||
builder_if_release_build_level cp "$KEYMAN_ROOT/core/build/x86/$TARGET_PATH/src/$KEYMANCORE_PDB" "$WIN32_TARGET_PATH"
|
||||
|
||||
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" "$DEVELOPER_PROGRAM/"
|
||||
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.x64.dll" "$DEVELOPER_PROGRAM/"
|
||||
|
|
|
|||
141
docs/build-bot.md
Normal file
141
docs/build-bot.md
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
# Controlling builds in CI with the Keyman build bot
|
||||
|
||||
The Keyman CI test infrastructure can be controlled by use of a `Build-bot:`
|
||||
commit trailer, or a PR body `Build-bot:` trailer. This allows you to specify
|
||||
what is built for any given platform. The primary purpose of the build bot is to
|
||||
reduce the build agent load, but it can also be used to ensure that specific
|
||||
artifacts are available for the purposes of testing.
|
||||
|
||||
The Keyman build bot is only available for test builds on pull requests. It is
|
||||
not used for any other builds, either test builds on target branches (master,
|
||||
beta, stable-x.y), or for release builds.
|
||||
|
||||
The default set of platform builds is determined by the files touched in the
|
||||
pull request; see /resources/build/trigger-definitions.inc.sh. The default build
|
||||
level for this set is 'build' (see [Build Level] section). This is known as the
|
||||
'build set'.
|
||||
|
||||
You may choose to increase or decrease what is built -- for example, for a
|
||||
documentation-only change, you may decide that nothing needs to be built at all,
|
||||
with:
|
||||
|
||||
```
|
||||
Build-bot: skip
|
||||
```
|
||||
|
||||
Or you may want to ensure that an artifact is built for Windows:
|
||||
|
||||
```
|
||||
Build-bot: release windows
|
||||
```
|
||||
|
||||
## Build Level
|
||||
|
||||
The build level specifies what we want to be run for a test build on a PR.
|
||||
|
||||
There is a bit of nomenclature overlap with a 'release build'. A 'release'
|
||||
buildLevel for a 'test build' is roughly equivalent to what is performed in a
|
||||
'release build', however, a test build is only ever uploaded to test endpoints
|
||||
(i.e. TestFlight, Play Store 'test' streams), and never to *.keyman.com, or to
|
||||
other release distribution endpoints.
|
||||
|
||||
The build level is controlled by the Build-bot commit trailer and PR body
|
||||
Build-bot/Test-bot trailers. The default build level will be 'build'.
|
||||
|
||||
For target branch builds, the build level will always be 'build', and Build-bot:
|
||||
commit trailers are ignored.
|
||||
|
||||
### 'skip' build level
|
||||
|
||||
Don't do a build at all. This would be appropriate for documentation PRs, for
|
||||
example, or changes only to comments in source files.
|
||||
|
||||
### 'build' build level
|
||||
|
||||
Build the code and run unit tests, but don't create artifacts. What this looks
|
||||
like will vary from platform to platform, but there are some common things we
|
||||
won't do:
|
||||
* we won't upload artifacts to TeamCity or *.keyman.com
|
||||
* we won't upload artifacts to any endpoint such as Play Store
|
||||
* we won't upload symbols to Sentry
|
||||
* we won't codesign
|
||||
|
||||
However, we _will_ still build an installer (skipping codesigning), as this is
|
||||
part of the 'build' buildLevel rather than the 'release' buildLevel. The
|
||||
installer will be thrown away for 'build' build level -- it will not be
|
||||
available for download as an artifact.
|
||||
|
||||
For a platform-specific example, on macOS we will also skip notarizing, as this
|
||||
is costly and depends on external network resources, making it fragile.
|
||||
|
||||
### 'release' build level
|
||||
|
||||
A full test build will be run, roughly equivalent to a release build. We will do
|
||||
the following steps:
|
||||
* codesign (many platforms) and notarization (macOS)
|
||||
* upload artifacts to TeamCity (all platforms)
|
||||
* upload builds to TestFlight / Play Store 'test' endpoints (iOS/Android)
|
||||
* upload symbols to Sentry
|
||||
|
||||
For a 'release' build level:
|
||||
* we won't upload artifacts to *.keyman.com
|
||||
* we won't upload artifacts to any release endpoint such as Debian,
|
||||
packages.sil.org, etc, or to the release areas for Play Store or App Store
|
||||
|
||||
(uploading to *.keyman.com and to release endpoints happens in the release TC build config/GHA)
|
||||
|
||||
## Controlling the build bot with trailers
|
||||
|
||||
The build bot respects commit trailers and trailers in the PR body. The commands
|
||||
are cumulative and applied in order; the Build-bot trailer in the PR body is
|
||||
applied after any trailers in commit messages.
|
||||
|
||||
If no platform is specified, then the command applies to all platforms in the
|
||||
current build set, overriding any previous Build-bot commands. Any platforms not
|
||||
in the current build set will remain 'skipped'.
|
||||
|
||||
If a platform is specified, then the command will apply only to that platform,
|
||||
and the platform will be added to the build set if not already present (and
|
||||
thus, subsequent Build-bot commands will be applied to this new platform as
|
||||
well).
|
||||
|
||||
It is important to note that the Build-bot trailers are read when builds are
|
||||
triggered, which happens within 2 minutes of a PR being opened or commits being
|
||||
pushed. Editing the PR body after the build trigger has run will have no effect
|
||||
on existing builds, until another commit is pushed, or the test build trigger is
|
||||
run manually from TeamCity. Thus, do not rely on editing the PR body after PR
|
||||
creation to control the build bot; either include your build bot trailers in
|
||||
commit messages, or include them in the PR body submitted when creating the PR.
|
||||
|
||||
## Interactions with Test Bot
|
||||
|
||||
The Build-bot has limited interactions with the Keyman test bot (aka
|
||||
keymanapp-test-bot): if a 'User Testing' section is found in the PR body, the
|
||||
default build level will be upgraded from 'build' to 'release'.
|
||||
|
||||
Build bot trailers found in the commits or in the PR body are applied after the
|
||||
test bot command.
|
||||
|
||||
WARNING: The build bot does not check PR comments for Test-bot commands.
|
||||
|
||||
## Example Build-bot interactions
|
||||
|
||||
Say we have a PR that touches `/android/build.sh`. The default build set will be
|
||||
`(android:build)`.
|
||||
|
||||
The PR body has a User Testing section: `# User Testing`. The build set is
|
||||
upgraded to: `(android:release)`.
|
||||
|
||||
The first commit includes a Build-bot command: `Build-bot: build ios`. The build
|
||||
set is now `(android:release ios:build)`.
|
||||
|
||||
In a subsequent commit, the PR author decides that nothing needs to be built,
|
||||
after all: `Build-bot: skip`. The build set is now `(android:skip ios:skip)`.
|
||||
Note that other platforms are still 'skip' but not included in the build set.
|
||||
|
||||
Finally, the PR author pushes another commit, with `Build-bot: release windows`.
|
||||
The build set is now: `(android:skip ios:skip windows:release)`.
|
||||
|
||||
|
||||
|
||||
[Build Level]: #Build_Level
|
||||
|
|
@ -105,7 +105,7 @@ function copy-installer() {
|
|||
cp firstvoices.msi "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices.msi"
|
||||
cp firstvoices.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/firstvoices-${KEYMAN_VERSION}.exe"
|
||||
|
||||
verify-installer-signatures
|
||||
builder_if_release_build_level verify-installer-signatures
|
||||
}
|
||||
|
||||
function verify-installer-signatures() {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ builder_parse "$@"
|
|||
|
||||
if builder_start_action test; then
|
||||
./build/test/test.sh
|
||||
./build/test/build-bot/trigger-build-bot.test.sh
|
||||
./build/publish-minimum-versions.sh test
|
||||
builder_finish_action success test
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ if [ "$action" == "commit" ]; then
|
|||
# Trigger builds for the previous version on TeamCity and GitHub
|
||||
#
|
||||
|
||||
triggerBuilds
|
||||
triggerReleaseBuilds
|
||||
|
||||
#
|
||||
# Now, create the PR on GitHub which will be merged when ready
|
||||
|
|
|
|||
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]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,22 +1,51 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Determine if we need to do a build based on rules in
|
||||
# trigger-definitions.inc.sh, rather than calculating changes in
|
||||
# TeamCity. If a build is needed, then we ask TeamCity to
|
||||
# start the build.
|
||||
# trigger-definitions.inc.sh and based on commit trailers and PR body comments,
|
||||
# rather than calculating changes in TeamCity. If a build is needed, then we ask
|
||||
# TeamCity / GHA to start the build.
|
||||
#
|
||||
|
||||
set -e
|
||||
set -u
|
||||
## 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
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo 'Usage: run-required-test-build.sh pull-request-number|branch'
|
||||
echo ' where branch can be master, beta'
|
||||
exit 1
|
||||
. "${THIS_SCRIPT%/*}/trigger-definitions.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/trigger-builds.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/trigger-build-bot.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/jq.inc.sh"
|
||||
|
||||
builder_describe "Run test builds for the given pull request/primary branch" \
|
||||
"--dry-run,-n Only report back which builds would be started" \
|
||||
"--branch,-b=PRNUM Branch (master,beta,stable-x.y) or pull-request to test"
|
||||
|
||||
if [[ $# -eq 1 ]] && [[ "$1" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then
|
||||
# For transitional period, when build configuration on TC needs to support
|
||||
# both forms, we will pass a modified set of params to builder_parse
|
||||
#
|
||||
# TODO: remove this condition once all branches have received this change,
|
||||
# and update TC build configuration to pass -b parameter
|
||||
#
|
||||
builder_parse -b "$1"
|
||||
else
|
||||
PRNUM="$1"
|
||||
builder_parse "$@"
|
||||
fi
|
||||
|
||||
# Validate parameters
|
||||
|
||||
if ! builder_has_option --branch; then
|
||||
builder_die "--branch parameter is required"
|
||||
fi
|
||||
|
||||
if [[ ! "$PRNUM" =~ ^([0-9]+|master|beta|stable-[0-9]+\.[0-9]+)$ ]]; then
|
||||
builder_die "Invalid parameter --branch '$PRNUM'; expected 'master', 'beta', 'stable-x.y', or PR number"
|
||||
fi
|
||||
|
||||
#
|
||||
# Debug logging
|
||||
#
|
||||
function debug_echo() {
|
||||
if [ ! -z ${DEBUG:-} ]; then
|
||||
echo "DEBUG: $1"
|
||||
|
|
@ -25,48 +54,77 @@ function debug_echo() {
|
|||
}
|
||||
|
||||
#
|
||||
# This must be run under a TeamCity environment in order to receive all variables
|
||||
#
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${THIS_SCRIPT%/*}/trigger-definitions.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/trigger-builds.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/jq.inc.sh"
|
||||
|
||||
#
|
||||
# Iterate through the platforms 'array' passed in and
|
||||
# Iterate through the platforms array passed in and
|
||||
# run builds associated with each platform found
|
||||
#
|
||||
|
||||
# Parameters:
|
||||
# 1: name of associative array of platforms, platform=skip|build|release|[fulltest?]
|
||||
# 2: branch name or PR number
|
||||
#
|
||||
function triggerTestBuilds() {
|
||||
# Note: we always run builds for 'all' platforms
|
||||
local platforms=( `echo "all $1"` )
|
||||
local -n platforms=$1
|
||||
local branch="$2"
|
||||
local force="${3:-false}"
|
||||
|
||||
local found_build=false
|
||||
|
||||
# Cancel any already running builds for this branch
|
||||
node "$THIS_SCRIPT_PATH/ci/cancel-builds/cancel-test-builds.mjs" "$branch" "$TEAMCITY_TOKEN"
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: cancel current builds for $branch"
|
||||
else
|
||||
node "$THIS_SCRIPT_PATH/ci/cancel-builds/cancel-test-builds.mjs" "$branch" "$TEAMCITY_TOKEN"
|
||||
fi
|
||||
|
||||
for platform in "${platforms[@]}"; do
|
||||
echo "# $platform: checking for changes"
|
||||
for platform in "${!platforms[@]}"; do
|
||||
local platformBuildLevel=${platforms[$platform]}
|
||||
if [[ $platformBuildLevel == skip ]]; then
|
||||
builder_echo heading "$platform: skipping build"
|
||||
continue
|
||||
fi
|
||||
|
||||
builder_echo heading "$platform: checking for git changes"
|
||||
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}
|
||||
echo " -- Triggering GitHub action build $job/$branch"
|
||||
triggerGitHubActionsBuild true "$job" "$branch"
|
||||
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: Triggering GitHub action build $job/$branch, level = $platformBuildLevel"
|
||||
else
|
||||
builder_echo "Triggering GitHub action build $job/$branch, level = $platformBuildLevel"
|
||||
triggerGitHubActionsBuild true $platformBuildLevel "$job" "$branch"
|
||||
fi
|
||||
else
|
||||
echo " -- Triggering build configuration $test_build on teamcity"
|
||||
triggerTeamCityBuild true "$test_build" "$vcs_test" "$branch"
|
||||
if builder_has_option --dry-run; then
|
||||
builder_echo "DRY RUN: Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel"
|
||||
else
|
||||
builder_echo "Triggering build configuration $test_build on teamcity for $branch, level = $platformBuildLevel"
|
||||
triggerTeamCityBuild true $platformBuildLevel "$test_build" "$vcs_test" "$branch"
|
||||
fi
|
||||
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
|
||||
}
|
||||
|
||||
#
|
||||
|
|
@ -75,9 +133,9 @@ function triggerTestBuilds() {
|
|||
#
|
||||
|
||||
if [[ ! "$PRNUM" =~ ^[[:digit:]]+$ ]]; then
|
||||
# branch name is 'master', 'beta' [, or 'stable' -- in the future]
|
||||
echo ". Branch $PRNUM needs to pass tests on all platforms."
|
||||
triggerTestBuilds "`echo ${available_platforms[@]}`" "$PRNUM" "true"
|
||||
# branch name is 'master', 'beta', or 'stable-x.y'
|
||||
builder_echo "Branch $PRNUM needs to pass tests on all platforms."
|
||||
triggerTestBuilds main_branch_platform_build_levels $PRNUM
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
|
@ -88,7 +146,7 @@ fi
|
|||
# targeted by the PR other than to ask GitHub, anyway.)
|
||||
#
|
||||
|
||||
echo ". Get information about pull request #$PRNUM from GitHub"
|
||||
builder_echo grey "# Get information about pull request #$PRNUM from GitHub"
|
||||
prinfo=`curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/keymanapp/keyman/pulls/$PRNUM`
|
||||
prbase=`echo ${prinfo} | "$JQ" -r '.base.ref'`
|
||||
prhead=`echo ${prinfo} | "$JQ" -r '.head.ref'`
|
||||
|
|
@ -111,7 +169,7 @@ fi
|
|||
#
|
||||
|
||||
# (Ensure we are within the repository before running git calls)
|
||||
echo ". Ensure our local branch is up to date"
|
||||
builder_echo grey "# Ensure our local branch is up to date"
|
||||
pushd "$KEYMAN_ROOT" > /dev/null
|
||||
git fetch origin > /dev/null
|
||||
|
||||
|
|
@ -132,7 +190,7 @@ fi
|
|||
# We work from origin so we don't need the branches in our local copy
|
||||
#
|
||||
|
||||
echo ". Get list of changed files in the pull request"
|
||||
builder_echo grey "# Get list of changed files in the pull request"
|
||||
prfiles=`git diff --name-only "origin/$prbase"..."$prremote/$prhead" || ( if [ $? == 128 ]; then echo abort; else exit $?; fi )`
|
||||
if [ "$prfiles" == "abort" ]; then
|
||||
# Don't trigger any builds, exit with success
|
||||
|
|
@ -155,43 +213,58 @@ popd > /dev/null
|
|||
# Find the platforms that have changes based on the watch_ variables in trigger-definitions.inc.sh
|
||||
#
|
||||
|
||||
echo ". Find platforms that have changes"
|
||||
build_platforms=()
|
||||
function find_platform_changes() {
|
||||
builder_echo grey "# Find platforms that have changes"
|
||||
declare -gA build_platforms
|
||||
local platform watch
|
||||
|
||||
# Scan the files found
|
||||
while IFS= read -r line; do
|
||||
# for each platform
|
||||
for platform in "${available_platforms[@]}"; do
|
||||
if [[ ! " ${build_platforms[@]} " =~ " $platform " ]]; then
|
||||
# Which platform are we watching?
|
||||
eval watch='$'watch_$platform
|
||||
# Add common patterns to the watch list
|
||||
watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity)|teamcity/(${platform}|includes))|${watch})"
|
||||
# Since bash doesn't support negative look-aheads we use perl to test
|
||||
# (grep has a --perl-regexp option, but not the version on macOS)
|
||||
if perl -e 'exit($ARGV[0] =~ /$ARGV[1]/ ? 0 : 1)' "${line}" "${watch}"; then
|
||||
build_platforms+=($platform)
|
||||
# Scan the files found
|
||||
while IFS= read -r line; do
|
||||
# for each platform
|
||||
for platform in "${available_platforms[@]}"; do
|
||||
if [[ ! " ${!build_platforms[@]} " =~ " $platform " ]]; then
|
||||
# Which platform are we watching?
|
||||
eval watch='$'watch_$platform
|
||||
|
||||
# Add common patterns to the watch list
|
||||
watch="^(${platform}|(oem/[^/]+/${platform})|resources/((?!teamcity)|teamcity/(${platform}|includes))|${watch})"
|
||||
# Since bash doesn't support negative look-aheads we use perl to test
|
||||
# (grep has a --perl-regexp option, but not the version on macOS)
|
||||
if perl -e 'exit($ARGV[0] =~ /$ARGV[1]/ ? 0 : 1)' "${line}" "${watch}"; then
|
||||
# By default, we'll build a 'release' test build for touched platforms
|
||||
build_platforms[$platform]=$build_level_release
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done <<< "$prfiles"
|
||||
done
|
||||
done <<< "$prfiles"
|
||||
|
||||
debug_echo "Build platforms: ${build_platforms[*]}"
|
||||
debug_echo "Default build platforms: ${!build_platforms[@]}"
|
||||
}
|
||||
|
||||
find_platform_changes
|
||||
|
||||
#
|
||||
# Now check PR commits for Build-bot: commands
|
||||
# This will modify the build_platforms array
|
||||
#
|
||||
|
||||
if [ "$prremote" == "origin" ]; then
|
||||
# We only accept Build-bot commands on trusted local origin PRs
|
||||
prcommits=`curl -s -H "User-Agent: @keymanapp" -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/keymanapp/keyman/pulls/$PRNUM/commits`
|
||||
test_bot_check_pr_body "$PRNUM" "$prinfo"
|
||||
build_bot_check_messages $PRNUM "$prinfo" "$prcommits"
|
||||
fi
|
||||
|
||||
#
|
||||
# Start test builds as required
|
||||
#
|
||||
|
||||
if (( ${#build_platforms[@]} > 0)); then
|
||||
#
|
||||
# Start the test builds
|
||||
#
|
||||
echo ". Start test builds"
|
||||
triggerTestBuilds "`echo ${build_platforms[@]}`" "$PRNUM"
|
||||
builder_echo heading "Start test builds"
|
||||
triggerTestBuilds build_platforms $PRNUM
|
||||
else
|
||||
echo ". No builds to start"
|
||||
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}"
|
||||
builder_echo heading "No builds to start"
|
||||
postSkippedBuildsStatusResult
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
171
resources/build/test/build-bot/pr-14013-commits.txt
Normal file
171
resources/build/test/build-bot/pr-14013-commits.txt
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
[
|
||||
{
|
||||
"sha": "b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"node_id": "C_kwDOAY2xT9oAKGI3OTcwZDQzOWNkZDE4ZTg0ZmQ3NTRiZjBmMDZhODg3OGMxOGJiOTc",
|
||||
"commit": {
|
||||
"author": {
|
||||
"name": "Marc Durdin",
|
||||
"email": "marc@durdin.net",
|
||||
"date": "2025-04-30T01:59:27Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Marc Durdin",
|
||||
"email": "marc@durdin.net",
|
||||
"date": "2025-04-30T01:59:27Z"
|
||||
},
|
||||
"message": "maint(common): consolidate VC++ environment setup for meson\n\nUse the automated environment setup that we built for Windows and\nDeveloper builds in our meson build configurations, so we don't need to\ncall out to vsdevcmd.bat for every build step. This makes the build\nfaster, more consistent across the various ways we call VC++, and\nsimplifies the shell scripts. Removes the complicated build.bat wrappers.\n\nAlso removes the `cleanup_visual_studio_path` function which should no\nlonger be necessary, as our environment builder does the job for us.\n\nMinor complication in that `$TARGET_PATH` variable in kmcmplib/build.sh\nis also used in the Windows environment setup, so eliminated its use in\nkmcmplib/build.sh.\n\nFinally, removes the `$MESON_LOW_VERSION` variable as we know we are\nalways on Meson 1.0 or later now.\n\nBuild-bot: skip developer, windows, foo\nBuild-bot: bash windows",
|
||||
"tree": {
|
||||
"sha": "df937f0cb23e2e3736d92cf39b3b7f62ce20bcc1",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/git/trees/df937f0cb23e2e3736d92cf39b3b7f62ce20bcc1"
|
||||
},
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/git/commits/b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"comment_count": 0,
|
||||
"verification": {
|
||||
"verified": false,
|
||||
"reason": "unsigned",
|
||||
"signature": null,
|
||||
"payload": null,
|
||||
"verified_at": null
|
||||
}
|
||||
},
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/commits/b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"html_url": "https://github.com/keymanapp/keyman/commit/b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"comments_url": "https://api.github.com/repos/keymanapp/keyman/commits/b7970d439cdd18e84fd754bf0f06a8878c18bb97/comments",
|
||||
"author": {
|
||||
"login": "mcdurdin",
|
||||
"id": 4498365,
|
||||
"node_id": "MDQ6VXNlcjQ0OTgzNjU=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4498365?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/mcdurdin",
|
||||
"html_url": "https://github.com/mcdurdin",
|
||||
"followers_url": "https://api.github.com/users/mcdurdin/followers",
|
||||
"following_url": "https://api.github.com/users/mcdurdin/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/mcdurdin/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/mcdurdin/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/mcdurdin/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/mcdurdin/orgs",
|
||||
"repos_url": "https://api.github.com/users/mcdurdin/repos",
|
||||
"events_url": "https://api.github.com/users/mcdurdin/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/mcdurdin/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"committer": {
|
||||
"login": "mcdurdin",
|
||||
"id": 4498365,
|
||||
"node_id": "MDQ6VXNlcjQ0OTgzNjU=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4498365?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/mcdurdin",
|
||||
"html_url": "https://github.com/mcdurdin",
|
||||
"followers_url": "https://api.github.com/users/mcdurdin/followers",
|
||||
"following_url": "https://api.github.com/users/mcdurdin/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/mcdurdin/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/mcdurdin/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/mcdurdin/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/mcdurdin/orgs",
|
||||
"repos_url": "https://api.github.com/users/mcdurdin/repos",
|
||||
"events_url": "https://api.github.com/users/mcdurdin/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/mcdurdin/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"parents": [
|
||||
{
|
||||
"sha": "926be6e9463e0497dbf1d32d4ef908ae2aad20db",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/commits/926be6e9463e0497dbf1d32d4ef908ae2aad20db",
|
||||
"html_url": "https://github.com/keymanapp/keyman/commit/926be6e9463e0497dbf1d32d4ef908ae2aad20db"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha": "0230010a509e317f835d18e1d3be116c30047cf9",
|
||||
"node_id": "C_kwDOAY2xT9oAKDAyMzAwMTBhNTA5ZTMxN2Y4MzVkMThlMWQzYmUxMTZjMzAwNDdjZjk",
|
||||
"commit": {
|
||||
"author": {
|
||||
"name": "Marc Durdin",
|
||||
"email": "marc@durdin.net",
|
||||
"date": "2025-04-30T02:18:26Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "GitHub",
|
||||
"email": "noreply@github.com",
|
||||
"date": "2025-04-30T02:18:26Z"
|
||||
},
|
||||
"message": "Merge branch 'maint/meson-versioning-improvements' into maint/core-developer/12639-meson-no-batch-wrappers\n\nBuild-bot: skip all",
|
||||
"tree": {
|
||||
"sha": "47bb7ca6f52998e8a14e478f5e9f5d7ba415ace3",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/git/trees/47bb7ca6f52998e8a14e478f5e9f5d7ba415ace3"
|
||||
},
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/git/commits/0230010a509e317f835d18e1d3be116c30047cf9",
|
||||
"comment_count": 0,
|
||||
"verification": {
|
||||
"verified": true,
|
||||
"reason": "valid",
|
||||
"signature": "-----BEGIN PGP SIGNATURE-----\n\nwsFcBAABCAAQBQJoEYhyCRC1aQ7uu5UhlAAAg4MQAFxQGq7WPhHNzlspVIFv5k8W\nJPmC2SP+eUXyDKpO38MqnAPlMqaNrleB071BtE9i2DeWHwYvGwJ87ki95aAtg00F\ne4RbhJYy5sW/F/0fNzHgGNFPNb3EcRyt6I7X3xRB+i1k0PgTSVIpnLzXn3gj9GwU\n7PuLgNO0CUpcgWc6X7a259RAlLInEJ+I8p9XhW827eRalogwgALt4IuJmWyJALFL\nJFeWGlCKdVfbHEgBgEyA98zNnvtoxQ+FlJn1V1jJEBpCY+MPB5tyCXZ4LZbn/n7V\n0+2Pp3c5lnZKmaOyUykwQw7P8s4R2+aQnghRHEHa5Fm6hjQkHMiCOwgTwpIrUV+U\n4AWzPjZSw71cBmAUJBUe7SIBv51r1TUeuNy9x3/0r588znBItR12C91KQD8u9o3/\nMqcWTkpEuGgD2nYbhTdb9PNF4Ik/wbBKLDvjQg180b/ocklVaGAAqjOHdvkyQ4in\nIBRqiaeI3zD5TQ7936Sk1JQPil97PWzk27vde0KAeCR59yL8lCnHr2Rtp8hEpytX\nGrlL14GIaVK6YEvumloPFXrWnbdETu0yRllVa4T2RbWskZUtPG93l/fmOV6tiAr2\np79lgJQ0eEJ1fBNm9UsvjDXU478fSU5Dobg1pwv+wxr10u43tPbqmMI6VLxOjt7i\nz44SZyX8udppGuASFSsw\n=3UIv\n-----END PGP SIGNATURE-----\n",
|
||||
"payload": "tree 47bb7ca6f52998e8a14e478f5e9f5d7ba415ace3\nparent b7970d439cdd18e84fd754bf0f06a8878c18bb97\nparent e976b5563589a9e38c778f3c06e404e82526358a\nauthor Marc Durdin <marc@durdin.net> 1745979506 +0700\ncommitter GitHub <noreply@github.com> 1745979506 +0700\n\nMerge branch 'maint/meson-versioning-improvements' into maint/core-developer/12639-meson-no-batch-wrappers",
|
||||
"verified_at": "2025-04-30T02:18:29Z"
|
||||
}
|
||||
},
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/commits/0230010a509e317f835d18e1d3be116c30047cf9",
|
||||
"html_url": "https://github.com/keymanapp/keyman/commit/0230010a509e317f835d18e1d3be116c30047cf9",
|
||||
"comments_url": "https://api.github.com/repos/keymanapp/keyman/commits/0230010a509e317f835d18e1d3be116c30047cf9/comments",
|
||||
"author": {
|
||||
"login": "mcdurdin",
|
||||
"id": 4498365,
|
||||
"node_id": "MDQ6VXNlcjQ0OTgzNjU=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/4498365?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/mcdurdin",
|
||||
"html_url": "https://github.com/mcdurdin",
|
||||
"followers_url": "https://api.github.com/users/mcdurdin/followers",
|
||||
"following_url": "https://api.github.com/users/mcdurdin/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/mcdurdin/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/mcdurdin/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/mcdurdin/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/mcdurdin/orgs",
|
||||
"repos_url": "https://api.github.com/users/mcdurdin/repos",
|
||||
"events_url": "https://api.github.com/users/mcdurdin/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/mcdurdin/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"committer": {
|
||||
"login": "web-flow",
|
||||
"id": 19864447,
|
||||
"node_id": "MDQ6VXNlcjE5ODY0NDQ3",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/19864447?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/web-flow",
|
||||
"html_url": "https://github.com/web-flow",
|
||||
"followers_url": "https://api.github.com/users/web-flow/followers",
|
||||
"following_url": "https://api.github.com/users/web-flow/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/web-flow/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/web-flow/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/web-flow/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/web-flow/orgs",
|
||||
"repos_url": "https://api.github.com/users/web-flow/repos",
|
||||
"events_url": "https://api.github.com/users/web-flow/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/web-flow/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"parents": [
|
||||
{
|
||||
"sha": "b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/commits/b7970d439cdd18e84fd754bf0f06a8878c18bb97",
|
||||
"html_url": "https://github.com/keymanapp/keyman/commit/b7970d439cdd18e84fd754bf0f06a8878c18bb97"
|
||||
},
|
||||
{
|
||||
"sha": "e976b5563589a9e38c778f3c06e404e82526358a",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/commits/e976b5563589a9e38c778f3c06e404e82526358a",
|
||||
"html_url": "https://github.com/keymanapp/keyman/commit/e976b5563589a9e38c778f3c06e404e82526358a"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
534
resources/build/test/build-bot/pr-14013-data.txt
Normal file
534
resources/build/test/build-bot/pr-14013-data.txt
Normal file
|
|
@ -0,0 +1,534 @@
|
|||
{
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/pulls/14013",
|
||||
"id": 2533896821,
|
||||
"node_id": "PR_kwDOAY2xT86XCDJ1",
|
||||
"html_url": "https://github.com/keymanapp/keyman/pull/14013",
|
||||
"diff_url": "https://github.com/keymanapp/keyman/pull/14013.diff",
|
||||
"patch_url": "https://github.com/keymanapp/keyman/pull/14013.patch",
|
||||
"issue_url": "https://api.github.com/repos/keymanapp/keyman/issues/14013",
|
||||
"number": 14013,
|
||||
"state": "open",
|
||||
"locked": false,
|
||||
"title": "feat(developer): kmc-convert validate keylayout file 😎",
|
||||
"user": {
|
||||
"login": "SabineSIL",
|
||||
"id": 86713187,
|
||||
"node_id": "MDQ6VXNlcjg2NzEzMTg3",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/86713187?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/SabineSIL",
|
||||
"html_url": "https://github.com/SabineSIL",
|
||||
"followers_url": "https://api.github.com/users/SabineSIL/followers",
|
||||
"following_url": "https://api.github.com/users/SabineSIL/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/SabineSIL/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/SabineSIL/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/SabineSIL/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/SabineSIL/orgs",
|
||||
"repos_url": "https://api.github.com/users/SabineSIL/repos",
|
||||
"events_url": "https://api.github.com/users/SabineSIL/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/SabineSIL/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"body": "\r\n\r\n**The setup:** \r\n- The xsd-file : `resources\\standards-data\\keylayout\\dtd\\keylayout.xsd`\r\n- The `resources\\standards-data\\keylayout\\create_keylayout_schema.sh` exists and creates `resources\\standards-data\\keylayout\\keylayout.schema.json`\r\n- The `common/web/types/build.sh configure` creates a `common\\web\\types\\src\\schemas\\keylayout.schema.validator.mjs` file \r\n- The .mjs is then used in validate() function of `keylayout-file-reader.ts` (`...SchemaValidators.default.keylayout(source)`) \r\n\r\n**The problem:** \r\nAll the above works but while using the `validate20()` function of `keylayout.schema.validator.mjs` , **key0 will be \"?xml\"** which then produces the error.\r\n \r\n> \r\n> for (const **key0** in data) {\r\n> if (!(key0 === \"keyboard\")) {\r\n> validate20.errors = [{ instancePath, schemaPath: \"#/additionalProperties\", keyword: \"additionalProperties\", params: { additionalProperty: key0 }, message: \"must NOT have additional properties\" }];\r\n> return false;\r\n> break;\r\n> }\r\n> }\r\n\r\n**The error:** \r\n\r\n\r\n**So the question is: Why is `?xml `treated as an element tag and how can we prevent this**\r\n\r\n@keymanapp-test-bot skip\r\n\r\nBuild-bot: skip",
|
||||
"created_at": "2025-05-21T10:02:29Z",
|
||||
"updated_at": "2025-05-26T01:30:35Z",
|
||||
"closed_at": null,
|
||||
"merged_at": null,
|
||||
"merge_commit_sha": null,
|
||||
"assignee": {
|
||||
"login": "SabineSIL",
|
||||
"id": 86713187,
|
||||
"node_id": "MDQ6VXNlcjg2NzEzMTg3",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/86713187?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/SabineSIL",
|
||||
"html_url": "https://github.com/SabineSIL",
|
||||
"followers_url": "https://api.github.com/users/SabineSIL/followers",
|
||||
"following_url": "https://api.github.com/users/SabineSIL/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/SabineSIL/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/SabineSIL/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/SabineSIL/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/SabineSIL/orgs",
|
||||
"repos_url": "https://api.github.com/users/SabineSIL/repos",
|
||||
"events_url": "https://api.github.com/users/SabineSIL/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/SabineSIL/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"assignees": [
|
||||
{
|
||||
"login": "SabineSIL",
|
||||
"id": 86713187,
|
||||
"node_id": "MDQ6VXNlcjg2NzEzMTg3",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/86713187?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/SabineSIL",
|
||||
"html_url": "https://github.com/SabineSIL",
|
||||
"followers_url": "https://api.github.com/users/SabineSIL/followers",
|
||||
"following_url": "https://api.github.com/users/SabineSIL/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/SabineSIL/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/SabineSIL/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/SabineSIL/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/SabineSIL/orgs",
|
||||
"repos_url": "https://api.github.com/users/SabineSIL/repos",
|
||||
"events_url": "https://api.github.com/users/SabineSIL/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/SabineSIL/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
}
|
||||
],
|
||||
"requested_reviewers": [
|
||||
|
||||
],
|
||||
"requested_teams": [
|
||||
|
||||
],
|
||||
"labels": [
|
||||
{
|
||||
"id": 648605999,
|
||||
"node_id": "MDU6TGFiZWw2NDg2MDU5OTk=",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/developer/",
|
||||
"name": "developer/",
|
||||
"color": "f9d0c4",
|
||||
"default": false,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": 1095223705,
|
||||
"node_id": "MDU6TGFiZWwxMDk1MjIzNzA1",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/common/",
|
||||
"name": "common/",
|
||||
"color": "006b75",
|
||||
"default": false,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": 1298035273,
|
||||
"node_id": "MDU6TGFiZWwxMjk4MDM1Mjcz",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/developer/compilers/",
|
||||
"name": "developer/compilers/",
|
||||
"color": "f9d0c4",
|
||||
"default": false,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": 1608627704,
|
||||
"node_id": "MDU6TGFiZWwxNjA4NjI3NzA0",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/common/resources/",
|
||||
"name": "common/resources/",
|
||||
"color": "006b75",
|
||||
"default": false,
|
||||
"description": "Build infrastructure"
|
||||
},
|
||||
{
|
||||
"id": 2058996244,
|
||||
"node_id": "MDU6TGFiZWwyMDU4OTk2MjQ0",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/feat",
|
||||
"name": "feat",
|
||||
"color": "84b6eb",
|
||||
"default": false,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": 2073602939,
|
||||
"node_id": "MDU6TGFiZWwyMDczNjAyOTM5",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/common/web/",
|
||||
"name": "common/web/",
|
||||
"color": "006b75",
|
||||
"default": false,
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"id": 7333596907,
|
||||
"node_id": "LA_kwDOAY2xT88AAAABtR3O6w",
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/labels/epic-kmc-convert",
|
||||
"name": "epic-kmc-convert",
|
||||
"color": "F9C2EE",
|
||||
"default": false,
|
||||
"description": ""
|
||||
}
|
||||
],
|
||||
"milestone": {
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman/milestones/229",
|
||||
"html_url": "https://github.com/keymanapp/keyman/milestone/229",
|
||||
"labels_url": "https://api.github.com/repos/keymanapp/keyman/milestones/229/labels",
|
||||
"id": 12655812,
|
||||
"node_id": "MI_kwDOAY2xT84AwRzE",
|
||||
"number": 229,
|
||||
"title": "A19S4",
|
||||
"description": null,
|
||||
"creator": {
|
||||
"login": "keyman-server",
|
||||
"id": 7018967,
|
||||
"node_id": "MDQ6VXNlcjcwMTg5Njc=",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/7018967?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keyman-server",
|
||||
"html_url": "https://github.com/keyman-server",
|
||||
"followers_url": "https://api.github.com/users/keyman-server/followers",
|
||||
"following_url": "https://api.github.com/users/keyman-server/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keyman-server/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keyman-server/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keyman-server/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keyman-server/orgs",
|
||||
"repos_url": "https://api.github.com/users/keyman-server/repos",
|
||||
"events_url": "https://api.github.com/users/keyman-server/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keyman-server/received_events",
|
||||
"type": "User",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"open_issues": 108,
|
||||
"closed_issues": 34,
|
||||
"state": "open",
|
||||
"created_at": "2025-04-02T03:26:05Z",
|
||||
"updated_at": "2025-05-31T00:48:44Z",
|
||||
"due_on": "2025-06-06T07:00:00Z",
|
||||
"closed_at": null
|
||||
},
|
||||
"draft": true,
|
||||
"commits_url": "https://api.github.com/repos/keymanapp/keyman/pulls/14013/commits",
|
||||
"review_comments_url": "https://api.github.com/repos/keymanapp/keyman/pulls/14013/comments",
|
||||
"review_comment_url": "https://api.github.com/repos/keymanapp/keyman/pulls/comments{/number}",
|
||||
"comments_url": "https://api.github.com/repos/keymanapp/keyman/issues/14013/comments",
|
||||
"statuses_url": "https://api.github.com/repos/keymanapp/keyman/statuses/00158fdeeefc98e7d2b507729e91fab845415f6f",
|
||||
"head": {
|
||||
"label": "keymanapp:feat/developer/kmc-convert-validateKeylayout",
|
||||
"ref": "feat/developer/kmc-convert-validateKeylayout",
|
||||
"sha": "00158fdeeefc98e7d2b507729e91fab845415f6f",
|
||||
"user": {
|
||||
"login": "keymanapp",
|
||||
"id": 12402926,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjEyNDAyOTI2",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12402926?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keymanapp",
|
||||
"html_url": "https://github.com/keymanapp",
|
||||
"followers_url": "https://api.github.com/users/keymanapp/followers",
|
||||
"following_url": "https://api.github.com/users/keymanapp/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keymanapp/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keymanapp/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keymanapp/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keymanapp/orgs",
|
||||
"repos_url": "https://api.github.com/users/keymanapp/repos",
|
||||
"events_url": "https://api.github.com/users/keymanapp/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keymanapp/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkyNjA2MzE4Mw==",
|
||||
"name": "keyman",
|
||||
"full_name": "keymanapp/keyman",
|
||||
"private": false,
|
||||
"owner": {
|
||||
"login": "keymanapp",
|
||||
"id": 12402926,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjEyNDAyOTI2",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12402926?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keymanapp",
|
||||
"html_url": "https://github.com/keymanapp",
|
||||
"followers_url": "https://api.github.com/users/keymanapp/followers",
|
||||
"following_url": "https://api.github.com/users/keymanapp/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keymanapp/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keymanapp/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keymanapp/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keymanapp/orgs",
|
||||
"repos_url": "https://api.github.com/users/keymanapp/repos",
|
||||
"events_url": "https://api.github.com/users/keymanapp/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keymanapp/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/keymanapp/keyman",
|
||||
"description": "Keyman cross platform input methods system running on Android, iOS, Linux, macOS, Windows and mobile and desktop web",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"forks_url": "https://api.github.com/repos/keymanapp/keyman/forks",
|
||||
"keys_url": "https://api.github.com/repos/keymanapp/keyman/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/keymanapp/keyman/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/keymanapp/keyman/teams",
|
||||
"hooks_url": "https://api.github.com/repos/keymanapp/keyman/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/keymanapp/keyman/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/keymanapp/keyman/events",
|
||||
"assignees_url": "https://api.github.com/repos/keymanapp/keyman/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/keymanapp/keyman/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/keymanapp/keyman/tags",
|
||||
"blobs_url": "https://api.github.com/repos/keymanapp/keyman/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/keymanapp/keyman/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/keymanapp/keyman/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/keymanapp/keyman/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/keymanapp/keyman/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/keymanapp/keyman/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/keymanapp/keyman/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/keymanapp/keyman/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/keymanapp/keyman/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/keymanapp/keyman/subscription",
|
||||
"commits_url": "https://api.github.com/repos/keymanapp/keyman/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/keymanapp/keyman/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/keymanapp/keyman/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/keymanapp/keyman/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/keymanapp/keyman/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/keymanapp/keyman/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/keymanapp/keyman/merges",
|
||||
"archive_url": "https://api.github.com/repos/keymanapp/keyman/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/keymanapp/keyman/downloads",
|
||||
"issues_url": "https://api.github.com/repos/keymanapp/keyman/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/keymanapp/keyman/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/keymanapp/keyman/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/keymanapp/keyman/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/keymanapp/keyman/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/keymanapp/keyman/releases{/id}",
|
||||
"deployments_url": "https://api.github.com/repos/keymanapp/keyman/deployments",
|
||||
"created_at": "2014-11-01T21:01:44Z",
|
||||
"updated_at": "2025-05-31T18:02:06Z",
|
||||
"pushed_at": "2025-05-31T18:02:03Z",
|
||||
"git_url": "git://github.com/keymanapp/keyman.git",
|
||||
"ssh_url": "git@github.com:keymanapp/keyman.git",
|
||||
"clone_url": "https://github.com/keymanapp/keyman.git",
|
||||
"svn_url": "https://github.com/keymanapp/keyman",
|
||||
"homepage": "https://keyman.com/",
|
||||
"size": 489713,
|
||||
"stargazers_count": 456,
|
||||
"watchers_count": 456,
|
||||
"language": "Pascal",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"has_discussions": false,
|
||||
"forks_count": 120,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 1043,
|
||||
"license": {
|
||||
"key": "other",
|
||||
"name": "Other",
|
||||
"spdx_id": "NOASSERTION",
|
||||
"url": null,
|
||||
"node_id": "MDc6TGljZW5zZTA="
|
||||
},
|
||||
"allow_forking": true,
|
||||
"is_template": false,
|
||||
"web_commit_signoff_required": false,
|
||||
"topics": [
|
||||
"android",
|
||||
"css",
|
||||
"hacktoberfest",
|
||||
"ios",
|
||||
"javascript",
|
||||
"keyboard",
|
||||
"keyboard-layouts",
|
||||
"keyman",
|
||||
"linux",
|
||||
"macos",
|
||||
"unicode",
|
||||
"web",
|
||||
"windows"
|
||||
],
|
||||
"visibility": "public",
|
||||
"forks": 120,
|
||||
"open_issues": 1043,
|
||||
"watchers": 456,
|
||||
"default_branch": "master"
|
||||
}
|
||||
},
|
||||
"base": {
|
||||
"label": "keymanapp:feat/developer/kmc-convert",
|
||||
"ref": "feat/developer/kmc-convert",
|
||||
"sha": "c7f6d15105920d8df18d8973b4f24846fa14ea27",
|
||||
"user": {
|
||||
"login": "keymanapp",
|
||||
"id": 12402926,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjEyNDAyOTI2",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12402926?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keymanapp",
|
||||
"html_url": "https://github.com/keymanapp",
|
||||
"followers_url": "https://api.github.com/users/keymanapp/followers",
|
||||
"following_url": "https://api.github.com/users/keymanapp/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keymanapp/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keymanapp/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keymanapp/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keymanapp/orgs",
|
||||
"repos_url": "https://api.github.com/users/keymanapp/repos",
|
||||
"events_url": "https://api.github.com/users/keymanapp/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keymanapp/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"repo": {
|
||||
"id": 26063183,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkyNjA2MzE4Mw==",
|
||||
"name": "keyman",
|
||||
"full_name": "keymanapp/keyman",
|
||||
"private": false,
|
||||
"owner": {
|
||||
"login": "keymanapp",
|
||||
"id": 12402926,
|
||||
"node_id": "MDEyOk9yZ2FuaXphdGlvbjEyNDAyOTI2",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/12402926?v=4",
|
||||
"gravatar_id": "",
|
||||
"url": "https://api.github.com/users/keymanapp",
|
||||
"html_url": "https://github.com/keymanapp",
|
||||
"followers_url": "https://api.github.com/users/keymanapp/followers",
|
||||
"following_url": "https://api.github.com/users/keymanapp/following{/other_user}",
|
||||
"gists_url": "https://api.github.com/users/keymanapp/gists{/gist_id}",
|
||||
"starred_url": "https://api.github.com/users/keymanapp/starred{/owner}{/repo}",
|
||||
"subscriptions_url": "https://api.github.com/users/keymanapp/subscriptions",
|
||||
"organizations_url": "https://api.github.com/users/keymanapp/orgs",
|
||||
"repos_url": "https://api.github.com/users/keymanapp/repos",
|
||||
"events_url": "https://api.github.com/users/keymanapp/events{/privacy}",
|
||||
"received_events_url": "https://api.github.com/users/keymanapp/received_events",
|
||||
"type": "Organization",
|
||||
"user_view_type": "public",
|
||||
"site_admin": false
|
||||
},
|
||||
"html_url": "https://github.com/keymanapp/keyman",
|
||||
"description": "Keyman cross platform input methods system running on Android, iOS, Linux, macOS, Windows and mobile and desktop web",
|
||||
"fork": false,
|
||||
"url": "https://api.github.com/repos/keymanapp/keyman",
|
||||
"forks_url": "https://api.github.com/repos/keymanapp/keyman/forks",
|
||||
"keys_url": "https://api.github.com/repos/keymanapp/keyman/keys{/key_id}",
|
||||
"collaborators_url": "https://api.github.com/repos/keymanapp/keyman/collaborators{/collaborator}",
|
||||
"teams_url": "https://api.github.com/repos/keymanapp/keyman/teams",
|
||||
"hooks_url": "https://api.github.com/repos/keymanapp/keyman/hooks",
|
||||
"issue_events_url": "https://api.github.com/repos/keymanapp/keyman/issues/events{/number}",
|
||||
"events_url": "https://api.github.com/repos/keymanapp/keyman/events",
|
||||
"assignees_url": "https://api.github.com/repos/keymanapp/keyman/assignees{/user}",
|
||||
"branches_url": "https://api.github.com/repos/keymanapp/keyman/branches{/branch}",
|
||||
"tags_url": "https://api.github.com/repos/keymanapp/keyman/tags",
|
||||
"blobs_url": "https://api.github.com/repos/keymanapp/keyman/git/blobs{/sha}",
|
||||
"git_tags_url": "https://api.github.com/repos/keymanapp/keyman/git/tags{/sha}",
|
||||
"git_refs_url": "https://api.github.com/repos/keymanapp/keyman/git/refs{/sha}",
|
||||
"trees_url": "https://api.github.com/repos/keymanapp/keyman/git/trees{/sha}",
|
||||
"statuses_url": "https://api.github.com/repos/keymanapp/keyman/statuses/{sha}",
|
||||
"languages_url": "https://api.github.com/repos/keymanapp/keyman/languages",
|
||||
"stargazers_url": "https://api.github.com/repos/keymanapp/keyman/stargazers",
|
||||
"contributors_url": "https://api.github.com/repos/keymanapp/keyman/contributors",
|
||||
"subscribers_url": "https://api.github.com/repos/keymanapp/keyman/subscribers",
|
||||
"subscription_url": "https://api.github.com/repos/keymanapp/keyman/subscription",
|
||||
"commits_url": "https://api.github.com/repos/keymanapp/keyman/commits{/sha}",
|
||||
"git_commits_url": "https://api.github.com/repos/keymanapp/keyman/git/commits{/sha}",
|
||||
"comments_url": "https://api.github.com/repos/keymanapp/keyman/comments{/number}",
|
||||
"issue_comment_url": "https://api.github.com/repos/keymanapp/keyman/issues/comments{/number}",
|
||||
"contents_url": "https://api.github.com/repos/keymanapp/keyman/contents/{+path}",
|
||||
"compare_url": "https://api.github.com/repos/keymanapp/keyman/compare/{base}...{head}",
|
||||
"merges_url": "https://api.github.com/repos/keymanapp/keyman/merges",
|
||||
"archive_url": "https://api.github.com/repos/keymanapp/keyman/{archive_format}{/ref}",
|
||||
"downloads_url": "https://api.github.com/repos/keymanapp/keyman/downloads",
|
||||
"issues_url": "https://api.github.com/repos/keymanapp/keyman/issues{/number}",
|
||||
"pulls_url": "https://api.github.com/repos/keymanapp/keyman/pulls{/number}",
|
||||
"milestones_url": "https://api.github.com/repos/keymanapp/keyman/milestones{/number}",
|
||||
"notifications_url": "https://api.github.com/repos/keymanapp/keyman/notifications{?since,all,participating}",
|
||||
"labels_url": "https://api.github.com/repos/keymanapp/keyman/labels{/name}",
|
||||
"releases_url": "https://api.github.com/repos/keymanapp/keyman/releases{/id}",
|
||||
"deployments_url": "https://api.github.com/repos/keymanapp/keyman/deployments",
|
||||
"created_at": "2014-11-01T21:01:44Z",
|
||||
"updated_at": "2025-05-31T18:02:06Z",
|
||||
"pushed_at": "2025-05-31T18:02:03Z",
|
||||
"git_url": "git://github.com/keymanapp/keyman.git",
|
||||
"ssh_url": "git@github.com:keymanapp/keyman.git",
|
||||
"clone_url": "https://github.com/keymanapp/keyman.git",
|
||||
"svn_url": "https://github.com/keymanapp/keyman",
|
||||
"homepage": "https://keyman.com/",
|
||||
"size": 489713,
|
||||
"stargazers_count": 456,
|
||||
"watchers_count": 456,
|
||||
"language": "Pascal",
|
||||
"has_issues": true,
|
||||
"has_projects": true,
|
||||
"has_downloads": true,
|
||||
"has_wiki": true,
|
||||
"has_pages": false,
|
||||
"has_discussions": false,
|
||||
"forks_count": 120,
|
||||
"mirror_url": null,
|
||||
"archived": false,
|
||||
"disabled": false,
|
||||
"open_issues_count": 1043,
|
||||
"license": {
|
||||
"key": "other",
|
||||
"name": "Other",
|
||||
"spdx_id": "NOASSERTION",
|
||||
"url": null,
|
||||
"node_id": "MDc6TGljZW5zZTA="
|
||||
},
|
||||
"allow_forking": true,
|
||||
"is_template": false,
|
||||
"web_commit_signoff_required": false,
|
||||
"topics": [
|
||||
"android",
|
||||
"css",
|
||||
"hacktoberfest",
|
||||
"ios",
|
||||
"javascript",
|
||||
"keyboard",
|
||||
"keyboard-layouts",
|
||||
"keyman",
|
||||
"linux",
|
||||
"macos",
|
||||
"unicode",
|
||||
"web",
|
||||
"windows"
|
||||
],
|
||||
"visibility": "public",
|
||||
"forks": 120,
|
||||
"open_issues": 1043,
|
||||
"watchers": 456,
|
||||
"default_branch": "master"
|
||||
}
|
||||
},
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/pulls/14013"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://github.com/keymanapp/keyman/pull/14013"
|
||||
},
|
||||
"issue": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/issues/14013"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/issues/14013/comments"
|
||||
},
|
||||
"review_comments": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/pulls/14013/comments"
|
||||
},
|
||||
"review_comment": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/pulls/comments{/number}"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/pulls/14013/commits"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.github.com/repos/keymanapp/keyman/statuses/00158fdeeefc98e7d2b507729e91fab845415f6f"
|
||||
}
|
||||
},
|
||||
"author_association": "CONTRIBUTOR",
|
||||
"auto_merge": null,
|
||||
"active_lock_reason": null,
|
||||
"merged": false,
|
||||
"mergeable": false,
|
||||
"rebaseable": false,
|
||||
"mergeable_state": "dirty",
|
||||
"merged_by": null,
|
||||
"comments": 1,
|
||||
"review_comments": 0,
|
||||
"maintainer_can_modify": false,
|
||||
"commits": 5,
|
||||
"additions": 639,
|
||||
"deletions": 12,
|
||||
"changed_files": 13
|
||||
}
|
||||
17
resources/build/test/build-bot/pr-9999-commits.txt
Normal file
17
resources/build/test/build-bot/pr-9999-commits.txt
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[
|
||||
{
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: build windows\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: release windows\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: skip windows\n"
|
||||
}
|
||||
}
|
||||
]
|
||||
3
resources/build/test/build-bot/pr-9999-data.txt
Normal file
3
resources/build/test/build-bot/pr-9999-data.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"body": "Build-bot: release developer\r\nBuild-bot: skip windows\r\n"
|
||||
}
|
||||
202
resources/build/test/build-bot/trigger-build-bot.test.sh
Executable file
202
resources/build/test/build-bot/trigger-build-bot.test.sh
Executable file
|
|
@ -0,0 +1,202 @@
|
|||
#!/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/build-utils.sh"
|
||||
# END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${THIS_SCRIPT_PATH}/../../trigger-definitions.inc.sh"
|
||||
. "${THIS_SCRIPT_PATH}/../../trigger-build-bot.inc.sh"
|
||||
. "${THIS_SCRIPT_PATH}/../test-utils.inc.sh"
|
||||
. "${KEYMAN_ROOT}/resources/build/jq.inc.sh"
|
||||
|
||||
# Initialize builder, no input parameters needed for these tests
|
||||
builder_parse ""
|
||||
|
||||
readonly ALL_BUILD_PLATFORMS_SKIP_EXPECTED='[ios]="skip" [web]="skip" [linux]="skip" [common_mac]="skip" [common_windows]="skip" [mac]="skip" [windows]="skip" [common_web]="skip" [android]="skip" [developer]="skip" [common_linux]="skip"'
|
||||
readonly ALL_BUILD_PLATFORMS_BUILD_EXPECTED='[ios]="build" [web]="build" [linux]="build" [common_mac]="build" [common_windows]="build" [mac]="build" [windows]="build" [common_web]="build" [android]="build" [developer]="build" [common_linux]="build"'
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
# Test build bot message parsing (e2e)
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_check_messages() {
|
||||
builder_echo start test_build_bot_check_messages 'START TEST: build_bot_check_messages'
|
||||
|
||||
# Mostly real data (only Build-bot commands edited for test)
|
||||
_do_test_build_bot_check_messages_file 14013 "[windows]=release" "$ALL_BUILD_PLATFORMS_SKIP_EXPECTED"
|
||||
# Simplified data for testing Build-bot command sequences only
|
||||
_do_test_build_bot_check_messages_file 9999 "[windows]=release" '[windows]="skip" [developer]="release"'
|
||||
|
||||
# test another sequence of commands
|
||||
_do_test_build_bot_check_messages_inline 8 "[windows]=release" '[common]="release" [windows]="skip" [developer]="build"' '{ "body": "Build-bot: skip windows\nBuild-bot: build developer\nBuild-bot: release common" }' '[]'
|
||||
|
||||
# Test some bad inputs
|
||||
_do_test_build_bot_check_messages_inline 1 "[windows]=release" '[windows]="release"' '{}' '[{ "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
# empty body, 'foo windows' in commit
|
||||
_do_test_build_bot_check_messages_inline 2 "[windows]=release" '[windows]="release"' '{ "body": "" }' '[{ "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
# attempts to escape jail
|
||||
_do_test_build_bot_check_messages_inline 3 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: '"'"'echo foo" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 4 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: echo *" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: \\0" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: `echo escaped`" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 6 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: ' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
# incomplete command
|
||||
_do_test_build_bot_check_messages_inline 7 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: " }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
|
||||
builder_echo end test_build_bot_check_messages success 'SUCCESS: build_bot_check_messages'
|
||||
}
|
||||
|
||||
#
|
||||
# Test a set of Build-bot commands from a file
|
||||
#
|
||||
# Parameters:
|
||||
# 1: PR number
|
||||
# 2: Input build platforms based on touched files in PR
|
||||
# 3: Expected build command
|
||||
_do_test_build_bot_check_messages_file() {
|
||||
local prnum=$1
|
||||
eval "declare -gA build_platforms=($2)"
|
||||
eval "declare -A expected_build_platforms=($3)"
|
||||
|
||||
build_bot_check_messages $prnum "$(cat ${THIS_SCRIPT_PATH}/pr-$prnum-data.txt)" "$(cat ${THIS_SCRIPT_PATH}/pr-$prnum-commits.txt)"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Test a set of Build-bot commands from parameters
|
||||
#
|
||||
# Parameters:
|
||||
# 1: PR number
|
||||
# 2: Input build platforms based on touched files in PR
|
||||
# 3: Expected build command
|
||||
# 4: JSON PR details (GitHub PR format)
|
||||
# 5: JSON commit messages (GitHub PR format)
|
||||
_do_test_build_bot_check_messages_inline() {
|
||||
local prnum=$1
|
||||
eval "declare -gA build_platforms=($2)"
|
||||
eval "declare -A expected_build_platforms=($3)"
|
||||
|
||||
local prinfo="$4"
|
||||
local prcommits="$5"
|
||||
|
||||
build_bot_check_messages $prnum "$prinfo" "$prcommits"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
# Test parsing of a single Build-bot command
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_update_commands() {
|
||||
builder_echo start test_build_bot_update_commands 'START TEST: build_bot_update_commands'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip windows" '[windows]="skip"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip windows,developer" '[windows]="skip" [developer]="skip"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build developer" '[windows]="release" [developer]="build"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip windows" '[windows]="skip"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "access foo" '[windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build foo" '[windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build common" '[common]="build" [windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build all" "$ALL_BUILD_PLATFORMS_BUILD_EXPECTED"
|
||||
builder_echo end test_build_bot_update_commands success 'SUCCESS: build_bot_update_commands'
|
||||
}
|
||||
|
||||
#
|
||||
# Test a command
|
||||
#
|
||||
# Parameters:
|
||||
# 1: starting build array based on PR files changed
|
||||
# 2: build-bot command
|
||||
# 3: expected finishing build array
|
||||
#
|
||||
_do_test_build_bot_update_commands() {
|
||||
eval "declare -gA build_platforms=($1)"
|
||||
local update_command="$2"
|
||||
eval "declare -A expected_build_platforms=($3)"
|
||||
|
||||
build_bot_update_commands $update_command
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "build_platforms[$i]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "build_platforms[$i]"
|
||||
done
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_verify_platforms() {
|
||||
builder_echo start test_build_bot_verify_platforms 'START TEST: build_bot_verify_platforms'
|
||||
_do_test_build_bot_verify_platforms "windows mac" "windows mac"
|
||||
_do_test_build_bot_verify_platforms "windows foo" "windows"
|
||||
_do_test_build_bot_verify_platforms "core" "core"
|
||||
_do_test_build_bot_verify_platforms "all" "${available_platforms[*]}"
|
||||
builder_echo end test_build_bot_verify_platforms success 'SUCCESS: build_bot_verify_platforms'
|
||||
}
|
||||
|
||||
_do_test_build_bot_verify_platforms() {
|
||||
local platforms=($1)
|
||||
local expected_platforms=($2)
|
||||
|
||||
build_bot_verify_platforms platforms
|
||||
|
||||
assert-equal ${#platforms[@]} ${#expected_platforms[@]} "#platforms[@]"
|
||||
for i in "${!expected_platforms[@]}"; do
|
||||
assert-equal "${platforms[$i]}" "${expected_platforms[$i]}" "platforms[$i]"
|
||||
done
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
# If we have a Test-bot: skip, or no Test-bot: command, then we downgrade
|
||||
# 'release' to a 'build'
|
||||
|
||||
test_test_bot_check_pr_body() {
|
||||
builder_echo start test_test_bot_check_pr_body 'START TEST: test_bot_check_pr_body'
|
||||
# Test-bot: skip means that we should only do a build+test, not a release build
|
||||
_do_test_test_bot_check_pr_body 1 '[windows]="release"' '{ "body": "Test-bot: skip" }' '[windows]="build"'
|
||||
# Empty body means that we should only do a build+test, not a release build
|
||||
_do_test_test_bot_check_pr_body 2 '[windows]="release"' '{ "body": "" }' '[windows]="build"'
|
||||
# '# User Testing' we will assume means there are unit tests
|
||||
_do_test_test_bot_check_pr_body 3 '[windows]="release"' '{ "body": "Some text\n# User Testing\n" }' '[windows]="release"'
|
||||
# Invalid test-bot command, treat as a skip
|
||||
_do_test_test_bot_check_pr_body 4 '[windows]="release"' '{ "body": "Test-bot: " }' '[windows]="build"'
|
||||
builder_echo end test_test_bot_check_pr_body success 'SUCCESS: test_bot_check_pr_body'
|
||||
}
|
||||
|
||||
_do_test_test_bot_check_pr_body() {
|
||||
local prnum=$1
|
||||
eval "declare -gA build_platforms=($2)"
|
||||
local prinfo="$3"
|
||||
eval "declare -A expected_build_platforms=($4)"
|
||||
|
||||
test_bot_check_pr_body $prnum "$prinfo"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
done
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_verify_platforms
|
||||
test_build_bot_update_commands
|
||||
test_build_bot_check_messages
|
||||
test_test_bot_check_pr_body
|
||||
|
|
@ -7,8 +7,8 @@ function assert-equal() {
|
|||
fi
|
||||
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
builder_die "FAIL: ${message}expected actual '$actual' to equal expected '$expected'"
|
||||
builder_die " ✕ FAIL: ${message}actual result '$actual' should equal expected '$expected'"
|
||||
else
|
||||
builder_echo green "PASS: ${message}expected actual '$actual' to be equal to expected '$expected'"
|
||||
builder_echo green " ✓ PASS: ${message}result '$actual' is correct"
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
164
resources/build/trigger-build-bot.inc.sh
Normal file
164
resources/build/trigger-build-bot.inc.sh
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# set -eu
|
||||
|
||||
# . ./trigger-definitions.inc.sh
|
||||
|
||||
#
|
||||
# Check the Test-bot command in the PR body for commands relevant to the build
|
||||
# bot, and update global build_platforms accordingly
|
||||
#
|
||||
# If the Test-bot command is 'skip', or there are no test bot instructions, then we
|
||||
# will default to a 'build' for all default build platforms, instead of
|
||||
# 'release'; this can be overridden by any Build-bot commands in commits or in
|
||||
# the PR body.
|
||||
#
|
||||
# Parameters:
|
||||
# 1: PR number (not currently used)
|
||||
# 2: PR JSON data from api.github.com/repos/keymanapp/keyman/pulls/#
|
||||
#
|
||||
function test_bot_check_pr_body() {
|
||||
local PRNUM=$1
|
||||
local prinfo="$2"
|
||||
|
||||
set -o noglob
|
||||
IFS=$'\n'
|
||||
local prbody="$(echo "$prinfo" | "${JQ}" -r '.body')"
|
||||
local prTestCommand="$(echo "$prbody" | grep 'Test-bot:' | cut -d: -f 2 - | cut -d' ' -f 1 -)"
|
||||
local prTestBody="$(echo "$prbody" | grep -i '# User Testing')"
|
||||
unset IFS
|
||||
set +o noglob
|
||||
|
||||
if ([[ "$prTestCommand" == skip ]] || [[ -z "${prTestCommand// }" ]]) && [[ -z "${prTestBody// }" ]]; then
|
||||
local platform
|
||||
for platform in "${!build_platforms[@]}"; do
|
||||
build_platforms[$platform]=build
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# Check PR commit messages for Build-bot commands. Later commands override
|
||||
# earlier ones. Also checks PR body for any overriding commands. Note, only
|
||||
# checks the first 250 commits, so later build commands will be ignored; in this
|
||||
# situation, use the PR body command. Also ignores build commands in PR
|
||||
# comments, for sake of performance
|
||||
#
|
||||
# The bot will also check the Test-bot: trailer in the PR body, if it exists, but
|
||||
# will not check any other commits. If the Test-bot: command is just 'skip', then
|
||||
# by default, all builds will be set to 'build' instead of 'release'
|
||||
#
|
||||
# Parameters:
|
||||
# 1: PR number to check
|
||||
# 2: PR JSON data from api.github.com/repos/keymanapp/keyman/pulls/#
|
||||
# 3: Commit JSON data from api.github.com/repos/keymanapp/keyman/pulls/#/commits
|
||||
#
|
||||
function build_bot_check_messages() {
|
||||
local PRNUM=$1
|
||||
local prinfo="$2"
|
||||
local prcommits="$3"
|
||||
|
||||
# We don't want any globbing happening in our parse
|
||||
|
||||
set -o noglob
|
||||
|
||||
# Extract the Build-bot commands from commit messages and the PR body
|
||||
|
||||
|
||||
IFS=$'\n'
|
||||
local buildBotCommands=($(echo "$prcommits" | "${JQ}" -r '.[].commit.message' | grep 'Build-bot:' | cut -d: -f 2 -))
|
||||
local prCommands=($(echo "$prinfo" | "${JQ}" -r '.body' | tr -d '\r' | grep 'Build-bot:' | cut -d: -f 2 -))
|
||||
unset IFS
|
||||
|
||||
# The PR body Build-bot comment will be read last, which allows it to override
|
||||
# all previous commands
|
||||
|
||||
if [[ ${#prCommands[@]} -gt 0 ]]; then
|
||||
buildBotCommands+=("${prCommands[@]}")
|
||||
fi
|
||||
|
||||
for buildBotCommand in "${buildBotCommands[@]}"; do
|
||||
# Block illegal Build-bot: commands
|
||||
if [[ ! "$buildBotCommand" =~ ^[a-z,\ ]+$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid command: '${buildBotCommand}'"
|
||||
continue
|
||||
fi
|
||||
|
||||
# We now know that our command has only a-z, comma and space, so we can
|
||||
# parse without risking escaping our bash jail
|
||||
|
||||
if [[ ! -z "${buildBotCommand// }" ]]; then
|
||||
build_bot_update_commands $buildBotCommand
|
||||
fi
|
||||
done
|
||||
|
||||
set +o noglob
|
||||
}
|
||||
|
||||
#
|
||||
# parses a 'Build-bot: <level> [platform...]' command and modifies
|
||||
# the global build_platforms associative array with new levels.
|
||||
#
|
||||
# Note that this function assumes that inputs are sanitized, see
|
||||
# build_bot_check_messages
|
||||
#
|
||||
function build_bot_update_commands() {
|
||||
local level=
|
||||
local platforms=
|
||||
local command="$*"
|
||||
|
||||
if [[ $# == 1 ]]; then
|
||||
level=$1
|
||||
platforms=all
|
||||
else
|
||||
level=$1
|
||||
shift
|
||||
|
||||
# remaining parameters are comma/space separated platforms
|
||||
IFS=', '
|
||||
read -r -a platforms <<< "$*"
|
||||
unset IFS
|
||||
fi
|
||||
|
||||
if [[ ! $level =~ ^$valid_build_levels$ ]]; then
|
||||
# Just skip this build command
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid build level '$level' in command '$command'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
build_bot_verify_platforms platforms
|
||||
|
||||
# a build command should be <level> [platform[, platform...]]
|
||||
|
||||
local platform
|
||||
for platform in "${platforms[@]}"; do
|
||||
builder_echo "Build-bot: Updating build level for $platform to $level"
|
||||
build_platforms[$platform]=$level
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Strip any unrecognized platforms and expand 'all' to actual platforms
|
||||
#
|
||||
# Parameters:
|
||||
# 1: name of platforms array parameter (byref)
|
||||
#
|
||||
function build_bot_verify_platforms() {
|
||||
local -n input_platforms=$1
|
||||
local output_platforms=()
|
||||
local platform
|
||||
for platform in "${input_platforms[@]}"; do
|
||||
# We'll emit a warning with invalid platforms, then remove them from the array
|
||||
if [[ ! $platform =~ ^(all|$available_platforms_regex)$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid platform '$platform'"
|
||||
elif [[ $platform == all ]]; then
|
||||
input_platforms=(${available_platforms[@]})
|
||||
return
|
||||
else
|
||||
if [[ ! "${output_platforms[@]}" =~ $platform ]]; then
|
||||
output_platforms+=($platform)
|
||||
fi
|
||||
fi
|
||||
done
|
||||
input_platforms=("${output_platforms[@]}")
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# Tell TeamCity to trigger new builds
|
||||
# Tell TeamCity to trigger new release builds
|
||||
#
|
||||
|
||||
function triggerBuilds() {
|
||||
function triggerReleaseBuilds() {
|
||||
local base=`git branch --show-current`
|
||||
# convert stable-14.0 to stable_14_0 to fit in with the definitions
|
||||
# in trigger-definitions.inc.sh
|
||||
|
|
@ -19,39 +18,49 @@ function triggerBuilds() {
|
|||
if [[ $build == "" ]]; then continue; fi
|
||||
if [ "${build:(-7)}" == "_GitHub" ]; then
|
||||
local job=${build%_GitHub}
|
||||
echo Triggering GitHub action build "$job" "$base"
|
||||
triggerGitHubActionsBuild false "$job" "$base"
|
||||
echo "Triggering GitHub action release build (isTestBuild=false) (level=release) $job $base"
|
||||
triggerGitHubActionsBuild false release "$job" "$base"
|
||||
else
|
||||
echo Triggering TeamCity build false $build $TEAMCITY_VCS_ID $base
|
||||
triggerTeamCityBuild false $build $TEAMCITY_VCS_ID $base
|
||||
echo "Triggering TeamCity release build (isTestBuild=false) (level=release) $build $TEAMCITY_VCS_ID $base"
|
||||
triggerTeamCityBuild false release $build $TEAMCITY_VCS_ID $base
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Trigger a build on TeamCity
|
||||
#
|
||||
# Parameters:
|
||||
# 1: isTestBuild: 'true' if this is for a PR branch or a test build
|
||||
# for a primary branch; false is this is a release
|
||||
# build for a primary branch (master/beta/stable-x.y)
|
||||
# 2: buildLevel: 'skip' - don't run build;
|
||||
# 'build' - run build and unit tests;
|
||||
# 'release' - run build, unit tests, and deploy
|
||||
# 3: TeamCity build configuration name
|
||||
# 4: TeamCity VCS id, which is different for PR test configurations
|
||||
# 5: branch name in git
|
||||
#
|
||||
function triggerTeamCityBuild() {
|
||||
local isTestBuild="$1"
|
||||
local TEAMCITY_BUILDTYPE="$2"
|
||||
local TEAMCITY_VCS_ID="$3"
|
||||
local buildLevel="$2"
|
||||
local TEAMCITY_BUILDTYPE="$3"
|
||||
local TEAMCITY_VCS_ID="$4"
|
||||
local TEAMCITY_BRANCH_NAME="$5"
|
||||
|
||||
if [[ $# -gt 3 ]]; then
|
||||
local TEAMCITY_BRANCH_NAME="$4"
|
||||
#debug echo " Triggering build for: $TEAMCITY_BRANCH_NAME"
|
||||
TEAMCITY_BRANCH_NAME="branchName='$TEAMCITY_BRANCH_NAME' defaultBranch='false'"
|
||||
else
|
||||
local TEAMCITY_BRANCH_NAME=
|
||||
fi
|
||||
|
||||
local GIT_OID=`git rev-parse HEAD`
|
||||
local TEAMCITY_SERVER=https://build.palaso.org
|
||||
|
||||
local command
|
||||
local commandProperties="<properties><property name='env.KEYMAN_BUILD_LEVEL' value='$buildLevel' /></properties>"
|
||||
local commandLastChanges=
|
||||
|
||||
if $isTestBuild; then
|
||||
command="<build $TEAMCITY_BRANCH_NAME><buildType id='$TEAMCITY_BUILDTYPE' /></build>"
|
||||
else
|
||||
command="<build $TEAMCITY_BRANCH_NAME><buildType id='$TEAMCITY_BUILDTYPE' /><lastChanges><change vcsRootInstance='$TEAMCITY_VCS_ID' locator='version:$GIT_OID'/></lastChanges></build>"
|
||||
if ! $isTestBuild; then
|
||||
local GIT_OID=`git rev-parse HEAD`
|
||||
commandLastChanges="<lastChanges><change vcsRootInstance='$TEAMCITY_VCS_ID' locator='version:$GIT_OID'/></lastChanges>"
|
||||
fi
|
||||
|
||||
local command="<build branchName='$TEAMCITY_BRANCH_NAME' defaultBranch='false'><buildType id='$TEAMCITY_BUILDTYPE' />$commandLastChanges$commandProperties</build>"
|
||||
|
||||
echo "TeamCity Build Command: $command"
|
||||
|
||||
# adjust indentation for output of curl
|
||||
|
|
@ -66,10 +75,20 @@ function triggerTeamCityBuild() {
|
|||
echo
|
||||
}
|
||||
|
||||
#
|
||||
# Trigger a build on GitHub Actions
|
||||
#
|
||||
# Parameters:
|
||||
# 1: 'true' if this is for a PR branch, otherwise 'false' (for master/beta/stable-x.y)
|
||||
# 2: 'skip' - don't run build; 'build' - run build and unit tests; 'test' - run build, unit tests, and deploy
|
||||
# 3: Action name
|
||||
# 4: branch name in git
|
||||
#
|
||||
function triggerGitHubActionsBuild() {
|
||||
local IS_TEST_BUILD="$1"
|
||||
local GITHUB_ACTION="$2"
|
||||
local GIT_BRANCH="${3:-master}"
|
||||
local BUILD_LEVEL="$2"
|
||||
local GITHUB_ACTION="$3"
|
||||
local GIT_BRANCH="$4"
|
||||
local GIT_BASE_BRANCH="${GIT_BRANCH}"
|
||||
local GIT_USER="keyman-server"
|
||||
local GIT_BUILD_SHA GIT_BASE_REF JSON
|
||||
|
|
@ -81,7 +100,8 @@ function triggerGitHubActionsBuild() {
|
|||
GIT_BUILD_SHA="$(git rev-parse "refs/tags/release@$KEYMAN_VERSION_WITH_TAG")"
|
||||
GIT_BASE_REF="$(git rev-parse "${GIT_BUILD_SHA}^")"
|
||||
GIT_EVENT_TYPE="${GITHUB_ACTION}: release@${KEYMAN_VERSION_WITH_TAG}"
|
||||
elif [[ $GIT_BRANCH != stable-* ]] && [[ $GIT_BRANCH =~ [0-9]+ ]]; then
|
||||
elif [[ $GIT_BRANCH =~ ^[0-9]+$ ]]; then
|
||||
# pull request
|
||||
local JSON=$(call_curl "${GITHUB_SERVER}/pulls/${GIT_BRANCH}" --header "Authorization: token $GITHUB_TOKEN")
|
||||
GIT_BUILD_SHA="$(echo "$JSON" | $JQ -r '.head.sha')"
|
||||
GIT_EVENT_TYPE="${GITHUB_ACTION}: PR #${GIT_BRANCH}"
|
||||
|
|
@ -90,6 +110,7 @@ function triggerGitHubActionsBuild() {
|
|||
GIT_BASE_REF="$(echo "$JSON" | $JQ -r '.base.sha')"
|
||||
GIT_BRANCH="PR-${GIT_BRANCH}"
|
||||
else
|
||||
# another branch: stable-x.y, beta, or master
|
||||
GIT_BUILD_SHA="$(git rev-parse "refs/heads/${GIT_BRANCH}")"
|
||||
GIT_BASE_REF="$(git rev-parse "${GIT_BUILD_SHA}^")"
|
||||
GIT_EVENT_TYPE="${GITHUB_ACTION}: ${GIT_BRANCH}"
|
||||
|
|
@ -103,7 +124,8 @@ function triggerGitHubActionsBuild() {
|
|||
\"baseBranch\": \"$GIT_BASE_BRANCH\", \
|
||||
\"baseRef\": \"$GIT_BASE_REF\", \
|
||||
\"user\": \"$GIT_USER\", \
|
||||
\"isTestBuild\": \"$IS_TEST_BUILD\" \
|
||||
\"isTestBuild\": \"$IS_TEST_BUILD\", \
|
||||
\"buildLevel\": \"$BUILD_LEVEL\" \
|
||||
}}"
|
||||
|
||||
echo "GitHub Action Data: $DATA"
|
||||
|
|
|
|||
|
|
@ -5,9 +5,39 @@
|
|||
# Maps to ci/cancel-builds/trigger-definitions.mjs and must be kept in sync
|
||||
#
|
||||
|
||||
#
|
||||
# # Build levels
|
||||
#
|
||||
# See /docs/build-bot.md
|
||||
#
|
||||
|
||||
readonly build_level_skip=skip
|
||||
readonly build_level_build=build
|
||||
readonly build_level_release=release
|
||||
# TODO: future build_level_fulltest=fulltest --> do all expensive e2e tests as well as producing release artifacts
|
||||
readonly valid_build_levels="$build_level_skip|$build_level_build|$build_level_release"
|
||||
|
||||
#
|
||||
# Target platforms
|
||||
#
|
||||
|
||||
available_platforms=(android common_web common_windows common_mac common_linux ios linux mac web windows developer)
|
||||
|
||||
# For test builds on master, beta, and stable-x.y branches, we always "build",
|
||||
# not "release" -- that is, we don't produce artifacts
|
||||
declare -Ag main_branch_platform_build_levels
|
||||
for available_platforms_i in "${available_platforms[@]}"; do
|
||||
main_branch_platform_build_levels[$available_platforms_i]=$build_level_build
|
||||
done
|
||||
readonly main_branch_platform_build_levels
|
||||
readonly available_platforms
|
||||
|
||||
|
||||
available_platforms_regex=`echo "${available_platforms[@]}" | sed 's/ /|/g'`
|
||||
|
||||
# We also allow 'common' and 'core' platforms for Build-bot: commands
|
||||
readonly available_platforms_regex="$available_platforms_regex|common|core"
|
||||
|
||||
# the base folder for each pattern does not need to be included, nor oem folders
|
||||
# e.g. android='common/models|common/predictive-text'
|
||||
# will expand into android='^(android|(oem/[^/]+/android)|common/models|common/predictive-text)'
|
||||
|
|
@ -57,6 +87,13 @@ bc_test_common_windows=(Keyman_Test_Common_Windows)
|
|||
bc_test_common_mac=(Keyman_Test_Common_Mac)
|
||||
bc_test_common_linux=(Keyman_Test_Common_Linux)
|
||||
|
||||
# These configuration arrays are triggered only by Build-bot commands:
|
||||
|
||||
bc_test_common=(Keyman_Test_Common_Web Keyman_Test_Common_Windows Keyman_Test_Common_Mac Keyman_Test_Common_Linux)
|
||||
bc_test_core=(Keyman_Common_KPAPI_TestPullRequests_Linux Keyman_Common_KPAPI_TestPullRequests_macOS Keyman_Common_KPAPI_TestPullRequests_Windows)
|
||||
|
||||
# Core is tested directly for target platforms
|
||||
|
||||
# Keymanweb_TestPullRequestRegressions : currently this is timing out so disabled until we have
|
||||
# time to investigate further
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ COMMON_ROOT="$KEYMAN_ROOT/common/windows/delphi"
|
|||
OUTLIB="$WINDOWS_ROOT/lib"
|
||||
COMMON_OUTLIB="$KEYMAN_ROOT/common/windows/lib"
|
||||
|
||||
if builder_is_debug_build || [[ $KEYMAN_VERSION_ENVIRONMENT == local ]] || [[ ! -z ${TEAMCITY_PR_NUMBER+x} ]]; then
|
||||
if builder_is_debug_build || [[ $KEYMAN_VERSION_ENVIRONMENT == local ]] || builder_is_ci_test_build; then
|
||||
# We do a fast build for debug builds, local builds, test PR builds but not for master/beta/stable release builds
|
||||
GO_FAST=1
|
||||
else
|
||||
|
|
@ -73,6 +73,11 @@ run_in_delphi_env() {
|
|||
}
|
||||
|
||||
sentrytool_delphiprep() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_build; then
|
||||
builder_echo "Skipping sentrytool_delphiprep - buildLevel=build: $@"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local EXE_PATH="$1"
|
||||
local DPR_PATH="$2"
|
||||
(
|
||||
|
|
@ -83,7 +88,7 @@ sentrytool_delphiprep() {
|
|||
}
|
||||
|
||||
tds2dbg() {
|
||||
"$TDS2DBG" "$@"
|
||||
builder_if_release_build_level "$TDS2DBG" "$@"
|
||||
}
|
||||
|
||||
delphi_msbuild() {
|
||||
|
|
@ -108,6 +113,11 @@ clean_windows_project_files() {
|
|||
}
|
||||
|
||||
wrap-signcode() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_build; then
|
||||
builder_echo "Skipping code signing - buildLevel=build: $@"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# CI will usually pass in a full path for signtool.exe; for local builds we
|
||||
# will hopefully find what we want on the path already
|
||||
if [[ -z "${SIGNTOOL+x}" ]]; then
|
||||
|
|
@ -117,6 +127,11 @@ wrap-signcode() {
|
|||
}
|
||||
|
||||
wrap-symstore() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_build; then
|
||||
builder_echo "Skipping symstore - buildLevel=build"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ -z "${KEYMAN_SYMSTOREPATH+x}" ]]; then
|
||||
builder_warn "\$KEYMAN_SYMSTOREPATH is not set. Skipping symstore for $@"
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -2221,6 +2221,47 @@ builder_is_ci_test_build() {
|
|||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if current ci build is a release-level build. Do not use for non-ci
|
||||
# builds.
|
||||
#
|
||||
builder_is_ci_build_level_release() {
|
||||
if builder_is_ci_release_build; then
|
||||
return 0
|
||||
fi
|
||||
if [[ "$KEYMAN_BUILD_LEVEL" == release ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#
|
||||
# Returns 0 if current ci build is a build-level build. Do not use for non-ci
|
||||
# builds.
|
||||
#
|
||||
builder_is_ci_build_level_build() {
|
||||
if builder_is_ci_release_build; then
|
||||
return 1
|
||||
fi
|
||||
if builder_is_ci_build_level_release; then
|
||||
# KEYMAN_BUILD_LEVEL == release, i.e. not build
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#
|
||||
# Executes statement if a ci build level of 'release', and for local builds, but
|
||||
# not for a ci build level of 'build'
|
||||
#
|
||||
builder_if_release_build_level() {
|
||||
if builder_is_ci_build && builder_is_ci_build_level_build; then
|
||||
builder_echo "Skipping - buildLevel=build: $@"
|
||||
return 0
|
||||
fi
|
||||
"$@"
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Final initialization
|
||||
################################################################################
|
||||
|
|
|
|||
|
|
@ -19,12 +19,17 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
|
||||
builder_describe \
|
||||
"Build Keyman Developer on Windows" \
|
||||
"all run all actions" \
|
||||
"all run all actions (used by TeamCity build configuration)" \
|
||||
"build build Keyman Developer and test keyboards" \
|
||||
"publish publish debug information files to sentry"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
if ! is_windows; then
|
||||
builder_echo error "This script is intended to be run on Windows only."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2154
|
||||
cd "${KEYMAN_ROOT}/developer/src"
|
||||
|
||||
|
|
@ -34,11 +39,11 @@ function build_developer_action() {
|
|||
}
|
||||
|
||||
function _build_developer() {
|
||||
builder_echo start "build developer" "Building Keyman Developer"
|
||||
builder_echo start "build developer" "Building and testing Keyman Developer"
|
||||
|
||||
./build.sh configure build test api publish --dry-run
|
||||
./build.sh configure build test
|
||||
|
||||
builder_echo end "build developer" success "Finished building Keyman Developer"
|
||||
builder_echo end "build developer" success "Finished building and testing Keyman Developer"
|
||||
}
|
||||
|
||||
function _build_testkeyboards() {
|
||||
|
|
@ -50,21 +55,22 @@ function _build_testkeyboards() {
|
|||
}
|
||||
|
||||
function publish_sentry_action() {
|
||||
builder_echo start "publish" "Dry-run publish and api"
|
||||
./build.sh api publish --dry-run
|
||||
builder_echo end "publish" "Dry-run publish and api"
|
||||
|
||||
builder_echo start "publish sentry" "Publishing debug information files to Sentry"
|
||||
|
||||
# TODO: move this into build.sh publish? re-scope --dry-run into build.sh?
|
||||
"${KEYMAN_ROOT}/developer/src/tools/sentry-upload-difs.sh"
|
||||
|
||||
builder_echo end "publish sentry" success "Finished publishing debug information files to Sentry"
|
||||
}
|
||||
|
||||
if ! is_windows; then
|
||||
builder_echo error "This script is intended to be run on Windows only."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if builder_has_action all; then
|
||||
# TODO: control codesign by KEYMAN_BUILD_LEVEL
|
||||
build_developer_action
|
||||
publish_sentry_action
|
||||
if builder_is_ci_build_level_release; then
|
||||
publish_sentry_action
|
||||
fi
|
||||
else
|
||||
builder_run_action build build_developer_action
|
||||
builder_run_action publish publish_sentry_action
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ function do_build_desktop_resources() {
|
|||
}
|
||||
|
||||
function do_publish() {
|
||||
verify-program-signatures
|
||||
builder_if_release_build_level verify-program-signatures
|
||||
|
||||
"$KEYMAN_ROOT/common/windows/cef-checkout.sh"
|
||||
|
||||
|
|
@ -73,6 +73,7 @@ function do_publish() {
|
|||
-sice:ICE82 -sice:ICE80 \
|
||||
-nologo \
|
||||
-dWixUILicenseRtf=License.rtf \
|
||||
"$WIXLIGHTCOMPRESSION" \
|
||||
-out keymandesktop.msi -ext WixUIExtension \
|
||||
keymandesktop.wixobj desktopui.wixobj cef.wixobj locale.wixobj
|
||||
|
||||
|
|
@ -106,7 +107,7 @@ function copy-installer() {
|
|||
cp keymandesktop.exe "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/keyman-${KEYMAN_VERSION}.exe"
|
||||
cp "$WINDOWS_PROGRAM_APP/setup.exe" "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/setup.exe"
|
||||
|
||||
verify-installer-signatures
|
||||
builder_if_release_build_level verify-installer-signatures
|
||||
|
||||
# Copy the unsigned setup.exe for use in bundling scenarios; zip it up for clarity
|
||||
wzzip "$KEYMAN_ROOT/windows/release/${KEYMAN_VERSION}/setup-redist.zip" "$WINDOWS_PROGRAM_APP/setup-redist.exe"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
|
||||
cp "$WIN32_TARGET_PATH/insthelp.dbg" "$WINDOWS_DEBUGPATH_APP/insthelp.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelp.dbg" "$WINDOWS_DEBUGPATH_APP/insthelp.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
|
||||
cp "$WIN32_TARGET_PATH/kmbrowserhost.dbg" "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmbrowserhost.dbg" "$WINDOWS_DEBUGPATH_APP/kmbrowserhost.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
|
||||
cp "$WIN32_TARGET_PATH/kmconfig.dbg" "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmconfig.dbg" "$WINDOWS_DEBUGPATH_APP/kmconfig.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
|
||||
cp "$WIN32_TARGET_PATH/kmshell.dbg" "$WINDOWS_DEBUGPATH_APP/kmshell.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmshell.dbg" "$WINDOWS_DEBUGPATH_APP/kmshell.dbg"
|
||||
|
||||
do_build_data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ function do_build() {
|
|||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP"
|
||||
# setup-redist.exe does not get signed as it is intended for a bundled installer
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_APP/setup-redist.exe"
|
||||
cp "$WIN32_TARGET_PATH/setup.dbg" "$WINDOWS_DEBUGPATH_APP/setup.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/setup.dbg" "$WINDOWS_DEBUGPATH_APP/setup.dbg"
|
||||
}
|
||||
|
||||
function do_build_debug_manifest() {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ fi
|
|||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
function do_publish() {
|
||||
verify-program-signatures
|
||||
builder_if_release_build_level verify-program-signatures
|
||||
|
||||
#
|
||||
# Build the installation archive
|
||||
|
|
@ -38,7 +38,12 @@ function do_publish() {
|
|||
"$WIXCANDLE" -dKEYMAN_VERSION=$KEYMAN_VERSION_WIN -dRELEASE=$KEYMAN_VERSION_RELEASE -ext WixUtilExtension keymanengine.wxs components.wxs
|
||||
|
||||
# warning 1072 relates to Error table defined by WixUtilExtension. Doesn't really affect us.
|
||||
"$WIXLIGHT" -sw1072 -ext WixUtilExtension keymanengine.wixobj components.wixobj -o keymanengine.msm
|
||||
"$WIXLIGHT" \
|
||||
-sw1072 \
|
||||
-ext WixUtilExtension \
|
||||
"$WIXLIGHTCOMPRESSION" \
|
||||
keymanengine.wixobj components.wixobj \
|
||||
-o keymanengine.msm
|
||||
|
||||
#
|
||||
# Sign the installation archive
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/insthelper.dbg" "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/insthelper.dbg" "$WINDOWS_DEBUGPATH_ENGINE/insthelper.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ function do_build() {
|
|||
tds2dbg "$WIN32_TARGET"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/keyman.dbg" "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/keyman.dbg" "$WINDOWS_DEBUGPATH_ENGINE/keyman.dbg"
|
||||
|
||||
# Also copy sentry files here
|
||||
cp "$KEYMAN_ROOT/common/windows/delphi/ext/sentry/sentry.dll" \
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function do_build_x86() {
|
|||
build_version.res
|
||||
vs_msbuild keyman32.vcxproj //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/keyman32.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/keyman32.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_build_x64() {
|
||||
|
|
@ -49,7 +49,7 @@ function do_build_x64() {
|
|||
run_in_vs_env rc version64.rc
|
||||
vs_msbuild keyman32.vcxproj //t:Build "//p:Platform=x64"
|
||||
cp "$X64_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$X64_TARGET_PATH/keyman64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/keyman64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_publish_x86() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ function do_build() {
|
|||
build_manifest.res
|
||||
vs_msbuild keymanx64.vcxproj //t:Build "//p:Platform=x64"
|
||||
cp "$X64_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$X64_TARGET_PATH/keymanx64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/keymanx64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function do_build() {
|
|||
mv -f "$WIN64_TARGET_PATH/kmcomapi.dll" "$WIN64_TARGET_PATH/kmcomapi.x64.dll"
|
||||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_DEBUGPATH_ENGINE/kmcomapi.dbg"
|
||||
cp "$WIN64_TARGET_PATH/kmcomapi.x64.dll" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dll"
|
||||
|
||||
# x64 Delphi symbols not supported: cp "$WIN64_TARGET_PATH/kmcomapi.dbg" "$WINDOWS_PROGRAM_ENGINE/kmcomapi.x64.dbg"
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ function do_build() {
|
|||
vs_msbuild kmrefresh.vcxproj //t:Build "//p:Platform=Win32"
|
||||
vs_msbuild kmrefresh.vcxproj //t:Build "//p:Platform=x64"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/kmrefresh.x86.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmrefresh.x86.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
cp "$X64_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$X64_TARGET_PATH/kmrefresh.x64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/kmrefresh.x64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ function do_build() {
|
|||
vs_msbuild kmtip.vcxproj //t:Build "//p:Platform=Win32"
|
||||
vs_msbuild kmtip.vcxproj //t:Build "//p:Platform=x64"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/kmtip.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/kmtip.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
cp "$X64_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$X64_TARGET_PATH/kmtip64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/kmtip64.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ function do_build() {
|
|||
build_manifest.res
|
||||
vs_msbuild mcompile.vcxproj //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp "$WIN32_TARGET_PATH/mcompile.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/mcompile.pdb" "$WINDOWS_DEBUGPATH_ENGINE"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ function do_build() {
|
|||
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_ENGINE"
|
||||
cp *.xslt "$WINDOWS_PROGRAM_ENGINE/"
|
||||
cp "$WIN32_TARGET_PATH/tsysinfo.dbg" "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/tsysinfo.dbg" "$WINDOWS_DEBUGPATH_ENGINE/tsysinfo.dbg"
|
||||
}
|
||||
|
||||
function do_publish() {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ function do_build() {
|
|||
build_version.res
|
||||
vs_msbuild etl2log.vcxproj //t:Build "//p:Platform=Win32"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_SUPPORT"
|
||||
cp "$WIN32_TARGET_PATH/etl2log.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/etl2log.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
}
|
||||
|
||||
builder_run_action clean:project do_clean
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ function do_build() {
|
|||
vs_msbuild Editor.vcxproj //t:Build "//p:Platform=x64"
|
||||
cp "$WIN32_TARGET" "$WINDOWS_PROGRAM_SUPPORT"
|
||||
cp "$X64_TARGET" "$WINDOWS_PROGRAM_SUPPORT"
|
||||
cp "$WIN32_TARGET_PATH/editor32.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
cp "$X64_TARGET_PATH/editor64.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
builder_if_release_build_level cp "$WIN32_TARGET_PATH/editor32.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
builder_if_release_build_level cp "$X64_TARGET_PATH/editor64.pdb" "$WINDOWS_DEBUGPATH_SUPPORT"
|
||||
}
|
||||
|
||||
builder_run_action clean:project do_clean
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue