Create Release for git_branches #13
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: Create Package Release | |
| run-name: Create Release for ${{ inputs.package }} | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| package: | |
| description: "Package to release" | |
| required: true | |
| default: changelog_cli | |
| type: choice | |
| options: | |
| - changelog_cli | |
| - slack_cli | |
| - disk_analyzer_cli | |
| - git_chain | |
| - git_branches | |
| dry_run: | |
| description: "Dry run (do not create tag/release)" | |
| required: false | |
| default: false | |
| type: boolean | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| outputs: | |
| package_dir: ${{ steps.package_config.outputs.package_dir }} | |
| executable_name: ${{ steps.package_config.outputs.executable_name }} | |
| build_mode: ${{ steps.package_config.outputs.build_mode }} | |
| build_matrix: ${{ steps.package_config.outputs.build_matrix }} | |
| tag_name: ${{ steps.tag_check.outputs.tag_name }} | |
| version: ${{ steps.version_pubspec.outputs.version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| # Use a PAT so the pushed tag triggers the publish.yaml workflow. | |
| # Tags pushed with the default GITHUB_TOKEN do not trigger workflows. | |
| # Falls back to GITHUB_TOKEN if RELEASE_PAT is not configured (tag | |
| # push then won't auto-publish to pub.dev). | |
| token: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }} | |
| - name: Setup Dart | |
| uses: dart-lang/setup-dart@v1 | |
| - name: Install and activate changelog_cli | |
| run: | | |
| cd changelog_cli | |
| dart pub get | |
| dart pub global activate --source=path . | |
| - name: Resolve package configuration | |
| id: package_config | |
| run: | | |
| PACKAGE="${{ inputs.package }}" | |
| case "$PACKAGE" in | |
| changelog_cli) | |
| echo "package_dir=changelog_cli" >> "$GITHUB_OUTPUT" | |
| echo "executable_name=changelog_cli" >> "$GITHUB_OUTPUT" | |
| echo "build_mode=exe" >> "$GITHUB_OUTPUT" | |
| echo 'build_matrix={"include":[{"os":"ubuntu-latest","artifact_name":"changelog_cli_linux","binary_name":"changelog_cli_linux"},{"os":"windows-latest","artifact_name":"changelog_cli_windows_x64","binary_name":"changelog_cli.exe"},{"os":"macos-latest","artifact_name":"changelog_cli_macos_arm64","binary_name":"changelog_cli_macos"}]}' >> "$GITHUB_OUTPUT" | |
| ;; | |
| slack_cli) | |
| echo "package_dir=slack_cli" >> "$GITHUB_OUTPUT" | |
| echo "executable_name=slack_cli" >> "$GITHUB_OUTPUT" | |
| echo "build_mode=exe" >> "$GITHUB_OUTPUT" | |
| echo 'build_matrix={"include":[{"os":"ubuntu-latest","artifact_name":"slack_cli_linux","binary_name":"slack_cli_linux"},{"os":"windows-latest","artifact_name":"slack_cli_windows_x64","binary_name":"slack_cli.exe"},{"os":"macos-latest","artifact_name":"slack_cli_macos_arm64","binary_name":"slack_cli_macos"}]}' >> "$GITHUB_OUTPUT" | |
| ;; | |
| disk_analyzer_cli) | |
| echo "package_dir=disk_analyzer_cli" >> "$GITHUB_OUTPUT" | |
| echo "executable_name=disk_analyzer_cli" >> "$GITHUB_OUTPUT" | |
| echo "build_mode=bundle" >> "$GITHUB_OUTPUT" | |
| echo 'build_matrix={"include":[{"os":"ubuntu-latest","artifact_name":"disk_analyzer_cli_linux_x64"},{"os":"windows-latest","artifact_name":"disk_analyzer_cli_windows_x64"},{"os":"macos-latest","artifact_name":"disk_analyzer_cli_macos_arm64"}]}' >> "$GITHUB_OUTPUT" | |
| ;; | |
| git_chain) | |
| echo "package_dir=git_chain" >> "$GITHUB_OUTPUT" | |
| echo "executable_name=git_chain" >> "$GITHUB_OUTPUT" | |
| echo "build_mode=bundle" >> "$GITHUB_OUTPUT" | |
| echo "changelog_printer=markdown" >> "$GITHUB_OUTPUT" | |
| echo 'build_matrix={"include":[{"os":"ubuntu-latest","artifact_name":"git_chain_linux_x64"},{"os":"windows-latest","artifact_name":"git_chain_windows_x64"},{"os":"macos-latest","artifact_name":"git_chain_macos_arm64"}]}' >> "$GITHUB_OUTPUT" | |
| ;; | |
| git_branches) | |
| echo "package_dir=git_branches" >> "$GITHUB_OUTPUT" | |
| echo "executable_name=git_branches" >> "$GITHUB_OUTPUT" | |
| echo "build_mode=exe" >> "$GITHUB_OUTPUT" | |
| echo "changelog_printer=markdown" >> "$GITHUB_OUTPUT" | |
| echo 'build_matrix={"include":[{"os":"ubuntu-latest","artifact_name":"git_branches_linux","binary_name":"git_branches_linux"},{"os":"windows-latest","artifact_name":"git_branches_windows_x64","binary_name":"git_branches.exe"},{"os":"macos-latest","artifact_name":"git_branches_macos_arm64","binary_name":"git_branches_macos"}]}' >> "$GITHUB_OUTPUT" | |
| ;; | |
| esac | |
| - name: Read version from pubspec.yaml | |
| id: version_pubspec | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION=$(grep '^version:' "$PACKAGE_DIR/pubspec.yaml" | sed 's/version: //') | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Version from pubspec.yaml: $VERSION" | |
| - name: Read version from version.dart if present | |
| id: version_dart | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION_FILE="$PACKAGE_DIR/lib/src/version.dart" | |
| if [ -f "$VERSION_FILE" ]; then | |
| VERSION=$(grep 'const packageVersion' "$VERSION_FILE" | sed "s/const packageVersion = '\(.*\)';/\1/") | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Version from version.dart: $VERSION" | |
| else | |
| echo 'version=' >> "$GITHUB_OUTPUT" | |
| echo "No version.dart for $PACKAGE_DIR, using pubspec.yaml only" | |
| fi | |
| - name: Verify versions match | |
| run: | | |
| if [ -n "${{ steps.version_dart.outputs.version }}" ] && [ "${{ steps.version_dart.outputs.version }}" != "${{ steps.version_pubspec.outputs.version }}" ]; then | |
| echo "ERROR: Version mismatch!" | |
| echo "version.dart: ${{ steps.version_dart.outputs.version }}" | |
| echo "pubspec.yaml: ${{ steps.version_pubspec.outputs.version }}" | |
| exit 1 | |
| fi | |
| echo "✅ Release version: ${{ steps.version_pubspec.outputs.version }}" | |
| - name: Get latest tag | |
| id: latest_tag | |
| run: | | |
| PACKAGE="${{ inputs.package }}" | |
| LATEST_TAG=$(git tag -l "$PACKAGE-v*" | sort -V | tail -n 1) | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No previous tags found for $PACKAGE, using initial commit" | |
| LATEST_TAG=$(git rev-list --max-parents=0 HEAD) | |
| fi | |
| echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT" | |
| echo "Latest tag: $LATEST_TAG" | |
| - name: Check if tag already exists | |
| id: tag_check | |
| run: | | |
| TAG_NAME="${{ inputs.package }}-v${{ steps.version_pubspec.outputs.version }}" | |
| if git tag -l | grep -q "^$TAG_NAME$"; then | |
| echo 'exists=true' >> "$GITHUB_OUTPUT" | |
| echo "❌ Tag $TAG_NAME already exists" | |
| else | |
| echo 'exists=false' >> "$GITHUB_OUTPUT" | |
| echo "✅ Tag $TAG_NAME does not exist" | |
| fi | |
| echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" | |
| - name: Fail if tag exists | |
| if: steps.tag_check.outputs.exists == 'true' | |
| run: | | |
| echo "ERROR: Tag ${{ steps.tag_check.outputs.tag_name }} already exists!" | |
| echo "Please update the version before releasing ${{ inputs.package }}" | |
| exit 1 | |
| - name: Check if version already in CHANGELOG.md | |
| id: changelog_present | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION="${{ steps.version_pubspec.outputs.version }}" | |
| CHANGELOG="$PACKAGE_DIR/CHANGELOG.md" | |
| present=false | |
| if [ -f "$CHANGELOG" ] && awk -v ver="$VERSION" ' | |
| { s=$0; sub(/^#+[[:space:]]*/,"",s); sub(/[[:space:]]+$/,"",s); if (s==ver) { found=1; exit } } | |
| END { exit (found ? 0 : 1) } | |
| ' "$CHANGELOG"; then | |
| present=true | |
| echo "ℹ️ Version $VERSION already present in $CHANGELOG, skipping changelog generation" | |
| else | |
| echo "✅ Version $VERSION not yet in $CHANGELOG, will generate" | |
| fi | |
| echo "present=$present" >> "$GITHUB_OUTPUT" | |
| - name: Generate changelog and update CHANGELOG.md | |
| id: changelog | |
| if: steps.changelog_present.outputs.present != 'true' | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION="${{ steps.version_pubspec.outputs.version }}" | |
| PRINTER="${{ steps.package_config.outputs.changelog_printer }}" | |
| PRINTER="${PRINTER:-simple}" | |
| echo "Generating changelog for $PACKAGE_DIR from ${{ steps.latest_tag.outputs.latest_tag }} to HEAD (printer: $PRINTER)" | |
| changelog_cli generate \ | |
| --start "${{ steps.latest_tag.outputs.latest_tag }}" \ | |
| --end HEAD \ | |
| --path "$PACKAGE_DIR" \ | |
| --version "$VERSION" \ | |
| --printer "$PRINTER" \ | |
| --output "$PACKAGE_DIR/CHANGELOG.md" | |
| changelog_cli generate \ | |
| --start "${{ steps.latest_tag.outputs.latest_tag }}" \ | |
| --end HEAD \ | |
| --path "$PACKAGE_DIR" \ | |
| --version "$VERSION" \ | |
| --printer "$PRINTER" \ | |
| --output /tmp/new_changelog.md | |
| echo "✅ Updated $PACKAGE_DIR/CHANGELOG.md with version $VERSION" | |
| - name: Extract existing changelog section for release notes | |
| if: steps.changelog_present.outputs.present == 'true' | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION="${{ steps.version_pubspec.outputs.version }}" | |
| CHANGELOG="$PACKAGE_DIR/CHANGELOG.md" | |
| # Extract the section for VERSION: from its header line up to the | |
| # next version header (a line whose text after stripping leading | |
| # '#'s starts with a version number). | |
| awk -v ver="$VERSION" ' | |
| function header_version(line, s) { | |
| s=line; sub(/^#+[[:space:]]*/,"",s); sub(/[[:space:]]+$/,"",s) | |
| return s | |
| } | |
| !found { | |
| if (header_version($0)==ver) { found=1; print; next } | |
| } | |
| found { | |
| s=header_version($0) | |
| if (s ~ /^[0-9]+\.[0-9]+/) { exit } | |
| } | |
| ' "$CHANGELOG" > /tmp/new_changelog.md | |
| echo "✅ Extracted existing CHANGELOG.md section for version $VERSION" | |
| echo "📝 Release notes content:" | |
| cat /tmp/new_changelog.md | |
| - name: Setup Git | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| - name: Commit updated CHANGELOG.md | |
| run: | | |
| PACKAGE_DIR="${{ steps.package_config.outputs.package_dir }}" | |
| VERSION="${{ steps.version_pubspec.outputs.version }}" | |
| git add "$PACKAGE_DIR/CHANGELOG.md" | |
| if git diff --staged --quiet; then | |
| echo "No changes to commit" | |
| else | |
| git commit -m "chore($PACKAGE_DIR): update CHANGELOG.md for v$VERSION" | |
| echo "✅ Committed CHANGELOG.md changes" | |
| fi | |
| - name: Push changes | |
| if: ${{ !inputs.dry_run }} | |
| run: git push origin main | |
| - name: Create and push tag | |
| if: ${{ !inputs.dry_run }} | |
| run: | | |
| TAG_NAME="${{ steps.tag_check.outputs.tag_name }}" | |
| git tag -a "$TAG_NAME" -m "Release $TAG_NAME" | |
| git push origin "$TAG_NAME" | |
| echo "✅ Created and pushed tag: $TAG_NAME" | |
| - name: Create GitHub Release | |
| if: ${{ !inputs.dry_run }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| TAG_NAME="${{ steps.tag_check.outputs.tag_name }}" | |
| gh release create "$TAG_NAME" \ | |
| --title "$TAG_NAME" \ | |
| --notes-file /tmp/new_changelog.md \ | |
| --draft | |
| echo "✅ Created GitHub release: $TAG_NAME" | |
| echo "The release is created as draft and will be published after artifacts are uploaded" | |
| - name: Dry run summary | |
| if: ${{ inputs.dry_run }} | |
| run: | | |
| echo "🔍 DRY RUN SUMMARY:" | |
| echo "- Package: ${{ steps.package_config.outputs.package_dir }}" | |
| echo "- Version: ${{ steps.version_pubspec.outputs.version }}" | |
| echo "- Tag that would be created: ${{ steps.tag_check.outputs.tag_name }}" | |
| echo "- Latest tag found: ${{ steps.latest_tag.outputs.latest_tag }}" | |
| if [ "${{ steps.changelog_present.outputs.present }}" == "true" ]; then | |
| echo "- Version already in CHANGELOG.md, generation skipped (existing section used for release notes)" | |
| else | |
| echo "- CHANGELOG.md would be updated with new content" | |
| fi | |
| echo "- No actual tag or release would be created" | |
| echo "" | |
| echo "📝 Release notes content:" | |
| cat /tmp/new_changelog.md | |
| build_assets: | |
| needs: release | |
| if: ${{ !inputs.dry_run }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: ${{ fromJson(needs.release.outputs.build_matrix) }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Setup Dart | |
| uses: dart-lang/setup-dart@v1 | |
| - name: Install dependencies | |
| run: | | |
| cd "${{ needs.release.outputs.package_dir }}" | |
| dart pub get | |
| - name: Build executable | |
| if: ${{ needs.release.outputs.build_mode == 'exe' }} | |
| run: | | |
| cd "${{ needs.release.outputs.package_dir }}" | |
| mkdir -p "build/${{ matrix.artifact_name }}" | |
| dart compile exe "bin/${{ needs.release.outputs.executable_name }}.dart" -o "build/${{ matrix.artifact_name }}/${{ matrix.binary_name }}" | |
| - name: Upload executable artifact | |
| if: ${{ needs.release.outputs.build_mode == 'exe' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: asset-${{ matrix.artifact_name }} | |
| path: ${{ needs.release.outputs.package_dir }}/build/${{ matrix.artifact_name }}/${{ matrix.binary_name }} | |
| - name: Build CLI bundle | |
| if: ${{ needs.release.outputs.build_mode == 'bundle' }} | |
| run: | | |
| cd "${{ needs.release.outputs.package_dir }}" | |
| dart build cli --target "bin/${{ needs.release.outputs.executable_name }}.dart" --output "build/${{ matrix.artifact_name }}" | |
| - name: Upload bundle artifact | |
| if: ${{ needs.release.outputs.build_mode == 'bundle' }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: asset-${{ matrix.artifact_name }} | |
| path: ${{ needs.release.outputs.package_dir }}/build/${{ matrix.artifact_name }}/bundle | |
| upload_artifacts: | |
| needs: [release, build_assets] | |
| if: ${{ !inputs.dry_run }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: release-assets | |
| pattern: asset-* | |
| - name: Package release bundles | |
| if: ${{ needs.release.outputs.build_mode == 'bundle' }} | |
| run: | | |
| mkdir -p dist | |
| for bundle_dir in release-assets/asset-*; do | |
| asset_name="${bundle_dir#release-assets/asset-}" | |
| ( | |
| cd "$bundle_dir" | |
| zip -r "../../dist/${asset_name}.zip" bin lib | |
| ) | |
| done | |
| - name: Upload executable artifacts to existing release | |
| if: ${{ needs.release.outputs.build_mode == 'exe' }} | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.release.outputs.tag_name }} | |
| files: release-assets/asset-*/* | |
| draft: false | |
| - name: Upload bundle archives to existing release | |
| if: ${{ needs.release.outputs.build_mode == 'bundle' }} | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.release.outputs.tag_name }} | |
| files: dist/*.zip | |
| draft: false | |
| update_homebrew: | |
| # Runs after the release is published with its assets, since the tap's | |
| # update workflow downloads the release binaries to recompute checksums. | |
| needs: [release, upload_artifacts] | |
| if: ${{ !inputs.dry_run }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Trigger homebrew formula update | |
| env: | |
| # Needs actions:write on orestesgaolin/homebrew-tap. | |
| GH_TOKEN: ${{ secrets.RELEASE_PAT }} | |
| run: | | |
| PACKAGE="${{ inputs.package }}" | |
| case "$PACKAGE" in | |
| changelog_cli) WORKFLOW=update-changelog-cli.yml ;; | |
| slack_cli) WORKFLOW=update-slack-cli.yml ;; | |
| disk_analyzer_cli) WORKFLOW=update-disk-analyzer-cli.yml ;; | |
| git_chain) WORKFLOW=update-git-chain.yml ;; | |
| git_branches) WORKFLOW=update-git-branches.yml ;; | |
| *) | |
| echo "No homebrew formula for $PACKAGE, skipping" | |
| exit 0 | |
| ;; | |
| esac | |
| if [ -z "$GH_TOKEN" ]; then | |
| echo "::warning::RELEASE_PAT not set; skipping homebrew update for $PACKAGE" | |
| exit 0 | |
| fi | |
| echo "Dispatching $WORKFLOW in orestesgaolin/homebrew-tap" | |
| gh workflow run "$WORKFLOW" --repo orestesgaolin/homebrew-tap --ref main |