Add release infrastructure and distribution docs #1
Workflow file for this run
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 | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Tag to release (e.g. v0.1.0). Must already exist." | |
| required: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.target }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: macos-arm64 | |
| runner: macos-14 | |
| archive: tar.gz | |
| - target: macos-x86_64 | |
| runner: macos-13 | |
| archive: tar.gz | |
| - target: linux-arm64 | |
| runner: ubuntu-24.04-arm | |
| archive: tar.gz | |
| - target: linux-x86_64 | |
| runner: ubuntu-24.04 | |
| archive: tar.gz | |
| - target: windows-x86_64 | |
| runner: windows-latest | |
| archive: zip | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.tag || github.ref }} | |
| - name: Setup Dart | |
| uses: dart-lang/setup-dart@v1 | |
| - name: Resolve tag | |
| id: tag | |
| shell: bash | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag }}" ]; then | |
| echo "name=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "name=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Verify pubspec version matches tag | |
| shell: bash | |
| run: | | |
| tag="${{ steps.tag.outputs.name }}" | |
| pubspec_version=$(awk '/^version:/ {print $2}' pubspec.yaml) | |
| expected="v${pubspec_version}" | |
| if [ "$tag" != "$expected" ]; then | |
| echo "::error::Tag $tag does not match pubspec.yaml version $pubspec_version (expected $expected)" | |
| exit 1 | |
| fi | |
| - name: Install dependencies | |
| run: dart pub get | |
| - name: Compile binary (Unix) | |
| if: runner.os != 'Windows' | |
| run: | | |
| mkdir -p build | |
| dart compile exe bin/space.dart -o build/space | |
| - name: Compile binary (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force -Path build | Out-Null | |
| dart compile exe bin/space.dart -o build/space.exe | |
| - name: Package archive (Unix) | |
| if: matrix.archive == 'tar.gz' | |
| shell: bash | |
| run: | | |
| name="space-cli-${{ matrix.target }}" | |
| staging="dist/$name" | |
| mkdir -p "$staging" | |
| cp build/space "$staging/" | |
| cp README.md LICENSE "$staging/" 2>/dev/null || cp README.md "$staging/" | |
| tar -czf "dist/${name}.tar.gz" -C dist "$name" | |
| (cd dist && shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256") | |
| - name: Package archive (Windows) | |
| if: matrix.archive == 'zip' | |
| shell: pwsh | |
| run: | | |
| $name = "space-cli-${{ matrix.target }}" | |
| $staging = "dist/$name" | |
| New-Item -ItemType Directory -Force -Path $staging | Out-Null | |
| Copy-Item build/space.exe "$staging/" | |
| Copy-Item README.md "$staging/" | |
| Compress-Archive -Path "$staging/*" -DestinationPath "dist/${name}.zip" | |
| $hash = (Get-FileHash -Algorithm SHA256 "dist/${name}.zip").Hash.ToLower() | |
| "$hash ${name}.zip" | Out-File -FilePath "dist/${name}.zip.sha256" -Encoding ascii | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.target }} | |
| path: | | |
| dist/*.tar.gz | |
| dist/*.zip | |
| dist/*.sha256 | |
| if-no-files-found: error | |
| retention-days: 7 | |
| release: | |
| name: Publish GitHub Release | |
| runs-on: ubuntu-latest | |
| needs: build | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Assemble checksums.txt | |
| run: | | |
| mkdir -p release | |
| find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" \) -exec cp {} release/ \; | |
| cd release | |
| sha256sum ./*.tar.gz ./*.zip | sed 's| \./| |g' > checksums.txt | |
| cat checksums.txt | |
| - name: Resolve tag | |
| id: tag | |
| run: | | |
| if [ -n "${{ github.event.inputs.tag }}" ]; then | |
| echo "name=${{ github.event.inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "name=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.tag.outputs.name }} | |
| name: ${{ steps.tag.outputs.name }} | |
| draft: false | |
| prerelease: ${{ contains(steps.tag.outputs.name, '-') }} | |
| generate_release_notes: true | |
| files: | | |
| release/*.tar.gz | |
| release/*.zip | |
| release/checksums.txt | |
| homebrew: | |
| name: Update Homebrew Tap | |
| runs-on: ubuntu-latest | |
| needs: release | |
| if: ${{ !contains(github.ref, '-') }} | |
| steps: | |
| - name: Checkout tap | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: space-org/homebrew-tap | |
| token: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| path: tap | |
| - name: Resolve tag and version | |
| id: meta | |
| run: | | |
| tag="${GITHUB_REF#refs/tags/}" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=${tag#v}" >> "$GITHUB_OUTPUT" | |
| - name: Fetch checksums | |
| id: sums | |
| run: | | |
| tag="${{ steps.meta.outputs.tag }}" | |
| base="https://github.com/${{ github.repository }}/releases/download/${tag}" | |
| curl -fsSL "${base}/checksums.txt" -o checksums.txt | |
| get() { grep " $1$" checksums.txt | awk '{print $1}'; } | |
| { | |
| echo "macos_arm64=$(get space-cli-macos-arm64.tar.gz)" | |
| echo "macos_x86_64=$(get space-cli-macos-x86_64.tar.gz)" | |
| echo "linux_arm64=$(get space-cli-linux-arm64.tar.gz)" | |
| echo "linux_x86_64=$(get space-cli-linux-x86_64.tar.gz)" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Render formula | |
| run: | | |
| cat > tap/Formula/space-cli.rb <<EOF | |
| class SpaceCli < Formula | |
| desc "CLI for querying your local Space flashcard database" | |
| homepage "https://getspace.dev/cli" | |
| version "${{ steps.meta.outputs.version }}" | |
| license "MIT" | |
| on_macos do | |
| on_arm do | |
| url "https://github.com/${{ github.repository }}/releases/download/${{ steps.meta.outputs.tag }}/space-cli-macos-arm64.tar.gz" | |
| sha256 "${{ steps.sums.outputs.macos_arm64 }}" | |
| end | |
| on_intel do | |
| url "https://github.com/${{ github.repository }}/releases/download/${{ steps.meta.outputs.tag }}/space-cli-macos-x86_64.tar.gz" | |
| sha256 "${{ steps.sums.outputs.macos_x86_64 }}" | |
| end | |
| end | |
| on_linux do | |
| on_arm do | |
| url "https://github.com/${{ github.repository }}/releases/download/${{ steps.meta.outputs.tag }}/space-cli-linux-arm64.tar.gz" | |
| sha256 "${{ steps.sums.outputs.linux_arm64 }}" | |
| end | |
| on_intel do | |
| url "https://github.com/${{ github.repository }}/releases/download/${{ steps.meta.outputs.tag }}/space-cli-linux-x86_64.tar.gz" | |
| sha256 "${{ steps.sums.outputs.linux_x86_64 }}" | |
| end | |
| end | |
| def install | |
| bin.install "space" | |
| end | |
| test do | |
| assert_match "space-cli", shell_output("#{bin}/space --version") | |
| end | |
| end | |
| EOF | |
| - name: Commit and push | |
| run: | | |
| cd tap | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add Formula/space-cli.rb | |
| if git diff --cached --quiet; then | |
| echo "No changes to formula." | |
| exit 0 | |
| fi | |
| git commit -m "space-cli ${{ steps.meta.outputs.tag }}" | |
| git push |