Compare commits

..

1 Commits

Author SHA1 Message Date
Stefan Zweifel
6c6996c9a1 WIP Hooks 2025-09-16 19:16:07 +02:00
10 changed files with 819 additions and 487 deletions

View File

@@ -16,7 +16,7 @@ jobs:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}

242
.github/workflows/hook-examples.yml vendored Normal file
View File

@@ -0,0 +1,242 @@
name: Hook Examples
on:
workflow_dispatch:
push:
branches: [ main ]
jobs:
# Example 1: Use pre_status_hook to unshallow repository
unshallow-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1 # Create shallow clone
- name: Make some changes
run: |
echo "$(date): Updated by workflow" >> updates.log
- name: Commit with unshallow hook
uses: ./
with:
commit_message: "Update logs with unshallow support"
pre_status_hook: |
echo "Checking if repository is shallow..."
if git rev-parse --is-shallow-repository 2>/dev/null | grep -q true; then
echo "Repository is shallow, running git fetch --unshallow"
git fetch --unshallow
else
echo "Repository is not shallow"
fi
# Example 2: Use pre_commit_hook for validation
validation-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Make changes to JavaScript files
run: |
echo "console.log('Hello World');" > example.js
- name: Commit with validation hook
uses: ./
with:
commit_message: "Add example JavaScript file"
pre_commit_hook: |
echo "Running pre-commit validation..."
# Validate JavaScript syntax
find . -name "*.js" -not -path "./node_modules/*" -exec node -c {} \;
echo "JavaScript validation passed!"
# Run tests if they exist
if [ -f "package.json" ] && npm run test --if-present; then
echo "Tests passed!"
fi
# Example 3: Use pre_commit_hook to generate additional files
file-generation-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Make some changes
run: |
echo "New feature added" > feature.txt
- name: Commit with file generation hook
uses: ./
with:
commit_message: "Add feature with generated manifest"
pre_commit_hook: |
echo "Generating build manifest..."
# Create build info file
cat > build-info.json << EOF
{
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"commit": "$GITHUB_SHA",
"workflow": "$GITHUB_WORKFLOW",
"run_id": "$GITHUB_RUN_ID",
"actor": "$GITHUB_ACTOR"
}
EOF
echo "Generated build-info.json"
cat build-info.json
# Example 4: Use pre_push_hook for final validation
pre-push-validation-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Make changes
run: |
echo "Important data: $(date)" > important-file.txt
- name: Commit with pre-push validation
uses: ./
with:
commit_message: "Add important file with validation"
pre_push_hook: |
echo "Running final validation before push..."
# Check if important file exists and has content
if [ ! -f "important-file.txt" ] || [ ! -s "important-file.txt" ]; then
echo "ERROR: important-file.txt is missing or empty!"
exit 1
fi
# Check git log for the commit we're about to push
echo "Latest commit details:"
git log -1 --oneline
echo "Pre-push validation passed!"
# Example 5: Use post_push_hook for notifications
notification-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update documentation
run: |
echo "# Documentation Updated" > README.md
echo "Last updated: $(date)" >> README.md
- name: Commit with notification hook
uses: ./
with:
commit_message: "Update documentation"
post_push_hook: |
echo "Changes successfully pushed!"
# Get the commit hash that was just pushed
COMMIT_HASH=$(git rev-parse HEAD)
echo "Pushed commit: $COMMIT_HASH"
# Create a summary
echo "📝 Documentation updated successfully" >> $GITHUB_STEP_SUMMARY
echo "- Commit: \`$COMMIT_HASH\`" >> $GITHUB_STEP_SUMMARY
echo "- Time: $(date)" >> $GITHUB_STEP_SUMMARY
# In a real scenario, you might send notifications to Slack, Discord, etc.
echo "This is where you would send notifications to your team!"
# Example 6: Multiple hooks working together
comprehensive-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup environment
run: |
echo "Setting up project..."
mkdir -p logs
- name: Make changes
run: |
echo "Feature implementation" > feature.txt
echo "$(date): Feature added" > logs/changes.log
- name: Commit with all hooks
uses: ./
with:
commit_message: "Add comprehensive feature"
pre_status_hook: |
echo "🔍 Pre-status: Preparing repository..."
git status --porcelain
pre_commit_hook: |
echo "🛠️ Pre-commit: Generating metadata..."
# Generate changelog entry
echo "## $(date +%Y-%m-%d)" > CHANGELOG_ENTRY.md
echo "- Added comprehensive feature" >> CHANGELOG_ENTRY.md
# Update version file
echo "1.0.$(date +%s)" > VERSION
pre_push_hook: |
echo "✅ Pre-push: Final validation..."
# Validate all required files exist
required_files=("feature.txt" "logs/changes.log" "CHANGELOG_ENTRY.md" "VERSION")
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
echo "ERROR: Required file $file is missing!"
exit 1
fi
done
echo "All validations passed!"
post_push_hook: |
echo "🎉 Post-push: Cleanup and notification..."
# Clean up temporary files if any
rm -f /tmp/build-*
# Log success
echo "Deployment completed at $(date)" >> logs/deployment.log
echo "Comprehensive feature deployment completed!"
# Example 7: Error handling demonstration
error-handling-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Make changes
run: |
echo "Some changes" > test-file.txt
- name: Demonstrate hook failure (this will fail)
uses: ./
continue-on-error: true
with:
commit_message: "This commit should fail"
pre_commit_hook: |
echo "Running validation that will fail..."
# This will cause the hook to fail
if [ "$(cat test-file.txt)" = "Some changes" ]; then
echo "ERROR: File content not allowed!"
exit 1
fi
- name: Show that workflow continues after failure
run: |
echo "This step runs even if the previous step failed due to continue-on-error: true"

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Lint Code Base - name: Lint Code Base
uses: github/super-linter@v7 uses: github/super-linter@v7

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Install testing dependencies - name: Install testing dependencies
run: yarn install run: yarn install

View File

@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
ref: master ref: master
@@ -27,7 +27,7 @@ jobs:
latest-version: ${{ github.event.release.name }} latest-version: ${{ github.event.release.name }}
- name: Commit updated CHANGELOG - name: Commit updated CHANGELOG
uses: stefanzweifel/git-auto-commit-action@v7 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
branch: master branch: master
commit_message: Update CHANGELOG commit_message: Update CHANGELOG

218
README.md
View File

