diff --git a/.bob/skills/dependency-update/SKILL.md b/.bob/skills/dependency-update/SKILL.md new file mode 100644 index 0000000..76801a5 --- /dev/null +++ b/.bob/skills/dependency-update/SKILL.md @@ -0,0 +1,213 @@ +--- +name: dependency-update +description: Update dependencies and plugins in Maven or Gradle projects — baseline build, version checking, risk assessment, inline pin comments, and PR description. +--- + +# Dependency Update Skill + +## Before Starting + +Ask the user for `JAVA_HOME` (for Maven projects) or any required SDK/toolchain path before running any build commands. + +--- + +## Phase 1: Baseline Build + +Run a clean build to confirm tests pass before making any changes. + +**Maven:** +```bash +JAVA_HOME= mvn clean verify 2>&1 | tee build-baseline.log +``` + +**Gradle:** +```bash +JAVA_HOME= ./gradlew clean build 2>&1 | tee build-baseline.log +``` + +If the baseline fails, stop and report to the user before proceeding. + +--- + +## Phase 2: Identify All Dependencies + +Collect every versioned dependency and plugin declared across all build files. + +**Maven:** Read all `pom.xml` files. Capture `groupId`, `artifactId`, and `version` for: +- `` +- `` +- `` +- `` + +**Gradle:** Read all `build.gradle` / `build.gradle.kts` files. Capture coordinates from: +- `dependencies { }` blocks +- `plugins { }` blocks +- Version catalogs (`libs.versions.toml`) +- `gradle/wrapper/gradle-wrapper.properties` (Gradle wrapper version) + +Record the source file and line for each entry so updates can be applied precisely. + +--- + +## Phase 3: Check Latest Versions + +Fetch the latest available version for **all dependencies in a single script** — do not make one request per dependency. + +### Version sources + +| Registry | Authoritative URL | Notes | +|----------|-------------------|-------| +| Maven Central | `https://repo1.maven.org/maven2///maven-metadata.xml` — read `` list | Primary source; always current | +| Gradle Plugin Portal | `https://plugins.gradle.org/m2//maven-metadata.xml` — read `` list | e.g. `org.jreleaser` → `org/jreleaser/jreleaser-gradle-plugin/maven-metadata.xml` | +| Gradle wrapper | `https://services.gradle.org/versions/current` — JSON, read `version` field | | + +> **Never use `` or `` tags.** Maven Central sets `` to the lexicographically highest version string, which can be a pre-release (e.g. `4.0.0-beta-2` sorts above `3.5.0`). Always read the full `` list and filter for stable yourself. + +### Script pattern (Python, no third-party deps) + +Populate `checks` with every dependency from Phase 2, then run once: + +```bash +python3 - << 'EOF' +import urllib.request, xml.etree.ElementTree as ET, json + +PRE_RELEASE_MARKERS = ['alpha', 'beta', 'rc', 'milestone', 'cr', + '.m1','.m2','.m3','.m4','.m5','.m6','.m7','.m8','.m9', + '-m1','-m2','-m3','-m4','-m5','-m6','-m7','-m8','-m9'] + +def is_stable(version): + v = version.lower() + return not any(m in v for m in PRE_RELEASE_MARKERS) + +def latest_stable(group, artifact): + path = group.replace('.', '/') + '/' + artifact + url = f"https://repo1.maven.org/maven2/{path}/maven-metadata.xml" + with urllib.request.urlopen(url, timeout=10) as r: + tree = ET.parse(r) + versions = [v.text for v in tree.findall('.//version')] + stable = [v for v in versions if is_stable(v)] + return stable[-1] if stable else 'unknown' + +def latest_stable_gradle_plugin(artifact_path): + url = f"https://plugins.gradle.org/m2/{artifact_path}/maven-metadata.xml" + with urllib.request.urlopen(url, timeout=10) as r: + tree = ET.parse(r) + versions = [v.text for v in tree.findall('.//version')] + stable = [v for v in versions if is_stable(v)] + return stable[-1] if stable else 'unknown' + +def gradle_current(): + with urllib.request.urlopen("https://services.gradle.org/versions/current", timeout=10) as r: + return json.load(r)['version'] + +checks = [ + # ("display name", fetch_call), + ("com.example:my-lib", latest_stable("com.example", "my-lib")), + ("plugin: org.foo", latest_stable_gradle_plugin("org/foo/foo-gradle-plugin")), + ("Gradle wrapper", gradle_current()), +] + +print(f"{'Dependency':<50} {'Latest stable'}") +print("-" * 65) +for name, ver in checks: + print(f"{name:<50} {ver}") +EOF +``` + +### Watch out for version format quirks + +- **Spock BOM** versions use `X.Y-groovy-Z.Z` — filter for the Groovy version the project actually uses after collecting the stable list. +- **POM-only aggregators** (e.g. `wiremock-jre8` 3.x): if `maven-metadata.xml` reports a version but the artifact only publishes a `.pom` with no `.jar`, it is a redirect artifact. Check the previous major line for the real latest JAR. +- **`search.maven.org`** Solr index can lag by days and miss recently published releases — always confirm with `repo1.maven.org`. If the current version in the project appears newer than what the search index reports, the index is stale. + +### Classify each dependency + +| Status | Meaning | +|--------|---------| +| `up-to-date` | Already on latest | +| `patch-available` | New patch release (x.y.Z) | +| `minor-available` | New minor release (x.Y.z) | +| `major-available` | New major release (X.y.z) | + +--- + +## Phase 4: Risk Assessment + +For each dependency not already up-to-date: + +1. **Patch updates** — treat as low risk; apply directly. +2. **Minor/major updates** — fetch the release notes or changelog and scan for: + - Breaking API changes + - Removed or renamed classes/methods used in this project + - Raised minimum Java/runtime version + - License changes + +Search GitHub releases (`https://api.github.com/repos///releases?per_page=20`), the project's `CHANGELOG.md`, or migration guides. Summarise findings as `low`, `medium`, or `high` risk with a one-line reason. + +--- + +## Phase 5: Apply Updates + +### Step 1 — Apply all patch updates at once + +Apply every `patch-available` version bump in one pass across all build files, then run a single full build. + +- If the build passes → all patches are done, record every one as **updated**. +- If the build fails → bisect: revert half the patches, rebuild, narrow down until the offending patch is isolated. Revert only that dependency and record it as **blocked** with the failure reason. + +### Step 2 — Apply minor and major updates one at a time + +Work through `minor-available` and `major-available` dependencies individually, lowest risk first (as assessed in Phase 4). + +For each: +1. Edit the version in the build file. +2. Run the full build and test suite. +3. If tests pass → keep the change, record as **updated**. +4. If tests fail → triage the failure: + - Read the error. Check whether it matches a known breaking change from the release notes. + - If a straightforward fix exists (e.g. a renamed import, a changed method signature) → apply the fix, re-run, and record as **updated with migration**. + - If the fix is non-trivial or out of scope → revert the version bump, record as **blocked** with the failure reason. + +### Annotating pinned dependencies + +Add an inline `pinned:` comment **only** for dependencies intentionally held below the latest available version. Do not annotate dependencies that are already on their latest version. + +**Maven (`pom.xml`):** +```xml + +4.11.0 +``` + +**Gradle (`build.gradle` / `build.gradle.kts`):** +```groovy +// pinned: +implementation 'org.example:lib:4.11.0' +``` + +--- + +## Phase 6: PR Description + +Produce a PR description using the template below. Output it as a fenced ` ```markdown ` code block in the chat so the user can copy the raw markdown. + +```markdown +## Dependencies updated + +### Updated + +| Dependency | Old | New | Notes | +|------------|-----|-----|-------| +| `com.example:lib` | `1.2.3` | `1.2.5` | patch | +| `plugin: org.foo` | `1.0.0` | `2.1.0` | major; migrated DSL (renamed property) | + +### Not updated + +| Dependency | Current | Latest | Reason | +|------------|---------|--------|--------| +| `org.example:other` | `2.3.0` | `3.0.0` | v3 requires Java 17; project targets Java 11 | + +### Build verification + +- Baseline: ✅ X tests passing +- After updates: ✅ X tests passing +``` diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 51b1013..520828f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 3542692..9df69b3 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -8,10 +8,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' diff --git a/.github/workflows/maven-scheduled-build.yml b/.github/workflows/maven-scheduled-build.yml index 60dd696..94cf7d5 100644 --- a/.github/workflows/maven-scheduled-build.yml +++ b/.github/workflows/maven-scheduled-build.yml @@ -10,10 +10,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '8' distribution: 'temurin' diff --git a/.gitignore b/.gitignore index d7a4340..a82a3ea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ target/ .idea/ **/*.iml .scans/ +build-after.log +build-baseline.log diff --git a/.vscode/settings.json b/.vscode/settings.json index c5f3f6b..e0f15db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "automatic" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..5a483df --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,23 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Maven: clean install", + "type": "shell", + "command": "JAVA_HOME=${input:javaHome} mvn clean install", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$mvn-compile" + } + ], + "inputs": [ + { + "id": "javaHome", + "type": "promptString", + "description": "JAVA_HOME path (Java 11 or higher)", + "default": "${env:JAVA_HOME}" + } + ] +} diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..ea29076 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,26 @@ +# Releasing + +## Prerequisites + +- Write access to this repository +- The current version on `main` must be a `-SNAPSHOT` version + +## Steps + +1. **Verify downstream projects** — before releasing, confirm the changes work correctly with [cics-bundle-maven](https://github.com/IBM/cics-bundle-maven) and [cics-bundle-gradle](https://github.com/IBM/cics-bundle-gradle) by updating their dependency to the current SNAPSHOT and running their builds locally. + +2. **Trigger the release workflow** — go to Actions → Prepare Release → Run workflow, or run: + ```bash + gh workflow run prepare-release.yml + ``` + This opens a PR that strips `-SNAPSHOT` from `pom.xml` (e.g. `2.0.5-SNAPSHOT → 2.0.5`). + +3. **Review and merge the PR** — merging triggers the existing deploy to OSSRH via `maven-build.yml`. + +4. **Publish to Maven Central** — a maintainer with Sonatype publishing permission must log in to [central.sonatype.com/publishing](https://central.sonatype.com/publishing) and publish the staged artifact. + +5. **Automation takes over** — once the release PR is merged, `post-release.yml` automatically: + - Creates a GitHub Release tagged `vX.Y.Z` with generated release notes + - Opens a PR bumping to the next patch SNAPSHOT (e.g. `2.0.6-SNAPSHOT`) + +6. **Merge the next-dev PR** — no rush, merge when ready. diff --git a/pom.xml b/pom.xml index 41f07fe..60e3ed5 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,7 @@ com.github.spotbugs spotbugs-maven-plugin + 4.7.3.4 Max @@ -196,7 +197,7 @@ maven-jar-plugin - 3.5.0 + 3.5.1 maven-resources-plugin @@ -323,7 +324,7 @@ org.apache.httpcomponents.client5 httpclient5 - 5.6.2 + 5.6.3 org.apache.httpcomponents.core5 @@ -352,13 +353,13 @@ org.xmlunit xmlunit-core - 2.12.0 + 2.13.0 test org.xmlunit xmlunit-matchers - 2.12.0 + 2.13.0 test