A GitHub API client for managing tags and repository content from third-party automation systems (e.g. Jenkins).
- Create and update tags, both lightweight and annotated.
- Add, update and delete content idempotently.
- Idempotent update of git refs (e.g. fast-forward merge and mutable tags).
- GitHub-verified commits (when using a GitHub App-derived token), facilitating the enforcement of commit signing.
- Configuration defaults inferred from local context (e.g. git clone and environment).
- Completely self-contained: no external dependencies.
- A GitHub Token, preferably derived from GitHub App credentials (for verified commits), with
contents=writeandmetadata=readpermissions on target repository. In addition,workflows=writeis also needed if used to manage.github/workflowscontent.
Note: works well with vault-plugin-secrets-github!
If the current working directory is a git repository, its first GitHub remote (if there is one) is used to infer default repository owner (--owner) and name (--repo), the current branch is used to set the default branch (--branch), and resolved git config is used to set a default author for a generated Co-Authored-By commit message trailer to help distinguish between different systems sharing common GitHub App credentials (override components with --author.trailer, --user.name and --user.email, or disable with --author.trailer= or export GHUP_AUTHOR_TRAILER=). Additional commit trailers can be specified with --trailer key=value flags.
If run outside a GitHub repository, then the --owner and --repo flags are required, with --branch defaulting to main.
All configuration may be passed via environment variable rather than flag. The environment variable associated with each flag is GHUP_[UPPERCASED_FLAG_NAME], e.g. GHUP_TOKEN, GHUP_OWNER, GHUP_REPO, GHUP_BRANCH, GHUP_AUTHOR_TRAILER, etc.
In addition, various fallback environment variables are supported for better integration with Jenkins and similar CI tools: GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO, CHANGE_BRANCH, BRANCH_NAME, GIT_BRANCH, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL, etc.
The environment variable GITHUB_REPOSITORY, always set in GitHub Actions workflow context in the form <owner>/<repo>, is only used to set initial defaults for --owner and --repo, but will be overridden by local repository context and more specific configuration.
If GITHUB_REPOSITORY is set, then --branch will also default from GITHUB_HEAD_REF in pull request context, or GITHUB_REF_NAME otherwise.
For security, it is strongly recommended that the GitHub Token by passed via environment (GHUP_TOKEN or GITHUB_TOKEN) or file path (--token /path/to/token-file, --token <(gh auth token) or export GHUP_TOKEN=/path/to/token-file ghup …)
Binaries for all supported platforms and architectures are available from GitHub Releases.
Alternatively, install to $GOBIN with a local Go toolchain:
go install github.com/nexthink-oss/ghup@latestbrew install isometry/tap/ghupThe nexthink-oss/ghup/actions/setup action is available to make the ghup tool available in GitHub Actions.
The content verb is used to generate arbitrary (verified) commits via the GitHub V4 API.
An arbitrary number of content adds, removes and deletes can be committed without the need for a local signing key or for checking out the target repository.
$ ghup content --help
Manage content via the GitHub V4 API
Usage:
ghup content [flags] [<file-spec> ...]
Flags:
--create-branch create missing target branch (default true)
--pr-title string create pull request iff target branch is created and title is specified
--pr-body string pull request body
--pr-draft create pull request in draft mode
--base-branch name base branch name (default: "[remote-default-branch])"
-s, --separator string file-spec separator (default ":")
-u, --update file-spec file-spec to update
-d, --delete file-path file-path to delete
-h, --help help for content
Global Flags:
--author.trailer key key for commit author trailer (blank to disable) (default "Co-Authored-By")
-b, --branch name target branch name (default "[local-branch-or-main]")
-f, --force force action
-m, --message string message (default "Commit via API")
-o, --owner name repository owner name (default "[owner-of-first-github-remote-or-required]")
-r, --repo name repository name (default "[repo-of-first-github-remote-or-required]")
--token string GitHub Token or path/to/token-file
--trailer key=value extra key=value commit trailers (default [])
--user.email email email for commit author trailer (default "[user.email]")
--user.name name name for commit author trailer (default "[user.name]")
-v, --verbosity count verbosityEach file-spec provided as a positional argument or explicitly via the --update flag takes the form <local-file-path>[:<remote-target-path>]. Content is read from the local file <local-file-path> and written to <remote-target-path> (defaulting to <local-file-path> if not specified).
Each file-path provided to the --delete flag is a <remote-target-path>: the path to a file on the target repository:branch that should be deleted.
Unless --force is used, content that already matches the remote repository state is ignored.
Note: Due to limitations in the GitHub V4 API, when the target branch does not exist, branch creation and content push will trigger two distinct "push" events.
Update .zshrc in my dotfiles repo, adding if's missing and updating if-and-only-if changed:
$ ghup content --owner=isometry --repo=dotfiles ~/.zshrc:.zshrc -m "chore: update zshrc"
https://github.com/isometry/dotfiles/commit/15b8630c81a051c2b128c94e5796c5d9c2bc8846
$ ghup content --owner=isometry --repo=dotfiles ~/.zshrc:.zshrc -m "chore: update zshrc"
nothing to doDelete .tcshrc from my dotfiles repo:
$ ghup content --owner=isometry --repo=dotfiles --delete .tcshrc -m "chore: remove tcshrc"
https://github.com/isometry/dotfiles/commit/bf120a96c65cb482eacc3c9e27d2d0935d108eca
$ ghup content --owner=isometry --repo=dotfiles --delete .tcshrc -m "chore: remove tcshrc"
nothing to doThe tag verb is used to create lightweight or annotated tags without the need to checkout the target repository.
Note: annotated tags are the default, but only lightweight tags (i.e. --lightweight), which simply point at an existing commit, are "verified".
$ ghup tag --help
Manage tags via the GitHub V3 API
Usage:
ghup tag [flags] [<name>]
Flags:
-h, --help help for tag
-l, --lightweight force lightweight tag
--tag string tag name
Global Flags:
--author.trailer key key for commit author trailer (blank to disable) (default "Co-Authored-By")
-b, --branch name target branch name (default "[local-branch-or-main]")
-f, --force force action
-m, --message string message (default "Commit via API")
-o, --owner name repository owner name (default "[owner-of-first-github-remote-or-required]")
-r, --repo name repository name (default "[repo-of-first-github-remote-or-required]")
--token string GitHub Token or path/to/token-file
--trailer key=value extra key=value commit trailers (default [])
--user.email email email for commit author trailer (default "[user.email]")
--user.name name name for commit author trailer (default "[user.name]")
-v, --verbosity count verbosityCreate lightweight tag v1.0.0 pointing at the head of the local repository's checked out branch:
$ ghup tag v1.0.0
https://github.com/nexthink-oss/ghup/releases/tag/v1.0.0Create an annotated repo v1.0 pointed at the head of the main branch of the ghup repo owned by nexthink-oss:
$ ghup -o nexthink-oss -r ghup -b main tag v1.0 -m "Release v1.0!"
https://github.com/nexthink-oss/ghup/releases/tag/v1.0The update-ref verb is used to update an arbitrary number head or tag references to match a source reference.
The source may take the form of a partial commit hash, or of a fully- or partially-qualified reference, defaulting to a branch reference (heads/…; overrideable via --source-type=tags).
The target(s) must take the form of fully- or partially-qualified references, defaulting to tag references, defaulting to tag references (tags/…; overrideable via --target-type=heads).
The --force flag will override standard fast-forward-only protection on branch updates.
$ ghup update-ref --help
Update target refs to match source
Usage:
ghup update-ref [flags] -s <source> <target> ...
Flags:
-s, --source ref-or-commit source ref-or-commit
-S, --source-type heads|tags unqualified source ref type (default heads)
-T, --target-type heads|tags unqualified target ref type (default tags)
-h, --help help for update-ref
Global Flags:
--author.trailer key key for commit author trailer (blank to disable) (default "Co-Authored-By")
-b, --branch name target branch name (default "[local-branch-or-main]")
-f, --force force action
-m, --message string message (default "Commit via API")
-o, --owner name repository owner name (default "[owner-of-first-github-remote-or-required]")
-r, --repo name repository name (default "[repo-of-first-github-remote-or-required]")
--token string GitHub Token or path/to/token-file
--trailer key=value extra key=value commit trailers (default [])
--user.email email email for commit author trailer (default "[user.email]")
--user.name name name for commit author trailer (default "[user.name]")
-v, --verbosity count verbosityNote: the --branch, --message and trailer-related flags are not used by the ref verb.
$ ghup update-ref -s staging heads/production
{"source":{"ref":"heads/staging","sha":"206e1a484f03cd320a2125a50aa73bd8a2b045dc"},"target":[{"ref":"heads/production","updated":true,"old_sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4","sha":"206e1a484f03cd320a2125a50aa73bd8a2b045dc"}]}$ ghup update-ref -s b7ccc4d example
{"source":{"ref":"b7ccc4d","sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4"},"target":[{"ref":"tags/example","updated":true,"sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4"}]}$ ghup update-ref -s tags/v1.1.7 v1.1 v1
{"source":{"ref":"tags/v1.1.7","sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4"},"target":[{"ref":"tags/v1.1","updated":true,"sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4"},{"ref":"tags/v1","updated":true,"sha":"b7ccc4db9bc43551fd3571c260869f4c69aa2fd4"}]}In order to better validate the configuration derived from context (working directory, environment variables and global flags), the info verb is available:
$ ghup info
{
"has_token": true,
"owner": "nexthink-oss",
"repository": "ghup",
"branch": "feature/branch",
"commit": "5e1692253399bd9ea6077dba27e4cdc8a15b9720",
"clean": false,
"message": {
"headline": "Commit via API",
"body": "Co-Authored-By: Example User <[email protected]>"
}
"trailers": [
"Co-Authored-By: Example User <[email protected]>"
],
}