@@ -19,7 +19,7 @@ Adding git-auto-commit to your Workflow only takes a couple lines of code.
2. Add the following step at the end of your job, after other steps that might add or change files. 2. Add the following step at the end of your job, after other steps that might add or change files.
```yaml ```yaml
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
``` ```
Your Workflow should look similar to this example. Your Workflow should look similar to this example.
@@ -39,7 +39,7 @@ jobs:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
@@ -47,7 +47,7 @@ jobs:
# … # …
# Commit all changed files back to the repository # Commit all changed files back to the repository
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
``` ```
> [!NOTE] > [!NOTE]
@@ -56,7 +56,7 @@ jobs:
The following is an extended example with all available options. The following is an extended example with all available options.
```yaml ```yaml
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
with: with:
# Optional. Commit message for the created commit. # Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes" # Defaults to "Apply automatic changes"
@@ -85,15 +85,10 @@ The following is an extended example with all available options.
commit_user_name: My GitHub Actions Bot # defaults to "github-actions[bot]" commit_user_name: My GitHub Actions Bot # defaults to "github-actions[bot]"
commit_user_email: my-github-actions-bot@example.org # defaults to "41898282+github-actions[bot]@users.noreply.github.com" commit_user_email: my-github-actions-bot@example.org # defaults to "41898282+github-actions[bot]@users.noreply.github.com"
commit_author: Author <actions@github.com> # defaults to "username <numeric_id+username@users.noreply.github.com>", where "numeric_id" and "username" belong to the author of the commit that triggered the run commit_author: Author <actions@github.com> # defaults to "username <numeric_id+username@users.noreply.github.com>", where "numeric_id" and "username" belong to the author of the commit that triggered the run
# 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. Message to annotate the created tag with. # Optional. Tag name being created in the local repository and
# 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. # pushed to remote repository and defined branch.
tagging_message: 'Codename "Sunshine"' tagging_message: 'v1.0.0'
# Optional. Option used by `git-status` to determine if the repository is # Optional. Option used by `git-status` to determine if the repository is
# dirty. See https://git-scm.com/docs/git-status#_options # dirty. See https://git-scm.com/docs/git-status#_options
@@ -110,21 +105,12 @@ The following is an extended example with all available options.
# Optional. Disable dirty check and always try to create a commit and push # 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
# Optional. Skip internal call to `git checkout`
skip_checkout: true
# Optional. Prevents the shell from expanding filenames. # Optional. Prevents the shell from expanding filenames.
# Details: https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html # Details: https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html
disable_globbing: true 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. # 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`. # Skips dirty check and changed files. Must be used with `tagging_message`.
create_git_tag_only: false create_git_tag_only: false
``` ```
@@ -155,14 +141,14 @@ jobs:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
- name: Run php-cs-fixer - name: Run php-cs-fixer
uses: docker://oskarstark/php-cs-fixer-ga uses: docker://oskarstark/php-cs-fixer-ga
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_message: Apply php-cs-fixer changes commit_message: Apply php-cs-fixer changes
``` ```
@@ -184,7 +170,7 @@ You can use these outputs to trigger other Actions in your Workflow run based on
### Example ### Example
```yaml ```yaml
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
id: auto-commit-action #mandatory for the output to show up in ${{ steps }} id: auto-commit-action #mandatory for the output to show up in ${{ steps }}
with: with:
commit_message: Apply php-cs-fixer changes commit_message: Apply php-cs-fixer changes
@@ -220,7 +206,7 @@ You must use `action/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: In non-`push` events, such as `pull_request`, make sure to specify the `ref` to check out:
```yaml ```yaml
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
ref: ${{ github.head_ref }} ref: ${{ github.head_ref }}
``` ```
@@ -238,7 +224,7 @@ 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. 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 ```yaml
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
token: ${{ secrets.PAT }} token: ${{ secrets.PAT }}
``` ```
@@ -258,6 +244,165 @@ If you would like to prevent this, you can add `skip-checks:true` to the commit
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. 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.
## Hooks
The action supports custom bash scripts that can be executed at various points during the git workflow. This allows for custom preparation, validation, or post-processing steps.
### Available Hooks
- **`pre_status_hook`** - Executed before checking git status. Useful for repository preparation like `git fetch --unshallow`.
- **`pre_commit_hook`** - Executed after detecting changes but before adding files. Allows modification of files that will be included in the commit.
- **`pre_push_hook`** - Executed after committing but before pushing to remote. Useful for final validation.
- **`post_push_hook`** - Executed after successfully pushing changes. Useful for notifications or cleanup.
### Hook Examples
#### Unshallow Repository Before Checking Status
This example addresses the common issue where workflows use shallow checkouts (`fetch-depth: 1`) for performance, but need to unshallow before merging or when full history is required for certain operations.
```yaml
name: Update Submodules
on: [push]
jobs:
update-submodules:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1 # Shallow checkout for performance
submodules: recursive
- name: Update submodule to latest
run: |
git submodule update --remote
- name: Commit submodule updates
uses: stefanzweifel/git-auto-commit-action@v6
with:
file_pattern: .gitmodules *
commit_message: "Update submodules to latest versions"
pre_status_hook: |
# Only unshallow if we have changes and need to merge
if git status --porcelain | grep -q .; then
echo "Changes detected, checking if repository is shallow..."
if git rev-parse --is-shallow-repository 2>/dev/null | grep -q true; then
echo "Repository is shallow, running git fetch --unshallow"
git fetch --unshallow
fi
fi
```
#### Addressing "refusing to merge unrelated histories" Error
This specific example addresses the use case discussed in [GitHub Issue #365](https://github.com/stefanzweifel/git-auto-commit-action/discussions/365), where shallow repositories can cause merge conflicts:
```yaml
name: Update Dependencies with Unshallow
on:
schedule:
- cron: '0 2 * * 1' # Weekly updates
workflow_dispatch:
jobs:
update-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1 # Start with shallow clone for performance
- name: Update dependencies
run: |
# Your dependency update commands here
npm update
# or pip install -r requirements.txt --upgrade
# or bundle update
- name: Commit dependency updates
uses: stefanzweifel/git-auto-commit-action@v6
with:
file_pattern: 'package*.json yarn.lock requirements.txt Gemfile.lock'
commit_message: 'Update dependencies'
pre_status_hook: |
# Check if repository is shallow and unshallow if changes are detected
if git status --porcelain | grep -q .; then
echo "Dependencies have been updated, checking repository depth..."
if git rev-parse --is-shallow-repository 2>/dev/null | grep -q true; then
echo "Repository is shallow ($(git rev-list --count HEAD) commits), running git fetch --unshallow"
git fetch --unshallow
echo "Repository unshallowed successfully"
else
echo "Repository is not shallow ($(git rev-list --count HEAD) commits)"
fi
else
echo "No dependency changes detected, skipping unshallow"
fi
```
#### Validate Files Before Committing
```yaml
- name: Commit with validation
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Apply automatic changes
pre_commit_hook: |
echo "Running validation..."
npm run lint
npm run test
```
#### Generate Additional Files in Pre-Commit Hook
```yaml
- name: Commit with file generation
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Update files and manifest
pre_commit_hook: |
echo "Generating manifest..."
echo "Build timestamp: $(date)" > build-info.txt
echo "Commit: $GITHUB_SHA" >> build-info.txt
```
#### Notify After Successful Push
```yaml
- name: Commit and notify
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Apply automatic changes
post_push_hook: |
echo "Changes successfully pushed!"
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Code changes have been committed and pushed"}' \
"$SLACK_WEBHOOK_URL"
```
### Hook Error Handling
If any hook fails (exits with non-zero status), the entire action will fail and stop execution. This ensures that validation hooks can prevent commits/pushes when issues are detected.
```yaml
- name: Commit with strict validation
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: Apply automatic changes
pre_commit_hook: |
# This will fail the action if any .js files have syntax errors
find . -name "*.js" -exec node -c {} \;
```
### Hook Execution Context
- Hooks are executed in the repository directory
- Hooks have access to all git commands and repository state
- Hooks can access GitHub Actions environment variables
- Files created/modified by `pre_commit_hook` will be included in the commit
- Hooks run in bash and support multi-line scripts
## Advanced Uses ## Advanced Uses
### Multiline Commit Messages ### Multiline Commit Messages
@@ -284,7 +429,7 @@ The example below can be used as a starting point to generate a multiline commit
# Quick and dirty step to get rid of the temporary file holding the commit message # Quick and dirty step to get rid of the temporary file holding the commit message
- run: rm -rf commitmessage.txt - run: rm -rf commitmessage.txt
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
id: commit id: commit
with: with:
commit_message: ${{ steps.commit_message_step.outputs.commit_message }} commit_message: ${{ steps.commit_message_step.outputs.commit_message }}
@@ -308,7 +453,7 @@ As git-auto-commit by default does not use **your** username and email when crea
git_commit_gpgsign: true git_commit_gpgsign: true
- name: "Commit and push changes" - name: "Commit and push changes"
uses: stefanzweifel/git-auto-commit-action@v7 uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_author: "${{ steps.import-gpg.outputs.name }} <${{ steps.import-gpg.outputs.email }}>" commit_author: "${{ steps.import-gpg.outputs.name }} <${{ steps.import-gpg.outputs.email }}>"
commit_user_name: ${{ steps.import-gpg.outputs.name }} commit_user_name: ${{ steps.import-gpg.outputs.name }}
@@ -371,7 +516,7 @@ jobs:
contents: write contents: write
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
# Checkout the fork/head-repository and push changes to the fork. # Checkout the fork/head-repository and push changes to the fork.
# If you skip this, the base repository will be checked out and changes # If you skip this, the base repository will be checked out and changes
@@ -385,7 +530,7 @@ jobs:
- name: Run php-cs-fixer - name: Run php-cs-fixer
uses: docker://oskarstark/php-cs-fixer-ga uses: docker://oskarstark/php-cs-fixer-ga
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
``` ```
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/). 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/).
@@ -420,13 +565,12 @@ The steps in your workflow might look like this:
echo "message=$(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 echo "author=$(git log -1 --pretty=\"%an <%ae>\")" >> $GITHUB_OUTPUT
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_author: ${{ steps.last-commit.outputs.author }} commit_author: ${{ steps.last-commit.outputs.author }}
commit_message: ${{ steps.last-commit.outputs.message }} commit_message: ${{ steps.last-commit.outputs.message }}
commit_options: '--amend --no-edit' commit_options: '--amend --no-edit'
push_options: '--force' push_options: '--force'
skip_fetch: true
``` ```
See discussion in [#159](https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950) for details. See discussion in [#159](https://github.com/stefanzweifel/git-auto-commit-action/issues/159#issuecomment-845347950) for details.
@@ -463,7 +607,7 @@ If you create a personal access token (classic), apply the `repo` and `workflow`
If you create a fine-grained personal access token, apply the `Contents`-permissions. If you create a fine-grained personal access token, apply the `Contents`-permissions.
```yaml ```yaml
- uses: actions/checkout@v5 - uses: actions/checkout@v4
with: with:
# 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". # 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 }} token: ${{ secrets.PAT || secrets.GITHUB_TOKEN }}
@@ -477,7 +621,7 @@ You can learn more about Personal Access Token in the [GitHub documentation](htt
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://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.
```yaml ```yaml
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
with: with:
commit_message: Apply php-cs-fixer changes commit_message: Apply php-cs-fixer changes
push_options: --force push_options: --force
@@ -507,7 +651,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. To fix this add `disable_globbing: true` to your Workflow.
```yaml ```yaml
- uses: stefanzweifel/git-auto-commit-action@v7 - uses: stefanzweifel/git-auto-commit-action@v5
with: with:
file_pattern: '*.md' file_pattern: '*.md'
disable_globbing: true disable_globbing: true
@@ -535,7 +679,7 @@ yarn test
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](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 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. 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@v5` 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).) (More information about this [here](https://help.github.com/en/actions/building-actions/about-actions#versioning-your-action).)
## Credits ## Credits

View File

@@ -1,12 +1,5 @@
# Upgrading # 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` option.
(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 ## From v5 to v6
The following options have been removed from git-auto-commit and can be removed from your workflows. The following options have been removed from git-auto-commit and can be removed from your workflows.

View File

@@ -1,5 +1,5 @@
name: Git Auto Commit name: Git Auto Commit
description: 'Automatically commits files which have been changed during the workflow run and push changes back to remote repository.' description: "Automatically commits files which have been changed during the workflow run and push changes back to remote repository."
author: Stefan Zweifel <stefan@stefanzweifel.dev> author: Stefan Zweifel <stefan@stefanzweifel.dev>
@@ -15,23 +15,23 @@ inputs:
commit_options: commit_options:
description: Commit options (eg. --no-verify) description: Commit options (eg. --no-verify)
required: false required: false
default: '' default: ""
add_options: add_options:
description: Add options (eg. -u) description: Add options (eg. -u)
required: false required: false
default: '' default: ""
status_options: status_options:
description: Status options (eg. --untracked-files=no) description: Status options (eg. --untracked-files=no)
required: false required: false
default: '' default: ""
file_pattern: file_pattern:
description: File pattern used for `git add`. For example `src/*.js` description: File pattern used for `git add`. For example `src/*.js`
required: false required: false
default: '.' default: "."
repository: repository:
description: Local file path to the git repository. Defaults to the current directory (`.`) description: Local file path to the git repository. Defaults to the current directory (`.`)
required: false required: false
default: '.' default: "."
commit_user_name: commit_user_name:
description: Name used for the commit user description: Name used for the commit user
required: false required: false
@@ -44,36 +44,21 @@ inputs:
description: Value used for the commit author. Defaults to the username of whoever triggered this workflow run. description: Value used for the commit author. Defaults to the username of whoever triggered this workflow run.
required: false required: false
default: ${{ github.actor }} <${{ github.actor_id }}+${{ 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: tagging_message:
description: Tagging message used for creating a new git tag with the commit. Keep this empty, if no tag should be created. description: Message used to create a new git tag with the commit. Keep this empty, if no tag should be created.
required: false required: false
default: '' default: ""
push_options: push_options:
description: Push options (eg. --force) description: Push options (eg. --force)
required: false required: false
default: '' default: ""
skip_dirty_check: skip_dirty_check:
description: Skip the check if the git repository is dirty and always try to create a commit. description: Skip the check if the git repository is dirty and always try to create a commit.
required: false required: false
default: false default: false
skip_fetch:
description: Skip the call to git-fetch.
required: false
default: false
skip_checkout:
description: Skip the call to git-checkout.
required: false
default: false
disable_globbing: disable_globbing:
description: Stop the shell from expanding filenames (https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html) description: Stop the shell from expanding filenames (https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html)
default: 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.
default: false
create_git_tag_only: create_git_tag_only:
description: Perform a clean git tag and push, without commiting anything description: Perform a clean git tag and push, without commiting anything
required: false required: false
@@ -81,7 +66,33 @@ inputs:
internal_git_binary: internal_git_binary:
description: Internal use only! Path to git binary used to check if git is available. (Don't change this!) description: Internal use only! Path to git binary used to check if git is available. (Don't change this!)
default: git default: git
pre_status_hook:
description: Bash script to execute before checking git status. Useful for git fetch --unshallow or other repository preparation.
required: false
default: ""
pre_commit_hook:
description: Bash script to execute before committing changes. Useful for validation or last-minute file modifications.
required: false
default: ""
pre_push_hook:
description: Bash script to execute before pushing changes to remote. Useful for final validation.
required: false
default: ""
post_push_hook:
description: Bash script to execute after successfully pushing changes. Useful for notifications or cleanup.
required: false
default: ""
skip_fetch:
description: "Deprecated: skip_fetch has been removed in v6. It does not have any effect anymore."
required: false
default: false
skip_checkout:
description: "Deprecated: skip_checkout has been removed in v6. It does not have any effect anymore."
required: false
default: false
create_branch:
description: "Deprecated: create_branch has been removed in v6. It does not have any effect anymore."
default: false
outputs: outputs:
changes_detected: changes_detected:
@@ -92,9 +103,9 @@ outputs:
description: Value is "true", if a git tag was created using the `create_git_tag_only`-input. description: Value is "true", if a git tag was created using the `create_git_tag_only`-input.
runs: runs:
using: 'node24' using: "node20"
main: 'index.js' main: "index.js"
branding: branding:
icon: 'git-commit' icon: "git-commit"
color: orange color: orange

View File

@@ -19,6 +19,32 @@ _set_github_output() {
fi fi
} }
_execute_hook() {
local hook_name=${1}
local hook_script=${2}
if [ -n "$hook_script" ]; then
_log "debug" "Executing $hook_name hook";
echo "::group::$hook_name hook"
# Execute the hook script and capture exit code
# Temporarily disable errexit to handle hook failures gracefully
set +e
eval "$hook_script"
local exit_code=$?
set -e
echo "::endgroup::"
if [ $exit_code -ne 0 ]; then
_log "error" "$hook_name hook failed with exit code $exit_code";
exit 1;
fi
_log "debug" "$hook_name hook completed successfully";
fi
}
_log() { _log() {
local level=${1} local level=${1}
local message=${2} local message=${2}
@@ -27,24 +53,47 @@ _log() {
} }
_main() { _main() {
if "$INPUT_SKIP_FETCH"; then
_log "warning" "git-auto-commit: skip_fetch has been removed in v6. It does not have any effect anymore.";
fi
if "$INPUT_SKIP_CHECKOUT"; then
_log "warning" "git-auto-commit: skip_checkout has been removed in v6. It does not have any effect anymore.";
fi
if "$INPUT_CREATE_BRANCH"; then
_log "warning" "git-auto-commit: create_branch has been removed in v6. It does not have any effect anymore.";
fi
_check_if_git_is_available _check_if_git_is_available
_switch_to_repository _switch_to_repository
_check_if_is_git_repository _check_if_is_git_repository
_check_if_repository_is_in_detached_state # _check_if_repository_is_in_detached_state
# Execute pre-status hook before checking repository state
_execute_hook "pre_status" "$INPUT_PRE_STATUS_HOOK"
if "$INPUT_CREATE_GIT_TAG_ONLY"; then if "$INPUT_CREATE_GIT_TAG_ONLY"; then
_log "debug" "Create git tag only"; _log "debug" "Create git tag only";
_set_github_output "create_git_tag_only" "true" _set_github_output "create_git_tag_only" "true"
_tag_commit _tag_commit
# Execute pre-push hook before pushing (tag-only mode)
_execute_hook "pre_push" "$INPUT_PRE_PUSH_HOOK"
_push_to_github _push_to_github
# Execute post-push hook after successful push (tag-only mode)
_execute_hook "post_push" "$INPUT_POST_PUSH_HOOK"
elif _git_is_dirty || "$INPUT_SKIP_DIRTY_CHECK"; then elif _git_is_dirty || "$INPUT_SKIP_DIRTY_CHECK"; then
_set_github_output "changes_detected" "true" _set_github_output "changes_detected" "true"
_switch_to_branch # Execute pre-commit hook before adding files so hook can modify files
_execute_hook "pre_commit" "$INPUT_PRE_COMMIT_HOOK"
_add_files _add_files
@@ -57,7 +106,13 @@ _main() {
_tag_commit _tag_commit
# Execute pre-push hook before pushing
_execute_hook "pre_push" "$INPUT_PRE_PUSH_HOOK"
_push_to_github _push_to_github
# Execute post-push hook after successful push
_execute_hook "post_push" "$INPUT_POST_PUSH_HOOK"
else else
_set_github_output "changes_detected" "false" _set_github_output "changes_detected" "false"
@@ -110,40 +165,13 @@ _check_if_is_git_repository() {
_check_if_repository_is_in_detached_state() { _check_if_repository_is_in_detached_state() {
if [ -z "$(git symbolic-ref HEAD)" ] if [ -z "$(git symbolic-ref HEAD)" ]
then 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."; _log "error" "Repository is in detached HEAD state. Please make sure you check out a branch. Adjust the `ref` input accordingly.";
exit 1;
else else
_log "debug" "Repository is on a branch."; _log "debug" "Repository is on a branch.";
fi fi
} }
_switch_to_branch() {
echo "INPUT_BRANCH value: $INPUT_BRANCH";
# Fetch remote to make sure that repo can be switched to the right branch.
if "$INPUT_SKIP_FETCH"; then
_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
_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
git checkout -B $INPUT_BRANCH --;
else
# Switch to branch from current Workflow run
# shellcheck disable=SC2086
git checkout $INPUT_BRANCH --;
fi
fi
}
_add_files() { _add_files() {
echo "INPUT_ADD_OPTIONS: ${INPUT_ADD_OPTIONS}"; echo "INPUT_ADD_OPTIONS: ${INPUT_ADD_OPTIONS}";
_log "debug" "Apply add options ${INPUT_ADD_OPTIONS}"; _log "debug" "Apply add options ${INPUT_ADD_OPTIONS}";
@@ -176,17 +204,14 @@ _local_commit() {
} }
_tag_commit() { _tag_commit() {
echo "INPUT_TAG_NAME: ${INPUT_TAG_NAME}"
echo "INPUT_TAGGING_MESSAGE: ${INPUT_TAGGING_MESSAGE}" echo "INPUT_TAGGING_MESSAGE: ${INPUT_TAGGING_MESSAGE}"
if [ -n "$INPUT_TAG_NAME" ] || [ -n "$INPUT_TAGGING_MESSAGE" ]; then if [ -n "$INPUT_TAGGING_MESSAGE" ]
INTERNAL_TAG=${INPUT_TAG_NAME:-$INPUT_TAGGING_MESSAGE} then
INTERNAL_TAGGING_MESSAGE=${INPUT_TAGGING_MESSAGE:-$INPUT_TAG_NAME} _log "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";
_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 else
echo "Neither tag nor tag message is set. No tag will be added."; echo "No tagging message supplied. No tag will be added.";
fi fi
} }
@@ -202,8 +227,8 @@ _push_to_github() {
if [ -z "$INPUT_BRANCH" ] if [ -z "$INPUT_BRANCH" ]
then then
# Only add `--tags` option, if `$INPUT_TAG_NAME` or `$INPUT_TAGGING_MESSAGE` is set # Only add `--tags` option, if `$INPUT_TAGGING_MESSAGE` is set
if [ -n "$INPUT_TAG_NAME" ] || [ -n "$INPUT_TAGGING_MESSAGE" ] if [ -n "$INPUT_TAGGING_MESSAGE" ]
then then
_log "debug" "git push origin --tags"; _log "debug" "git push origin --tags";
git push origin --follow-tags --atomic ${INPUT_PUSH_OPTIONS:+"${INPUT_PUSH_OPTIONS_ARRAY[@]}"}; git push origin --follow-tags --atomic ${INPUT_PUSH_OPTIONS:+"${INPUT_PUSH_OPTIONS_ARRAY[@]}"};

View File

@@ -32,15 +32,22 @@ setup() {
export INPUT_COMMIT_USER_NAME="Test Suite" export INPUT_COMMIT_USER_NAME="Test Suite"
export INPUT_COMMIT_USER_EMAIL="test@github.com" export INPUT_COMMIT_USER_EMAIL="test@github.com"
export INPUT_COMMIT_AUTHOR="Test Suite <test@users.noreply.github.com>" export INPUT_COMMIT_AUTHOR="Test Suite <test@users.noreply.github.com>"
export INPUT_TAG_NAME=""
export INPUT_TAGGING_MESSAGE="" export INPUT_TAGGING_MESSAGE=""
export INPUT_PUSH_OPTIONS="" export INPUT_PUSH_OPTIONS=""
export INPUT_SKIP_DIRTY_CHECK=false export INPUT_SKIP_DIRTY_CHECK=false
export INPUT_DISABLE_GLOBBING=false
export INPUT_INTERNAL_GIT_BINARY=git
# Hook variables
export INPUT_PRE_STATUS_HOOK=""
export INPUT_PRE_COMMIT_HOOK=""
export INPUT_PRE_PUSH_HOOK=""
export INPUT_POST_PUSH_HOOK=""
# Deprecated variables. Will be removed in future versions
export INPUT_CREATE_BRANCH=false
export INPUT_SKIP_FETCH=false export INPUT_SKIP_FETCH=false
export INPUT_SKIP_CHECKOUT=false export INPUT_SKIP_CHECKOUT=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 # Set GitHub environment variables used by the GitHub Action
temp_github_output_file=$(mktemp -t github_output_test.XXXXX) temp_github_output_file=$(mktemp -t github_output_test.XXXXX)
@@ -122,9 +129,8 @@ cat_github_output() {
assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_FILE_PATTERN: ."
assert_line "INPUT_COMMIT_OPTIONS: " assert_line "INPUT_COMMIT_OPTIONS: "
assert_line "::debug::Apply commit options " assert_line "::debug::Apply commit options "
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "INPUT_PUSH_OPTIONS: " assert_line "INPUT_PUSH_OPTIONS: "
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}"
@@ -146,9 +152,8 @@ cat_github_output() {
assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_FILE_PATTERN: ."
assert_line "INPUT_COMMIT_OPTIONS: " assert_line "INPUT_COMMIT_OPTIONS: "
assert_line "::debug::Apply commit options " assert_line "::debug::Apply commit options "
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "INPUT_PUSH_OPTIONS: " assert_line "INPUT_PUSH_OPTIONS: "
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}"
@@ -294,8 +299,7 @@ cat_github_output() {
} }
@test "It creates a tag with the commit" { @test "It creates a tag with the commit" {
INPUT_TAG_NAME="v1.0.0" INPUT_TAGGING_MESSAGE="v1.0.0"
INPUT_TAGGING_MESSAGE="MyProduct v1.0.0"
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
@@ -303,15 +307,13 @@ cat_github_output() {
assert_success assert_success
assert_line "INPUT_TAG_NAME: v1.0.0" assert_line "INPUT_TAGGING_MESSAGE: v1.0.0"
assert_line "INPUT_TAGGING_MESSAGE: MyProduct v1.0.0" assert_line "::debug::Create tag 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_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}"
# Assert a tag v1.0.0 has been created # Assert a tag v1.0.0 has been created
run git tag -n run git tag
assert_output 'v1.0.0 MyProduct v1.0.0' assert_output v1.0.0
run git ls-remote --tags --refs run git ls-remote --tags --refs
assert_output --partial refs/tags/v1.0.0 assert_output --partial refs/tags/v1.0.0
@@ -392,11 +394,9 @@ cat_github_output() {
assert_equal $current_sha $remote_sha assert_equal $current_sha $remote_sha
} }
@test "It uses existing branch when INPUT_BRANCH is empty and INPUT_TAG is set" { @test "It uses existing branch when INPUT_BRANCH is empty and INPUT_TAGGING_MESSAGE is set" {
INPUT_BRANCH="" INPUT_BRANCH=""
INPUT_TAG_NAME="v2.0.0" INPUT_TAGGING_MESSAGE="v2.0.0"
INPUT_TAGGING_MESSAGE="MyProduct v2.0.0"
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
@@ -404,8 +404,8 @@ cat_github_output() {
assert_success assert_success
assert_line "INPUT_TAG_NAME: v2.0.0" assert_line "INPUT_TAGGING_MESSAGE: v2.0.0"
assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::Create tag v2.0.0"
assert_line "::debug::git push origin --tags" assert_line "::debug::git push origin --tags"
# Assert a tag v2.0.0 has been created # Assert a tag v2.0.0 has been created
@@ -417,37 +417,9 @@ cat_github_output() {
assert_output --partial refs/tags/v2.0.0 assert_output --partial refs/tags/v2.0.0
} }
@test "If SKIP_FETCH is true git-fetch will not be called" {
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
INPUT_SKIP_FETCH=true
run git_auto_commit
assert_success
assert_line "::debug::git-fetch will not be executed."
}
@test "If SKIP_CHECKOUT is true git-checkout will not be called" {
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
INPUT_SKIP_CHECKOUT=true
run git_auto_commit
assert_success
assert_line "::debug::git-checkout will not be executed."
}
@test "It pushes generated commit and tag to remote and actually updates the commit shas" { @test "It pushes generated commit and tag to remote and actually updates the commit shas" {
INPUT_BRANCH="" INPUT_BRANCH=""
INPUT_TAG_NAME="v2.0.0" INPUT_TAGGING_MESSAGE="v2.0.0"
INPUT_TAGGING_MESSAGE="MyProduct v2.0.0"
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
@@ -455,8 +427,8 @@ cat_github_output() {
assert_success assert_success
assert_line "INPUT_TAG_NAME: v2.0.0" assert_line "INPUT_TAGGING_MESSAGE: v2.0.0"
assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::Create tag v2.0.0"
assert_line "::debug::git push origin --tags" assert_line "::debug::git push origin --tags"
# Assert a tag v2.0.0 has been created # Assert a tag v2.0.0 has been created
@@ -475,14 +447,8 @@ cat_github_output() {
} }
@test "It pushes generated commit and tag to remote branch and updates commit sha" { @test "It pushes generated commit and tag to remote branch and updates commit sha" {
# Create "a-new-branch"-branch and then immediately switch back to ${FAKE_DEFAULT_BRANCH}
git checkout -b a-new-branch
git checkout ${FAKE_DEFAULT_BRANCH}
INPUT_BRANCH="a-new-branch" INPUT_BRANCH="a-new-branch"
INPUT_TAG_NAME="v2.0.0" INPUT_TAGGING_MESSAGE="v2.0.0"
INPUT_TAGGING_MESSAGE="MyProduct v2.0.0"
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
@@ -490,8 +456,8 @@ cat_github_output() {
assert_success assert_success
assert_line "INPUT_TAG_NAME: v2.0.0" assert_line "INPUT_TAGGING_MESSAGE: v2.0.0"
assert_line "::debug::Create tag v2.0.0: MyProduct v2.0.0" assert_line "::debug::Create tag v2.0.0"
assert_line "::debug::Push commit to remote branch a-new-branch" assert_line "::debug::Push commit to remote branch a-new-branch"
# Assert a tag v2.0.0 has been created # Assert a tag v2.0.0 has been created
@@ -503,7 +469,7 @@ cat_github_output() {
assert_output --partial refs/tags/v2.0.0 assert_output --partial refs/tags/v2.0.0
# Assert that branch "a-new-branch" was updated on remote # Assert that branch "a-new-branch" was updated on remote
current_sha="$(git rev-parse --verify --short a-new-branch)" current_sha="$(git rev-parse --verify --short ${FAKE_DEFAULT_BRANCH})"
remote_sha="$(git rev-parse --verify --short origin/a-new-branch)" remote_sha="$(git rev-parse --verify --short origin/a-new-branch)"
assert_equal $current_sha $remote_sha assert_equal $current_sha $remote_sha
@@ -547,7 +513,6 @@ cat_github_output() {
@test "it does not throw an error if not changes are detected and SKIP_DIRTY_CHECK is false" { @test "it does not throw an error if not changes are detected and SKIP_DIRTY_CHECK is false" {
INPUT_FILE_PATTERN="." INPUT_FILE_PATTERN="."
INPUT_SKIP_DIRTY_CHECK=false INPUT_SKIP_DIRTY_CHECK=false
INPUT_SKIP_FETCH=false
run git_auto_commit run git_auto_commit
@@ -601,8 +566,6 @@ cat_github_output() {
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
INPUT_SKIP_CHECKOUT=true
run git_auto_commit run git_auto_commit
assert_success assert_success
@@ -632,8 +595,6 @@ cat_github_output() {
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
INPUT_SKIP_CHECKOUT=true
run git_auto_commit run git_auto_commit
assert_success assert_success
@@ -643,9 +604,8 @@ cat_github_output() {
assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_FILE_PATTERN: ."
assert_line "INPUT_COMMIT_OPTIONS: " assert_line "INPUT_COMMIT_OPTIONS: "
assert_line "::debug::Apply commit options " assert_line "::debug::Apply commit options "
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "INPUT_PUSH_OPTIONS: " assert_line "INPUT_PUSH_OPTIONS: "
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch not-existend-branch" assert_line "::debug::Push commit to remote branch not-existend-branch"
@@ -664,6 +624,51 @@ cat_github_output() {
assert_line -e "commit_hash=[0-9a-f]{40}$" 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"
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"
# 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" { @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 # Create `existing-remote-branch` on remote with changes the local repository does not yet have
cd $FAKE_TEMP_LOCAL_REPOSITORY cd $FAKE_TEMP_LOCAL_REPOSITORY
@@ -681,7 +686,6 @@ cat_github_output() {
cd $FAKE_LOCAL_REPOSITORY cd $FAKE_LOCAL_REPOSITORY
INPUT_BRANCH="existing-remote-branch" INPUT_BRANCH="existing-remote-branch"
INPUT_SKIP_CHECKOUT=true
run git branch run git branch
refute_line --partial "existing-remote-branch" refute_line --partial "existing-remote-branch"
@@ -702,9 +706,8 @@ cat_github_output() {
assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_FILE_PATTERN: ."
assert_line "INPUT_COMMIT_OPTIONS: " assert_line "INPUT_COMMIT_OPTIONS: "
assert_line "::debug::Apply commit options " assert_line "::debug::Apply commit options "
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "INPUT_PUSH_OPTIONS: " assert_line "INPUT_PUSH_OPTIONS: "
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch existing-remote-branch" assert_line "::debug::Push commit to remote branch existing-remote-branch"
@@ -727,7 +730,7 @@ cat_github_output() {
assert_line -e "commit_hash=[0-9a-f]{40}$" assert_line -e "commit_hash=[0-9a-f]{40}$"
} }
@test "It fails if local branch is behind remote and when remote has newer commits and skip_checkout is set to true" { @test "It fails if local branch is behind remote and when remote has newer commits" {
# Create `existing-remote-branch` on remote with changes the local repository does not yet have # Create `existing-remote-branch` on remote with changes the local repository does not yet have
cd $FAKE_TEMP_LOCAL_REPOSITORY cd $FAKE_TEMP_LOCAL_REPOSITORY
git checkout -b "existing-remote-branch" git checkout -b "existing-remote-branch"
@@ -744,7 +747,6 @@ cat_github_output() {
cd $FAKE_LOCAL_REPOSITORY cd $FAKE_LOCAL_REPOSITORY
INPUT_BRANCH="existing-remote-branch" INPUT_BRANCH="existing-remote-branch"
INPUT_SKIP_CHECKOUT=true
run git branch run git branch
refute_line --partial "existing-remote-branch" refute_line --partial "existing-remote-branch"
@@ -776,7 +778,7 @@ cat_github_output() {
refute [assert_equal $current_sha $remote_sha] refute [assert_equal $current_sha $remote_sha]
} }
@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" { @test "It fails to push 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 # Create `new-branch` on remote with changes the local repository does not yet have
cd $FAKE_TEMP_LOCAL_REPOSITORY cd $FAKE_TEMP_LOCAL_REPOSITORY
@@ -795,7 +797,6 @@ cat_github_output() {
cd $FAKE_LOCAL_REPOSITORY cd $FAKE_LOCAL_REPOSITORY
INPUT_BRANCH="new-branch" INPUT_BRANCH="new-branch"
INPUT_SKIP_CHECKOUT=true
# Assert that local remote does not have a "new-branch"-branch nor does # Assert that local remote does not have a "new-branch"-branch nor does
# know about the remote branch. # know about the remote branch.
@@ -814,7 +815,8 @@ cat_github_output() {
assert_line "INPUT_BRANCH value: new-branch" assert_line "INPUT_BRANCH value: new-branch"
assert_line --partial "::debug::Push commit to remote branch new-branch" assert_line --partial "::debug::Push commit to remote branch new-branch"
assert_line --partial "Updates were rejected because a pushed branch tip is behind its remote" assert_line --partial "Updates were rejected because the remote contains work that you do"
assert_line --partial "This is usually caused by another repository pushing"
} }
@test "throws fatal error if file pattern includes files that do not exist" { @test "throws fatal error if file pattern includes files that do not exist" {
@@ -1035,9 +1037,8 @@ cat_github_output() {
assert_line "INPUT_FILE_PATTERN: ." assert_line "INPUT_FILE_PATTERN: ."
assert_line "INPUT_COMMIT_OPTIONS: " assert_line "INPUT_COMMIT_OPTIONS: "
assert_line "::debug::Apply commit options " assert_line "::debug::Apply commit options "
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "INPUT_PUSH_OPTIONS: " assert_line "INPUT_PUSH_OPTIONS: "
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}"
@@ -1102,7 +1103,8 @@ END
assert_line "::error::Not a git repository. Please make sure to run this action in a git repository. Adjust the `repository` input if necessary." 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" { @test "It detects if the repository is in a detached state and exits with an error" {
skip
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt
run git_auto_commit run git_auto_commit
@@ -1117,14 +1119,13 @@ END
run git_auto_commit run git_auto_commit
assert_success; assert_failure;
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." assert_line "::error::Repository is in detached HEAD state. Please make sure you check out a branch. Adjust the `ref` input accordingly."
} }
@test "it creates a tag if create_git_tag_only is set to true and a message has been supplied" { @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_CREATE_GIT_TAG_ONLY=true
INPUT_TAG_NAME=v1.0.0 INPUT_TAGGING_MESSAGE=v1.0.0
INPUT_TAGGING_MESSAGE="MyProduct v1.0.0"
run git_auto_commit run git_auto_commit
@@ -1132,8 +1133,8 @@ END
assert_line "::debug::Create git tag only" assert_line "::debug::Create git tag only"
assert_line "::debug::Create tag v1.0.0: MyProduct v1.0.0" assert_line "::debug::Create tag v1.0.0"
refute_line "Neither tag nor tag message is set. No tag will be added." refute_line "No tagging message supplied. No tag will be added."
assert_line "::debug::Apply push options " assert_line "::debug::Apply push options "
assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}" assert_line "::debug::Push commit to remote branch ${FAKE_DEFAULT_BRANCH}"
@@ -1144,8 +1145,8 @@ END
refute_line -e "commit_hash=[0-9a-f]{40}$" refute_line -e "commit_hash=[0-9a-f]{40}$"
# Assert a tag v1.0.0 has been created # Assert a tag v1.0.0 has been created
run git tag -n run git tag
assert_output 'v1.0.0 MyProduct v1.0.0' assert_output v1.0.0
run git ls-remote --tags --refs run git ls-remote --tags --refs
assert_output --partial refs/tags/v1.0.0 assert_output --partial refs/tags/v1.0.0
@@ -1153,16 +1154,14 @@ END
@test "it output no tagging message supplied if no tagging message is set but create_git_tag_only is set to true" { @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_CREATE_GIT_TAG_ONLY=true
INPUT_TAG_NAME=""
INPUT_TAGGING_MESSAGE="" INPUT_TAGGING_MESSAGE=""
run git_auto_commit run git_auto_commit
assert_success assert_success
assert_line "INPUT_TAG_NAME: "
assert_line "INPUT_TAGGING_MESSAGE: " assert_line "INPUT_TAGGING_MESSAGE: "
assert_line "Neither tag nor tag message is set. No tag will be added." assert_line "No tagging message supplied. No tag will be added."
assert_line "::debug::Create git tag only" assert_line "::debug::Create git tag only"
run cat_github_output run cat_github_output
@@ -1175,330 +1174,248 @@ END
assert_output "" assert_output ""
} }
@test "script fails to push commit to new branch that does not exist yet" { @test "it shows warning message if any deprecated options are used" {
INPUT_BRANCH="not-existend-branch" INPUT_SKIP_FETCH=true
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"
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 INPUT_SKIP_CHECKOUT=true
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 run git_auto_commit
assert_success assert_success
assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" assert_line "::warning::git-auto-commit: skip_fetch has been removed in v6. It does not have any effect anymore."
assert_line "INPUT_BRANCH value: not-existend-remote-branch" assert_line "::warning::git-auto-commit: skip_checkout has been removed in v6. It does not have any effect anymore."
assert_line "INPUT_FILE_PATTERN: ." assert_line "::warning::git-auto-commit: create_branch has been removed in v6. It does not have any effect anymore."
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" { @test "it executes pre_status_hook when provided" {
# Create a dummy file and setup the hook to create a marker file
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-created-by-hook.txt
# Create `existing-remote-branch` on remote with changes the local repository does not yet have INPUT_PRE_STATUS_HOOK="echo 'pre-status-hook-executed' > hook-marker.txt"
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 run git_auto_commit
assert_success assert_success
assert_line "::debug::Executing pre_status hook"
assert_line "::debug::pre_status hook completed successfully"
assert_line "INPUT_REPOSITORY value: ${INPUT_REPOSITORY}" # Verify the hook actually executed
assert_line "INPUT_BRANCH value: existing-remote-branch" assert [ -f "${FAKE_LOCAL_REPOSITORY}/hook-marker.txt" ]
assert_line "INPUT_FILE_PATTERN: ." run cat "${FAKE_LOCAL_REPOSITORY}/hook-marker.txt"
assert_line "INPUT_COMMIT_OPTIONS: " assert_output "pre-status-hook-executed"
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" { @test "it executes pre_commit_hook when changes are detected" {
# Create `existing-remote-branch` on remote with changes the local repository does not yet have # Create a dummy file to trigger commit process
cd $FAKE_TEMP_LOCAL_REPOSITORY touch "${FAKE_LOCAL_REPOSITORY}"/new-file-created-by-hook.txt
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 INPUT_PRE_COMMIT_HOOK="echo 'pre-commit-hook-executed' > pre-commit-marker.txt"
assert_line --partial "existing-remote-branch"
# --------- run git_auto_commit
# Switch to our regular local repository and run `git-auto-commit`
cd $FAKE_LOCAL_REPOSITORY
INPUT_BRANCH="existing-remote-branch" assert_success
INPUT_CREATE_BRANCH=true assert_line "::debug::Executing pre_commit hook"
assert_line "::debug::pre_commit hook completed successfully"
run git branch # Verify the hook actually executed
refute_line --partial "existing-remote-branch" assert [ -f "${FAKE_LOCAL_REPOSITORY}/pre-commit-marker.txt" ]
run cat "${FAKE_LOCAL_REPOSITORY}/pre-commit-marker.txt"
assert_output "pre-commit-hook-executed"
}
run git fetch --all @test "it executes pre_push_hook when changes are detected" {
run git branch -r # Create a dummy file to trigger commit process
assert_line --partial "origin/existing-remote-branch" touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt INPUT_PRE_PUSH_HOOK="echo 'pre-push-hook-executed' > pre-push-marker.txt"
run git_auto_commit
assert_success
assert_line "::debug::Executing pre_push hook"
assert_line "::debug::pre_push hook completed successfully"
# Verify the hook actually executed
assert [ -f "${FAKE_LOCAL_REPOSITORY}/pre-push-marker.txt" ]
run cat "${FAKE_LOCAL_REPOSITORY}/pre-push-marker.txt"
assert_output "pre-push-hook-executed"
}
@test "it executes post_push_hook when changes are detected" {
# Create a dummy file to trigger commit process
touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
INPUT_POST_PUSH_HOOK="echo 'post-push-hook-executed' > post-push-marker.txt"
run git_auto_commit
assert_success
assert_line "::debug::Executing post_push hook"
assert_line "::debug::post_push hook completed successfully"
# Verify the hook actually executed
assert [ -f "${FAKE_LOCAL_REPOSITORY}/post-push-marker.txt" ]
run cat "${FAKE_LOCAL_REPOSITORY}/post-push-marker.txt"
assert_output "post-push-hook-executed"
}
@test "it executes all hooks in correct order when changes are detected" {
# Create a dummy file to trigger commit process
touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
INPUT_PRE_STATUS_HOOK="echo '1' > execution-order.txt"
INPUT_PRE_COMMIT_HOOK="echo '2' >> execution-order.txt"
INPUT_PRE_PUSH_HOOK="echo '3' >> execution-order.txt"
INPUT_POST_PUSH_HOOK="echo '4' >> execution-order.txt"
run git_auto_commit
assert_success
assert_line "::debug::Executing pre_status hook"
assert_line "::debug::Executing pre_commit hook"
assert_line "::debug::Executing pre_push hook"
assert_line "::debug::Executing post_push hook"
# Verify all hooks executed in the correct order
run cat "${FAKE_LOCAL_REPOSITORY}/execution-order.txt"
assert_line --index 0 "1"
assert_line --index 1 "2"
assert_line --index 2 "3"
assert_line --index 3 "4"
}
@test "it executes pre_status_hook even when no changes are detected" {
INPUT_PRE_STATUS_HOOK="echo 'pre-status-hook-executed' > /tmp/hook-marker.txt"
run git_auto_commit
assert_success
assert_line "::debug::Executing pre_status hook"
assert_line "Working tree clean. Nothing to commit."
# Verify the hook actually executed
assert [ -f "/tmp/hook-marker.txt" ]
run cat "/tmp/hook-marker.txt"
assert_output "pre-status-hook-executed"
rm -f "/tmp/hook-marker.txt"
}
@test "it does not execute commit/push hooks when no changes are detected" {
INPUT_PRE_COMMIT_HOOK="echo 'should-not-execute' > pre-commit-marker.txt"
INPUT_PRE_PUSH_HOOK="echo 'should-not-execute' > pre-push-marker.txt"
INPUT_POST_PUSH_HOOK="echo 'should-not-execute' > post-push-marker.txt"
run git_auto_commit
assert_success
assert_line "Working tree clean. Nothing to commit."
# Verify the hooks did not execute
assert [ ! -f "${FAKE_LOCAL_REPOSITORY}/pre-commit-marker.txt" ]
assert [ ! -f "${FAKE_LOCAL_REPOSITORY}/pre-push-marker.txt" ]
assert [ ! -f "${FAKE_LOCAL_REPOSITORY}/post-push-marker.txt" ]
}
@test "it executes hooks in tag-only mode" {
INPUT_CREATE_GIT_TAG_ONLY=true
INPUT_TAGGING_MESSAGE="v1.0.0"
INPUT_PRE_STATUS_HOOK="echo 'pre-status-tag-only' > pre-status-marker.txt"
INPUT_PRE_PUSH_HOOK="echo 'pre-push-tag-only' > pre-push-marker.txt"
INPUT_POST_PUSH_HOOK="echo 'post-push-tag-only' > post-push-marker.txt"
run git_auto_commit
assert_success
assert_line "::debug::Executing pre_status hook"
assert_line "::debug::Executing pre_push hook"
assert_line "::debug::Executing post_push hook"
# Verify hooks executed
assert [ -f "${FAKE_LOCAL_REPOSITORY}/pre-status-marker.txt" ]
assert [ -f "${FAKE_LOCAL_REPOSITORY}/pre-push-marker.txt" ]
assert [ -f "${FAKE_LOCAL_REPOSITORY}/post-push-marker.txt" ]
}
@test "it fails when pre_status_hook fails" {
INPUT_PRE_STATUS_HOOK="false"
run git_auto_commit run git_auto_commit
assert_failure assert_failure
assert_line "::debug::Executing pre_status hook"
assert_line "hint: Updates were rejected because the tip of your current branch is behind" assert_line "::error::pre_status hook failed with exit code 1"
# 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" { @test "it fails when pre_commit_hook fails" {
# Create `new-branch` on remote with changes the local repository does not yet have # Create a dummy file to trigger commit process
cd $FAKE_TEMP_LOCAL_REPOSITORY touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
git checkout -b "new-branch" INPUT_PRE_COMMIT_HOOK="false"
touch new-branch-file.txt
git add new-branch-file.txt
git commit --quiet -m "Add additional file" run git_auto_commit
git push origin new-branch
run git branch -r assert_failure
assert_line --partial "origin/new-branch" assert_line "::debug::Executing pre_commit hook"
assert_line "::error::pre_commit hook failed with exit code 1"
}
# --------- @test "it fails when pre_push_hook fails" {
# Switch to our regular local repository and run `git-auto-commit` # Create a dummy file to trigger commit process
cd $FAKE_LOCAL_REPOSITORY touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
INPUT_BRANCH="new-branch" INPUT_PRE_PUSH_HOOK="false"
# Assert that local remote does not know have "new-branch" locally nor does run git_auto_commit
# know about the remote branch.
run git branch
refute_line --partial "new-branch"
run git branch -r assert_failure
refute_line --partial "origin/new-branch" assert_line "::debug::Executing pre_push hook"
assert_line "::error::pre_push hook failed with exit code 1"
}
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt @test "it fails when post_push_hook fails" {
# Create a dummy file to trigger commit process
touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
INPUT_POST_PUSH_HOOK="false"
run git_auto_commit
assert_failure
assert_line "::debug::Executing post_push hook"
assert_line "::error::post_push hook failed with exit code 1"
}
@test "hook can access git commands and repository state" {
# Create a dummy file to trigger commit process
touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
INPUT_PRE_COMMIT_HOOK="git log --oneline | head -1 > git-log-output.txt"
run git_auto_commit run git_auto_commit
assert_success assert_success
assert_line "::debug::Executing pre_commit hook"
assert_line "INPUT_BRANCH value: new-branch" # Verify the hook could access git commands
assert_line --partial "::debug::Push commit to remote branch new-branch" assert [ -f "${FAKE_LOCAL_REPOSITORY}/git-log-output.txt" ]
run cat "${FAKE_LOCAL_REPOSITORY}/git-log-output.txt"
# Assert that branch "new-branch" was updated on remote assert_line --partial "Init Remote Repository"
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" { @test "hook can modify files that get included in commit" {
INPUT_TAGGING_MESSAGE="v1.0.0" # Create a dummy file to trigger commit process
touch "${FAKE_LOCAL_REPOSITORY}"/new-file.txt
touch "${FAKE_LOCAL_REPOSITORY}"/new-file-{1,2,3}.txt INPUT_PRE_COMMIT_HOOK="echo 'modified by hook' > hook-modified-file.txt"
run git_auto_commit run git_auto_commit
assert_success assert_success
assert_line "::debug::Executing pre_commit hook"
assert_line "INPUT_TAG_NAME: " # Verify the file created by the hook was committed
assert_line "INPUT_TAGGING_MESSAGE: v1.0.0" run git log --name-only -1
assert_line "hook-modified-file.txt"
assert_line "::debug::Create tag v1.0.0: v1.0.0" assert_line "new-file.txt"
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
} }