Spaces:
Running
Running
| # Sprint A9 (M-5 + M-6) β pipeline de release. | |
| # | |
| # DΓ©clenchΓ© sur push d'un tag ``v*.*.*`` (ex : ``v1.1.0``, | |
| # ``v1.2.0-rc1``). Comportement : | |
| # | |
| # 1. Build sdist + wheel via ``python -m build`` (setuptools_scm | |
| # dΓ©rive automatiquement la version du tag). | |
| # 2. Validation ``twine check`` (taille, README rendu, mΓ©tadonnΓ©es). | |
| # 3. Smoke test : install dans un container vierge + ``picarones demo``. | |
| # 4. Publication TestPyPI (validation finale avant prod). | |
| # 5. Smoke test depuis TestPyPI (``pip install --index-url testpypi``). | |
| # 6. Publication PyPI via Trusted Publisher (OIDC, sans token). | |
| # 7. Build image Docker multi-arch (linux/amd64 + linux/arm64). | |
| # 8. Push ghcr.io/maribakulj/picarones:<version> + :latest. | |
| # 9. CrΓ©ation GitHub Release avec corps gΓ©nΓ©rΓ© depuis CHANGELOG. | |
| # | |
| # PrΓ©requis (Γ configurer une fois cΓ΄tΓ© GitHub) : | |
| # - PyPI Trusted Publisher : ajouter ce workflow dans | |
| # https://pypi.org/manage/account/publishing/ | |
| # - GHCR_TOKEN n'est pas requis : ``GITHUB_TOKEN`` natif suffit | |
| # avec ``packages: write`` (cf. permissions du job docker). | |
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Tag Γ releaser (ex: v1.1.0). Manuel uniquement." | |
| required: true | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 1 β Build & validate distribution Python | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| build: | |
| name: Build & validate | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| steps: | |
| - name: Checkout (full history for setuptools_scm) | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # tags + history requis par setuptools_scm | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| cache: pip | |
| - name: Install build tools | |
| run: python -m pip install --upgrade build twine setuptools_scm | |
| - name: Detect version from tag | |
| id: version | |
| run: | | |
| VERSION=$(python -m setuptools_scm) | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "Version dΓ©tectΓ©e : ${VERSION}" | |
| - name: Build sdist + wheel | |
| run: python -m build | |
| - name: Twine validate | |
| run: twine check dist/* | |
| - name: Smoke test wheel install | |
| run: | | |
| python -m venv /tmp/smoke | |
| /tmp/smoke/bin/pip install dist/*.whl | |
| /tmp/smoke/bin/picarones --version | |
| - name: Upload artefacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: dist-${{ steps.version.outputs.version }} | |
| path: dist/ | |
| retention-days: 30 | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 2 β Publication TestPyPI (validation finale) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| publish-testpypi: | |
| name: Publish to TestPyPI | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: testpypi | |
| url: https://test.pypi.org/p/picarones | |
| permissions: | |
| id-token: write # OIDC trust pour TestPyPI | |
| steps: | |
| - name: Download artefacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist-${{ needs.build.outputs.version }} | |
| path: dist/ | |
| - name: Publish to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| skip-existing: true | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 3 β Smoke test depuis TestPyPI (avant publication finale) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| testpypi-smoke: | |
| name: Smoke test TestPyPI install | |
| needs: [build, publish-testpypi] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| - name: Install Tesseract (pour le demo) | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y tesseract-ocr tesseract-ocr-fra | |
| - name: Install from TestPyPI | |
| run: | | |
| # Retry pour le dΓ©lai d'indexation TestPyPI (~30s typique) | |
| for i in 1 2 3 4 5; do | |
| pip install \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| picarones==${{ needs.build.outputs.version }} && break | |
| echo "Tentative $i Γ©chouΓ©e, retry dans 30s..." | |
| sleep 30 | |
| done | |
| - name: Run demo | |
| run: | | |
| picarones --version | |
| picarones demo --output /tmp/demo.html | |
| test -s /tmp/demo.html | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 4 β Publication PyPI (production) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| publish-pypi: | |
| name: Publish to PyPI | |
| needs: [build, testpypi-smoke] | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/picarones | |
| permissions: | |
| id-token: write # OIDC trust β pas de token long-lived | |
| steps: | |
| - name: Download artefacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist-${{ needs.build.outputs.version }} | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 5 β Image Docker multi-arch publiΓ©e sur ghcr.io | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| docker: | |
| name: Build & push Docker image | |
| needs: [build, publish-pypi] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write # push ghcr.io | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU (for arm64 emulation) | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to ghcr.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build & push (multi-arch) | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: | | |
| ghcr.io/${{ github.repository_owner }}/picarones:${{ needs.build.outputs.version }} | |
| ghcr.io/${{ github.repository_owner }}/picarones:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| provenance: true # SLSA attestation | |
| sbom: true # Software Bill of Materials | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 6 β GitHub Release avec corps depuis CHANGELOG | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| github-release: | |
| name: Create GitHub Release | |
| needs: [build, publish-pypi, docker] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # crΓ©er la release | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download artefacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: dist-${{ needs.build.outputs.version }} | |
| path: dist/ | |
| - name: Extract release notes from CHANGELOG | |
| id: notes | |
| run: | | |
| # Lit la section ``## [X.Y.Z]`` correspondant Γ la version. | |
| # Si pas trouvΓ©e, fallback sur un message gΓ©nΓ©rique. | |
| VERSION="${{ needs.build.outputs.version }}" | |
| NOTES_FILE=/tmp/release_notes.md | |
| python <<PYEOF > "$NOTES_FILE" | |
| import re, sys | |
| changelog = open("CHANGELOG.md", encoding="utf-8").read() | |
| version = "$VERSION" | |
| # On cherche soit ``## [1.2.3]``, ``## [v1.2.3]``, ou ``## [1.2.3]`` | |
| pat = re.compile(rf"^## \[v?{re.escape(version)}\][^\n]*\n(.+?)(?=^## \[|^---|\Z)", re.MULTILINE | re.DOTALL) | |
| m = pat.search(changelog) | |
| if m: | |
| print(m.group(1).strip()) | |
| else: | |
| print(f"Release {version} β voir CHANGELOG.md pour les dΓ©tails.") | |
| PYEOF | |
| echo "Notes extraites depuis CHANGELOG :" | |
| cat "$NOTES_FILE" | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.ref_name }} | |
| name: Picarones ${{ needs.build.outputs.version }} | |
| body_path: /tmp/release_notes.md | |
| files: | | |
| dist/*.whl | |
| dist/*.tar.gz | |
| draft: false | |
| prerelease: ${{ contains(needs.build.outputs.version, 'rc') || contains(needs.build.outputs.version, 'a') || contains(needs.build.outputs.version, 'b') }} | |