-
Notifications
You must be signed in to change notification settings - Fork 134
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
9 changed files
with
840 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
name: Close vote | ||
|
||
on: | ||
# Using `issue_comment` is a bit noisy, let's disable it for now. | ||
# issue_comment: | ||
# types: [created] | ||
workflow_dispatch: | ||
inputs: | ||
pr: | ||
description: ID of the Vote PR that contains a vote ready to be closed | ||
required: true | ||
type: number | ||
|
||
permissions: | ||
contents: write | ||
pull-requests: write | ||
|
||
jobs: | ||
close-vote: | ||
if: github.event.inputs.pr || | ||
(github.event.issue.pull_request && contains(github.event.comment.body, '-----BEGIN SHAMIR KEY PART-----')) | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Get PR URL | ||
id: pr-url | ||
run: | | ||
echo "URL=${{ github.event.repository.html_url }}/pull/${{ github.event.inputs.pr || github.event.issue.number }}" >> "$GITHUB_OUTPUT" | ||
- name: Filter comments | ||
id: comments | ||
run: gh pr view ${{ steps.pr-url.outputs.URL }} --json | ||
comments --jq '.comments | map(.body | select(contains("-----BEGIN | ||
SHAMIR KEY PART-----"))) | "comments=" + tostring' >> "$GITHUB_OUTPUT" | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
- name: Get PR branch | ||
id: branch | ||
run: gh pr view ${{ steps.pr-url.outputs.URL }} --json | ||
headRefName --jq '"head=" + .headRefName' >> "$GITHUB_OUTPUT" | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
- name: Compute number of commits in the PR | ||
id: nb-of-commits | ||
run: | | ||
NB_OF_COMMITS=$(gh pr view --json commits --jq '.commits | length' "${{ steps.pr-url.outputs.URL }}") | ||
echo "exact=$NB_OF_COMMITS" >> $GITHUB_OUTPUT | ||
echo "minusOne=$(($NB_OF_COMMITS - 1))" >> $GITHUB_OUTPUT | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 | ||
with: | ||
# Loading the default branch so we use the last version of the mailmap | ||
# rather than getting stuck to when the vote PR was open. | ||
ref: ${{ github.event.repository.default_branch }} | ||
persist-credentials: true # we need the credentials to push the new vote branch | ||
- name: Download nodejs/node mailmap file | ||
run: | ||
curl -L https://raw.githubusercontent.com/nodejs/node/main/.mailmap >> | ||
.mailmap | ||
- name: Configure git | ||
run: | | ||
git config --global user.email "[email protected]" | ||
git config --global user.name "Node.js GitHub Bot" | ||
- name: Load vote branch | ||
run: | | ||
git fetch origin '${{ steps.branch.outputs.head }}' | ||
git reset FETCH_HEAD --mixed | ||
git checkout HEAD -- '${{ steps.branch.outputs.head }}' | ||
- run: npm install @node-core/caritat | ||
- name: Attempt closing the vote | ||
id: vote-summary | ||
run: | | ||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | ||
echo "markdown<<$EOF" >> "$GITHUB_OUTPUT" | ||
./votes/initiateNewVote/decryptPrivateKeyAndCloseVote.mjs \ | ||
--remote origin --branch "${{ steps.branch.outputs.head }}" \ | ||
--fromCommit "FETCH_HEAD~${{ steps.nb-of-commits.outputs.minusOne }}" \ | ||
--toCommit "FETCH_HEAD" \ | ||
--prURL "${{ steps.pr-url.outputs.URL }}" \ | ||
--save-markdown-summary summaryComment.md \ | ||
--comments "$COMMENTS" --commit-json-summary >> "$GITHUB_OUTPUT" | ||
echo "$EOF" >> "$GITHUB_OUTPUT" | ||
env: | ||
COMMENTS: ${{ steps.comments.outputs.comments }} | ||
- name: Push to the PR branch | ||
run: git push origin "HEAD:${{ steps.branch.outputs.head }}" | ||
- name: Publish vote summary comment | ||
run: | | ||
gh pr comment "${{ steps.pr-url.outputs.URL }}" --body-file summaryComment.md | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
SUMMARY: ${{ steps.vote-summary.outputs.markdown }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
name: Initiate new vote | ||
|
||
on: | ||
pull_request: | ||
types: [opened, synchronize, reopened, ready_for_review] | ||
paths: | ||
- votes/initiateNewVote/_EDIT_ME.yml | ||
push: | ||
branches: | ||
- initiateNewVote | ||
|
||
permissions: | ||
contents: read | ||
|
||
jobs: | ||
lint-vote-init-file: | ||
if: github.event.pull_request && github.event.pull_request.draft == false | ||
permissions: | ||
contents: write | ||
pull-requests: write | ||
repository-projects: read | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 | ||
with: | ||
persist-credentials: false | ||
# If the subject is still REPLACEME, that would mean it's a PR to modify | ||
# the sample file, not a PR initializing a vote. | ||
- run: '! grep -q "subject: REPLACEME" votes/initiateNewVote.yml' | ||
- name: Use Node.js ${{ env.NODE_VERSION }} | ||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 | ||
with: | ||
node-version: lts/* | ||
- name: Validate YAML and ensure there are more than 1 candidate | ||
run: | ||
npx js-yaml votes/initiateNewVote.yml | jq '.candidates | unique | | ||
length > 1 or error("Not enough candidates")' | ||
- name: Change base branch | ||
if: github.base_ref == github.event.repository.default_branch | ||
run: | | ||
gh api \ | ||
--method POST \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "X-GitHub-Api-Version: 2022-11-28" \ | ||
/repos/${{ github.repository }}/git/refs \ | ||
-f ref='refs/heads/initiateNewVote' \ | ||
-f sha='${{ github.event.pull_request.base.sha }}' | ||
gh pr edit ${{ github.event.pull_request.html_url }} --base 'initiateNewVote' | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
initiate-new-vote: | ||
if: github.event.pusher | ||
permissions: | ||
contents: write | ||
pull-requests: write | ||
repository-projects: read | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 | ||
with: | ||
persist-credentials: true # we need the credentials to push the new vote branch | ||
- name: Install Node.js | ||
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0 | ||
with: | ||
node-version: lts/* | ||
- name: Extract info from the pushed file | ||
id: data | ||
run: | | ||
npx js-yaml votes/initiateNewVote/_EDIT_ME.yml > data.json | ||
echo "json_data<<EOF" >> "$GITHUB_OUTPUT" | ||
cat data.json >> "$GITHUB_OUTPUT" | ||
echo "EOF" >> "$GITHUB_OUTPUT" | ||
echo "branchName=votes/$(node -p 'require("./data.json")["path-friendly-id"] || crypto.randomUUID()')" >> "$GITHUB_OUTPUT" | ||
node >> "$GITHUB_ENV" <<'EOF' | ||
'use strict'; | ||
const { createHash } = require('node:crypto'); | ||
const { candidates } = require("./data.json"); | ||
for (let i = 0; i < candidates.length; i++) { | ||
const delimiter = createHash('sha256').update(candidates[i], 'utf8').digest('base64'); | ||
console.log(`__CANDIDATES_${i}<<${delimiter}`) | ||
process.stdout.write(candidates[i]); | ||
process.stdout.write(`\n${delimiter}\n`); | ||
} | ||
console.log('__CANDIDATES<<EOF'); | ||
for (let i = 0; i < candidates.length; i++) { | ||
console.log(`--candidate "$__CANDIDATES_${i}" \\`); | ||
} | ||
if (candidates.length) console.log(''); | ||
console.log('EOF'); | ||
EOF | ||
- name: Reset to the base branch | ||
run: git fetch origin HEAD && git reset FETCH_HEAD --hard | ||
- name: Install npm dependencies | ||
run: npm install @node-core/caritat | ||
- name: Configure git | ||
run: | | ||
git config --global user.email "[email protected]" | ||
git config --global user.name "Node.js GitHub Bot" | ||
- name: Configure and (re)start GPG agent | ||
shell: bash | ||
run: | | ||
if [ -f /usr/lib/systemd/user/gpg-agent.service ]; then | ||
mkdir ~/.gnupg | ||
cat <<EOT >> ~/.gnupg/gpg-agent.conf | ||
allow-preset-passphrase | ||
default-cache-ttl 60 | ||
max-cache-ttl 50 | ||
EOT | ||
chmod 600 ~/.gnupg/* | ||
chmod 700 ~/.gnupg | ||
systemctl --user restart gpg-agent | ||
else | ||
gpg-agent --daemon --allow-preset-passphrase \ | ||
--default-cache-ttl 60 --max-cache-ttl 60 | ||
fi | ||
- name: Generate the vote branch and PR | ||
run: | | ||
./votes/initiateNewVote/generateNewVotePR.mjs \ | ||
--remote origin \ | ||
--github-repo-name "$GITHUB_REPOSITORY" \ | ||
--vote-repository-path . \ | ||
--branch "$__BRANCH" \ | ||
--subject "$__SUBJECT" \ | ||
${{ env.__CANDIDATES }} \ | ||
--shuffle-candidates "$__SHUFFLE_CANDIDATES" \ | ||
--header-instructions "$__HEADER_INSTRUCTIONS" \ | ||
--footer-instructions "$__FOOTER_INSTRUCTIONS" \ | ||
--create-pull-request --pr-intro "$__PR_INTRO" | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
__BRANCH: ${{ steps.data.outputs.branchName }} | ||
__SUBJECT: ${{ fromJSON(steps.data.outputs.json_data).subject }} | ||
__SHUFFLE_CANDIDATES: ${{ fromJSON(steps.data.outputs.json_data).canShuffleCandidates }} | ||
__HEADER_INSTRUCTIONS: ${{ fromJSON(steps.data.outputs.json_data).headerInstructions }} | ||
__FOOTER_INSTRUCTIONS: ${{ fromJSON(steps.data.outputs.json_data).footerInstructions }} | ||
__PR_INTRO: ${{ fromJSON(steps.data.outputs.json_data).prBody }} | ||
- name: Remove initiateNewVote branch | ||
run: | | ||
gh api \ | ||
--method DELETE \ | ||
-H "Accept: application/vnd.github+json" \ | ||
-H "X-GitHub-Api-Version: 2022-11-28" \ | ||
"/repos/$GITHUB_REPOSITORY/git/$GITHUB_REF" | ||
env: | ||
GH_TOKEN: ${{ github.token }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
name: Validate vote commit and update participation | ||
|
||
on: | ||
pull_request: | ||
types: [synchronize] | ||
paths: [votes/**] | ||
|
||
concurrency: ${{ github.workflow }}--${{ github.head_ref }} | ||
permissions: | ||
contents: read | ||
pull-requests: write | ||
repository-projects: read | ||
|
||
jobs: | ||
validate-commit-and-update-participation: | ||
if: startsWith(github.head_ref, 'votes/') | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Compute number of commits in the PR | ||
id: nb-of-commits | ||
run: | | ||
echo "plusOne=$((${{ github.event.pull_request.commits }} + 1))" >> $GITHUB_OUTPUT | ||
echo "minusOne=$((${{ github.event.pull_request.commits }} - 1))" >> $GITHUB_OUTPUT | ||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 | ||
with: | ||
fetch-depth: ${{ steps.nb-of-commits.outputs.plusOne }} | ||
persist-credentials: false | ||
- name: Download nodejs/node mailmap file | ||
run: | ||
curl -L https://raw.githubusercontent.com/nodejs/node/main/.mailmap >> | ||
.mailmap | ||
- run: npm install @node-core/caritat | ||
- name: Get PR description | ||
id: desc | ||
run: | | ||
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | ||
echo "markdown<<$EOF" >> "$GITHUB_OUTPUT" | ||
gh pr view "${{ github.event.pull_request.html_url }}" --json body --jq '.body' >> "$GITHUB_OUTPUT" | ||
echo "$EOF" >> "$GITHUB_OUTPUT" | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
- name: Get updated vote status | ||
id: status | ||
run: | ||
echo "prStatus=$(./votes/initiateNewVote/getVoteStatus.mjs)" >> | ||
"$GITHUB_OUTPUT"; cat "$GITHUB_OUTPUT" | ||
env: | ||
SUBPATH: ${{ github.head_ref }} | ||
FIRST_COMMIT_REF: HEAD^2~${{ steps.nb-of-commits.outputs.minusOne }} | ||
LAST_COMMIT_REF: ${{ github.event.after }} | ||
CHECK_COMMITS_AFTER: ${{ github.event.before }} | ||
PR_DESCRIPTION: ${{steps.desc.outputs.markdown}} | ||
- name: Update PR description | ||
run: | ||
gh pr edit "${{ github.event.pull_request.html_url }}" --body "$BODY" | ||
env: | ||
BODY: ${{ fromJSON(steps.status.outputs.prStatus).body }} | ||
GH_TOKEN: ${{ github.token }} | ||
- name: Add comment if some invalid commits were found | ||
if: fromJSON(steps.status.outputs.prStatus).hasFailures | ||
run: | ||
gh pr comment "${{ github.event.pull_request.html_url }}" -b | ||
"$SUMMARY" | ||
env: | ||
GH_TOKEN: ${{ github.token }} | ||
SUMMARY: | ||
${{ fromJSON(steps.status.outputs.prStatus).invalidCommitReason }} | ||
- name: Mark workflow as failed if some invalid commits were found | ||
if: fromJSON(steps.status.outputs.prStatus).hasFailures | ||
run: 'false' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Initiate a new TSC vote | ||
|
||
For yes/no questions, the TSC will typically use GH reactions to conduct a vote, | ||
and use this workflow only for questions with several candidate answers, or to | ||
e.g guarantee vote secrecy until the vote is counted. | ||
|
||
## From the GitHub web UI | ||
|
||
1. Edit the [`_EDIT_ME.yml`](./_EDIT_ME.yml) file, fill in the info related to | ||
vote to open. | ||
2. When committing, chose to commit to new branch and open a Pull Request to | ||
discuss the vote terms with the whole TSC. | ||
3. Once the PR has approvals, merge it on the `initiateNewVote` branch (GHA | ||
should have set that as the target/base branch automatically). | ||
4. GHA will open a new PR with the vote initiated. | ||
|
||
## From the CLI | ||
|
||
This method is not recommended. | ||
|
||
1. Edit the [`_EDIT_ME.yml`](./_EDIT_ME.yml) file, fill in the info related to | ||
vote to open. | ||
2. Commit your changes. | ||
3. Push that to the remote `refs/heads/initiateNewVote`. | ||
4. GHA will open a new PR with the vote initiated. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# To initiate a new vote, you need to open a PR modifying this file. The vote | ||
# can start once the PR has approvals and is merged via the GitHub interface. | ||
|
||
# 1. Select a subject for the vote. This can be a question addressed to the TSC | ||
# voting members. | ||
subject: REPLACEME | ||
|
||
# 2. You can leave the header instructions as is, or modify them if you see fit. | ||
headerInstructions: | | ||
Please set a score to each proposal according to your preferences. | ||
You should set the highest score to your favorite option. | ||
Negative scores are allowed, only the order matters. | ||
You can tie two or more proposals if you have no preference. | ||
To abstain, keep all the propositions tied. | ||
# 3. Give a list of "candidates". Those should be answers to the subject | ||
# question, and should leave as little room to interpretation as possible. Do | ||
# not list candidates that don't have a champion, there should be a | ||
# clear plan for each candidates in the event where it wins the vote; listing | ||
# a "troll candidate" will only hurt the credibility of the voting process if | ||
# it wins and everyone realize we have to re-take the vote because it can't | ||
# happen. Don't hesitate to list very similar candidates, with however small | ||
# nuances: we are using the Condorcet method to count the votes, which lets | ||
# voters express their preference for each candidates, no matter how many | ||
# there are. | ||
candidates: | ||
- TODO | ||
- TODO | ||
|
||
# 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 | ||
# give an unfair advantage to the first option. | ||
canShuffleCandidates: true | ||
|
||
# 5. Insert here a short description of the vote objectives and link to the | ||
# issue it was discussed on to give the full context. | ||
footerInstructions: | | ||
TBD | ||
# 6. Optionally, insert a brief introduction for the vote PR, in the markdown format. | ||
prBody: | | ||
# 7. Optionally, choose an id that will be used for the branch name as well as | ||
# the vote folder name. If not supplied, a UUID will be used. | ||
path-friendly-id: null |
Oops, something went wrong.