diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d82eb4e Binary files /dev/null and b/.DS_Store differ diff --git a/.github/prompts/maven-upgrade-java-spring-boot.prompt.md b/.github/prompts/maven-upgrade-java-spring-boot.prompt.md index 59db346..f2eee90 100644 --- a/.github/prompts/maven-upgrade-java-spring-boot.prompt.md +++ b/.github/prompts/maven-upgrade-java-spring-boot.prompt.md @@ -1,23 +1,24 @@ --- name: "maven-upgrade-java-spring-boot" description: "GitHub copilot prompt to upgrade java and spring-boot" -# argument-hint: "Optional arguments (for example: name=Paul)" +argument-hint: "application-id (required), module-name (optional), java-version (optional), spring-boot-version (optional)" agent: agent -model: gpt-5.3-codex -tools: ['editFiles'] +model: GPT-5.3-Codex (copilot) +tools: ['codebase', 'search', 'editFiles', 'runCommands'] authors: ["paul58914080@gmail.com"] --- -[//]: # (todo: Describe the persona and goal of this prompt) +You are a Maven Java and Spring Boot upgrade automation agent. Your goal is to +validate upgrade inputs, orchestrate a safe and traceable pre-flight and +OpenRewrite execution workflow, and produce run artifacts under a single +timestamped directory per invocation. ## Execution order -[//]: # (todo: Describe the execution order of the prompt, for example: "The agent will first do X, then Y, and finally Z.") +1. Start the skill `.github/skills/maven-upgrade-java-spring-boot/SKILL.md`. Do not block waiting for completion of any scripts. The skill will manage the overall workflow and timing. +2. Once the orchestrator command returns successfully after pre-flight, stop immediately and exit. Do not run any additional commands. ## Hard constraints -[//]: # (todo: Describe the hard constraints of the prompt, for example: "The agent must always do X, and never do Y.") - -## Output expectations - -[//]: # (todo: Describe the output expectations of the prompt, for example: "The agent should output X, and never output Y.") +1. All scripts must be executed in the context of the root directory. +2. Do not inspect logs, artifacts, process status, or repository status after the orchestrator returns. \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/SKILL.md b/.github/skills/maven-upgrade-java-spring-boot/SKILL.md new file mode 100644 index 0000000..12b93dd --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/SKILL.md @@ -0,0 +1,48 @@ +--- +name: "maven-upgrade-java-spring-boot" +description: "Skill to upgrade java and spring-boot" +argument-hint: "application-id (required), module-name (optional), java-version (optional), spring-boot-version (optional)" +--- + +# Maven Upgrade Java Spring Boot Skill + +This skill orchestrates Maven-based Java and Spring Boot upgrades with +validated inputs, timestamped run artifacts, and non-blocking OpenRewrite +execution. + +## Inputs + +- `application-id` (required) +- `module-name` (optional, defaults to `.`) +- `java-version` (optional, defaults to `25`) +- `spring-boot-version` (optional, defaults to `4.0`) +- `timestamp` (optional externally, otherwise computed once by orchestrator in + format `yyyyMMdd-HHmmss`) + +## Execution + +Run this from the repository root after replacing the placeholders with your actual values: + +```bash +bash .github/skills/maven-upgrade-java-spring-boot/scripts/orchestrator.sh \ + --application-id "" \ + --module-name "" \ + --java-version "" \ + --spring-boot-version "" +``` + +## Suggested Invocation + +```bash +bash .github/skills/maven-upgrade-java-spring-boot/scripts/orchestrator.sh \ + --application-id "my-app" \ + --module-name "." \ + --java-version "25" \ + --spring-boot-version "4.0" +``` + +## Hard constraints + +Strict mode: execute only requested Execution, no extra validation, no assumptions, no follow-up actions without my explicit go-ahead. + +Post-execution stop rule: once the orchestrator command returns, exit immediately. Do not run artifact checks, log reads, process polling, or any additional commands unless explicitly requested. \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/artifact-coordinates.txt b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/artifact-coordinates.txt new file mode 100644 index 0000000..ea5eaab --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/artifact-coordinates.txt @@ -0,0 +1 @@ +org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/rewrite.yml b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/rewrite.yml new file mode 100644 index 0000000..810998b --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/17/rewrite.yml @@ -0,0 +1,6 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.cortexaidevkit.java.UpgradeJava17 +displayName: Upgrade Java to 17 +recipeList: + - org.openrewrite.java.migrate.UpgradeToJava17 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/artifact-coordinates.txt b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/artifact-coordinates.txt new file mode 100644 index 0000000..ea5eaab --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/artifact-coordinates.txt @@ -0,0 +1 @@ +org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/rewrite.yml b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/rewrite.yml new file mode 100644 index 0000000..211670d --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/21/rewrite.yml @@ -0,0 +1,6 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.cortexaidevkit.java.UpgradeJava21 +displayName: Upgrade Java to 21 +recipeList: + - org.openrewrite.java.migrate.UpgradeToJava21 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/artifact-coordinates.txt b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/artifact-coordinates.txt new file mode 100644 index 0000000..ea5eaab --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/artifact-coordinates.txt @@ -0,0 +1 @@ +org.openrewrite.recipe:rewrite-migrate-java:3.36.0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/rewrite.yml b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/rewrite.yml new file mode 100644 index 0000000..d1c8b2d --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/java/25/rewrite.yml @@ -0,0 +1,6 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.cortexaidevkit.java.UpgradeJava25 +displayName: Upgrade Java to 25 +recipeList: + - org.openrewrite.java.migrate.UpgradeToJava25 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/artifact-coordinates.txt b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/artifact-coordinates.txt new file mode 100644 index 0000000..62ea1d2 --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/artifact-coordinates.txt @@ -0,0 +1 @@ +org.openrewrite.recipe:rewrite-spring:6.32.0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/rewrite.yml b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/rewrite.yml new file mode 100644 index 0000000..7ca06ad --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/3_5/rewrite.yml @@ -0,0 +1,6 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.cortexaidevkit.spring.boot.UpgradeSpringBoot_4_0 +displayName: Migrate to Spring Boot 4.0 +recipeList: + - org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/artifact-coordinates.txt b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/artifact-coordinates.txt new file mode 100644 index 0000000..62ea1d2 --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/artifact-coordinates.txt @@ -0,0 +1 @@ +org.openrewrite.recipe:rewrite-spring:6.32.0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/rewrite.yml b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/rewrite.yml new file mode 100644 index 0000000..7ca06ad --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/resources/rewrite/spring-boot/4_0/rewrite.yml @@ -0,0 +1,6 @@ +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.cortexaidevkit.spring.boot.UpgradeSpringBoot_4_0 +displayName: Migrate to Spring Boot 4.0 +recipeList: + - org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0 \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/scripts/common.sh b/.github/skills/maven-upgrade-java-spring-boot/scripts/common.sh new file mode 100755 index 0000000..f8746c8 --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/scripts/common.sh @@ -0,0 +1,286 @@ +#!/usr/bin/env bash + +set -euo pipefail + +timestamp_now() { + date +"%Y%m%d-%H%M%S" +} + +normalize_module_name() { + local value="${1:-}" + if [[ -z "$value" ]]; then + echo "." + else + echo "$value" + fi +} + +normalize_java_version() { + local value="${1:-}" + if [[ -z "$value" ]]; then + echo "25" + else + echo "$value" + fi +} + +normalize_spring_boot_version() { + local value="${1:-}" + if [[ -z "$value" ]]; then + echo "4.0" + return + fi + + if [[ "$value" == "4" ]]; then + echo "4.0" + return + fi + + echo "$value" +} + +generate_run_id() { + if command -v uuidgen >/dev/null 2>&1; then + uuidgen + else + echo "run-$(timestamp_now)-$$" + fi +} + +json_escape() { + printf '%s' "${1:-}" | awk '{ + gsub(/\\/, "\\\\"); gsub(/"/, "\\\"") + gsub(/\t/, "\\t"); gsub(/\r/, "\\r") + printf "%s%s", sep, $0; sep = "\\n" + }' +} + +run_output_dir() { + local workspace_root="$1" + local timestamp="$2" + local module_name="$3" + local module_segment + module_segment="$(artifact_module_segment "$module_name")" + echo "$workspace_root/maven-upgrade-java-spring-boot/$timestamp/$module_segment" +} + +artifact_module_segment() { + local module_name="$1" + if [[ "$module_name" == "." ]]; then + echo "root" + else + echo "$module_name" + fi +} + +write_run_log() { + local run_log_file="$1" + local run_id="$2" + local timestamp="$3" + local application_id="$4" + local module_name="$5" + local java_version="$6" + local spring_boot_version="$7" + local overall_status="$8" + local orchestrator_status="$9" + local preflight_status="${10}" + local execute_rewrite_status="${11}" + local orchestrator_log="${12}" + local preflight_log="${13}" + local execute_rewrite_log="${14}" + + local tmp_file + tmp_file="$(mktemp)" + + cat >"$tmp_file" </dev/null 2>&1; then + jq -r --arg k "$key" '.[$k] // empty' "$file" 2>/dev/null || echo "" + return + fi + + awk -v key="$key" ' + $0 ~ "\\\"" key "\\\"" { + match($0, /: "[^"]*"/) + if (RSTART > 0) { + value = substr($0, RSTART + 3, RLENGTH - 4) + print value + exit + } + } + ' "$file" +} + +extract_status_value() { + local key="$1" + local file="$2" + + if [[ ! -f "$file" ]]; then + echo "" + return + fi + + if command -v jq >/dev/null 2>&1; then + jq -r --arg k "$key" '.status[$k] // empty' "$file" 2>/dev/null || echo "" + return + fi + + awk -v key="$key" ' + /"status"[[:space:]]*:[[:space:]]*\{/ { in_status = 1; next } + in_status && /\}/ { in_status = 0 } + in_status && $0 ~ "\\\"" key "\\\"" { + match($0, /: "[^"]*"/) + if (RSTART > 0) { + value = substr($0, RSTART + 3, RLENGTH - 4) + print value + exit + } + } + ' "$file" +} + +extract_log_value() { + local key="$1" + local file="$2" + + if [[ ! -f "$file" ]]; then + echo "" + return + fi + + if command -v jq >/dev/null 2>&1; then + jq -r --arg k "$key" '.log[$k] // empty' "$file" 2>/dev/null || echo "" + return + fi + + awk -v key="$key" ' + /"log"[[:space:]]*:[[:space:]]*\{/ { in_log = 1; next } + in_log && /\}/ { in_log = 0 } + in_log && $0 ~ "\\\"" key "\\\"" { + match($0, /: "[^"]*"/) + if (RSTART > 0) { + value = substr($0, RSTART + 3, RLENGTH - 4) + print value + exit + } + } + ' "$file" +} + +extract_input_value() { + local key="$1" + local file="$2" + + if [[ ! -f "$file" ]]; then + echo "" + return + fi + + if command -v jq >/dev/null 2>&1; then + jq -r --arg k "$key" '.inputs[$k] // empty' "$file" 2>/dev/null || echo "" + return + fi + + awk -v key="$key" ' + /"inputs"[[:space:]]*:[[:space:]]*\{/ { in_input = 1; next } + in_input && /\}/ { in_input = 0 } + in_input && $0 ~ "\\\"" key "\\\"" { + match($0, /: "[^"]*"/) + if (RSTART > 0) { + value = substr($0, RSTART + 3, RLENGTH - 4) + print value + exit + } + } + ' "$file" +} + +update_run_log_stage() { + local run_log_file="$1" + local stage_key="$2" + local stage_status="$3" + local overall_status="$4" + + local run_id timestamp application_id module_name java_version spring_boot_version + local current_orchestrator current_preflight current_execute + local orchestrator_log preflight_log execute_rewrite_log + + run_id="$(extract_json_value "runId" "$run_log_file")" + timestamp="$(extract_json_value "timestamp" "$run_log_file")" + application_id="$(extract_input_value "application-id" "$run_log_file")" + module_name="$(extract_input_value "module-name" "$run_log_file")" + java_version="$(extract_input_value "java-version" "$run_log_file")" + spring_boot_version="$(extract_input_value "spring-boot-version" "$run_log_file")" + current_orchestrator="$(extract_status_value "orchestrator" "$run_log_file")" + current_preflight="$(extract_status_value "preflight" "$run_log_file")" + current_execute="$(extract_status_value "execute-rewrite" "$run_log_file")" + orchestrator_log="$(extract_log_value "orchestrator" "$run_log_file")" + preflight_log="$(extract_log_value "preflight" "$run_log_file")" + execute_rewrite_log="$(extract_log_value "execute-rewrite" "$run_log_file")" + + case "$stage_key" in + orchestrator) + current_orchestrator="$stage_status" + ;; + preflight) + current_preflight="$stage_status" + ;; + execute-rewrite) + current_execute="$stage_status" + ;; + *) + echo "Unsupported stage key: $stage_key" >&2 + return 1 + ;; + esac + + write_run_log \ + "$run_log_file" \ + "$run_id" \ + "$timestamp" \ + "$application_id" \ + "$module_name" \ + "$java_version" \ + "$spring_boot_version" \ + "$overall_status" \ + "$current_orchestrator" \ + "$current_preflight" \ + "$current_execute" \ + "$orchestrator_log" \ + "$preflight_log" \ + "$execute_rewrite_log" +} \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/scripts/execute-rewrite.sh b/.github/skills/maven-upgrade-java-spring-boot/scripts/execute-rewrite.sh new file mode 100755 index 0000000..9c66dfe --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/scripts/execute-rewrite.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +APPLICATION_ID="" +MODULE_NAME="" +JAVA_VERSION="" +SPRING_BOOT_VERSION="" +TIMESTAMP="" +RUN_ID="" +WORKSPACE_ROOT="$(pwd)" + +while [[ $# -gt 0 ]]; do + case "$1" in + --application-id) + APPLICATION_ID="${2:-}" + shift 2 + ;; + --module-name) + MODULE_NAME="${2:-}" + shift 2 + ;; + --java-version) + JAVA_VERSION="${2:-}" + shift 2 + ;; + --spring-boot-version) + SPRING_BOOT_VERSION="${2:-}" + shift 2 + ;; + --timestamp) + TIMESTAMP="${2:-}" + shift 2 + ;; + --run-id) + RUN_ID="${2:-}" + shift 2 + ;; + --workspace-root) + WORKSPACE_ROOT="${2:-}" + shift 2 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +MODULE_NAME="$(normalize_module_name "$MODULE_NAME")" +JAVA_VERSION="$(normalize_java_version "$JAVA_VERSION")" +SPRING_BOOT_VERSION="$(normalize_spring_boot_version "$SPRING_BOOT_VERSION")" + +if [[ -z "$TIMESTAMP" ]]; then + TIMESTAMP="$(timestamp_now)" +fi + +RUN_DIR="$(run_output_dir "$WORKSPACE_ROOT" "$TIMESTAMP" "$MODULE_NAME")" +mkdir -p "$RUN_DIR" + +RUN_LOG="$RUN_DIR/run.log" +EXECUTE_REWRITE_LOG="$RUN_DIR/execute-rewrite.log" +PID_FILE="$RUN_DIR/execute-rewrite.pid" +REWRITE_COPY_FILE="$RUN_DIR/rewrite.yml" +REWRITE_MAVEN_PLUGIN_VERSION="6.40.0" +WORKSPACE_REWRITE_FILE="$WORKSPACE_ROOT/rewrite.yml" + +log_execute_rewrite_line() { + echo "[$(date +"%Y-%m-%d %H:%M:%S")] $1" >>"$EXECUTE_REWRITE_LOG" +} + +log_execute_rewrite_command() { + local label="$1" + shift + + log_execute_rewrite_line "$label" + for argument in "$@"; do + printf ' %q\n' "$argument" >>"$EXECUTE_REWRITE_LOG" + done +} + +GENERATE_REWRITE_SCRIPT="$SCRIPT_DIR/generate-rewrite-config.sh" +if [[ ! -x "$GENERATE_REWRITE_SCRIPT" ]]; then + echo "generate rewrite script is missing or not executable: $GENERATE_REWRITE_SCRIPT" >&2 + exit 1 +fi + +REWRITE_FLAGS="$($GENERATE_REWRITE_SCRIPT \ + --java-version "$JAVA_VERSION" \ + --spring-boot-version "$SPRING_BOOT_VERSION" \ + --workspace-root "$WORKSPACE_ROOT")" + +if [[ -z "$REWRITE_FLAGS" ]]; then + echo "failed to generate rewrite flags" >&2 + exit 1 +fi + +if [[ ! -f "$WORKSPACE_REWRITE_FILE" ]]; then + echo "generated rewrite file not found: $WORKSPACE_REWRITE_FILE" >&2 + exit 1 +fi + +cp "$WORKSPACE_REWRITE_FILE" "$REWRITE_COPY_FILE" + +read -r -a REWRITE_FLAGS_ARRAY <<< "$REWRITE_FLAGS" + +if [[ "$MODULE_NAME" == "." ]]; then + MVN_CMD=(mvn -B -U org.openrewrite.maven:rewrite-maven-plugin:$REWRITE_MAVEN_PLUGIN_VERSION:run "${REWRITE_FLAGS_ARRAY[@]}") +else + MVN_CMD=(mvn -B -pl "$MODULE_NAME" -am org.openrewrite.maven:rewrite-maven-plugin:$REWRITE_MAVEN_PLUGIN_VERSION:run "${REWRITE_FLAGS_ARRAY[@]}") +fi + +log_execute_rewrite_line "execute-rewrite launch" +log_execute_rewrite_line "application-id=$APPLICATION_ID" +log_execute_rewrite_line "module-name=$MODULE_NAME" +log_execute_rewrite_line "java-version=$JAVA_VERSION" +log_execute_rewrite_line "spring-boot-version=$SPRING_BOOT_VERSION" +log_execute_rewrite_line "rewrite-file=$REWRITE_COPY_FILE" +log_execute_rewrite_line "rewrite-flags:" +printf ' %s\n' "$REWRITE_FLAGS" >>"$EXECUTE_REWRITE_LOG" +log_execute_rewrite_command "command:" "${MVN_CMD[@]}" + +( + set +e + cleanup_generated_rewrite() { + rm -f "$WORKSPACE_REWRITE_FILE" + } + trap cleanup_generated_rewrite EXIT + + cd "$WORKSPACE_ROOT" || exit 1 + "${MVN_CMD[@]}" >>"$EXECUTE_REWRITE_LOG" 2>&1 + rewrite_exit_code=$? + + if [[ -f "$RUN_LOG" ]]; then + if [[ $rewrite_exit_code -eq 0 ]]; then + update_run_log_stage "$RUN_LOG" "execute-rewrite" "SUCCESS" "COMPLETED" || true + else + update_run_log_stage "$RUN_LOG" "execute-rewrite" "FAILED" "FAILED" || true + fi + fi + + log_execute_rewrite_line "execute-rewrite finished with code=$rewrite_exit_code" +) & + +bg_pid=$! +echo "$bg_pid" >"$PID_FILE" +log_execute_rewrite_line "execute-rewrite started in background pid=$bg_pid" \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/scripts/generate-rewrite-config.sh b/.github/skills/maven-upgrade-java-spring-boot/scripts/generate-rewrite-config.sh new file mode 100755 index 0000000..3baab1b --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/scripts/generate-rewrite-config.sh @@ -0,0 +1,242 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RESOURCE_ROOT="$SCRIPT_DIR/../resources/rewrite" +WORKSPACE_ROOT="$(pwd)" +OUTPUT_FILE="" +JAVA_VERSION="" +SPRING_BOOT_VERSION="" + +usage() { + cat <<'EOF' +Usage: + generate-rewrite-config.sh --java-version <17|21|25> --spring-boot-version <3.5|4.0> [--workspace-root ] [--output-file ] + +Description: + 1) Reads rewrite resources for the selected Java and Spring Boot versions. + 2) Generates a combined rewrite.yml at the repository root (or --output-file). + 3) Prints a Maven flags string: + -Drewrite.activeRecipes= -Drewrite.recipeArtifactCoordinates= +EOF +} + +trim() { + local value="$1" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + printf '%s' "$value" +} + +normalize_spring_boot_dir() { + local value="$1" + case "$value" in + 3.5) + echo "3_5" + ;; + 4.0|4) + echo "4_0" + ;; + *) + echo "" + ;; + esac +} + +get_recipe_name() { + local file="$1" + awk '/^name:[[:space:]]+/ {print $2; exit}' "$file" +} + +dedupe_documents_by_name() { + local input_file="$1" + local output_file="$2" + + awk ' + function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s } + function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s } + function trim(s) { return rtrim(ltrim(s)) } + + BEGIN { + RS="---[ \t\r\n]*\n" + ORS="" + } + + { + block = trim($0) + if (block == "") { + next + } + + recipe_name = "" + n = split(block, lines, /\n/) + for (i = 1; i <= n; i++) { + if (lines[i] ~ /^name:[ \t]+/) { + recipe_name = lines[i] + sub(/^name:[ \t]+/, "", recipe_name) + recipe_name = trim(recipe_name) + break + } + } + + if (recipe_name == "") { + key = block + } else { + key = "name::" recipe_name + } + + if (!(key in seen)) { + seen[key] = 1 + print "---\n" block "\n\n" + } + } + ' "$input_file" > "$output_file" +} + +build_unique_coordinates_csv() { + local java_coords_file="$1" + local spring_coords_file="$2" + + awk -F',' ' + function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s } + function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s } + function trim(s) { return rtrim(ltrim(s)) } + + { + for (i = 1; i <= NF; i++) { + token = trim($i) + if (token != "" && !(token in seen)) { + seen[token] = 1 + ordered[++count] = token + } + } + } + + END { + for (i = 1; i <= count; i++) { + printf "%s", ordered[i] + if (i < count) { + printf "," + } + } + } + ' "$java_coords_file" "$spring_coords_file" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --java-version) + JAVA_VERSION="${2:-}" + shift 2 + ;; + --spring-boot-version) + SPRING_BOOT_VERSION="${2:-}" + shift 2 + ;; + --workspace-root) + WORKSPACE_ROOT="${2:-}" + shift 2 + ;; + --output-file) + OUTPUT_FILE="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [[ -z "$JAVA_VERSION" || -z "$SPRING_BOOT_VERSION" ]]; then + echo "Both --java-version and --spring-boot-version are required." >&2 + usage >&2 + exit 1 +fi + +case "$JAVA_VERSION" in + 17|21|25) + ;; + *) + echo "Unsupported java version: $JAVA_VERSION (expected 17, 21, or 25)." >&2 + exit 1 + ;; +esac + +SPRING_BOOT_DIR="$(normalize_spring_boot_dir "$SPRING_BOOT_VERSION")" +if [[ -z "$SPRING_BOOT_DIR" ]]; then + echo "Unsupported spring boot version: $SPRING_BOOT_VERSION (expected 3.5 or 4.0)." >&2 + exit 1 +fi + +if [[ "$SPRING_BOOT_VERSION" == "4" ]]; then + SPRING_BOOT_VERSION="4.0" +fi + +JAVA_DIR="$RESOURCE_ROOT/java/$JAVA_VERSION" +SPRING_DIR="$RESOURCE_ROOT/spring-boot/$SPRING_BOOT_DIR" + +JAVA_REWRITE_FILE="$JAVA_DIR/rewrite.yml" +SPRING_REWRITE_FILE="$SPRING_DIR/rewrite.yml" +JAVA_COORDS_FILE="$JAVA_DIR/artifact-coordinates.txt" +SPRING_COORDS_FILE="$SPRING_DIR/artifact-coordinates.txt" + +for path in "$JAVA_REWRITE_FILE" "$SPRING_REWRITE_FILE" "$JAVA_COORDS_FILE" "$SPRING_COORDS_FILE"; do + if [[ ! -f "$path" ]]; then + echo "Required file not found: $path" >&2 + exit 1 + fi +done + +if [[ -z "$OUTPUT_FILE" ]]; then + OUTPUT_FILE="$WORKSPACE_ROOT/rewrite.yml" +fi + +JAVA_RECIPE_NAME="$(get_recipe_name "$JAVA_REWRITE_FILE")" +SPRING_RECIPE_NAME="$(get_recipe_name "$SPRING_REWRITE_FILE")" + +if [[ -z "$JAVA_RECIPE_NAME" || -z "$SPRING_RECIPE_NAME" ]]; then + echo "Failed to parse recipe names from source rewrite.yml files." >&2 + exit 1 +fi + +SPRING_BOOT_UNDERSCORE="${SPRING_BOOT_VERSION//./_}" +COMBINED_RECIPE_NAME="org.cortexaidevkit.java.spring.boot.UpgradeJava${JAVA_VERSION}SpringBoot_${SPRING_BOOT_UNDERSCORE}" + +TMP_INPUT="$(mktemp)" +TMP_OUTPUT="$(mktemp)" +trap 'rm -f "$TMP_INPUT" "$TMP_OUTPUT"' EXIT + +{ + cat "$JAVA_REWRITE_FILE" + echo + cat "$SPRING_REWRITE_FILE" + echo + cat < "$TMP_INPUT" + +dedupe_documents_by_name "$TMP_INPUT" "$TMP_OUTPUT" +mv "$TMP_OUTPUT" "$OUTPUT_FILE" + +COORDINATES_CSV="$(build_unique_coordinates_csv "$JAVA_COORDS_FILE" "$SPRING_COORDS_FILE")" + +if [[ -z "$COORDINATES_CSV" ]]; then + echo "Failed to resolve rewrite recipe artifact coordinates." >&2 + exit 1 +fi + +echo "-Drewrite.activeRecipes=$COMBINED_RECIPE_NAME -Drewrite.recipeArtifactCoordinates=$COORDINATES_CSV" diff --git a/.github/skills/maven-upgrade-java-spring-boot/scripts/orchestrator.sh b/.github/skills/maven-upgrade-java-spring-boot/scripts/orchestrator.sh new file mode 100755 index 0000000..7b99a8a --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/scripts/orchestrator.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +APPLICATION_ID="" +MODULE_NAME="" +JAVA_VERSION="" +SPRING_BOOT_VERSION="" +TIMESTAMP="" +WORKSPACE_ROOT="$(pwd)" + +while [[ $# -gt 0 ]]; do + case "$1" in + --application-id) + APPLICATION_ID="${2:-}" + shift 2 + ;; + --module-name) + MODULE_NAME="${2:-}" + shift 2 + ;; + --java-version) + JAVA_VERSION="${2:-}" + shift 2 + ;; + --spring-boot-version) + SPRING_BOOT_VERSION="${2:-}" + shift 2 + ;; + --timestamp) + TIMESTAMP="${2:-}" + shift 2 + ;; + --workspace-root) + WORKSPACE_ROOT="${2:-}" + shift 2 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +if [[ -z "$APPLICATION_ID" ]]; then + echo "application-id is required" >&2 + exit 1 +fi + +MODULE_NAME="$(normalize_module_name "$MODULE_NAME")" +JAVA_VERSION="$(normalize_java_version "$JAVA_VERSION")" +SPRING_BOOT_VERSION="$(normalize_spring_boot_version "$SPRING_BOOT_VERSION")" + +if [[ -z "$TIMESTAMP" ]]; then + TIMESTAMP="$(timestamp_now)" +fi + +RUN_ID="$(generate_run_id)" +RUN_DIR="$(run_output_dir "$WORKSPACE_ROOT" "$TIMESTAMP" "$MODULE_NAME")" +mkdir -p "$RUN_DIR" + +RUN_LOG="$RUN_DIR/run.log" +ORCHESTRATOR_LOG="$RUN_DIR/orchestrator.log" +PREFLIGHT_LOG="$RUN_DIR/preflight.log" +EXECUTE_REWRITE_LOG="$RUN_DIR/execute-rewrite.log" +MODULE_SEGMENT="$(artifact_module_segment "$MODULE_NAME")" + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] orchestrator started runId=$RUN_ID" >>"$ORCHESTRATOR_LOG" + +REL_ORCHESTRATOR_LOG="maven-upgrade-java-spring-boot/$TIMESTAMP/$MODULE_SEGMENT/orchestrator.log" +REL_PREFLIGHT_LOG="maven-upgrade-java-spring-boot/$TIMESTAMP/$MODULE_SEGMENT/preflight.log" +REL_EXECUTE_REWRITE_LOG="maven-upgrade-java-spring-boot/$TIMESTAMP/$MODULE_SEGMENT/execute-rewrite.log" + +write_run_log \ + "$RUN_LOG" \ + "$RUN_ID" \ + "$TIMESTAMP" \ + "$APPLICATION_ID" \ + "$MODULE_NAME" \ + "$JAVA_VERSION" \ + "$SPRING_BOOT_VERSION" \ + "STARTED" \ + "FAILED" \ + "FAILED" \ + "FAILED" \ + "$REL_ORCHESTRATOR_LOG" \ + "$REL_PREFLIGHT_LOG" \ + "$REL_EXECUTE_REWRITE_LOG" + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] invoking pre-flight" >>"$ORCHESTRATOR_LOG" +if ! bash "$SCRIPT_DIR/pre-flight.sh" \ + --application-id "$APPLICATION_ID" \ + --module-name "$MODULE_NAME" \ + --java-version "$JAVA_VERSION" \ + --spring-boot-version "$SPRING_BOOT_VERSION" \ + --timestamp "$TIMESTAMP" \ + --workspace-root "$WORKSPACE_ROOT"; then + echo "[$(date +"%Y-%m-%d %H:%M:%S")] pre-flight failed" >>"$ORCHESTRATOR_LOG" + write_run_log \ + "$RUN_LOG" \ + "$RUN_ID" \ + "$TIMESTAMP" \ + "$APPLICATION_ID" \ + "$MODULE_NAME" \ + "$JAVA_VERSION" \ + "$SPRING_BOOT_VERSION" \ + "FAILED" \ + "FAILED" \ + "FAILED" \ + "FAILED" \ + "$REL_ORCHESTRATOR_LOG" \ + "$REL_PREFLIGHT_LOG" \ + "$REL_EXECUTE_REWRITE_LOG" + exit 1 +fi + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] pre-flight succeeded" >>"$ORCHESTRATOR_LOG" + +write_run_log \ + "$RUN_LOG" \ + "$RUN_ID" \ + "$TIMESTAMP" \ + "$APPLICATION_ID" \ + "$MODULE_NAME" \ + "$JAVA_VERSION" \ + "$SPRING_BOOT_VERSION" \ + "IN-PROGRESS" \ + "SUCCESS" \ + "SUCCESS" \ + "FAILED" \ + "$REL_ORCHESTRATOR_LOG" \ + "$REL_PREFLIGHT_LOG" \ + "$REL_EXECUTE_REWRITE_LOG" + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] launching execute-rewrite" >>"$ORCHESTRATOR_LOG" +if ! bash "$SCRIPT_DIR/execute-rewrite.sh" \ + --application-id "$APPLICATION_ID" \ + --module-name "$MODULE_NAME" \ + --java-version "$JAVA_VERSION" \ + --spring-boot-version "$SPRING_BOOT_VERSION" \ + --timestamp "$TIMESTAMP" \ + --run-id "$RUN_ID" \ + --workspace-root "$WORKSPACE_ROOT"; then + echo "[$(date +"%Y-%m-%d %H:%M:%S")] execute-rewrite launch failed" >>"$ORCHESTRATOR_LOG" + write_run_log \ + "$RUN_LOG" \ + "$RUN_ID" \ + "$TIMESTAMP" \ + "$APPLICATION_ID" \ + "$MODULE_NAME" \ + "$JAVA_VERSION" \ + "$SPRING_BOOT_VERSION" \ + "FAILED" \ + "FAILED" \ + "SUCCESS" \ + "FAILED" \ + "$REL_ORCHESTRATOR_LOG" \ + "$REL_PREFLIGHT_LOG" \ + "$REL_EXECUTE_REWRITE_LOG" + exit 1 +fi + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] execute-rewrite launched in non-blocking mode" >>"$ORCHESTRATOR_LOG" + +write_run_log \ + "$RUN_LOG" \ + "$RUN_ID" \ + "$TIMESTAMP" \ + "$APPLICATION_ID" \ + "$MODULE_NAME" \ + "$JAVA_VERSION" \ + "$SPRING_BOOT_VERSION" \ + "IN-PROGRESS" \ + "SUCCESS" \ + "SUCCESS" \ + "SUCCESS" \ + "$REL_ORCHESTRATOR_LOG" \ + "$REL_PREFLIGHT_LOG" \ + "$REL_EXECUTE_REWRITE_LOG" + +echo "[$(date +"%Y-%m-%d %H:%M:%S")] orchestrator completed without waiting for rewrite" >>"$ORCHESTRATOR_LOG" \ No newline at end of file diff --git a/.github/skills/maven-upgrade-java-spring-boot/scripts/pre-flight.sh b/.github/skills/maven-upgrade-java-spring-boot/scripts/pre-flight.sh new file mode 100755 index 0000000..04295d1 --- /dev/null +++ b/.github/skills/maven-upgrade-java-spring-boot/scripts/pre-flight.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +APPLICATION_ID="" +MODULE_NAME="" +JAVA_VERSION="" +SPRING_BOOT_VERSION="" +TIMESTAMP="" +WORKSPACE_ROOT="$(pwd)" + +while [[ $# -gt 0 ]]; do + case "$1" in + --application-id) + APPLICATION_ID="${2:-}" + shift 2 + ;; + --module-name) + MODULE_NAME="${2:-}" + shift 2 + ;; + --java-version) + JAVA_VERSION="${2:-}" + shift 2 + ;; + --spring-boot-version) + SPRING_BOOT_VERSION="${2:-}" + shift 2 + ;; + --timestamp) + TIMESTAMP="${2:-}" + shift 2 + ;; + --workspace-root) + WORKSPACE_ROOT="${2:-}" + shift 2 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 2 + ;; + esac +done + +MODULE_NAME="$(normalize_module_name "$MODULE_NAME")" +JAVA_VERSION="$(normalize_java_version "$JAVA_VERSION")" +SPRING_BOOT_VERSION="$(normalize_spring_boot_version "$SPRING_BOOT_VERSION")" + +if [[ -z "$TIMESTAMP" ]]; then + TIMESTAMP="$(timestamp_now)" +fi + +RUN_DIR="$(run_output_dir "$WORKSPACE_ROOT" "$TIMESTAMP" "$MODULE_NAME")" +mkdir -p "$RUN_DIR" + +PREFLIGHT_LOG="$RUN_DIR/preflight.log" +PREFLIGHT_ERROR_LOG="$RUN_DIR/pre-flight-error.log" + +log() { + echo "[$(date +"%Y-%m-%d %H:%M:%S")] $*" | tee -a "$PREFLIGHT_LOG" +} + +fail() { + local message="$1" + log "ERROR: $message" + echo "$message" >>"$PREFLIGHT_ERROR_LOG" + exit 1 +} + +log "Starting pre-flight validation" +log "application-id=$APPLICATION_ID module-name=$MODULE_NAME java-version=$JAVA_VERSION spring-boot-version=$SPRING_BOOT_VERSION" + +if [[ -z "$APPLICATION_ID" ]]; then + fail "application-id is required" +fi + +case "$JAVA_VERSION" in + 17|21|25) + ;; + *) + fail "java-version must be one of: 17, 21, 25" + ;; +esac + +case "$SPRING_BOOT_VERSION" in + 3.5|4.0) + ;; + *) + fail "spring-boot-version must be one of: 3.5, 4.0" + ;; +esac + +ROOT_POM="$WORKSPACE_ROOT/pom.xml" +if [[ ! -f "$ROOT_POM" ]]; then + fail "root pom.xml not found at $ROOT_POM" +fi + +if [[ "$MODULE_NAME" == "." ]]; then + TARGET_POM="$ROOT_POM" +else + TARGET_POM="$WORKSPACE_ROOT/$MODULE_NAME/pom.xml" + + if command -v xmllint >/dev/null 2>&1; then + MODULE_LIST="$(xmllint --xpath '//modules/module/text()' "$ROOT_POM" 2>/dev/null \ + | tr ' ' '\n' | grep -v '^$' || true)" + else + # awk fallback: handles simple single-line elements only + MODULE_LIST="$(awk ' + // { in_modules=1; next } + /<\/modules>/ { in_modules=0 } + in_modules { + gsub(/^[ \t]+|[ \t]+$/, "", $0) + if ($0 ~ //) { + gsub(/|<\/module>/, "", $0) + gsub(/^[ \t]+|[ \t]+$/, "", $0) + print $0 + } + } + ' "$ROOT_POM")" + fi + + if ! echo "$MODULE_LIST" | grep -Fxq "$MODULE_NAME"; then + fail "module-name '$MODULE_NAME' is not declared in root pom.xml " + fi +fi + +if [[ ! -f "$TARGET_POM" ]]; then + fail "target pom.xml not found at $TARGET_POM" +fi + +if ! grep -q ") at $TARGET_POM" +fi + +log "Pre-flight validation completed successfully" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ff7f85b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.idea +*.iml \ No newline at end of file