Nightly Release v3-alpha #375
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: Nightly Release v3-alpha | |
| on: | |
| schedule: | |
| - cron: '0 15 * * *' # 3 PM UTC daily | |
| workflow_dispatch: | |
| inputs: | |
| force_release: | |
| description: 'Force release even if no changes detected' | |
| required: false | |
| default: false | |
| type: boolean | |
| dry_run: | |
| description: 'Run in dry-run mode (no actual release)' | |
| required: false | |
| default: true | |
| type: boolean | |
| jobs: | |
| nightly-release: | |
| runs-on: ubuntu-latest | |
| env: | |
| GOWORK: "off" | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| actions: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: master | |
| fetch-depth: 0 | |
| token: ${{ secrets.WAILS_REPO_TOKEN || github.token }} | |
| - name: Setup Go | |
| uses: actions/setup-go@v4 | |
| with: | |
| go-version: '1.25' | |
| cache: true | |
| cache-dependency-path: 'v3/go.sum' | |
| - name: Install Task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Git | |
| run: | | |
| git config --global user.name "github-actions[bot]" | |
| git config --global user.email "github-actions[bot]@users.noreply.github.com" | |
| # Configure git to use the token for authentication | |
| git config --global url."https://x-access-token:${{ secrets.WAILS_REPO_TOKEN || github.token }}@github.com/".insteadOf "https://github.com/" | |
| - name: Sync webview2 to latest release | |
| # Ensure v3 always releases against the newest released webview2 module. | |
| # Reads the latest webview2/v* tag; if v3/go.mod is behind, bumps it and | |
| # folds the webview2 release notes into the v3 unreleased changelog (so | |
| # they appear in the v3 release notes). Best-effort: a failure here must | |
| # never block the v3 release — it just proceeds on the current version. | |
| id: webview2_sync | |
| env: | |
| GH_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }} | |
| run: | | |
| set -e | |
| LATEST_TAG=$(git tag -l "webview2/v*" | sort -V | tail -n 1) | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No webview2 tag found — skipping." | |
| exit 0 | |
| fi | |
| LATEST="${LATEST_TAG#webview2/}" | |
| CURRENT=$(awk '/wailsapp\/wails\/webview2 /{print $2}' v3/go.mod | head -n1) | |
| echo "webview2: current=$CURRENT latest=$LATEST" | |
| if [ "$LATEST" = "$CURRENT" ]; then | |
| echo "Already on latest webview2 ($CURRENT)." | |
| exit 0 | |
| fi | |
| echo "Bumping webview2 $CURRENT -> $LATEST" | |
| if ! (cd v3 && go get "github.com/wailsapp/wails/webview2@${LATEST}" && go mod tidy); then | |
| echo "::warning::webview2 bump to ${LATEST} failed; continuing with ${CURRENT}" | |
| git checkout -- v3/go.mod v3/go.sum 2>/dev/null || true | |
| exit 0 | |
| fi | |
| # Fold the webview2 release notes into the v3 unreleased changelog. | |
| NOTES=$(gh release view "webview2/${LATEST}" --repo "${{ github.repository }}" --json body -q .body 2>/dev/null || true) | |
| ENTRY="- Bump \`webview2\` to ${LATEST}." | |
| if [ -n "$NOTES" ]; then | |
| BODY=$(printf '%s\n' "$NOTES" | sed '1{/^## /d;}' | sed '/^[[:space:]]*$/d' | sed 's/^/ /') | |
| INSERT="${ENTRY}"$'\n'"${BODY}" | |
| else | |
| INSERT="${ENTRY}" | |
| fi | |
| # Insert the notes under "## Changed". If that anchor is ever missing, | |
| # append the section so the notes are never silently dropped. | |
| if grep -q '^## Changed' v3/UNRELEASED_CHANGELOG.md; then | |
| awk -v ins="$INSERT" ' | |
| /^## Changed/ && !done { print; print ins; done=1; next } | |
| { print } | |
| ' v3/UNRELEASED_CHANGELOG.md > v3/UNRELEASED_CHANGELOG.md.tmp | |
| mv v3/UNRELEASED_CHANGELOG.md.tmp v3/UNRELEASED_CHANGELOG.md | |
| else | |
| printf '\n## Changed\n%s\n' "$INSERT" >> v3/UNRELEASED_CHANGELOG.md | |
| fi | |
| git add v3/go.mod v3/go.sum v3/UNRELEASED_CHANGELOG.md | |
| if git diff --cached --quiet; then | |
| echo "No staged changes after bump — nothing to commit." | |
| exit 0 | |
| fi | |
| # Commit/push are best-effort too: a failure here must not block the v3 | |
| # release. Reset to the remote and continue on the current version. | |
| if ! git commit -m "chore(v3): bump webview2 to ${LATEST}" || ! git push origin master; then | |
| echo "::warning::webview2 sync commit/push failed; resetting and continuing without the bump" | |
| git fetch origin master || true | |
| git reset --hard origin/master || true | |
| exit 0 | |
| fi | |
| echo "bumped=true" >> "$GITHUB_OUTPUT" | |
| - name: Check for existing release tag | |
| id: check_tag | |
| run: | | |
| if git describe --tags --exact-match HEAD 2>/dev/null; then | |
| echo "has_tag=true" >> $GITHUB_OUTPUT | |
| echo "tag=$(git describe --tags --exact-match HEAD)" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_tag=false" >> $GITHUB_OUTPUT | |
| echo "tag=" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Check for unreleased changelog content | |
| id: changelog_check | |
| run: | | |
| echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..." | |
| # Run the release script in check mode to see if there's content | |
| cd v3/tasks/release | |
| # Use the release script itself to check for content | |
| if go run release.go --check-only 2>/dev/null; then | |
| echo "has_unreleased_content=true" >> $GITHUB_OUTPUT | |
| echo "✅ Found unreleased changelog content" | |
| else | |
| echo "has_unreleased_content=false" >> $GITHUB_OUTPUT | |
| echo "ℹ️ No unreleased changelog content found" | |
| fi | |
| - name: Quick change detection and early exit | |
| id: quick_check | |
| run: | | |
| echo "🔍 Quick check for changes to determine if we should continue..." | |
| # First check if we have unreleased changelog content | |
| if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then | |
| echo "✅ Found unreleased changelog content, proceeding with release" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "should_continue=true" >> $GITHUB_OUTPUT | |
| echo "reason=Found unreleased changelog content" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # If no unreleased changelog content, check for git changes as fallback | |
| echo "No unreleased changelog content found, checking for git changes..." | |
| # Check if current commit has a release tag | |
| if git describe --tags --exact-match HEAD 2>/dev/null; then | |
| CURRENT_TAG=$(git describe --tags --exact-match HEAD) | |
| echo "Current commit has release tag: $CURRENT_TAG" | |
| # For tagged commits, check if there are changes since the tag | |
| COMMIT_COUNT=$(git rev-list ${CURRENT_TAG}..HEAD --count) | |
| if [ "$COMMIT_COUNT" -eq 0 ]; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "should_continue=false" >> $GITHUB_OUTPUT | |
| echo "reason=No changes since existing tag $CURRENT_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "should_continue=true" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| # No current tag, check against latest release. | |
| # Only consider the active "alpha2.*" series: it outranks the legacy | |
| # "alpha.*" tags in semver, and on the very first run (no alpha2 tag | |
| # yet) the empty result correctly forces the inaugural alpha2 release. | |
| LATEST_TAG=$(git tag --list "v3.0.0-alpha2.*" | sort -V | tail -1) | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous release found, proceeding with release" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "should_continue=true" >> $GITHUB_OUTPUT | |
| else | |
| COMMIT_COUNT=$(git rev-list ${LATEST_TAG}..HEAD --count) | |
| if [ "$COMMIT_COUNT" -gt 0 ]; then | |
| echo "Found $COMMIT_COUNT commits since $LATEST_TAG" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "should_continue=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "should_continue=false" >> $GITHUB_OUTPUT | |
| echo "reason=No changes since latest release $LATEST_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT | |
| fi | |
| fi | |
| fi | |
| - name: Early exit - No changes detected | |
| if: | | |
| steps.quick_check.outputs.should_continue == 'false' && | |
| github.event.inputs.force_release != 'true' | |
| run: | | |
| echo "🛑 EARLY EXIT: ${{ steps.quick_check.outputs.reason }}" | |
| echo "" | |
| echo "ℹ️ No changes detected since last release and force_release is not enabled." | |
| echo " Workflow will exit early to save resources." | |
| echo "" | |
| echo " To force a release anyway, run this workflow with 'force_release=true'" | |
| echo "" | |
| echo "## 🛑 Early Exit Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "**Reason:** ${{ steps.quick_check.outputs.reason }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Action:** Workflow exited early to save resources" >> $GITHUB_STEP_SUMMARY | |
| echo "**Force Release:** Set 'force_release=true' to override this behavior" >> $GITHUB_STEP_SUMMARY | |
| exit 0 | |
| - name: Continue with release process | |
| if: | | |
| steps.quick_check.outputs.should_continue == 'true' || | |
| github.event.inputs.force_release == 'true' | |
| run: | | |
| echo "✅ Proceeding with release process..." | |
| if [ "${{ github.event.inputs.force_release }}" == "true" ]; then | |
| echo "🔨 FORCE RELEASE: Overriding change detection" | |
| fi | |
| - name: Run release script | |
| id: release | |
| if: | | |
| steps.quick_check.outputs.should_continue == 'true' || | |
| github.event.inputs.force_release == 'true' | |
| env: | |
| # Keep these DISTINCT: the release script validates WAILS_REPO_TOKEN | |
| # against the API before pushing anything and falls back to | |
| # GITHUB_TOKEN if the PAT is expired/revoked. Passing the same value | |
| # for both (as before) made the fallback meaningless and an expired | |
| # PAT produced a half-release: tag pushed, no GitHub release. | |
| WAILS_REPO_TOKEN: ${{ secrets.WAILS_REPO_TOKEN }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: | | |
| cd v3/tasks/release | |
| ARGS=() | |
| if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then | |
| ARGS+=(--dry-run) | |
| fi | |
| go run release.go "${ARGS[@]}" | |
| - name: Summary | |
| if: always() | |
| run: | | |
| if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then | |
| echo "## 🧪 DRY RUN Release Summary" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "## 🚀 Nightly Release Summary" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "================================" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "${{ steps.release.outputs.release_version }}" ]; then | |
| echo "- **Version:** ${{ steps.release.outputs.release_version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tag:** ${{ steps.release.outputs.release_tag }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Status:** ${{ steps.release.outcome == 'success' && '✅ Success' || '⚠️ Failed' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Mode:** ${{ steps.release.outputs.release_dry_run == 'true' && '🧪 Dry Run' || '🚀 Live release' }}" >> $GITHUB_STEP_SUMMARY | |
| if [ -n "${{ steps.release.outputs.release_url }}" ]; then | |
| echo "- **Release URL:** ${{ steps.release.outputs.release_url }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Changelog" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then | |
| echo "✅ Unreleased changelog processed and reset." >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "ℹ️ No unreleased changelog content detected." >> $GITHUB_STEP_SUMMARY | |
| fi | |
| else | |
| echo "- Release script did not run (skipped or failed before execution)." >> $GITHUB_STEP_SUMMARY | |
| fi | |