diff --git a/.claude/skills/port-to-jdk21/SKILL.md b/.claude/skills/port-to-jdk21/SKILL.md new file mode 100644 index 000000000..b652e97d4 --- /dev/null +++ b/.claude/skills/port-to-jdk21/SKILL.md @@ -0,0 +1,95 @@ +--- +name: port-to-jdk21 +description: Port changes from a stage-jdk8-based branch to a stage-based branch, upgrading Java 8 code to Java 21 +argument-hint: [source-branch] +allowed-tools: Bash, Read, Edit, Write, Grep, Glob +user-invocable: true +--- + +# Port Branch Changes from stage-jdk8 (Java 8) to stage (Java 21) + +Port changes from the source branch `$ARGUMENTS` (based on `stage-jdk8`) to a new branch based on `stage`. + +## Target Branch Naming + +The target branch name MUST be derived from the source branch name by removing the `_jdk8` suffix: +- Source: `CLIENT-1234_feature_jdk8` -> Target: `CLIENT-1234_feature` +- If the source branch doesn't have a `_jdk8` suffix, append `_jdk21` to create the target name: + - Source: `bugfix-something` -> Target: `bugfix-something_jdk21` + +## Steps + +1. **Validate** the source branch `$ARGUMENTS` exists and is based on `stage-jdk8`. +2. **Analyze** the commits on the source branch that are not on `stage-jdk8`: + ``` + git log --oneline stage-jdk8..$ARGUMENTS + ``` +3. **Create** the target branch off `stage`: + ``` + git checkout stage + git pull + git checkout -b + ``` +4. **Cherry-pick** the commits from the source branch onto the target branch. If there are conflicts, resolve them with the Java 21 adaptations listed below. +5. **Adapt** the code for Java 21 idioms (see adaptation rules below). +6. **Purge Maven cache** to avoid stale JDK 8 artifacts causing build failures: + ``` + mvn dependency:purge-local-repository \ + -DreResolve=true \ + -DactTransitively=false + ``` +7. **Build** to verify: + ``` + mvn clean install -U + ``` + +## Java 8 to Java 21 Adaptation Rules + +When porting code, apply these modernization transformations where appropriate: + +### Language Features +- **Traditional instanceof + cast** -> Use pattern matching for instanceof + ```java + // Java 8 + if (obj instanceof String) { String s = (String) obj; use(s); } + // Java 21 + if (obj instanceof String s) { use(s); } + ``` +- **Verbose switch statements** -> Consider switch expressions where cleaner + ```java + // Java 8 + String result; + switch (x) { + case 1: result = "one"; break; + case 2: result = "two"; break; + default: result = "other"; break; + } + // Java 21 + var result = switch (x) { + case 1 -> "one"; + case 2 -> "two"; + default -> "other"; + }; + ``` +- **String concatenation for multiline** -> Consider text blocks where appropriate +- **Explicit types for local variables** -> Use `var` where the type is obvious from context + +### API Differences +- **`Collections.unmodifiableList(Arrays.asList(...))`** -> Use `List.of(...)` +- **`Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))`** -> Use `Set.of(...)` +- **`string.trim().isEmpty()`** -> Use `string.isBlank()` +- **`!optional.isPresent()`** -> Use `optional.isEmpty()` +- **`.collect(Collectors.toList())`** -> Use `.toList()` +- **Traditional I/O patterns** -> Consider `Files.readString()` / `Files.writeString()` where appropriate + +### Project-Specific Differences +- The `stage` branch uses `aerospike-client-jdk21` artifact instead of `aerospike-client-jdk8` +- pom.xml uses `java.version=21` instead of `java.version=1.8` / `java.api=8` +- The `stage` branch may have additional methods (e.g., `estimateKeySize()`) not present in jdk8 +- Do NOT modify pom.xml java version settings - those are already correct on `stage` + +### Important +- **Be conservative with modernization**: Only modernize code that is part of the change being ported. Do not refactor surrounding unchanged code. +- Preserve the original commit messages when cherry-picking +- If a file does not exist on `stage`, check if the functionality lives in a different file or should be skipped +- After all changes, run `mvn clean install -U` and fix any compilation errors diff --git a/.claude/skills/port-to-jdk8/SKILL.md b/.claude/skills/port-to-jdk8/SKILL.md new file mode 100644 index 000000000..b1c22b53c --- /dev/null +++ b/.claude/skills/port-to-jdk8/SKILL.md @@ -0,0 +1,98 @@ +--- +name: port-to-jdk8 +description: Port changes from a stage-based branch to a stage-jdk8-based branch, adapting Java 21 code to Java 8 compatibility +argument-hint: [source-branch] +allowed-tools: Bash, Read, Edit, Write, Grep, Glob +user-invocable: true +--- + +# Port Branch Changes from stage (Java 21) to stage-jdk8 (Java 8) + +Port changes from the source branch `$ARGUMENTS` (based on `stage`) to a new branch based on `stage-jdk8`. + +## Target Branch Naming + +The target branch name MUST be derived from the source branch name by appending `_jdk8`: +- Source: `CLIENT-1234_feature` -> Target: `CLIENT-1234_feature_jdk8` +- Source: `bugfix-something` -> Target: `bugfix-something_jdk8` + +## Steps + +1. **Validate** the source branch `$ARGUMENTS` exists and is based on `stage`. +2. **Analyze** the commits on the source branch that are not on `stage`: + ``` + git log --oneline stage..$ARGUMENTS + ``` +3. **Create** the target branch off `stage-jdk8`: + ``` + git checkout stage-jdk8 + git pull + git checkout -b + ``` +4. **Cherry-pick** the commits from the source branch onto the target branch. If there are conflicts, resolve them with the Java 8 adaptations listed below. +5. **Adapt** the code for Java 8 compatibility (see adaptation rules below). +6. **Purge Maven cache** to avoid stale JDK 21 artifacts causing build failures: + ``` + mvn dependency:purge-local-repository \ + -DreResolve=true \ + -DactTransitively=false + ``` +7. **Build** to verify: + ``` + mvn clean install -U + ``` + +## Java 21 to Java 8 Adaptation Rules + +When porting code, apply these transformations: + +### Language Features +- **Records** -> Replace with traditional classes (private final fields, constructor, getters, equals/hashCode/toString) +- **Sealed classes/interfaces** -> Remove `sealed`, `permits`, `non-sealed` keywords; use regular class hierarchy +- **Pattern matching for instanceof** -> Use traditional instanceof + explicit cast + ```java + // Java 21 + if (obj instanceof String s) { use(s); } + // Java 8 + if (obj instanceof String) { String s = (String) obj; use(s); } + ``` +- **Switch expressions** -> Convert to switch statements or if-else chains + ```java + // Java 21 + var result = switch (x) { + case 1 -> "one"; + case 2 -> "two"; + default -> "other"; + }; + // Java 8 + String result; + switch (x) { + case 1: result = "one"; break; + case 2: result = "two"; break; + default: result = "other"; break; + } + ``` +- **Text blocks (triple quotes)** -> Use string concatenation or `String.join` +- **`var` keyword** -> Replace with explicit types +- **Enhanced switch with arrow syntax** -> Use traditional colon-case syntax + +### API Differences +- **`List.of()`, `Set.of()`, `Map.of()`** -> Use `Collections.unmodifiableList(Arrays.asList(...))`, `Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))`, or manual map construction +- **`String.isBlank()`** -> Use `string.trim().isEmpty()` +- **`String.strip()`** -> Use `string.trim()` +- **`String.repeat(n)`** -> Use a loop or `String.join("", Collections.nCopies(n, str))` +- **`Optional.isEmpty()`** -> Use `!optional.isPresent()` +- **`Stream.toList()`** -> Use `.collect(Collectors.toList())` +- **`Map.entry()`** -> Use `new AbstractMap.SimpleEntry<>()` +- **`Files.readString()` / `Files.writeString()`** -> Use traditional I/O with `BufferedReader`/`BufferedWriter` + +### Project-Specific Differences +- The `stage-jdk8` branch uses `aerospike-client-jdk8` artifact instead of `aerospike-client-jdk21` +- pom.xml uses `java.version=1.8` and `java.api=8` instead of `java.version=21` +- Some features like `estimateKeySize()` do not exist in the jdk8 branch +- Do NOT modify pom.xml java version settings - those are already correct on `stage-jdk8` + +### Important +- Preserve the original commit messages when cherry-picking +- If a file does not exist on `stage-jdk8`, check if the functionality lives in a different file or should be skipped +- After all changes, run `mvn clean install -U` and fix any compilation errors diff --git a/.cursor/commands/port-to-jdk21.md b/.cursor/commands/port-to-jdk21.md new file mode 100644 index 000000000..01e3b4179 --- /dev/null +++ b/.cursor/commands/port-to-jdk21.md @@ -0,0 +1,91 @@ +# Port Branch Changes from stage-jdk8 (Java 8) to stage (Java 21) + +Port changes from the source branch (based on `stage-jdk8`) to a new branch based on `stage`. + +## Instructions + +The user will provide a source branch name as an argument. Use it to perform the port. + +## Target Branch Naming + +The target branch name MUST be derived from the source branch name by removing the `_jdk8` suffix: +- Source: `CLIENT-1234_feature_jdk8` -> Target: `CLIENT-1234_feature` +- If the source branch doesn't have a `_jdk8` suffix, append `_jdk21` to create the target name: + - Source: `bugfix-something` -> Target: `bugfix-something_jdk21` + +## Steps + +1. **Validate** the source branch exists and is based on `stage-jdk8`. +2. **Analyze** the commits on the source branch that are not on `stage-jdk8`: + ``` + git log --oneline stage-jdk8.. + ``` +3. **Create** the target branch off `stage`: + ``` + git checkout stage + git pull + git checkout -b + ``` +4. **Cherry-pick** the commits from the source branch onto the target branch. If there are conflicts, resolve them with the Java 21 adaptations listed below. +5. **Adapt** the code for Java 21 idioms (see adaptation rules below). +6. **Purge Maven cache** to avoid stale JDK 8 artifacts causing build failures: + ``` + mvn dependency:purge-local-repository \ + -DreResolve=true \ + -DactTransitively=false + ``` +7. **Build** to verify: + ``` + mvn clean install -U + ``` + +## Java 8 to Java 21 Adaptation Rules + +When porting code, apply these modernization transformations where appropriate: + +### Language Features +- **Traditional instanceof + cast** -> Use pattern matching for instanceof + ```java + // Java 8 + if (obj instanceof String) { String s = (String) obj; use(s); } + // Java 21 + if (obj instanceof String s) { use(s); } + ``` +- **Verbose switch statements** -> Consider switch expressions where cleaner + ```java + // Java 8 + String result; + switch (x) { + case 1: result = "one"; break; + case 2: result = "two"; break; + default: result = "other"; break; + } + // Java 21 + var result = switch (x) { + case 1 -> "one"; + case 2 -> "two"; + default -> "other"; + }; + ``` +- **String concatenation for multiline** -> Consider text blocks where appropriate +- **Explicit types for local variables** -> Use `var` where the type is obvious from context + +### API Differences +- **`Collections.unmodifiableList(Arrays.asList(...))`** -> Use `List.of(...)` +- **`Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))`** -> Use `Set.of(...)` +- **`string.trim().isEmpty()`** -> Use `string.isBlank()` +- **`!optional.isPresent()`** -> Use `optional.isEmpty()` +- **`.collect(Collectors.toList())`** -> Use `.toList()` +- **Traditional I/O patterns** -> Consider `Files.readString()` / `Files.writeString()` where appropriate + +### Project-Specific Differences +- The `stage` branch uses `aerospike-client-jdk21` artifact instead of `aerospike-client-jdk8` +- pom.xml uses `java.version=21` instead of `java.version=1.8` / `java.api=8` +- The `stage` branch may have additional methods (e.g., `estimateKeySize()`) not present in jdk8 +- Do NOT modify pom.xml java version settings - those are already correct on `stage` + +### Important +- **Be conservative with modernization**: Only modernize code that is part of the change being ported. Do not refactor surrounding unchanged code. +- Preserve the original commit messages when cherry-picking +- If a file does not exist on `stage`, check if the functionality lives in a different file or should be skipped +- After all changes, run `mvn clean install -U` and fix any compilation errors diff --git a/.cursor/commands/port-to-jdk8.md b/.cursor/commands/port-to-jdk8.md new file mode 100644 index 000000000..7134ba29f --- /dev/null +++ b/.cursor/commands/port-to-jdk8.md @@ -0,0 +1,94 @@ +# Port Branch Changes from stage (Java 21) to stage-jdk8 (Java 8) + +Port changes from the source branch (based on `stage`) to a new branch based on `stage-jdk8`. + +## Instructions + +The user will provide a source branch name as an argument. Use it to perform the port. + +## Target Branch Naming + +The target branch name MUST be derived from the source branch name by appending `_jdk8`: +- Source: `CLIENT-1234_feature` -> Target: `CLIENT-1234_feature_jdk8` +- Source: `bugfix-something` -> Target: `bugfix-something_jdk8` + +## Steps + +1. **Validate** the source branch exists and is based on `stage`. +2. **Analyze** the commits on the source branch that are not on `stage`: + ``` + git log --oneline stage.. + ``` +3. **Create** the target branch off `stage-jdk8`: + ``` + git checkout stage-jdk8 + git pull + git checkout -b + ``` +4. **Cherry-pick** the commits from the source branch onto the target branch. If there are conflicts, resolve them with the Java 8 adaptations listed below. +5. **Adapt** the code for Java 8 compatibility (see adaptation rules below). +6. **Purge Maven cache** to avoid stale JDK 21 artifacts causing build failures: + ``` + mvn dependency:purge-local-repository \ + -DreResolve=true \ + -DactTransitively=false + ``` +7. **Build** to verify: + ``` + mvn clean install -U + ``` + +## Java 21 to Java 8 Adaptation Rules + +When porting code, apply these transformations: + +### Language Features +- **Records** -> Replace with traditional classes (private final fields, constructor, getters, equals/hashCode/toString) +- **Sealed classes/interfaces** -> Remove `sealed`, `permits`, `non-sealed` keywords; use regular class hierarchy +- **Pattern matching for instanceof** -> Use traditional instanceof + explicit cast + ```java + // Java 21 + if (obj instanceof String s) { use(s); } + // Java 8 + if (obj instanceof String) { String s = (String) obj; use(s); } + ``` +- **Switch expressions** -> Convert to switch statements or if-else chains + ```java + // Java 21 + var result = switch (x) { + case 1 -> "one"; + case 2 -> "two"; + default -> "other"; + }; + // Java 8 + String result; + switch (x) { + case 1: result = "one"; break; + case 2: result = "two"; break; + default: result = "other"; break; + } + ``` +- **Text blocks (triple quotes)** -> Use string concatenation or `String.join` +- **`var` keyword** -> Replace with explicit types +- **Enhanced switch with arrow syntax** -> Use traditional colon-case syntax + +### API Differences +- **`List.of()`, `Set.of()`, `Map.of()`** -> Use `Collections.unmodifiableList(Arrays.asList(...))`, `Collections.unmodifiableSet(new HashSet<>(Arrays.asList(...)))`, or manual map construction +- **`String.isBlank()`** -> Use `string.trim().isEmpty()` +- **`String.strip()`** -> Use `string.trim()` +- **`String.repeat(n)`** -> Use a loop or `String.join("", Collections.nCopies(n, str))` +- **`Optional.isEmpty()`** -> Use `!optional.isPresent()` +- **`Stream.toList()`** -> Use `.collect(Collectors.toList())` +- **`Map.entry()`** -> Use `new AbstractMap.SimpleEntry<>()` +- **`Files.readString()` / `Files.writeString()`** -> Use traditional I/O with `BufferedReader`/`BufferedWriter` + +### Project-Specific Differences +- The `stage-jdk8` branch uses `aerospike-client-jdk8` artifact instead of `aerospike-client-jdk21` +- pom.xml uses `java.version=1.8` and `java.api=8` instead of `java.version=21` +- Some features like `estimateKeySize()` do not exist in the jdk8 branch +- Do NOT modify pom.xml java version settings - those are already correct on `stage-jdk8` + +### Important +- Preserve the original commit messages when cherry-picking +- If a file does not exist on `stage-jdk8`, check if the functionality lives in a different file or should be skipped +- After all changes, run `mvn clean install -U` and fix any compilation errors diff --git a/CLIENT-DEV.md b/CLIENT-DEV.md new file mode 100644 index 000000000..fd94e1d06 --- /dev/null +++ b/CLIENT-DEV.md @@ -0,0 +1,51 @@ +Client Development Guide +======================== + +AI-Assisted Branch Porting +-------------------------- + +This repository maintains two main staging branches: + +* **stage** — targets Java 21 +* **stage-jdk8** — targets Java 8 + +Custom skills are provided for [Claude Code](https://claude.ai/claude-code) and +[Cursor](https://cursor.com) to automate porting changes between these branches, +including Java version-specific code adaptations. + +### Claude Code + +Use the slash commands in the Claude Code CLI or IDE extension: + + /port-to-jdk8 + +Ports commits from a `stage`-based branch to a new branch based on `stage-jdk8`. +The target branch is named `_jdk8`. + + /port-to-jdk21 + +Ports commits from a `stage-jdk8`-based branch to a new branch based on `stage`. +The target branch is named by removing the `_jdk8` suffix (or appending `_jdk21` +if no suffix is present). + +### Cursor + +Open the Cursor chat or agent panel and type: + + /port-to-jdk8 + +or + + /port-to-jdk21 + +then provide the source branch name when prompted. + +### What the skills do + +1. Cherry-pick commits from the source branch onto the correct base. +2. Adapt Java language features (records, switch expressions, pattern matching, + `var`, text blocks) and API calls (`List.of()` vs `Arrays.asList()`, + `isBlank()` vs `trim().isEmpty()`, etc.) for the target Java version. +3. Handle project-specific differences (artifact names, pom.xml settings, + methods that only exist on one branch). +4. Run `mvn clean install` to verify the build.