Auto Release #75
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Release | |
| on: | |
| schedule: | |
| # Run every 24 hours at midnight UTC | |
| - cron: '0 0 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version tag to create (optional, defaults to date-based)' | |
| required: false | |
| default: '' | |
| jobs: | |
| check-and-release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get latest release | |
| id: latest_release | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const releases = await github.rest.repos.listReleases({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 1 | |
| }); | |
| if (releases.data.length > 0) { | |
| console.log('Latest release found:', releases.data[0].tag_name); | |
| return releases.data[0].tag_name; | |
| } else { | |
| console.log('No previous releases found'); | |
| return null; | |
| } | |
| - name: Determine if release is needed | |
| id: check_changes | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const latestRelease = '${{ steps.latest_release.outputs.result }}'; | |
| let commitCount = 0; | |
| if (latestRelease && latestRelease !== 'null') { | |
| // Compare against the latest release tag | |
| try { | |
| const compare = await github.rest.repos.compareCommits({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| base: latestRelease, | |
| head: 'HEAD' | |
| }); | |
| commitCount = compare.data.ahead_by; | |
| console.log('Commits ahead of last release:', commitCount); | |
| if (commitCount > 0) { | |
| const commits = compare.data.commits.slice(0, 5).map(c => ` - ${c.commit.message.split('\n')[0]}`).join('\n'); | |
| core.setOutput('changes_summary', `${commitCount} new commit(s) since ${latestRelease}:\n${commits}`); | |
| } | |
| } catch (error) { | |
| console.log('Could not compare commits, treating as no changes:', error.message); | |
| commitCount = 0; | |
| } | |
| } else { | |
| // No previous release - check all commits | |
| const commits = await github.rest.repos.listCommits({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 100 | |
| }); | |
| commitCount = commits.data.length; | |
| console.log('Total commits (no previous release):', commitCount); | |
| if (commitCount > 0) { | |
| const firstCommits = commits.data.slice(0, 5).map(c => ` - ${c.commit.message.split('\n')[0]}`).join('\n'); | |
| core.setOutput('changes_summary', `${commitCount} commit(s) in repository (first release):\n${firstCommits}`); | |
| } | |
| } | |
| core.setOutput('needs_release', commitCount > 0 ? 'true' : 'false'); | |
| core.setOutput('commit_count', commitCount); | |
| - name: Create release | |
| if: steps.check_changes.outputs.needs_release == 'true' | |
| id: create_release | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| // Get version from input or use date-based version | |
| const manualVersion = '${{ github.event.inputs.version }}'; | |
| const version = manualVersion || new Date().toISOString().split('T')[0]; | |
| // Validate version format | |
| const versionPattern = /^[a-zA-Z0-9._-]+$/; | |
| if (manualVersion && !versionPattern.test(manualVersion)) { | |
| throw new Error('Invalid version format: must match pattern [a-zA-Z0-9._-]'); | |
| } | |
| const changesSummary = '${{ steps.check_changes.outputs.changes_summary }}'; | |
| console.log('Creating release with version:', version); | |
| console.log('Changes:', changesSummary); | |
| // Check if release already exists first | |
| const existing = await github.rest.repos.listReleases({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| per_page: 100 | |
| }); | |
| const existingRelease = existing.data.find(r => r.tag_name === version); | |
| if (existingRelease) { | |
| console.log('Release with this tag already exists'); | |
| core.setOutput('release_url', existingRelease.html_url); | |
| core.setOutput('release_id', existingRelease.id); | |
| return; | |
| } | |
| // Then create the release | |
| const release = await github.rest.repos.createRelease({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag_name: version, | |
| name: `Release ${version}`, | |
| body: `## Changes since last release\n\n${changesSummary}\n\n---\n\n*Auto-generated release on ${new Date().toISOString()}*`, | |
| draft: false, | |
| prerelease: false | |
| }); | |
| console.log('Release created:', release.data.html_url); | |
| core.setOutput('release_url', release.data.html_url); | |
| core.setOutput('release_id', release.data.id); | |
| - name: Summary | |
| if: always() | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const needsRelease = '${{ steps.check_changes.outputs.needs_release }}'; | |
| const releaseUrl = '${{ steps.create_release.outputs.release_url }}'; | |
| const commitCount = '${{ steps.check_changes.outputs.commit_count }}'; | |
| if (needsRelease === 'true' && releaseUrl) { | |
| console.log('✅ Release created successfully!'); | |
| console.log(' Commits:', commitCount); | |
| console.log(' URL:', releaseUrl); | |
| } else if (needsRelease === 'true' && !releaseUrl) { | |
| console.log('⚠️ Changes detected but release already exists'); | |
| } else { | |
| console.log('ℹ️ No changes since last release, skipping release'); | |
| } |