From 3b3014adac5e456db8ad428ccc01bec75242487f Mon Sep 17 00:00:00 2001 From: Chris Trzesniewski Date: Sat, 28 May 2022 18:01:04 +0200 Subject: [PATCH] v1.14.0 --- action.yml | 3 ++ dist/src/config.js | 6 +++ dist/src/default_index_html.js | 1 + dist/src/extract.js | 21 ++++++++++ dist/src/git.js | 49 ++++++++++++++++------- dist/src/write.js | 71 +++++++++++++++++++++++----------- package-lock.json | 26 ++++++------- 7 files changed, 128 insertions(+), 49 deletions(-) diff --git a/action.yml b/action.yml index 267990ef5..772dfce4e 100644 --- a/action.yml +++ b/action.yml @@ -20,6 +20,9 @@ inputs: description: 'Branch for gh-pages' required: true default: 'gh-pages' + gh-repository: + description: 'Url to an optional different repository to store benchmark results' + required: false benchmark-data-dir-path: description: 'Path to directory which contains benchmark files on GitHub pages branch' required: true diff --git a/dist/src/config.js b/dist/src/config.js index d3553bff3..bf4be0ad2 100644 --- a/dist/src/config.js +++ b/dist/src/config.js @@ -28,6 +28,7 @@ exports.VALID_TOOLS = [ 'cargo', 'go', 'benchmarkjs', + 'benchmarkluau', 'pytest', 'googlecpp', 'catch2', @@ -196,6 +197,7 @@ async function configFromJobInput() { const tool = core.getInput('tool'); let outputFilePath = core.getInput('output-file-path'); const ghPagesBranch = core.getInput('gh-pages-branch'); + const ghRepository = core.getInput('gh-repository'); let benchmarkDataDirPath = core.getInput('benchmark-data-dir-path'); const name = core.getInput('name'); const githubToken = core.getInput('github-token') || undefined; @@ -224,6 +226,9 @@ async function configFromJobInput() { if (commentOnAlert) { validateGitHubToken('comment-on-alert', githubToken, 'to send commit comment on alert'); } + if (ghRepository) { + validateGitHubToken('gh-repository', githubToken, 'to clone the repository'); + } validateAlertThreshold(alertThreshold, failThreshold); validateAlertCommentCcUsers(alertCommentCcUsers); externalDataJsonPath = await validateExternalDataJsonPath(externalDataJsonPath, autoPush); @@ -236,6 +241,7 @@ async function configFromJobInput() { tool, outputFilePath, ghPagesBranch, + ghRepository, benchmarkDataDirPath, githubToken, autoPush, diff --git a/dist/src/default_index_html.js b/dist/src/default_index_html.js index 2c336d878..97181c872 100644 --- a/dist/src/default_index_html.js +++ b/dist/src/default_index_html.js @@ -117,6 +117,7 @@ exports.DEFAULT_INDEX_HTML = String.raw ` cargo: '#dea584', go: '#00add8', benchmarkjs: '#f1e05a', + benchmarkluau: '#000080', pytest: '#3572a5', googlecpp: '#f34b7d', catch2: '#f34b7d', diff --git a/dist/src/extract.js b/dist/src/extract.js index 8e611d39a..8a32cbbf1 100644 --- a/dist/src/extract.js +++ b/dist/src/extract.js @@ -358,6 +358,24 @@ function extractCustomBenchmarkResult(output) { throw new Error(`Output file for 'custom-(bigger|smaller)-is-better' must be JSON file containing an array of entries in BenchmarkResult format: ${err.message}`); } } +function extractLuauBenchmarkResult(output) { + const lines = output.split(/\n/); + const results = []; + output; + for (const line of lines) { + if (!line.startsWith('SUCCESS')) + continue; + const [_0, name, _2, valueStr, _4, range, _6, extra] = line.split(/\s+/); + results.push({ + name: name, + value: parseFloat(valueStr), + unit: valueStr.replace(/.[0-9]+/g, ''), + range: `±${range}`, + extra: extra, + }); + } + return results; +} async function extractResult(config) { const output = await fs_1.promises.readFile(config.outputFilePath, 'utf8'); const { tool, githubToken } = config; @@ -393,6 +411,9 @@ async function extractResult(config) { case 'customSmallerIsBetter': benches = extractCustomBenchmarkResult(output); break; + case 'benchmarkluau': + benches = extractLuauBenchmarkResult(output); + break; default: throw new Error(`FATAL: Unexpected tool: '${tool}'`); } diff --git a/dist/src/git.js b/dist/src/git.js index ac3037ce5..8b1630c1d 100644 --- a/dist/src/git.js +++ b/dist/src/git.js @@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.fetch = exports.pull = exports.push = exports.cmd = exports.getServerName = exports.getServerUrl = void 0; +exports.checkout = exports.clone = exports.fetch = exports.pull = exports.push = exports.cmd = exports.getServerName = exports.getServerUrl = void 0; const exec_1 = require("@actions/exec"); const core = __importStar(require("@actions/core")); const github = __importStar(require("@actions/github")); @@ -61,11 +61,12 @@ function getServerName(repositoryUrl) { return repositoryUrl ? urlObj.hostname : DEFAULT_GITHUB_URL.replace('https://', ''); } exports.getServerName = getServerName; -async function cmd(...args) { +async function cmd(additionalGitOptions, ...args) { var _a; core.debug(`Executing Git: ${args.join(' ')}`); const serverUrl = getServerUrl((_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.html_url); const userArgs = [ + ...additionalGitOptions, '-c', 'user.name=github-action-benchmark', '-c', @@ -80,40 +81,62 @@ async function cmd(...args) { return res.stdout; } exports.cmd = cmd; -function getRemoteUrl(token) { +function getCurrentRepoRemoteUrl(token) { var _a; const { repo, owner } = github.context.repo; const serverName = getServerName((_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.html_url); - return `https://x-access-token:${token}@${serverName}/${owner}/${repo}.git`; + return getRepoRemoteUrl(token, `${serverName}/${owner}/${repo}`); } -async function push(token, branch, ...options) { +function getRepoRemoteUrl(token, repoUrl) { + return `https://x-access-token:${token}@${repoUrl}.git`; +} +async function push(token, repoUrl, branch, additionalGitOptions = [], ...options) { core.debug(`Executing 'git push' to branch '${branch}' with token and options '${options.join(' ')}'`); - const remote = getRemoteUrl(token); + const remote = repoUrl ? getRepoRemoteUrl(token, repoUrl) : getCurrentRepoRemoteUrl(token); let args = ['push', remote, `${branch}:${branch}`, '--no-verify']; if (options.length > 0) { args = args.concat(options); } - return cmd(...args); + return cmd(additionalGitOptions, ...args); } exports.push = push; -async function pull(token, branch, ...options) { +async function pull(token, branch, additionalGitOptions = [], ...options) { core.debug(`Executing 'git pull' to branch '${branch}' with token and options '${options.join(' ')}'`); - const remote = token !== undefined ? getRemoteUrl(token) : 'origin'; + const remote = token !== undefined ? getCurrentRepoRemoteUrl(token) : 'origin'; let args = ['pull', remote, branch]; if (options.length > 0) { args = args.concat(options); } - return cmd(...args); + return cmd(additionalGitOptions, ...args); } exports.pull = pull; -async function fetch(token, branch, ...options) { +async function fetch(token, branch, additionalGitOptions = [], ...options) { core.debug(`Executing 'git fetch' for branch '${branch}' with token and options '${options.join(' ')}'`); - const remote = token !== undefined ? getRemoteUrl(token) : 'origin'; + const remote = token !== undefined ? getCurrentRepoRemoteUrl(token) : 'origin'; let args = ['fetch', remote, `${branch}:${branch}`]; if (options.length > 0) { args = args.concat(options); } - return cmd(...args); + return cmd(additionalGitOptions, ...args); } exports.fetch = fetch; +async function clone(token, ghRepository, baseDirectory, additionalGitOptions = [], ...options) { + core.debug(`Executing 'git clone' to directory '${baseDirectory}' with token and options '${options.join(' ')}'`); + const remote = getRepoRemoteUrl(token, ghRepository); + let args = ['clone', remote, baseDirectory]; + if (options.length > 0) { + args = args.concat(options); + } + return cmd(additionalGitOptions, ...args); +} +exports.clone = clone; +async function checkout(ghRef, additionalGitOptions = [], ...options) { + core.debug(`Executing 'git checkout' to ref '${ghRef}' with token and options '${options.join(' ')}'`); + let args = ['checkout', ghRef]; + if (options.length > 0) { + args = args.concat(options); + } + return cmd(additionalGitOptions, ...args); +} +exports.checkout = checkout; //# sourceMappingURL=git.js.map \ No newline at end of file diff --git a/dist/src/write.js b/dist/src/write.js index 040f7d245..84550d217 100644 --- a/dist/src/write.js +++ b/dist/src/write.js @@ -51,19 +51,20 @@ async function storeDataJs(dataPath, data) { await fs_1.promises.writeFile(dataPath, script, 'utf8'); core.debug(`Overwrote ${dataPath} for adding new data`); } -async function addIndexHtmlIfNeeded(dir) { - const indexHtml = path.join(dir, 'index.html'); +async function addIndexHtmlIfNeeded(additionalGitArguments, dir, baseDir) { + const indexHtmlRelativePath = path.join(dir, 'index.html'); + const indexHtmlFullPath = path.join(baseDir, indexHtmlRelativePath); try { - await fs_1.promises.stat(indexHtml); - core.debug(`Skipped to create default index.html since it is already existing: ${indexHtml}`); + await fs_1.promises.stat(indexHtmlFullPath); + core.debug(`Skipped to create default index.html since it is already existing: ${indexHtmlFullPath}`); return; } catch (_) { // Continue } - await fs_1.promises.writeFile(indexHtml, default_index_html_1.DEFAULT_INDEX_HTML, 'utf8'); - await git.cmd('add', indexHtml); - console.log('Created default index.html at', indexHtml); + await fs_1.promises.writeFile(indexHtmlFullPath, default_index_html_1.DEFAULT_INDEX_HTML, 'utf8'); + await git.cmd(additionalGitArguments, 'add', indexHtmlRelativePath); + console.log('Created default index.html at', indexHtmlFullPath); } function biggerIsBetter(tool) { switch (tool) { @@ -73,6 +74,8 @@ function biggerIsBetter(tool) { return false; case 'benchmarkjs': return true; + case 'benchmarkluau': + return false; case 'pytest': return true; case 'googlecpp': @@ -307,27 +310,47 @@ function isRemoteRejectedError(err) { } async function writeBenchmarkToGitHubPagesWithRetry(bench, config, retry) { var _a, _b; - const { name, tool, ghPagesBranch, benchmarkDataDirPath, githubToken, autoPush, skipFetchGhPages, maxItemsInChart, } = config; - const dataPath = path.join(benchmarkDataDirPath, 'data.js'); + const { name, tool, ghPagesBranch, ghRepository, benchmarkDataDirPath, githubToken, autoPush, skipFetchGhPages, maxItemsInChart, } = config; // FIXME: This payload is not available on `schedule:` or `workflow_dispatch:` events. const isPrivateRepo = (_b = (_a = github.context.payload.repository) === null || _a === void 0 ? void 0 : _a.private) !== null && _b !== void 0 ? _b : false; - if (!skipFetchGhPages && (!isPrivateRepo || githubToken)) { + let benchmarkBaseDir = './'; + let extraGitArguments = []; + if (githubToken && !skipFetchGhPages && ghRepository) { + benchmarkBaseDir = './benchmark-data-repository'; + await git.clone(githubToken, ghRepository, benchmarkBaseDir); + extraGitArguments = [`--work-tree=${benchmarkBaseDir}`, `--git-dir=${benchmarkBaseDir}/.git`]; + await git.checkout(ghPagesBranch, extraGitArguments); + } + else if (!skipFetchGhPages && (!isPrivateRepo || githubToken)) { await git.pull(githubToken, ghPagesBranch); } else if (isPrivateRepo && !skipFetchGhPages) { core.warning("'git pull' was skipped. If you want to ensure GitHub Pages branch is up-to-date " + "before generating a commit, please set 'github-token' input to pull GitHub pages branch"); } - await io.mkdirP(benchmarkDataDirPath); + else { + console.warn('NOTHING EXECUTED:', { + skipFetchGhPages, + ghRepository, + isPrivateRepo, + githubToken: !!githubToken, + }); + } + // `benchmarkDataDirPath` is an absolute path at this stage, + // so we need to convert it to relative to be able to prepend the `benchmarkBaseDir` + const benchmarkDataRelativeDirPath = path.relative(process.cwd(), benchmarkDataDirPath); + const benchmarkDataDirFullPath = path.join(benchmarkBaseDir, benchmarkDataRelativeDirPath); + const dataPath = path.join(benchmarkDataDirFullPath, 'data.js'); + await io.mkdirP(benchmarkDataDirFullPath); const data = await loadDataJs(dataPath); const prevBench = addBenchmarkToDataJson(name, bench, data, maxItemsInChart); await storeDataJs(dataPath, data); - await git.cmd('add', dataPath); - await addIndexHtmlIfNeeded(benchmarkDataDirPath); - await git.cmd('commit', '-m', `add ${name} (${tool}) benchmark result for ${bench.commit.id}`); + await git.cmd(extraGitArguments, 'add', path.join(benchmarkDataRelativeDirPath, 'data.js')); + await addIndexHtmlIfNeeded(extraGitArguments, benchmarkDataRelativeDirPath, benchmarkBaseDir); + await git.cmd(extraGitArguments, 'commit', '-m', `add ${name} (${tool}) benchmark result for ${bench.commit.id}`); if (githubToken && autoPush) { try { - await git.push(githubToken, ghPagesBranch); + await git.push(githubToken, ghRepository, ghPagesBranch, extraGitArguments); console.log(`Automatically pushed the generated commit to ${ghPagesBranch} branch since 'auto-push' is set to true`); } catch (err) { @@ -338,7 +361,7 @@ async function writeBenchmarkToGitHubPagesWithRetry(bench, config, retry) { core.warning(`Auto-push failed because the remote ${ghPagesBranch} was updated after git pull`); if (retry > 0) { core.debug('Rollback the auto-generated commit before retry'); - await git.cmd('reset', '--hard', 'HEAD~1'); + await git.cmd(extraGitArguments, 'reset', '--hard', 'HEAD~1'); core.warning(`Retrying to generate a commit and push to remote ${ghPagesBranch} with retry count ${retry}...`); return await writeBenchmarkToGitHubPagesWithRetry(bench, config, retry - 1); // Recursively retry } @@ -354,17 +377,21 @@ async function writeBenchmarkToGitHubPagesWithRetry(bench, config, retry) { return prevBench; } async function writeBenchmarkToGitHubPages(bench, config) { - const { ghPagesBranch, skipFetchGhPages, githubToken } = config; - if (!skipFetchGhPages) { - await git.fetch(githubToken, ghPagesBranch); + const { ghPagesBranch, skipFetchGhPages, ghRepository, githubToken } = config; + if (!ghRepository) { + if (!skipFetchGhPages) { + await git.fetch(githubToken, ghPagesBranch); + } + await git.cmd([], 'switch', ghPagesBranch); } - await git.cmd('switch', ghPagesBranch); try { return await writeBenchmarkToGitHubPagesWithRetry(bench, config, 10); } finally { - // `git switch` does not work for backing to detached head - await git.cmd('checkout', '-'); + if (!ghRepository) { + // `git switch` does not work for backing to detached head + await git.cmd([], 'checkout', '-'); + } } } async function loadDataJson(jsonPath) { diff --git a/package-lock.json b/package-lock.json index 53119d8ad..2abc0d368 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6192,12 +6192,6 @@ "node": ">=6" } }, - "node_modules/json5/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6446,6 +6440,12 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -12609,14 +12609,6 @@ "dev": true, "requires": { "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } } }, "kleur": { @@ -12823,6 +12815,12 @@ "brace-expansion": "^1.1.7" } }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",