Release from commit messages #3
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: Release from commit messages | |
| on: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| jobs: | |
| build-and-release: | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.13' | |
| update-environment: true | |
| cache: 'pipenv' | |
| - name: Install pipenv | |
| shell: pwsh | |
| run: python -m pip install --upgrade pip pipenv | |
| - name: Install dependencies with pipenv | |
| shell: pwsh | |
| run: | | |
| pipenv --python 3.13 | |
| pipenv install --dev | |
| - name: Build the application | |
| shell: pwsh | |
| run: pipenv run pyinstaller --clean --noconfirm tkinter_ui/tkinter_ui.spec | |
| - name: Read version info | |
| id: version | |
| shell: pwsh | |
| run: | | |
| if (-not (Test-Path -Path version.json)) { | |
| Write-Error 'version.json not found.' | |
| exit 1 | |
| } | |
| $json = Get-Content version.json -Raw | ConvertFrom-Json | |
| if (-not $json.name -or -not $json.version) { | |
| Write-Error 'version.json must contain name and version.' | |
| exit 1 | |
| } | |
| Add-Content -Path $env:GITHUB_ENV -Value "name=$($json.name)" | |
| Add-Content -Path $env:GITHUB_ENV -Value "version=$($json.version)" | |
| Add-Content -Path $env:GITHUB_ENV -Value "release_tag=$($json.version)" | |
| Add-Content -Path $env:GITHUB_ENV -Value "release_title=v$($json.version)" | |
| - name: Validate release tag does not already exist | |
| shell: pwsh | |
| run: | | |
| $tag = $env:release_tag | |
| if (-not $tag) { | |
| Write-Error 'release_tag is empty.' | |
| exit 1 | |
| } | |
| $existingTag = git tag --list $tag | |
| if ($existingTag) { | |
| Write-Error "Tag '$tag' already exists. Bump version.json before releasing." | |
| exit 1 | |
| } | |
| - name: Generate release notes from commit messages | |
| id: notes | |
| shell: pwsh | |
| run: | | |
| $ErrorActionPreference = 'Stop' | |
| $versionCommits = @(git log --format=%H -- version.json) | |
| $baseCommit = '' | |
| if ($versionCommits.Count -ge 2) { | |
| $baseCommit = $versionCommits[1].Trim() | |
| } elseif ($versionCommits.Count -eq 1) { | |
| $baseCommit = $versionCommits[0].Trim() | |
| } | |
| if (-not $baseCommit) { | |
| try { | |
| $baseCommit = (git describe --tags --abbrev=0).Trim() | |
| } catch { | |
| $baseCommit = '' | |
| } | |
| } | |
| if (-not $baseCommit) { | |
| $baseCommit = (git rev-list --max-parents=0 HEAD | Select-Object -First 1).Trim() | |
| } | |
| $rawMessages = git log --reverse --format=%s "$baseCommit..HEAD" | |
| $skipPatterns = @( | |
| '^(?i)release:\s*v?\d', | |
| '^(?i)release\b', | |
| '^(?i)chore:\s*version\b', | |
| '^(?i)chore(?:\(.+\))?:\s*bump\s+version\b', | |
| '^(?i)bump\s+version\b', | |
| '^(?i)version\s+update\b', | |
| '^(?i)merge\s+pull\s+request\b', | |
| '^(?i)merge\b' | |
| ) | |
| $notesLines = New-Object System.Collections.Generic.List[string] | |
| foreach ($line in $rawMessages) { | |
| $message = $line.Trim() | |
| if (-not $message) { continue } | |
| $skip = $false | |
| foreach ($pattern in $skipPatterns) { | |
| if ($message -match $pattern) { | |
| $skip = $true | |
| break | |
| } | |
| } | |
| if ($skip) { continue } | |
| $notesLines.Add("- $message") | |
| } | |
| $notesPath = Join-Path $PWD 'release-notes.md' | |
| $content = New-Object System.Collections.Generic.List[string] | |
| $content.AddRange($notesLines) | |
| $content | Set-Content -Path $notesPath -Encoding utf8 | |
| Add-Content -Path $env:GITHUB_ENV -Value "base_commit=$baseCommit" | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "notes_path=$notesPath" | |
| - name: Summarize release notes | |
| shell: pwsh | |
| run: | | |
| Write-Host "Release notes generated from base commit: $env:base_commit" | |
| Get-Content "${{ steps.notes.outputs.notes_path }}" | ForEach-Object { | |
| Write-Host $_ | |
| } | |
| - name: Package dist into zip | |
| id: package | |
| shell: pwsh | |
| run: | | |
| $zipName = "$($env:name)-v$($env:version).zip" | |
| $zipPath = Join-Path $PWD $zipName | |
| if (-not (Test-Path -Path dist)) { | |
| Write-Error 'dist directory not found. Build failed.' | |
| exit 1 | |
| } | |
| if (Test-Path $zipPath) { | |
| Remove-Item $zipPath -Force | |
| } | |
| Compress-Archive -Path dist\* -DestinationPath $zipPath | |
| if (-not (Test-Path $zipPath)) { | |
| Write-Error 'Zip creation failed.' | |
| exit 1 | |
| } | |
| $size = (Get-Item $zipPath).Length | |
| if ($size -gt 2147483648) { | |
| Write-Error "Zip size ($size bytes) exceeds GitHub's 2 GB limit." | |
| exit 1 | |
| } | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "zip_path=$zipPath" | |
| Add-Content -Path $env:GITHUB_OUTPUT -Value "zip_name=$zipName" | |
| - name: Publish GitHub release | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $tag = $env:release_tag | |
| $title = $env:release_title | |
| $zipPath = "${{ steps.package.outputs.zip_path }}" | |
| $notesPath = "${{ steps.notes.outputs.notes_path }}" | |
| if (-not $env:GH_TOKEN) { | |
| Write-Error 'GH_TOKEN is not available.' | |
| exit 1 | |
| } | |
| gh release create $tag $zipPath --title $title --notes-file $notesPath --target $env:GITHUB_SHA | |
| - name: Confirm release | |
| shell: pwsh | |
| run: | | |
| Write-Host "Released version $env:version with asset ${{ steps.package.outputs.zip_name }}." | |