From c6fc8ddaaa34ba172d240625c635327708876e34 Mon Sep 17 00:00:00 2001 From: dapplion Date: Fri, 15 Jan 2021 18:51:10 +0100 Subject: [PATCH 1/3] Allow initializing Github from arbitrary repoSlug --- .../build/cleanPinsFromDeletedBranches.ts | 2 +- src/commands/githubActions/build/index.ts | 2 +- src/providers/github/Github.ts | 31 ++++++++++--------- src/tasks/createGithubRelease.ts | 2 +- src/tasks/createNextBranch.ts | 2 +- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/commands/githubActions/build/cleanPinsFromDeletedBranches.ts b/src/commands/githubActions/build/cleanPinsFromDeletedBranches.ts index 81109535..2c1f40ec 100644 --- a/src/commands/githubActions/build/cleanPinsFromDeletedBranches.ts +++ b/src/commands/githubActions/build/cleanPinsFromDeletedBranches.ts @@ -16,7 +16,7 @@ export async function cleanPinsFromDeletedBranches({ const manifest = readManifest(dir); // Connect to Github Octokit REST API to know existing branches - const github = new Github(dir); + const github = Github.fromDir(dir); const branches = await github.listBranches(); // Fetch Pinata credentials from ENVs diff --git a/src/commands/githubActions/build/index.ts b/src/commands/githubActions/build/index.ts index 0ed0db37..2a9d289f 100644 --- a/src/commands/githubActions/build/index.ts +++ b/src/commands/githubActions/build/index.ts @@ -42,7 +42,7 @@ export async function gaBuildHandler({ const ref = parseRef(refString); // Connect to Github Octokit REST API and post or edit a comment on PR - const github = new Github(dir); + const github = Github.fromDir(dir); // Clean pins that were added from past runs. // Doing it here prevents having to add two workflows per repo. diff --git a/src/providers/github/Github.ts b/src/providers/github/Github.ts index b937cf6a..9e8bc6a4 100644 --- a/src/providers/github/Github.ts +++ b/src/providers/github/Github.ts @@ -9,9 +9,20 @@ export class Github { octokit: Octokit; owner: string; repo: string; - repoSlug: string; - constructor(dir: string) { + constructor({ owner, repo }: { owner: string; repo: string }) { + this.owner = owner; + this.repo = repo; + + // OAuth2 token from Github + if (!process.env.GITHUB_TOKEN) + throw Error("GITHUB_TOKEN ENV (OAuth2) is required"); + this.octokit = new Octokit({ + auth: `token ${process.env.GITHUB_TOKEN}` + }); + } + + static fromDir(dir: string): Github { const repoSlug = getRepoSlugFromManifest(dir) || process.env.TRAVIS_REPO_SLUG || @@ -27,27 +38,19 @@ export class Github { if (!owner) throw Error(`repoSlug "${repoSlug}" hasn't an owner`); if (!repo) throw Error(`repoSlug "${repoSlug}" hasn't a repo`); - this.owner = owner; - this.repo = repo; - this.repoSlug = repoSlug; - - // OAuth2 token from Github - if (!process.env.GITHUB_TOKEN) - throw Error("GITHUB_TOKEN ENV (OAuth2) is required"); - this.octokit = new Octokit({ - auth: `token ${process.env.GITHUB_TOKEN}` - }); + return new Github({ owner, repo }); } async assertRepoExists(): Promise { try { await this.octokit.repos.get({ owner: this.owner, repo: this.repo }); } catch (e) { + const repoSlug = `${this.owner}/${this.repo}`; if (e.status === 404) throw Error( - `Repo does not exist: ${this.repoSlug}. Check the manifest.repository object and correct the repo URL` + `Repo does not exist: ${repoSlug}. Check the manifest.repository object and correct the repo URL` ); - e.message = `Error verifying repo ${this.repoSlug}: ${e.message}`; + e.message = `Error verifying repo ${repoSlug}: ${e.message}`; throw e; } } diff --git a/src/tasks/createGithubRelease.ts b/src/tasks/createGithubRelease.ts index 3f36d453..86009430 100644 --- a/src/tasks/createGithubRelease.ts +++ b/src/tasks/createGithubRelease.ts @@ -29,7 +29,7 @@ export function createGithubRelease({ if (!process.env.GITHUB_TOKEN) throw Error("GITHUB_TOKEN ENV (OAuth2) is required"); - const github = new Github(dir); + const github = Github.fromDir(dir); // Gather repo data, repoSlug = "dappnode/DNP_ADMIN" const repoSlug = diff --git a/src/tasks/createNextBranch.ts b/src/tasks/createNextBranch.ts index 54e47186..29eb520e 100644 --- a/src/tasks/createNextBranch.ts +++ b/src/tasks/createNextBranch.ts @@ -16,7 +16,7 @@ export function createNextBranch({ silent }: CliGlobalOptions): Listr { // Gather repo data, repoSlug = "dappnode/DNP_ADMIN" - const github = new Github(dir); + const github = Github.fromDir(dir); return new Listr( [ From 138a75a2ef33aa609ad6e70600f19508982ae477 Mon Sep 17 00:00:00 2001 From: dapplion Date: Fri, 15 Jan 2021 18:51:27 +0100 Subject: [PATCH 2/3] Add dispatchEvent method --- src/providers/github/Github.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/providers/github/Github.ts b/src/providers/github/Github.ts index 9e8bc6a4..b1ca5d82 100644 --- a/src/providers/github/Github.ts +++ b/src/providers/github/Github.ts @@ -302,4 +302,29 @@ export class Github { }); return res.data; } + + /** + * trigger a webhook event called repository_dispatch in a repository + * The consumer must add in workflow.yml + * ```yaml + * on: + * repository_dispatch: + * types: [backend_automation] + * ``` + * Example payload on the receiver https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#repository_dispatch + * @param eventType "backend_automation" + * @param clientPayload Arbitrary JSON payload: `{ "extraData": 1234 }` + */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + async dispatchEvent( + eventType: string, + clientPayload?: { [key: string]: string | number } + ) { + await this.octokit.repos.createDispatchEvent({ + owner: this.owner, + repo: this.repo, + event_type: eventType, + client_payload: clientPayload + }); + } } From 18b4abcf60a22a50c1ade5accb875da95466cff2 Mon Sep 17 00:00:00 2001 From: dapplion Date: Fri, 15 Jan 2021 19:08:03 +0100 Subject: [PATCH 3/3] Add triggerCoreDnpWorkflowMaybe --- src/commands/githubActions/build/index.ts | 12 +++-- .../githubActions/build/triggerCoreDnp.ts | 48 +++++++++++++++++++ src/params.ts | 11 +++++ 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 src/commands/githubActions/build/triggerCoreDnp.ts diff --git a/src/commands/githubActions/build/index.ts b/src/commands/githubActions/build/index.ts index 2a9d289f..7cfd151d 100644 --- a/src/commands/githubActions/build/index.ts +++ b/src/commands/githubActions/build/index.ts @@ -7,6 +7,7 @@ import { Github } from "../../../providers/github/Github"; import { parseRef } from "../../../providers/github/utils"; import { getBuildBotComment, isTargetComment } from "./botComment"; import { cleanPinsFromDeletedBranches } from "./cleanPinsFromDeletedBranches"; +import { triggerCoreDnpWorkflowMaybe } from "./triggerCoreDnp"; // This action should be run on 'push' and 'pull_request' events // @@ -80,10 +81,15 @@ export async function gaBuildHandler({ github.commentToPr({ number: pr.number, body, isTargetComment }) ) ); - return; // done - } - if (eventName === "push" || eventName === "pull_request") { + // Trigger CORE package for a possible release + await triggerCoreDnpWorkflowMaybe({ + dir, + releaseMultiHash, + branch: ref.branch, + commitSha + }); + } else if (eventName === "push" || eventName === "pull_request") { // Consider that for 'pull_request' commitSha does not represent a known commit // The incoming branch is merged into the target branch and the resulting // new commit is tested. gitHead() will return 'HEAD' for branch and a foreign commit diff --git a/src/commands/githubActions/build/triggerCoreDnp.ts b/src/commands/githubActions/build/triggerCoreDnp.ts new file mode 100644 index 00000000..8aa34fa1 --- /dev/null +++ b/src/commands/githubActions/build/triggerCoreDnp.ts @@ -0,0 +1,48 @@ +import { Github } from "../../../providers/github/Github"; +import { readManifest } from "../../../utils/manifest"; +import { + DNP_CORE_GITHUB_EVENT_TYPE, + DNP_CORE_GITHUB_USER, + DNP_CORE_GITHUB_REPO, + DNP_CORE_DEPENDENCIES +} from "../../../params"; + +/** + * Send a repository_dispatch event to the DNP_CORE Github repo only if: + * - Current package is a dependency of DNP_CORE + */ +export async function triggerCoreDnpWorkflowMaybe({ + dir, + releaseMultiHash, + branch, + commitSha +}: { + dir: string; + releaseMultiHash: string; + branch: string; + commitSha: string; +}): Promise { + const manifest = readManifest(dir); + if (!DNP_CORE_DEPENDENCIES.includes(manifest.name)) { + console.log(`Skipping triggerCoreDnpWorkflow for ${manifest.name}`); + return; + } + + const github = new Github({ + owner: DNP_CORE_GITHUB_USER, + repo: DNP_CORE_GITHUB_REPO + }); + + const clientPayload = { + releaseMultiHash, + branch, + commitSha + }; + + try { + await github.dispatchEvent(DNP_CORE_GITHUB_EVENT_TYPE, clientPayload); + console.log("Triggered DNP_CORE workflow"); + } catch (e) { + console.log("Error on triggerCoreDnpWorkflow", e); + } +} diff --git a/src/params.ts b/src/params.ts index 7898c1cb..595199cd 100644 --- a/src/params.ts +++ b/src/params.ts @@ -9,6 +9,17 @@ export const UPSTREAM_VERSION_VARNAME = "UPSTREAM_VERSION"; export const upstreamImageLabel = "dappnode.dnp.upstreamImage"; export const PINATA_URL = "https://api.pinata.cloud"; +export const DNP_CORE_GITHUB_EVENT_TYPE = "dappnodesdk_build"; +export const DNP_CORE_GITHUB_USER = "dappnode"; +export const DNP_CORE_GITHUB_REPO = "DNP_CORE"; +export const DNP_CORE_DEPENDENCIES = [ + "bind.dnp.dappnode.eth", + "ipfs.dnp.dappnode.eth", + "vpn.dnp.dappnode.eth", + "dappmanager.dnp.dappnode.eth", + "wifi.dnp.dappnode.eth" +]; + /** * Plain text file with should contain the IPFS hash of the release * Necessary for the installer script to fetch the latest content hash