diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..222ca62 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +*.yml +flow-typed diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..94e612e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "tabWidth": 4, + "printWidth": 100, + "trailingComma": "all", + "bracketSpacing": false, + "singleQuote": true, + "arrowParens": "avoid" +} diff --git a/__test__/get-base-ref.test.js b/__test__/get-base-ref.test.js index 4ef954c..ddb8349 100644 --- a/__test__/get-base-ref.test.js +++ b/__test__/get-base-ref.test.js @@ -1,23 +1,23 @@ // @-flow -const { validateBaseRef } = require("../get-base-ref"); +const {validateBaseRef} = require('../get-base-ref'); -describe("validateBaseRef", () => { - it("should accept HEAD", () => { - expect(validateBaseRef("HEAD")).toEqual("HEAD"); - }); - it("should translate a remote branch", () => { - expect(validateBaseRef("an-example-remote-branch")).toEqual( - `refs/remotes/origin/an-example-remote-branch` - ); - }); - - const { GITHUB_BASE_REF } = process.env; - if (GITHUB_BASE_REF) { - // Skip this test, because the remote environment isn't finding local `master` - } else { - it("should accept a local branch", () => { - expect(validateBaseRef("master")).toEqual("master"); +describe('validateBaseRef', () => { + it('should accept HEAD', () => { + expect(validateBaseRef('HEAD')).toEqual('HEAD'); + }); + it('should translate a remote branch', () => { + expect(validateBaseRef('an-example-remote-branch')).toEqual( + `refs/remotes/origin/an-example-remote-branch`, + ); }); - } + + const {GITHUB_BASE_REF} = process.env; + if (GITHUB_BASE_REF) { + // Skip this test, because the remote environment isn't finding local `master` + } else { + it('should accept a local branch', () => { + expect(validateBaseRef('master')).toEqual('master'); + }); + } }); diff --git a/exec-prom.js b/exec-prom.js index 7b104d7..5346d4c 100644 --- a/exec-prom.js +++ b/exec-prom.js @@ -2,35 +2,35 @@ /** * A simple promisified version of child_process.exec, so we can `await` it */ -const { spawn } = require("child_process"); +const {spawn} = require('child_process'); const execProm = ( - command /*: string*/, - { rejectOnError, ...options } /*: {rejectOnError: boolean} & mixed */ = {} + command /*: string*/, + {rejectOnError, ...options} /*: {rejectOnError: boolean} & mixed */ = {}, ) /*: Promise<{err: ?number, stdout: string, stderr: string}>*/ => - new Promise((res, rej) => { - const proc = spawn( - command, - // $FlowFixMe - { ...options, shell: true } - ); - let stdout = ""; - let stderr = ""; - proc.stdout.setEncoding("utf8"); - proc.stderr.setEncoding("utf8"); - proc.stdout.on("data", (data /*: string*/) => (stdout += data)); - proc.stderr.on("data", (data /*: string*/) => (stderr += data)); - proc.on("close", (code /*: number*/) => { - if (code !== 0 && rejectOnError) { - rej(new Error(`Exited with non-zero error code: ${code}`)); - } else { - res({ - err: code === 0 ? null : code, - stdout, - stderr, + new Promise((res, rej) => { + const proc = spawn( + command, + // $FlowFixMe + {...options, shell: true}, + ); + let stdout = ''; + let stderr = ''; + proc.stdout.setEncoding('utf8'); + proc.stderr.setEncoding('utf8'); + proc.stdout.on('data', (data /*: string*/) => (stdout += data)); + proc.stderr.on('data', (data /*: string*/) => (stderr += data)); + proc.on('close', (code /*: number*/) => { + if (code !== 0 && rejectOnError) { + rej(new Error(`Exited with non-zero error code: ${code}`)); + } else { + res({ + err: code === 0 ? null : code, + stdout, + stderr, + }); + } }); - } }); - }); module.exports = execProm; diff --git a/get-base-ref.js b/get-base-ref.js index acf3cca..fa0c1cc 100644 --- a/get-base-ref.js +++ b/get-base-ref.js @@ -13,94 +13,94 @@ * TODO(jared): Consider using the github pull-request API (if we're online) * to determine the base branch. */ -const { execSync, spawnSync } = require("child_process"); +const {execSync, spawnSync} = require('child_process'); -const checkRef = (ref) => spawnSync("git", ["rev-parse", ref]).status === 0; +const checkRef = ref => spawnSync('git', ['rev-parse', ref]).status === 0; -const validateBaseRef = (baseRef /*:string*/) => { - // It's locally accessible! - if (checkRef(baseRef)) { - return baseRef; - } - // If it's not locally accessible, then it's probably a remote branch - const remote = `refs/remotes/origin/${baseRef}`; - if (checkRef(remote)) { - return remote; - } +const validateBaseRef = (baseRef /*:string*/) /*: string | null */ => { + // It's locally accessible! + if (checkRef(baseRef)) { + return baseRef; + } + // If it's not locally accessible, then it's probably a remote branch + const remote = `refs/remotes/origin/${baseRef}`; + if (checkRef(remote)) { + return remote; + } - // Otherwise return null - no valid ref provided - return null; + // Otherwise return null - no valid ref provided + return null; }; -const getBaseRef = (head /*:string*/ = "HEAD") => { - const { GITHUB_BASE_REF } = process.env; - if (GITHUB_BASE_REF) { - return validateBaseRef(GITHUB_BASE_REF); - } else { - let upstream = execSync(`git rev-parse --abbrev-ref '${head}@{upstream}'`, { - encoding: "utf8", - }); - upstream = upstream.trim(); +const getBaseRef = (head /*:string*/ = 'HEAD') /*: string | null */ => { + const {GITHUB_BASE_REF} = process.env; + if (GITHUB_BASE_REF) { + return validateBaseRef(GITHUB_BASE_REF); + } else { + let upstream = execSync(`git rev-parse --abbrev-ref '${head}@{upstream}'`, { + encoding: 'utf8', + }); + upstream = upstream.trim(); - // if upstream is local and not empty, use that. - if (upstream && !upstream.trim().startsWith("origin/")) { - return `refs/heads/${upstream}`; - } - let headRef = execSync(`git rev-parse --abbrev-ref ${head}`, { - encoding: "utf8", - }); - headRef = headRef.trim(); - for (let i = 1; i < 100; i++) { - try { - const stdout = execSync( - `git branch --contains ${head}~${i} --format='%(refname)'`, - { encoding: "utf8" } - ); - let lines = stdout.split("\n").filter(Boolean); - lines = lines.filter((line) => line !== `refs/heads/${headRef}`); + // if upstream is local and not empty, use that. + if (upstream && !upstream.trim().startsWith('origin/')) { + return `refs/heads/${upstream}`; + } + let headRef = execSync(`git rev-parse --abbrev-ref ${head}`, { + encoding: 'utf8', + }); + headRef = headRef.trim(); + for (let i = 1; i < 100; i++) { + try { + const stdout = execSync( + `git branch --contains ${head}~${i} --format='%(refname)'`, + {encoding: 'utf8'}, + ); + let lines = stdout.split('\n').filter(Boolean); + lines = lines.filter(line => line !== `refs/heads/${headRef}`); - // Note (Lilli): When running our actions locally, we want to be a little more - // aggressive in choosing a baseRef, going back to a shared commit on only `develop`, - // `master`, feature or release branches, so that we can cover more commits. In case, - // say, I create a bunch of experimental, first-attempt, throw-away branches that - // share commits higher in my stack... - for (const line of lines) { - if ( - line === "refs/heads/develop" || - line === "refs/heads/master" || - line.startsWith("refs/heads/feature/") || - line.startsWith("refs/heads/release/") - ) { - return line; - } + // Note (Lilli): When running our actions locally, we want to be a little more + // aggressive in choosing a baseRef, going back to a shared commit on only `develop`, + // `master`, feature or release branches, so that we can cover more commits. In case, + // say, I create a bunch of experimental, first-attempt, throw-away branches that + // share commits higher in my stack... + for (const line of lines) { + if ( + line === 'refs/heads/develop' || + line === 'refs/heads/master' || + line.startsWith('refs/heads/feature/') || + line.startsWith('refs/heads/release/') + ) { + return line; + } + } + } catch { + // Ran out of history, probably + return null; + } } - } catch { - // Ran out of history, probably + // We couldn't find it return null; - } } - // We couldn't find it - return null; - } }; // Multiple action microservices might encounter this, so give them a canned message to print. // Logging from inside this lib didn't seem to make it to the GitHub Actions console, so I'll // just pass the string back for them to log. const cannedGithubErrorMessage = () /*:string*/ => { - const { GITHUB_BASE_REF } = process.env; + const {GITHUB_BASE_REF} = process.env; - return GITHUB_BASE_REF - ? `No valid base ref given. Found \`${GITHUB_BASE_REF}\`, but \`${GITHUB_BASE_REF}\` does not ` + - `appear to be a valid branch. Perhaps this is coming from a GitHub pull-request that ` + - `you reparented, and the old parent no longer exists. This is a bug on GitHub; unless ` + - `you push a new commit, the old base ref won't update. You can try solving this by: \n` + - `\t1. Merging the new base branch into your pull-request and re-running your checks.\n` + - `\t2. Rebasing your pull-request branch onto the new base branch and re-running your checks.\n` + - `\t3. Creating and pushing an empty commit (e.g., \`$ git commit --allow-empty -m ` + - `'Trigger checks' && git push\`).` - : `No valid base ref given. The envar \`GITHUB_BASE_REF\` was null and no other base ref could ` + - `be determined.`; + return GITHUB_BASE_REF + ? `No valid base ref given. Found \`${GITHUB_BASE_REF}\`, but \`${GITHUB_BASE_REF}\` does not ` + + `appear to be a valid branch. Perhaps this is coming from a GitHub pull-request that ` + + `you reparented, and the old parent no longer exists. This is a bug on GitHub; unless ` + + `you push a new commit, the old base ref won't update. You can try solving this by: \n` + + `\t1. Merging the new base branch into your pull-request and re-running your checks.\n` + + `\t2. Rebasing your pull-request branch onto the new base branch and re-running your checks.\n` + + `\t3. Creating and pushing an empty commit (e.g., \`$ git commit --allow-empty -m ` + + `'Trigger checks' && git push\`).` + : `No valid base ref given. The envar \`GITHUB_BASE_REF\` was null and no other base ref could ` + + `be determined.`; }; module.exports = getBaseRef; diff --git a/git-changed-files.js b/git-changed-files.js index 7e4816a..2e47b81 100644 --- a/git-changed-files.js +++ b/git-changed-files.js @@ -2,12 +2,13 @@ const execProm = require('./exec-prom'); const path = require('path'); const fs = require('fs'); -const minimatch = require('minimatch'); +const minimatch = require('minimatch'); // flow-uncovered-line -const getIgnoredPatterns = (fileContents) => { +// ok +const getIgnoredPatterns = (fileContents /*: string*/) => { return fileContents .split('\n') - .map((line) => { + .map(line => { if (line.startsWith('#')) { return null; } @@ -18,10 +19,7 @@ const getIgnoredPatterns = (fileContents) => { return null; } const [pattern, ...attributes] = line.trim().split(' '); - if ( - attributes.includes('binary') || - attributes.includes('linguist-generated=true') - ) { + if (attributes.includes('binary') || attributes.includes('linguist-generated=true')) { return pattern; } return null; @@ -29,8 +27,8 @@ const getIgnoredPatterns = (fileContents) => { .filter(Boolean); }; -const ignoredPatternsByDirectory = {}; -const isFileIgnored = (workingDirectory, file) => { +const ignoredPatternsByDirectory /*: {[key: string]: Array}*/ = {}; +const isFileIgnored = (workingDirectory /*: string*/, file /*: string*/) => { // If it's outside of the "working directory", we ignore it if (!file.startsWith(workingDirectory)) { return true; @@ -49,6 +47,7 @@ const isFileIgnored = (workingDirectory, file) => { } } for (const pattern of ignoredPatternsByDirectory[dir]) { + // flow-next-uncovered-line if (minimatch(name, pattern)) { return true; } @@ -65,10 +64,7 @@ const isFileIgnored = (workingDirectory, file) => { * It also respects '.gitattributes', filtering out files that have been marked * as "binary" or "linguist-generated=true". */ -const gitChangedFiles = async ( - base /*:string*/, - cwd /*:string*/, -) /*: Promise>*/ => { +const gitChangedFiles = async (base /*:string*/, cwd /*:string*/) /*: Promise>*/ => { cwd = path.resolve(cwd); // Github actions jobs can run the following steps to get a fully accurate @@ -88,22 +84,23 @@ const gitChangedFiles = async ( // ALL_CHANGED_FILES: '${{ steps.changed.outputs.added_modified }}' // if (process.env.ALL_CHANGED_FILES) { - const files = JSON.parse(process.env.ALL_CHANGED_FILES); - return files.filter((path) => !isFileIgnored(cwd, path)); + const files /*: Array */ = JSON.parse(process.env.ALL_CHANGED_FILES); // flow-uncovered-line + return files.filter(path => !isFileIgnored(cwd, path)); } - const { stdout } = await execProm( - `git diff --name-only ${base} --relative`, - { cwd, encoding: 'utf8', rejectOnError: true }, - ); + const {stdout} = await execProm(`git diff --name-only ${base} --relative`, { + cwd, + encoding: 'utf8', + rejectOnError: true, + }); return ( stdout .split('\n') .filter(Boolean) - .map((name) => path.join(cwd, name)) + .map(name => path.join(cwd, name)) // Filter out paths that were deleted - .filter((path) => fs.existsSync(path)) - .filter((path) => !isFileIgnored(cwd, path)) + .filter((path /*: string*/) => fs.existsSync(path)) + .filter((path /*: string*/) => !isFileIgnored(cwd, path)) ); }; diff --git a/list-changed-files.js b/list-changed-files.js index e5e15b9..380b56b 100755 --- a/list-changed-files.js +++ b/list-changed-files.js @@ -7,21 +7,21 @@ */ // $FlowFixMe: shhhhh -require("@babel/register"); // flow-uncovered-line +require('@babel/register'); // flow-uncovered-line -const getBaseRef = require("./get-base-ref"); -const gitChangedFiles = require("./git-changed-files"); +const getBaseRef = require('./get-base-ref'); +const gitChangedFiles = require('./git-changed-files'); const run = async () => { - const baseRef = await getBaseRef(); + const baseRef = await getBaseRef(); - // Note (Lilli): If baseRef is null for any reason, use `master` as opposed to failing the check silently - const files = await gitChangedFiles(baseRef ? baseRef : "master", "."); - files.forEach((file) => console.log(file)); + // Note (Lilli): If baseRef is null for any reason, use `master` as opposed to failing the check silently + const files = await gitChangedFiles(baseRef ? baseRef : 'master', '.'); + files.forEach(file => console.log(file)); }; // flow-next-uncovered-line -run().catch((err) => { - console.error(err); // flow-uncovered-line - process.exit(1); +run().catch(err => { + console.error(err); // flow-uncovered-line + process.exit(1); }); diff --git a/send-report.js b/send-report.js index cb5dbff..4c1cabf 100644 --- a/send-report.js +++ b/send-report.js @@ -11,7 +11,7 @@ * the GITHUB_TOKEN env variable. */ -const { GITHUB_TOKEN, GITHUB_WORKSPACE } = process.env; +const {GITHUB_TOKEN, GITHUB_WORKSPACE} = process.env; const fs = require('fs'); const path = require('path'); const chalk = require('chalk'); @@ -39,7 +39,7 @@ const localReport = async (title /*:string*/, messages /*:Array*/) => { console.log(chalk.yellow(`[[ ${title} ]]`)); console.log(); const fileCache /*: {[key: string]: Array}*/ = {}; - const getFile = (filePath) => { + const getFile = filePath => { if (!fileCache[filePath]) { const ext = path.extname(filePath).slice(1); fileCache[filePath] = highlight(fs.readFileSync(filePath, 'utf8'), { @@ -50,7 +50,7 @@ const localReport = async (title /*:string*/, messages /*:Array*/) => { return fileCache[filePath]; }; const byFile /*:{[key: string]: number}*/ = {}; - messages.forEach((message) => { + messages.forEach(message => { const lines = getFile(message.path); const lineStart = Math.max(message.start.line - 3, 0); const indexStart = lineStart + 1; @@ -62,9 +62,7 @@ const localReport = async (title /*:string*/, messages /*:Array*/) => { } console.error( ':error:', - chalk.cyan( - `${message.path}:${message.start.line}:${message.start.column}`, - ), + chalk.cyan(`${message.path}:${message.start.line}:${message.start.column}`), ); console.error(message.message); console.error( @@ -116,8 +114,8 @@ const githubReport = async ( messages /*: Array*/, ) => { /* flow-uncovered-block */ - const { GitHub, context } = require('@actions/github'); - const { owner, repo } /*: {owner: string, repo: string}*/ = context.repo; + const {GitHub, context} = require('@actions/github'); + const {owner, repo} /*: {owner: string, repo: string}*/ = context.repo; const client = new GitHub(token, {}); const headSha = context.payload.pull_request.head.sha; const check = await client.checks.create({ @@ -144,7 +142,7 @@ const githubReport = async ( } /* end flow-uncovered-block */ - const annotations = messages.map((message) => ({ + const annotations = messages.map(message => ({ path: removeWorkspace(message.path), start_line: message.start.line, end_line: message.end.line, @@ -153,7 +151,7 @@ const githubReport = async ( })); let errorCount = 0; let warningCount = 0; - messages.forEach((message) => { + messages.forEach(message => { if (message.annotationLevel === 'failure') { errorCount += 1; } else { @@ -184,7 +182,7 @@ const githubReport = async ( } }; -const makeReport = (title /*: string*/, messages /*: Array*/) => { +const makeReport = (title /*: string*/, messages /*: Array*/) /*: Promise */ => { if (GITHUB_TOKEN) { return githubReport(title, GITHUB_TOKEN, messages); } else { diff --git a/yarn.lock b/yarn.lock index ed7a3bd..38c0c0e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -783,8 +783,8 @@ acorn@^7.1.1: integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== "actions-utils@git+https://git@github.com/Khan/actions-utils#v1.2.5": - version "1.2.4" - resolved "git+https://git@github.com/Khan/actions-utils#025b6d5658291e3c73fbedfda6f83a17f48ae7db" + version "1.2.5" + resolved "git+https://git@github.com/Khan/actions-utils#6d8556b72761a15ac97ab6d811acd53175bc36b2" dependencies: "@actions/github" "^2.1.1" chalk "^2.4.2"