Mode commit
Builds, then commits the build output to the checked out branch and pushes it. This is the default mode and the original behavior of the action.
Use it when the branch accepts pushes from the workflow. It needs a branch checkout. The getting started page shows the smallest workflow. The example below does two jobs in one file: mode check on pull requests, and mode commit on pushes to main.
Example
# One workflow for pull requests and for the main branch.
#
# On a pull request the action only checks that the committed build output is fresh
# (mode check). It reads no token, so it also works for pull requests from forks.
# On a push to main it builds and commits the changed build output (mode commit).
#
# Copy the file to .github/workflows/ and change the build script and the paths.
name: Build
on:
push:
branches: [main]
pull_request:
branches: [main]
# The least privilege for the jobs that only read. The commit job asks for more.
permissions:
contents: read
# One run per branch at a time, so two pushes never race to commit the build.
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: false
jobs:
check:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: miguelcolmenares/npm-auto-build@v3
with:
mode: check
command: build:prod
paths: dist
commit:
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write # the push of the build commit
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false # the build cannot read a token from .git/config
- uses: miguelcolmenares/npm-auto-build@v3
with:
command: build:prod
paths: dist
git-user-name: Build Bot
git-user-email: build-bot@example.comOutputs
| Output | Value |
|---|---|
changed | true when the action pushed a commit with new build output, false when nothing changed. |
commit-sha | SHA of the pushed commit. Empty when nothing changed. |
The other outputs are empty in this mode.
What the workflow needs
permissions: contents: write. The defaultGITHUB_TOKENof a read-only workflow cannot push.persist-credentials: falseonactions/checkout. The action looks for a persisted credential before the install, and modecommitfails when it finds one.concurrency. If two runs push to the same branch, the second push is rejected. The action then fetches the branch, rebases its one commit on top and pushes again, up to three attempts, and it never force-pushes. Aconcurrencygroup avoids the race, and the extra build, in the first place. If the rebase conflicts, the step aborts it and fails.
Limits
- A branch push only. A tag push and a
pull_requestevent leave HEAD detached, so there is no branch to push to and the step fails with an error that says so. Use mode check for pull requests. - A protected branch.
GITHUB_TOKENcannot push there. Pass a personal access token or a GitHub App token that may bypass the protection astoken, or use mode pr. See the recipe. - The loop risk. The default message ends with
[skip ci]. A push withGITHUB_TOKENdoes not start workflows, but a push with a personal access token or a GitHub App token does, so keep[skip ci]in a customcommit-message. The step prints a notice when it is missing. - Safe paths.
., the repository root,.gitand a path outside the repository are refused, and anything already staged before the step is refused, so it cannot end up in the build commit.
How the token is used
Only the commit step receives it (and the pr step in mode pr). It copies it into a shell variable, masks it and its base64 form, and gives it to git fetch and git push only, as an http.extraheader in the GIT_CONFIG_* environment variables of that one command. It is never in a command line, a URL, .git/config, git config --global or a file. This needs git 2.31 or newer, which every GitHub-hosted runner has. See Security.