mirror of
https://github.com/WeblateOrg/weblate.git
synced 2026-07-27 22:09:03 +08:00
416 lines
14 KiB
YAML
Vendored
416 lines
14 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:
|
|
- name: Checkout first source tree
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
path: source/first
|
|
- name: Checkout second source tree
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
path: source/second
|
|
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
with:
|
|
enable-cache: false
|
|
version: 0.11.32
|
|
- name: Setup Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: '3.14'
|
|
- name: Set reproducible build timestamp
|
|
run: echo "SOURCE_DATE_EPOCH=$(git -C source/first log -1 --pretty=%ct)" >> "$GITHUB_ENV"
|
|
- name: build
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
echo "::add-matcher::source/first/.github/matchers/setuptools.json"
|
|
uv build source/first --out-dir dist/first --no-create-gitignore
|
|
uv build source/second --out-dir dist/second --no-create-gitignore
|
|
echo "::remove-matcher owner=setuptools::"
|
|
python source/first/scripts/normalize-dist-archives.py dist/first/*.tar.gz dist/first/*.whl
|
|
python source/first/scripts/normalize-dist-archives.py dist/second/*.tar.gz dist/second/*.whl
|
|
|
|
report_artifact_difference() {
|
|
filename=$1
|
|
first=dist/first/"$filename"
|
|
second=dist/second/"$filename"
|
|
|
|
echo "::group::sha256sum $filename"
|
|
sha256sum "$first" "$second"
|
|
echo "::endgroup::"
|
|
|
|
case "$filename" in
|
|
*.tar.gz)
|
|
echo "::group::tar metadata diff $filename"
|
|
diff -u \
|
|
<(tar --numeric-owner --full-time -tvf "$first") \
|
|
<(tar --numeric-owner --full-time -tvf "$second") || true
|
|
echo "::endgroup::"
|
|
;;
|
|
*.whl)
|
|
echo "::group::zip metadata diff $filename"
|
|
diff -u \
|
|
<(zipinfo -l "$first") \
|
|
<(zipinfo -l "$second") || true
|
|
echo "::endgroup::"
|
|
;;
|
|
esac
|
|
|
|
extracted=$(mktemp -d)
|
|
mkdir "$extracted/first" "$extracted/second"
|
|
case "$filename" in
|
|
*.tar.gz)
|
|
tar -xf "$first" -C "$extracted/first"
|
|
tar -xf "$second" -C "$extracted/second"
|
|
;;
|
|
*.whl)
|
|
unzip -q "$first" -d "$extracted/first"
|
|
unzip -q "$second" -d "$extracted/second"
|
|
;;
|
|
esac
|
|
echo "::group::extracted content diff $filename"
|
|
diff -qr "$extracted/first" "$extracted/second" || true
|
|
echo "::endgroup::"
|
|
rm -rf "$extracted"
|
|
}
|
|
|
|
if ! diff -u \
|
|
<(cd dist/first && find . -maxdepth 1 -type f -printf '%f\n' | sort) \
|
|
<(cd dist/second && find . -maxdepth 1 -type f -printf '%f\n' | sort); then
|
|
echo "::error::Package artifact names differ between builds"
|
|
sha256sum dist/first/* dist/second/*
|
|
exit 1
|
|
fi
|
|
|
|
for artifact in dist/first/*; do
|
|
filename=${artifact##*/}
|
|
if ! cmp --silent "$artifact" "dist/second/$filename"; then
|
|
echo "::error file=$filename::Normalized package artifact differs between builds"
|
|
report_artifact_difference "$filename"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
rm -rf dist/second
|
|
mv dist/first/* dist/
|
|
rmdir dist/first
|
|
- 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@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: '3.14'
|
|
- name: Sign the dists with Sigstore
|
|
uses: sigstore/gh-action-sigstore-python@5b79a39c381910c090341a2c9b0bf022c8b387e1 # v3.4.0
|
|
with:
|
|
inputs: dist/*
|
|
- name: Attest
|
|
uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
|
|
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
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
with:
|
|
enable-cache: false
|
|
version: 0.11.32
|
|
- name: Setup Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
id: setup_python
|
|
with:
|
|
python-version: '3.14'
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: 26
|
|
package-manager-cache: false
|
|
- name: Set reproducible SBOM timestamp
|
|
run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> "$GITHUB_ENV"
|
|
- name: Generate SBOM
|
|
id: generate-sbom
|
|
env:
|
|
PYTHON_VERSION: ${{ steps.setup_python.outputs.python-version }}
|
|
# renovate-cyclonedx-cli
|
|
CYCLONEDX_CLI_VERSION: v0.33.1
|
|
CYCLONEDX_CLI_DIGEST: sha256:bfc8b2538da86fe239bc53658bbb63c1c8c510a293c1e6891aa5bea5d3c58746
|
|
run: |
|
|
version=$(uv run --frozen --only-group sbom ./scripts/show-version.py)
|
|
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 \
|
|
--locked \
|
|
--python "$PYTHON_VERSION" \
|
|
--no-python-downloads \
|
|
> build/sbom/python.json
|
|
cd client
|
|
yarn install --immutable --check-files
|
|
npm sbom --omit dev --sbom-format cyclonedx --sbom-type application > ../build/sbom/javascript.json
|
|
cd ..
|
|
cyclonedx_cli=/tmp/cyclonedx-linux-x64
|
|
curl -fsSL "https://github.com/CycloneDX/cyclonedx-cli/releases/download/$CYCLONEDX_CLI_VERSION/cyclonedx-linux-x64" > "$cyclonedx_cli"
|
|
echo "${CYCLONEDX_CLI_DIGEST#sha256:} $cyclonedx_cli" | sha256sum --check --strict
|
|
chmod +x "$cyclonedx_cli"
|
|
"$cyclonedx_cli" 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@f7c74d28b9d84cb8768d0b8ca14a4bac6ef463e6 # v4.2.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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- name: Install apt dependencies
|
|
run: sudo ./ci/apt-install
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
with:
|
|
python-version: '3.14'
|
|
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
with:
|
|
enable-cache: false
|
|
version: 0.11.32
|
|
- 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: uv run --frozen --only-group package-lint twine check --strict dist/*
|
|
- name: pydistcheck
|
|
run: uv run --frozen --only-group package-lint pydistcheck --inspect dist/*
|
|
- name: pyroma
|
|
run: uv run --frozen --only-group package-lint pyroma dist/*.tar.gz
|
|
- name: check-manifest
|
|
run: uv run --frozen --only-group package-lint check-manifest -v
|
|
- name: Prepare sdist tree for wheel comparison
|
|
id: wheel-source
|
|
run: |
|
|
source_dir=$(mktemp -d)
|
|
tar --strip-components=1 -xf dist/*.tar.gz -C "$source_dir"
|
|
echo "path=$source_dir" >> "$GITHUB_OUTPUT"
|
|
- name: Build source catalogs
|
|
working-directory: ${{ steps.wheel-source.outputs.path }}
|
|
# Wheels ship generated .mo files, while Git and sdist ship .po sources.
|
|
# Compile catalogs before comparing the wheel against the sdist tree.
|
|
run: uv run --frozen --project "$GITHUB_WORKSPACE" --only-group package-build python setup.py build_mo
|
|
- name: check-wheel-contents
|
|
working-directory: ${{ steps.wheel-source.outputs.path }}
|
|
run: uv run --frozen --project "$GITHUB_WORKSPACE" --only-group package-lint check-wheel-contents "$GITHUB_WORKSPACE"/dist/*.whl
|
|
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
with:
|
|
enable-cache: false
|
|
version: 0.11.32
|
|
- name: Set up Python
|
|
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
with:
|
|
enable-cache: false
|
|
version: 0.11.32
|
|
- 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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
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/*
|