From e33da6ee89d72c3b8023c18e27712f71029157bb Mon Sep 17 00:00:00 2001 From: Harsh Shandilya Date: Fri, 25 Aug 2023 05:09:47 +0530 Subject: [PATCH] feat(ci): upgrade to cargo-dist 0.2.0-prerelease.5 --- .github/workflows/release.yml | 217 ++++++++++++++++++++++------------ Cargo.toml | 17 +++ flake.nix | 3 +- 3 files changed, 158 insertions(+), 79 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 153f6ef..55e1e5c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,15 @@ +# Copyright 2022-2023, axodotdev +# SPDX-License-Identifier: MIT or Apache-2.0 +# # CI that: # -# * checks for a Git Tag that looks like a release ("v1.2.0") -# * creates a Github Release™️ -# * builds binaries/packages with cargo-dist -# * uploads those packages to the Github Release™️ +# * checks for a Git Tag that looks like a release +# * creates a Github Release™ and fills in its text +# * builds artifacts with cargo-dist (executable-zips, installers) +# * uploads those artifacts to the Github Release™ # -# Note that the Github Release™️ will be created before the packages, -# so there will be a few minutes where the release has no packages +# Note that the Github Release™ will be created before the artifacts, +# so there will be a few minutes where the release has no artifacts # and then they will slowly trickle in, possibly failing. To make # this more pleasant we mark the release as a "draft" until all # artifacts have been successfully uploaded. This allows you to @@ -17,134 +20,192 @@ name: Release permissions: contents: write -# This task will run whenever you push a git tag that looks like -# a version number. We just look for `v` followed by at least one number -# and then whatever. so `v1`, `v1.0.0`, and `v1.0.0-prerelease` all work. +# This task will run whenever you push a git tag that looks like a version +# like "v1", "v1.2.0", "v0.1.0-prerelease01", "my-app-v1.0.0", etc. +# The version will be roughly parsed as ({PACKAGE_NAME}-)?v{VERSION}, where +# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION +# must be a Cargo-style SemVer Version. # -# If there's a prerelease-style suffix to the version then the Github Release™️ -# will be marked as a prerelease (handled by taiki-e/create-gh-release-action). +# If PACKAGE_NAME is specified, then we will create a Github Release™ for that +# package (erroring out if it doesn't have the given version or isn't cargo-dist-able). # -# Note that when generating links to uploaded artifacts, cargo-dist will currently -# assume that your git tag is always v{VERSION} where VERSION is the version in -# the published package's Cargo.toml (this is the default behaviour of cargo-release). -# In the future this may be made more robust/configurable. +# If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all +# (cargo-dist-able) packages in the workspace with that version (this is mode is +# intended for workspaces with only one dist-able package, or with all dist-able +# packages versioned/released in lockstep). +# +# If you push multiple tags at once, separate instances of this workflow will +# spin up, creating an independent Github Release™ for each one. +# +# If there's a prerelease-style suffix to the version then the Github Release™ +# will be marked as a prerelease. on: push: tags: - - v[0-9]+.* - -env: - ALL_CARGO_DIST_TARGET_ARGS: --target=x86_64-unknown-linux-gnu --target=x86_64-apple-darwin --target=x86_64-pc-windows-msvc - ALL_CARGO_DIST_INSTALLER_ARGS: --installer=github-shell --installer=github-powershell + - '*-?v[0-9]+*' jobs: - # Create the Github Release™️ so the packages have something to be uploaded to + # Create the Github Release™ so the packages have something to be uploaded to create-release: runs-on: ubuntu-latest outputs: - tag: ${{ steps.create-gh-release.outputs.computed-prefix }}${{ steps.create-gh-release.outputs.version }} + has-releases: ${{ steps.create-release.outputs.has-releases }} + releases: ${{ steps.create-release.outputs.releases }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 - - id: create-gh-release - uses: taiki-e/create-gh-release-action@v1 with: - draft: true - # (required) GitHub token for creating GitHub Releases. - token: ${{ secrets.GITHUB_TOKEN }} + submodules: recursive + - name: Install cargo-dist + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh" + - id: create-release + run: | + cargo dist plan --tag=${{ github.ref_name }} --output-format=json > dist-manifest.json + echo "dist plan ran successfully" + cat dist-manifest.json + # Create the Github Release™ based on what cargo-dist thinks it should be + ANNOUNCEMENT_TITLE=$(jq --raw-output ".announcement_title" dist-manifest.json) + IS_PRERELEASE=$(jq --raw-output ".announcement_is_prerelease" dist-manifest.json) + jq --raw-output ".announcement_github_body" dist-manifest.json > new_dist_announcement.md + gh release create ${{ github.ref_name }} --draft --prerelease="$IS_PRERELEASE" --title="$ANNOUNCEMENT_TITLE" --notes-file=new_dist_announcement.md + echo "created announcement!" - # Build and packages all the things - upload-artifacts: + # Upload the manifest to the Github Release™ + gh release upload ${{ github.ref_name }} dist-manifest.json + echo "uploaded manifest!" + + # Disable all the upload-artifacts tasks if we have no actual releases + HAS_RELEASES=$(jq --raw-output ".releases != null" dist-manifest.json) + echo "has-releases=$HAS_RELEASES" >> "$GITHUB_OUTPUT" + echo "releases=$(jq --compact-output ".releases" dist-manifest.json)" >> "$GITHUB_OUTPUT" + + # Build and packages all the platform-specific things + upload-local-artifacts: + # Let the initial task tell us to not run (currently very blunt) needs: create-release + if: ${{ needs.create-release.outputs.has-releases == 'true' }} strategy: + fail-fast: false matrix: # For these target platforms include: - - target: x86_64-unknown-linux-gnu - os: ubuntu-20.04 - install-dist: curl --proto '=https' --tlsv1.2 -L -sSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.2/installer.sh | sh - - target: x86_64-apple-darwin - os: macos-11 - install-dist: curl --proto '=https' --tlsv1.2 -L -sSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.2/installer.sh | sh - - target: x86_64-pc-windows-msvc - os: windows-2019 - install-dist: irm 'https://github.com/axodotdev/cargo-dist/releases/download/v0.0.2/installer.ps1' | iex + - os: "macos-11" + dist-args: "--artifacts=local --target=aarch64-apple-darwin" + install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh" + - os: "macos-11" + dist-args: "--artifacts=local --target=x86_64-apple-darwin" + install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh" + - os: "windows-2019" + dist-args: "--artifacts=local --target=x86_64-pc-windows-msvc" + install-dist: "irm https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.ps1 | iex" + - os: "ubuntu-20.04" + dist-args: "--artifacts=local --target=x86_64-unknown-linux-gnu" + install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh" runs-on: ${{ matrix.os }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 - - name: Install Rust - run: rustup update stable && rustup default stable + with: + submodules: recursive - name: Install cargo-dist run: ${{ matrix.install-dist }} - name: Run cargo-dist # This logic is a bit janky because it's trying to be a polyglot between # powershell and bash since this will run on windows, macos, and linux! # The two platforms don't agree on how to talk about env vars but they - # do agree on 'cat' and '$()' so we use that to marshal values between commmands. + # do agree on 'cat' and '$()' so we use that to marshal values between commands. run: | # Actually do builds and make zips and whatnot - cargo dist --target=${{ matrix.target }} --output-format=json > dist-manifest.json + cargo dist build --tag=${{ github.ref_name }} --output-format=json ${{ matrix.dist-args }} > dist-manifest.json echo "dist ran successfully" cat dist-manifest.json - # Parse out what we just built and upload it to the Github Release™️ - cat dist-manifest.json | jq --raw-output ".releases[].artifacts[].path" > uploads.txt + + # Parse out what we just built and upload it to the Github Release™ + jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json > uploads.txt echo "uploading..." cat uploads.txt - gh release upload ${{ needs.create-release.outputs.tag }} $(cat uploads.txt) + gh release upload ${{ github.ref_name }} $(cat uploads.txt) echo "uploaded!" - # Compute and upload the manifest for everything - upload-manifest: - needs: create-release - runs-on: ubuntu-latest + # Build and packages all the platform-agnostic(ish) things + upload-global-artifacts: + needs: upload-local-artifacts + runs-on: "ubuntu-20.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 - - name: Install Rust - run: rustup update stable && rustup default stable + with: + submodules: recursive - name: Install cargo-dist - run: curl --proto '=https' --tlsv1.2 -L -sSf https://github.com/axodotdev/cargo-dist/releases/download/v0.0.2/installer.sh | sh - - name: Run cargo-dist manifest + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh" + # Get all the local artifacts for the global tasks to use (for e.g. checksums) + - name: Fetch local artifacts run: | - # Generate a manifest describing everything - cargo dist manifest --no-local-paths --output-format=json $ALL_CARGO_DIST_TARGET_ARGS $ALL_CARGO_DIST_INSTALLER_ARGS > dist-manifest.json - echo "dist manifest ran successfully" - cat dist-manifest.json - # Upload the manifest to the Github Release™️ - gh release upload ${{ needs.create-release.outputs.tag }} dist-manifest.json - echo "uploaded manifest!" - # Edit the Github Release™️ title/body to match what cargo-dist thinks it should be - CHANGELOG_TITLE=$(cat dist-manifest.json | jq --raw-output ".releases[].changelog_title") - cat dist-manifest.json | jq --raw-output ".releases[].changelog_body" > new_dist_changelog.md - gh release edit ${{ needs.create-release.outputs.tag }} --title="$CHANGELOG_TITLE" --notes-file=new_dist_changelog.md - echo "updated release notes!" - - name: Run cargo-dist --installer=... + gh release download ${{ github.ref_name }} --dir target/distrib/ + - name: Run cargo-dist + # This logic is a bit janky because it's trying to be a polyglot between + # powershell and bash since this will run on windows, macos, and linux! + # The two platforms don't agree on how to talk about env vars but they + # do agree on 'cat' and '$()' so we use that to marshal values between commands. run: | - # Run cargo dist with --no-builds to get agnostic artifacts like installers - cargo dist --output-format=json --no-builds $ALL_CARGO_DIST_INSTALLER_ARGS > dist-manifest.json + cargo dist build --tag=${{ github.ref_name }} --output-format=json "--artifacts=global" > dist-manifest.json echo "dist ran successfully" cat dist-manifest.json - # Grab the installers that were generated and upload them. - # This filter is working around the fact that --no-builds is kinds hacky - # and still makes/reports malformed zips that we don't want to upload. - cat dist-manifest.json | jq --raw-output '.releases[].artifacts[] | select(.kind == "installer") | .path' > uploads.txt + + # Parse out what we just built and upload it to the Github Release™ + jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json > uploads.txt echo "uploading..." cat uploads.txt - gh release upload ${{ needs.create-release.outputs.tag }} $(cat uploads.txt) - echo "uploaded installers!" + gh release upload ${{ github.ref_name }} $(cat uploads.txt) + echo "uploaded!" - # Mark the Github Release™️ as a non-draft now that everything has succeeded! + upload-homebrew-formula: + needs: [create-release, upload-global-artifacts] + runs-on: "ubuntu-20.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASES: ${{ needs.create-release.outputs.releases }} + GITHUB_USER: "axo bot" + GITHUB_EMAIL: "admin+bot@axo.dev" + steps: + - uses: actions/checkout@v3 + with: + repository: "msfjarvis/homebrew-tap" + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + # So we have access to the formula + - name: Fetch local artifacts + run: | + gh release download ${{ github.ref_name }} --dir Formula --repo ${GITHUB_REPOSITORY} --clobber + - name: Commit formula files + run: | + git config --global user.name "${GITHUB_USER}" + git config --global user.email "${GITHUB_EMAIL}" + + for release in $(echo "$RELEASES" | jq --compact-output '.[]'); do + name=$(echo "$release" | jq .app_name --raw-output) + version=$(echo "$release" | jq .app_version --raw-output) + + git add Formula/${name}.rb + git commit -m "${name} ${version}" + done + git push + + # Mark the Github Release™ as a non-draft now that everything has succeeded! publish-release: - needs: [create-release, upload-artifacts, upload-manifest] + # Only run after all the other tasks, but it's ok if upload-artifacts was skipped + needs: [create-release, upload-local-artifacts, upload-global-artifacts] + if: ${{ always() && needs.create-release.result == 'success' && (needs.upload-local-artifacts.result == 'skipped' || needs.upload-local-artifacts.result == 'success') && (needs.upload-global-artifacts.result == 'skipped' || needs.upload-global-artifacts.result == 'success') }} runs-on: ubuntu-latest env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 + with: + submodules: recursive - name: mark release as non-draft run: | - gh release edit ${{ needs.create-release.outputs.tag }} --draft=false - + gh release edit ${{ github.ref_name }} --draft=false diff --git a/Cargo.toml b/Cargo.toml index c0d46a9..30a0999 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,20 @@ videos = [] inherits = "release" debug = true split-debuginfo = "packed" + +# Config for 'cargo dist' +[workspace.metadata.dist] +# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) +cargo-dist-version = "0.2.0-prerelease.5" +# CI backends to support (see 'cargo dist generate-ci') +ci = ["github"] +# The installers to generate for each app +installers = ["shell", "powershell", "homebrew"] +# A GitHub repo to push Homebrew formulas to +tap = "msfjarvis/homebrew-tap" +# Target platforms to build apps for (Rust target-triple syntax) +targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"] +# Publish jobs to run in CI +publish-jobs = ["homebrew"] +# Whether to consider the binaries in a package for distribution (defaults true) +dist = true diff --git a/flake.nix b/flake.nix index 3a8e82e..b7c7375 100644 --- a/flake.nix +++ b/flake.nix @@ -95,10 +95,11 @@ ]; packages = with pkgs; [ + cargo-dist-unstable cargo-nextest cargo-release - rustStable oranda + rustStable ]; }; });