Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions .github/workflows/auto-release-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# This workflow periodically checks if there are unreleased changes and automatically triggers a release

name: Auto Release Check

on:
workflow_call:
inputs:
cron-schedule:
description: Cron schedule for periodic checks (default is daily at 2 AM UTC)
required: false
type: string
default: '0 2 * * *'
Comment on lines +8 to +12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: workflow_call trigger's cron-schedule input is defined but never used

The cron-schedule input is declared under workflow_call inputs (line 8–12) but is never referenced anywhere in the workflow. The hardcoded schedule trigger (line 23–25) always runs at 0 2 * * * and ignores this input entirely. GitHub Actions does not support dynamically changing the schedule cron from a workflow_call input β€” schedules are static and cannot be parameterised at call time.

The cron-schedule input should be removed to avoid misleading callers who might believe they can override the schedule through it.

Suggested change
cron-schedule:
description: Cron schedule for periodic checks (default is daily at 2 AM UTC)
required: false
type: string
default: '0 2 * * *'
dry-run:
description: Dry run mode for testing
required: false
type: boolean
default: false

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful

dry-run:
description: Dry run mode for testing
required: false
type: boolean
default: false
tag:
description: The tag to use during publish, values are latest, next
required: false
type: string
default: latest
schedule:
# Default: Run daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
dry-run:
description: Dry run mode for testing
required: false
type: boolean
default: false
tag:
description: The tag to use during publish, values are latest, next
required: false
type: string
default: latest

permissions:
contents: write
id-token: write
Comment on lines +39 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security: Workflow-level contents: write and id-token: write permissions are broader than necessary

The check-unreleased-changes job only reads git history and requires no elevated permissions. Granting contents: write and id-token: write at the top level gives every jobβ€”including the read-only check-unreleased-changes and summary jobsβ€”those permissions.

Consider scoping permissions per-job: give contents: read (and no id-token) to check-unreleased-changes and summary, and only grant the elevated permissions to trigger-release (or inherit them from the called workflow).

Suggested change
permissions:
contents: write
id-token: write
permissions:
contents: read

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful


jobs:
check-unreleased-changes:
runs-on: ubuntu-latest
outputs:
has-unreleased-changes: ${{ steps.check-changes.outputs.has-unreleased-changes }}
latest-tag: ${{ steps.check-changes.outputs.latest-tag }}
commits-count: ${{ steps.check-changes.outputs.commits-count }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for tags and commits

- name: Check for unreleased changes
id: check-changes
run: |
# Get the latest release tag (assuming format vX.Y.Z)
latest_tag=$(git tag -l 'v*' --sort=-version:refname | head -n 1)

if [ -z "$latest_tag" ]; then
echo "No release tags found, assuming first release needed"
echo "has-unreleased-changes=true" >> $GITHUB_OUTPUT
echo "latest-tag=none" >> $GITHUB_OUTPUT
echo "commits-count=all" >> $GITHUB_OUTPUT
exit 0
fi

echo "Latest release tag: $latest_tag"
echo "latest-tag=$latest_tag" >> $GITHUB_OUTPUT

# Count commits since the latest tag
commits_count=$(git rev-list ${latest_tag}..HEAD --count)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Unquoted variable ${latest_tag} in git rev-list is vulnerable to tag names with special characters

If latest_tag ever contains spaces or shell-special characters, the command will break or behave unexpectedly. The variable should be double-quoted.

Same issue also applies to line 84 (git log ${latest_tag}..HEAD --oneline).

Suggested change
commits_count=$(git rev-list ${latest_tag}..HEAD --count)
commits_count=$(git rev-list "${latest_tag}..HEAD" --count)

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful

echo "commits-count=$commits_count" >> $GITHUB_OUTPUT

echo "Commits since $latest_tag: $commits_count"

if [ "$commits_count" -gt 0 ]; then
echo "Found $commits_count unreleased commit(s)"
echo "has-unreleased-changes=true" >> $GITHUB_OUTPUT

# Show the commits for logging
echo "Unreleased commits:"
git log ${latest_tag}..HEAD --oneline

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Unquoted variable ${latest_tag} in git log is vulnerable to word splitting / glob expansion

The variable should be double-quoted, consistent with the fix suggested for line 73.

Suggested change
git log ${latest_tag}..HEAD --oneline
git log "${latest_tag}..HEAD" --oneline

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful

else
echo "No unreleased changes found"
echo "has-unreleased-changes=false" >> $GITHUB_OUTPUT
fi

trigger-release:
needs: check-unreleased-changes
if: ${{ needs.check-unreleased-changes.outputs.has-unreleased-changes == 'true' }}
uses: ./.github/workflows/release.yml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: trigger-release job references release.yml via a relative path, but release.yml is not a reusable workflow

release.yml is triggered by push to main and workflow_dispatch; it does not declare an on: workflow_call trigger. Calling it with uses: ./.github/workflows/release.yml will therefore fail at runtime with an error like "workflow is not callable".

The referenced workflow must expose a workflow_call event before it can be used here. Either add workflow_call to release.yml, or call the underlying reusable workflow (cap-js/.github/.github/workflows/release.yml@main) directly from this job.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful

with:
dry-run: ${{ inputs.dry-run || false }}
tag: ${{ inputs.tag || 'latest' }}
secrets: inherit

summary:
runs-on: ubuntu-latest
needs: [check-unreleased-changes, trigger-release]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic Error: summary job will be skipped whenever trigger-release is skipped

needs: [check-unreleased-changes, trigger-release] causes GitHub Actions to mark the summary job as skipped when trigger-release is skipped (i.e. when there are no unreleased changes), even though if: always() is set. The always() condition overrides failure/cancellation but not skipping induced by a skipped dependency.

To always run the summary regardless of whether trigger-release ran, remove trigger-release from the needs list and read its result via needs.trigger-release.result only when needed (it will be 'skipped' rather than causing the job itself to be skipped).

Suggested change
needs: [check-unreleased-changes, trigger-release]
needs: [check-unreleased-changes, trigger-release]
if: ${{ always() }}

Double-check suggestion before committing. Edit this comment for amendments.


Please provide feedback on the review comment by checking the appropriate box:

  • 🌟 Awesome comment, a human might have missed that.
  • βœ… Helpful comment
  • 🀷 Neutral
  • ❌ This comment is not helpful

if: always()
steps:
- name: Generate summary
run: |
echo "# Auto Release Check Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Latest Tag:** ${{ needs.check-unreleased-changes.outputs.latest-tag }}" >> $GITHUB_STEP_SUMMARY
echo "**Unreleased Commits:** ${{ needs.check-unreleased-changes.outputs.commits-count }}" >> $GITHUB_STEP_SUMMARY
echo "**Has Unreleased Changes:** ${{ needs.check-unreleased-changes.outputs.has-unreleased-changes }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

if [ "${{ needs.check-unreleased-changes.outputs.has-unreleased-changes }}" == "true" ]; then
echo "βœ… **Action:** Release workflow triggered" >> $GITHUB_STEP_SUMMARY
echo "**Release Status:** ${{ needs.trigger-release.result }}" >> $GITHUB_STEP_SUMMARY
else
echo "ℹ️ **Action:** No release needed" >> $GITHUB_STEP_SUMMARY
fi
Loading