name: Publish on: push: tags: - 'v*' workflow_dispatch: inputs: job: description: 'Job to run (leave empty to run all)' required: false type: choice options: - '' - publish-pypi - publish-npm - publish-docker concurrency: group: publish cancel-in-progress: false jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Python tests run: | pip install -e ".[dev]" pytest pytest-asyncio pytest tests/ -v -m "not slow" - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 - name: JavaScript tests run: cd js && npm ci && npm run build && npm test validate-version: if: startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Check tag matches package versions run: | TAG="${GITHUB_REF_NAME#v}" PY=$(python -c 'import re; print(re.search(r"__version__\s*=\s*[\"'\'']([^\"'\'']+)", open("cloakbrowser/_version.py").read()).group(1))') JS=$(python -c 'import json; print(json.load(open("js/package.json"))["version"])') echo "Tag: $TAG | Python: $PY | npm: $JS" [ "$TAG" = "$PY" ] || { echo "ERROR: tag v$TAG != _version.py $PY"; exit 1; } [ "$TAG" = "$JS" ] || { echo "ERROR: tag v$TAG != package.json $JS"; exit 1; } publish-pypi: needs: [test, validate-version] if: always() && needs.test.result == 'success' && (needs.validate-version.result == 'success' || needs.validate-version.result == 'skipped') runs-on: ubuntu-latest permissions: id-token: write # OIDC trusted publishing — no PYPI_TOKEN needed steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Build run: | pip install build python -m build - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1 publish-npm: needs: [test, validate-version] if: always() && needs.test.result == 'success' && (needs.validate-version.result == 'success' || needs.validate-version.result == 'skipped') runs-on: ubuntu-latest permissions: id-token: write # OIDC trusted publishing + provenance — no NPM_TOKEN needed steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 # npm 11.11.0 native — no upgrade needed (Node 22.22.2 has broken npm) registry-url: 'https://registry.npmjs.org' - name: Build run: cd js && npm ci && npm run build - name: Publish to npm run: cd js && npm publish --provenance --access public publish-docker: needs: [test, validate-version] if: always() && needs.test.result == 'success' && (needs.validate-version.result == 'success' || needs.validate-version.result == 'skipped') runs-on: ubuntu-latest permissions: id-token: write # Cosign keyless signing + attestations contents: read attestations: write packages: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Extract version run: | VERSION=$(python -c 'import re; print(re.search(r"__version__\s*=\s*[\"'\'']([^\"'\'']+)", open("cloakbrowser/_version.py").read()).group(1))') echo "VERSION=$VERSION" >> $GITHUB_ENV - uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 - uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: username: ${{ secrets.DOCKER_USER }} password: ${{ secrets.DOCKER_PAT }} - name: Build and push id: build uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: | cloakhq/cloakbrowser:${{ env.VERSION }} cloakhq/cloakbrowser:latest provenance: true sbom: true - uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 - name: Sign image run: cosign sign --yes cloakhq/cloakbrowser@${{ steps.build.outputs.digest }} - name: Attest build provenance uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 with: subject-name: index.docker.io/cloakhq/cloakbrowser subject-digest: ${{ steps.build.outputs.digest }} push-to-registry: true