diff --git a/.github/workflows/release-prepare-branch.yml b/.github/workflows/release-prepare-branch.yml new file mode 100644 index 0000000000..78660c245c --- /dev/null +++ b/.github/workflows/release-prepare-branch.yml @@ -0,0 +1,179 @@ +# +# Copyright OpenSearch Contributors +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# + +# This workflow automates the preparation of patch release branches. +# It validates the release branch, updates gradle.properties to the next version +# generates the THIRD-PARTY file, and creates a pull request with all changes. + +name: Prepare Release Branch + +on: + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + prepare-release: + runs-on: ubuntu-latest + + steps: + - name: Checkout Data Prepper + uses: actions/checkout@v2 + + - name: Validate release branch + id: validate_branch + run: | + BRANCH_NAME=${GITHUB_REF#refs/heads/} + echo "Running on branch: $BRANCH_NAME" + + # Validate branch name matches {major}.{minor} pattern + if ! [[ $BRANCH_NAME =~ ^[0-9]+\.[0-9]+$ ]]; then + echo "::error::Invalid release branch name: $BRANCH_NAME" + echo "This workflow must run on a release branch with format {major}.{minor} (e.g., '2.6')" + echo "Current branch: $BRANCH_NAME" + echo "" + echo "To run this workflow:" + echo " 1. Switch to a release branch (e.g., 2.6)" + echo " 2. Navigate to Actions > Prepare Patch Release" + echo " 3. Click 'Run workflow' and select the release branch" + exit 1 + fi + + echo "✅ Valid release branch: $BRANCH_NAME" + echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: temurin + + - name: Update version to next release + id: version + run: | + ./gradlew updateToNextVersion + + RELEASE_VERSION=$(./gradlew -q printCurrentVersion) + echo "version=$RELEASE_VERSION" >> $GITHUB_OUTPUT + + - name: Generate THIRD-PARTY file + run: | + echo "Generating THIRD-PARTY file..." + + # Run the Gradle task to generate the THIRD-PARTY file + # Capture both stdout and stderr for better error reporting + if ! OUTPUT=$(./gradlew --no-daemon generateThirdPartyReport 2>&1); then + echo "::error::Failed to generate THIRD-PARTY file" + echo "Gradle output:" + echo "$OUTPUT" + exit 1 + fi + + # Verify the THIRD-PARTY file was created/updated + if [ ! -f THIRD-PARTY ]; then + echo "::error::THIRD-PARTY file not found after generation" + echo "The generateThirdPartyReport task completed but did not create the expected file" + exit 1 + fi + + echo "Successfully generated THIRD-PARTY file" + + - name: GitHub App token + id: github_app_token + uses: tibdex/github-app-token@v2 + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Create Pull Request + id: create_pr + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ steps.github_app_token.outputs.token }} + add-paths: | + gradle.properties + THIRD-PARTY + commit-message: 'Prepare release ${{ steps.version.outputs.version }}' + signoff: true + branch: prepare-release-${{ steps.version.outputs.version }} + delete-branch: true + base: ${{ steps.validate_branch.outputs.branch }} + title: 'Prepare release ${{ steps.version.outputs.version }}' + body: | + ## Automated Release Preparation + + This PR prepares the release branch for version **${{ steps.version.outputs.version }}**. + + ### Automated Changes + - Updated `gradle.properties` version to `${{ steps.version.outputs.version }}` + - Generated `THIRD-PARTY` file + + ### Manual Steps Remaining + + Before merging this PR: + - [ ] Review the version number is correct + - [ ] Review the THIRD-PARTY file changes + + After merging this PR: + - [ ] Prepare release notes (see [release notes script](release/script/release-notes/README.md)) + - [ ] Run the [Release Artifacts workflow](https://github.com/opensearch-project/data-prepper/actions/workflows/release.yml) + - [ ] Approve the release issue (requires 2 maintainer approvals) + - [ ] Update the draft release with release notes + - [ ] Publish the release + - [ ] Close the milestone + + --- + + Branch: ${{ steps.validate_branch.outputs.branch }} + + Generated by the [Prepare Patch Release workflow](https://github.com/opensearch-project/data-prepper/actions/workflows/release-prepare-patch.yml) + + - name: Output PR URL + if: steps.create_pr.outputs.pull-request-url + run: | + echo "✅ Pull Request created successfully!" + echo "URL: ${{ steps.create_pr.outputs.pull-request-url }}" + echo "::notice::Pull Request created: ${{ steps.create_pr.outputs.pull-request-url }}" + + - name: Check PR creation + if: steps.create_pr.outcome == 'failure' + run: | + echo "::error::Failed to create pull request" + echo "This could be due to:" + echo " - Missing or invalid GitHub App credentials" + echo " - Insufficient permissions" + echo " - Network issues" + echo "Please check the logs above for more details" + exit 1 + + - name: Workflow summary + if: always() + run: | + echo "## Workflow Execution Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ steps.validate_branch.outcome }}" == "success" ]; then + echo "✅ Branch validation: **${{ steps.validate_branch.outputs.branch }}**" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Branch validation failed" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ steps.version.outcome }}" == "success" ]; then + echo "✅ Version determination: **${{ steps.version.outputs.version }}**" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Version determination failed" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ steps.create_pr.outcome }}" == "success" ]; then + echo "✅ Pull request created: ${{ steps.create_pr.outputs.pull-request-url }}" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Pull request creation failed" >> $GITHUB_STEP_SUMMARY + fi diff --git a/build.gradle b/build.gradle index 6fd3227fac..d610c8027d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,15 @@ /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. */ import com.github.jk1.license.render.TextReportRenderer +import org.opensearch.dataprepper.gradle.release.PrintCurrentVersionTask +import org.opensearch.dataprepper.gradle.release.PrintNextVersionTask +import org.opensearch.dataprepper.gradle.release.UpdateVersionTask buildscript { dependencies { @@ -328,6 +335,21 @@ task generateThirdPartyReport(type: Copy) { generateThirdPartyReport.dependsOn(generateLicenseReport) } +tasks.register('printNextVersion', PrintNextVersionTask) { + description = 'Prints the next release version based on the current version in gradle.properties' + group = 'release' +} + +tasks.register('printCurrentVersion', PrintCurrentVersionTask) { + description = 'Prints the current version from gradle.properties' + group = 'release' +} + +tasks.register('updateToNextVersion', UpdateVersionTask) { + description = 'Updates gradle.properties to the next release version' + group = 'release' +} + // Utility method to recursively collect tasks from all projects and subprojects def collectTasksRecursively(Project project, String taskName) { def tasks = [] diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index d54585f626..517689adf6 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -15,4 +15,10 @@ repositories { dependencies { implementation('me.champeau.jmh:me.champeau.jmh.gradle.plugin:0.7.2') + testImplementation('org.junit.jupiter:junit-jupiter:5.9.3') + testImplementation('org.hamcrest:hamcrest:2.2') +} + +test { + useJUnitPlatform() } diff --git a/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintCurrentVersionTask.java b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintCurrentVersionTask.java new file mode 100644 index 0000000000..65a79baef2 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintCurrentVersionTask.java @@ -0,0 +1,24 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.dataprepper.gradle.release; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; + +/** + * Gradle task that prints the current version from gradle.properties. + */ +public class PrintCurrentVersionTask extends DefaultTask { + + @TaskAction + public void printCurrentVersion() { + System.out.println(getProject().getVersion().toString()); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintNextVersionTask.java b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintNextVersionTask.java new file mode 100644 index 0000000000..1aea82cdc5 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/PrintNextVersionTask.java @@ -0,0 +1,29 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.dataprepper.gradle.release; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.TaskAction; + +/** + * Gradle task that prints the next release version based on the current version in gradle.properties. + */ +public class PrintNextVersionTask extends DefaultTask { + + /** + * Executes the task to determine and print the next version. + */ + @TaskAction + public void printNextVersion() { + final String currentVersion = getProject().getVersion().toString(); + final String nextVersion = VersionCalculator.determineNextVersion(currentVersion); + System.out.println(nextVersion); + } +} diff --git a/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/UpdateVersionTask.java b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/UpdateVersionTask.java new file mode 100644 index 0000000000..f9071387a8 --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/UpdateVersionTask.java @@ -0,0 +1,37 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.dataprepper.gradle.release; + +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.tasks.TaskAction; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * Gradle task that updates the version in gradle.properties to the next release version. + */ +public class UpdateVersionTask extends DefaultTask { + + @TaskAction + public void updateVersion() { + final String currentVersion = getProject().getVersion().toString(); + final String nextVersion = VersionCalculator.determineNextVersion(currentVersion); + final Path propertiesFile = getProject().getRootDir().toPath().resolve("gradle.properties"); + + try { + VersionUpdater.updateVersionInFile(propertiesFile, nextVersion); + getLogger().lifecycle("Updated version from {} to {}", currentVersion, nextVersion); + } catch (IOException e) { + throw new GradleException("Failed to update gradle.properties", e); + } + } +} diff --git a/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionCalculator.java b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionCalculator.java new file mode 100644 index 0000000000..42553531ff --- /dev/null +++ b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionCalculator.java @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.dataprepper.gradle.release; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Utility class for calculating the next release version from a current version string. + */ +class VersionCalculator { + private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)(-SNAPSHOT)?$"); + + /** + * Determines the next release version from the current version. + *
+ * If the current version ends with "-SNAPSHOT", the suffix is removed.
+ * If the current version does not end with "-SNAPSHOT", the patch version is incremented by 1.
+ *
+ * @param currentVersion The current version string (e.g., "2.6.2-SNAPSHOT" or "2.6.2")
+ * @return The next release version (e.g., "2.6.2" or "2.6.3")
+ * @throws IllegalArgumentException if version format is invalid
+ * @throws NullPointerException if currentVersion is null
+ */
+ static String determineNextVersion(String currentVersion) {
+ if (currentVersion == null) {
+ throw new NullPointerException("currentVersion cannot be null");
+ }
+
+ Matcher matcher = VERSION_PATTERN.matcher(currentVersion);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(
+ "Invalid version format: " + currentVersion +
+ ". Expected format: {major}.{minor}.{patch}[-SNAPSHOT]"
+ );
+ }
+
+ String major = matcher.group(1);
+ String minor = matcher.group(2);
+ String patch = matcher.group(3);
+ String snapshot = matcher.group(4);
+
+ // If version has -SNAPSHOT suffix, remove it
+ if (snapshot != null) {
+ return major + "." + minor + "." + patch;
+ }
+
+ // Otherwise, increment patch version
+ int patchNumber = Integer.parseInt(patch);
+ int nextPatch = patchNumber + 1;
+ return major + "." + minor + "." + nextPatch;
+ }
+}
diff --git a/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionUpdater.java b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionUpdater.java
new file mode 100644
index 0000000000..d5369b31f3
--- /dev/null
+++ b/buildSrc/src/main/java/org/opensearch/dataprepper/gradle/release/VersionUpdater.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+package org.opensearch.dataprepper.gradle.release;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Utility class for updating version in gradle.properties files.
+ */
+class VersionUpdater {
+
+ /**
+ * Updates the version line in a gradle.properties file.
+ *
+ * @param propertiesFile Path to the gradle.properties file
+ * @param newVersion The new version to set
+ * @throws IOException if file operations fail
+ */
+ static void updateVersionInFile(final Path propertiesFile, final String newVersion) throws IOException {
+ final List