diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml index 3221ab95..1d09ac07 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -5,7 +5,7 @@ body: - type: markdown attributes: value: | - Before opening a bug report, please search for the behaviour in the existing issues. + Before opening a bug report, please search for the behaviour in existing issues or discussions. --- @@ -17,7 +17,7 @@ body: description: "Which exact version of git-auto-commit are you using in your Workflow?" placeholder: "v4.14.0" validations: - required: true + required: true - type: dropdown id: machine attributes: @@ -33,7 +33,7 @@ body: id: bug-description attributes: label: Bug description - description: What exactly happened? + description: What exactly happened? Please describe your problem in detail. validations: required: true - type: textarea @@ -52,7 +52,7 @@ body: id: example-workflow attributes: label: Example Workflow - description: Please share your GitHub Actions workflow which causes the bug. We use this to reproduce the error. No need for backticks here. + description: Please share the YAML-code of your GitHub Actions workflow which causes the bug. We use this to reproduce the error. If the workflow is in a private repostory, please provide a minimal example. (No need for backticks here, the pasted code will be correctly formatted.) render: yaml validations: required: true @@ -60,5 +60,10 @@ body: id: logs attributes: label: Relevant log output - description: If applicable, provide relevant log output. No need for backticks here. + description: If applicable, provide relevant log output. Please copy and paste the output here, and make sure to remove any sensitive information. (No need for backticks here, the pasted code will be correctly formatted.) render: shell + - type: input + id: repository-url + attributes: + label: Repository + description: If applicable, please provide the repository where the bug occurred. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index edb1c778..fe104f76 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,5 +4,5 @@ contact_links: url: https://github.com/stefanzweifel/git-auto-commit-action/discussions/new?category=help about: If you can't get something to work the way you expect, open a question in our discussion forums. - name: Feature Request - url: https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas - about: 'Suggest any ideas you have using our discussion forums.' + url: https://github.com/stefanzweifel/git-auto-commit-action/discussions/new?category=ideas + about: "Suggest any ideas you have using our discussion forums." diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000..ec06066f --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email stefan@stefanzweifel.dev instead of using the issue tracker. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..782a0ad7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 69cfc0b3..6b9b1759 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -12,7 +12,7 @@ categories: - 'changelog:changed' - title: Deprecated labels: - - 'changelog:deprecated ' + - 'changelog:deprecated' - title: Removed labels: - 'changelog:removed' diff --git a/.github/workflows/git-auto-commit.yml b/.github/workflows/git-auto-commit.yml index f3265f52..5260e3ee 100644 --- a/.github/workflows/git-auto-commit.yml +++ b/.github/workflows/git-auto-commit.yml @@ -10,17 +10,24 @@ jobs: git-auto-commit: runs-on: ubuntu-latest + permissions: + # Give the default GITHUB_TOKEN write permission. + # https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ + contents: write + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 + with: + ref: ${{ github.head_ref }} - name: Use git-auto-commit-action id: "auto-commit-action" uses: ./ - name: "no changes detected" - if: steps.auto-commit-action.outputs.changes_detected == false + if: steps.auto-commit-action.outputs.changes_detected == 'false' run: "echo \"No changes detected\"" - name: "changes detected" - if: steps.auto-commit-action.outputs.changes_detected == true + if: steps.auto-commit-action.outputs.changes_detected == 'true' run: "echo \"Changes detected\"" diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 57d44fdc..abaf4028 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -9,10 +9,10 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v2 + uses: actions/checkout@v7 - name: Lint Code Base - uses: github/super-linter@v3 + uses: github/super-linter@v7 env: VALIDATE_ALL_CODEBASE: false VALIDATE_MARKDOWN: false diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 17fdb961..5cfa1a0f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -8,7 +8,13 @@ on: jobs: update_release_draft: runs-on: ubuntu-latest + + permissions: + # Give the default GITHUB_TOKEN write permission. + # https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ + contents: write + steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0f5808a3..61ede29f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v7 - name: Install testing dependencies run: yarn install diff --git a/.github/workflows/update-changelog.yaml b/.github/workflows/update-changelog.yaml index ddeaa42b..26f63f4a 100644 --- a/.github/workflows/update-changelog.yaml +++ b/.github/workflows/update-changelog.yaml @@ -8,9 +8,15 @@ jobs: update: runs-on: ubuntu-latest + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # updated CHANGELOG back to the repository. + # https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ + contents: write + steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v7 with: ref: master @@ -21,7 +27,7 @@ jobs: latest-version: ${{ github.event.release.name }} - name: Commit updated CHANGELOG - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v7 with: branch: master commit_message: Update CHANGELOG diff --git a/.github/workflows/versioning.yml b/.github/workflows/versioning.yml index ad052193..04b1f87c 100644 --- a/.github/workflows/versioning.yml +++ b/.github/workflows/versioning.yml @@ -7,6 +7,12 @@ on: jobs: actions-tagger: runs-on: windows-latest + + permissions: + # Give the default GITHUB_TOKEN write permission. + # https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ + contents: write + steps: - uses: Actions-R-Us/actions-tagger@latest env: diff --git a/CHANGELOG.md b/CHANGELOG.md index d7244c87..8e93c5ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,156 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.15.2...HEAD) +## [Unreleased](https://github.com/stefanzweifel/git-auto-commit-action/compare/v7.1.0...HEAD) > TBD +## [v7.1.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v7.0.0...v7.1.0) - 2025-12-17 + +### Added + +- Add skip_push input option ([#401](https://github.com/stefanzweifel/git-auto-commit-action/pull/401)) [@kvanzuijlen](https://github.com/@kvanzuijlen) + +### Changes + +- docs: fix typo in README.md ([#400](https://github.com/stefanzweifel/git-auto-commit-action/pull/400)) [@GideonBear](https://github.com/@GideonBear) + +### Dependency Updates + +- Bump actions/checkout from 5 to 6 ([#399](https://github.com/stefanzweifel/git-auto-commit-action/pull/399)) [@[dependabot[bot]](https://github.com/apps/dependabot)](https://github.com/@[dependabot[bot]](https://github.com/apps/dependabot)) +- Bump bats from 1.12.0 to 1.13.0 ([#398](https://github.com/stefanzweifel/git-auto-commit-action/pull/398)) [@[dependabot[bot]](https://github.com/apps/dependabot)](https://github.com/@[dependabot[bot]](https://github.com/apps/dependabot)) + +## [v7.0.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v6.0.1...v7.0.0) - 2025-10-12 + +### Added + +- Restore skip_fetch, skip_checkout, create_branch ([#388](https://github.com/stefanzweifel/git-auto-commit-action/pull/388)) [@stefanzweifel](https://github.com/@stefanzweifel) +- Restore Detached State Detection ([#393](https://github.com/stefanzweifel/git-auto-commit-action/pull/393)) [@stefanzweifel](https://github.com/@stefanzweifel) +- Add Support for Tag Messages ([#391](https://github.com/stefanzweifel/git-auto-commit-action/pull/391)) [@EliasBoulharts](https://github.com/@EliasBoulharts) + +### Changed + +- Run Action on Node 24 ([#389](https://github.com/stefanzweifel/git-auto-commit-action/pull/389)) [@stefanzweifel](https://github.com/@stefanzweifel) + +### Dependency Updates + +- Bump actions/checkout from 4 to 5 ([#386](https://github.com/stefanzweifel/git-auto-commit-action/pull/386)) [@[dependabot[bot]](https://github.com/apps/dependabot)](https://github.com/@[dependabot[bot]](https://github.com/apps/dependabot)) + +## [v6.0.1](https://github.com/stefanzweifel/git-auto-commit-action/compare/v6.0.0...v6.0.1) - 2025-06-11 + +### Fixed + +- Disable Check if Repo is in Detached State ([#379](https://github.com/stefanzweifel/git-auto-commit-action/pull/379)) [@stefanzweifel](https://github.com/@stefanzweifel) + +## [v6.0.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5.2.0...v6.0.0) - 2025-06-10 + +### Added + +- Throw error early if repository is in a detached state ([#357](https://github.com/stefanzweifel/git-auto-commit-action/pull/357)) + +### Fixed + +- Fix PAT instructions with Dependabot ([#376](https://github.com/stefanzweifel/git-auto-commit-action/pull/376)) [@Dreamsorcerer](https://github.com/@Dreamsorcerer) + +### Removed + +- Remove support for `create_branch`, `skip_checkout`, `skip_Fetch` ([#314](https://github.com/stefanzweifel/git-auto-commit-action/pull/314)) + +## [v5.2.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5.1.0...v5.2.0) - 2025-04-19 + +### Added + +- Add `create_git_tag_only` option to skip commiting and always create a git-tag. ([#364](https://github.com/stefanzweifel/git-auto-commit-action/pull/364)) [@zMynxx](https://github.com/@zMynxx) +- Add Test for `create_git_tag_only` feature ([#367](https://github.com/stefanzweifel/git-auto-commit-action/pull/367)) [@stefanzweifel](https://github.com/@stefanzweifel) + +### Fixed + +- docs: Update README.md per #354 ([#361](https://github.com/stefanzweifel/git-auto-commit-action/pull/361)) [@rasa](https://github.com/@rasa) + +## [v5.1.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5.0.1...v5.1.0) - 2025-01-11 + +### Changed + +- Include `github.actor_id` in default `commit_author` ([#354](https://github.com/stefanzweifel/git-auto-commit-action/pull/354)) [@parkerbxyz](https://github.com/@parkerbxyz) + +### Fixed + +- docs(README): fix broken protected branch docs link ([#346](https://github.com/stefanzweifel/git-auto-commit-action/pull/346)) [@scarf005](https://github.com/@scarf005) +- Update README.md ([#343](https://github.com/stefanzweifel/git-auto-commit-action/pull/343)) [@Kludex](https://github.com/@Kludex) + +### Dependency Updates + +- Bump bats from 1.11.0 to 1.11.1 ([#353](https://github.com/stefanzweifel/git-auto-commit-action/pull/353)) [@dependabot](https://github.com/@dependabot) +- Bump github/super-linter from 6 to 7 ([#342](https://github.com/stefanzweifel/git-auto-commit-action/pull/342)) [@dependabot](https://github.com/@dependabot) +- Bump github/super-linter from 5 to 6 ([#335](https://github.com/stefanzweifel/git-auto-commit-action/pull/335)) [@dependabot](https://github.com/@dependabot) + +## [v5.0.1](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5.0.0...v5.0.1) - 2024-04-12 + +### Fixed + +- Fail if attempting to execute git commands in a directory that is not a git-repo. ([#326](https://github.com/stefanzweifel/git-auto-commit-action/pull/326)) [@ccomendant](https://github.com/@ccomendant) + +### Dependency Updates + +- Bump bats from 1.10.0 to 1.11.0 ([#325](https://github.com/stefanzweifel/git-auto-commit-action/pull/325)) [@dependabot](https://github.com/@dependabot) +- Bump release-drafter/release-drafter from 5 to 6 ([#319](https://github.com/stefanzweifel/git-auto-commit-action/pull/319)) [@dependabot](https://github.com/@dependabot) + +### Misc + +- Clarify `commit_author` input option ([#315](https://github.com/stefanzweifel/git-auto-commit-action/pull/315)) [@npanuhin](https://github.com/@npanuhin) +- Add step id explanation for output in README.md ([#324](https://github.com/stefanzweifel/git-auto-commit-action/pull/324)) [@ChristianVermeulen](https://github.com/@ChristianVermeulen) +- Linux is not UNIX ([#321](https://github.com/stefanzweifel/git-auto-commit-action/pull/321)) [@couling](https://github.com/@couling) + +## [v5.0.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.16.0...v5.0.0) - 2023-10-06 + +New major release that bumps the default runtime to Node 20. There are no other breaking changes. + +### Changed + +- Update node version to node20 ([#300](https://github.com/stefanzweifel/git-auto-commit-action/pull/300)) [@ryudaitakai](https://github.com/@ryudaitakai) +- Add _log and _set_github_output functions ([#273](https://github.com/stefanzweifel/git-auto-commit-action/pull/273)) [@stefanzweifel](https://github.com/@stefanzweifel) + +### Fixed + +- Seems like there is an extra space ([#288](https://github.com/stefanzweifel/git-auto-commit-action/pull/288)) [@pedroamador](https://github.com/@pedroamador) +- Fix git-auto-commit.yml ([#277](https://github.com/stefanzweifel/git-auto-commit-action/pull/277)) [@zcong1993](https://github.com/@zcong1993) + +### Dependency Updates + +- Bump actions/checkout from 3 to 4 ([#302](https://github.com/stefanzweifel/git-auto-commit-action/pull/302)) [@dependabot](https://github.com/@dependabot) +- Bump bats from 1.9.0 to 1.10.0 ([#293](https://github.com/stefanzweifel/git-auto-commit-action/pull/293)) [@dependabot](https://github.com/@dependabot) +- Bump github/super-linter from 4 to 5 ([#289](https://github.com/stefanzweifel/git-auto-commit-action/pull/289)) [@dependabot](https://github.com/@dependabot) +- Bump bats from 1.8.2 to 1.9.0 ([#282](https://github.com/stefanzweifel/git-auto-commit-action/pull/282)) [@dependabot](https://github.com/@dependabot) + +## [v4.16.0](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.15.4...v4.16.0) - 2022-12-02 + +### Changed + +- Don't commit files when only LF/CRLF changes ([#265](https://github.com/stefanzweifel/git-auto-commit-action/pull/265)) [@ZeroRin](https://github.com/@ZeroRin) +- Update default email address of github-actions[bot] ([#264](https://github.com/stefanzweifel/git-auto-commit-action/pull/264)) [@Teko012](https://github.com/@Teko012) + +### Fixed + +- Fix link and text for workflow limitation ([#263](https://github.com/stefanzweifel/git-auto-commit-action/pull/263)) [@Teko012](https://github.com/@Teko012) + +## [v4.15.4](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.15.3...v4.15.4) - 2022-11-05 + +### Fixed + +- Let Action fail if git binary can't be located ([#261](https://github.com/stefanzweifel/git-auto-commit-action/pull/261)) [@stefanzweifel](https://github.com/@stefanzweifel) + +### Dependency Updates + +- Bump github/super-linter from 3 to 4 ([#258](https://github.com/stefanzweifel/git-auto-commit-action/pull/258)) [@dependabot](https://github.com/@dependabot) +- Bump bats from 1.7.0 to 1.8.2 ([#259](https://github.com/stefanzweifel/git-auto-commit-action/pull/259)) [@dependabot](https://github.com/@dependabot) +- Bump actions/checkout from 2 to 3 ([#257](https://github.com/stefanzweifel/git-auto-commit-action/pull/257)) [@dependabot](https://github.com/@dependabot) + +## [v4.15.3](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.15.2...v4.15.3) - 2022-10-26 + +### Changed + +- Use deprecated set-output syntax if GITHUB_OUTPUT environment is not available ([#255](https://github.com/stefanzweifel/git-auto-commit-action/pull/255)) [@stefanzweifel](https://github.com/@stefanzweifel) + ## [v4.15.2](https://github.com/stefanzweifel/git-auto-commit-action/compare/v4.15.1...v4.15.2) - 2022-10-22 ### Changed diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ed87d512..bda99b09 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at hello@stefanzweifel.io. All +reported by contacting the project team at stefan@stefanzweifel.dev. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. @@ -72,5 +72,4 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht [homepage]: https://www.contributor-covenant.org -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq +For answers to common questions about this code of conduct, see the [Contributor Covenant FAQ](https://www.contributor-covenant.org/faq). diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 00000000..530ebbc4 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,551 @@ +# Examples + +This document shows real-world scenarios where `git-auto-commit` is useful, with a working GitHub Actions workflow for each. The scenarios are based on questions and use cases that have come up in the [issues](https://github.com/stefanzweifel/git-auto-commit-action/issues) and [discussions](https://github.com/stefanzweifel/git-auto-commit-action/discussions) of this repository over the years. + +If you have a use case that isn't covered here, [open a discussion](https://github.com/stefanzweifel/git-auto-commit-action/discussions) — we may add it. + +## Table of Contents + +- [Auto-format code on pull requests](#auto-format-code-on-pull-requests) +- [Auto-fix lint errors](#auto-fix-lint-errors) +- [Update dependency lock files](#update-dependency-lock-files) +- [Build and commit compiled assets](#build-and-commit-compiled-assets) +- [Auto-generate API documentation](#auto-generate-api-documentation) +- [Update README with generated content](#update-readme-with-generated-content) +- [Maintain a CHANGELOG](#maintain-a-changelog) +- [Sync translations / i18n files](#sync-translations--i18n-files) +- [Publish a static site to a separate branch](#publish-a-static-site-to-a-separate-branch) +- [Scheduled data refresh](#scheduled-data-refresh) +- [Create a release tag without a commit](#create-a-release-tag-without-a-commit) +- [Fail the build instead of pushing changes (drift check)](#fail-the-build-instead-of-pushing-changes-drift-check) +- [Sign automated commits with GPG](#sign-automated-commits-with-gpg) +- [Squash automated changes into the previous commit](#squash-automated-changes-into-the-previous-commit) + +--- + +## Auto-format code on pull requests + +**Description:** Run a code formatter (Prettier, php-cs-fixer, Black, gofmt, rustfmt, …) on every pull request and commit the resulting changes back to the contributor's branch. Contributors don't have to think about style; the bot fixes it for them. + +This is the most common use case for this Action. Running it on `pull_request` means the formatter only touches the PR branch — never your default branch directly. + +```yaml +name: Format + +on: pull_request + +jobs: + prettier: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: npx prettier --write . + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "style: apply prettier formatting" +``` + +> [!TIP] +> If the PR comes from a fork, the Action can only push back if the contributor enabled "Allow edits by maintainers". See the [forks section in the README](README.md#use-in-forks-from-public-repositories) for details. + +--- + +## Auto-fix lint errors + +**Description:** Many linters can both report and auto-fix problems (`eslint --fix`, `rubocop -a`, `ruff --fix`, `stylelint --fix`, …). Use the Action to commit the auto-fixed result so reviewers only see the issues that need human judgement. + +```yaml +name: Lint and fix + +on: pull_request + +jobs: + eslint: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + - run: npx eslint . --fix + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore: apply eslint --fix" +``` + +> [!TIP] +> `file_pattern` is intentionally omitted here. If one of several custom patterns does not match a file in the repository, `git add` can fail with a pathspec error. Let the linter decide which files to change, or use a custom pattern that you know exists in your repository. + +--- + +## Update dependency lock files + +**Description:** When a dependency tool produces a fresh lock file (`package-lock.json`, `composer.lock`, `Gemfile.lock`, `poetry.lock`, …), commit only the lock file. Pair this with a scheduled job to keep lock files in sync without manual intervention. + +```yaml +name: Refresh lock file + +on: + schedule: + - cron: "0 6 * * 1" # every Monday at 06:00 UTC + workflow_dispatch: + +jobs: + refresh: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: npm install --package-lock-only + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "chore(deps): refresh package-lock.json" + file_pattern: package-lock.json +``` + +--- + +## Build and commit compiled assets + +**Description:** For projects that ship a `dist/` folder (libraries, browser extensions, themes), build the assets in CI and commit them so consumers can install directly from the repo without a build step. + +```yaml +name: Build dist + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + - run: npm run build + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "build: update dist/" + file_pattern: "dist/**" +``` + +> [!NOTE] +> If `dist/` is in `.gitignore`, the Action will not pick up changes. See the [troubleshooting section in the README](README.md#change-to-file-is-not-detected). + +--- + +## Auto-generate API documentation + +**Description:** Tools like TypeDoc, Doxygen, Sphinx, or `cargo doc` generate documentation from source comments. Regenerate on every push to `main` and commit the output so the published docs always match the latest code. + +```yaml +name: Docs + +on: + push: + branches: [main] + +jobs: + typedoc: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + - run: npx typedoc --out docs/api src/index.ts + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "docs: regenerate API reference" + file_pattern: "docs/api/**" +``` + +--- + +## Update README with generated content + +**Description:** Many projects keep dynamic sections in the README — a contributor list, a badge gallery, a table of contents, a list of supported plugins. Regenerate them on a schedule (or when a related file changes) and commit the result. + +```yaml +name: Update contributors + +on: + schedule: + - cron: "0 0 * * 0" # weekly + workflow_dispatch: + +jobs: + contributors: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - uses: akhilmhdh/contributors-readme-action@v2.3.10 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "docs: update contributors" + file_pattern: README.md +``` + +--- + +## Maintain a CHANGELOG + +**Description:** Generate or update `CHANGELOG.md` from commit history or release notes after each merge to `main`, and commit it back. + +```yaml +name: Update CHANGELOG + +on: + push: + branches: [main] + +jobs: + changelog: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history so the generator sees all commits + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: npx conventional-changelog -p angular -i CHANGELOG.md -s + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "docs: update CHANGELOG" + file_pattern: CHANGELOG.md +``` + +--- + +## Sync translations / i18n files + +**Description:** When translations are managed in an external service (Crowdin, Lokalise, Weblate) or extracted from source, pull or generate the latest catalogs on a schedule and commit them. + +```yaml +name: Sync translations + +on: + schedule: + - cron: "0 3 * * *" # daily at 03:00 UTC + +jobs: + sync: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - name: Download translations + run: ./scripts/pull-translations.sh + env: + CROWDIN_TOKEN: ${{ secrets.CROWDIN_TOKEN }} + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "i18n: sync translations from Crowdin" + file_pattern: "locales/**/*.json" +``` + +--- + +## Publish a static site to a separate branch + +**Description:** Build a static site on `main` and push the generated output to a `gh-pages` branch so GitHub Pages can serve it. Use a second checkout directory for the publish branch so the source checkout and generated site do not get mixed together. + +Create the `gh-pages` branch once before using this workflow. + +```yaml +name: Build site + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + path: source + + - uses: actions/checkout@v5 + with: + ref: gh-pages + path: site + + - name: Build site + working-directory: source + run: ./build-site.sh # produces output in ./public + + - name: Replace publish branch contents + run: | + find site -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} + + cp -R source/public/. site/ + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + repository: site + branch: gh-pages + commit_message: "site: rebuild from ${{ github.sha }}" +``` + +> [!NOTE] +> For most Pages workflows the official [`actions/deploy-pages`](https://github.com/actions/deploy-pages) is a better fit. Use this approach when you specifically want the build output stored in a branch. + +--- + +## Scheduled data refresh + +**Description:** Pull data from an external source on a schedule and commit it. Common examples: tracking statistics, snapshotting an API response, refreshing a cached dataset. + +```yaml +name: Refresh stats + +on: + schedule: + - cron: "0 * * * *" # hourly + workflow_dispatch: + +jobs: + refresh: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - run: curl -sSL https://api.example.com/stats.json -o data/stats.json + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "data: refresh hourly stats" + file_pattern: "data/*.json" +``` + +--- + +## Create a release tag without a commit + +**Description:** Sometimes you want to tag the current HEAD as a release without committing any files. Use `create_git_tag_only` together with `tag_name` and `tagging_message`. + +```yaml +name: Tag release + +on: + workflow_dispatch: + inputs: + version: + description: "Version to tag (e.g. v1.4.0)" + required: true + +jobs: + tag: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + create_git_tag_only: true + tag_name: ${{ inputs.version }} + tagging_message: "Release ${{ inputs.version }}" +``` + +--- + +## Fail the build instead of pushing changes (drift check) + +**Description:** Sometimes you don't want a bot to push fixes — you want to fail the build so the contributor fixes them locally. Use the `changes_detected` output as a check: run the formatter, skip branch checkout/fetch/push, and fail if anything changed. + +```yaml +name: Format check + +on: pull_request + +jobs: + check: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - run: npx prettier --write . + + - uses: stefanzweifel/git-auto-commit-action@v7 + id: auto-commit + with: + skip_checkout: true + skip_fetch: true + skip_push: true + + - name: Fail if formatting was needed + if: steps.auto-commit.outputs.changes_detected == 'true' + run: | + echo "::error::Code is not formatted. Run 'npx prettier --write .' locally." + exit 1 +``` + +--- + +## Sign automated commits with GPG + +**Description:** If your branch protection rules require signed commits, the bot's commits need to be signed too. Import a GPG key first, then tell the Action to use the key's identity as the commit author. + +```yaml +name: Format (signed) + +on: pull_request + +jobs: + format: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + + - name: Import GPG key + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true + + - run: npx prettier --write . + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: "style: apply prettier" + commit_user_name: ${{ steps.import-gpg.outputs.name }} + commit_user_email: ${{ steps.import-gpg.outputs.email }} + commit_author: "${{ steps.import-gpg.outputs.name }} <${{ steps.import-gpg.outputs.email }}>" +``` + +See discussion [#334](https://github.com/stefanzweifel/git-auto-commit-action/discussions/334) for background. + +--- + +## Squash automated changes into the previous commit + +**Description:** Avoid noisy "apply automatic changes" commits by amending the last commit instead. Useful when the bot fix is trivial and you don't want a separate entry in the history. + +> [!CAUTION] +> Amending rewrites history. Only use this on branches where force-pushing is acceptable (typically PR branches, never `main`). + +```yaml +name: Format (amend) + +on: pull_request + +jobs: + format: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + fetch-depth: 2 # need previous commit for --amend + + - run: npx prettier --write . + + - name: Read previous commit metadata + id: last + run: | + echo "message=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT + echo "author=$(git log -1 --pretty='%an <%ae>')" >> $GITHUB_OUTPUT + + - uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_message: ${{ steps.last.outputs.message }} + commit_author: ${{ steps.last.outputs.author }} + commit_options: "--amend --no-edit" + push_options: "--force" + skip_fetch: true +``` + +See discussion [#159](https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950) for details. diff --git a/README.md b/README.md index 28725ca6..19ef1c10 100644 --- a/README.md +++ b/README.md @@ -11,29 +11,66 @@ By default, the commit is made in the name of "GitHub Actions" and co-authored b If you want to learn more how this Action works under the hood, check out [this article](https://michaelheap.com/git-auto-commit/) by Michael Heap. +If your use case is not covered by git-auto-commit, you might want to check out the following alternative Actions: + +- [planetscale/ghcommit-action](https://github.com/planetscale/ghcommit-action) +- [EndBug/add-and-commit](https://github.com/EndBug/add-and-commit) + ## Usage -Add the following step at the end of your job, after other steps that might add or change files. +Adding git-auto-commit to your Workflow only takes a couple lines of code. + +1. Set the `contents`-permission of the default GITHUB_TOKEN to `write`. (Required to push new commits to the repository) +2. Add the following step at the end of your job, after other steps that might add or change files. ```yaml -- uses: stefanzweifel/git-auto-commit-action@v4 +- uses: stefanzweifel/git-auto-commit-action@v7 ``` -Note that the Action has to be used in a Job that runs on a UNIX system (e.g. `ubuntu-latest`). -If you don't use the default permission of the GITHUB_TOKEN, give the Job or Workflow at least the `contents: write` permission. +Your Workflow should look similar to this example. + +```yaml +name: Format + +on: push + +jobs: + format-code: + runs-on: ubuntu-latest + + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the + # added or changed files to the repository. + contents: write + + steps: + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + # Value already defaults to true, but `persist-credentials` is required to push new commits to the repository. + persist-credentials: true + + # Other steps that change files in the repository go here + # … + + # Commit all changed files back to the repository + - uses: stefanzweifel/git-auto-commit-action@v7 +``` + +> [!NOTE] +> The Action has to be used in a Job that runs on a UNIX-like system (e.g. `ubuntu-latest`). The following is an extended example with all available options. ```yaml -- uses: stefanzweifel/git-auto-commit-action@v4 +- uses: stefanzweifel/git-auto-commit-action@v7 with: # Optional. Commit message for the created commit. # Defaults to "Apply automatic changes" commit_message: Automated Change - # Optional. Local and remote branch name where commit is going to be pushed - # to. Defaults to the current branch. - # You might need to set `create_branch: true` if the branch does not exist. + # Optional. Remote branch name where commit is going to be pushed to. + # Defaults to the current branch. branch: feature-123 # Optional. Options used by `git-commit`. @@ -47,20 +84,25 @@ The following is an extended example with all available options. # - https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec file_pattern: '*.php src/*.js tests/*.js' - # Optional. Local file path to the repository. + # Optional. Relative file path under $GITHUB_WORKSPACE to the repository. # Defaults to the root of the repository. repository: . # Optional commit user and author settings commit_user_name: My GitHub Actions Bot # defaults to "github-actions[bot]" - commit_user_email: my-github-actions-bot@example.org # defaults to "github-actions[bot]@users.noreply.github.com" - commit_author: Author # defaults to author of the commit that triggered the run + commit_user_email: my-github-actions-bot@example.org # defaults to "41898282+github-actions[bot]@users.noreply.github.com" + commit_author: Author # defaults to "username ", where "numeric_id" and "username" belong to the author of the commit that triggered the run - # Optional. Tag name being created in the local repository and - # pushed to remote repository and defined branch. - tagging_message: 'v1.0.0' + # Optional. Tag name to be created in the local repository and + # pushed to the remote repository on the defined branch. + # If only one of `tag_name` or `tagging_message` is provided, the value of the provided field will be used for both tag name and message. + tag_name: 'v1.0.0' - # Optional. Option used by `git-status` to determine if the repository is + # Optional. Message to annotate the created tag with. + # If only one of `tag_name` or `tagging_message` is provided, the value of the provided field will be used for both tag name and message. + tagging_message: 'Codename "Sunshine"' + + # Optional. Option used by `git-status` to determine if the repository is # dirty. See https://git-scm.com/docs/git-status#_options status_options: '--untracked-files=no' @@ -71,22 +113,29 @@ The following is an extended example with all available options. # Optional. Options used by `git-push`. # See https://git-scm.com/docs/git-push#_options push_options: '--force' - + # Optional. Disable dirty check and always try to create a commit and push - skip_dirty_check: true - + skip_dirty_check: true + # Optional. Skip internal call to `git fetch` - skip_fetch: true - + skip_fetch: true + # Optional. Skip internal call to `git checkout` skip_checkout: true - # Optional. Prevents the shell from expanding filenames. + # Optional. Skip internal call to `git push` + skip_push: true + + # Optional. Prevents the shell from expanding filenames. # Details: https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html disable_globbing: true # Optional. Create given branch name in local and remote repository. create_branch: true + + # Optional. Creates a new tag and pushes it to remote without creating a commit. + # Skips dirty check and changed files. Must be used in combination with `tag` and `tagging_message`. + create_git_tag_only: false ``` Please note that the Action depends on `bash`. If you're using the Action in a job in combination with a custom Docker container, make sure that `bash` is installed. @@ -111,19 +160,25 @@ jobs: php-cs-fixer: runs-on: ubuntu-latest + permissions: + # Give the default GITHUB_TOKEN write permission to commit and push the changed files back to the repository. + contents: write + steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 with: ref: ${{ github.head_ref }} - name: Run php-cs-fixer uses: docker://oskarstark/php-cs-fixer-ga - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Apply php-cs-fixer changes ``` +See [EXAMPLES.md](EXAMPLES.md) for more scenarios, including auto-formatting, dependency updates, generated docs, release tagging, drift checks, and GPG-signed commits. + ## Inputs Checkout [`action.yml`](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/action.yml) for a full list of supported inputs. @@ -134,10 +189,18 @@ You can use these outputs to trigger other Actions in your Workflow run based on - `changes_detected`: Returns either "true" or "false" if the repository was dirty and files have changed. - `commit_hash`: Returns the full hash of the commit if one was created. +- `create_git_tag_only`: Returns either "true" or "false" if a tag was created, when `create_git_tag_only` was used. + +**⚠️ When using outputs, the step needs to be given an id. See example below.** ### Example ```yaml + - uses: stefanzweifel/git-auto-commit-action@v7 + id: auto-commit-action #mandatory for the output to show up in ${{ steps }} + with: + commit_message: Apply php-cs-fixer changes + - name: "Run if changes have been detected" if: steps.auto-commit-action.outputs.changes_detected == 'true' run: echo "Changes!" @@ -153,11 +216,11 @@ The goal of this Action is to be "the Action for committing files for the 80% us The following is a list of edge cases the Action knowingly does not support: -**No `git pull` when the repository is out of date with remote.** The Action will not do a `git pull` before doing the `git push`. **You** are responsible for keeping the repository up to date in your Workflow runs. +**No `git pull` when the repository is out of date with remote.** The Action will not do a `git pull` before doing the `git push`. **You** are responsible for keeping the repository up to date in your Workflow runs. **No support for running the Action in build matrices**. If your Workflow is using build matrices, and you want that each job commits and pushes files to the remote, you will run into the issue, that the repository in the workflow will become out of date. As the Action will not do a `git pull` for you, you have to do that yourself. -**No support for `git rebase` or `git merge`**. There are many strategies on how to integrate remote upstream changes to a local repository. `git-auto-commit` does not want to be responsible for doing that. +**No support for `git rebase` or `git merge`**. There are many strategies on how to integrate remote upstream changes to a local repository. `git-auto-commit` does not want to be responsible for doing that. **No support for detecting line break changes between CR (Carriage Return) and LF (Line Feed)**. This is a low level issue, you have to resolve differently in your project. Sorry. @@ -165,11 +228,11 @@ If this Action doesn't work for your workflow, check out [EndBug/add-and-commit] ### Checkout the correct branch -You must use `action/checkout@v2` or later versions to check out the repository. +You must use `actions/checkout@v2` or later versions to check out the repository. In non-`push` events, such as `pull_request`, make sure to specify the `ref` to check out: ```yaml -- uses: actions/checkout@v2 +- uses: actions/checkout@v5 with: ref: ${{ github.head_ref }} ``` @@ -187,155 +250,177 @@ You can change this by creating a new [Personal Access Token (PAT)](https://gith storing the token as a secret in your repository and then passing the new token to the [`actions/checkout`](https://github.com/actions/checkout#usage) Action step. ```yaml -- uses: actions/checkout@v2 +- uses: actions/checkout@v5 with: token: ${{ secrets.PAT }} ``` -If you create a personal access token, apply the `repo` and `workflow` scopes. +If you create a personal access token (classic), apply the `repo` and `workflow` scopes. +If you create a fine-grained personal access token, apply the `Contents`-permissions. -If you work in an organization and don't want to create a PAT from your personal account, we recommend using a [robot account](https://docs.github.com/en/github/getting-started-with-github/types-of-github-accounts) for the token. +If you work in an organization and don't want to create a PAT from your personal account, we recommend using a [robot account](https://docs.github.com/en/get-started/learning-about-github/types-of-github-accounts) for the token. -### Change to file is not detected +### Prevent Infinite Loop when using a Personal Access Token -Does your workflow change a file but "git-auto-commit" does not detect the change? Check the `.gitignore` that applies to the respective file. You might have accidentally marked the file to be ignored by git. +If you're using a Personal Access Token (PAT) to push commits to GitHub repository, the resulting commit or push can trigger other GitHub Actions workflows. This can result in an infinite loop. -## Advanced Uses +If you would like to prevent this, you can add `skip-checks:true` to the commit message. See [Skipping workflow runs](https://docs.github.com/en/actions/how-tos/manage-workflow-runs/skip-workflow-runs) for details. -
-Use in forks from public repositories +### Change to file is not detected -**☝️ Important Notice**: This Action technically works with forks. However, please note that the combination of triggers and their options can cause issues. Please read [the documentation](https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows) on which triggers GitHub Actions support.\ -If you use this Action in combination with a linter/fixer, it's easier if you run the Action on `push` on your `main`-branch. +Does your workflow change a file, but "git-auto-commit" does not detect the change? Check the `.gitignore` that applies to the respective file. You might have accidentally marked the file to be ignored by git. ---- +## Advanced Uses -By default, this Action will not run on Pull Requests which have been opened by forks. (This is a limitation by GitHub, not by us.) -However, there are a couple of ways to use this Actions in Workflows that should be triggered by forked repositories. +### Multiline Commit Messages -### Workflow should run in **base** repository +If your commit message should span multiple lines, you have to create a separate step to generate the string. -The workflow below runs whenever a commit is pushed to the `main`-branch or when activity on a pull request happens, by listening to the [`pull_request_target`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target) event. - -If the workflow is triggered by the `pull_request_target`-event, the workflow will run in the context of the base of the pull request, rather than in the context of the merge commit, as the `pull_request` event does. -In other words, this will allow your workflow to be run in the repository where the pull request is opened to and will push changes back to the fork. - -Check out the discussion in [#211](https://github.com/stefanzweifel/git-auto-commit-action/issues/211) for more information on this. +The example below can be used as a starting point to generate a multiline commit meesage. Learn more how multiline strings in GitHub Actions work in the [GitHub documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#multiline-strings). ```yaml -name: Format PHP - -on: - push: - branches: - - main - pull_request_target: - -jobs: - php-cs-fixer: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 + # Building a multiline commit message + # Adjust to your liking + - run: echo "Commit Message 1" >> commitmessage.txt + - run: echo "Commit Message 2" >> commitmessage.txt + - run: echo "Commit Message 3" >> commitmessage.txt + + # Create a multiline string to be used by the git-auto-commit Action + - name: Set commit message + id: commit_message_step + run: | + echo 'commit_message<> $GITHUB_OUTPUT + cat commitmessage.txt >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + + # Quick and dirty step to get rid of the temporary file holding the commit message + - run: rm -rf commitmessage.txt + + - uses: stefanzweifel/git-auto-commit-action@v7 + id: commit with: - repository: ${{ github.event.pull_request.head.repo.full_name }} - ref: ${{ github.head_ref }} - - - name: Run php-cs-fixer - uses: docker://oskarstark/php-cs-fixer-ga - - - uses: stefanzweifel/git-auto-commit-action@v4 + commit_message: ${{ steps.commit_message_step.outputs.commit_message }} ``` -### Workflow should run in **forked** repository - -If the workflow should run in the forked repository, follow these steps: - -1. In addition to listening to the `pull_request` event in your Workflow triggers, you have to add an additional event: `pull_request_target`. You can learn more about this event in [the GitHub docs](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#pull_request_target). -2. GitHub Action has to be enabled on the forked repository. \ -For security reasons, GitHub does not automatically enable GitHub Actions on forks. The user has to explicitly enable GitHub Actions in the "Actions"-tab of the forked repository. (Mention this in your projects README or CONTRIBUTING.md!) +### Signing Commits -After you have added the `pull_request_target` to your desired Workflow and the forked repository has enabled Actions and a new Pull Request is opened, the Workflow will run **on the forked repository**. +If you would like to sign your commits using a GPG key, you will need to use an additional action. +You can use the [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) action and follow its setup instructions. -Due to the fact that the Workflow is not run on the repository the Pull Request is opened in, you won't see any status indicators inside the Pull Request. +As git-auto-commit by default does not use **your** username and email when creating a commit, you have to override these values in your workflow. -#### Example - -The following workflow runs `php-cs-fixer` (a code linter and fixer for PHP) when a `pull_request` is opened. We've added the `pull_request_target`-trigger too, to make it work for forks. +```yml +- name: "Import GPG key" + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} + passphrase: ${{ secrets.GPG_PASSPHRASE }} + git_user_signingkey: true + git_commit_gpgsign: true -```yaml -name: Format PHP +- name: "Commit and push changes" + uses: stefanzweifel/git-auto-commit-action@v7 + with: + commit_author: "${{ steps.import-gpg.outputs.name }} <${{ steps.import-gpg.outputs.email }}>" + commit_user_name: ${{ steps.import-gpg.outputs.name }} + commit_user_email: ${{ steps.import-gpg.outputs.email }} +``` -on: [push, pull_request, pull_request_target] +See discussion [#334](https://github.com/stefanzweifel/git-auto-commit-action/discussions/334) for details. -jobs: - php-cs-fixer: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 +### Use in forks from private repositories - - name: Run php-cs-fixer - uses: docker://oskarstark/php-cs-fixer-ga - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: Apply php-cs-fixer changes -``` +By default, GitHub Actions doesn't run Workflows on forks from **private** repositories. To enable Actions for **private** repositories enable "Run workflows from pull requests" in your repository settings. -Next time a user forks your project **and** enabled GitHub Actions **and** opened a Pull Request, the Workflow will run on the **forked** repository and will push commits to the same branch. +See [this announcement from GitHub](https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/) or the [GitHub docs](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#enabling-workflows-for-private-repository-forks) for details. -Here's how the Pull Request will look like: -![Screenshot of a Pull Request from a Fork](https://user-images.githubusercontent.com/1080923/90955964-9c74c080-e482-11ea-8097-aa7f5161f50e.png) +### Use in forks from public repositories +> [!NOTE] +> This Action technically works with forks. However, please note that the combination of triggers and their options can cause issues. Please read [the documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) on which triggers GitHub Actions support.\ +> Ensure your contributors enable "Allow edits by maintainers" when opening a pull request. ([Learn more](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)) \ +> \ +> **If you use this Action in combination with a linter/fixer, it's easier if you run the Action on `push` on your `main`-branch.** -As you can see, your contributors have to go through hoops to make this work. **For Workflows which run linters and fixers (like the example above) we recommend running them when a push happens on the `main`-branch.** +> [!WARNING] +> Due to limitations of GitHub, this Action currently can't push commits to a base repository, if the fork _lives_ under an organisation. See [github/community#6634](https://github.com/orgs/community/discussions/5634) and [this comment](https://github.com/stefanzweifel/git-auto-commit-action/issues/211#issuecomment-1428849944) for details. +By default, this Action will not run on Pull Requests which have been opened by forks. (This is a limitation by GitHub, not by us.) +However, there are a couple of ways to use this Action in Workflows that should be triggered by forked repositories. -For more information about running Actions on forks, see [this announcement from GitHub](https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/). +### Workflow should run in **base** repository -
+> [!CAUTION] +> The following section explains how you can use git-auto-commit in combination with the `pull_request_target` trigger. +> **Using `pull_request_target` in your workflows can lead to repository compromise as [mentioned](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) by GitHub's own security team. This means, that a bad actor could potentially leak/steal your GitHub Actions repository secrets.** +> Please be aware of this risk when using `pull_request_target` in your workflows. +> +> If your workflow runs code-fixing tools, consider running the workflow on your default branch by listening to the `push` event or use a third-party tool like [autofix.ci](https://autofix.ci/). +> We keep this documentation around, as many questions came in over the years, on how to use this action for public forks. -
-Push to forks from private repositories +The workflow below runs whenever a commit is pushed to the `main`-branch or when activity on a pull request happens, by listening to the [`pull_request_target`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target) event. -By default, GitHub Actions doesn't run Workflows on forks from private repositories. To enable Actions for **private** repositories enable "Run workflows from pull requests" in your repository settings. +If the workflow is triggered by the `pull_request_target`-event, the workflow will run in the context of the base of the pull request, rather than in the context of the merge commit, as the `pull_request` event does. +In other words, this will allow your workflow to be run in the repository where the pull request is opened to and will push changes back to the fork. -See [this announcement from GitHub](https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/) or the [GitHub docs](https://docs.github.com/en/github/administering-a-repository/disabling-or-limiting-github-actions-for-a-repository#enabling-workflows-for-private-repository-forks) for details. +Check out the discussion in [#211](https://github.com/stefanzweifel/git-auto-commit-action/issues/211) for more information on this. -
+```yaml +name: Format PHP -
- - Signing Commits & Other Git Command Line Options - +on: + push: + branches: + - main + pull_request_target: -Using command lines options needs to be done manually for each workflow which you require the option enabled. So for example signing commits requires you to import the gpg signature each and every time. The following list of actions are worth checking out if you need to automate these tasks regularly. +jobs: + php-cs-fixer: + runs-on: ubuntu-latest + permissions: + contents: write -- [Import GPG Signature](https://github.com/crazy-max/ghaction-import-gpg) (Suggested by [TGTGamer](https://github.com/tgtgamer)) + steps: + - uses: actions/checkout@v5 + with: + # Checkout the fork/head-repository and push changes to the fork. + # If you skip this, the base repository will be checked out and changes + # will be committed to the base repository! + repository: ${{ github.event.pull_request.head.repo.full_name }} -
+ # Checkout the branch made in the fork. Will automatically push changes + # back to this branch. + ref: ${{ github.head_ref }} -
- - Using `--amend` and `--no-edit` as commit options - + - name: Run php-cs-fixer + uses: docker://oskarstark/php-cs-fixer-ga + - uses: stefanzweifel/git-auto-commit-action@v7 +``` +For more information about running Actions on forks, see [this announcement from GitHub](https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/). +### Using `--amend` and `--no-edit` as commit options If you would like to use this Action to create a commit using [`--amend`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---amend) and [`--no-edit`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---no-edit) you need to make some adjustments. -**☝️ Important Notice:** You should understand the implications of rewriting history if you amend a commit that has already been published. [See rebasing](https://git-scm.com/docs/git-rebase#_recovering_from_upstream_rebase) +> [!CAUTION] +> You should understand the implications of rewriting history if you amend a commit that has already been published. [See rebasing](https://git-scm.com/docs/git-rebase#_recovering_from_upstream_rebase). First, you need to extract the previous commit message by using `git log -1 --pretty=%s`. Then you need to provide this last commit message to the Action through the `commit_message` input option. +By default, the commit author is changed to `username `, where `username` is the name of the user who triggered the workflow (The [`github.actor`](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context) context is used here). If you want to preserve the name and email of the original author, you must extract them from the last commit and provide them to the Action through the `commit_author` input option. + Finally, you have to use `push_options: '--force'` to overwrite the git history on the GitHub remote repository. (git-auto-commit will not do a `git-rebase` for you!) The steps in your workflow might look like this: ```yaml -- uses: actions/checkout@master +- uses: actions/checkout@v4 with: # Fetch the last 2 commits instead of just 1. (Fetching just 1 commit would overwrite the whole history) fetch-depth: 2 @@ -343,13 +428,15 @@ The steps in your workflow might look like this: # Other steps in your workflow to trigger a changed file - name: Get last commit message - id: last-commit-message + id: last-commit run: | - echo "msg=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT + echo "message=$(git log -1 --pretty=%s)" >> $GITHUB_OUTPUT + echo "author=$(git log -1 --pretty=\"%an <%ae>\")" >> $GITHUB_OUTPUT -- uses: stefanzweifel/git-auto-commit-action@v4 +- uses: stefanzweifel/git-auto-commit-action@v7 with: - commit_message: ${{ steps.last-commit-message.outputs.msg }} + commit_author: ${{ steps.last-commit.outputs.author }} + commit_message: ${{ steps.last-commit.outputs.message }} commit_options: '--amend --no-edit' push_options: '--force' skip_fetch: true @@ -357,9 +444,8 @@ The steps in your workflow might look like this: See discussion in [#159](https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950) for details. -
- ## Troubleshooting + ### Action does not push commit to repository Make sure to [checkout the correct branch](#checkout-the-correct-branch). @@ -369,29 +455,45 @@ Make sure to [checkout the correct branch](#checkout-the-correct-branch). If your Workflow can't push the commit to the repository because of authentication issues, please update your Workflow configuration and usage of [`actions/checkout`](https://github.com/actions/checkout#usage). -Updating the `token` value with a Personal Access Token should fix your issues. +Please note that `persist-credentials` in `actions/checkout` must be set to `true` to push new commits to the repository. + +If you still can't push the commit, and you're using branch protection rules or similar features, updating the `token` value with a Personal Access Token should fix your issues. + +### git-auto-commit fails to push commit that creates or updates files in `.github/workflows/` + +The default `GITHUB_TOKEN` issued by GitHub Action does not have permission to make changes to workflow files located in `.github/workflows/`. +To fix this, please create a personal access token (PAT) and pass the token to the `actions/checkout`-step in your workflow. (Similar to [how to push to protected branches](https://github.com/stefanzweifel/git-auto-commit-action?tab=readme-ov-file#push-to-protected-branches)). + +If a PAT does not work for you, you could also create a new GitHub app and use its token in your workflows. See [this comment in #87](https://github.com/stefanzweifel/git-auto-commit-action/issues/87#issuecomment-1939138661) for details. + +See [#322](https://github.com/stefanzweifel/git-auto-commit-action/issues/322) for details and discussions around this topic. ### Push to protected branches -If your repository uses [protected branches](https://help.github.com/en/github/administering-a-repository/configuring-protected-branches) you have to make some changes to your Workflow for the Action to work properly: You need a Personal Access Token and you either have to allow force pushes or the Personal Access Token needs to belong to an Administrator. +If your repository uses [protected branches](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) you have to make some changes to your Workflow for the Action to work properly: You need a Personal Access Token and you either have to allow force pushes or the Personal Access Token needs to belong to an Administrator. First, you have to create a new [Personal Access Token (PAT)](https://github.com/settings/tokens/new), store the token as a secret in your repository and pass the new token to the [`actions/checkout`](https://github.com/actions/checkout#usage) Action step. +If you create a personal access token (classic), apply the `repo` and `workflow` scopes. +If you create a fine-grained personal access token, apply the `Contents`-permissions. + ```yaml -- uses: actions/checkout@v2 +- uses: actions/checkout@v5 with: - token: ${{ secrets.PAT }} + # We pass the "PAT" secret to the checkout action; if no PAT secret is available to the workflow runner (eg. Dependabot) we fall back to the default "GITHUB_TOKEN". + token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }} ``` -You can learn more about Personal Access Token in the [GitHub documentation](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token). +You can learn more about Personal Access Token in the [GitHub documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). -**Note:** If you're working in an organisation, and you don't want to create the PAT from your personal account, we recommend using a bot-account for such tokens. +> [!TIP] +> If you're working in an organisation, and you don't want to create the PAT from your personal account, we recommend using a bot-account for such tokens. -If you go the "force pushes" route, you have to enable force pushes to a protected branch (See [documentation](https://help.github.com/en/github/administering-a-repository/enabling-force-pushes-to-a-protected-branch)) and update your Workflow to use force push like this. +If you go the "force pushes" route, you have to enable force pushes to a protected branch (see [documentation](https://docs.github.com/en/github/administering-a-repository/enabling-force-pushes-to-a-protected-branch)) and update your Workflow to use force push like this. ```yaml - - uses: stefanzweifel/git-auto-commit-action@v4 + - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: Apply php-cs-fixer changes push_options: --force @@ -399,11 +501,11 @@ If you go the "force pushes" route, you have to enable force pushes to a protect ### No new workflows are triggered by the commit of this action -This is due to limitations set up by GitHub, [commits of this Action do not trigger new Workflow runs](#commits-of-this-action-do-not-trigger-new-workflow-runs). +This is due to limitations set up by GitHub, [commits made by this Action do not trigger new Workflow runs](#commits-made-by-this-action-do-not-trigger-new-workflow-runs). ### Pathspec 'x' did not match any files -If you're using the Action with a custom `file_pattern` and the Action throws a fatal error with the message "Pathspec 'file-pattern' did not match any files", the problem is probably that no file for the pattern exists in the repository. +If you're using the Action with a custom `file_pattern` and the Action throws a fatal error with the message "Pathspec 'file-pattern' did not match any files", the problem is probably that no file for the pattern **exists** in the repository. `file_pattern` is used both for `git-status` and `git-add` in this Action. `git-add` will throw a fatal error, if for example, you use a file pattern like `*.js *.ts` but no `*.ts` files exist in your projects' repository. @@ -411,7 +513,7 @@ See [Issue #227](https://github.com/stefanzweifel/git-auto-commit-action/issues/ ### Custom `file_pattern`, changed files but seeing "Working tree clean. Nothing to commit." in the logs -If you're using a custom `file_pattern` and the Action does not detect the changes made in your worfklow, you're probably running into a globbing issue. +If you're using a custom `file_pattern` and the Action does not detect the changes made in your workflow, you're probably running into a globbing issue. Let's imagine you use `file_pattern: '*.md'` to detect and commit changes to all Markdown files in your repository. If your Workflow now only updates `.md`-files in a subdirectory, but you have an untouched `.md`-file in the root of the repository, the git-auto-commit Action will display "Working tree clean. Nothing to commit." in the Workflow log. @@ -421,7 +523,7 @@ This is due to the fact, that the `*.md`-glob is expanded before sending it to ` To fix this add `disable_globbing: true` to your Workflow. ```yaml -- uses: stefanzweifel/git-auto-commit-action@v4 +- uses: stefanzweifel/git-auto-commit-action@v7 with: file_pattern: '*.md' disable_globbing: true @@ -447,10 +549,10 @@ yarn test ## Versioning -We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/stefanzweifel/git-auto-commit-action/tags). +We use [SemVer](https://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/stefanzweifel/git-auto-commit-action/tags). -We also provide major version tags to make it easier to always use the latest release of a major version. For example, you can use `stefanzweifel/git-auto-commit-action@v4` to always use the latest release of the current major version. -(More information about this [here](https://help.github.com/en/actions/building-actions/about-actions#versioning-your-action).) +We also provide major version tags to make it easier to always use the latest release of a major version. For example, you can use `stefanzweifel/git-auto-commit-action@v7` to always use the latest release of the current major version. +(More information about this [here](https://docs.github.com/en/actions/building-actions/about-actions#versioning-your-action).) ## Credits diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000..700d5bf9 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,18 @@ +# Upgrading + +## From v6 to v7 + +The previously removed options `create_branch`, `skip_fetch`, and `skip_checkout` have been reintroduced in git-auto-commit v7. If you had removed these options from your workflows when upgrading to v6, you can now add them back if needed. + +Tagging a commit has been reworked. In addition to the existing `tagging_message`-option, a new `tag_name` option has been added. If you were using `tagging_message`, you can continue to do so, but if you want to specify a custom tag name and tag message, you can now use the `tag_name` and `tagging_message` options. + +(Specifying a `tagging_message` without a `tag_name` will create a tag with the name and message both set to the value of `tagging_message`.) + +## From v5 to v6 + +The following options have been removed from git-auto-commit and can be removed from your workflows. + +- `create_branch` (git-auto-commit no longer switches branches locally during a workflow run) +- `skip_fetch` +- `skip_checkout` + diff --git a/action.yml b/action.yml index 64797216..6160932e 100644 --- a/action.yml +++ b/action.yml @@ -1,7 +1,7 @@ name: Git Auto Commit description: 'Automatically commits files which have been changed during the workflow run and push changes back to remote repository.' -author: Stefan Zweifel +author: Stefan Zweifel inputs: commit_message: @@ -25,11 +25,11 @@ inputs: required: false default: '' file_pattern: - description: File pattern used for `git add`. For example `src/*.js` + description: File pattern used for `git add` and the dirty-check (`git status`). Supports multiple space-separated patterns. For example `src/*.js` required: false default: '.' repository: - description: Local file path to the git repository. Defaults to the current directory (`.`) + description: Relative file path under $GITHUB_WORKSPACE to the git repository. Defaults to the current directory (`.`) required: false default: '.' commit_user_name: @@ -39,13 +39,17 @@ inputs: commit_user_email: description: Email address used for the commit user required: false - default: github-actions[bot]@users.noreply.github.com + default: 41898282+github-actions[bot]@users.noreply.github.com commit_author: description: Value used for the commit author. Defaults to the username of whoever triggered this workflow run. required: false - default: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com> + default: ${{ github.actor }} <${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com> + tag_name: + description: Tag name used for creating a new git tag with the commit. Keep this empty, if no tag should be created. + required: false + default: '' tagging_message: - description: Message used to create a new git tag with the commit. Keep this empty, if no tag should be created. + description: Tagging message used for creating a new git tag with the commit. Keep this empty, if no tag should be created. required: false default: '' push_options: @@ -64,21 +68,38 @@ inputs: description: Skip the call to git-checkout. required: false default: false + skip_push: + description: Skip the call to git-push. + required: false + default: false disable_globbing: description: Stop the shell from expanding filenames (https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html) + required: false default: false create_branch: description: Create new branch with the name of `branch`-input in local and remote repository, if it doesn't exist yet. + required: false + default: false + create_git_tag_only: + description: Perform a clean git tag and push, without commiting anything + required: false default: false + internal_git_binary: + description: Internal use only! Path to git binary used to check if git is available. (Don't change this!) + required: false + default: git + outputs: changes_detected: - description: Value is "true", if the repository was dirty and file changes have been detected. Value is "false", if no changes have been detected. + description: Value is "true" if matching changes were detected and committed. Value is "false" if no matching changes were detected or only CRLF changes were staged. Not set in `create_git_tag_only` mode. commit_hash: - description: Full hash of the created commit. Only present if the "changes_detected" output is "true". + description: Full hash of the created commit. Only set when a commit was actually made (i.e. `changes_detected` is "true"). + create_git_tag_only: + description: Set to "true" when the action ran in `create_git_tag_only` mode. Never set to "false". runs: - using: 'node16' + using: 'node24' main: 'index.js' branding: diff --git a/entrypoint.sh b/entrypoint.sh index 026b8791..09fb6b73 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -6,30 +6,78 @@ if "$INPUT_DISABLE_GLOBBING"; then set -o noglob; fi +_set_github_output() { + local name=${1} + local value=${2} + + # Check if $GITHUB_OUTPUT is available + # (Feature detection will be removed in spring 2023) + if [ -z ${GITHUB_OUTPUT+x} ]; then + echo "::set-output name=$name::$value"; + else + echo "$name=$value" >> $GITHUB_OUTPUT; + fi +} + +_log() { + local level=${1} + local message=${2} + + echo "::$level::$message"; +} + _main() { + _check_if_git_is_available + _switch_to_repository - if _git_is_dirty || "$INPUT_SKIP_DIRTY_CHECK"; then + _check_if_is_git_repository + + _check_if_repository_is_in_detached_state - echo "changes_detected=true" >> $GITHUB_OUTPUT; + if "$INPUT_CREATE_GIT_TAG_ONLY"; then + _log "debug" "Create git tag only"; + _set_github_output "create_git_tag_only" "true" + _tag_commit + _push_to_github + elif _git_is_dirty || "$INPUT_SKIP_DIRTY_CHECK"; then + + _set_github_output "changes_detected" "true" _switch_to_branch _add_files - _local_commit + # Check dirty state of repo again using git-diff. + # (git-diff detects better if CRLF of files changes and does NOT + # proceed, if only CRLF changes are detected. See #241 and #265 + # for more details.) + if [ -n "$(git diff --staged)" ] || "$INPUT_SKIP_DIRTY_CHECK"; then + _local_commit - _tag_commit + _tag_commit - _push_to_github - else + _push_to_github + else + _set_github_output "changes_detected" "false" - echo "changes_detected=false" >> $GITHUB_OUTPUT; + echo "Working tree clean. Nothing to commit."; + fi + else + _set_github_output "changes_detected" "false" echo "Working tree clean. Nothing to commit."; fi } +_check_if_git_is_available() { + if hash -- "$INPUT_INTERNAL_GIT_BINARY" 2> /dev/null; then + _log "debug" "git binary found."; + else + _log "error" "git-auto-commit could not find git binary. Please make sure git is available." + exit 1; + fi +} _switch_to_repository() { echo "INPUT_REPOSITORY value: $INPUT_REPOSITORY"; @@ -38,13 +86,34 @@ _switch_to_repository() { _git_is_dirty() { echo "INPUT_STATUS_OPTIONS: ${INPUT_STATUS_OPTIONS}"; - echo "::debug::Apply status options ${INPUT_STATUS_OPTIONS}"; + _log "debug" "Apply status options ${INPUT_STATUS_OPTIONS}"; echo "INPUT_FILE_PATTERN: ${INPUT_FILE_PATTERN}"; read -r -a INPUT_FILE_PATTERN_EXPANDED <<< "$INPUT_FILE_PATTERN"; + # capture stderr + gitStatusMessage="$((git status -s $INPUT_STATUS_OPTIONS -- ${INPUT_FILE_PATTERN_EXPANDED:+${INPUT_FILE_PATTERN_EXPANDED[@]}} >/dev/null ) 2>&1)"; # shellcheck disable=SC2086 - [ -n "$(git status -s $INPUT_STATUS_OPTIONS -- ${INPUT_FILE_PATTERN_EXPANDED:+${INPUT_FILE_PATTERN_EXPANDED[@]}})" ] + gitStatus="$(git status -s $INPUT_STATUS_OPTIONS -- ${INPUT_FILE_PATTERN_EXPANDED:+${INPUT_FILE_PATTERN_EXPANDED[@]}})"; + [ -n "$gitStatus" ] +} + +_check_if_is_git_repository() { + if [ -d ".git" ]; then + _log "debug" "Repository found."; + else + _log "error" "Not a git repository. Please make sure to run this action in a git repository. Adjust the `repository` input if necessary."; + exit 1; + fi +} + +_check_if_repository_is_in_detached_state() { + if [ -z "$(git symbolic-ref HEAD)" ] + then + _log "warning" "Repository is in a detached HEAD state. git-auto-commit will likely handle this automatically. To avoid it, check out a branch using the ref option in actions/checkout."; + else + _log "debug" "Repository is on a branch."; + fi } _switch_to_branch() { @@ -52,15 +121,17 @@ _switch_to_branch() { # Fetch remote to make sure that repo can be switched to the right branch. if "$INPUT_SKIP_FETCH"; then - echo "::debug::git-fetch has not been executed"; + _log "debug" "git-fetch will not be executed."; else + _log "debug" "git-fetch will be executed."; git fetch --depth=1; fi # If `skip_checkout`-input is true, skip the entire checkout step. if "$INPUT_SKIP_CHECKOUT"; then - echo "::debug::git-checkout has not been executed"; + _log "debug" "git-checkout will not be executed."; else + _log "debug" "git-checkout will be executed."; # Create new local branch if `create_branch`-input is true if "$INPUT_CREATE_BRANCH"; then # shellcheck disable=SC2086 @@ -75,7 +146,7 @@ _switch_to_branch() { _add_files() { echo "INPUT_ADD_OPTIONS: ${INPUT_ADD_OPTIONS}"; - echo "::debug::Apply add options ${INPUT_ADD_OPTIONS}"; + _log "debug" "Apply add options ${INPUT_ADD_OPTIONS}"; echo "INPUT_FILE_PATTERN: ${INPUT_FILE_PATTERN}"; read -r -a INPUT_FILE_PATTERN_EXPANDED <<< "$INPUT_FILE_PATTERN"; @@ -86,7 +157,7 @@ _add_files() { _local_commit() { echo "INPUT_COMMIT_OPTIONS: ${INPUT_COMMIT_OPTIONS}"; - echo "::debug::Apply commit options ${INPUT_COMMIT_OPTIONS}"; + _log "debug" "Apply commit options ${INPUT_COMMIT_OPTIONS}"; # shellcheck disable=SC2206 INPUT_COMMIT_OPTIONS_ARRAY=( $INPUT_COMMIT_OPTIONS ); @@ -101,43 +172,52 @@ _local_commit() { --author="$INPUT_COMMIT_AUTHOR" \ ${INPUT_COMMIT_OPTIONS:+"${INPUT_COMMIT_OPTIONS_ARRAY[@]}"}; - echo "commit_hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT; + _set_github_output "commit_hash" $(git rev-parse HEAD) } _tag_commit() { + echo "INPUT_TAG_NAME: ${INPUT_TAG_NAME}" echo "INPUT_TAGGING_MESSAGE: ${INPUT_TAGGING_MESSAGE}" - if [ -n "$INPUT_TAGGING_MESSAGE" ] - then - echo "::debug::Create tag $INPUT_TAGGING_MESSAGE"; - git -c user.name="$INPUT_COMMIT_USER_NAME" -c user.email="$INPUT_COMMIT_USER_EMAIL" tag -a "$INPUT_TAGGING_MESSAGE" -m "$INPUT_TAGGING_MESSAGE"; + if [ -n "$INPUT_TAG_NAME" ] || [ -n "$INPUT_TAGGING_MESSAGE" ]; then + INTERNAL_TAG=${INPUT_TAG_NAME:-$INPUT_TAGGING_MESSAGE} + INTERNAL_TAGGING_MESSAGE=${INPUT_TAGGING_MESSAGE:-$INPUT_TAG_NAME} + + _log "debug" "Create tag $INTERNAL_TAG: $INTERNAL_TAGGING_MESSAGE" + git -c user.name="$INPUT_COMMIT_USER_NAME" -c user.email="$INPUT_COMMIT_USER_EMAIL" tag -a "$INTERNAL_TAG" -m "$INTERNAL_TAGGING_MESSAGE" else - echo "No tagging message supplied. No tag will be added."; + echo "Neither tag nor tag message is set. No tag will be added."; fi } _push_to_github() { + if "$INPUT_SKIP_PUSH"; then + _log "debug" "git-push will not be executed."; + return + fi + + echo "INPUT_BRANCH value: $INPUT_BRANCH"; echo "INPUT_PUSH_OPTIONS: ${INPUT_PUSH_OPTIONS}"; - echo "::debug::Apply push options ${INPUT_PUSH_OPTIONS}"; + _log "debug" "Apply push options ${INPUT_PUSH_OPTIONS}"; # shellcheck disable=SC2206 INPUT_PUSH_OPTIONS_ARRAY=( $INPUT_PUSH_OPTIONS ); if [ -z "$INPUT_BRANCH" ] then - # Only add `--tags` option, if `$INPUT_TAGGING_MESSAGE` is set - if [ -n "$INPUT_TAGGING_MESSAGE" ] + # Only add `--tags` option, if `$INPUT_TAG_NAME` or `$INPUT_TAGGING_MESSAGE` is set + if [ -n "$INPUT_TAG_NAME" ] || [ -n "$INPUT_TAGGING_MESSAGE" ] then - echo "::debug::git push origin --tags"; + _log "debug" "git push origin --tags"; git push origin --follow-tags --atomic ${INPUT_PUSH_OPTIONS:+"${INPUT_PUSH_OPTIONS_ARRAY[@]}"}; else - echo "::debug::git push origin"; + _log "debug" "git push origin"; git push origin ${INPUT_PUSH_OPTIONS:+"${INPUT_PUSH_OPTIONS_ARRAY[@]}"}; fi else - echo "::debug::Push commit to remote branch $INPUT_BRANCH"; + _log "debug" "Push commit to remote branch $INPUT_BRANCH"; git push --set-upstream origin "HEAD:$INPUT_BRANCH" --follow-tags --atomic ${INPUT_PUSH_OPTIONS:+"${INPUT_PUSH_OPTIONS_ARRAY[@]}"}; fi } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..8a8dfcf3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,37 @@ +{ + "name": "git-auto-commit-action", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "bats": "^1.13.0", + "bats-assert": "ztombol/bats-assert", + "bats-support": "ztombol/bats-support" + } + }, + "node_modules/bats": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/bats/-/bats-1.13.0.tgz", + "integrity": "sha512-giSYKGTOcPZyJDbfbTtzAedLcNWdjCLbXYU3/MwPnjyvDXzu6Dgw8d2M+8jHhZXSmsCMSQqCp+YBsJ603UO4vQ==", + "dev": true, + "license": "MIT", + "bin": { + "bats": "bin/bats" + } + }, + "node_modules/bats-assert": { + "version": "0.3.0", + "resolved": "git+ssh://git@github.com/ztombol/bats-assert.git#9f88b4207da750093baabc4e3f41bf68f0dd3630", + "dev": true, + "peerDependencies": { + "bats-support": "git+https://github.com/ztombol/bats-support.git#v0.2.0" + } + }, + "node_modules/bats-support": { + "version": "0.3.0", + "resolved": "git+ssh://git@github.com/ztombol/bats-support.git#004e707638eedd62e0481e8cdc9223ad471f12ee", + "dev": true + } + } +} diff --git a/package.json b/package.json index 21ea2e23..e45dac4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "devDependencies": { - "bats": "^1.1.0", + "bats": "^1.13.0", "bats-assert": "ztombol/bats-assert", "bats-support": "ztombol/bats-support" }, diff --git a/tests/git-auto-commit.bats b/tests/git-auto-commit.bats index 61fc407a..9e39f7ea 100644 --- a/tests/git-auto-commit.bats +++ b/tests/git-auto-commit.bats @@ -8,6 +8,7 @@ setup() { export FAKE_LOCAL_REPOSITORY="${BATS_TEST_DIRNAME}/tests_local_repository" export FAKE_REMOTE="${BATS_TEST_DIRNAME}/tests_remote_repository" export FAKE_TEMP_LOCAL_REPOSITORY="${BATS_TEST_DIRNAME}/tests_clone_of_remote_repository" + export FAKE_FOLDER_WITHOUT_GIT_REPO="/tmp/tests_folder_without_git_repo" # While it is likely the GitHub hosted runners will use master as the default branch, # locally anyone may change that. So for tests lets grab whatever is currently set @@ -20,6 +21,7 @@ setup() { export FAKE_DEFAULT_BRANCH=$(git config init.defaultBranch) # Set default INPUT variables used by the GitHub Action + export INPUT_CREATE_GIT_TAG_ONLY=false export INPUT_REPOSITORY="${FAKE_LOCAL_REPOSITORY}" export INPUT_COMMIT_MESSAGE="Commit Message" export INPUT_BRANCH="${FAKE_DEFAULT_BRANCH}" @@ -30,13 +32,16 @@ setup() { export INPUT_COMMIT_USER_NAME="Test Suite" export INPUT_COMMIT_USER_EMAIL="test@github.com" export INPUT_COMMIT_AUTHOR="Test Suite " + export INPUT_TAG_NAME="" export INPUT_TAGGING_MESSAGE="" export INPUT_PUSH_OPTIONS="" export INPUT_SKIP_DIRTY_CHECK=false export INPUT_SKIP_FETCH=false export INPUT_SKIP_CHECKOUT=false + export INPUT_SKIP_PUSH=false export INPUT_DISABLE_GLOBBING=false export INPUT_CREATE_BRANCH=false + export INPUT_INTERNAL_GIT_BINARY=git # Set GitHub environment variables used by the GitHub Action temp_github_output_file=$(mktemp -t github_output_test.XXXXX) @@ -57,7 +62,13 @@ teardown() { rm -rf "${FAKE_LOCAL_REPOSITORY}" rm -rf "${FAKE_REMOTE}" rm -rf "${FAKE_TEMP_LOCAL_REPOSITORY}" - rm "${GITHUB_OUTPUT}" + rm -rf "${INPUT_REPOSITORY}" + + if [ -z ${GITHUB_OUTPUT+x} ]; then + echo "GITHUB_OUTPUT is not set" + else + rm "${GITHUB_OUTPUT}" + fi } # Create a fake remote repository which tests can push against @@ -112,8 +123,9 @@ cat_github_output() { assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_COMMIT_OPTIONS: " assert_line "::debug::Apply commit options " + assert_line "INPUT_TAG_NAME: " assert_line "INPUT_TAGGING_MESSAGE: " - assert_line "No tagging message supplied. No tag will be added." + assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "INPUT_PUSH_OPTIONS: " assert_line "::debug::Apply push options " assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" @@ -135,8 +147,9 @@ cat_github_output() { assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_COMMIT_OPTIONS: " assert_line "::debug::Apply commit options " + assert_line "INPUT_TAG_NAME: " assert_line "INPUT_TAGGING_MESSAGE: " - assert_line "No tagging message supplied. No tag will be added." + assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "INPUT_PUSH_OPTIONS: " assert_line "::debug::Apply push options " assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" @@ -184,7 +197,6 @@ cat_github_output() { assert_failure assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" - assert_line "INPUT_BRANCH value: ${FAKE_DEFAULT_BRANCH}" assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_COMMIT_OPTIONS: " assert_line "::debug::Apply commit options " @@ -253,7 +265,7 @@ cat_github_output() { } @test "It applies commit user and author settings" { - INPUT_COMMIT_USER_NAME="A Single Test" + INPUT_COMMIT_USER_NAME="Custom User Name" INPUT_COMMIT_USER_EMAIL="single-test@github.com" INPUT_COMMIT_AUTHOR="A Single Test " @@ -263,7 +275,7 @@ cat_github_output() { assert_success - assert_line "INPUT_COMMIT_USER_NAME: A Single Test" + assert_line "INPUT_COMMIT_USER_NAME: Custom User Name" assert_line "INPUT_COMMIT_USER_EMAIL: single-test@github.com" assert_line "INPUT_COMMIT_AUTHOR: A Single Test " assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" @@ -276,14 +288,15 @@ cat_github_output() { assert_output --partial "A Single Test" run git log -1 --pretty=format:'%cn' - assert_output --partial "A Single Test" + assert_output --partial "Custom User Name" run git log -1 --pretty=format:'%ce' assert_output --partial "single-test@github.com" } @test "It creates a tag with the commit" { - INPUT_TAGGING_MESSAGE="v1.0.0" + INPUT_TAG_NAME="v1.0.0" + INPUT_TAGGING_MESSAGE="MyProduct v1.0.0" touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt @@ -291,13 +304,15 @@ cat_github_output() { assert_success - assert_line "INPUT_TAGGING_MESSAGE: v1.0.0" - assert_line "::debug::Create tag v1.0.0" + assert_line "INPUT_TAG_NAME: v1.0.0" + assert_line "INPUT_TAGGING_MESSAGE: MyProduct v1.0.0" + + assert_line "::debug::Create tag v1.0.0: MyProduct v1.0.0" assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" # Assert a tag v1.0.0 has been created - run git tag - assert_output v1.0.0 + run git tag -n + assert_output 'v1.0.0 MyProduct v1.0.0' run git ls-remote --tags --refs assert_output --partial refs/tags/v1.0.0 @@ -338,6 +353,24 @@ cat_github_output() { assert_equal $current_sha $remote_sha } +@test "If SKIP_PUSH is true git-push will not be called" { + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + INPUT_SKIP_PUSH=true + + run git_auto_commit + + assert_success + + assert_line "::debug::git-push will not be executed." + + # Assert that the sha values are not equal on local and remote + current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})" + remote_sha="$(git rev-parse --verify --short origin/${FAKE_DEFAULT_BRANCH})" + + refute [assert_equal $current_sha $remote_sha] +} + @test "It can checkout a different branch" { # Create foo-branch and then immediately switch back to ${FAKE_DEFAULT_BRANCH} git checkout -b foo @@ -378,9 +411,11 @@ cat_github_output() { assert_equal $current_sha $remote_sha } -@test "It uses existing branch when INPUT_BRANCH is empty and INPUT_TAGGING_MESSAGE is set" { +@test "It uses existing branch when INPUT_BRANCH is empty and INPUT_TAG is set" { INPUT_BRANCH="" - INPUT_TAGGING_MESSAGE="v2.0.0" + INPUT_TAG_NAME="v2.0.0" + INPUT_TAGGING_MESSAGE="MyProduct v2.0.0" + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt @@ -388,8 +423,8 @@ cat_github_output() { assert_success - assert_line "INPUT_TAGGING_MESSAGE: v2.0.0" - assert_line "::debug::Create tag v2.0.0" + assert_line "INPUT_TAG_NAME: v2.0.0" + assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::git push origin --tags" # Assert a tag v2.0.0 has been created @@ -411,7 +446,7 @@ cat_github_output() { assert_success - assert_line "::debug::git-fetch has not been executed" + assert_line "::debug::git-fetch will not be executed." } @test "If SKIP_CHECKOUT is true git-checkout will not be called" { @@ -424,12 +459,14 @@ cat_github_output() { assert_success - assert_line "::debug::git-checkout has not been executed" + assert_line "::debug::git-checkout will not be executed." } @test "It pushes generated commit and tag to remote and actually updates the commit shas" { INPUT_BRANCH="" - INPUT_TAGGING_MESSAGE="v2.0.0" + INPUT_TAG_NAME="v2.0.0" + INPUT_TAGGING_MESSAGE="MyProduct v2.0.0" + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt @@ -437,8 +474,8 @@ cat_github_output() { assert_success - assert_line "INPUT_TAGGING_MESSAGE: v2.0.0" - assert_line "::debug::Create tag v2.0.0" + assert_line "INPUT_TAG_NAME: v2.0.0" + assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::git push origin --tags" # Assert a tag v2.0.0 has been created @@ -462,7 +499,9 @@ cat_github_output() { git checkout ${FAKE_DEFAULT_BRANCH} INPUT_BRANCH="a-new-branch" - INPUT_TAGGING_MESSAGE="v2.0.0" + INPUT_TAG_NAME="v2.0.0" + INPUT_TAGGING_MESSAGE="MyProduct v2.0.0" + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt @@ -470,8 +509,8 @@ cat_github_output() { assert_success - assert_line "INPUT_TAGGING_MESSAGE: v2.0.0" - assert_line "::debug::Create tag v2.0.0" + assert_line "INPUT_TAG_NAME: v2.0.0" + assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::Push commit to remote branch a-new-branch" # Assert a tag v2.0.0 has been created @@ -570,9 +609,8 @@ cat_github_output() { assert_line "changes_detected=true" } -@test "script fails to push commit to new branch that does not exist yet" { +@test "It pushes commit to new branch that does not exist yet" { INPUT_BRANCH="not-existend-branch" - INPUT_CREATE_BRANCH=false run git branch refute_line --partial "not-existend-branch" @@ -582,27 +620,28 @@ cat_github_output() { touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + INPUT_SKIP_CHECKOUT=true + run git_auto_commit - assert_failure + assert_success assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" assert_line "INPUT_BRANCH value: not-existend-branch" - assert_line "fatal: invalid reference: not-existend-branch" run git branch + assert_line --partial ${FAKE_DEFAULT_BRANCH} refute_line --partial "not-existend-branch" run git branch -r - refute_line --partial "origin/not-existend-branch" + assert_line --partial "origin/not-existend-branch" run cat_github_output assert_line "changes_detected=true" } -@test "It creates new local branch and pushes the new branch to remote" { +@test "It does not create new local branch and pushes the commit to a new branch on remote" { INPUT_BRANCH="not-existend-branch" - INPUT_CREATE_BRANCH=true run git branch refute_line --partial "not-existend-branch" @@ -612,6 +651,8 @@ cat_github_output() { touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + INPUT_SKIP_CHECKOUT=true + run git_auto_commit assert_success @@ -621,15 +662,19 @@ cat_github_output() { assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_COMMIT_OPTIONS: " assert_line "::debug::Apply commit options " + assert_line "INPUT_TAG_NAME: " assert_line "INPUT_TAGGING_MESSAGE: " - assert_line "No tagging message supplied. No tag will be added." + assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "INPUT_PUSH_OPTIONS: " assert_line "::debug::Apply push options " assert_line "::debug::Push commit to remote branch not-existend-branch" + # Assert that local repo is still on default branch and not on new branch. run git branch - assert_line --partial "not-existend-branch" + assert_line --partial ${FAKE_DEFAULT_BRANCH} + refute_line --partial "not-existend-branch" + # Assert branch has been created on remote run git branch -r assert_line --partial "origin/not-existend-branch" @@ -638,50 +683,7 @@ cat_github_output() { assert_line -e "commit_hash=[0-9a-f]{40}$" } -@test "it does not create new local branch if local branch already exists" { - - git checkout -b not-existend-remote-branch - git checkout ${FAKE_DEFAULT_BRANCH} - - INPUT_BRANCH="not-existend-remote-branch" - INPUT_CREATE_BRANCH=true - - run git branch - assert_line --partial "not-existend-remote-branch" - - run git branch -r - refute_line --partial "origin/not-existend-remote-branch" - - touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt - - run git_auto_commit - - assert_success - - assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" - assert_line "INPUT_BRANCH value: not-existend-remote-branch" - assert_line "INPUT_FILE_PATTERN: ." - assert_line "INPUT_COMMIT_OPTIONS: " - assert_line "::debug::Apply commit options " - assert_line "INPUT_TAGGING_MESSAGE: " - assert_line "No tagging message supplied. No tag will be added." - assert_line "INPUT_PUSH_OPTIONS: " - assert_line "::debug::Apply push options " - assert_line "::debug::Push commit to remote branch not-existend-remote-branch" - - run git branch - assert_line --partial "not-existend-remote-branch" - - run git branch -r - assert_line --partial "origin/not-existend-remote-branch" - - run cat_github_output - assert_line "changes_detected=true" - assert_line -e "commit_hash=[0-9a-f]{40}$" -} - -@test "it creates new local branch and pushes branch to remote even if the remote branch already exists" { - +@test "It creates new local branch and pushes branch to remote even if the remote branch already exists" { # Create `existing-remote-branch` on remote with changes the local repository does not yet have cd $FAKE_TEMP_LOCAL_REPOSITORY git checkout -b "existing-remote-branch" @@ -698,7 +700,7 @@ cat_github_output() { cd $FAKE_LOCAL_REPOSITORY INPUT_BRANCH="existing-remote-branch" - INPUT_CREATE_BRANCH=true + INPUT_SKIP_CHECKOUT=true run git branch refute_line --partial "existing-remote-branch" @@ -719,20 +721,22 @@ cat_github_output() { assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_COMMIT_OPTIONS: " assert_line "::debug::Apply commit options " + assert_line "INPUT_TAG_NAME: " assert_line "INPUT_TAGGING_MESSAGE: " - assert_line "No tagging message supplied. No tag will be added." + assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "INPUT_PUSH_OPTIONS: " assert_line "::debug::Apply push options " assert_line "::debug::Push commit to remote branch existing-remote-branch" run git branch - assert_line --partial "existing-remote-branch" + assert_line --partial ${FAKE_DEFAULT_BRANCH} + refute_line --partial "existing-remote-branch" run git branch -r assert_line --partial "origin/existing-remote-branch" # Assert that branch "existing-remote-branch" was updated on remote - current_sha="$(git rev-parse --verify --short existing-remote-branch)" + current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})" remote_sha="$(git rev-parse --verify --short origin/existing-remote-branch)" assert_equal $current_sha $remote_sha @@ -742,7 +746,7 @@ cat_github_output() { assert_line -e "commit_hash=[0-9a-f]{40}$" } -@test "script fails if new local branch is checked out and push fails as remote has newer commits than local" { +@test "It fails if local branch is behind remote and when remote has newer commits and skip_checkout is set to true" { # Create `existing-remote-branch` on remote with changes the local repository does not yet have cd $FAKE_TEMP_LOCAL_REPOSITORY git checkout -b "existing-remote-branch" @@ -759,7 +763,7 @@ cat_github_output() { cd $FAKE_LOCAL_REPOSITORY INPUT_BRANCH="existing-remote-branch" - INPUT_CREATE_BRANCH=true + INPUT_SKIP_CHECKOUT=true run git branch refute_line --partial "existing-remote-branch" @@ -774,23 +778,24 @@ cat_github_output() { assert_failure - assert_line "hint: Updates were rejected because the tip of your current branch is behind" + assert_line "hint: Updates were rejected because a pushed branch tip is behind its remote" # Assert that branch exists locally and on remote run git branch - assert_line --partial "existing-remote-branch" + assert_line --partial ${FAKE_DEFAULT_BRANCH} + refute_line --partial "existing-remote-branch" run git branch -r assert_line --partial "origin/existing-remote-branch" # Assert that branch "existing-remote-branch" was not updated on remote - current_sha="$(git rev-parse --verify --short existing-remote-branch)" + current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})" remote_sha="$(git rev-parse --verify --short origin/existing-remote-branch)" refute [assert_equal $current_sha $remote_sha] } -@test "It pushes commit to remote if branch already exists and local repo is behind its remote counterpart" { +@test "It fails to push commit to remote if branch already exists and local repo is behind its remote counterpart and SKIP_CHECKOUT is used" { # Create `new-branch` on remote with changes the local repository does not yet have cd $FAKE_TEMP_LOCAL_REPOSITORY @@ -809,8 +814,9 @@ cat_github_output() { cd $FAKE_LOCAL_REPOSITORY INPUT_BRANCH="new-branch" + INPUT_SKIP_CHECKOUT=true - # Assert that local remote does not know have "new-branch" locally nor does + # Assert that local remote does not have a "new-branch"-branch nor does # know about the remote branch. run git branch refute_line --partial "new-branch" @@ -822,16 +828,12 @@ cat_github_output() { run git_auto_commit - assert_success + assert_failure assert_line "INPUT_BRANCH value: new-branch" assert_line --partial "::debug::Push commit to remote branch new-branch" - # Assert that branch "new-branch" was updated on remote - current_sha="$(git rev-parse --verify --short new-branch)" - remote_sha="$(git rev-parse --verify --short origin/new-branch)" - - assert_equal $current_sha $remote_sha + assert_line --partial "Updates were rejected because a pushed branch tip is behind its remote" } @test "throws fatal error if file pattern includes files that do not exist" { @@ -968,7 +970,7 @@ cat_github_output() { assert_line --partial "another-subdirectory/new-file-3.txt" } -@test "fails to detect crlf change in files and does not detect change or commit changes" { +@test "detects if crlf in files change and does not create commit" { # Set autocrlf to true cd "${FAKE_LOCAL_REPOSITORY}" git config core.autocrlf true @@ -976,24 +978,546 @@ cat_github_output() { assert_line "true" # Add more .txt files - touch "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt - touch "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt + echo -ne "crlf test1\r\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt + echo -ne "crlf test1\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt # Run git-auto-commit to add new files to repository run git_auto_commit # Change control characters in files - sed 's/^M$//' "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt - sed 's/$/^M/' "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt + echo -ne "crlf test1\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt + echo -ne "crlf test1\r\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt # Run git-auto-commit to commit the 2 changes files run git_auto_commit assert_success - # Changes are not detected + refute_line --partial "2 files changed, 2 insertions(+), 2 deletions(-)" + assert_line --partial "warning: in the working copy of 'new-file-2.txt', LF will be replaced by CRLF the next time Git touches it" + assert_line --partial "Working tree clean. Nothing to commit." + assert_line --partial "new-file-2.txt" + # assert_line --partial "new-file-3.txt" + + # Changes are not detected + run cat_github_output + assert_line "changes_detected=false" +} + +@test "detects if crlf in files change and creates commit if the actual content of the files change" { + # Set autocrlf to true + cd "${FAKE_LOCAL_REPOSITORY}" + git config core.autocrlf true + run git config --get-all core.autocrlf + assert_line "true" + + # Add more .txt files + echo -ne "crlf test1\r\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt + echo -ne "crlf test1\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt + + # Run git-auto-commit to add new files to repository + run git_auto_commit + + # Change control characters in files + echo -ne "crlf test2\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-2.txt + echo -ne "crlf test2\r\n" > "${FAKE_LOCAL_REPOSITORY}"/new-file-3.txt + + # Run git-auto-commit to commit the 2 changes files + run git_auto_commit + + assert_success + + assert_line --partial "2 files changed, 2 insertions(+), 2 deletions(-)" + assert_line --partial "warning: in the working copy of 'new-file-2.txt', LF will be replaced by CRLF the next time Git touches it" + + assert_line --partial "new-file-2.txt" + # assert_line --partial "new-file-3.txt" + + # Changes are detected + run cat_github_output + assert_line "changes_detected=true" +} + + +@test "It uses old set-output syntax if GITHUB_OUTPUT environment is not available when changes are committed" { + unset GITHUB_OUTPUT + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "INPUT_BRANCH value: ${FAKE_DEFAULT_BRANCH}" + assert_line "INPUT_FILE_PATTERN: ." + assert_line "INPUT_COMMIT_OPTIONS: " + assert_line "::debug::Apply commit options " + assert_line "INPUT_TAG_NAME: " + assert_line "INPUT_TAGGING_MESSAGE: " + assert_line "Neither tag nor tag message is set. No tag will be added." + assert_line "INPUT_PUSH_OPTIONS: " + assert_line "::debug::Apply push options " + assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" + + assert_line "::set-output name=changes_detected::true" + assert_line -e "::set-output name=commit_hash::[0-9a-f]{40}$" +} + +@test "It uses old set-output syntax if GITHUB_OUTPUT environment is not available when no changes have been detected" { + unset GITHUB_OUTPUT + + run git_auto_commit + + assert_success + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "Working tree clean. Nothing to commit." + + assert_line "::set-output name=changes_detected::false" + refute_line -e "::set-output name=commit_hash::[0-9a-f]{40}$" +} + +@test "It fails hard if git is not available" { + INPUT_INTERNAL_GIT_BINARY=binary-does-not-exist + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_failure; + assert_line "::error::git-auto-commit could not find git binary. Please make sure git is available." +} + +@test "It creates multi-line commit messages" { + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + COMMIT_MESSAGE=$(cat <<-END + this commit message + has multiple lines +END +) + + INPUT_COMMIT_MESSAGE=$COMMIT_MESSAGE + + run git_auto_commit + + assert_success + + # Assert last commit was signed off + run git log -n 1 + assert_output --partial $COMMIT_MESSAGE +} + +@test "It exits with error message if entrypoint.sh is being run not in a git repository" { + INPUT_REPOSITORY="${FAKE_FOLDER_WITHOUT_GIT_REPO}" + + mkdir "${INPUT_REPOSITORY}" + + run git_auto_commit + + assert_failure; + assert_line "::error::Not a git repository. Please make sure to run this action in a git repository. Adjust the `repository` input if necessary." +} + +@test "It detects if the repository is in a detached state and logs a warning" { + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + # Bring local repository into a detached state + prev_commit=$(git rev-parse HEAD~1); + git checkout "$prev_commit"; + + touch "${FAKE_TEMP_LOCAL_REPOSITORY}"/remote-files{4,5,6}.txt + + run git_auto_commit + + assert_success; + assert_line "::warning::Repository is in a detached HEAD state. git-auto-commit will likely handle this automatically. To avoid it, check out a branch using the ref option in actions/checkout." +} + +@test "it creates a tag if create_git_tag_only is set to true and a message has been supplied" { + INPUT_CREATE_GIT_TAG_ONLY=true + INPUT_TAG_NAME=v1.0.0 + INPUT_TAGGING_MESSAGE="MyProduct v1.0.0" + + run git_auto_commit + + assert_success + + assert_line "::debug::Create git tag only" + + assert_line "::debug::Create tag v1.0.0: MyProduct v1.0.0" + refute_line "Neither tag nor tag message is set. No tag will be added." + + assert_line "::debug::Apply push options " + assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" + + run cat_github_output + assert_line "create_git_tag_only=true" + refute_line "changes_detected=false" + refute_line -e "commit_hash=[0-9a-f]{40}$" + + # Assert a tag v1.0.0 has been created + run git tag -n + assert_output 'v1.0.0 MyProduct v1.0.0' + + run git ls-remote --tags --refs + assert_output --partial refs/tags/v1.0.0 +} + +@test "it output no tagging message supplied if no tagging message is set but create_git_tag_only is set to true" { + INPUT_CREATE_GIT_TAG_ONLY=true + INPUT_TAG_NAME="" + INPUT_TAGGING_MESSAGE="" + + run git_auto_commit + + assert_success + + assert_line "INPUT_TAG_NAME: " + assert_line "INPUT_TAGGING_MESSAGE: " + assert_line "Neither tag nor tag message is set. No tag will be added." + assert_line "::debug::Create git tag only" + + run cat_github_output + assert_line "create_git_tag_only=true" + refute_line "changes_detected=false" + refute_line -e "commit_hash=[0-9a-f]{40}$" + + # Assert no tag has been created + run git tag + assert_output "" +} + +@test "script fails to push commit to new branch that does not exist yet" { + INPUT_BRANCH="not-existend-branch" + INPUT_CREATE_BRANCH=false + + run git branch + refute_line --partial "not-existend-branch" + + run git branch -r + refute_line --partial "origin/not-existend-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_failure + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "INPUT_BRANCH value: not-existend-branch" + assert_line "fatal: invalid reference: not-existend-branch" - refute_line --partial "new-file-2.txt" - refute_line --partial "new-file-3.txt" + run git branch + refute_line --partial "not-existend-branch" + + run git branch -r + refute_line --partial "origin/not-existend-branch" + + run cat_github_output + assert_line "changes_detected=true" +} + +@test "It creates new local branch and pushes the new branch to remote" { + INPUT_BRANCH="not-existend-branch" + INPUT_CREATE_BRANCH=true + + run git branch + refute_line --partial "not-existend-branch" + + run git branch -r + refute_line --partial "origin/not-existend-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "INPUT_BRANCH value: not-existend-branch" + assert_line "INPUT_FILE_PATTERN: ." + assert_line "INPUT_COMMIT_OPTIONS: " + assert_line "::debug::Apply commit options " + assert_line "INPUT_TAGGING_MESSAGE: " + assert_line "Neither tag nor tag message is set. No tag will be added." + assert_line "INPUT_PUSH_OPTIONS: " + assert_line "::debug::Apply push options " + assert_line "::debug::Push commit to remote branch not-existend-branch" + + run git branch + assert_line --partial "not-existend-branch" + + run git branch -r + assert_line --partial "origin/not-existend-branch" + + run cat_github_output + assert_line "changes_detected=true" + assert_line -e "commit_hash=[0-9a-f]{40}$" +} + +@test "It does not create new local branch if local branch already exists and SKIP_CHECKOUT is true" { + git checkout -b not-existend-remote-branch + git checkout ${FAKE_DEFAULT_BRANCH} + + INPUT_BRANCH="not-existend-remote-branch" + INPUT_SKIP_CHECKOUT=true + + run git branch + assert_line --partial "not-existend-remote-branch" + + run git branch -r + refute_line --partial "origin/not-existend-remote-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "INPUT_BRANCH value: not-existend-remote-branch" + assert_line "INPUT_FILE_PATTERN: ." + assert_line "INPUT_COMMIT_OPTIONS: " + assert_line "::debug::Apply commit options " + assert_line "INPUT_TAGGING_MESSAGE: " + assert_line "Neither tag nor tag message is set. No tag will be added." + assert_line "INPUT_PUSH_OPTIONS: " + assert_line "::debug::Apply push options " + assert_line "::debug::Push commit to remote branch not-existend-remote-branch" + + # Assert checked out branch is still the same. + run git rev-parse --abbrev-ref HEAD + assert_line --partial ${FAKE_DEFAULT_BRANCH} + refute_line --partial "not-existend-remote-branch" + + run git branch + assert_line --partial "not-existend-remote-branch" + + run git branch -r + assert_line --partial "origin/not-existend-remote-branch" + + run cat_github_output + assert_line "changes_detected=true" + assert_line -e "commit_hash=[0-9a-f]{40}$" +} + +@test "it creates new local branch and pushes branch to remote even if the remote branch already exists" { + + # Create `existing-remote-branch` on remote with changes the local repository does not yet have + cd $FAKE_TEMP_LOCAL_REPOSITORY + git checkout -b "existing-remote-branch" + touch new-branch-file.txt + git add new-branch-file.txt + git commit -m "Add additional file" + git push origin existing-remote-branch + + run git branch + assert_line --partial "existing-remote-branch" + + # --------- + # Switch to our regular local repository and run `git-auto-commit` + cd $FAKE_LOCAL_REPOSITORY + + INPUT_BRANCH="existing-remote-branch" + INPUT_CREATE_BRANCH=true + + run git branch + refute_line --partial "existing-remote-branch" + + run git fetch --all + run git pull origin existing-remote-branch + run git branch -r + assert_line --partial "origin/existing-remote-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" + assert_line "INPUT_BRANCH value: existing-remote-branch" + assert_line "INPUT_FILE_PATTERN: ." + assert_line "INPUT_COMMIT_OPTIONS: " + assert_line "::debug::Apply commit options " + assert_line "INPUT_TAGGING_MESSAGE: " + assert_line "Neither tag nor tag message is set. No tag will be added." + assert_line "INPUT_PUSH_OPTIONS: " + assert_line "::debug::Apply push options " + assert_line "::debug::Push commit to remote branch existing-remote-branch" + + run git branch + assert_line --partial "existing-remote-branch" + + run git branch -r + assert_line --partial "origin/existing-remote-branch" + + # Assert that branch "existing-remote-branch" was updated on remote + current_sha="$(git rev-parse --verify --short existing-remote-branch)" + remote_sha="$(git rev-parse --verify --short origin/existing-remote-branch)" + + assert_equal $current_sha $remote_sha + + run cat_github_output + assert_line "changes_detected=true" + assert_line -e "commit_hash=[0-9a-f]{40}$" +} + +@test "script fails if new local branch is checked out and push fails as remote has newer commits than local" { + # Create `existing-remote-branch` on remote with changes the local repository does not yet have + cd $FAKE_TEMP_LOCAL_REPOSITORY + git checkout -b "existing-remote-branch" + touch new-branch-file.txt + git add new-branch-file.txt + git commit -m "Add additional file" + git push origin existing-remote-branch + + run git branch + assert_line --partial "existing-remote-branch" + + # --------- + # Switch to our regular local repository and run `git-auto-commit` + cd $FAKE_LOCAL_REPOSITORY + + INPUT_BRANCH="existing-remote-branch" + INPUT_CREATE_BRANCH=true + + run git branch + refute_line --partial "existing-remote-branch" + + run git fetch --all + run git branch -r + assert_line --partial "origin/existing-remote-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_failure + + assert_line "hint: Updates were rejected because the tip of your current branch is behind" + + # Assert that branch exists locally and on remote + run git branch + assert_line --partial "existing-remote-branch" + + run git branch -r + assert_line --partial "origin/existing-remote-branch" + + # Assert that branch "existing-remote-branch" was not updated on remote + current_sha="$(git rev-parse --verify --short existing-remote-branch)" + remote_sha="$(git rev-parse --verify --short origin/existing-remote-branch)" + + refute [assert_equal $current_sha $remote_sha] +} + +@test "It pushes commit to remote if branch already exists and local repo is behind its remote counterpart" { + # Create `new-branch` on remote with changes the local repository does not yet have + cd $FAKE_TEMP_LOCAL_REPOSITORY + + git checkout -b "new-branch" + touch new-branch-file.txt + git add new-branch-file.txt + + git commit --quiet -m "Add additional file" + git push origin new-branch + + run git branch -r + assert_line --partial "origin/new-branch" + + # --------- + # Switch to our regular local repository and run `git-auto-commit` + cd $FAKE_LOCAL_REPOSITORY + + INPUT_BRANCH="new-branch" + + # Assert that local remote does not know have "new-branch" locally nor does + # know about the remote branch. + run git branch + refute_line --partial "new-branch" + + run git branch -r + refute_line --partial "origin/new-branch" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_BRANCH value: new-branch" + assert_line --partial "::debug::Push commit to remote branch new-branch" + + # Assert that branch "new-branch" was updated on remote + current_sha="$(git rev-parse --verify --short new-branch)" + remote_sha="$(git rev-parse --verify --short origin/new-branch)" + + assert_equal $current_sha $remote_sha +} + +@test "Set a tag message only" { + INPUT_TAGGING_MESSAGE="v1.0.0" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_TAG_NAME: " + assert_line "INPUT_TAGGING_MESSAGE: v1.0.0" + + assert_line "::debug::Create tag v1.0.0: v1.0.0" + assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" + + # Assert a tag v1.0.0 has been created + run git tag -n + assert_output 'v1.0.0 v1.0.0' + + run git ls-remote --tags --refs + assert_output --partial refs/tags/v1.0.0 + + # Assert that the commit has been pushed with --force and + # sha values are equal on local and remote + current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})" + remote_sha="$(git rev-parse --verify --short origin/${FAKE_DEFAULT_BRANCH})" + + assert_equal $current_sha $remote_sha +} + +@test "Set a tag only" { + INPUT_TAG_NAME="v1.0.0" + + touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt + + run git_auto_commit + + assert_success + + assert_line "INPUT_TAG_NAME: v1.0.0" + assert_line "INPUT_TAGGING_MESSAGE: " + + assert_line "::debug::Create tag v1.0.0: v1.0.0" + assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" + + # Assert a tag v1.0.0 has been created + run git tag -n + assert_output 'v1.0.0 v1.0.0' + + run git ls-remote --tags --refs + assert_output --partial refs/tags/v1.0.0 + + # Assert that the commit has been pushed with --force and + # sha values are equal on local and remote + current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})" + remote_sha="$(git rev-parse --verify --short origin/${FAKE_DEFAULT_BRANCH})" + + assert_equal $current_sha $remote_sha } diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index efbe05d5..00000000 --- a/yarn.lock +++ /dev/null @@ -1,16 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -bats-assert@ztombol/bats-assert: - version "0.3.0" - resolved "https://codeload.github.com/ztombol/bats-assert/tar.gz/9f88b4207da750093baabc4e3f41bf68f0dd3630" - -bats-support@ztombol/bats-support: - version "0.3.0" - resolved "https://codeload.github.com/ztombol/bats-support/tar.gz/004e707638eedd62e0481e8cdc9223ad471f12ee" - -bats@^1.1.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/bats/-/bats-1.7.0.tgz#caae958b1d211eda6b1322ac7792515de40165a2" - integrity sha512-pt5zjJQUB4+JI8Si+z/IAWc8yhMyhdZs6IXMCKgzF768dUIyW5xyBstWtDI5uGSa80v7UQOhh+w0GA4p4+01Bg==