# commit-headless **Repository Path**: mirrors_DataDog/commit-headless ## Basic Information - **Project Name**: commit-headless - **Description**: A binary tool and GitHub action for creating signed commits from headless workflows - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-08 - **Last Updated**: 2025-10-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # commit-headless A binary tool and GitHub Action for creating signed commits from headless workflows For the Action, please see [the action branch][action-branch] and the associated `action/` release tags. For example usage, see [Examples](#examples). `commit-headless` is focused on turning local commits into signed commits on the remote. It does this using the GitHub API, more specifically the [createCommitOnBranch][mutation] mutation. When commits are created using the API (instead of via `git push`), the commits will be signed and verified by GitHub on behalf of the owner of the credentials used to access the API. *NOTE:* One limitation of creating commits using the GraphQL API is that it does not expose any mechanism to set or change file modes. It merely takes the file contents, base64 encoded. This means that if you rely on `commit-headless` to push binary files (or executable scripts), the file in the resulting commit will not retain that executable bit. [mutation]: https://docs.github.com/en/graphql/reference/mutations#createcommitonbranch [action-branch]: https://github.com/DataDog/commit-headless/tree/action ## Usage There are two ways to create signed headless commits with this tool: `push` and `commit`. Both of these commands take a target owner/repository (eg, `--target/-T DataDog/commit-headless`) and remote branch name (eg, `--branch bot-branch`) as required flags and expect to find a GitHub token in one of the following environment variables: - HEADLESS_TOKEN - GITHUB_TOKEN - GH_TOKEN In normal usage, `commit-headless` will print *only* the reference to the last commit created on the remote, allowing this to easily be captured in a script. More on the specifics for each command below. See also: `commit-headless --help` ### Specifying the expected head commit When creating remote commits via API, `commit-headless` must specify the "expected head sha" of the remote branch. By default, `commit-headless` will query the GitHub API to get the *current* HEAD commit of the remote branch and use that as the "expected head sha". This introduces some risk, especially for active branches or long running jobs, as a new commit introduced after the job starts will not be considered when pushing the new commits. The commit itself will not be replaced, but the changes it introduces may be lost. For example, consider an auto-formatting job. It runs `gofmt` over the entire codebase. If the job starts on commit A and formats a file `main.go`, and while the job is running the branch gains commit B, which adds *new* changes to `main.go`, when the lint job finishes the formatted version of `main.go` from commit A will be pushed to the remote, and overwrite the changes to `main.go` introduced in commit B. You can avoid this by specifying `--head-sha`. This will skip auto discovery of the remote branch HEAD and instead require that the remote branch HEAD matches the value of `--head-sha`. If the remote branch HEAD does not match `--head-sha`, the push will fail (which is likely what you want). ### Creating a new branch Note that, by default, both of these commands expect the remote branch to already exist. If your workflow primarily works on *new* branches, you should additionally add the `--create-branch` flag and supply a commit hash to use as a branch point via `--head-sha`. With this flag, `commit-headless` will create the branch on GitHub from that commit hash if it doesn't already exist. Example: `commit-headless [flags...] --head-sha=$(git rev-parse main HEAD) --create-branch ...` ### commit-headless push In addition to the required target and branch flags, the `push` command expects a list of commit hashes as arguments *or* a list of commit hashes *in reverse chronological order (newest first)* on standard input. It will iterate over the supplied commits, extract the set of changed files and commit message, then craft new *remote* commits corresponding to each local commit. The remote commit will have the original commit message, with "Co-authored-by" trailer for the original commit author. You can use `commit-headless push` via: commit-headless push [flags...] HASH1 HASH2 HASH3 ... Or, using git log (note `--oneline`): git log --oneline main.. | commit-headless push [flags...] ### commit-headless commit This command is more geared for creating single commits at a time. It takes a list of files to commit changes to, and those files will either be updated/added or deleted in a single commit. Note that you cannot delete a file without also adding `--force` for safety reasons. Usage example: # Commit changes to these two files commit-headless commit [flags...] -- README.md .gitlab-ci.yml # Remove a file, add another one, and commit rm file/i/do/not/want echo "hello" > hi-there.txt commit-headless commit [flags...] --force -- hi-there.txt file/i/do/not/want # Commit a change with a custom message commit-headless commit [flags...] -m"ran a pipeline" -- output.txt ## Try it! You can easily try `commit-headless` locally. Create a commit with a different author (to demonstrate how commit-headless attributes changes to the original author), and run it with a GitHub token. For example, create a commit locally and push it to a new branch using the current branch as the branch point: ``` cd ~/Code/repo echo "bot commit here" >> README.md git add README.md git commit --author='A U Thor ' --message="test bot commit" # Assuming a github token in $GITHUB_TOKEN or $HEADLESS_TOKEN commit-headless push \ --target=owner/repo \ --branch=bot-branch \ --head-sha="$(git rev-parse HEAD^)" \ # use the previous commit as our branch point --create-branch \ "$(git rev-parse HEAD)" # push the commit we just created ``` ## Action Releases On a merge to main, if there's not already a tagged release for the current version (in `version.go`), a new tag will be created on the action branch. The action branch contains prebuilt binaries of `commit-headless` to avoid having to use Docker based (composite) actions, or to avoid having to download the binary when the action runs. Because the workflow uses the rendered action (and the built binary) to create the commit to the action branch we are fairly safe from releasing a broken version of the action. Assuming the previous step works, the workflow will then create a tag of the form `action/vVERSION`. For more on the action release, see the [workflow](.github/workflows/release.yml).