diff --git a/.github/workflows/release-draft.yml b/.github/workflows/release-draft.yml index bbfa8b258..e8fd2955a 100644 --- a/.github/workflows/release-draft.yml +++ b/.github/workflows/release-draft.yml @@ -4,13 +4,24 @@ on: workflow_dispatch: # checkov:skip=CKV_GHA_7 inputs: bump-type: - description: "Specify if the version should be bumped as patch, minor, or major" + description: | + How to bump the base version (X.Y.Z). Use `none` to keep the base unchanged + and only re-qualify an existing pre-release (e.g. rc1 -> rc2). required: true type: choice options: - patch - minor - major + - none + qualifier: + description: | + Optional pre-release qualifier to append, without the leading hyphen + (e.g. `rc1`, `alpha2`, `beta`). Leave blank for a stable release. + Allowed characters: alphanumerics, dot, hyphen. + required: false + type: string + default: "" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -32,6 +43,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 with: + ref: ${{ github.ref_name }} fetch-depth: 0 - name: Install JDK 17 @@ -43,32 +55,72 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2 + - name: Validate qualifier input + env: + QUALIFIER: ${{ github.event.inputs.qualifier }} + run: | + if [ -n "$QUALIFIER" ] && ! [[ "$QUALIFIER" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*$ ]]; then + echo "::error::qualifier '$QUALIFIER' is invalid. It must start with an alphanumeric character and may only contain alphanumerics, dot, and hyphen." + exit 1 + fi + - name: Get current version id: version-file run: | version_from_file=$(head -n 1 VERSION) echo "release-version=$version_from_file" >> $GITHUB_OUTPUT + base_version="${version_from_file%%-*}" + echo "base-version=$base_version" >> $GITHUB_OUTPUT - - name: Bump version + - name: Bump base version id: bump-version + if: ${{ github.event.inputs.bump-type != 'none' }} uses: actions-ecosystem/action-bump-semver@34e334551143a5301f38c830e44a22273c6ff5c5 # v1.0.0 with: - current_version: ${{ steps.version-file.outputs.release-version }} - level: ${{ github.event.inputs.bump-type || 'patch' }} + current_version: ${{ steps.version-file.outputs.base-version }} + level: ${{ github.event.inputs.bump-type }} + + - name: Compose new version + id: compose-version + env: + CURRENT: ${{ steps.version-file.outputs.release-version }} + BUMPED: ${{ steps.bump-version.outputs.new_version }} + BASE: ${{ steps.version-file.outputs.base-version }} + BUMP_TYPE: ${{ github.event.inputs.bump-type }} + QUALIFIER: ${{ github.event.inputs.qualifier }} + run: | + if [ "$BUMP_TYPE" = "none" ]; then + new_base="$BASE" + else + new_base="$BUMPED" + fi + + if [ -n "$QUALIFIER" ]; then + new_version="${new_base}-${QUALIFIER}" + else + new_version="$new_base" + fi + + if [ "$new_version" = "$CURRENT" ]; then + echo "::error::Computed version '$new_version' is identical to the current VERSION. Pick a different bump-type or qualifier." + exit 1 + fi + + echo "new-version=$new_version" >> $GITHUB_OUTPUT - name: Save bumped version to file run: | - echo "${{ steps.bump-version.outputs.new_version }}" > VERSION + echo "${{ steps.compose-version.outputs.new-version }}" > VERSION - name: Publish to Maven local (smoke test) - run: ./gradlew publishMavenPublicationToMavenLocal -PVERSION=${{ steps.bump-version.outputs.new_version }} + run: ./gradlew publishMavenPublicationToMavenLocal -PVERSION=${{ steps.compose-version.outputs.new-version }} - name: Generate changelog entry id: changelog # Pinned SHA for Semgrep (no mutable @main); bump when upgrading the action. uses: ROKT/rokt-workflows/actions/generate-changelog@c5c93e92107c520fb8b8cf71070995abdf4c403f with: - version: ${{ steps.bump-version.outputs.new_version }} + version: ${{ steps.compose-version.outputs.new-version }} repo-url: https://github.com/${{ github.repository }} changelog-path: CHANGELOG.md exclude-types: chore,ci,test,build @@ -78,10 +130,10 @@ jobs: uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 with: token: ${{ secrets.MP_SEMANTIC_RELEASE_BOT }} - commit-message: "chore: prepare release ${{ steps.bump-version.outputs.new_version }}" - branch: release/${{ steps.bump-version.outputs.new_version }} - title: "Prepare release ${{ steps.bump-version.outputs.new_version }}" - base: main + commit-message: "chore: prepare release ${{ steps.compose-version.outputs.new-version }}" + branch: release-prep/${{ steps.compose-version.outputs.new-version }} + title: "Prepare release ${{ steps.compose-version.outputs.new-version }}" + base: ${{ github.ref_name }} body: | - Preparing for release ${{ steps.bump-version.outputs.new_version }} - - Bumped version to ${{ steps.bump-version.outputs.new_version }} + Preparing for release ${{ steps.compose-version.outputs.new-version }} from `${{ github.ref_name }}`. + - Bumped version to ${{ steps.compose-version.outputs.new-version }} diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 19d6d67f0..22d5c4414 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - workstation/* paths: - VERSION @@ -15,6 +16,7 @@ jobs: runs-on: ubuntu-latest outputs: final_version: ${{ steps.version-file.outputs.release-version }} + is_prerelease: ${{ steps.version-file.outputs.is-prerelease }} steps: - name: Checkout repository uses: actions/checkout@v6 @@ -24,6 +26,11 @@ jobs: run: | version_from_file=$(head -n 1 VERSION) echo "release-version=$version_from_file" >> $GITHUB_OUTPUT + if [[ "$version_from_file" == *"-"* ]]; then + echo "is-prerelease=true" >> $GITHUB_OUTPUT + else + echo "is-prerelease=false" >> $GITHUB_OUTPUT + fi build-and-release: needs: setup-and-version @@ -58,7 +65,8 @@ jobs: - name: Create GitHub release uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 with: - makeLatest: true + makeLatest: ${{ needs.setup-and-version.outputs.is_prerelease != 'true' }} + prerelease: ${{ needs.setup-and-version.outputs.is_prerelease == 'true' }} tag: ${{ needs.setup-and-version.outputs.final_version }} body: | Release ${{ needs.setup-and-version.outputs.final_version }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 523e1dc13..be8d3490e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +### Changed + +- Add support for qualified alpha, beta, and release candidate versions in release workflows. + ### Removed - Remove deprecated `KitIntegration.getAllUserAttributes()`. Custom kits must use `getCurrentUser().getUserAttributes()` (or other `FilteredMParticleUser` APIs) and `AttributeListener` callbacks instead ([#682](https://github.com/mParticle/mparticle-android-sdk/pull/682)) diff --git a/RELEASE.md b/RELEASE.md index 2eafd710f..11b0971f4 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,82 +2,81 @@ This document outlines the process for releasing the mParticle Android SDK and its kits. -## Step 1: Preparing the SDK for Release +The Android SDK and kits are released together from this monorepo using GitHub Actions. -The Android SDK and kits are released using GitHub Actions. The SDK and kits are currently coupled together in the release process. +## Release workflows -### Pre-release Checklist +### Release - Draft -- Ensure all commits are in the public main branch -- Review `release.yml` in the repo for specific workflow details -- The release job deploys the most current snapshot of main branch release tag to main branch +Use `Release - Draft` to prepare a release PR. The workflow: -## Step 2: Release via GitHub Actions +1. Reads the current version from `VERSION`. +2. Computes the next release version from `bump-type` and optional `qualifier`. +3. Writes the computed version back to `VERSION`. +4. Publishes to Maven local as a smoke test. +5. Generates the changelog entry. +6. Opens a PR back into the branch selected in the workflow dispatch UI. -### What the GitHub Release Job Does +Generated release PR branches use `release/prep-`, for example `release/prep-6.0.0-rc1`. -1. **Initial Setup** - - Verifies job is running from public repo and on main branch - - Creates temporary `release/{run_number}` branch +### Release - Publish -2. **Testing Phase** - - Runs unit and instrumented tests in parallel - - Instrumented tests require an emulator - - Unit tests run independently - - Updates kits and runs additional tests +`Release - Publish` runs when `VERSION` changes on: -3. **Version Management** - - Runs semantic version action - - Automatically bumps `build.gradle` version based on commit messages - - No version bump if no new commits (e.g., feat/fix) - - Generates release notes automatically - - Requires linear history between development and main branches +- `main` +- `workstation/*` -4. **Artifact Publishing** - - Uploads artifacts to Sonatype (core and kits) - - Builds and signs the core SDK and all kit artifacts - - Uploads to Sonatype Nexus (staging area) - - Syncs artifacts to Maven Central - > Note: This step will be moved before version bump during semantic release +The workflow publishes the SDK and kits to Maven Central using the exact version in `VERSION`, then creates a GitHub release with the same tag. -5. **Branch Synchronization** - - Pushes release branch to: - - Public main branch - - Public development branch - - Internal repo main branch - - Deletes release branch on success (preserved on failure for debugging) +## Stable releases -### How to Release +1. Run `Release - Draft` from the target branch. +2. Choose `patch`, `minor`, or `major` for `bump-type`. +3. Leave `qualifier` empty. +4. Review, approve, and merge the generated release PR. +5. Confirm `Release - Publish` succeeds. -1. Navigate to the Actions tab -2. Select "release SDK" -3. Run the workflow from main branch with "true" first to perform a dry run - > Important: Always start with a dry run to validate the release process. This will perform all steps up to semantic release without actually publishing, helping catch potential issues early. -4. If the dry run succeeds, run the workflow again with "false" option to perform the actual release - > Note: Only proceed with the actual release after confirming a successful dry run +Stable releases are marked as the latest GitHub release. -### Important Notes +## Alpha, beta, and RC releases -- **Release Duration**: Expect ~20 minutes due to comprehensive test suite -- **Emulator Issues**: - - Sometimes GitHub Actions emulators fail - - We have a custom script to install and start the emulator `scripts/install-start-emulator.sh` - - OS version is hardcoded to avoid issues with new releases -- **Code Reusability**: - - Reusable GitHub Actions are defined in the [mparticle-workflows repo](https://github.com/mParticle/mparticle-workflows) - - This enables other platforms to reuse similar jobs +Use a qualified release when a partner or internal validation flow needs a fixed Maven Central version before the stable release, for example `6.0.0-alpha1`, `6.0.0-beta1`, or `6.0.0-rc1`. -## Post-Release Verification +1. Run `Release - Draft` from the target branch, such as `workstation/6.0-Release`. +2. Choose the base `bump-type`: + - `patch`, `minor`, or `major` to bump the base version. + - `none` to keep the base version unchanged and only change or remove the qualifier. +3. Set `qualifier` to the pre-release identifier without the leading hyphen, for example `alpha1`, `beta1`, or `rc1`. +4. Review, approve, and merge the generated release PR. +5. Confirm `Release - Publish` succeeds. -After a successful build through GitHub Actions, verify: +Qualified releases are published to Maven Central with the exact qualified version. Their GitHub releases are marked as pre-releases and are not marked as latest. -1. Public repo has a new semantic release tag -2. New artifact is present in [Sonatype](https://central.sonatype.com/publishing) +The qualifier must start with an alphanumeric character and may only contain alphanumerics, dots, and hyphens. + +## Version examples + +- `5.78.2` with `bump-type=minor` and `qualifier=alpha1` produces `5.79.0-alpha1`. +- `5.79.0-alpha1` with `bump-type=none` and `qualifier=beta1` produces `5.79.0-beta1`. +- `5.79.0-beta1` with `bump-type=none` and `qualifier=rc1` produces `5.79.0-rc1`. +- `5.79.0-rc1` with `bump-type=none` and an empty `qualifier` produces `5.79.0`. + +The draft workflow rejects any input combination that would produce the same version already stored in `VERSION`. + +## Post-release verification + +After a successful publish workflow, verify: + +1. The GitHub release exists with the expected tag. +2. The SDK artifact is present in [Sonatype](https://central.sonatype.com/publishing). +3. The kit artifacts are present in [Sonatype](https://central.sonatype.com/publishing). +4. Pre-release tags are marked as pre-releases and stable tags are marked as latest. ## Troubleshooting -If you encounter emulator issues during testing, check: +If release validation fails: -- [Emulator setup script](https://github.com/mParticle/mparticle-android-sdk/blob/main/scripts/install-start-emulator.sh) -- Current OS version compatibility -- GitHub Actions logs for specific error messages +- Check the generated release PR for the computed `VERSION` and changelog changes. +- Check the Maven local smoke-test step in `Release - Draft`. +- Check the Maven Central publish steps in `Release - Publish`. +- For emulator-related validation issues, check `scripts/install-start-emulator.sh` and the GitHub Actions logs.