Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions .github/workflows/release-prepare-branch.yml
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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 = []
Expand Down
6 changes: 6 additions & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* 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;
}
}
Loading
Loading