vouchington-tooling 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,6 +80,7 @@ vouchington install-playwright-chromium-arm64
80
80
  vouchington ghcr-package-retention example%2Fapi
81
81
  vouchington harness-admission-lane 4
82
82
  vouchington harness-assert-gates HARNESS_DISPATCH_ENABLED HARNESS_SHEPHERD_ENABLED
83
+ echo '[{"login":"octocat","type":"User"}]' | vouchington gha-collaborator-trust owner/repo --allow-bot github-actions[bot]
83
84
  vouchington nuget-central-version trusted.props candidate.props metadata.json out.props
84
85
  vouchington swift-semantic-equal BASE HEAD App.swift
85
86
  vouchington post-review
@@ -73,6 +73,10 @@ const SCRIPT_PATHS = {
73
73
  path: 'scripts/gha/harness-admission-lane.sh',
74
74
  },
75
75
  'harness-assert-gates': { command: 'bash', path: 'scripts/gha/harness-assert-gates.sh' },
76
+ 'gha-collaborator-trust': {
77
+ command: 'bash',
78
+ path: 'scripts/gha/gha-collaborator-trust.sh',
79
+ },
76
80
  };
77
81
  export function runCli(argv = process.argv) {
78
82
  const parsed = parseCli(argv);
@@ -89,7 +89,7 @@ export type ParsedCli = {
89
89
  workflowDirectories?: string[];
90
90
  actionDirectories?: string[];
91
91
  } | ParsedGhaRuntimeAudit | ParsedGhaArtifactsCleanup;
92
- export type ScriptCommand = 'gha-output' | 'gha-needs-results' | 'download-with-diagnostics' | 'download-optional-run-artifacts' | 'host-pressure-diagnostics' | 'allocate-browser-safe-ports' | 'diagnose-port-collision' | 'prepare-trivy-db' | 'check-cache-size' | 'make-shard-matrix' | 'load-runner-env' | 'clean-workspace' | 'install-github-release' | 'run-with-timeout' | 'lint-links' | 'materialize-pr-context' | 'wait-for-apt-locks' | 'install-playwright-chromium-arm64' | 'ghcr-package-retention' | 'harness-admission-lane' | 'harness-assert-gates';
92
+ export type ScriptCommand = 'gha-output' | 'gha-needs-results' | 'download-with-diagnostics' | 'download-optional-run-artifacts' | 'host-pressure-diagnostics' | 'allocate-browser-safe-ports' | 'diagnose-port-collision' | 'prepare-trivy-db' | 'check-cache-size' | 'make-shard-matrix' | 'load-runner-env' | 'clean-workspace' | 'install-github-release' | 'run-with-timeout' | 'lint-links' | 'materialize-pr-context' | 'wait-for-apt-locks' | 'install-playwright-chromium-arm64' | 'ghcr-package-retention' | 'harness-admission-lane' | 'harness-assert-gates' | 'gha-collaborator-trust';
93
93
  export declare function parseCli(argv: readonly string[]): ParsedCli;
94
94
  export declare function parseCommandHelp(command: string | undefined, rest: readonly string[]): {
95
95
  kind: 'command-help';
@@ -25,6 +25,7 @@ const SCRIPT_COMMANDS = new Set([
25
25
  'ghcr-package-retention',
26
26
  'harness-admission-lane',
27
27
  'harness-assert-gates',
28
+ 'gha-collaborator-trust',
28
29
  ]);
29
30
  export function parseCli(argv) {
30
31
  const args = argv.slice(2);
@@ -1,4 +1,4 @@
1
- export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n mcp Serve the agent-blackboard journal tools over stdio (MCP)\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\nagent-harness-config dump\nagent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...\n [--home PATH]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n [--median-threshold-floor <seconds>] Off unless set. Must be below the ceiling\n [--median-threshold-ceiling <seconds>] Default 360. Flags a five-sample median above this\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name>... | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports <count> [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --mode interactive|autonomous --source-event-id ID --work-outcome OUTCOME --coverage-status STATUS --session-id UUID --agent NAME --version VERSION --file PATH --repository OWNER/NAME [--repository OWNER/NAME ...] [--parent-session-id UUID] [--timestamp ISO8601] [--coverage-source SOURCE,...] [--dropped-count N] [--outbox-directory PATH]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard journal flush --outbox-directory PATH\nInteractive journal append requires --outbox-directory PATH.\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\nmcp\n (no args) Serve journal_append, journal_entries, outbox_status, outbox_flush,\n session_ensure, snapshot_export, and session_archive over stdio.\n Needs the optional @modelcontextprotocol/sdk peer (and zod).\n Launch it from inside the repository's git worktree.\n";
1
+ export declare const USAGE = "Usage: vouchington <command> [options]\n\nCommands:\n runner-port-policy Print or validate a runner port policy\n with-host-lock Run a command under a host-wide lock\n agent-harness-config Apply classifier-auto + sandbox keys to agent harnesses\n gha-runtime-audit Audit successful GitHub Actions job runtimes\n require-up-to-date Require HEAD to include a fetched remote branch\n gitleaks-directory-scan Scan a directory with Gitleaks\n ast-grep-examples Run AST-grep rule examples\n ast-grep-pack Print shipped unconditional AST-grep pack paths as JSON\n gha-workspace-policy Check GitHub Actions workspace safety policy\n gha-output Write a collision-safe multiline GITHUB_OUTPUT record\n gha-needs-results Fail if required GitHub Actions job results failed\n download-with-diagnostics Download a URL and report HTTP status on failure\n download-optional-run-artifacts Download optional artifacts from the current run\n host-pressure-diagnostics Print a bounded host memory/OOM/PSI snapshot\n allocate-browser-safe-ports Allocate Fetch-safe localhost ports\n diagnose-port-collision Capture bounded localhost port diagnostics\n prepare-trivy-db Download the Trivy vulnerability database\n gha-artifacts-cleanup Delete classified GitHub Actions artifacts\n http-origin Validate an optional HTTP(S) origin\n vitest-blob-manifest Stamp a vitest-blob-manifest:v1 identity file\n vitest-report-attempt Write or read a Vitest report-attempt marker\n prepare-vitest-reports Validate and select Vitest report JSON files\n pnpm-install Install a pnpm workspace with retry and release-age fail-fast\n check-cache-size Measure a path and decide whether to save a GHA cache\n make-shard-matrix Emit a [1..N] GitHub Actions shard matrix\n load-runner-env Overlay a runner env file onto GITHUB_ENV with injection guards\n clean-workspace Reset a persistent-runner workspace with a fork-PR trust gate\n install-github-release Download a checksum-verified GitHub Release binary\n run-with-timeout Run a command with GNU timeout or a Perl fallback\n lint-links Two-pass lychee: internal links fail, external warn\n materialize-pr-context Dump PR title/body/files/diff/comments and #N crawl\n wait-for-apt-locks Wait until apt/dpkg lock files are free\n install-playwright-chromium-arm64 Install Playwright Chromium from browsers.json\n ghcr-package-retention Delete old GHCR package versions past KEEP_MIN\n harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out\n harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled\n gha-collaborator-trust Classify authors as write-access collaborators or not\n nuget-central-version Validate a Directory.Packages.props PackageVersion delta\n swift-semantic-equal Compare Swift sources ignoring comments and whitespace\n post-review Post one COMMENT review from a staged payload file\n stage-review-payload Validate a review payload file into a staging directory\n retrospective-transcript Format facts from Claude-compatible, Codex, or Grok transcripts\n link-skill Link one packaged skill into an explicit consumer directory\n retrospective-facts Gather immutable facts for a retrospective\n agent-blackboard Probe and journal an Agent Blackboard deployment\n mcp Serve the agent-blackboard journal tools over stdio (MCP)\n\nOptions:\n -h, --help Show this help\n -v, --version Print the package version\n\nrunner-port-policy\n (no args) Print the shipped policy as JSON\n --file <path> Validate and print a policy file\n --reserved <port> Print true if the port is reserved\n\nwith-host-lock\n --name <family>\n [--slots <n>]\n --timeout-seconds <n>\n [--command-timeout-seconds <n>]\n [--failure-diagnostics <absolute-script>]\n [--on-acquire-timeout fail|run-unlocked]\n -- <command> [args...]\n\nagent-harness-config dump\nagent-harness-config check|apply [--global] [--repo PATH]... [--harness claude|codex|grok|cursor]...\n [--home PATH]\n\ngha-runtime-audit\n [--repository owner/name] Default GITHUB_REPOSITORY\n [--branch main]\n [--median-threshold-floor <seconds>] Off unless set. Must be below the ceiling\n [--median-threshold-ceiling <seconds>] Default 360. Flags a five-sample median above this\n --pr-workflow <name|/regex/> Repeatable\n --push-workflow <name|/regex/> Repeatable\n\nrequire-up-to-date --remote <name> --branch <name>\ngitleaks-directory-scan --config <path> [--directory <path>]\nast-grep-examples --rules <directory> --config <path>\nast-grep-pack\ngha-workspace-policy [--root <directory>] [--workflow-directory <directory>] [--action-directory <directory>]\n\ngha-output <name>\ngha-needs-results [label]\ndownload-with-diagnostics <url> <destination> [-- curl-args...]\ndownload-optional-run-artifacts (--name <name>... | --pattern <pattern>) --dir <directory>\nhost-pressure-diagnostics\nallocate-browser-safe-ports <count> [--policy path] [--forbidden-ports path]\ndiagnose-port-collision [--ports \"2200 2216\"] [--output-dir PATH]\nprepare-trivy-db\ngha-artifacts-cleanup run --run-id <id> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\ngha-artifacts-cleanup sweep --older-than-hours <n> [--keep-pattern glob] [--delete-pattern glob] [--patterns-file json]\nhttp-origin [--field NAME] [value]\nvitest-blob-manifest <suite> [reports-directory]\nvitest-report-attempt <write DIRECTORY SUITE|read ROOT>\nprepare-vitest-reports [primary-directory] [fallback-directory] [output-directory]\npnpm-install --runner-lifecycle persistent|ephemeral|ephemeral-full --install-scripts true|false\ncheck-cache-size <path> <max-bytes> <label>\nmake-shard-matrix <total>\nload-runner-env\nclean-workspace\ninstall-github-release --repo owner/name --version X --asset 'name-{platform}.tar.gz' --bin name [--tag-prefix PREFIX] [--expected-sha256 SHA256] [--no-checksum] [--checksums-asset NAME] [--version-flag FLAG] [--bin-dir DIR]\nrun-with-timeout <timeout-seconds> <kill-after-seconds> <command...>\nlint-links [--offline] [--config PATH] [--glob PATTERN] [files...]\nmaterialize-pr-context\nwait-for-apt-locks\ninstall-playwright-chromium-arm64 [name:archive...]\nghcr-package-retention <url-encoded-package>...\nharness-admission-lane <lanes>\nharness-assert-gates <gate>...\ngha-collaborator-trust <owner/repo> [--allow-bot <login>]... < authors.json\nnuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>\nswift-semantic-equal <base> <head> <file.swift>\npost-review\nstage-review-payload optional|required <source> <destination>\nretrospective-transcript [--session-id ID] [--jsonl PATH] [--projects-dir PATH] [--codex-sessions-dir PATH] [--grok-sessions-dir PATH]\nlink-skill <name> --source-root <skills-dir> --target-root <consumer-skills-dir> Link a packaged or repository-local skill\nretrospective-facts (--pr NUMBER | --branch NAME | --no-pr) [--repo OWNER/NAME] [--raw]\nagent-blackboard probe\nagent-blackboard journal append --mode interactive|autonomous --source-event-id ID --work-outcome OUTCOME --coverage-status STATUS --session-id UUID --agent NAME --version VERSION --file PATH --repository OWNER/NAME [--repository OWNER/NAME ...] [--parent-session-id UUID] [--timestamp ISO8601] [--coverage-source SOURCE,...] [--dropped-count N] [--outbox-directory PATH]\nagent-blackboard journal entries --session-id UUID\nagent-blackboard journal flush --outbox-directory PATH\nInteractive journal append requires --outbox-directory PATH.\nagent-blackboard snapshot partition --snapshot PATH --checksum SHA256 --counts '{\"sessions\":N,\"entries\":N,\"records\":N,\"bytes\":N}'\nagent-blackboard snapshot cleanup [--snapshot PATH] [--partition-directory PATH --receipt JSON]\nmcp\n (no args) Serve journal_append, journal_entries, outbox_status, outbox_flush,\n session_ensure, snapshot_export, and session_archive over stdio.\n Needs the optional @modelcontextprotocol/sdk peer (and zod).\n Launch it from inside the repository's git worktree.\n";
2
2
  export declare function printUsage(stream?: NodeJS.WritableStream): void;
3
3
  export declare function commandNames(usage?: string): string[];
4
4
  export declare function commandUsage(command: string, usage?: string): string;
@@ -37,6 +37,7 @@ Commands:
37
37
  ghcr-package-retention Delete old GHCR package versions past KEEP_MIN
38
38
  harness-admission-lane Compute a GITHUB_RUN_ID admission lane for fleet fan-out
39
39
  harness-assert-gates Fail if any named HARNESS_*_ENABLED gate is enabled
40
+ gha-collaborator-trust Classify authors as write-access collaborators or not
40
41
  nuget-central-version Validate a Directory.Packages.props PackageVersion delta
41
42
  swift-semantic-equal Compare Swift sources ignoring comments and whitespace
42
43
  post-review Post one COMMENT review from a staged payload file
@@ -111,6 +112,7 @@ install-playwright-chromium-arm64 [name:archive...]
111
112
  ghcr-package-retention <url-encoded-package>...
112
113
  harness-admission-lane <lanes>
113
114
  harness-assert-gates <gate>...
115
+ gha-collaborator-trust <owner/repo> [--allow-bot <login>]... < authors.json
114
116
  nuget-central-version <trusted-props> <candidate-props> <metadata-json> <output-props>
115
117
  swift-semantic-equal <base> <head> <file.swift>
116
118
  post-review
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vouchington-tooling",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "Vouchington CLI and extractable tooling libraries.",
5
5
  "homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
6
6
  "bugs": {
@@ -346,8 +346,8 @@
346
346
  "dependencies": {
347
347
  "@types/mdast": "4.0.4",
348
348
  "@types/unist": "3.0.3",
349
- "csv-parse": "7.0.2",
350
- "csv-stringify": "6.8.3",
349
+ "csv-parse": "7.0.3",
350
+ "csv-stringify": "6.9.0",
351
351
  "dockerfile-ast": "0.7.1",
352
352
  "picomatch": "4.0.7",
353
353
  "remark": "15.0.1",
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env bash
2
+ # Classify GitHub authors by their live permission on one repository.
3
+ #
4
+ # usage: gha-collaborator-trust.sh <owner/repo> [--allow-bot <login>]... < authors.json
5
+ # stdin: JSON array of {"login": string, "type": string} (GitHub user objects work as-is).
6
+ # stdout: {"trusted": [login...], "untrusted": [login...]}, each sorted and deduplicated.
7
+ #
8
+ # A user is trusted when the legacy `permission` field is admin or write (maintain maps to
9
+ # write). A GitHub App bot (type "Bot") is trusted only when its login is passed with
10
+ # --allow-bot, because an app with only issue or pull-request write access can still comment.
11
+ # A definitive 404 means no permission. Any other lookup failure exits non-zero so callers
12
+ # fail closed.
13
+ set -euo pipefail
14
+
15
+ usage() {
16
+ echo "usage: gha-collaborator-trust.sh <owner/repo> [--allow-bot <login>]... < authors.json" >&2
17
+ exit 2
18
+ }
19
+
20
+ [ "$#" -ge 1 ] || usage
21
+ repository="$1"
22
+ shift
23
+ [[ "$repository" =~ ^[A-Za-z0-9_-][A-Za-z0-9_.-]*/[A-Za-z0-9_-][A-Za-z0-9_.-]*$ ]] || usage
24
+
25
+ allowed_bots=$'\n'
26
+ while [ "$#" -gt 0 ]; do
27
+ if [ "$1" != "--allow-bot" ] || [ "$#" -lt 2 ] || ! [[ "$2" =~ ^[A-Za-z0-9-]+\[bot\]$ ]]; then
28
+ usage
29
+ fi
30
+ allowed_bots+="$2"$'\n'
31
+ shift 2
32
+ done
33
+
34
+ input="$(cat)"
35
+ if ! jq -e '
36
+ type == "array" and all(.[];
37
+ type == "object"
38
+ and (.login | type) == "string" and (.login | length) > 0
39
+ and ((.type | type) == "string" or .type == null))' >/dev/null 2>&1 <<<"$input"; then
40
+ echo "::error::gha-collaborator-trust: stdin must be a JSON array of {login, type}" >&2
41
+ exit 2
42
+ fi
43
+ rows="$(jq -r 'unique_by(.login) | .[] | [.login, (.type // "")] | @tsv' <<<"$input")"
44
+
45
+ results="$(mktemp)"
46
+ errors="$(mktemp)"
47
+ trap 'rm -f "$results" "$errors"' EXIT
48
+
49
+ while IFS=$'\t' read -r login type; do
50
+ [ -n "$login" ] || continue
51
+ verdict=untrusted
52
+ if [ "$type" = "Bot" ]; then
53
+ if [[ "$allowed_bots" == *$'\n'"$login"$'\n'* ]]; then
54
+ verdict=trusted
55
+ fi
56
+ elif [[ "$login" =~ ^[A-Za-z0-9-]+$ ]]; then
57
+ if permission="$(gh api "repos/$repository/collaborators/$login/permission" --jq '.permission' 2>"$errors")"; then
58
+ case "$permission" in
59
+ admin | write) verdict=trusted ;;
60
+ esac
61
+ elif ! grep -q 'HTTP 404' "$errors"; then
62
+ cat "$errors" >&2
63
+ echo "::error::gha-collaborator-trust: permission lookup failed for $login" >&2
64
+ exit 1
65
+ fi
66
+ fi
67
+ printf '%s\t%s\n' "$verdict" "$login" >>"$results"
68
+ done <<<"$rows"
69
+
70
+ jq -Rn '
71
+ [inputs | split("\t")] as $rows
72
+ | {
73
+ trusted: [$rows[] | select(.[0] == "trusted") | .[1]] | sort,
74
+ untrusted: [$rows[] | select(.[0] == "untrusted") | .[1]] | sort
75
+ }' <"$results"