weblate/.github/workflows/setup.yml
Michal Čihař f8ca155e09 fix(ci): separate attestation from SBOM generation
This makes the privileges separation more tight.
2026-05-07 19:03:56 +02:00

317 lines
10 KiB
YAML
Vendored

# Copyright © Michal Čihař <michal@weblate.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later
name: Distribution
on:
push:
branches-ignore:
- renovate/**
- weblate
- dependabot/**
tags:
- weblate-*
pull_request:
permissions:
contents: read
jobs:
dist:
runs-on: ubuntu-24.04
name: Build packages
env:
PYTHONUNBUFFERED: 1
PYTHONWARNINGS: default,ignore:unclosed:ResourceWarning
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: false
version: 0.11.11
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: build
run: |
echo "::add-matcher::.github/matchers/setuptools.json"
uv build
echo "::remove-matcher owner=setuptools::"
rm -f dist/.gitignore
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
path: dist/*
name: dist
sign_dist:
runs-on: ubuntu-24.04
name: Sign packages
if: github.event_name == 'push'
needs:
- dist
permissions:
contents: read
# Needed for Sigstore
id-token: write
# Needed for attestations
attestations: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@04cffa1d795717b140764e8b640de88853c92acc # v3.3.0
with:
inputs: dist/*
- name: Attest
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: dist/*
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
path: dist/*
name: dist-signed
sbom:
runs-on: ubuntu-24.04
name: Build SBOM
needs:
- dist
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: false
version: 0.11.11
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: Generate SBOM
id: generate-sbom
env:
# renovate: datasource=github-releases depName=CycloneDX/cyclonedx-cli versioning=loose
CYCLONEDX_CLI_VERSION: v0.31.0
run: |
version=$(sed -n '/^VERSION =/ s/.*"\(.*\)"/\1/p' weblate/utils/version.py)
version=${version%-dev}
version=${version%-rc}
sbom="build/sbom/weblate-$version-sbom.cdx.json"
mkdir -p build/sbom
uv export --preview-features sbom-export --format cyclonedx1.5 --all-extras --no-dev > build/sbom/python.json
./scripts/reproducible-sbom.py build/sbom/python.json
cd client
yarn install --check-files
npm sbom --omit dev --sbom-format cyclonedx --sbom-type application > ../build/sbom/javascript.json
cd ..
./scripts/reproducible-sbom.py build/sbom/javascript.json
curl -L "https://github.com/CycloneDX/cyclonedx-cli/releases/download/$CYCLONEDX_CLI_VERSION/cyclonedx-linux-x64" > /tmp/cyclonedx-linux-x64
chmod +x /tmp/cyclonedx-linux-x64
/tmp/cyclonedx-linux-x64 merge --input-files build/sbom/*.json --output-file "$sbom"
./scripts/reproducible-sbom.py "$sbom"
echo "path=$sbom" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
path: ${{ steps.generate-sbom.outputs.path }}
name: sbom
attest_sbom:
runs-on: ubuntu-24.04
name: Attest SBOM
if: github.event_name == 'push'
needs:
- sign_dist
- sbom
permissions:
# Needed for attestations
attestations: write
contents: read
id-token: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist-signed
path: dist
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sbom
path: sbom
- name: Locate SBOM
id: locate-sbom
run: |
path=$(find sbom -type f -name '*-sbom.cdx.json' -print -quit)
test -n "$path"
echo "path=$path" >> "$GITHUB_OUTPUT"
- uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-path: dist/*
sbom-path: ${{ steps.locate-sbom.outputs.path }}
lint:
runs-on: ubuntu-24.04
name: Lint packages
env:
PYTHONUNBUFFERED: 1
needs:
- dist
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Install apt dependencies
run: sudo ./ci/apt-install
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: false
version: 0.11.11
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist
- name: Cleanup dist
# Remove files not supported on PyPI (eg. Sigstore signatures)
run: find dist -mindepth 1 -not -name '*.tar.gz' -not -name '*.whl' -delete
- name: list wheel
run: unzip -l dist/*.whl
- name: list sdist
run: tar tvf dist/*.tar.gz
- name: twine check
run: uvx twine check --strict dist/*
- name: pydistcheck
run: uvx pydistcheck --inspect dist/*
- name: pyroma
run: uvx --with pip pyroma dist/*.tar.gz
- name: check-wheel-contents
run: uvx check-wheel-contents dist/*.whl
- name: check-manifest
run: uvx --with pip check-manifest -v
- name: install
run: |
uv venv .venv-install
source .venv-install/bin/activate
uv pip install dist/*.whl
notes:
runs-on: ubuntu-24.04
name: Build release notes
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: false
version: 0.11.11
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.14'
- name: Install apt dependencies
run: |
sudo apt update
sudo apt install -y graphviz pandoc
- name: Install Python dependencies
run: uv sync --only-group docs --frozen
- name: Sphinx build
run: |
. .venv/bin/activate
./ci/run-docs
- name: Convert release notes
run: |
version=$(sed -n '/^VERSION =/ s/.*"\(.*\)"/\1/p' weblate/utils/version.py)
namever="weblate-$version"
sed "s/latest/$namever/" < scripts/release-notes-filter.lua > scripts/release-notes-filter.version.lua
mkdir dist
./scripts/extract-release-notes.py > "dist/Weblate-$version.html"
pandoc "dist/Weblate-$version.html" --write=gfm --wrap=none --lua-filter=scripts/release-notes-filter.version.lua -o "dist/Weblate-$version.md"
rm scripts/release-notes-filter.version.lua
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
path: dist/*
name: notes
publish_pypi:
name: Publish to PyPI
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/weblate')
environment: pypi
permissions:
# this permission is mandatory for trusted publishing
id-token: write
needs:
- notes
- sign_dist
- attest_sbom
- lint
runs-on: ubuntu-24.04
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist-signed
path: dist
- name: Cleanup dist
# Remove files not supported on PyPI (eg. Sigstore signatures)
run: find dist -mindepth 1 -not -name '*.tar.gz' -not -name '*.whl' -delete
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with:
enable-cache: false
version: 0.11.11
- run: uv publish --trusted-publishing always
publish_github:
name: Publish to GitHub
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/weblate')
permissions:
# this permission is mandatory for creating a release
contents: write
needs:
- notes
- sign_dist
- attest_sbom
- lint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist-signed
path: dist
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sbom
path: sbom
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: notes
path: notes
- name: Extract notes body
run: tail -n+3 notes/Weblate-*.md > notes.md
- name: Extract notes title
id: get-name
run: echo "name=$(head -n1 notes/Weblate-*.md)" > "$GITHUB_OUTPUT"
- env:
VERSION: ${{ github.ref_name }}
GH_TOKEN: ${{ github.token }}
TITLE: ${{ steps.get-name.outputs.name }}
run: |
gh release create "$VERSION" --verify-tag --title "$TITLE" --notes-file notes.md dist/* sbom/*