mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-07 04:14:26 +00:00
remove built-in vouch, prep to replace with upstream
This commit is contained in:
14
.github/vouch/AGENTS.md
vendored
14
.github/vouch/AGENTS.md
vendored
@@ -1,14 +0,0 @@
|
||||
# Agent Development Guide
|
||||
|
||||
A file for [guiding coding agents](https://agents.md/).
|
||||
|
||||
- All commands must have a `--dry-run` option that is default on.
|
||||
- Commands that do not modify external state don't need a `--dry-run` option.
|
||||
- The order of definitions in Nu files should be:
|
||||
(1) General CLI commands (exported, sorted alphabetically)
|
||||
(2) Platform-specific CLI commands like GitHub (exported, `gh-`)
|
||||
(3) Helper commands (exported)
|
||||
(4) Helper commands (non exported)
|
||||
- Verify help output using `use <module> *; help <def>`. Everything
|
||||
must have human-friendly help output.
|
||||
- See `VOUCHED.example` for an example vouch file.
|
||||
158
.github/vouch/README.md
vendored
158
.github/vouch/README.md
vendored
@@ -1,158 +0,0 @@
|
||||
# Vouch System
|
||||
|
||||
This implements a system where users must be vouched prior to interacting
|
||||
with certain parts of the project. The implementation in this folder is generic
|
||||
and can be used by any project.
|
||||
|
||||
Going further, the vouch system also has an explicit **denouncement** feature,
|
||||
where particularly bad actors can be explicitly denounced. This blocks
|
||||
these users from interacting with the project completely but also makes
|
||||
it a public record for other projects to see and use if they so wish.
|
||||
|
||||
The vouch list is maintained in a single flat file with a purposefully
|
||||
minimal format that can be trivially parsed using standard POSIX tools and
|
||||
any programming language without any external libraries.
|
||||
|
||||
This is based on ideas I first saw in the [Pi project](https://github.com/badlogic/pi-mono).
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> This is a work-in-progress and experimental system. We're going to
|
||||
> continue to test this in Ghostty, refine it, and improve it over time.
|
||||
|
||||
## Why?
|
||||
|
||||
Open source has always worked on a system of _trust and verify_.
|
||||
|
||||
Historically, the effort required to understand a codebase, implement
|
||||
a change, and submit that change for review was high enough that it
|
||||
naturally filtered out many low quality contributions from unqualified people.
|
||||
For over 20 years of my life, this was enough for my projects as well
|
||||
as enough for most others.
|
||||
|
||||
Unfortunately, the landscape has changed particularly with the advent
|
||||
of AI tools that allow people to trivially create plausible-looking but
|
||||
extremely low-quality contributions with little to no true understanding.
|
||||
Contributors can no longer be trusted based on the minimal barrier to entry
|
||||
to simply submit a change.
|
||||
|
||||
But, open source still works on trust! And every project has a definite
|
||||
group of trusted individuals (maintainers) and a larger group of probably
|
||||
trusted individuals (active members of the community in any form). So,
|
||||
let's move to an explicit trust model where trusted individuals can vouch
|
||||
for others, and those vouched individuals can then contribute.
|
||||
|
||||
## Usage
|
||||
|
||||
The only requirement is [Nu](https://www.nushell.sh/).
|
||||
|
||||
### VOUCHED File
|
||||
|
||||
See [VOUCHED.example.td](VOUCHED.example.td) for the file format. The file is
|
||||
looked up at `VOUCHED.td` or `.github/VOUCHED.td` by default. Create an
|
||||
empty `VOUCHED.td` file.
|
||||
|
||||
Overview:
|
||||
|
||||
```
|
||||
# Comments start with #
|
||||
platform:username
|
||||
-platform:denounced-user
|
||||
-platform:denounced-user reason for denouncement
|
||||
```
|
||||
|
||||
The platform prefix (e.g., `github:`) specifies where the user identity
|
||||
comes from. The platform prefix is optional, since most projects exist
|
||||
within the realm of a single platform. All the commands below take
|
||||
`--default-platform` flags to specify what platform to assume when none
|
||||
is present.
|
||||
|
||||
### Commands
|
||||
|
||||
#### Integrated Help
|
||||
|
||||
This is Nu, so you can get help on any command:
|
||||
|
||||
```bash
|
||||
use vouch.nu *; help main
|
||||
use vouch.nu *; help main add
|
||||
use vouch.nu *; help main check
|
||||
use vouch.nu *; help main denounce
|
||||
use vouch.nu *; help main gh-check-pr
|
||||
use vouch.nu *; help main gh-manage-by-issue
|
||||
```
|
||||
|
||||
#### Local Commands
|
||||
|
||||
**Check a user's vouch status:**
|
||||
|
||||
```bash
|
||||
./vouch.nu check <username>
|
||||
```
|
||||
|
||||
Exit codes: 0 = vouched, 1 = denounced, 2 = unknown.
|
||||
|
||||
**Add a user to the vouched list:**
|
||||
|
||||
```bash
|
||||
# Dry run (default) - see what would happen
|
||||
./vouch.nu add someuser
|
||||
|
||||
# Actually add the user
|
||||
./vouch.nu add someuser --dry-run=false
|
||||
```
|
||||
|
||||
**Denounce a user:**
|
||||
|
||||
```bash
|
||||
# Dry run (default)
|
||||
./vouch.nu denounce badactor
|
||||
|
||||
# With a reason
|
||||
./vouch.nu denounce badactor --reason "Submitted AI slop"
|
||||
|
||||
# Actually denounce
|
||||
./vouch.nu denounce badactor --dry-run=false
|
||||
```
|
||||
|
||||
#### GitHub Integration
|
||||
|
||||
This requires the `GITHUB_TOKEN` environment variable to be set. If
|
||||
that isn't set and `gh` is available, we'll use the token from `gh`.
|
||||
|
||||
**Check if a PR author is vouched:**
|
||||
|
||||
```bash
|
||||
# Check PR author status
|
||||
./vouch.nu gh-check-pr 123
|
||||
|
||||
# Auto-close unvouched PRs (dry run)
|
||||
./vouch.nu gh-check-pr 123 --auto-close
|
||||
|
||||
# Actually close unvouched PRs
|
||||
./vouch.nu gh-check-pr 123 --auto-close --dry-run=false
|
||||
|
||||
# Allow unvouched users, only block denounced
|
||||
./vouch.nu gh-check-pr 123 --require-vouch=false --auto-close
|
||||
```
|
||||
|
||||
Outputs status: "skipped" (bot), "vouched", "allowed", or "closed".
|
||||
|
||||
**Manage contributor status via issue comments:**
|
||||
|
||||
```bash
|
||||
# Dry run (default)
|
||||
./vouch.nu gh-manage-by-issue 123 456789
|
||||
|
||||
# Actually perform the action
|
||||
./vouch.nu gh-manage-by-issue 123 456789 --dry-run=false
|
||||
```
|
||||
|
||||
Responds to comments:
|
||||
|
||||
- `lgtm` - vouches for the issue author
|
||||
- `denounce` - denounces the issue author
|
||||
- `denounce username` - denounces a specific user
|
||||
- `denounce username reason` - denounces with a reason
|
||||
|
||||
Only collaborators with write access can vouch or denounce.
|
||||
23
.github/vouch/VOUCHED.example.td
vendored
23
.github/vouch/VOUCHED.example.td
vendored
@@ -1,23 +0,0 @@
|
||||
# The list of vouched (or actively denounced) users for this repository.
|
||||
#
|
||||
# The high-level idea is that only vouched users can participate in
|
||||
# contributing to this project. And a denounced user is explicitly
|
||||
# blocked from contributing (issues, PRs, etc. auto-closed).
|
||||
#
|
||||
# We choose to maintain a denouncement list rather than or in addition to
|
||||
# using the platform's block features so other projects can slurp in our
|
||||
# list of denounced users if they trust us and want to adopt our prior
|
||||
# knowledge about bad actors.
|
||||
#
|
||||
# Syntax:
|
||||
# - One handle per line (without @). Sorted alphabetically.
|
||||
# - Optionally specify platform: `platform:username` (e.g., `github:mitchellh`).
|
||||
# - To denounce a user, prefix with minus: `-username` or `-platform:username`.
|
||||
# - Optionally, add details after a space following the handle.
|
||||
#
|
||||
# Maintainers can vouch for new contributors by commenting "lgtm" on an
|
||||
# issue by the author. Maintainers can denounce users by commenting
|
||||
# "denounce" or "denounce [username]" on an issue or PR.
|
||||
mitchellh
|
||||
-github:badguy
|
||||
-github:slopmaster3000 Submitted endless amounts of AI slop
|
||||
32
.github/vouch/github.nu
vendored
32
.github/vouch/github.nu
vendored
@@ -1,32 +0,0 @@
|
||||
# GitHub API utilities for Nu scripts
|
||||
|
||||
# Make a GitHub API request with proper headers
|
||||
export def api [
|
||||
method: string, # HTTP method (get, post, patch, etc.)
|
||||
endpoint: string # API endpoint (e.g., /repos/owner/repo/issues/1/comments)
|
||||
body?: record # Optional request body
|
||||
] {
|
||||
let url = $"https://api.github.com($endpoint)"
|
||||
let headers = [
|
||||
Authorization $"Bearer (get-token)"
|
||||
Accept "application/vnd.github+json"
|
||||
X-GitHub-Api-Version "2022-11-28"
|
||||
]
|
||||
|
||||
match $method {
|
||||
"get" => { http get $url --headers $headers },
|
||||
"post" => { http post $url --headers $headers $body },
|
||||
"patch" => { http patch $url --headers $headers $body },
|
||||
_ => { error make { msg: $"Unsupported HTTP method: ($method)" } }
|
||||
}
|
||||
}
|
||||
|
||||
# Get GitHub token from environment or gh CLI (cached in env)
|
||||
def get-token [] {
|
||||
if ($env.GITHUB_TOKEN? | is-not-empty) {
|
||||
return $env.GITHUB_TOKEN
|
||||
}
|
||||
|
||||
$env.GITHUB_TOKEN = (gh auth token | str trim)
|
||||
$env.GITHUB_TOKEN
|
||||
}
|
||||
606
.github/vouch/vouch.nu
vendored
606
.github/vouch/vouch.nu
vendored
@@ -1,606 +0,0 @@
|
||||
#!/usr/bin/env nu
|
||||
|
||||
use github.nu
|
||||
|
||||
# Vouch - contributor trust management.
|
||||
#
|
||||
# Environment variables required:
|
||||
#
|
||||
# GITHUB_TOKEN - GitHub API token with repo access. If this isn't
|
||||
# set then we'll attempt to read from `gh` if it exists.
|
||||
export def main [] {
|
||||
print "Usage: vouch <command>"
|
||||
print ""
|
||||
print "Local Commands:"
|
||||
print " add Add a user to the vouched contributors list"
|
||||
print " check Check a user's vouch status"
|
||||
print " denounce Denounce a user by adding them to the vouched file"
|
||||
print ""
|
||||
print "GitHub integration:"
|
||||
print " gh-check-pr Check if a PR author is a vouched contributor"
|
||||
print " gh-manage-by-issue Manage contributor status via issue comment"
|
||||
}
|
||||
|
||||
# Add a user to the vouched contributors list.
|
||||
#
|
||||
# This adds the user to the vouched list, removing any existing entry
|
||||
# (vouched or denounced) for that user first.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Preview new file contents (default)
|
||||
# ./vouch.nu add someuser
|
||||
#
|
||||
# # Write the file in-place
|
||||
# ./vouch.nu add someuser --write
|
||||
#
|
||||
# # Add with platform prefix
|
||||
# ./vouch.nu add github:someuser --write
|
||||
#
|
||||
export def "main add" [
|
||||
username: string, # Username to vouch for (supports platform:user format)
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
--vouched-file: string, # Path to vouched contributors file (default: VOUCHED.td or .github/VOUCHED.td)
|
||||
--write (-w), # Write the file in-place (default: output to stdout)
|
||||
] {
|
||||
let file = if ($vouched_file | is-empty) {
|
||||
let default = default-vouched-file
|
||||
if ($default | is-empty) {
|
||||
error make { msg: "no VOUCHED file found" }
|
||||
}
|
||||
$default
|
||||
} else {
|
||||
$vouched_file
|
||||
}
|
||||
|
||||
let content = open $file
|
||||
let lines = $content | lines
|
||||
let comments = $lines | where { |line| ($line | str starts-with "#") or ($line | str trim | is-empty) }
|
||||
let contributors = $lines
|
||||
| where { |line| not (($line | str starts-with "#") or ($line | str trim | is-empty)) }
|
||||
|
||||
let new_contributors = add-user $username $contributors --default-platform $default_platform
|
||||
let new_content = ($comments | append $new_contributors | str join "\n") + "\n"
|
||||
|
||||
if $write {
|
||||
$new_content | save -f $file
|
||||
print $"Added ($username) to vouched contributors"
|
||||
} else {
|
||||
print -n $new_content
|
||||
}
|
||||
}
|
||||
|
||||
# Manage contributor status via issue comments.
|
||||
#
|
||||
# This checks if a comment matches "lgtm" (vouch) or "denounce" (denounce),
|
||||
# verifies the commenter has write access, and updates the vouched list accordingly.
|
||||
#
|
||||
# For denounce, the comment can be:
|
||||
# - "denounce" - denounces the issue author
|
||||
# - "denounce username" - denounces the specified user
|
||||
# - "denounce username reason" - denounces with a reason
|
||||
#
|
||||
# Outputs a status to stdout: "vouched", "denounced", or "unchanged"
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Dry run (default) - see what would happen
|
||||
# ./vouch.nu gh-manage-by-issue 123 456789
|
||||
#
|
||||
# # Actually perform the action
|
||||
# ./vouch.nu gh-manage-by-issue 123 456789 --dry-run=false
|
||||
#
|
||||
export def "main gh-manage-by-issue" [
|
||||
issue_id: int, # GitHub issue number
|
||||
comment_id: int, # GitHub comment ID
|
||||
--repo (-R): string = "ghostty-org/ghostty", # Repository in "owner/repo" format
|
||||
--vouched-file: string, # Path to vouched contributors file (default: VOUCHED.td or .github/VOUCHED.td)
|
||||
--allow-vouch = true, # Enable "lgtm" handling to vouch for contributors
|
||||
--allow-denounce = true, # Enable "denounce" handling to denounce users
|
||||
--dry-run = true, # Print what would happen without making changes
|
||||
] {
|
||||
let file = if ($vouched_file | is-empty) {
|
||||
let default = default-vouched-file
|
||||
if ($default | is-empty) {
|
||||
error make { msg: "no VOUCHED file found" }
|
||||
}
|
||||
$default
|
||||
} else {
|
||||
$vouched_file
|
||||
}
|
||||
|
||||
# Fetch issue and comment data from GitHub API
|
||||
let owner = ($repo | split row "/" | first)
|
||||
let repo_name = ($repo | split row "/" | last)
|
||||
let issue_data = github api "get" $"/repos/($owner)/($repo_name)/issues/($issue_id)"
|
||||
let comment_data = github api "get" $"/repos/($owner)/($repo_name)/issues/comments/($comment_id)"
|
||||
|
||||
let issue_author = $issue_data.user.login
|
||||
let commenter = $comment_data.user.login
|
||||
let comment_body = ($comment_data.body | default "" | str trim)
|
||||
|
||||
# Determine action type
|
||||
let is_lgtm = $allow_vouch and ($comment_body | parse -r '(?i)^\s*lgtm\b' | is-not-empty)
|
||||
let denounce_match = if $allow_denounce {
|
||||
$comment_body | parse -r '(?i)^\s*denounce(?:\s+(\S+))?(?:\s+(.+))?$'
|
||||
} else {
|
||||
[]
|
||||
}
|
||||
let is_denounce = ($denounce_match | is-not-empty)
|
||||
|
||||
if not $is_lgtm and not $is_denounce {
|
||||
print "Comment does not match any enabled action"
|
||||
print "unchanged"
|
||||
return
|
||||
}
|
||||
|
||||
# Check if commenter has write access
|
||||
let permission = try {
|
||||
github api "get" $"/repos/($owner)/($repo_name)/collaborators/($commenter)/permission" | get permission
|
||||
} catch {
|
||||
print $"($commenter) does not have collaborator access"
|
||||
print "unchanged"
|
||||
return
|
||||
}
|
||||
|
||||
if not ($permission in ["admin", "write"]) {
|
||||
print $"($commenter) does not have write access"
|
||||
print "unchanged"
|
||||
return
|
||||
}
|
||||
|
||||
let lines = open-vouched-file $file
|
||||
|
||||
if $is_lgtm {
|
||||
let status = check-user $issue_author $lines --default-platform github
|
||||
if $status == "vouched" {
|
||||
print $"($issue_author) is already vouched"
|
||||
|
||||
if not $dry_run {
|
||||
github api "post" $"/repos/($owner)/($repo_name)/issues/($issue_id)/comments" {
|
||||
body: $"@($issue_author) is already in the vouched contributors list."
|
||||
}
|
||||
} else {
|
||||
print "(dry-run) Would post 'already vouched' comment"
|
||||
}
|
||||
|
||||
print "unchanged"
|
||||
return
|
||||
}
|
||||
|
||||
if $dry_run {
|
||||
print $"(dry-run) Would add ($issue_author) to ($file)"
|
||||
print "vouched"
|
||||
return
|
||||
}
|
||||
|
||||
let new_lines = add-user $issue_author $lines --default-platform github
|
||||
let new_content = ($new_lines | str join "\n") + "\n"
|
||||
$new_content | save -f $file
|
||||
|
||||
print $"Added ($issue_author) to vouched contributors"
|
||||
print "vouched"
|
||||
return
|
||||
}
|
||||
|
||||
if $is_denounce {
|
||||
let match = $denounce_match | first
|
||||
let target_user = if ($match.capture0? | default "" | is-empty) {
|
||||
$issue_author
|
||||
} else {
|
||||
$match.capture0
|
||||
}
|
||||
let reason = $match.capture1? | default ""
|
||||
|
||||
let status = check-user $target_user $lines --default-platform github
|
||||
if $status == "denounced" {
|
||||
print $"($target_user) is already denounced"
|
||||
print "unchanged"
|
||||
return
|
||||
}
|
||||
|
||||
if $dry_run {
|
||||
let entry = if ($reason | is-empty) { $"-($target_user)" } else { $"-($target_user) ($reason)" }
|
||||
print $"(dry-run) Would add ($entry) to ($file)"
|
||||
print "denounced"
|
||||
return
|
||||
}
|
||||
|
||||
let new_lines = denounce-user $target_user $reason $lines --default-platform github
|
||||
let new_content = ($new_lines | str join "\n") + "\n"
|
||||
$new_content | save -f $file
|
||||
|
||||
print $"Denounced ($target_user)"
|
||||
print "denounced"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
# Denounce a user by adding them to the VOUCHED file with a minus prefix.
|
||||
#
|
||||
# This removes any existing entry for the user and adds them as denounced.
|
||||
# An optional reason can be provided which will be added after the username.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Preview new file contents (default)
|
||||
# ./vouch.nu denounce badactor
|
||||
#
|
||||
# # Denounce with a reason
|
||||
# ./vouch.nu denounce badactor --reason "Submitted AI slop"
|
||||
#
|
||||
# # Write the file in-place
|
||||
# ./vouch.nu denounce badactor --write
|
||||
#
|
||||
# # Denounce with platform prefix
|
||||
# ./vouch.nu denounce github:badactor --write
|
||||
#
|
||||
export def "main denounce" [
|
||||
username: string, # Username to denounce (supports platform:user format)
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
--reason: string, # Optional reason for denouncement
|
||||
--vouched-file: string, # Path to vouched contributors file (default: VOUCHED.td or .github/VOUCHED.td)
|
||||
--write (-w), # Write the file in-place (default: output to stdout)
|
||||
] {
|
||||
let file = if ($vouched_file | is-empty) {
|
||||
let default = default-vouched-file
|
||||
if ($default | is-empty) {
|
||||
error make { msg: "no VOUCHED file found" }
|
||||
}
|
||||
$default
|
||||
} else {
|
||||
$vouched_file
|
||||
}
|
||||
|
||||
let lines = open-vouched-file $file
|
||||
let new_lines = denounce-user $username $reason $lines --default-platform $default_platform
|
||||
let new_content = ($new_lines | str join "\n") + "\n"
|
||||
|
||||
if $write {
|
||||
$new_content | save -f $file
|
||||
print $"Denounced ($username)"
|
||||
} else {
|
||||
print -n $new_content
|
||||
}
|
||||
}
|
||||
|
||||
# Check a user's vouch status.
|
||||
#
|
||||
# Checks if a user is vouched or denounced (prefixed with -) in a local VOUCHED file.
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 - vouched
|
||||
# 1 - denounced
|
||||
# 2 - unknown
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# ./vouch.nu check someuser
|
||||
# ./vouch.nu check github:someuser
|
||||
# ./vouch.nu check someuser --vouched-file path/to/VOUCHED
|
||||
# ./vouch.nu check someuser --default-platform github
|
||||
#
|
||||
export def "main check" [
|
||||
username: string, # Username to check (supports platform:user format)
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
--vouched-file: string, # Path to vouched contributors file (default: VOUCHED.td or .github/VOUCHED.td)
|
||||
] {
|
||||
let lines = try {
|
||||
open-vouched-file $vouched_file
|
||||
} catch {
|
||||
print "error: no VOUCHED file found"
|
||||
exit 1
|
||||
}
|
||||
|
||||
let status = check-user $username $lines --default-platform $default_platform
|
||||
print $status
|
||||
match $status {
|
||||
"vouched" => { exit 0 }
|
||||
"denounced" => { exit 1 }
|
||||
_ => { exit 2 }
|
||||
}
|
||||
}
|
||||
|
||||
# Check if a PR author is a vouched contributor.
|
||||
#
|
||||
# Checks if a PR author is a bot, collaborator with write access,
|
||||
# or in the vouched contributors list. If not vouched and --auto-close is set,
|
||||
# it closes the PR with a comment explaining the process.
|
||||
#
|
||||
# Outputs a status to stdout: "skipped", "vouched", "allowed", or "closed"
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Check if PR author is vouched
|
||||
# ./vouch.nu gh-check-pr 123
|
||||
#
|
||||
# # Dry run with auto-close - see what would happen
|
||||
# ./vouch.nu gh-check-pr 123 --auto-close
|
||||
#
|
||||
# # Actually close an unvouched PR
|
||||
# ./vouch.nu gh-check-pr 123 --auto-close --dry-run=false
|
||||
#
|
||||
# # Allow unvouched users but still block denounced users
|
||||
# ./vouch.nu gh-check-pr 123 --require-vouch=false --auto-close
|
||||
#
|
||||
export def "main gh-check-pr" [
|
||||
pr_number: int, # GitHub pull request number
|
||||
--repo (-R): string = "ghostty-org/ghostty", # Repository in "owner/repo" format
|
||||
--vouched-file: string = ".github/VOUCHED.td", # Path to vouched contributors file
|
||||
--require-vouch = true, # Require users to be vouched; if false, only denounced users are blocked
|
||||
--auto-close = false, # Close unvouched PRs with a comment
|
||||
--dry-run = true, # Print what would happen without making changes
|
||||
] {
|
||||
let owner = ($repo | split row "/" | first)
|
||||
let repo_name = ($repo | split row "/" | last)
|
||||
|
||||
# Fetch PR data from GitHub API
|
||||
let pr_data = github api "get" $"/repos/($owner)/($repo_name)/pulls/($pr_number)"
|
||||
let pr_author = $pr_data.user.login
|
||||
let default_branch = $pr_data.base.repo.default_branch
|
||||
|
||||
# Skip bots
|
||||
if ($pr_author | str ends-with "[bot]") or ($pr_author == "dependabot[bot]") {
|
||||
print $"Skipping bot: ($pr_author)"
|
||||
print "skipped"
|
||||
return
|
||||
}
|
||||
|
||||
# Check if user is a collaborator with write access
|
||||
let permission = try {
|
||||
github api "get" $"/repos/($owner)/($repo_name)/collaborators/($pr_author)/permission" | get permission
|
||||
} catch {
|
||||
""
|
||||
}
|
||||
|
||||
if ($permission in ["admin", "write"]) {
|
||||
print $"($pr_author) is a collaborator with ($permission) access"
|
||||
print "vouched"
|
||||
return
|
||||
}
|
||||
|
||||
# Fetch vouched contributors list from default branch
|
||||
let file_data = github api "get" $"/repos/($owner)/($repo_name)/contents/($vouched_file)?ref=($default_branch)"
|
||||
let content = $file_data.content | decode base64 | decode utf-8
|
||||
let lines = $content | lines
|
||||
let status = check-user $pr_author $lines --default-platform github
|
||||
|
||||
if $status == "vouched" {
|
||||
print $"($pr_author) is in the vouched contributors list"
|
||||
print "vouched"
|
||||
return
|
||||
}
|
||||
|
||||
if $status == "denounced" {
|
||||
print $"($pr_author) is denounced"
|
||||
|
||||
if not $auto_close {
|
||||
print "closed"
|
||||
return
|
||||
}
|
||||
|
||||
print "Closing PR"
|
||||
|
||||
let message = "This PR has been automatically closed because the author has been denounced."
|
||||
|
||||
if $dry_run {
|
||||
print "(dry-run) Would post comment and close PR"
|
||||
print "closed"
|
||||
return
|
||||
}
|
||||
|
||||
github api "post" $"/repos/($owner)/($repo_name)/issues/($pr_number)/comments" {
|
||||
body: $message
|
||||
}
|
||||
|
||||
github api "patch" $"/repos/($owner)/($repo_name)/pulls/($pr_number)" {
|
||||
state: "closed"
|
||||
}
|
||||
|
||||
print "closed"
|
||||
return
|
||||
}
|
||||
|
||||
# Unknown - not vouched
|
||||
print $"($pr_author) is not vouched"
|
||||
|
||||
if not $require_vouch {
|
||||
print $"($pr_author) is allowed (vouch not required)"
|
||||
print "allowed"
|
||||
return
|
||||
}
|
||||
|
||||
if not $auto_close {
|
||||
print "closed"
|
||||
return
|
||||
}
|
||||
|
||||
print "Closing PR"
|
||||
|
||||
let message = $"Hi @($pr_author), thanks for your interest in contributing!
|
||||
|
||||
We ask new contributors to open an issue first before submitting a PR. This helps us discuss the approach and avoid wasted effort.
|
||||
|
||||
**Next steps:**
|
||||
1. Open an issue describing what you want to change and why \(keep it concise, write in your human voice, AI slop will be closed\)
|
||||
2. Once a maintainer vouches for you with `lgtm`, you'll be added to the vouched contributors list
|
||||
3. Then you can submit your PR
|
||||
|
||||
This PR will be closed automatically. See https://github.com/($owner)/($repo_name)/blob/($default_branch)/CONTRIBUTING.md for more details."
|
||||
|
||||
if $dry_run {
|
||||
print "(dry-run) Would post comment and close PR"
|
||||
print "closed"
|
||||
return
|
||||
}
|
||||
|
||||
github api "post" $"/repos/($owner)/($repo_name)/issues/($pr_number)/comments" {
|
||||
body: $message
|
||||
}
|
||||
|
||||
github api "patch" $"/repos/($owner)/($repo_name)/pulls/($pr_number)" {
|
||||
state: "closed"
|
||||
}
|
||||
|
||||
print "closed"
|
||||
}
|
||||
|
||||
# Check a user's status in contributor lines.
|
||||
#
|
||||
# Filters out comments and blank lines before checking.
|
||||
# Supports platform:username format (e.g., github:mitchellh).
|
||||
# Returns "vouched", "denounced", or "unknown".
|
||||
export def check-user [
|
||||
username: string, # Username to check (supports platform:user format)
|
||||
lines: list<string>, # Lines from the vouched file
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
] {
|
||||
let contributors = $lines
|
||||
| where { |line| not (($line | str starts-with "#") or ($line | str trim | is-empty)) }
|
||||
|
||||
let parsed_input = parse-handle $username
|
||||
let input_user = $parsed_input.username
|
||||
let input_platform = $parsed_input.platform
|
||||
let default_platform_lower = ($default_platform | str downcase)
|
||||
|
||||
for line in $contributors {
|
||||
let handle = ($line | str trim | split row " " | first)
|
||||
|
||||
let is_denounced = ($handle | str starts-with "-")
|
||||
let entry = if $is_denounced { $handle | str substring 1.. } else { $handle }
|
||||
|
||||
# Parse platform:username or just username
|
||||
let parsed = parse-handle $entry
|
||||
let entry_platform = if ($parsed.platform | is-empty) { $default_platform_lower } else { $parsed.platform }
|
||||
let entry_user = $parsed.username
|
||||
|
||||
# Determine platform to match against
|
||||
let check_platform = if ($input_platform | is-empty) { $default_platform_lower } else { $input_platform }
|
||||
|
||||
# Match if usernames match and platforms match (or either is empty)
|
||||
let platform_matches = ($check_platform | is-empty) or ($entry_platform | is-empty) or ($entry_platform == $check_platform)
|
||||
|
||||
if ($entry_user == $input_user) and $platform_matches {
|
||||
if $is_denounced {
|
||||
return "denounced"
|
||||
} else {
|
||||
return "vouched"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"unknown"
|
||||
}
|
||||
|
||||
# Add a user to the contributor lines, removing any existing entry first.
|
||||
# Comments and blank lines are ignored and preserved.
|
||||
#
|
||||
# Supports platform:username format (e.g., github:mitchellh).
|
||||
#
|
||||
# Returns the updated lines with the user added and sorted.
|
||||
export def add-user [
|
||||
username: string, # Username to add (supports platform:user format)
|
||||
lines: list<string>, # Lines from the vouched file
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
] {
|
||||
let filtered = remove-user $username $lines --default-platform $default_platform
|
||||
$filtered | append $username | sort -i
|
||||
}
|
||||
|
||||
# Denounce a user in the contributor lines, removing any existing entry first.
|
||||
#
|
||||
# Supports platform:username format (e.g., github:mitchellh).
|
||||
# Returns the updated lines with the user added as denounced and sorted.
|
||||
export def denounce-user [
|
||||
username: string, # Username to denounce (supports platform:user format)
|
||||
reason: string, # Reason for denouncement (can be empty)
|
||||
lines: list<string>, # Lines from the vouched file
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
] {
|
||||
let filtered = remove-user $username $lines --default-platform $default_platform
|
||||
let entry = if ($reason | is-empty) { $"-($username)" } else { $"-($username) ($reason)" }
|
||||
$filtered | append $entry | sort -i
|
||||
}
|
||||
|
||||
# Remove a user from the contributor lines (whether vouched or denounced).
|
||||
# Comments and blank lines are ignored (passed through unchanged).
|
||||
#
|
||||
# Supports platform:username format (e.g., github:mitchellh).
|
||||
# Returns the filtered lines after removal.
|
||||
export def remove-user [
|
||||
username: string, # Username to remove (supports platform:user format)
|
||||
lines: list<string>, # Lines from the vouched file
|
||||
--default-platform: string = "", # Assumed platform for entries without explicit platform
|
||||
] {
|
||||
let parsed_input = parse-handle $username
|
||||
let input_user = $parsed_input.username
|
||||
let input_platform = $parsed_input.platform
|
||||
let default_platform_lower = ($default_platform | str downcase)
|
||||
|
||||
$lines | where { |line|
|
||||
# Pass through comments and blank lines
|
||||
if ($line | str starts-with "#") or ($line | str trim | is-empty) {
|
||||
return true
|
||||
}
|
||||
|
||||
let handle = ($line | split row " " | first)
|
||||
let entry = if ($handle | str starts-with "-") {
|
||||
$handle | str substring 1..
|
||||
} else {
|
||||
$handle
|
||||
}
|
||||
|
||||
let parsed = parse-handle $entry
|
||||
let entry_platform = if ($parsed.platform | is-empty) { $default_platform_lower } else { $parsed.platform }
|
||||
let entry_user = $parsed.username
|
||||
|
||||
# Determine platform to match against
|
||||
let check_platform = if ($input_platform | is-empty) { $default_platform_lower } else { $input_platform }
|
||||
|
||||
# Keep if username doesn't match OR platforms don't match (when both have platforms)
|
||||
let platform_matches = ($check_platform | is-empty) or ($entry_platform | is-empty) or ($entry_platform == $check_platform)
|
||||
not (($entry_user == $input_user) and $platform_matches)
|
||||
}
|
||||
}
|
||||
|
||||
# Find the default VOUCHED file by checking common locations.
|
||||
#
|
||||
# Checks for VOUCHED.td in the current directory first, then .github/VOUCHED.td.
|
||||
# Returns null if neither exists.
|
||||
def default-vouched-file [] {
|
||||
if ("VOUCHED.td" | path exists) {
|
||||
"VOUCHED.td"
|
||||
} else if (".github/VOUCHED.td" | path exists) {
|
||||
".github/VOUCHED.td"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
# Open a vouched file and return all lines.
|
||||
def open-vouched-file [vouched_file?: path] {
|
||||
let file = if ($vouched_file | is-empty) {
|
||||
let default = default-vouched-file
|
||||
if ($default | is-empty) {
|
||||
error make { msg: "no VOUCHED file found" }
|
||||
}
|
||||
$default
|
||||
} else {
|
||||
$vouched_file
|
||||
}
|
||||
|
||||
open $file | lines
|
||||
}
|
||||
|
||||
# Parse a handle into platform and username components.
|
||||
#
|
||||
# Handles format: "platform:username" or just "username"
|
||||
# Returns a record with {platform: string, username: string}
|
||||
def parse-handle [handle: string] {
|
||||
let parts = $handle | str downcase | split row ":"
|
||||
if ($parts | length) >= 2 {
|
||||
{platform: ($parts | first), username: ($parts | skip 1 | str join ":")}
|
||||
} else {
|
||||
{platform: "", username: ($parts | first)}
|
||||
}
|
||||
}
|
||||
64
.github/workflows/vouch-issue-comment.yml
vendored
64
.github/workflows/vouch-issue-comment.yml
vendored
@@ -1,64 +0,0 @@
|
||||
name: Vouch Issue Comment
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
vouch:
|
||||
if: ${{ !github.event.issue.pull_request }}
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
determinate: true
|
||||
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Manage contributor
|
||||
id: update
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
status=$(nix develop -c nu .github/vouch/vouch.nu gh-manage-by-issue \
|
||||
-R ${{ github.repository }} \
|
||||
${{ github.event.issue.number }} \
|
||||
${{ github.event.comment.id }} \
|
||||
--dry-run=false \
|
||||
| tail -1)
|
||||
echo "status=$status" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Commit and push
|
||||
if: steps.update.outputs.status != 'unchanged' && steps.update.outputs.status != ''
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add .github/VOUCHED
|
||||
git diff --staged --quiet || git commit -m "chore: update VOUCHED for ${{ github.event.issue.user.login }}"
|
||||
git push
|
||||
|
||||
- name: Comment on vouch
|
||||
if: steps.update.outputs.status == 'vouched'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh issue comment ${{ github.event.issue.number }} \
|
||||
--body "@${{ github.event.issue.user.login }} has been vouched for and added to the contributors list. You can now submit PRs. Thanks for contributing!"
|
||||
|
||||
- name: Comment on denounce
|
||||
if: steps.update.outputs.status == 'denounced'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh issue comment ${{ github.event.issue.number }} \
|
||||
--body "@${{ github.event.issue.user.login }} has been denounced from this project. Bye, Felicia!"
|
||||
57
.github/workflows/vouch-pr-comment.yml
vendored
57
.github/workflows/vouch-pr-comment.yml
vendored
@@ -1,57 +0,0 @@
|
||||
name: Vouch PR Comment
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
vouch:
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
determinate: true
|
||||
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Manage contributor
|
||||
id: update
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
status=$(nix develop -c nu .github/vouch/vouch.nu gh-manage-by-issue \
|
||||
-R ${{ github.repository }} \
|
||||
${{ github.event.issue.number }} \
|
||||
${{ github.event.comment.id }} \
|
||||
--allow-vouch=false \
|
||||
--dry-run=false \
|
||||
| tail -1)
|
||||
echo "status=$status" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Commit and push
|
||||
if: steps.update.outputs.status != 'unchanged' && steps.update.outputs.status != ''
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add .github/VOUCHED
|
||||
git diff --staged --quiet || git commit -m "chore: update VOUCHED for ${{ github.event.issue.user.login }}"
|
||||
git push
|
||||
|
||||
- name: Comment on denounce
|
||||
if: steps.update.outputs.status == 'denounced'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr comment ${{ github.event.issue.number }} \
|
||||
--body "@${{ github.event.issue.user.login }} has been denounced and will not be able to submit PRs."
|
||||
35
.github/workflows/vouch-pr-gate.yml
vendored
35
.github/workflows/vouch-pr-gate.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Vouch PR Gate
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
check-contributor:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||
with:
|
||||
ref: ${{ github.event.repository.default_branch }}
|
||||
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
with:
|
||||
determinate: true
|
||||
- uses: cachix/cachix-action@3ba601ff5bbb07c7220846facfa2cd81eeee15a1 # v16
|
||||
with:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Check if contributor is vouched
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
nix develop -c nu .github/vouch/vouch.nu gh-check-pr \
|
||||
-R ${{ github.repository }} \
|
||||
${{ github.event.pull_request.number }} \
|
||||
--dry-run=false
|
||||
Reference in New Issue
Block a user