03. Release #105
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: 03. Release | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| target: | |
| description: 'Select where to publish' | |
| required: true | |
| type: choice | |
| default: 'testpypi' | |
| options: | |
| - none | |
| - testpypi | |
| - pypi | |
| - both | |
| build_sdist: | |
| description: 'Whether to build source distribution' | |
| required: false | |
| type: boolean | |
| default: true | |
| build_wheels: | |
| description: 'Whether to build wheel distribution' | |
| required: false | |
| type: boolean | |
| default: true | |
| os_json: | |
| description: 'JSON string of runner labels to build on (Manual only; ubuntu-24.04=x86_64, ubuntu-24.04-arm=aarch64, macos-14=arm64, macos-15-intel=x86_64, windows-latest=x86_64)' | |
| required: false | |
| type: string | |
| default: '["ubuntu-24.04", "ubuntu-24.04-arm", "macos-14", "macos-15-intel", "windows-latest"]' | |
| python_json: | |
| description: 'JSON string of Python versions (Manual only)' | |
| required: false | |
| type: string | |
| default: '["3.10"]' | |
| permissions: | |
| contents: write | |
| id-token: write | |
| actions: read | |
| jobs: | |
| build: | |
| # Skip this workflow for CLI releases (tags starting with cli-) | |
| if: "!startsWith(github.event.release.tag_name, 'cli-')" | |
| uses: ./.github/workflows/_build.yml | |
| with: | |
| os_json: ${{ inputs.os_json || '["ubuntu-24.04", "ubuntu-24.04-arm", "macos-14", "macos-15-intel", "windows-latest"]' }} | |
| python_json: ${{ inputs.python_json || '["3.10"]' }} | |
| build_sdist: ${{ github.event_name == 'release' || inputs.build_sdist != false }} | |
| build_wheels: ${{ github.event_name == 'release' || inputs.build_wheels != false }} | |
| permission-check: | |
| name: Check write permission | |
| needs: [build] | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| outputs: | |
| allowed: ${{ steps.check.outputs.allowed }} | |
| steps: | |
| - name: Verify actor permission | |
| id: check | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| // Only check permission for manual dispatch | |
| if (context.eventName !== 'workflow_dispatch') { | |
| core.setOutput('allowed', 'true'); | |
| return; | |
| } | |
| const { owner, repo } = context.repo; | |
| const actor = context.actor; | |
| const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ | |
| owner, | |
| repo, | |
| username: actor, | |
| }); | |
| const perm = data.permission; | |
| core.info(`Actor ${actor} permission: ${perm}`); | |
| const allowed = ['admin', 'maintain', 'write'].includes(perm); | |
| core.setOutput('allowed', allowed ? 'true' : 'false'); | |
| if (!allowed) { | |
| core.setFailed(`User ${actor} does not have write permission`); | |
| } | |
| publish-testpypi: | |
| name: Publish to TestPyPI | |
| needs: [build, permission-check] | |
| if: >- | |
| needs.permission-check.outputs.allowed == 'true' && | |
| (inputs.target == 'testpypi' || inputs.target == 'both') | |
| runs-on: ubuntu-24.04 | |
| environment: | |
| name: testpypi | |
| url: https://test.pypi.org/p/openviking | |
| permissions: | |
| id-token: write | |
| actions: read | |
| steps: | |
| - name: Download all the dists (Same Run) | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: python-package-distributions-* | |
| path: dist/ | |
| merge-multiple: true | |
| - name: Publish distribution to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| skip-existing: true | |
| verbose: true | |
| - name: Display published version | |
| run: | | |
| # Get version from the first wheel file found | |
| VERSION=$(ls dist/*.whl | head -n 1 | xargs basename | cut -d- -f2) | |
| echo "Published to TestPyPI (or already existed) with version: $VERSION" | |
| echo "::notice::Published to TestPyPI (or already existed) with version: $VERSION" | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: [build, permission-check] | |
| if: >- | |
| needs.permission-check.outputs.allowed == 'true' && | |
| (github.event_name == 'release' || inputs.target == 'pypi' || inputs.target == 'both') | |
| runs-on: ubuntu-24.04 | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/openviking | |
| permissions: | |
| id-token: write | |
| actions: read | |
| steps: | |
| - name: Download all the dists (Same Run) | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: python-package-distributions-* | |
| path: dist/ | |
| merge-multiple: true | |
| - name: Publish distribution to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| skip-existing: true | |
| verbose: true | |
| - name: Display published version | |
| run: | | |
| # Get version from the first wheel file found | |
| VERSION=$(ls dist/*.whl | head -n 1 | xargs basename | cut -d- -f2) | |
| echo "Published to PyPI (or already existed) with version: $VERSION" | |
| echo "::notice::Published to PyPI (or already existed) with version: $VERSION" | |
| docker: | |
| name: Build and Push Docker Image (${{ matrix.arch }}) | |
| needs: [build, permission-check] | |
| if: >- | |
| needs.permission-check.outputs.allowed == 'true' && | |
| github.event_name == 'release' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| platform: linux/amd64 | |
| runner: ubuntu-24.04 | |
| - arch: arm64 | |
| platform: linux/arm64 | |
| runner: ubuntu-24.04-arm | |
| runs-on: ${{ matrix.runner }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| attestations: write | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| - name: Normalize image name | |
| id: image-name | |
| env: | |
| RAW_IMAGE_NAME: ${{ github.repository }} | |
| run: | | |
| echo "image=$(echo "$RAW_IMAGE_NAME" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Docker | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ghcr.io/${{ steps.image-name.outputs.image }} | |
| tags: | | |
| type=raw,value=${{ github.event.release.tag_name }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Build and push Docker image | |
| id: push | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| platforms: ${{ matrix.platform }} | |
| outputs: type=image,name=ghcr.io/${{ steps.image-name.outputs.image }},push-by-digest=true,name-canonical=true,push=true | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| OPENVIKING_VERSION=${{ github.event.release.tag_name }} | |
| - name: Export image digest | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.push.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Upload image digest | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: docker-digests-${{ matrix.arch }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| docker-manifest: | |
| name: Publish Docker Manifest | |
| needs: [docker, permission-check] | |
| if: >- | |
| needs.permission-check.outputs.allowed == 'true' && | |
| github.event_name == 'release' | |
| runs-on: ubuntu-24.04 | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| submodules: recursive | |
| - name: Normalize image name | |
| id: image-name | |
| env: | |
| RAW_IMAGE_NAME: ${{ github.repository }} | |
| run: | | |
| echo "image=$(echo "$RAW_IMAGE_NAME" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT" | |
| - name: Log in to the Container registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata (tags, labels) for Docker | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ghcr.io/${{ steps.image-name.outputs.image }} | |
| tags: | | |
| type=raw,value=${{ github.event.release.tag_name }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Download image digests | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: docker-digests-* | |
| path: /tmp/digests | |
| merge-multiple: true | |
| - name: Create multi-arch manifests | |
| env: | |
| SOURCE_TAGS: ${{ steps.meta.outputs.tags }} | |
| run: | | |
| image_refs=() | |
| for digest_file in /tmp/digests/*; do | |
| [ -e "$digest_file" ] || continue | |
| image_refs+=("ghcr.io/${{ steps.image-name.outputs.image }}@sha256:$(basename "$digest_file")") | |
| done | |
| [ ${#image_refs[@]} -gt 0 ] || { | |
| echo "No image digests found" >&2 | |
| exit 1 | |
| } | |
| while IFS= read -r tag; do | |
| [ -n "$tag" ] || continue | |
| docker buildx imagetools create \ | |
| --tag "$tag" \ | |
| "${image_refs[@]}" | |
| done <<< "$SOURCE_TAGS" |