Conda Publish #287
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: Conda Publish | |
| # Chained off Auto-publish rather than triggered by `release: published`. | |
| # | |
| # Auto-publish creates the GitHub release with the default GITHUB_TOKEN, and | |
| # GitHub deliberately does not start new workflow runs from events raised that | |
| # way -- so the release trigger never once fired, and every conda package up to | |
| # 0.0.11 was published by hand. workflow_run is the documented way out: it fires | |
| # on the *workflow* finishing, not on the release event. | |
| # | |
| # The chain is also an ordering requirement, not just a convenience: the recipe | |
| # builds from `tag: v${{ version }}`, so the tag Auto-publish creates has to | |
| # exist before rattler-build can fetch the source. | |
| on: | |
| workflow_run: | |
| workflows: ["Auto-publish"] | |
| types: [completed] | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| force_build: | |
| description: 'Force build even if the version did not change' | |
| required: false | |
| default: 'false' | |
| type: boolean | |
| jobs: | |
| check: | |
| name: Decide whether there is a release to build | |
| runs-on: ubuntu-latest | |
| # Auto-publish runs on every push to main and succeeds whether or not it | |
| # published anything, so its success is not by itself a new release. | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| github.event.workflow_run.conclusion == 'success' | |
| outputs: | |
| should_publish: ${{ steps.decide.outputs.should_publish }} | |
| version: ${{ steps.decide.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| # The commit Auto-publish ran on, plus its parent, so the version can | |
| # be compared against what main held before the push. | |
| ref: ${{ github.event.workflow_run.head_sha || github.ref }} | |
| fetch-depth: 2 | |
| - name: Compare the packaged version with the previous commit | |
| id: decide | |
| run: | | |
| set -euo pipefail | |
| # `rust/Cargo.toml` since 0.1.0: the version moved there and the recipe | |
| # reads it from there too (`load_from_file`), so this is the same number | |
| # the package will be named with. `[workspace.package] version` is the | |
| # first `version = "..."` line in the file. Auto-publish decides the same | |
| # way -- keep the two in step. | |
| # | |
| # Note what is deliberately *not* here: Auto-publish also skips a version | |
| # that is already tagged, because it is the workflow that creates the tag. | |
| # By the time this one runs, the tag exists on purpose. | |
| read_version() { sed -n 's/^version = "\(.*\)"/\1/p' "$1" | head -1; } | |
| version=$(read_version rust/Cargo.toml) | |
| if [ -z "$version" ]; then | |
| echo "could not read a version from rust/Cargo.toml" >&2 | |
| exit 1 | |
| fi | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| if [ "${{ inputs.force_build }}" = "true" ]; then | |
| echo "forced: building $version regardless of whether it changed" | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git show HEAD^:rust/Cargo.toml > /tmp/previous-cargo.toml 2>/dev/null || : > /tmp/previous-cargo.toml | |
| previous=$(read_version /tmp/previous-cargo.toml) | |
| if [ "$version" = "$previous" ]; then | |
| echo "version is still ${version:-unset}; nothing to publish" | |
| echo "should_publish=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "version went from ${previous:-none} to $version; publishing" | |
| echo "should_publish=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| build: | |
| name: Build conda package | |
| needs: check | |
| if: needs.check.outputs.should_publish == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha || github.ref }} | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Wait for the release tag to exist | |
| # Auto-publish pushes the tag inside the run that triggers this one, so | |
| # it is normally already there. Retrying keeps a slow tag push from | |
| # failing the build with a confusing rattler-build fetch error. | |
| run: | | |
| set -euo pipefail | |
| tag="v${{ needs.check.outputs.version }}" | |
| for attempt in $(seq 1 10); do | |
| if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then | |
| echo "$tag exists" | |
| exit 0 | |
| fi | |
| echo "waiting for $tag (attempt $attempt)" | |
| sleep 15 | |
| done | |
| echo "$tag never appeared; the recipe builds from that tag, so stopping" >&2 | |
| exit 1 | |
| - name: Build conda package | |
| uses: prefix-dev/rattler-build-action@v0.2.39 | |
| with: | |
| recipe-path: conda.recipe/recipe.yaml | |
| # `--experimental` is what enables the recipe's `load_from_file`, which | |
| # is how its version comes from `rust/Cargo.toml` instead of being a | |
| # second number to bump. It gates that jinja function only; nothing about | |
| # the package it produces is experimental. | |
| # | |
| # The recipe compiles Rust now, with `${{ compiler('rust') }}` from | |
| # conda-forge -- no toolchain step is needed here, and none should be | |
| # added: the toolchain is a build dependency resolved from the channels | |
| # like any other, which is what makes the build reproducible from the | |
| # recipe alone. | |
| build-args: --experimental --channel conda-forge --channel https://prefix.dev/blooop --no-include-recipe | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: conda-packages | |
| path: output/ | |
| if-no-files-found: error | |
| publish: | |
| name: Publish to prefix.dev | |
| needs: [check, build] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: conda-packages | |
| path: output/ | |
| - name: Setup pixi | |
| uses: prefix-dev/setup-pixi@v0.10.2 | |
| with: | |
| run-install: false | |
| - name: Upload to prefix.dev | |
| env: | |
| PIXI_TOKEN: ${{ secrets.PIXI_TOKEN }} | |
| run: | | |
| for pkg in $(find output -type f \( -name "*.conda" -o -name "*.tar.bz2" \)); do | |
| echo "Uploading ${pkg}" | |
| if pixi exec rattler-build upload prefix \ | |
| -c blooop \ | |
| --api-key "$PIXI_TOKEN" \ | |
| "${pkg}"; then | |
| echo "Successfully uploaded ${pkg}" | |
| else | |
| echo "Failed to upload ${pkg} (may already exist)" | |
| fi | |
| done |