diff --git a/.github/actions/docker-build/action.yml b/.github/actions/docker-build/action.yml index 087e7a9..cc7da2b 100644 --- a/.github/actions/docker-build/action.yml +++ b/.github/actions/docker-build/action.yml @@ -39,6 +39,10 @@ inputs: description: "Cache destination for Docker build" required: false default: "" + setup-qemu: + description: "Whether to set up QEMU for multi-platform builds" + required: false + default: "false" runs: using: "composite" @@ -46,6 +50,12 @@ runs: - name: Checkout repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - name: Set up QEMU + if: inputs.setup-qemu == 'true' + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + with: + platforms: ${{ inputs.platforms }} + - name: Set up Docker Buildx uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 diff --git a/.github/actions/python-package-build/action.yml b/.github/actions/python-package-build/action.yml new file mode 100644 index 0000000..c37ae14 --- /dev/null +++ b/.github/actions/python-package-build/action.yml @@ -0,0 +1,72 @@ +name: "Python Package Build" +description: "Build Python package with uv, test it, and optionally publish" +author: "Elements Interactive" + +inputs: + test-package: + description: "Whether to test the package (both library and CLI)" + required: false + default: "false" + cli-test-command: + description: "CLI command to test" + required: false + default: "" + publish: + description: "Whether to publish the package" + required: false + default: "false" + uv-version: + description: "Version of uv to use" + required: false + default: "latest" + +outputs: + wheel-file: + description: "Path to the built wheel file" + value: ${{ steps.build.outputs.wheel-file }} + dist-path: + description: "Path to the dist directory" + value: ${{ steps.build.outputs.dist-path }} + +runs: + using: "composite" + steps: + - name: Install uv + uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0 + with: + version: ${{ inputs.uv-version }} + + - name: Build the package + id: build + shell: bash + run: | + uv build + + # Set outputs + WHEEL_FILE=$(ls dist/*.whl | head -1) + echo "wheel-file=$WHEEL_FILE" >> $GITHUB_OUTPUT + echo "dist-path=dist" >> $GITHUB_OUTPUT + echo "Built wheel: $WHEEL_FILE" + + - name: Install the built wheel + shell: bash + run: | + uv venv + uv pip install "${{ steps.build.outputs.wheel-file }}" + + - name: Test package as CLI tool + if: inputs.test-package == 'true' + shell: bash + run: | + uv pip install "${{ steps.build.outputs.wheel-file }}[cli]" + uv run ${{ inputs.cli-test-command }} + + - name: Publish package + if: inputs.publish == 'true' + shell: bash + run: | + uv publish + +branding: + icon: "package" + color: "blue" diff --git a/.github/workflows/build-test-docker.yml b/.github/workflows/build-test-docker.yml new file mode 100644 index 0000000..e6cada4 --- /dev/null +++ b/.github/workflows/build-test-docker.yml @@ -0,0 +1,77 @@ +name: Build and Test Docker Image + +on: + pull_request: + branches: ["main"] + workflow_dispatch: + +jobs: + should-test-docker-build: + permissions: + contents: read + pull-requests: read + name: Check if should `test_docker_build` run + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Check if Dockerfile changed + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: docker-changes + with: + filters: | + docker: + - 'Dockerfile' + - '.dockerignore' + workflow: + - ./.github/actions/docker-build/action.yml + outputs: + docker: ${{ steps.docker-changes.outputs.docker }} + workflow: ${{ steps.docker-changes.outputs.workflow }} + + test-docker-build: + needs: [should-test-docker-build] + name: Test Docker build ${{ matrix.arch }} + runs-on: ubuntu-latest + if: (needs.should-test-docker-build.outputs.workflow == 'true' || needs.should-test-docker-build.outputs.docker == 'true') + permissions: + contents: read + packages: read + strategy: + matrix: + include: + - arch: amd64 + platform: linux/amd64 + image-name: build-amd64 + needs-qemu: false + - arch: arm64 + platform: linux/arm64 + image-name: build-arm64 + needs-qemu: true + steps: + - name: Check out the repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build image + uses: ./.github/actions/docker-build + with: + context: . + file: ./Dockerfile + push: false + load: true + platforms: ${{ matrix.platform }} + cache-from: type=registry,ref=ghcr.io/elementsinteractive/twyn:buildcache-${{ matrix.arch }} + image-name: ${{ matrix.image-name }} + setup-qemu: ${{ matrix.needs-qemu }} + + - name: Test + run: | + docker run --platform ${{ matrix.platform }} --rm ${{ matrix.image-name }}:pr-${{ github.event.pull_request.number }} --version diff --git a/.github/workflows/build-test-python.yml b/.github/workflows/build-test-python.yml new file mode 100644 index 0000000..125424e --- /dev/null +++ b/.github/workflows/build-test-python.yml @@ -0,0 +1,26 @@ +# This workflow builds the package, installs it, and tests basic functionality + +name: Build and Test Python Package + +on: + pull_request: + branches: ["main"] + workflow_dispatch: + +jobs: + test-build-package: + name: Test package build + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Check out the repo + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Test build package + uses: ./.github/actions/python-package-build + with: + test-package: "true" + uv-version: "0.8.22" + cli-test-command: "twyn --version" diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml deleted file mode 100644 index 4296321..0000000 --- a/.github/workflows/build-test.yml +++ /dev/null @@ -1,122 +0,0 @@ -# This workflow builds the package, installs it, and tests basic functionality - -name: Build and Test Package - -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] - -jobs: - test-build-package: - name: Test package build - runs-on: ubuntu-latest - if: "!startsWith(github.event.head_commit.message, 'bump:')" - - steps: - - name: Check out the repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Install uv - uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0 - - - name: Build the package - run: uv build - - - name: List built artifacts - run: ls -la dist/ - - - name: Install the built wheel - run: | - uv venv - # Find the wheel file and install it - WHEEL_FILE=$(ls dist/*.whl | head -1) - echo "Installing wheel: $WHEEL_FILE" - uv pip install "$WHEEL_FILE" - - - name: Test twyn as a library - run: uv run python -c "import twyn; twyn.check_dependencies" - - - name: Test twyn as a cli tool - run: | - WHEEL_FILE=$(ls dist/*.whl | head -1) - echo "Installing wheel: `$WHEEL_FILE` with `cli` extra." - uv pip install "${WHEEL_FILE}[cli]" - # Test that the CLI is available and --version works - uv run twyn --version - - should-test-docker-build: - permissions: - contents: read - pull-requests: read - name: Check if should `test_docker_build` run - runs-on: ubuntu-latest - if: "!startsWith(github.event.head_commit.message, 'bump:')" - steps: - - name: Check out the repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Check if Dockerfile changed - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: docker-changes - with: - filters: | - docker: - - 'Dockerfile' - - '.dockerignore' - workflow: - - ./.github/actions/docker-build/action.yml - outputs: - docker: ${{ steps.docker-changes.outputs.docker }} - workflow: ${{ steps.docker-changes.outputs.workflow }} - - test-docker-build: - needs: [should-test-docker-build] - name: Test Docker build ${{ matrix.arch }} - runs-on: ubuntu-latest - if: (needs.should-test-docker-build.outputs.workflow == 'true' || needs.should-test-docker-build.outputs.docker == 'true') && !startsWith(github.event.head_commit.message, 'bump:') - permissions: - contents: read - packages: read - strategy: - matrix: - include: - - arch: amd64 - platform: linux/amd64 - image-name: build-amd64 - - arch: arm64 - platform: linux/arm64 - image-name: build-arm64 - needs-qemu: true - steps: - - name: Check out the repo - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - if: matrix.needs-qemu - uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - with: - platforms: ${{ matrix.platform }} - - - name: Build image - uses: ./.github/actions/docker-build - with: - context: . - file: ./Dockerfile - push: false - load: true - platforms: ${{ matrix.platform }} - cache-from: type=registry,ref=ghcr.io/elementsinteractive/twyn:buildcache-${{ matrix.arch }} - image-name: ${{ matrix.image-name }} - - - name: Test - run: | - docker run --platform ${{ matrix.platform }} --rm ${{ matrix.image-name }}:pr-${{ github.event.pull_request.number }} --version diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f86db8f..7c45050 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,25 +7,35 @@ on: tags: - "v*.*.*" -permissions: - contents: write +env: + UV_VERSION: "0.8.22" jobs: - push_to_pypi: + build_and_test_package: + name: Build and test package runs-on: ubuntu-latest permissions: - id-token: write contents: read steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - name: Install uv - uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0 - - name: Publish to pypi - run: | - uv build - uv publish + + - name: Build and test package + id: build-test + uses: ./.github/actions/python-package-build + with: + test-package: "true" + publish: "false" + uv-version: ${{ env.UV_VERSION }} + cli-test-command: "twyn --version" + + - name: Upload package artifacts + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: python-package-${{ github.sha }} + path: ${{ steps.build-test.outputs.dist-path }} + retention-days: 1 build_and_test_docker: name: Build and test Docker image (${{ matrix.arch }}) @@ -39,6 +49,7 @@ jobs: - arch: amd64 platform: linux/amd64 cache-ref: buildcache-amd64 + needs-qemu: false - arch: arm64 platform: linux/arm64 cache-ref: buildcache-arm64 @@ -54,12 +65,6 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up QEMU - if: matrix.needs-qemu - uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - with: - platforms: ${{ matrix.arch }} - - name: Build Docker image (${{ matrix.arch }}) uses: ./.github/actions/docker-build with: @@ -71,15 +76,43 @@ jobs: image-name: elementsinteractive/twyn cache-from: type=registry,ref=ghcr.io/elementsinteractive/twyn:${{ matrix.cache-ref }} cache-to: type=registry,ref=ghcr.io/elementsinteractive/twyn:${{ matrix.cache-ref }},mode=max,compression=zstd,force-compression=true,oci-mediatypes=true + setup-qemu: ${{ matrix.needs-qemu }} - name: Test Docker image (${{ matrix.arch }}) run: | docker run --platform ${{ matrix.platform }} --rm elementsinteractive/twyn --version - publish_docker_images: + publish_to_pypi: + name: Publish to PyPI + runs-on: ubuntu-latest + needs: [build_and_test_package, build_and_test_docker] + + permissions: + id-token: write + contents: read + + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Download package artifacts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: python-package-${{ github.sha }} + path: dist + + - name: Install uv + uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0 + with: + version: ${{ env.UV_VERSION }} + + - name: Publish package + run: | + uv publish + + publish_to_dockerhub: name: Push Docker images to registries runs-on: ubuntu-latest - needs: [build_and_test_docker] + needs: [build_and_test_docker, build_and_test_package] permissions: contents: read packages: write @@ -128,7 +161,9 @@ jobs: release_notes: runs-on: ubuntu-latest - needs: [push_to_pypi, publish_docker_images] + needs: [publish_to_pypi, publish_to_dockerhub] + permissions: + contents: write steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Release