diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml deleted file mode 100644 index 008ee3f3..00000000 --- a/.forgejo/workflows/release.yml +++ /dev/null @@ -1,227 +0,0 @@ -# Build and publish a release on the Forgejo source-of-truth instance -# with the PostIt Android APK as an attached asset. -# -# Triggered by a push of a git tag. Validates the tag/changelog pair, -# builds the APK using the existing Dockerfile (--target build-env), then -# publishes a Forgejo release via rasterstate/forgejo-release-action and -# uploads the APK as an asset. -# -# Authentication uses ${{ secrets.RELEASE_TOKEN }}, a Forgejo PAT scoped -# to `write:repository` configured in the repository's Actions secrets. -# The runner-provided ${{ secrets.GITHUB_TOKEN }} would also work, but -# a dedicated PAT is preferred for least-privilege and revocability. -# -# This workflow complements .github/workflows/docker-publish-android.yml -# which targets the GitHub mirror; the validate-release logic mirrors -# the GitHub-side job so the two channels stay consistent. -name: Forgejo Release - -on: - push: - tags: - - '*' - workflow_dispatch: - inputs: - tag: - description: 'Tag à publier (requis en dispatch, ex. 1.0.6 ou 1.0.7-rc1).' - required: true - type: string - force_unstable: - description: 'Publier une release avec suffixe (ex. 1.0.0-rc1) malgré le fail-fast par défaut.' - required: false - type: boolean - default: false - -permissions: - contents: write - -jobs: - # Parse le tag, applique la règle de parité du patch - # (pair=stable / impair=preview / suffixe=unstable), fail-fast sur - # instable sauf opt-in, et vérifie que CHANGELOG.md contient une - # section `## [TAG] - ` cohérente. Le body est extrait - # dans un artifact consommé par le job release. - validate-release: - runs-on: docker - steps: - - name: Checkout du code - uses: actions/checkout@v7 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Valider le tag et la section CHANGELOG - env: - # En push tag : github.ref_name est le tag. - # En workflow_dispatch : on lit l'input 'tag' (obligatoire). - TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} - FORCE_UNSTABLE: ${{ inputs.force_unstable || 'false' }} - run: | - if [[ -z "$TAG" ]]; then - echo "::error::No tag provided. In workflow_dispatch, set the 'tag' input." - exit 1 - fi - - # Parse semver : MAJOR.MINOR.PATCH[-SUFFIX] - if [[ ! "$TAG" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$ ]]; then - echo "::error::Tag '$TAG' does not match MAJOR.MINOR.PATCH[-SUFFIX] format." - exit 1 - fi - - MAJOR="${BASH_REMATCH[1]}" - MINOR="${BASH_REMATCH[2]}" - PATCH="${BASH_REMATCH[3]}" - SUFFIX="${BASH_REMATCH[4]}" - - # Classification du canal par parité du patch. - # Patch pair + pas de suffixe -> stable. - # Patch impair + pas de suffixe -> preview. - # Suffixe présent -> instable. - if [[ -n "$SUFFIX" ]]; then - CHANNEL="unstable" - elif (( PATCH % 2 == 0 )); then - CHANNEL="stable" - else - CHANNEL="preview" - fi - - echo "Tag $TAG classifié comme channel=$CHANNEL" - - # Fail-fast sur instable sauf opt-in explicite. - if [[ "$CHANNEL" == "unstable" && "$FORCE_UNSTABLE" != "true" ]]; then - echo "::error::Tag '$TAG' is unstable (suffix '$SUFFIX'). Refusing to publish." - echo "Set force_unstable=true via workflow_dispatch to override." - exit 1 - fi - - # Lecture du CHANGELOG.md (doit exister à la racine du repo). - if [[ ! -f CHANGELOG.md ]]; then - echo "::error::CHANGELOG.md not found at repo root." - exit 1 - fi - - # Extraction de la section [TAG]. On cherche la première ligne - # commençant par '## [' qui contient '[TAG]' (entre '## [' et - # la prochaine ligne '## [' ou fin de fichier). awk en mode - # paragraphe suffit et reste POSIX. - BODY=$(awk -v tag="[$TAG]" ' - /^## \[/ { - if (in_section) exit - if (index($0, tag) > 0) in_section=1 - next - } - in_section { print } - ' CHANGELOG.md) - - if [[ -z "$BODY" ]]; then - echo "::error::No section matching '## [$TAG]' found in CHANGELOG.md." - echo "Add a '## [$TAG] - $CHANNEL' section before tagging." - exit 1 - fi - - # Vérification cohérence du canal déclaré dans le suffixe. - # Format attendu : "## [TAG] - stable" / "- preview" / "- unstable". - if [[ "$BODY" != *" - $CHANNEL"* ]]; then - echo "::error::Section '## [$TAG]' must declare suffix '- $CHANNEL' to match tag parity." - echo "Current section body (first 5 lines):" - echo "$BODY" | head -5 - exit 1 - fi - - echo "Section CHANGELOG validée pour [$TAG] - $CHANNEL" - - # Écrit le body dans un fichier pour transmission via artifact. - # Le body est multi-ligne, donc artifact > heredoc $GITHUB_ENV. - mkdir -p release-body - printf '%s\n' "$BODY" > release-body/body.md - - - name: Uploader le body de la release comme artifact - uses: actions/upload-artifact@v7 - with: - name: release-body - path: release-body/body.md - retention-days: 1 - - # Construit l'APK via le Dockerfile (stage build-env), puis publie - # la release Forgejo avec le body validé et l'APK en asset. - release: - needs: validate-release - runs-on: docker - steps: - - name: Checkout du code - uses: actions/checkout@v7 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Checkout du tag (workflow_dispatch uniquement) - # En push tag, le runner checkout déjà au bon commit. - # En workflow_dispatch, on checkout explicitement le tag demandé - # pour que l'APK soit bien construit depuis ce commit. - if: github.event_name == 'workflow_dispatch' - env: - TAG: ${{ inputs.tag }} - run: | - if [[ -z "$TAG" ]]; then - echo "::error::No tag provided. In workflow_dispatch, set the 'tag' input." - exit 1 - fi - git checkout "$TAG" - - - name: Build de l'image Docker (stage build-env uniquement) - run: docker build --build-arg ANDROID_TARGET_RID=android-arm64 --target build-env -t postit-android . - - - name: Extraire l'APK signé du conteneur - run: | - docker create --name extractor postit-android - docker cp extractor:/src/src/PostIt/PostIt.Android/bin/Release/net10.0-android/android-arm64/com.CompanyName.PostIt-Signed.apk ./PostIt.Android.apk - docker rm extractor - - - name: Récupérer le body validé - uses: actions/download-artifact@v7 - with: - name: release-body - path: release-body - - - name: Calculer le canal (stable / preview / unstable) depuis le tag - # On re-parse le tag ici plutôt que de transporter le channel - # via artifact. Le calcul est trivial (parité du patch + suffixe) - # et reste ainsi explicite. - id: set-channel - env: - # En push tag : github.ref_name est le tag. - # En workflow_dispatch : on lit l'input 'tag'. - TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.tag }} - run: | - if [[ -z "$TAG" ]]; then - echo "::error::No tag provided. In workflow_dispatch, set the 'tag' input." - exit 1 - fi - if [[ ! "$TAG" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$ ]]; then - echo "::error::Tag '$TAG' does not match MAJOR.MINOR.PATCH[-SUFFIX] format." - exit 1 - fi - PATCH="${BASH_REMATCH[3]}" - SUFFIX="${BASH_REMATCH[4]}" - if [[ -n "$SUFFIX" ]]; then - CHANNEL="unstable" - elif (( PATCH % 2 == 0 )); then - CHANNEL="stable" - else - CHANNEL="preview" - fi - echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT" - echo "is_prerelease=$([[ $CHANNEL != stable ]] && echo true || echo false)" >> "$GITHUB_OUTPUT" - - - name: Publier la release Forgejo et uploader l'APK - uses: https://rasterhub.com/rasterstate/forgejo-release-action@v1 - with: - # tag_name defaults to the pushed tag (GITHUB_REF_NAME). - body_path: release-body/body.md - # Stable -> Latest (false). - # Preview et Unstable -> prerelease (true). - prerelease: ${{ steps.set-channel.outputs.is_prerelease }} - files: | - PostIt.Android.apk - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} \ No newline at end of file