From c8c1609cba3570ca1a9a51f76e1619fdf9add12e Mon Sep 17 00:00:00 2001 From: Chen Asraf Date: Sun, 12 Apr 2026 23:05:17 +0300 Subject: [PATCH] feat: add release-please+fastlane workflow --- .../release-please-fastlane-changelog.yml | 156 ++++++++++++++++++ README.md | 80 +++++++++ 2 files changed, 236 insertions(+) create mode 100644 .github/workflows/release-please-fastlane-changelog.yml diff --git a/.github/workflows/release-please-fastlane-changelog.yml b/.github/workflows/release-please-fastlane-changelog.yml new file mode 100644 index 0000000..36e8678 --- /dev/null +++ b/.github/workflows/release-please-fastlane-changelog.yml @@ -0,0 +1,156 @@ +# Reusable Release Please + Fastlane Changelog Workflow +# +# Runs release-please, then if a PR was created/updated, generates a +# fastlane changelog from CHANGELOG.md and amends it into the release +# commit. The changelog is truncated to 500 chars (Play Store limit). +# +# Supports writing the changelog to multiple directories (e.g. Android +# and iOS fastlane metadata). Pass a newline-separated list of paths. +# +# Call from other repos: +# jobs: +# release-please: +# uses: chenasraf/workflows/.github/workflows/release-please-fastlane-changelog.yml@master +# with: +# release-type: dart +# +# Outputs are forwarded from release-please so downstream jobs can +# use release_created, tag_name, and version. + +name: Release Please + Fastlane Changelog + +on: + workflow_call: + inputs: + release-type: + description: 'Release Please release type (e.g., dart, node, python, go)' + required: true + type: string + version-file: + description: 'File containing the version string' + required: false + type: string + default: 'pubspec.yaml' + changelog-file: + description: 'Path to the CHANGELOG.md file' + required: false + type: string + default: 'CHANGELOG.md' + fastlane-changelog-dirs: + description: | + Newline-separated list of directories to write the changelog file to. + Each directory gets a `{versionCode}.txt` file with the same content. + required: false + type: string + default: 'fastlane/metadata/android/en-US/changelogs' + max-length: + description: 'Maximum changelog length (Play Store limit is 500)' + required: false + type: number + default: 500 + truncation-trailer: + description: 'Text appended when changelog is truncated' + required: false + type: string + default: "\n\n… see full notes on GitHub." + secrets: + token: + description: 'GitHub token for release-please (defaults to GITHUB_TOKEN)' + required: false + outputs: + release_created: + description: 'Whether a release was created' + value: ${{ jobs.release-please.outputs.release_created }} + tag_name: + description: 'The release tag name' + value: ${{ jobs.release-please.outputs.tag_name }} + version: + description: 'The release version' + value: ${{ jobs.release-please.outputs.version }} + +jobs: + release-please: + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + version: ${{ steps.release.outputs.version }} + + steps: + - name: Release Please + id: release + uses: googleapis/release-please-action@v4 + with: + release-type: ${{ inputs.release-type }} + token: ${{ secrets.token || github.token }} + + - name: Update fastlane changelog on release PR + if: ${{ steps.release.outputs.pr && !steps.release.outputs.release_created }} + env: + PR_JSON: ${{ steps.release.outputs.pr }} + VERSION_FILE: ${{ inputs.version-file }} + CHANGELOG_FILE: ${{ inputs.changelog-file }} + OUT_DIRS: ${{ inputs.fastlane-changelog-dirs }} + MAX_LENGTH: ${{ inputs.max-length }} + TRAILER: ${{ inputs.truncation-trailer }} + run: | + PR_BRANCH=$(echo "$PR_JSON" | jq -r .headBranchName) + git clone --depth=5 --branch "$PR_BRANCH" \ + https://x-access-token:${{ secrets.token || github.token }}@github.com/${{ github.repository }}.git _pr + cd _pr + + # Extract version name and code + VERSION_LINE=$(grep '^version:' "$VERSION_FILE") + VERSION=$(echo "$VERSION_LINE" | sed 's/version: *//;s/+.*//') + CODE=$(echo "$VERSION_LINE" | sed 's/.*+//') + + # Generate changelog content + if [ ! -f "$CHANGELOG_FILE" ]; then + NOTES="Release $VERSION" + echo "No $CHANGELOG_FILE found, using fallback." + else + NOTES=$(awk " + /^## \\[${VERSION//./\\.}\\]/ { found=1; next } + /^## / { if (found) exit } + found { print } + " "$CHANGELOG_FILE") + + NOTES=$(echo "$NOTES" \ + | sed -E 's/ *\(\[[0-9a-f]+\]\([^)]+\)\)//g' \ + | sed -E 's/^\* \*\*[^*]+:\*\* */- /; s/^\* /- /' \ + | sed 's/^### //' \ + | sed '/^[[:space:]]*$/d') + + NOTES=$(echo "$NOTES" | sed -e '/./,$!d' -e :a -e '/^\n*$/{$d;N;ba;}') + [ -z "$NOTES" ] && NOTES="Release $VERSION" + + if [ ${#NOTES} -gt "$MAX_LENGTH" ]; then + BUDGET=$((MAX_LENGTH - ${#TRAILER})) + NOTES="${NOTES:0:$BUDGET}" + NOTES=$(echo "$NOTES" | sed '$d') + NOTES="${NOTES}${TRAILER}" + fi + fi + + echo "Changelog (${#NOTES} chars):" + echo "$NOTES" + echo "---" + + # Write to each output directory + echo "$OUT_DIRS" | while IFS= read -r dir; do + dir=$(echo "$dir" | xargs) # trim whitespace + [ -z "$dir" ] && continue + mkdir -p "$dir" + echo "$NOTES" > "$dir/$CODE.txt" + echo "Wrote $dir/$CODE.txt" + done + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add . + if git diff --cached --quiet; then + echo "No changelog changes." + else + git commit --amend --no-edit + git push --force-with-lease + fi diff --git a/README.md b/README.md index ac21ff2..1dd49aa 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A collection of reusable GitHub Actions workflows. - [Workflows](#workflows) - [Go Release](#go-release-go-releaseyml) - [Manual Homebrew Release](#manual-homebrew-release-manual-homebrew-releaseyml) + - [Release Please + Fastlane Changelog](#release-please--fastlane-changelog-release-please-fastlane-changelogyml) - [Nextcloud Workflows](#nextcloud-workflows) - [PHPUnit MySQL](#phpunit-mysql-nextcloud-phpunit-mysqlyml) - [PHPUnit PostgreSQL](#phpunit-postgresql-nextcloud-phpunit-pgsqlyml) @@ -123,6 +124,85 @@ jobs: --- +### Release Please + Fastlane Changelog (`release-please-fastlane-changelog.yml`) + +Runs [release-please](https://github.com/googleapis/release-please), then when a release PR is +created or updated, extracts the changelog for the current version from `CHANGELOG.md`, formats it +for the Play Store (stripped of markdown links, commit hashes, etc.), truncates to the 500-char +limit, and writes it to one or more fastlane metadata directories. The changelog is amended into +release-please's commit so the tagged release includes the file. + +#### Features + +- Runs release-please with configurable release type +- Extracts version-specific notes from `CHANGELOG.md` +- Strips commit links, reformats bullets, removes markdown headers +- Truncates to Play Store's 500-char limit with a configurable trailer +- Writes to multiple output directories (e.g. Android + iOS fastlane metadata) +- Amends the release-please commit (no extra commits in the PR) +- Forwards `release_created`, `tag_name`, and `version` outputs + +#### Usage + +```yaml +name: CI/CD + +on: + push: + branches: [master] + +jobs: + release-please: + uses: chenasraf/workflows/.github/workflows/release-please-fastlane-changelog.yml@master + with: + release-type: dart +``` + +With multiple output directories (Android + iOS): + +```yaml +jobs: + release-please: + uses: chenasraf/workflows/.github/workflows/release-please-fastlane-changelog.yml@master + with: + release-type: dart + fastlane-changelog-dirs: | + fastlane/metadata/android/en-US/changelogs + fastlane/metadata/ios/en-US/changelogs + + build: + needs: release-please + if: ${{ needs.release-please.outputs.release_created }} + # ... +``` + +#### Inputs + +| Input | Description | Required | Default | +| -------------------------- | ------------------------------------------------------------------ | -------- | ------------------------------------------------ | +| `release-type` | Release Please release type (`dart`, `node`, `python`, `go`, etc.) | Yes | - | +| `version-file` | File containing the version string | No | `pubspec.yaml` | +| `changelog-file` | Path to the CHANGELOG.md file | No | `CHANGELOG.md` | +| `fastlane-changelog-dirs` | Newline-separated list of output directories | No | `fastlane/metadata/android/en-US/changelogs` | +| `max-length` | Maximum changelog length in characters | No | `500` | +| `truncation-trailer` | Text appended when the changelog is truncated | No | `\n\n… see full notes on GitHub.` | + +#### Secrets + +| Secret | Description | Required | +| ------- | -------------------------------------------------- | -------- | +| `token` | GitHub token for release-please (defaults to `GITHUB_TOKEN`) | No | + +#### Outputs + +| Output | Description | +| ----------------- | ---------------------------- | +| `release_created` | Whether a release was created | +| `tag_name` | The release tag name | +| `version` | The release version | + +--- + ## Nextcloud Workflows Reusable workflows for Nextcloud app development. These workflows include automatic path filtering to skip unnecessary runs when irrelevant files change.