diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000000..190d4c8e5805 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 + +updates: + # Enable version updates for npm + - package-ecosystem: "npm" + # Look for `package.json` and `lock` files in the `root` directory + directory: "/" + # Check the npm registry for updates every day (weekdays) + schedule: + interval: "weekly" + day: "monday" + labels: + - "dependabot" + - "dependencies" + + # Do we need to declare each package.json :scream: ? + # - package-ecosystem: "npm" + # directory: "apps/ledger-live-desktop" + # schedule: + # interval: "weekly" + # day: "monday" + # labels: + # - "dependabot" + # - "dependencies" diff --git a/apps/ledger-live-desktop/.dependabot/config.yml b/apps/ledger-live-desktop/.dependabot/config.yml deleted file mode 100644 index af2950dcbaa1..000000000000 --- a/apps/ledger-live-desktop/.dependabot/config.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: 1 -update_configs: - - package_manager: 'javascript' - directory: '/' - update_schedule: 'weekly' - target_branch: 'master' # TODO: change to develop - - default_labels: - - 'dependabot' - - 'dependencies' diff --git a/tools/github-bot/.dockerignore b/tools/github-bot/.dockerignore deleted file mode 100644 index c961acb50598..000000000000 --- a/tools/github-bot/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -**/node_modules/ -**/.git -**/README.md -**/LICENSE -**/.vscode -**/npm-debug.log -**/coverage -**/.env -**/.editorconfig -**/dist -**/*.pem -Dockerfile diff --git a/tools/github-bot/Dockerfile b/tools/github-bot/Dockerfile deleted file mode 100644 index df02fe4831b2..000000000000 --- a/tools/github-bot/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM node:12-slim -WORKDIR /usr/src/app -COPY package.json package-lock.json ./ -RUN npm ci --production -RUN npm cache clean --force -ENV NODE_ENV="production" -COPY . . -CMD [ "npm", "start" ] diff --git a/tools/github-bot/src/commands/generate-screenshots.ts b/tools/github-bot/src/commands/generate-screenshots.ts index 2d01144a7f7d..2dc4cc49c14a 100644 --- a/tools/github-bot/src/commands/generate-screenshots.ts +++ b/tools/github-bot/src/commands/generate-screenshots.ts @@ -60,7 +60,7 @@ export function generateScreenshots(app: Probot) { }); }); - app.on("check_run.requested_action", async (context) => { + app.on("check_run.requested_action", async context => { const { payload } = context; if (payload.requested_action.identifier !== ACTION_ID) return; @@ -83,7 +83,7 @@ export function generateScreenshots(app: Probot) { description: "Regenerates playwright screenshots for the Live Desktop app and commit the changes.", summaryFile: "summary.json", - getInputs: (payload) => ({ + getInputs: payload => ({ ref: payload.check_run.head_sha, login: payload.sender.login, }), diff --git a/tools/github-bot/src/commands/regen-doc.ts b/tools/github-bot/src/commands/regen-doc.ts index 96712532276d..f0ec58c6b4e9 100644 --- a/tools/github-bot/src/commands/regen-doc.ts +++ b/tools/github-bot/src/commands/regen-doc.ts @@ -61,7 +61,7 @@ export function regenDoc(app: Probot) { }); }); - app.on("check_run.requested_action", async (context) => { + app.on("check_run.requested_action", async context => { const { payload } = context; if (payload.requested_action.identifier !== ACTION_ID) return; @@ -83,7 +83,7 @@ export function regenDoc(app: Probot) { checkRunName: "@Libs • Regen Doc Files", description: "Regenerates documentation files for LedgerJS libraries", summaryFile: "summary.json", - getInputs: (payload) => ({ + getInputs: payload => ({ ref: payload.check_run.head_sha, login: payload.sender.login, }), diff --git a/tools/github-bot/src/commands/regen-pods.ts b/tools/github-bot/src/commands/regen-pods.ts index 8ffbb7985414..e55ed55a3270 100644 --- a/tools/github-bot/src/commands/regen-pods.ts +++ b/tools/github-bot/src/commands/regen-pods.ts @@ -61,7 +61,7 @@ export function regenPods(app: Probot) { }); }); - app.on("check_run.requested_action", async (context) => { + app.on("check_run.requested_action", async context => { const { payload } = context; if (payload.requested_action.identifier !== ACTION_ID) return; @@ -83,7 +83,7 @@ export function regenPods(app: Probot) { checkRunName: "@Mobile • Regen Pods", description: "Regenerates iOS podlock file", summaryFile: "summary.json", - getInputs: (payload) => ({ + getInputs: payload => ({ ref: payload.check_run.head_sha, login: payload.sender.login, }), diff --git a/tools/github-bot/src/commands/tools.ts b/tools/github-bot/src/commands/tools.ts index 44ebd15c6f85..a2126899699f 100644 --- a/tools/github-bot/src/commands/tools.ts +++ b/tools/github-bot/src/commands/tools.ts @@ -15,12 +15,12 @@ export const commands = ( name: string; arguments?: string; commentId?: number; - } - ) => {} + }, + ) => Promise, ) => { const matcher = /^\/([\w-]+)\b *(.*)?$/m; - app.on(["issue_comment.created" /*, "issues.opened" */], async (context) => { + app.on(["issue_comment.created"], async context => { if (context.isBot) return; const { payload, octokit } = context; diff --git a/tools/github-bot/src/features/autoClose/index.ts b/tools/github-bot/src/features/autoClose/index.ts index 94ea9527539c..a1aaec8c0bf6 100644 --- a/tools/github-bot/src/features/autoClose/index.ts +++ b/tools/github-bot/src/features/autoClose/index.ts @@ -7,7 +7,7 @@ import { isValidBody, isValidBranchName, isValidUser } from "./tools"; * @param app The Probot application. */ export function autoClose(app: Probot) { - app.on(["pull_request.opened", "pull_request.reopened"], async (context) => { + app.on(["pull_request.opened", "pull_request.reopened"], async context => { const { payload, octokit } = context; const repository = context.repo(); @@ -19,11 +19,10 @@ export function autoClose(app: Probot) { if ( !isValidUser(login) && // Close automatic PRs from smartling - except the ones triggered manually - !/^(smartling-content-updated|smartling-translation-completed)-.+/.test( - branch - ) - ) + !/^(smartling-content-updated|smartling-translation-completed)-.+/.test(branch) + ) { return; + } const isBranchValid = isValidBranchName(branch); const isBodyValid = isValidBody(payload.pull_request.body); @@ -40,8 +39,6 @@ export function autoClose(app: Probot) { "\n" + "Found Issues:\n"; - let comment; - if (!isBranchValid) { body += `- _the branch name \`${branch}\` is invalid_\n`; } @@ -52,7 +49,8 @@ export function autoClose(app: Probot) { body += "_💡 make sure you added comments only inside the template sections - and not above the `📝 Description` heading_\n"; } - comment = context.issue({ + + const comment = context.issue({ body, }); diff --git a/tools/github-bot/src/features/autoClose/tools.ts b/tools/github-bot/src/features/autoClose/tools.ts index f285ba5d972f..7930c6dea43f 100644 --- a/tools/github-bot/src/features/autoClose/tools.ts +++ b/tools/github-bot/src/features/autoClose/tools.ts @@ -4,12 +4,7 @@ export const isValidBranchName = (branch: string): boolean => ["release", "hotfix"].includes(branch); export const isValidUser = (user: string): boolean => - ![ - "ledgerlive", - "live-github-bot[bot]", - "github-actions[bot]", - "dependabot[bot]", - ].includes(user); + !["ledgerlive", "live-github-bot[bot]", "github-actions[bot]", "dependabot[bot]"].includes(user); export const isValidBody = (body: string | null): boolean => { if (!body) return false; @@ -34,9 +29,7 @@ export const isValidBody = (body: string | null): boolean => { }; } - const headingIndex = requiredHeadings.findIndex((heading) => - line.startsWith(heading) - ); + const headingIndex = requiredHeadings.findIndex(heading => line.startsWith(heading)); // Template required heading is still in the body. if (headingIndex > -1) { acc.matchHeadings[headingIndex] = true; @@ -48,7 +41,7 @@ export const isValidBody = (body: string | null): boolean => { { dummyDescription: false, matchHeadings: requiredHeadings.map(() => false), - } + }, ); return !results.dummyDescription && results.matchHeadings.every(Boolean); diff --git a/tools/github-bot/src/features/lintCommits/index.ts b/tools/github-bot/src/features/lintCommits/index.ts index 48795836ad0e..01eb7e4581dc 100644 --- a/tools/github-bot/src/features/lintCommits/index.ts +++ b/tools/github-bot/src/features/lintCommits/index.ts @@ -17,7 +17,7 @@ export function lintCommits(app: Probot) { // …even though the docs say that it's supposed to be .synchronize, it's not. "pull_request.edited", ], - async (context) => { + async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); const login = payload.sender.login; @@ -35,7 +35,7 @@ export function lintCommits(app: Probot) { login: login, }, }); - } + }, ); monitorWorkflow(app, { @@ -44,7 +44,7 @@ export function lintCommits(app: Probot) { description: "Lint the Pull Request commit messages according to the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.", summaryFile: "summary.json", - getInputs: (payload) => ({ + getInputs: payload => ({ ref: payload.check_run.head_sha, from: payload.check_run.pull_requests[0]?.base.ref, login: payload.sender.login, diff --git a/tools/github-bot/src/features/orchestrator/const.ts b/tools/github-bot/src/features/orchestrator/const.ts index 3404631ca9ed..816fe3b2dbd4 100644 --- a/tools/github-bot/src/features/orchestrator/const.ts +++ b/tools/github-bot/src/features/orchestrator/const.ts @@ -13,9 +13,7 @@ export type PullRequestMetadata = { base_owner: string; is_fork: boolean; }; -export type CheckSuite = Awaited< - ReturnType ->["data"]; +export type CheckSuite = Awaited>["data"]; export const SYNC_ACTION = "sync_action"; export const REPO_OWNER = "LedgerHQ"; @@ -30,19 +28,14 @@ export enum RUNNERS { const commonGetInputs = ( payload: GetInputsPayload, metadata?: PullRequestMetadata, - localRef?: string + localRef?: string, ) => { return "workflow_run" in payload ? { login: payload.workflow_run.actor.login, - ref: - localRef ?? - metadata?.head_branch ?? - payload.workflow_run.pull_requests[0]?.head.ref, + ref: localRef ?? metadata?.head_branch ?? payload.workflow_run.pull_requests[0]?.head.ref, base_ref: - metadata?.base_branch || - payload.workflow_run.pull_requests[0]?.base.ref || - "develop", + metadata?.base_branch || payload.workflow_run.pull_requests[0]?.base.ref || "develop", } : { login: payload.sender.login, @@ -63,7 +56,7 @@ export const WORKFLOWS = { payload: GetInputsPayload, metadata?: PullRequestMetadata, localRef?: string, - draft?: boolean + draft?: boolean, ) => { const common = commonGetInputs(payload, metadata, localRef); return { ...common, draft }; @@ -81,7 +74,7 @@ export const WORKFLOWS = { payload: GetInputsPayload, metadata?: PullRequestMetadata, localRef?: string, - draft?: boolean + draft?: boolean, ) => { const common = commonGetInputs(payload, metadata, localRef); return { ...common, draft }; @@ -99,7 +92,7 @@ export const WORKFLOWS = { payload: GetInputsPayload, metadata?: PullRequestMetadata, localRef?: string, - draft?: boolean + draft?: boolean, ) => { const common = commonGetInputs(payload, metadata, localRef); return { ...common, draft }; @@ -117,7 +110,7 @@ export const WORKFLOWS = { payload: GetInputsPayload, metadata?: PullRequestMetadata, localRef?: string, - draft?: boolean + draft?: boolean, ) => { const common = commonGetInputs(payload, metadata, localRef); return { ...common, draft }; @@ -125,8 +118,7 @@ export const WORKFLOWS = { }, "build-mobile.yml": { checkRunName: "@Mobile • Build App", - description: - "Build the Ledger Live Mobile application and attach the apk to the workflow run.", + description: "Build the Ledger Live Mobile application and attach the apk to the workflow run.", runsOn: RUNNERS.internal, required: true, affected: ["live-mobile"], @@ -135,8 +127,7 @@ export const WORKFLOWS = { }, "build-mobile-external.yml": { checkRunName: "@Mobile • Build App (external)", - description: - "Build the Ledger Live Mobile application and attach the apk to the workflow run.", + description: "Build the Ledger Live Mobile application and attach the apk to the workflow run.", runsOn: RUNNERS.external, required: true, affected: ["live-mobile"], @@ -169,28 +160,19 @@ export const WORKFLOWS = { required: true, affected: [/^libs\/.*/], summaryFile: "summary.json", - getInputs: ( - payload: GetInputsPayload, - metadata?: PullRequestMetadata, - localRef?: string - ) => { + getInputs: (payload: GetInputsPayload, metadata?: PullRequestMetadata, localRef?: string) => { return "workflow_run" in payload ? { login: payload.workflow_run.actor.login, ref: - localRef ?? - metadata?.head_branch ?? - payload.workflow_run.pull_requests[0]?.head.ref, + localRef ?? metadata?.head_branch ?? payload.workflow_run.pull_requests[0]?.head.ref, since_branch: - metadata?.base_branch || - payload.workflow_run.pull_requests[0]?.base.ref || - "develop", + metadata?.base_branch || payload.workflow_run.pull_requests[0]?.base.ref || "develop", } : { login: payload.sender.login, ref: payload.check_run.pull_requests[0]?.head.ref, - since_branch: - payload.check_run.pull_requests[0]?.base.ref || "develop", + since_branch: payload.check_run.pull_requests[0]?.base.ref || "develop", }; }, }, diff --git a/tools/github-bot/src/features/orchestrator/index.ts b/tools/github-bot/src/features/orchestrator/index.ts index 0392c3975369..5b2f80059bd8 100644 --- a/tools/github-bot/src/features/orchestrator/index.ts +++ b/tools/github-bot/src/features/orchestrator/index.ts @@ -29,76 +29,73 @@ export function orchestrator(app: Probot) { * When a workflow is requested for the first time: * - Create the related check run */ - app.on( - "workflow_run.requested", - async (context): Promise => { - const { payload, octokit } = context; + app.on("workflow_run.requested", async context => { + const { payload, octokit } = context; - const { owner, repo } = context.repo(); - const workflowFile = extractWorkflowFile(payload); - const matchedWorkflow = WORKFLOWS[workflowFile as keyof typeof WORKFLOWS]; + const { owner, repo } = context.repo(); - if (matchedWorkflow) { - context.log.info( - `[Orchestrator](workflow_run.requested) ${payload.workflow_run.name}` - ); - const workflowUrl = payload.workflow_run.html_url; - const summaryPrefix = matchedWorkflow.description - ? `#### ${matchedWorkflow.description}\n\n` - : ""; + if (repo !== "ledger-live") return; - const { data: checkSuite } = await octokit.checks.getSuite({ - owner, - repo, - check_suite_id: payload.workflow_run.check_suite_id, - }); + const workflowFile = extractWorkflowFile(payload); + const matchedWorkflow = WORKFLOWS[workflowFile as keyof typeof WORKFLOWS]; - // Will trigger the check_run.created event (which will update the watcher) - context.log.info( - `[Orchestrator](workflow_run.requested) Creating check run ${matchedWorkflow.checkRunName} @sha ${checkSuite.head_sha}` - ); + if (matchedWorkflow) { + context.log.info(`[Orchestrator](workflow_run.requested) ${payload.workflow_run.name}`); + const workflowUrl = payload.workflow_run.html_url; + const summaryPrefix = matchedWorkflow.description + ? `#### ${matchedWorkflow.description}\n\n` + : ""; - const response = await octokit.checks.create({ - owner, - repo, - name: matchedWorkflow.checkRunName, - head_sha: checkSuite.head_sha, - status: "queued", - started_at: new Date().toISOString(), - output: { - title: "⏱️ Queued", - summary: - summaryPrefix + - `The **[workflow](${workflowUrl})** is currently queued.`, - details_url: workflowUrl, - }, - }); - context.log.info( - `[Orchestrator](workflow_run.requested) Check run created @id ${response.data.id}` - ); - } + const { data: checkSuite } = await octokit.checks.getSuite({ + owner, + repo, + check_suite_id: payload.workflow_run.check_suite_id, + }); + + // Will trigger the check_run.created event (which will update the watcher) + context.log.info( + `[Orchestrator](workflow_run.requested) Creating check run ${matchedWorkflow.checkRunName} @sha ${checkSuite.head_sha}`, + ); + + const response = await octokit.checks.create({ + owner, + repo, + name: matchedWorkflow.checkRunName, + head_sha: checkSuite.head_sha, + status: "queued", + started_at: new Date().toISOString(), + output: { + title: "⏱️ Queued", + summary: summaryPrefix + `The **[workflow](${workflowUrl})** is currently queued.`, + details_url: workflowUrl, + }, + }); + context.log.info( + `[Orchestrator](workflow_run.requested) Check run created @id ${response.data.id}`, + ); } - ); + }); /** * When a workflow is re-run, or the first time it gets in progress: * - Re-create the check run (unfortunately rerequested does not reset the status to "queued") * - Update the fields to reflect the fact that the run is in progress and link the workflow */ - app.on("workflow_run", async (context) => { + app.on("workflow_run", async context => { const { payload, octokit } = context; // @ts-expect-error Expected because probot does not declare this webhook event even though it exists. if (context.payload.action !== "in_progress") return; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflowFile = extractWorkflowFile(payload); const matchedWorkflow = WORKFLOWS[workflowFile as keyof typeof WORKFLOWS]; if (matchedWorkflow) { - context.log.info( - `[Orchestrator](workflow_run.in_progress) ${payload.workflow_run.name}` - ); + context.log.info(`[Orchestrator](workflow_run.in_progress) ${payload.workflow_run.name}`); const workflowUrl = payload.workflow_run.html_url; const summaryPrefix = matchedWorkflow.description ? `#### ${matchedWorkflow.description}\n\n` @@ -113,14 +110,11 @@ export function orchestrator(app: Probot) { run_id: payload.workflow_run.id, }); - const [tips, workflowRun] = await Promise.all([ - tipsPromise, - workflowRunPromise, - ]); + const [tips, workflowRun] = await Promise.all([tipsPromise, workflowRunPromise]); if (workflowRun.data.status !== "in_progress") { context.log.info( - `[Orchestrator](workflow_run.in_progress) The workflow run seems to be completed already, skipping…` + `[Orchestrator](workflow_run.in_progress) The workflow run seems to be completed already, skipping…`, ); // Oops, the workflow is not in progress anymore, we should not update the check run return; @@ -133,7 +127,7 @@ export function orchestrator(app: Probot) { }); context.log.info( - `[Orchestrator](workflow_run.in_progress) Creating check run ${matchedWorkflow.checkRunName} @sha ${checkSuite.head_sha}` + `[Orchestrator](workflow_run.in_progress) Creating check run ${matchedWorkflow.checkRunName} @sha ${checkSuite.head_sha}`, ); // Will trigger the check_run.created event (which will update the watcher) @@ -158,15 +152,13 @@ export function orchestrator(app: Probot) { ], output: { title: "⚙️ Running", - summary: - summaryPrefix + - `The **[workflow](${workflowUrl})** is currently running.`, + summary: summaryPrefix + `The **[workflow](${workflowUrl})** is currently running.`, text: tips, }, }, }); context.log.info( - `[Orchestrator](workflow_run.in_progress) Check run created @id ${response.id}` + `[Orchestrator](workflow_run.in_progress) Check run created @id ${response.id}`, ); } }); @@ -176,74 +168,65 @@ export function orchestrator(app: Probot) { * - If the workflow is the gate, get the affected.json artifact and trigger the related workflows * - Otherwise, update the associated check run with the conclusion */ - app.on("workflow_run.completed", async (context) => { + app.on("workflow_run.completed", async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflowFile = extractWorkflowFile(payload); const matchedWorkflow = WORKFLOWS[workflowFile as keyof typeof WORKFLOWS]; if (workflowFile === "gate.yml") { if (payload.workflow_run.conclusion !== "success") return; - context.log.info( - `[Orchestrator](workflow_run.completed) ${payload.workflow_run.name}` - ); + context.log.info(`[Orchestrator](workflow_run.completed) ${payload.workflow_run.name}`); const artifacts = await listWorkflowRunArtifacts( octokit, owner, repo, - payload.workflow_run.id + payload.workflow_run.id, ); context.log.info( - `[Orchestrator](workflow_run.completed) Linked artifacts ${artifacts.map( - (a) => - JSON.stringify({ - name: a.name, - id: a.id, - created_at: a.created_at, - expires_at: a.expires_at, - }) - )}` + `[Orchestrator](workflow_run.completed) Linked artifacts ${artifacts.map(a => + JSON.stringify({ + name: a.name, + id: a.id, + created_at: a.created_at, + expires_at: a.expires_at, + }), + )}`, ); // Get the affected / metadata artifacts const [affected, metadata] = (await Promise.all( ["affected.json", "pr_metadata.json"].map( async ( - fileName: string - ): Promise< - Record | PullRequestMetadata | undefined - > => { - const artifactId = artifacts.find( - (artifact) => artifact.name === fileName - )?.id; + fileName: string, + ): Promise | PullRequestMetadata | undefined> => { + const artifactId = artifacts.find(artifact => artifact.name === fileName)?.id; context.log.info( - `[Orchestrator](workflow_run.completed) Found artifact ${fileName} @id ${artifactId}` + `[Orchestrator](workflow_run.completed) Found artifact ${fileName} @id ${artifactId}`, ); if (!artifactId) return undefined; try { - const raw = await downloadArtifact( - octokit, - owner, - repo, - artifactId - ); + const raw = await downloadArtifact(octokit, owner, repo, artifactId); return JSON.parse(raw.toString()); } catch (e) { context.log.error( - `[Orchestrator](workflow_run.completed) Error while downloading / parsing artifact: ${fileName} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}` + `[Orchestrator](workflow_run.completed) Error while downloading / parsing artifact: ${fileName} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}`, ); context.log.error(e as Error); return undefined; } - } - ) + }, + ), )) as [Record?, PullRequestMetadata?]; const baseOwner = metadata?.base_owner || REPO_OWNER; @@ -253,11 +236,11 @@ export function orchestrator(app: Probot) { octokit, repo, owner, - metadata?.number || payload.workflow_run.pull_requests[0]?.number + metadata?.number || payload.workflow_run.pull_requests[0]?.number, )); context.log.info( - `[Orchestrator](workflow_run.completed) Is pull requests from a forked repo? ${isFork}` + `[Orchestrator](workflow_run.completed) Is pull requests from a forked repo? ${isFork}`, ); const { data: checkSuite } = await octokit.checks.getSuite({ @@ -267,7 +250,7 @@ export function orchestrator(app: Probot) { }); let affectedWorkflows = 0; - let localRef = `forked/${checkSuite.head_sha}`; + const localRef = `forked/${checkSuite.head_sha}`; if (isFork) { try { @@ -278,11 +261,11 @@ export function orchestrator(app: Probot) { ref: `${REF_PREFIX}/${localRef}`, }); context.log.warn( - `[Orchestrator](workflow_run.completed) created a ref "${data.ref}" on sha "${data.object.sha}" for forked PR (${status})` + `[Orchestrator](workflow_run.completed) created a ref "${data.ref}" on sha "${data.object.sha}" for forked PR (${status})`, ); } catch (error) { context.log.warn( - `[Orchestrator](workflow_run.completed) createRef ${localRef} error: ${error}` + `[Orchestrator](workflow_run.completed) createRef ${localRef} error: ${error}`, ); } } @@ -295,18 +278,17 @@ export function orchestrator(app: Probot) { // Determine if the workflow is affected const isAffected = !affected || - workflow.affected.some((specifier) => { + workflow.affected.some(specifier => { return typeof specifier === "string" ? affected[specifier] : Object.values(affected).some(({ path }: { path: string }) => - specifier.test(path) + specifier.test(path), ); }); if (isAffected) { affectedWorkflows++; const workflowRef = - metadata?.head_branch || - payload.workflow_run.pull_requests[0]?.head.ref; + metadata?.head_branch || payload.workflow_run.pull_requests[0]?.head.ref; let draft = false; if (typeof metadata?.number == "number") { @@ -322,13 +304,13 @@ export function orchestrator(app: Probot) { payload, metadata, isFork ? localRef : undefined, - draft + draft, ); context.log.info( `[Orchestrator](workflow_run.completed) Dispatching workflow ${fileName} on ref ${workflowRef} with inputs ${JSON.stringify( - inputs - )}` + inputs, + )}`, ); // Trigger the associated workflow. @@ -342,10 +324,10 @@ export function orchestrator(app: Probot) { inputs, }); context.log.info( - `[Orchestrator](workflow_run.completed) Dispatched workflow run response @status ${response.status}` + `[Orchestrator](workflow_run.completed) Dispatched workflow run response @status ${response.status}`, ); } - }) + }), ); // Create or recreate the Watcher check run @@ -358,13 +340,13 @@ export function orchestrator(app: Probot) { }); context.log.info( - `[Orchestrator](workflow_run.completed) Created watcher check run @name ${checkRun.name} @id ${checkRun.id}` + `[Orchestrator](workflow_run.completed) Created watcher check run @name ${checkRun.name} @id ${checkRun.id}`, ); // If there are no affected workflows, update the watcher to success. if (checkRun && affectedWorkflows < 1) { context.log.info( - `[Orchestrator](workflow_run.completed) No affected workflows, updating watcher to success` + `[Orchestrator](workflow_run.completed) No affected workflows, updating watcher to success`, ); await octokit.checks.update({ owner: baseOwner, @@ -375,9 +357,7 @@ export function orchestrator(app: Probot) { }); } } else if (matchedWorkflow) { - context.log.info( - `[Orchestrator](workflow_run.completed) ${payload.workflow_run.name}` - ); + context.log.info(`[Orchestrator](workflow_run.completed) ${payload.workflow_run.name}`); const { data: checkSuite } = await octokit.checks.getSuite({ owner, @@ -399,7 +379,7 @@ export function orchestrator(app: Probot) { checkRuns.data.total_count } check runs named ${matchedWorkflow.checkRunName} @sha ${ checkSuite.head_sha - }\n${checkRuns.data.check_runs.map((cr) => cr.id)}` + }\n${checkRuns.data.check_runs.map(cr => cr.id)}`, ); if (checkRuns.data.total_count === 0) { @@ -416,21 +396,16 @@ export function orchestrator(app: Probot) { octokit, owner, repo, - payload.workflow_run.id + payload.workflow_run.id, ); const artifactId = artifacts.find( - (artifact) => artifact.name === matchedWorkflow.summaryFile + artifact => artifact.name === matchedWorkflow.summaryFile, )?.id; if (artifactId) { try { - const rawSummary = await downloadArtifact( - octokit, - owner, - repo, - artifactId - ); + const rawSummary = await downloadArtifact(octokit, owner, repo, artifactId); const newSummary = JSON.parse(rawSummary.toString()); if (newSummary.summary) { summary = newSummary?.summary; @@ -440,7 +415,7 @@ export function orchestrator(app: Probot) { annotations = newSummary?.annotations; } catch (e) { context.log.error( - `[Orchestrator](workflow_run.completed) Error while downloading / parsing artifact: ${matchedWorkflow.summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}` + `[Orchestrator](workflow_run.completed) Error while downloading / parsing artifact: ${matchedWorkflow.summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}`, ); context.log.error(e as Error); } @@ -449,9 +424,7 @@ export function orchestrator(app: Probot) { const summaryPrefix = matchedWorkflow.description ? `#### ${matchedWorkflow.description}${ - !defaultSummary - ? `\n##### [🔗 Workflow run](${payload.workflow_run.html_url})` - : "" + !defaultSummary ? `\n##### [🔗 Workflow run](${payload.workflow_run.html_url})` : "" }\n\n` : ""; summary = summaryPrefix + summary; @@ -461,7 +434,7 @@ export function orchestrator(app: Probot) { const tips = await getTips(workflowFile); context.log.info( - `[Orchestrator](workflow_run.completed) Updating check run: ${checkRun.name} @id ${checkRun.id}` + `[Orchestrator](workflow_run.completed) Updating check run: ${checkRun.name} @id ${checkRun.id}`, ); const updateRes = await octokit.checks.update({ @@ -479,7 +452,7 @@ export function orchestrator(app: Probot) { }); context.log.info( - `[Orchestrator](workflow_run.completed) Check run updated: ${updateRes.data.name} @status ${updateRes.data.status} @conclusion ${updateRes.data.conclusion}` + `[Orchestrator](workflow_run.completed) Check run updated: ${updateRes.data.name} @status ${updateRes.data.status} @conclusion ${updateRes.data.conclusion}`, ); // Batch annotations to avoid hitting the API rate limit. @@ -508,17 +481,20 @@ export function orchestrator(app: Probot) { * - Create the watcher check run * - Update it with the right status based on the other check runs of the suite */ - app.on(["check_run.created"], async (context) => { + app.on(["check_run.created"], async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const matchedWorkflow = Object.values(WORKFLOWS).find( - (w) => w.checkRunName === payload.check_run.name + w => w.checkRunName === payload.check_run.name, ); if (matchedWorkflow) { context.log.info( - `[Orchestrator](check_run.created) ${payload.check_run.name} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}` + `[Orchestrator](check_run.created) ${payload.check_run.name} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}`, ); // (Re)Create watcher check run in pending state const response = await createRunByName({ @@ -529,21 +505,16 @@ export function orchestrator(app: Probot) { checkName: WATCHER_CHECK_RUN_NAME, }); context.log.info( - `[Orchestrator](check_run.created) Created watcher check run @id ${response.id}` - ); - const result = await updateWatcherCheckRun( - octokit, - owner, - repo, - payload.check_run.head_sha + `[Orchestrator](check_run.created) Created watcher check run @id ${response.id}`, ); + const result = await updateWatcherCheckRun(octokit, owner, repo, payload.check_run.head_sha); if (result) { context.log.info( - `[Orchestrator](check_run.created) Synchronized watcher @id ${result.data.id}` + `[Orchestrator](check_run.created) Synchronized watcher @id ${result.data.id}`, ); } else { context.log.error( - `[Orchestrator](check_run.created) Unable to update watcher check run @sha ${payload.check_run.head_sha}` + `[Orchestrator](check_run.created) Unable to update watcher check run @sha ${payload.check_run.head_sha}`, ); } } @@ -553,18 +524,21 @@ export function orchestrator(app: Probot) { * When a check run is rerequested: * - Trigger the related workflow */ - app.on(["check_run.rerequested"], async (context) => { + app.on(["check_run.rerequested"], async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflow = Object.entries(WORKFLOWS).find( - ([, w]) => w.checkRunName === payload.check_run.name + ([, w]) => w.checkRunName === payload.check_run.name, ); if (workflow) { const [workflowName, workflowMeta] = workflow; context.log.info( - `[Orchestrator](check_run.rerequested) ${payload.check_run.name} @workflow ${workflowName} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}` + `[Orchestrator](check_run.rerequested) ${payload.check_run.name} @workflow ${workflowName} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}`, ); octokit.actions.createWorkflowDispatch({ owner, @@ -580,31 +554,29 @@ export function orchestrator(app: Probot) { * When a check run is completed: * - Update the watcher with the right status based on the other check runs of the suite */ - app.on("check_run.completed", async (context) => { + app.on("check_run.completed", async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const matchedWorkflow = Object.values(WORKFLOWS).find( - (w) => w.checkRunName === payload.check_run.name + w => w.checkRunName === payload.check_run.name, ); if (matchedWorkflow) { context.log.info( - `[Orchestrator](check_run.completed) ${payload.check_run.name} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}` - ); - const result = await updateWatcherCheckRun( - octokit, - owner, - repo, - payload.check_run.head_sha + `[Orchestrator](check_run.completed) ${payload.check_run.name} @sha ${payload.check_run.head_sha} @id ${payload.check_run.id} @url ${payload.check_run.html_url}`, ); + const result = await updateWatcherCheckRun(octokit, owner, repo, payload.check_run.head_sha); if (result) { context.log.info( - `[Orchestrator](check_run.completed) Synchronized watcher @id ${result.data.id}` + `[Orchestrator](check_run.completed) Synchronized watcher @id ${result.data.id}`, ); } else { context.log.error( - `[Orchestrator](check_run.completed) Unable to update watcher check run @sha ${payload.check_run.head_sha}` + `[Orchestrator](check_run.completed) Unable to update watcher check run @sha ${payload.check_run.head_sha}`, ); } } @@ -614,13 +586,13 @@ export function orchestrator(app: Probot) { * When a check run sync action is sent: * - Update the check run and the watcher with the right status based on the other check runs of the suite */ - app.on("check_run.requested_action", async (context) => { + app.on("check_run.requested_action", async context => { const { payload, octokit } = context; if (payload.requested_action.identifier !== SYNC_ACTION) return; context.log.info( - `[Orchestrator](check_run.requested_action) Running requested action: ${SYNC_ACTION}` + `[Orchestrator](check_run.requested_action) Running requested action: ${SYNC_ACTION}`, ); /** @@ -635,37 +607,38 @@ export function orchestrator(app: Probot) { }); const workflowMeta = Object.entries(WORKFLOWS).find( - ([, w]) => w.checkRunName === payload.check_run.name + ([, w]) => w.checkRunName === payload.check_run.name, ); if (!workflowMeta) { context.log.info( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Could not find a workflow run for ${payload.check_run.name}` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Could not find a workflow run for ${payload.check_run.name}`, ); return; } - const workflowRun = data.workflow_runs.reduce< - WorkflowRunPayload["workflow_run"] | undefined - >((wf, curr) => { - if ((curr as any).path === ".github/workflows/" + workflowMeta[0]) { - if (!wf || wf.run_attempt < (curr?.run_attempt ?? 1)) { - return curr as WorkflowRunPayload["workflow_run"]; + const workflowRun = data.workflow_runs.reduce( + (wf, curr) => { + if ((curr as any).path === ".github/workflows/" + workflowMeta[0]) { + if (!wf || wf.run_attempt < (curr?.run_attempt ?? 1)) { + return curr as WorkflowRunPayload["workflow_run"]; + } } - } - return wf; - }, undefined); + return wf; + }, + undefined, + ); if (!workflowRun) { context.log.info( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Could not find a workflow run for ${workflowMeta[0]}` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Could not find a workflow run for ${workflowMeta[0]}`, ); return; } if (workflowRun.status === "in_progress") { context.log.info( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) The workflow run seems to be running still, no updates to check run` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) The workflow run seems to be running still, no updates to check run`, ); // The workflow is still running, we should not update the check run return; @@ -681,25 +654,15 @@ export function orchestrator(app: Probot) { if (workflowMeta[1].summaryFile) { // Get the summary artifact - const artifacts = await listWorkflowRunArtifacts( - octokit, - owner, - repo, - workflowRun.id - ); + const artifacts = await listWorkflowRunArtifacts(octokit, owner, repo, workflowRun.id); const artifactId = artifacts.find( - (artifact) => artifact.name === workflowMeta[1].summaryFile + artifact => artifact.name === workflowMeta[1].summaryFile, )?.id; if (artifactId) { try { - const rawSummary = await downloadArtifact( - octokit, - owner, - repo, - artifactId - ); + const rawSummary = await downloadArtifact(octokit, owner, repo, artifactId); const newSummary = JSON.parse(rawSummary.toString()); if (newSummary.summary) { summary = newSummary?.summary; @@ -709,7 +672,7 @@ export function orchestrator(app: Probot) { annotations = newSummary?.annotations; } catch (e) { context.log.error( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Error while downloading / parsing artifact: ${workflowMeta[1].summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${workflowRun.id}` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Error while downloading / parsing artifact: ${workflowMeta[1].summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${workflowRun.id}`, ); context.log.error(e as Error); } @@ -718,9 +681,7 @@ export function orchestrator(app: Probot) { const summaryPrefix = workflowMeta[1].description ? `#### ${workflowMeta[1].description}${ - !defaultSummary - ? `\n##### [🔗 Workflow run](${workflowRun.html_url})` - : "" + !defaultSummary ? `\n##### [🔗 Workflow run](${workflowRun.html_url})` : "" }\n\n` : ""; summary = summaryPrefix + summary; @@ -730,7 +691,7 @@ export function orchestrator(app: Probot) { const tips = await getTips(workflowMeta[0]); context.log.info( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Updating check run: ${payload.check_run.name} @id ${payload.check_run.id}` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Updating check run: ${payload.check_run.name} @id ${payload.check_run.id}`, ); const updateRes = await octokit.checks.update({ @@ -748,7 +709,7 @@ export function orchestrator(app: Probot) { }); context.log.info( - `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Check run updated: ${updateRes.data.name} @status ${updateRes.data.status} @conclusion ${updateRes.data.conclusion}` + `[Orchestrator](check_run.requested_action)(${SYNC_ACTION}) Check run updated: ${updateRes.data.name} @status ${updateRes.data.status} @conclusion ${updateRes.data.conclusion}`, ); // Batch annotations to avoid hitting the API rate limit. diff --git a/tools/github-bot/src/features/orchestrator/tools.ts b/tools/github-bot/src/features/orchestrator/tools.ts index 075d58c314a0..94dfc4f0b717 100644 --- a/tools/github-bot/src/features/orchestrator/tools.ts +++ b/tools/github-bot/src/features/orchestrator/tools.ts @@ -2,12 +2,7 @@ import fs from "fs/promises"; import path from "path"; import { ProbotOctokit } from "probot"; import { formatConclusion, getStatusEmoji } from "../../tools"; -import { - BOT_APP_ID, - REF_PREFIX, - WATCHER_CHECK_RUN_NAME, - WORKFLOWS, -} from "./const"; +import { BOT_APP_ID, REF_PREFIX, WATCHER_CHECK_RUN_NAME, WORKFLOWS } from "./const"; type Octokit = InstanceType; @@ -15,7 +10,7 @@ export async function updateWatcherCheckRun( octokit: Octokit, owner: string, repo: string, - ref: string + ref: string, ): Promise | void> { const checkSuites = await octokit.checks.listSuitesForRef({ owner, @@ -23,9 +18,7 @@ export async function updateWatcherCheckRun( ref, app_id: BOT_APP_ID, }); - const checkSuite = checkSuites.data.check_suites.find( - (suite) => suite.app?.id === BOT_APP_ID - ); + const checkSuite = checkSuites.data.check_suites.find(suite => suite.app?.id === BOT_APP_ID); if (checkSuite) { const [rawCheckRuns, rawWorkflowRuns] = await Promise.all([ @@ -62,10 +55,7 @@ export async function updateWatcherCheckRun( summary += `\n\n`; summary += `### 👁 Watching`; - const [ - aggregatedConclusion, - aggregatedStatus, - ] = rawCheckRuns.data.check_runs + const [aggregatedConclusion, aggregatedStatus] = rawCheckRuns.data.check_runs .slice() .sort((a, b) => a.name.localeCompare(b.name)) .reduce( @@ -76,7 +66,7 @@ export async function updateWatcherCheckRun( } const workflowMeta = Object.entries(WORKFLOWS).find( - ([, w]) => w.checkRunName === check_run.name + ([, w]) => w.checkRunName === check_run.name, ); if (!workflowMeta) { @@ -84,34 +74,27 @@ export async function updateWatcherCheckRun( } const workflowRun = rawWorkflowRuns.data.workflow_runs.find( - (wr) => (wr as any).path === ".github/workflows/" + workflowMeta[0] + wr => (wr as any).path === ".github/workflows/" + workflowMeta[0], ); const workflowLink = workflowRun?.html_url ? ` _[(workflow)](${workflowRun.html_url})_` : ""; - summary += `\n- ${getStatusEmoji( + summary += `\n- ${getStatusEmoji(check_run.conclusion || check_run.status)} **[${ + check_run.name + }](${check_run.html_url})**${workflowLink}: \`${ check_run.conclusion || check_run.status - )} **[${check_run.name}](${ - check_run.html_url - })**${workflowLink}: \`${check_run.conclusion || - check_run.status}\` ${ - workflowMeta[1].required ? "" : "_(optional)_" - }`; + }\` ${workflowMeta[1].required ? "" : "_(optional)_"}`; let newPriority; let newStatus; - const priority = conclusions.indexOf( - check_run.conclusion || "neutral" - ); + const priority = conclusions.indexOf(check_run.conclusion || "neutral"); const accumulatorPriority = conclusions.indexOf(acc[0]); if (workflowMeta[1].required) { newPriority = - priority > accumulatorPriority - ? check_run.conclusion || "neutral" - : acc[0]; + priority > accumulatorPriority ? check_run.conclusion || "neutral" : acc[0]; newStatus = check_run.status === "completed" && acc[1] === "completed" ? "completed" @@ -123,7 +106,7 @@ export async function updateWatcherCheckRun( return [newPriority, newStatus]; }, - ["success", "completed"] + ["success", "completed"], ); if (watcherId) { @@ -169,12 +152,7 @@ export async function updateWatcherCheckRun( } } -export async function prIsFork( - octokit: Octokit, - repo: string, - owner: string, - prNumber?: number -) { +export async function prIsFork(octokit: Octokit, repo: string, owner: string, prNumber?: number) { if (prNumber === undefined) return true; const { data: pr } = await octokit.rest.pulls.get({ @@ -186,9 +164,7 @@ export async function prIsFork( return !!pr.head.repo?.fork; } -export async function getTips( - workflowFile: string -): Promise { +export async function getTips(workflowFile: string): Promise { const tipsFile = workflowFile.replace(".yml", ".md"); const p = path.join(__dirname, "..", "..", "..", "tips", tipsFile); let tips = undefined; diff --git a/tools/github-bot/src/features/upToDate/index.ts b/tools/github-bot/src/features/upToDate/index.ts index a45a83f7f5fc..2d140ea6eb68 100644 --- a/tools/github-bot/src/features/upToDate/index.ts +++ b/tools/github-bot/src/features/upToDate/index.ts @@ -19,7 +19,7 @@ export function upToDate(app: Probot) { // …even though the docs say that it's supposed to be .synchronize, it's not. "pull_request.edited", ], - async (context) => { + async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); const prHead = payload.pull_request.head; @@ -61,10 +61,10 @@ export function upToDate(app: Probot) { check_run_id: checkRun.id, }); } - } + }, ); - app.on("push", async (context) => { + app.on("push", async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); @@ -78,7 +78,7 @@ export function upToDate(app: Probot) { base: payload.ref.split("/").pop(), }); - const tasks = matchingPullRequests.map((pr) => async () => { + const tasks = matchingPullRequests.map(pr => async () => { // Retrieve the check run by name. const checkRunResponse = await octokit.checks.listForRef({ owner: pr.base.repo.owner.login, @@ -104,7 +104,7 @@ export function upToDate(app: Probot) { }); // Perform the actual check when created or retriggered. - app.on(["check_run.rerequested", "check_run.created"], async (context) => { + app.on(["check_run.rerequested", "check_run.created"], async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); diff --git a/tools/github-bot/src/features/upToDate/tools.ts b/tools/github-bot/src/features/upToDate/tools.ts index b90c26aa10f1..bf96ca5b0855 100644 --- a/tools/github-bot/src/features/upToDate/tools.ts +++ b/tools/github-bot/src/features/upToDate/tools.ts @@ -22,10 +22,7 @@ export const getCheckRunByName = async ({ check_name, }); -export const batch = ( - tasks: (() => T)[], - batchSize: number -): Promise => { +export const batch = (tasks: (() => T)[], batchSize: number): Promise => { const results: T[] = []; const tasksLength = tasks.length; @@ -38,9 +35,9 @@ export const batch = ( const task = tasks.shift(); if (task) { Promise.resolve(task()) - .then((result) => results.push(result)) + .then(result => results.push(result)) .then(shiftAndRun) - .catch((error) => { + .catch(error => { aborted = true; reject(error); }); @@ -93,7 +90,7 @@ export async function updateCheckRun({ owner, repo, }) - ).filter((pr) => pr.head.sha === checkRun.head_sha); + ).filter(pr => pr.head.sha === checkRun.head_sha); for await (const pr of associatedPRs) { const isFork = pr.head.repo?.fork; @@ -108,9 +105,7 @@ export async function updateCheckRun({ per_page: 1, }); - const isUpToDate = ["ahead", "identical"].includes( - comparison.data.status - ); + const isUpToDate = ["ahead", "identical"].includes(comparison.data.status); outcome.push({ isUpToDate, @@ -134,7 +129,7 @@ export async function updateCheckRun({ ], }; }, - { valid: true, branchList: [] } + { valid: true, branchList: [] }, ); const output = valid diff --git a/tools/github-bot/src/index.ts b/tools/github-bot/src/index.ts index 1e3a201e4723..be606a212e2e 100644 --- a/tools/github-bot/src/index.ts +++ b/tools/github-bot/src/index.ts @@ -32,7 +32,7 @@ export default (app: Probot) => { orchestrator(app); /* Log errors */ - app.onError((error) => { + app.onError(error => { app.log.error("[ERROR] Unhandled error"); app.log.error(error); }); diff --git a/tools/github-bot/src/tools/index.ts b/tools/github-bot/src/tools/index.ts index 691b43039452..3f04bfe07615 100644 --- a/tools/github-bot/src/tools/index.ts +++ b/tools/github-bot/src/tools/index.ts @@ -2,9 +2,8 @@ import { Context, ProbotOctokit } from "probot"; import https from "https"; import { unzipSingleFile } from "./zip"; -export const extractWorkflowFile = ( - payload: Context<"workflow_run">["payload"] -) => payload.workflow.path.split("@")[0].replace(".github/workflows/", ""); +export const extractWorkflowFile = (payload: Context<"workflow_run">["payload"]) => + payload.workflow.path.split("@")[0].replace(".github/workflows/", ""); type Octokit = InstanceType; type CheckRunResponse = ReturnType; @@ -32,7 +31,7 @@ export async function downloadArtifact( octokit: Octokit, owner: string, repo: string, - artifactId: number + artifactId: number, ): Promise { const artifactResponse = await octokit.actions.downloadArtifact({ owner, @@ -46,10 +45,10 @@ export async function downloadArtifact( const headers = { "User-Agent": "Node.js", }; - https.get(downloadUrl, { headers }, (res) => { + https.get(downloadUrl, { headers }, res => { const chunks: Buffer[] = []; - res.on("data", (chunk) => { + res.on("data", chunk => { chunks.push(chunk); }); @@ -58,11 +57,7 @@ export async function downloadArtifact( if (res.statusCode !== 200) { return reject( - new Error( - `Request failed with status code ${ - res.statusCode - }\n${buffer.toString()}` - ) + new Error(`Request failed with status code ${res.statusCode}\n${buffer.toString()}`), ); } @@ -73,7 +68,7 @@ export async function downloadArtifact( } }); - res.on("error", (error) => { + res.on("error", error => { reject(error); }); }); @@ -131,12 +126,7 @@ export function getGenericOutput(conclusion: string, summary?: string) { } export function formatConclusion(conclusion: string) { - return ( - getStatusEmoji(conclusion) + - " " + - conclusion[0].toLocaleUpperCase() + - conclusion.slice(1) - ); + return getStatusEmoji(conclusion) + " " + conclusion[0].toLocaleUpperCase() + conclusion.slice(1); } export function getStatusEmoji(status: string) { @@ -220,7 +210,7 @@ export async function listWorkflowRunArtifacts( owner: string, repo: string, run_id: number, - attempt: number = 0 + attempt = 0, ): Promise { const artifacts = await octokit.actions.listWorkflowRunArtifacts({ owner, @@ -233,10 +223,10 @@ export async function listWorkflowRunArtifacts( } if (artifacts.data.artifacts.length === 0 && attempt <= MAX_RETRIES) { - return new Promise((res) => + return new Promise(res => setTimeout(() => { res(listWorkflowRunArtifacts(octokit, owner, repo, run_id, ++attempt)); - }, TIMEOUT) + }, TIMEOUT), ); } diff --git a/tools/github-bot/src/tools/monitorWorkflow.ts b/tools/github-bot/src/tools/monitorWorkflow.ts index e633e6c51748..44f9e3379dfb 100644 --- a/tools/github-bot/src/tools/monitorWorkflow.ts +++ b/tools/github-bot/src/tools/monitorWorkflow.ts @@ -25,26 +25,25 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { * When a workflow is requested for the first time: * - Create the related check run */ - app.on("workflow_run.requested", async (context) => { + app.on("workflow_run.requested", async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflowFile = extractWorkflowFile(payload); if (workflowFile !== workflow.file) return; - context.log.info( - `[Monitoring Workflow](workflow_run.requested) ${payload.workflow_run.name}` - ); + context.log.info(`[Monitoring Workflow](workflow_run.requested) ${payload.workflow_run.name}`); const { data: checkSuite } = await octokit.checks.getSuite({ owner, repo, check_suite_id: payload.workflow_run.check_suite_id, }); const workflowUrl = payload.workflow_run.html_url; - const summaryPrefix = workflow.description - ? `#### ${workflow.description}\n\n` - : ""; + const summaryPrefix = workflow.description ? `#### ${workflow.description}\n\n` : ""; // Will trigger the check_run.created event await octokit.checks.create({ @@ -56,9 +55,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { started_at: new Date().toISOString(), output: { title: "⏱️ Queued", - summary: - summaryPrefix + - `The **[workflow](${workflowUrl})** is currently queued.`, + summary: summaryPrefix + `The **[workflow](${workflowUrl})** is currently queued.`, details_url: workflowUrl, }, }); @@ -68,10 +65,13 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { * When a workflow is completed: * - update the associated check run with the conclusion */ - app.on("workflow_run.completed", async (context) => { + app.on("workflow_run.completed", async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflowFile = extractWorkflowFile(payload); if (workflowFile !== workflow.file) return; @@ -103,21 +103,14 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { octokit, owner, repo, - payload.workflow_run.id + payload.workflow_run.id, ); - const artifactId = artifacts.find( - (artifact) => artifact.name === workflow.summaryFile - )?.id; + const artifactId = artifacts.find(artifact => artifact.name === workflow.summaryFile)?.id; if (artifactId) { try { - const rawSummary = await downloadArtifact( - octokit, - owner, - repo, - artifactId - ); + const rawSummary = await downloadArtifact(octokit, owner, repo, artifactId); const newSummary = JSON.parse(rawSummary.toString()); if (newSummary.summary) { summary = newSummary?.summary; @@ -127,7 +120,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { annotations = newSummary?.annotations; } catch (e) { context.log.error( - `[Monitoring Workflow](downloadArtifact) Error while downloading / parsing artifact: ${workflow.summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}` + `[Monitoring Workflow](downloadArtifact) Error while downloading / parsing artifact: ${workflow.summaryFile} @ artifactId: ${artifactId} & workflow_run.id: ${payload.workflow_run.id}`, ); context.log.error(e as Error); } @@ -136,9 +129,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { const summaryPrefix = workflow.description ? `#### ${workflow.description}${ - !defaultSummary - ? `\n##### [🔗 Workflow run](${payload.workflow_run.html_url})` - : "" + !defaultSummary ? `\n##### [🔗 Workflow run](${payload.workflow_run.html_url})` : "" }\n\n` : ""; summary = summaryPrefix + summary; @@ -196,23 +187,26 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { * - Re-create the check run (unfortunately rerequested does not reset the status to "queued") * - Update the fields to reflect the fact that the run is in progress and link the workflow */ - app.on("workflow_run", async (context) => { + app.on("workflow_run", async context => { const { payload, octokit } = context; context.log.debug( - `[Monitoring Workflow](workflow_run.${payload.action}) ${payload.workflow_run.name}` + `[Monitoring Workflow](workflow_run.${payload.action}) ${payload.workflow_run.name}`, ); // @ts-expect-error Expected because probot does not declare this webhook event even though it exists. if (context.payload.action !== "in_progress") return; const { owner, repo } = context.repo(); + + if (repo !== "ledger-live") return; + const workflowFile = extractWorkflowFile(payload); if (workflow.file !== workflowFile) return; context.log.info( - `[Monitoring Workflow](workflow_run.in_progress) ${payload.workflow_run.name}` + `[Monitoring Workflow](workflow_run.in_progress) ${payload.workflow_run.name}`, ); // Ensure that the latest workflow run status is "in progress" @@ -225,7 +219,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { if (workflowRun.data.status !== "in_progress") { context.log.info( - `[Monitoring Workflow](workflow_run.in_progress) The workflow run seems to be completed already, skipping…` + `[Monitoring Workflow](workflow_run.in_progress) The workflow run seems to be completed already, skipping…`, ); // Oops, the workflow is not in progress anymore, we should not update the check run return; @@ -238,9 +232,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { }); const workflowUrl = `https://github.com/${owner}/${repo}/actions/runs/${payload.workflow_run.id}`; - const summaryPrefix = workflow.description - ? `#### ${workflow.description}\n\n` - : ""; + const summaryPrefix = workflow.description ? `#### ${workflow.description}\n\n` : ""; // Will trigger the check_run.created event await createRunByName({ octokit, @@ -252,9 +244,7 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { details_url: workflowUrl, output: { title: "⚙️ Running", - summary: - summaryPrefix + - `The **[workflow](${workflowUrl})** is currently running.`, + summary: summaryPrefix + `The **[workflow](${workflowUrl})** is currently running.`, }, }, }); @@ -264,11 +254,13 @@ export function monitorWorkflow(app: Probot, workflow: WorkflowDescriptor) { * When a check run is rerequested: * - trigger the related workflow */ - app.on(["check_run.rerequested"], async (context) => { + app.on(["check_run.rerequested"], async context => { const { payload, octokit } = context; const { owner, repo } = context.repo(); + if (repo !== "ledger-live") return; + if (workflow.checkRunName === payload.check_run.name) { octokit.actions.createWorkflowDispatch({ owner, diff --git a/tools/github-bot/src/tools/zip.ts b/tools/github-bot/src/tools/zip.ts index ddd1bd05c561..f6eaab6c6103 100644 --- a/tools/github-bot/src/tools/zip.ts +++ b/tools/github-bot/src/tools/zip.ts @@ -20,9 +20,7 @@ function getEndOfCentralDirectory(buffer: Buffer) { eodSignature: buffer.readUInt32LE(eodOffset), diskNumber: buffer.readUInt16LE(eodOffset + 4), diskWhereCentralDirectoryStarts: buffer.readUInt16LE(eodOffset + 6), - numberOfCentralDirectoryRecordsOnThisDisk: buffer.readUInt16LE( - eodOffset + 8 - ), + numberOfCentralDirectoryRecordsOnThisDisk: buffer.readUInt16LE(eodOffset + 8), totalNumberOfCentralDirectoryRecords: buffer.readUInt16LE(eodOffset + 10), sizeOfCentralDirectory: buffer.readUInt32LE(eodOffset + 12), offsetOfStartOfCentralDirectory: buffer.readUInt32LE(eodOffset + 16), @@ -30,11 +28,7 @@ function getEndOfCentralDirectory(buffer: Buffer) { } // Generator function that yields one entry from the central directory -function* centralDirectoryYielder( - buffer: Buffer, - offset: number, - eodOffset: number -) { +function* centralDirectoryYielder(buffer: Buffer, offset: number, eodOffset: number) { while (offset < eodOffset) { const fileNameLength = buffer.readUInt16LE(offset + 28); const extraFieldLength = buffer.readUInt16LE(offset + 30); @@ -57,16 +51,14 @@ function* centralDirectoryYielder( internalFileAttributes: buffer.readUInt16LE(offset + 36), externalFileAttributes: buffer.readUInt32LE(offset + 38), relativeOffsetOfLocalFileHeader: buffer.readUInt32LE(offset + 42), - fileName: buffer - .slice(offset + 46, offset + 46 + fileNameLength) - .toString("utf8"), + fileName: buffer.slice(offset + 46, offset + 46 + fileNameLength).toString("utf8"), extraField: buffer.slice( offset + 46 + fileNameLength, - offset + 46 + fileNameLength + extraFieldLength + offset + 46 + fileNameLength + extraFieldLength, ), fileComment: buffer.slice( offset + 46 + fileNameLength + extraFieldLength, - offset + 46 + fileNameLength + extraFieldLength + fileCommentLength + offset + 46 + fileNameLength + extraFieldLength + fileCommentLength, ), }; yield entry; @@ -92,12 +84,10 @@ function readLocalHeader(buffer: Buffer, offset: number) { uncompressedSize: buffer.readUInt32LE(offset + 22), fileNameLength, extraFieldLength, - fileName: buffer - .slice(offset + 30, offset + 30 + fileNameLength) - .toString("utf-8"), + fileName: buffer.slice(offset + 30, offset + 30 + fileNameLength).toString("utf-8"), extraField: buffer.slice( offset + 30 + fileNameLength, - offset + 30 + fileNameLength + extraFieldLength + offset + 30 + fileNameLength + extraFieldLength, ), }; } @@ -109,16 +99,15 @@ export function unzipSingleFile(buffer: Buffer): Buffer { const centralDirectory = centralDirectoryYielder( buffer, endOfCentralDirectory.offsetOfStartOfCentralDirectory, - endOfCentralDirectory.eodOffset + endOfCentralDirectory.eodOffset, ); const centralDirectoryEntry = centralDirectory.next().value; if (!centralDirectoryEntry) return Buffer.alloc(0); const localHeader = readLocalHeader( buffer, - centralDirectoryEntry.relativeOffsetOfLocalFileHeader + centralDirectoryEntry.relativeOffsetOfLocalFileHeader, ); - const start = - centralDirectoryEntry.relativeOffsetOfLocalFileHeader + localHeader.length; + const start = centralDirectoryEntry.relativeOffsetOfLocalFileHeader + localHeader.length; const end = start + centralDirectoryEntry.compressedSize; return zlib.inflateRawSync(buffer.slice(start, end)); }