diff --git a/.ci/release.sh b/.ci/release.sh index 1cae23691..a0d3457ee 100755 --- a/.ci/release.sh +++ b/.ci/release.sh @@ -28,8 +28,7 @@ if [[ "$dry_run" == "true" ]] ; then publishArg='publishAllPublicationsToDryRunRepository' else echo "--- Build and publish the release :package:" - ### TODO: changeme - publishArg='assemble' + publishArg='publishToSonatype closeAndReleaseStagingRepository' fi ./gradlew \ diff --git a/.github/workflows/gradle-goal/action.yml b/.github/workflows/gradle-goal/action.yml new file mode 100644 index 000000000..62bfaf43d --- /dev/null +++ b/.github/workflows/gradle-goal/action.yml @@ -0,0 +1,33 @@ +--- + +name: gradle-goal +description: Install specific JDK and run a command + +inputs: + version: + description: 'Java version' + required: true + default: '17' + distribution: + description: 'Java distribution' + required: true + default: 'temurin' + command: + description: 'Command to execute' + required: true + shell: + description: 'Default shell' + default: 'bash' + required: false + +runs: + using: "composite" + steps: + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: ${{ inputs.version }} + distribution: ${{ inputs.distribution }} + cache: 'gradle' + - run: ${{ inputs.command }} + shell: ${{ inputs.shell }} diff --git a/.github/workflows/pre-post-release.yml b/.github/workflows/pre-post-release.yml new file mode 100644 index 000000000..764bfd470 --- /dev/null +++ b/.github/workflows/pre-post-release.yml @@ -0,0 +1,97 @@ +--- + +name: Pre/Post Release + +on: + workflow_call: + inputs: + ref: + description: 'Branch or tag ref to run the workflow on' + type: string + required: true + default: 'main' + version: + description: 'The version to release (e.g. 1.2.3). This workflow will automatically perform the required version bumps' + type: string + required: true + phase: + description: 'Pre or post release phase' + type: string # valid values are 'pre' or 'post' + required: true + +env: + RELEASE_VERSION: ${{ inputs.version }} + BRANCH_NAME: ${{ inputs.phase }}-release-v${{ inputs.version }} + +permissions: + contents: read + +jobs: + validate-tag: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Validate release tag does not exist in repo + uses: ./.github/workflows/validate-tag + with: + tag: v${{ env.RELEASE_VERSION }} + + create-pr: + name: "Bump versions and create PR" + runs-on: ubuntu-latest + needs: + - validate-tag + permissions: + contents: write + steps: + - uses: elastic/apm-pipeline-library/.github/actions/github-token@current + with: + url: ${{ secrets.VAULT_ADDR }} + roleId: ${{ secrets.VAULT_ROLE_ID }} + secretId: ${{ secrets.VAULT_SECRET_ID }} + + - uses: elastic/apm-pipeline-library/.github/actions/setup-git@current + with: + username: ${{ env.GIT_USER }} + email: ${{ env.GIT_EMAIL }} + token: ${{ env.GITHUB_TOKEN }} + + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + token: ${{ env.GITHUB_TOKEN }} + + - name: Create the release tag (post phase) + if: inputs.phase == 'post' + run: | + git tag "v${{ env.RELEASE_VERSION }}" + git push origin "v${{ env.RELEASE_VERSION }}" + + - name: Create a ${{ inputs.phase }} release branch + run: git checkout -b ${{ env.BRANCH_NAME }} + + - name: Set release version (pre release) + if: inputs.phase == 'pre' + uses: ./.github/workflows/gradle-goal + with: + command: ./gradlew -q setVersion -PnewVersion=${{ env.RELEASE_VERSION }} + + - name: Set next snapshot version (post release) + if: inputs.phase == 'post' + uses: ./.github/workflows/gradle-goal + with: + command: ./gradlew -q setNextVersion + + - name: Push the ${{ inputs.phase }} release branch + run: | + git add --all + git commit -m "${{ inputs.phase }} release: elastic-otel-java v${{ env.RELEASE_VERSION }}" + git push origin ${{ env.BRANCH_NAME }} + + - name: Create the ${{ inputs.phase }} release PR + run: gh pr create --title="${{ inputs.phase }} release v${{ env.RELEASE_VERSION }}" --base main --head ${{ env.BRANCH_NAME }} -b "${{ inputs.phase }} release v${{ env.RELEASE_VERSION }}" + env: + GH_TOKEN: ${{ env.GITHUB_TOKEN }} diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 000000000..4d97c1fff --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,32 @@ +--- + +name: Pre release + +on: + workflow_dispatch: + inputs: + ref: + description: 'Branch or tag ref to run the workflow on' + required: true + default: 'main' + version: + description: 'The version to release (e.g. 1.2.3). This workflow will automatically perform the required version bumps' + required: true + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} + +jobs: + pre-release: + name: "Bump versions and create PR" + uses: ./.github/workflows/pre-post-release.yml + permissions: + contents: write + with: + ref: ${{ inputs.ref }} + version: ${{ inputs.version }} + phase: 'pre' + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c18d50e4d..b10cdb4e2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,22 +1,75 @@ --- -name: release + +name: Release on: workflow_dispatch: inputs: + ref: + description: 'Branch or tag ref to run the workflow on' + required: true + default: "main" + version: + description: 'The version to release (e.g. 1.2.3). This workflow will automatically perform the required version bumps' + required: true dry_run: description: If set, run a dry-run release default: false type: boolean + skip_maven_deploy: + description: | + If enabled, the deployment to maven central will be skipped. + Select this if the deployment job for this release failed in a previous version but the release was actually published. + Check manually on maven central beforehand! + type: boolean + required: true + default: false permissions: contents: read +concurrency: + group: ${{ github.workflow }} + +env: + RELEASE_VERSION: ${{ inputs.version }} + jobs: + validate-tag: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref }} + fetch-depth: 0 + - name: Warmup gradle wrapper + uses: ./.github/workflows/gradle-goal + with: + command: "./gradlew -q currentVersion" + - name: Validate release tag does not exist in repo + uses: ./.github/workflows/validate-tag + with: + tag: v${{ env.RELEASE_VERSION }} + - name: Validate tag match current version + run: | + if [ "$(./gradlew -q currentVersion)" != "${{ env.RELEASE_VERSION }}" ]; then + echo "Tag should match version set in 'version.properties'" + exit 1 + fi + - name: Validate version is a release version + run: | + if [[ "$(./gradlew -q currentVersion)" =~ "-SNAPSHOT" ]]; then + echo "This is a snapshot version" + exit 1 + fi + release: name: Release runs-on: ubuntu-latest - + if: ${{ ! inputs.skip_maven_deploy }} + needs: + - validate-tag steps: - id: buildkite name: Run Release @@ -29,6 +82,7 @@ jobs: waitFor: true printBuildLogs: false buildEnvVars: | + ref=${{ inputs.ref }} dry_run=${{ inputs.dry_run || 'false' }} - if: ${{ success() && ! inputs.dry_run }} @@ -52,14 +106,29 @@ jobs: :ghost: [${{ github.repository }}] Release *${{ github.ref_name }}* didn't get triggered in Buildkite. Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>) - post_release: - name: Post Release - needs: release + await-maven-central-artifact: runs-on: ubuntu-latest - permissions: - contents: write + name: Wait for release to be available on maven-central + if: inputs.dry_run == false + needs: + - release steps: - - uses: actions/checkout@v4 + - uses: elastic/apm-pipeline-library/.github/actions/await-maven-artifact@current + with: + groupid: 'co.elastic.otel' + artifactid: 'elastic-otel-javaagent' + version: ${{ inputs.version }} - - if: ${{ ! inputs.dry_run }} - run: echo "TODO" \ No newline at end of file + post-release: + name: "Bump versions and create PR" + needs: + - await-maven-central-artifact + uses: ./.github/workflows/pre-post-release.yml + permissions: + contents: write + if: inputs.dry_run == false + with: + ref: ${{ inputs.ref }} + version: ${{ inputs.version }} + phase: 'post' + secrets: inherit diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index c31c19d5a..322f55c53 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -16,10 +16,28 @@ permissions: contents: read jobs: + validate: + runs-on: ubuntu-latest + outputs: + is-snapshot: ${{ steps.validate.outputs.is-snapshot }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Validate version is a snapshot version + run: | + output=false + if [[ "$(./gradlew -q currentVersion)" =~ "-SNAPSHOT" ]]; then + echo "This is a snapshot version" + output=true + fi + echo "is-snapshot=${output}" >> "${GITHUB_OUTPUT}" + deploy: name: Deploy runs-on: ubuntu-latest - + needs: + - validate + if: ${{ contains(needs.validate.outputs.is-snapshot, 'true') }} steps: - id: buildkite name: Run Deploy @@ -44,4 +62,3 @@ jobs: message: | :ghost: [${{ github.repository }}] Snapshot *${{ github.ref_name }}* didn't get triggered in Buildkite. Build: (<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|here>) - diff --git a/.github/workflows/validate-tag/action.yml b/.github/workflows/validate-tag/action.yml new file mode 100644 index 000000000..c880256fc --- /dev/null +++ b/.github/workflows/validate-tag/action.yml @@ -0,0 +1,25 @@ +--- + +name: validate-tag +description: Validate tag format + +inputs: + tag: + description: 'Tag to validate' + required: true + +runs: + using: "composite" + steps: + - name: Validate tag does not exist on current commit + id: validate-tag + shell: 'bash' + run: | + if ! [ $(echo "${{ inputs.tag }}" | grep -P "(\d{1,2})\.(\d{1,2})\.(\d{1,2})") ]; then + echo "Tag should be a SemVer format" + exit 1 + fi + if [ $(git tag -l "${{ inputs.tag }}") ]; then + echo "The tag ${{ inputs.tag }} already exists" + exit 1 + fi diff --git a/build.gradle b/build.gradle index d99945e17..5d7ae7f5c 100644 --- a/build.gradle +++ b/build.gradle @@ -111,6 +111,30 @@ task copyGitHooks(type: Copy) { into layout.projectDirectory.dir(".git").dir("hooks") } +task currentVersion { + doLast { + println project.version + } +} + +task setVersion { + doLast { + def versionFile = file('version.properties') + versionFile.text = "version=${newVersion}\n" + } +} + +task setNextVersion { + doLast { + def versionFile = file('version.properties') + def pattern = /version=(\d+\.\d+\.\d+)(-SNAPSHOT)?/ + versionFile.text = versionFile.text.replaceFirst(pattern) { match, version, suffix -> + def (major, minor, patch) = version.split(/\./).collect { it as int } + "version=${major}.${minor}.${patch + 1}-SNAPSHOT" + } + } +} + tasks.configure { doLast { copyGitHooks