A collection of zero-dependency Node.js CLI utilities for managing GitHub Enterprise Server (GHES) repositories and secrets. All scripts require Node.js 18+ and git/gh on PATH.
| Script | Purpose | Docs |
|---|---|---|
| clone-org-repos | Bulk clone all non-archived repos from a GHES org | clone-org-repos.md |
| sync-repos | Keep local repos in sync with remote | sync-repos.md |
| create-org-secret | Set a GitHub org secret from a local env var | create-org-secret.md |
| backfill-release-notes | Retroactively generate release notes for GitHub releases | backfill-release-notes.md |
| search-assigned-issues | Search open issues assigned to a user across a GHES org | search-assigned-issues.md |
| report-failed-workflows | Report still-broken GitHub Actions workflows across an org | report-failed-workflows.md |
| create-issue | Create a GitHub issue from a markdown file | create-issue.md |
Purpose: Fetches all repositories from a GHES organization via the REST API and clones every non-archived repo to a local directory.
GHES_TOKEN=<pat> node clone-org-repos.js <ghes-host> <org> [clone-dir]- Pagination — Loops through
/api/v3/orgs/{org}/reposwithper_page=100, incrementingpageuntil an empty batch is returned. Handles orgs with any number of repos. - Archive filtering —
allRepos.filter((r) => !r.archived)removes archived repos before cloning. - Idempotency —
fs.existsSync(repoDir)skips repos that already exist on disk, making re-runs safe. - Auth via URL — Embeds the token in the clone URL (
https://<token>@host/...) so no interactive auth prompt is needed. - Error isolation — Each clone is wrapped in try/catch; a single repo failure doesn't halt the entire run.
Purpose: Iterates over all git repos in a directory, pulls the latest main/master from remote, and merges it into whatever branch is currently checked out — all without losing uncommitted work.
node sync-repos.js [repos-dir]- Two-tier git helpers —
git()throws on failure (used when failure is unexpected), whilegitSafe()returns a result object (used when failure is a valid outcome like a merge conflict). - Default branch detection — Tries
mainfirst, falls back tomasterviarev-parse --verify. Skips repos where neither exists. - Stash management — Uses
git stash push -u(includes untracked files) with a labeled message. Thefinallyblock guarantees stash pop is always attempted, even if pull or merge fails. - Pull strategy — Tries
--ff-onlyfirst (safest). Falls back to--rebaseif fast-forward isn't possible. Aborts and reports if both fail. - Non-destructive merge — On conflict, immediately runs
merge --abortso the branch is left exactly as it was. - Safety checkout — A final check ensures the repo ends up on the original branch regardless of what happened.
| Status | Meaning |
|---|---|
synced |
Default branch pulled and merged successfully |
skipped |
Not a git repo, no default branch, or unreadable |
pull-failed |
Could not pull the default branch from remote |
merge-conflict |
Default branch could not be merged cleanly (aborted) |
stash-conflict |
Stashed changes could not be re-applied (still in stash) |
Purpose: Reads a value from a local environment variable and creates/updates a GitHub organization-level Actions secret via the gh CLI.
GH_HOST=github.tools.sap node create-org-secret.js <org> <secret-name> <env-var> [visibility] [repos]- Visibility modes — Supports
private(default),all, andselected. Whenselected, a comma-separated repo list is required. - GHES support — If
GH_HOSTis set, it's forwarded into the child process environment soghtargets the correct instance. Without it, the script defaults togithub.com. - Secure value passing — The secret value is piped via stdin, never as a CLI argument. This prevents it from appearing in
psoutput or shell history. - Encryption — Delegated to
gh, which uses libsodium/NaCl sealed boxes as required by the GitHub API.
Purpose: Finds GitHub releases with empty bodies, generates structured release notes via the GitHub API, and updates them. Excludes releases matching a configurable tag prefix (default: helm-).
GH_HOST=github.tools.sap node backfill-release-notes.js <owner/repo> [--apply] [--exclude-prefix=helm-]- Tag filtering —
r.tag_name.startsWith(excludePrefix)removes helm-chart and similar releases before processing. - Chronological ordering — Releases are sorted by
created_atto correctly identify each release's predecessor for the generate-notes API. - Dry-run by default — Previews what would be updated. The
--applyflag is required to actually modify releases, preventing accidental changes. - Auto-generated notes — Uses
POST /repos/{owner}/{repo}/releases/generate-noteswithtag_nameandprevious_tag_nameto produce notes with PR links and author attribution. - Secure body passing — The release notes body is piped via stdin using
--input -to avoid shell escaping issues with markdown content. - Error isolation — Each release update is wrapped in try/catch; a single failure doesn't halt the entire run.
Purpose: Searches for open issues assigned to a user across all repositories in a GitHub organization, filtering out issues labeled as done, completed, or archived.
GH_HOST=github.tools.sap node search-assigned-issues.js <org> [assignee] [--exclude-label=done,completed,archived] [--output]- Search API — Uses
GET /search/issueswith queryorg:<org> assignee:<user> is:issue state:open -label:<excluded>to find matching issues across all repos in a single call. - Auto-detect user — If no assignee is provided, resolves the current
ghuser viagh api user. - Label exclusion — Excludes issues with configurable labels (default:
done,completed,archived). Archived repos are excluded automatically by the search API. - Grouped output — Results are grouped by repository and sorted by repo name then issue number for easy scanning.
- Markdown export —
--outputwrites results to a.mdfile with clickable issue links, dates, and labels for personal tracking.
Purpose: Scans all repos in a GitHub org for failed Actions workflow runs within a lookback window, filters out workflows that have since recovered (newer successful run), and writes a markdown report with failure analysis.
GH_HOST=github.tools.sap node report-failed-workflows.js <org> [--hours=24] [--output=failed-workflows.md]- Lookback window — Queries
repos/{repo}/actions/runs?status=failure&created=>={since}wheresinceis computed from--hours(default 24). Manual pagination handles the nested{ workflow_runs: [] }response. - Still-broken filter — Groups failures by workflow file path, picks the most recent failure per workflow, then checks
repos/{repo}/actions/workflows/{id}/runs?status=success&per_page=1to see if a newer success exists. Only workflows with no newer success are reported. - Failure analysis — Fetches job details via the Jobs API, extracts failed job and step names to produce a short analysis like
Job "build" failed at step "Run tests". - GHES support — Uses
GH_HOSTenvironment variable, consistent with other scripts. Works on both GHES and github.com. - Error isolation — Per-repo try/catch ensures one broken or Actions-disabled repo doesn't halt the scan. Skipped repos are logged and counted in the summary.
Purpose: Reads a .md file, extracts the first # Heading as the issue title and the rest as the body, then creates a GitHub issue. Dry-run by default.
GH_HOST=github.tools.sap node create-issue.js <org> <repo> --source=issue.md [--assignee=user] [--label=bug,urgent] [--apply]- Markdown parsing — The first
# Headingline becomes the issue title; everything after becomes the body. An error is raised if no heading is found. - Dry-run by default — Previews the issue (title, body, labels, assignee) without creating it. The
--applyflag is required to actually create the issue, preventing accidental submissions. - Template included — A
template.mdfile provides a starting point with Description, Steps to Reproduce, Expected/Actual Behavior sections. - Label support —
--label=bug,urgentattaches comma-separated labels to the issue. - Auto-assignee — Defaults to the current
ghuser viaGET /userif--assigneeis omitted. - Secure body passing — The issue body is piped via stdin using
-F body=@-to avoid shell escaping issues with markdown content. - GHES support — Uses
GH_HOSTenvironment variable, consistent with other scripts.
| Pattern | Detail |
|---|---|
| Zero dependencies | Only Node.js built-ins (child_process, fs, path) + fetch (Node 18+) |
| Env vars for secrets | PATs/tokens are always read from environment, never from CLI args |
| Idempotent | Safe to re-run — clone skips existing dirs, sync stashes/restores, secret set is an upsert |
| Graceful failure | Individual item failures don't halt the batch; summaries report what needs attention |
| GHES-first | Designed for GitHub Enterprise Server with github.com as a fallback |
- helper-scripts-k8s — Kubernetes cluster diagnostics and bulk operations (Bash)






