From ba7d5193d7f086420cf87ab9a9a7011fb3b7bbd9 Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Tue, 13 Feb 2024 20:07:03 +0100 Subject: [PATCH] Add rattler-build-action --- .github/CODEOWNERS | 1 + .github/dependabot.yml | 10 +++ .github/release.yml | 26 ++++++ .github/scripts/release.sh | 40 +++++++++ .github/workflows/bump-versions.yml | 36 ++++++++ .github/workflows/release.yml | 32 +++++++ .github/workflows/test.yml | 59 ++++++++++++ LICENSE | 28 ++++++ README.md | 135 +++++++++++++++++++++++++++- action.yml | 76 ++++++++++++++++ project.toml | 1 + test/polarify/recipe.yaml | 34 +++++++ test/test-package/recipe.yaml | 19 ++++ 13 files changed, 496 insertions(+), 1 deletion(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/release.yml create mode 100755 .github/scripts/release.sh create mode 100644 .github/workflows/bump-versions.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100644 LICENSE create mode 100644 action.yml create mode 100644 project.toml create mode 100644 test/polarify/recipe.yaml create mode 100644 test/test-package/recipe.yaml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..e4573f9 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @pavelzw diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7e316fc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + groups: + dependencies: + patterns: + - "*" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 0000000..eea87a7 --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,26 @@ +changelog: + exclude: + labels: + - ignore for release + categories: + - title: 💥 Breaking changes + labels: + - breaking + - title: ✨ New features + labels: + - enhancement + - title: 👷🏻 CI + labels: + - ci + - title: 🐛 Bug fixes + labels: + - bug + - title: 📝 Documentation + labels: + - documentation + - title: ⬆️ Dependency updates + labels: + - dependencies + - title: 🤷🏻 Other changes + labels: + - "*" diff --git a/.github/scripts/release.sh b/.github/scripts/release.sh new file mode 100755 index 0000000..5f4d37c --- /dev/null +++ b/.github/scripts/release.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +version="${TAG_NAME}" +major="${version%%.*}" # see https://linuxjournal.com/article/8919 for an explanation of this bash magic + +git tag -d "${major}" +local_delete=$? +if [ $local_delete -eq 0 ]; then + echo "Deleted local tag ${major}." + echo "Deleted local tag \`${major}\`." >> "$GITHUB_STEP_SUMMARY" +else + echo "No local tag ${major} to delete." + echo "No local tag \`${major}\` to delete." >> "$GITHUB_STEP_SUMMARY" +fi +git tag "${major}" + +git push -d origin "${major}" +remote_delete=$? +if [ $remote_delete -eq 0 ]; then + echo "Deleted remote tag ${major}." + echo "Deleted remote tag \`${major}\`." >> "$GITHUB_STEP_SUMMARY" +else + echo "No remote tag ${major} to delete." + echo "No remote tag \`${major}\` to delete." >> "$GITHUB_STEP_SUMMARY" +fi +git push origin "${major}" +push_worked=$? +if [ $push_worked -ne 0 ]; then + echo "Failed to push ${major} tag to remote." + echo "Failed to push \`${major}\` tag to remote." >> "$GITHUB_STEP_SUMMARY" + exit 1 +fi + +if [ $remote_delete -eq 0 ]; then + echo "Result: moved ${major} -> ${version} tag on remote." + echo "Result: moved \`${major}\` -> \`${version}\` tag on remote." >> "$GITHUB_STEP_SUMMARY" +else + echo "Result: created ${major} -> ${version} tag on remote." + echo "Result: created \`${major}\` -> \`${version}\` tag on remote." >> "$GITHUB_STEP_SUMMARY" +fi diff --git a/.github/workflows/bump-versions.yml b/.github/workflows/bump-versions.yml new file mode 100644 index 0000000..f1b0865 --- /dev/null +++ b/.github/workflows/bump-versions.yml @@ -0,0 +1,36 @@ +name: Bump versions +on: + workflow_dispatch: + schedule: + - cron: "0 6 * * *" + +jobs: + bump: + name: Bump ${{ matrix.tool }} + runs-on: ubuntu-latest + strategy: + matrix: + include: + - tool: rattler-build + repo: prefix-dev/rattler-build + steps: + - uses: actions/checkout@v4 + - name: Bump versions + id: bump + run: | + set -exuo pipefail + new_version="$(gh repo view --json latestRelease ${{ matrix.repo }} | jq -r '.latestRelease.tagName')" + echo "new-version=$new_version" >> "$GITHUB_OUTPUT" + yq -i ".inputs.${{ matrix.tool }}-version.default = \"$new_version\"" action.yml + env: + GH_TOKEN: ${{ github.token }} + - name: Create Pull Request + uses: peter-evans/create-pull-request@b1ddad2c994a25fbc81a28b3ec0e368bb2021c50 + with: + title: Bump ${{ matrix.tool }} to ${{ steps.bump.outputs.new-version }} + delete-branch: true + commit-message: Bump ${{ matrix.tool }} version to ${{ steps.bump.outputs.new-version }} + branch: bump-${{ matrix.tool }}-${{ steps.bump.outputs.new-version }} + labels: dependencies,breaking + body: | + - [ ] Update version in [project.toml](https://github.com/prefix-dev/rattler-build-action/blob/bump-${{ matrix.tool }}-${{ steps.bump.outputs.new-version }}/project.toml) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2db02e9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + push: + branches: main +permissions: + contents: write + +# To release a new version, update the version in `project.toml` and push to main. +# This will create a draft release with the changelog and push a 'vx' tag that points to the new release as well as 'vx.y.z'. + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: quantco/ui-actions/version-metadata@v1 + id: version-metadata + with: + file: ./project.toml + token: ${{ secrets.GITHUB_TOKEN }} + version-extraction-override: 'regex:version = "(.*)"' + - run: .github/scripts/release.sh + if: steps.version-metadata.outputs.changed == 'true' + env: + TAG_NAME: v${{ steps.version-metadata.outputs.newVersion }} + - name: Create release + if: steps.version-metadata.outputs.changed == 'true' + uses: softprops/action-gh-release@v1 + with: + generate_release_notes: true + tag_name: v${{ steps.version-metadata.outputs.newVersion }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b21fe6e --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,59 @@ +name: Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + bump: + name: Test action + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + recipe: + - polarify + - test-package + os: + - ubuntu-latest + - macos-latest + - macos-14 + - windows-latest + rattler-build-version: + - '' # default + - latest + steps: + - uses: actions/checkout@v4 + - uses: ./ + with: + recipe-path: test/${{ matrix.recipe }}/recipe.yaml + artifact-name: package-${{ matrix.recipe }}-${{ matrix.os }}-${{ matrix.rattler-build-version }} + + check-readme: + name: Check versions in README.md + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check rattler-build-action version mentions + run: | + project_version="$(yq '.version' project.toml)" + count_expected=5 + count_actual="$(grep -c "prefix-dev/rattler-build-action@v${project_version}" README.md || true)" + if [ "$count_actual" -ne "$count_expected" ]; then + echo "::error file=README.md::Expected $count_expected mentions of \`rattler-build-action@v$project_version\` in README.md, but found $count_actual." + exit 1 + fi + - name: Check rattler-build version mentions + run: | + rattler_build_version="$(yq '.inputs.rattler-build-version' action.yml)" + count_expected=1 + count_actual="$(grep -c "rattler-build-version" README.md | grep "$rattler_build_version" || true)" + if [ "$count_actual" -ne "$count_expected" ]; then + echo "::error file=README.md::Expected $count_expected mentions of \`rattler-build\` version \`0.6.0\` in README.md, but found $count_actual." + exit 1 + fi diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..91ee055 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, Pavel Zwerschke + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index f809565..0eeb2ec 100644 --- a/README.md +++ b/README.md @@ -1 +1,134 @@ -# rattler-build-action +# rattler-build-action 📦🐍 + +`rattler-build-action` is a GitHub Action for building conda packages using [rattler-build](https://github.com/prefix-dev/rattler-build). + +## Usage + +Create `.github/workflows/package.yml` in your repository. Here's a quick template: + +```yml +name: Package + +on: [push] + +jobs: + build: + name: Build package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build conda package + uses: prefix-dev/rattler-build-action@v0.1.0 +``` + +> [!WARNING] +> Since rattler-build is still experimental and the API can change in minor versions, please pin this action to its minor version, i.e., `prefix-dev/rattler-build-action@v0.1.0`. +> [!TIP] +> Please make sure to have private repositories enabled for dependabot, otherwise dependabot will fail silently for your repository. +> +> ```yml +> version: 2 +> updates: +> - package-ecosystem: github-actions +> directory: / +> schedule: +> interval: monthly +> groups: +> gh-actions: +> patterns: +> - "*" +> ``` + +This action will build the conda recipe in `conda.recipe/recipe.yaml` and upload the built packages as a build artifact. + +## Customizations + +- `rattler-build-version`: Define the version of rattler-build. Pins to the latest version that is available when releasing. +- `recipe-path`: Path to the rattler recipe. Defaults to `conda.recipe/recipe.yaml`. +- `upload-artifact`: Decide whether to upload the built packages as a build artifact. +- `build-args`: Additional arguments to pass to `rattler-build build`. +- `artifact-name`: Name of the artifact that is uploaded after build. If running `rattler-build-action` multiple times in a matrix, you need a distinct name for each workflow. + +### `rattler-build-version` + +`rattler-build-version` is strictly pinned to the latest version of `rattler-build` that is available when releasing a new version of `rattler-build-action`. +This is to ensure that no breakages occur if a new version of `rattler-build` is released. + +You can use dependabot to automatically update the version of `prefix-dev/rattler-build-action` which will also update the version of `rattler-build` to the latest version. + +## Examples + +### Build for multiple targets using matrix + +```yml +jobs: + build: + name: Build package + runs-on: ${{ matrix.os }} + strategy: + matrix: + include: + - os: ubuntu-latest + target-platform: linux-64 + - os: ubuntu-latest + target-platform: linux-aarch64 + - os: windows-latest + target-platform: win-64 + - os: macos-latest + target-platform: osx-64 + - os: macos-14 + target-platform: osx-arm64 + steps: + - uses: actions/checkout@v4 + - name: Build conda package + uses: prefix-dev/rattler-build-action@v0.1.0 + with: + # needs to be unique for each matrix entry + artifact-name: package-${{ matrix.target-platform }} + build-args: --target-platform ${{ matrix.target-platform }}${{ matrix.target-platform == 'linux-aarch64' && ' --no-test' || '' }} +``` + +### Upload to quetz + +```yml +jobs: + build: + name: Build package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Build conda package + uses: prefix-dev/rattler-build-action@v0.1.0 + - run: | + for pkg in $(find output -type f \( -name "*.conda" -o -name "*.tar.bz2" \) ); do + echo "Uploading ${pkg}" + rattler-build upload quetz "${pkg}" + done + env: + QUETZ_SERVER_URL: https://my.quetz.server + QUETZ_API_KEY: ${{ secrets.QUETZ_API_KEY }} + QUETZ_CHANNEL: my-channel +``` + +### Use private channel + +You can use a private channel while building your conda package by setting the `RATTLER_AUTH_FILE` environment variable. +As of now, `rattler-build` does not support a `login` command [prefix-dev/rattler-build#334](https://github.com/prefix-dev/rattler-build/issues/334), so you need to create the `RATTLER_AUTH_FILE` manually. + +```yml +jobs: + build: + name: Build package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Authenticate with private channel + run: | + RATTLER_AUTH_FILE=${{ runner.temp }}/credentials.json + echo '{"my.quetz.server": {"CondaToken": "${{ secrets.QUETZ_API_KEY }}"}}' > "$RATTLER_AUTH_FILE" + echo "RATTLER_AUTH_FILE=$RATTLER_AUTH_FILE" >> "$GITHUB_ENV" + - name: Build conda package + uses: prefix-dev/rattler-build-action@v0.1.0 + with: + build-args: -c conda-forge -c https://my.quetz.server/get/my-channel +``` diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..b95329f --- /dev/null +++ b/action.yml @@ -0,0 +1,76 @@ +name: rattler-build-action +description: GitHub action to build conda packages using rattler-build +inputs: + rattler-build-version: + description: Version of rattler-build to use + required: false + default: 'v0.9.0' + recipe-path: + description: Path to the recipe.yaml file + required: true + default: conda.recipe/recipe.yaml + upload-artifact: + description: whether to upload the built packages as a build artifact + required: false + default: 'true' + artifact-name: + description: Package name of the uploaded artifact + required: false + default: 'package' + build-args: + description: arguments to pass to rattler-build + required: false + default: '' +runs: + using: composite + steps: + - name: Generate rattler-build URL + shell: bash + id: url + run: | + arch=$(uname -m) + if [ "$arch" = "arm64" ]; then + arch="aarch64" + fi + platform=${{ runner.os == 'macOS' && 'apple-darwin' || '' }}${{ runner.os == 'Linux' && 'unknown-linux-musl' || '' }}${{ runner.os == 'Windows' && 'pc-windows-msvc' || '' }} + if [ ${{ inputs.rattler-build-version }} = "latest" ]; then + url="https://github.com/prefix-dev/rattler-build/releases/latest/download/rattler-build-$arch-$platform${{ runner.os == 'Windows' && '.exe' || '' }}" + else + url="https://github.com/prefix-dev/rattler-build/releases/download/${{ inputs.rattler-build-version }}/rattler-build-$arch-$platform${{ runner.os == 'Windows' && '.exe' || '' }}" + fi + echo "url=$url" >> $GITHUB_OUTPUT + - name: Install rattler-build (Unix) + shell: bash + if: ${{ runner.os != 'Windows' }} + run: | + mkdir -p ${{ runner.temp }}/rattler-build + curl -Ls \ + ${{ steps.url.outputs.url }} \ + -o ${{ runner.temp }}/rattler-build/rattler-build + chmod +x ${{ runner.temp }}/rattler-build/rattler-build + echo ${{ runner.temp }}/rattler-build >> $GITHUB_PATH + - name: Install rattler-build (Windows) + shell: powershell + if: ${{ runner.os == 'Windows' }} + run: | + New-Item -ItemType Directory -Path "${{ runner.temp }}\rattler-build" -Force + Invoke-WebRequest -Uri "${{ steps.url.outputs.url }}" -OutFile "${{ runner.temp }}\rattler-build\rattler-build.exe" + Add-Content -Path $env:GITHUB_PATH -Value "${{ runner.temp }}\rattler-build" + - name: Build conda package (non-Windows) + shell: bash + if: ${{ runner.os != 'Windows' }} + run: | + rattler-build build --recipe "${{ inputs.recipe-path }}" ${{ inputs.build-args }} + - name: Build conda package (Windows) + shell: powershell + if: ${{ runner.os == 'Windows' }} + run: | + rattler-build build --recipe "${{ inputs.recipe-path }}" ${{ inputs.build-args }} + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + if: ${{ inputs.upload-artifact == 'true' }} + with: + name: ${{ inputs.artifact-name }} + path: | + output/**/*.tar.bz2 + output/**/*.conda diff --git a/project.toml b/project.toml new file mode 100644 index 0000000..9f7a875 --- /dev/null +++ b/project.toml @@ -0,0 +1 @@ +version = "0.1.0" diff --git a/test/polarify/recipe.yaml b/test/polarify/recipe.yaml new file mode 100644 index 0000000..29e9f3e --- /dev/null +++ b/test/polarify/recipe.yaml @@ -0,0 +1,34 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json +context: + name: polarify + version: 0.1.3 + +source: + - url: https://github.com/quantco/polarify/archive/refs/tags/v${{ version }}.tar.gz + sha256: 93441164c23b764d72c8a66d14b11d5bbd353ed6112ccf3b35efda2a98f9df02 + +outputs: + - package: + name: ${{ name }} + version: ${{ version }} + build: + noarch: python + script: + - python -m pip install . --no-deps --ignore-installed -vv --no-build-isolation --disable-pip-version-check + requirements: + host: + - python >=3.9 + - pip + - hatchling + run: + - python >=3.9 + - polars >=0.14.24,<0.20 + tests: + - python: + imports: + - polarify + pip_check: true + +about: + homepage: https://github.com/quantco/polarify + license: BSD-3-Clause diff --git a/test/test-package/recipe.yaml b/test/test-package/recipe.yaml new file mode 100644 index 0000000..0903c43 --- /dev/null +++ b/test/test-package/recipe.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json + +context: + name: test-package + version: 0.1.0 + +outputs: + - package: + name: ${{ name }} + version: ${{ version }} + build: + noarch: python + script: + - echo "Hello, world!" + requirements: + host: + - python >=3.9 + run: + - python >=3.9