From e6fe2c805d62690bba820f9897310ffe6936394e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 13 Jun 2025 18:16:18 +0200 Subject: [PATCH 1/3] chore: change file structure, remove use of `gh` from JS Spawning a different process makes the process more fragile, for little benefit. Removing the dependency to `gh` follows the least surprise path --- .github/workflows/initiateNewVote.yml | 2 +- .../decryptPrivateKeyAndCloseVote.mjs | 2 +- votes/initiateNewVote/generateNewVotePR.mjs | 2 +- ...NewVote.mjs => generateVoteInitCommit.mjs} | 0 votes/initiateNewVote/getVoteStatus.mjs | 2 +- .../generateNewVotePR.mjs} | 76 ++++++++----------- .../{ => utils}/getVoteSubpath.mjs | 0 7 files changed, 36 insertions(+), 48 deletions(-) rename votes/initiateNewVote/{generateNewVote.mjs => generateVoteInitCommit.mjs} (100%) rename votes/initiateNewVote/{_generateNewVotePR.mjs => utils/generateNewVotePR.mjs} (59%) rename votes/initiateNewVote/{ => utils}/getVoteSubpath.mjs (100%) diff --git a/.github/workflows/initiateNewVote.yml b/.github/workflows/initiateNewVote.yml index 16cb5db..e0b54ac 100644 --- a/.github/workflows/initiateNewVote.yml +++ b/.github/workflows/initiateNewVote.yml @@ -129,7 +129,7 @@ jobs: fi - name: Generate the vote init commit run: | - ./votes/initiateNewVote/generateNewVote.mjs \ + ./votes/initiateNewVote/generateVoteInitCommit.mjs \ --remote origin \ --github-repo-name "$GITHUB_REPOSITORY" \ --vote-repository-path . \ diff --git a/votes/initiateNewVote/decryptPrivateKeyAndCloseVote.mjs b/votes/initiateNewVote/decryptPrivateKeyAndCloseVote.mjs index f6dc2df..69569e2 100755 --- a/votes/initiateNewVote/decryptPrivateKeyAndCloseVote.mjs +++ b/votes/initiateNewVote/decryptPrivateKeyAndCloseVote.mjs @@ -5,7 +5,7 @@ import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; import countFromGit from "@node-core/caritat/countBallotsFromGit"; -import { findVoteSubPath } from "./getVoteSubpath.mjs"; +import { findVoteSubPath } from "./utils/getVoteSubpath.mjs"; const { values: parsedArgs } = parseArgs({ options: { diff --git a/votes/initiateNewVote/generateNewVotePR.mjs b/votes/initiateNewVote/generateNewVotePR.mjs index 339cd85..5633dfd 100755 --- a/votes/initiateNewVote/generateNewVotePR.mjs +++ b/votes/initiateNewVote/generateNewVotePR.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node import { parseArgs } from "node:util"; -import { prOptions, createVotePR } from "./_generateNewVotePR.mjs"; +import { prOptions, createVotePR } from "./utils/generateNewVotePR.mjs"; const { values: argv } = parseArgs({ options: prOptions }); diff --git a/votes/initiateNewVote/generateNewVote.mjs b/votes/initiateNewVote/generateVoteInitCommit.mjs similarity index 100% rename from votes/initiateNewVote/generateNewVote.mjs rename to votes/initiateNewVote/generateVoteInitCommit.mjs diff --git a/votes/initiateNewVote/getVoteStatus.mjs b/votes/initiateNewVote/getVoteStatus.mjs index 90facd0..39c300e 100755 --- a/votes/initiateNewVote/getVoteStatus.mjs +++ b/votes/initiateNewVote/getVoteStatus.mjs @@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"; import count from "@node-core/caritat/countParticipationFromGit"; import countFromGit from "@node-core/caritat/countBallotsFromGit"; -import { findVoteSubPath } from "./getVoteSubpath.mjs"; +import { findVoteSubPath } from "./utils/getVoteSubpath.mjs"; const { allowedVoters, shares } = JSON.parse(argv[2]) diff --git a/votes/initiateNewVote/_generateNewVotePR.mjs b/votes/initiateNewVote/utils/generateNewVotePR.mjs similarity index 59% rename from votes/initiateNewVote/_generateNewVotePR.mjs rename to votes/initiateNewVote/utils/generateNewVotePR.mjs index 18ccf24..028c72a 100644 --- a/votes/initiateNewVote/_generateNewVotePR.mjs +++ b/votes/initiateNewVote/utils/generateNewVotePR.mjs @@ -1,8 +1,3 @@ -import { once } from "node:events"; -import { spawn } from "node:child_process"; -import { exit } from "node:process"; -import { Buffer } from "node:buffer"; - export const keyServerURL = "hkps://keys.openpgp.org"; export function secretHolderThreshold(argv) { @@ -41,46 +36,39 @@ export const prOptions = { } export async function createVotePR(argv) { - const cp = spawn( - "gh", - [ - "api", - `repos/${argv["github-repo-name"]}/pulls`, - "-F", - "base=main", - "-F", - `head=${argv.branch}`, - "-F", - `title=${argv.subject}`, - "-F", - `body=${argv["pr-intro"] ?? ""}, + const response = await fetch(`${process.env.GITHUB_API_URL}/repos/${argv["github-repo-name"]}/pulls`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${process.env.GH_TOKEN}`, + 'Accept': 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + body: JSON.stringify({ + base: 'main', + head: argv.branch, + title: argv.subject, + body: `${argv["pr-intro"] ?? ""}, To close the vote, a minimum of ${secretHolderThreshold(argv)} key parts would need to be revealed. Vote instructions will follow.`, - "--jq", - ".html_url", - ], - { stdio: ["inherit", "pipe", "inherit"] } - ); - // @ts-ignore toArray does exist! - const out = cp.stdout.toArray(); - const [code] = await once(cp, "exit"); - if (code !== 0) exit(code); - - const prUrl = Buffer.concat(await out) - .toString() - .trim(); + }), + }); + if (!response.ok) { + throw new Error('Failed to create PR: ' + response.statusText, { cause: response }) + } + const { html_url: prURL, url } = await response.json(); { - const cp = spawn( - "gh", - [ - "pr", - "edit", - prUrl, - "--body", - `${argv["pr-intro"] ?? ""} + const response = await fetch(url, { + method: 'PATCH', + headers: { + 'Authorization': `Bearer ${process.env.GH_TOKEN}`, + 'Accept': 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + }, + body: JSON.stringify({ + body: `${argv["pr-intro"] ?? ""} Vote instructions: @@ -95,12 +83,12 @@ ${argv['secret-holder']?.length ? run the following command: ${"`"}git node vote ${prUrl} --decrypt-key-part --post-comment${"`"} ` : ''} `, - ], - { stdio: "inherit" }, - ); + }), + }); - const [code] = await once(cp, "exit"); - if (code !== 0) exit(code); + if (!response.ok) { + throw new Error('Failed to edit PR: ' + response.statusText, { cause: response }) + } } console.log("PR created", prUrl); diff --git a/votes/initiateNewVote/getVoteSubpath.mjs b/votes/initiateNewVote/utils/getVoteSubpath.mjs similarity index 100% rename from votes/initiateNewVote/getVoteSubpath.mjs rename to votes/initiateNewVote/utils/getVoteSubpath.mjs From b62fe68f1b1bfe33abce9b070ece10fad3d795af Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 13 Jun 2025 18:18:23 +0200 Subject: [PATCH 2/3] Update _EDIT_ME.yml (#4) --- votes/initiateNewVote/_EDIT_ME.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/votes/initiateNewVote/_EDIT_ME.yml b/votes/initiateNewVote/_EDIT_ME.yml index 079bd07..dfd4176 100644 --- a/votes/initiateNewVote/_EDIT_ME.yml +++ b/votes/initiateNewVote/_EDIT_ME.yml @@ -3,7 +3,7 @@ # 1. Select a subject for the vote. This can be a question addressed to the # voting members. -subject: REPLACEME +subject: Test test # 2. You can leave the header instructions as is, or modify them if you see fit. headerInstructions: | @@ -25,7 +25,7 @@ headerInstructions: | # there are. candidates: - TODO - - TODO + - TODO2 # 4. Pass the following to false if it's important to keep the candidates in the # order you define above. Presenting candidates in a fixed order tends to From 3c097d82fc9d2cd947f8b2d4812335638ea45ebc Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 13 Jun 2025 18:22:08 +0200 Subject: [PATCH 3/3] Update _EDIT_ME.yml --- votes/initiateNewVote/_EDIT_ME.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/votes/initiateNewVote/_EDIT_ME.yml b/votes/initiateNewVote/_EDIT_ME.yml index dfd4176..5f02fe2 100644 --- a/votes/initiateNewVote/_EDIT_ME.yml +++ b/votes/initiateNewVote/_EDIT_ME.yml @@ -24,7 +24,7 @@ headerInstructions: | # voters express their preference for each candidates, no matter how many # there are. candidates: - - TODO + - TODO1 - TODO2 # 4. Pass the following to false if it's important to keep the candidates in the