diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cd56ba3..0d2a5be 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -# Default ownership for repository review requests. -* @HauntedMC +* @remdui diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 54df9ac..1f8f6ba 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -63,7 +63,7 @@ body: id: paper_version attributes: label: Paper Version and Build - placeholder: 26.2 build 65 + placeholder: 26.2 build 129 validations: required: true - type: input diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 353ee2c..4795e08 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,19 +1,16 @@ version: 2 +# Platform-owned Maven versions are updated by HauntedPlatform's reviewed reconciler. updates: - - package-ecosystem: "gradle" - directory: "/" - schedule: - interval: "weekly" - open-pull-requests-limit: 10 - labels: - - "dependencies" - - "java" - - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" + day: "monday" + time: "06:15" + timezone: "Europe/Amsterdam" open-pull-requests-limit: 5 - labels: - - "dependencies" - - "ci" + groups: + routine-action-updates: + patterns: ["*"] + update-types: ["minor", "patch"] + labels: ["dependencies", "ci"] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9443373..d71c223 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,23 +1,13 @@ -## What Changed +## Change -Describe the change in a few sentences. - -## Why - -Why is this needed? +Describe the behavior or tooling change, why it is needed, and any operator impact. ## Validation -- [ ] `./gradlew test` (or explain why skipped) -- [ ] `./gradlew acceptanceTest` for lifecycle, command, or watcher changes -- [ ] Documentation updated when operator behavior changed - -## Related Issues - -List related issues (example: `Closes #123`). +- [ ] `./mvnw -B -ntp verify` +- [ ] `./mvnw -B -ntp -Pplatform-acceptance verify` for runtime, packaging, or dependency changes +- [ ] `bash scripts/verify-artifact.sh` for packaging changes -## Notes +## Compatibility -- Breaking changes: -- Config/API changes: -- Anything reviewers should pay extra attention to: +Document configuration, command, API, and packaging changes, or state that there are none. diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml deleted file mode 100644 index cbfd3d7..0000000 --- a/.github/workflows/ci-lint.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: CI Lint - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: [main] - -jobs: - lint: - name: Build Hygiene - runs-on: ubuntu-24.04 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Set up JDK 25 - uses: actions/setup-java@v6 - with: - distribution: temurin - java-version: "25" - cache: gradle - - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6.3.0 - - - name: Verify build hygiene - run: | - ./gradlew --no-daemon check - shellcheck src/acceptance/run-acceptance.sh scripts/bump-version.sh diff --git a/.github/workflows/ci-tests-and-coverage.yml b/.github/workflows/ci-tests-and-coverage.yml deleted file mode 100644 index c3cb516..0000000 --- a/.github/workflows/ci-tests-and-coverage.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: CI Tests and Coverage - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: [main] - -jobs: - tests: - name: Tests and Coverage - runs-on: ubuntu-24.04 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Set up JDK 25 - uses: actions/setup-java@v6 - with: - distribution: temurin - java-version: "25" - cache: gradle - - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6.3.0 - - - name: Run tests and generate coverage report - run: ./gradlew --no-daemon test jacocoTestReport - - - name: Upload test report - if: always() - uses: actions/upload-artifact@v7 - with: - name: test-report - if-no-files-found: warn - path: build/reports/tests/test - - - name: Upload JaCoCo report - if: always() - uses: actions/upload-artifact@v7 - with: - name: jacoco-report - if-no-files-found: warn - path: build/reports/jacoco/test - - platform-acceptance: - name: Paper Platform Acceptance - runs-on: ubuntu-24.04 - timeout-minutes: 15 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@v7 - - - name: Set up JDK 25 - uses: actions/setup-java@v6 - with: - distribution: temurin - java-version: "25" - cache: gradle - - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6.3.0 - - - name: Run Paper acceptance - env: - PHR_ACCEPTANCE_WORK_DIRECTORY: ${{ runner.temp }}/paperhotreloader-platform-acceptance - PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY: "true" - run: ./gradlew --no-daemon acceptanceTest - - - name: Upload Paper acceptance logs - if: always() - uses: actions/upload-artifact@v7 - with: - name: paperhotreloader-platform-acceptance-logs - if-no-files-found: warn - path: ${{ runner.temp }}/paperhotreloader-platform-acceptance/paper/paper.log diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1b67d9a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,127 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: read + +jobs: + tests: + name: Maven tests and artifact audit + runs-on: ubuntu-24.04 + timeout-minutes: 20 + env: + PACKAGES_USER: ${{ secrets.HAUNTEDMC_PACKAGES_USERNAME || github.actor }} + PACKAGES_TOKEN: ${{ secrets.HAUNTEDMC_PACKAGES_TOKEN || github.token }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: temurin + java-version: '25' + cache: maven + server-id: github + server-username: PACKAGES_USER + server-password: PACKAGES_TOKEN + - run: ./mvnw -U -B -ntp verify + - run: bash scripts/verify-artifact.sh + - name: Upload test and coverage reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: test-and-coverage-reports + if-no-files-found: warn + path: | + target/surefire-reports + target/site/jacoco + + platform-acceptance: + name: Paper platform acceptance + runs-on: ubuntu-24.04 + timeout-minutes: 20 + env: + PACKAGES_USER: ${{ secrets.HAUNTEDMC_PACKAGES_USERNAME || github.actor }} + PACKAGES_TOKEN: ${{ secrets.HAUNTEDMC_PACKAGES_TOKEN || github.token }} + PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY: 'true' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: temurin + java-version: '25' + cache: maven + server-id: github + server-username: PACKAGES_USER + server-password: PACKAGES_TOKEN + - name: Run platform acceptance + env: + PHR_ACCEPTANCE_WORK_DIRECTORY: ${{ runner.temp }}/paperhotreloader-acceptance + run: ./mvnw -U -B -ntp -Pplatform-acceptance verify + - name: Upload acceptance logs + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: platform-acceptance-logs + if-no-files-found: warn + path: ${{ runner.temp }}/paperhotreloader-acceptance/paper/paper.log + + maven-policy: + name: Shared Maven policy + uses: HauntedMC/HauntedPlatform/.github/workflows/maven-ci.yml@10dfbacc8515208bf3b0ee236a8c7688fda49c9e # release hardening + with: + maven-command: ./mvnw -U -B -ntp validate + secrets: + PACKAGES_USER: ${{ secrets.HAUNTEDMC_PACKAGES_USERNAME || github.actor }} + PACKAGES_TOKEN: ${{ secrets.HAUNTEDMC_PACKAGES_TOKEN || github.token }} + + tooling: + name: ShellCheck and version tooling + runs-on: ubuntu-24.04 + permissions: + contents: read + env: + GH_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - run: | + mapfile -d '' scripts < <(git ls-files -z '*.sh') + shellcheck tools/release/update-version "${scripts[@]}" + shell: bash + - run: gh extension install HauntedMC/gh-haunted-release --pin de43c102d81797001eef36fe45725744fdd22031 + - run: ./tools/release/update-version --dry-run patch + + ci-required: + name: ci-required + if: ${{ always() }} + needs: [tests, platform-acceptance, maven-policy, tooling] + runs-on: ubuntu-24.04 + permissions: + contents: read + env: + CHECK_RESULTS: ${{ toJSON(needs) }} + steps: + - name: Require every validation job + run: | + python3 - <<'PYTHON' + import json + import os + results = {name: job['result'] for name, job in json.loads(os.environ['CHECK_RESULTS']).items()} + failed = {name: result for name, result in results.items() if result != 'success'} + if failed: + raise SystemExit(f'Validation did not pass: {failed}') + PYTHON diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml deleted file mode 100644 index c67bc6b..0000000 --- a/.github/workflows/release-package.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Release Package - -on: - push: - tags: ["v*"] - -jobs: - release: - name: Build, Verify, and Release - runs-on: ubuntu-24.04 - permissions: - contents: write - - steps: - - name: Checkout - uses: actions/checkout@v7 - with: - fetch-depth: 0 - - - name: Set up JDK 25 - uses: actions/setup-java@v6 - with: - distribution: temurin - java-version: "25" - cache: gradle - - - name: Set up Gradle - uses: gradle/actions/setup-gradle@v6.3.0 - - - name: Run release verification - env: - PHR_ACCEPTANCE_WORK_DIRECTORY: ${{ runner.temp }}/paperhotreloader-platform-acceptance - PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY: "true" - run: ./gradlew --no-daemon clean test acceptanceTest - - - name: Resolve distribution artifact - run: | - version="$(./gradlew --quiet properties | sed -n 's/^version: //p')" - test -n "$version" || { echo "Could not determine project version." >&2; exit 1; } - test "${GITHUB_REF_NAME}" = "v$version" || { - echo "Tag ${GITHUB_REF_NAME} does not match project version $version." >&2 - exit 1 - } - echo "DIST_JAR=build/libs/PaperHotReloader-$version.jar" >> "$GITHUB_ENV" - - - name: Create checksum - run: sha256sum "${{ env.DIST_JAR }}" > "${{ env.DIST_JAR }}.sha256" - - - name: Upload failing Paper acceptance logs - if: failure() - uses: actions/upload-artifact@v7 - with: - name: paperhotreloader-platform-acceptance-logs - if-no-files-found: warn - path: ${{ runner.temp }}/paperhotreloader-platform-acceptance/paper/paper.log - - - name: Upload distribution - uses: actions/upload-artifact@v7 - with: - name: dist - path: | - ${{ env.DIST_JAR }} - ${{ env.DIST_JAR }}.sha256 - - - name: Create GitHub release - uses: softprops/action-gh-release@v3 - with: - name: PaperHotReloader ${{ github.ref_name }} - generate_release_notes: true - files: | - ${{ env.DIST_JAR }} - ${{ env.DIST_JAR }}.sha256 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b8de9b3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,95 @@ +name: Release Package + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: release-main + cancel-in-progress: false + +permissions: + contents: read + +jobs: + gate: + runs-on: ubuntu-24.04 + outputs: + publish: ${{ steps.version.outputs.publish }} + version: ${{ steps.version.outputs.version }} + tag: ${{ steps.version.outputs.tag }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Install pinned release CLI + env: + GH_TOKEN: ${{ github.token }} + run: gh extension install HauntedMC/gh-haunted-release --pin de43c102d81797001eef36fe45725744fdd22031 + - id: version + name: Detect a reviewed version change + env: + GITHUB_EVENT_BEFORE: ${{ github.event.before }} + GITHUB_EVENT_NAME: ${{ github.event_name }} + run: gh haunted-release gate + + publish: + needs: gate + if: needs.gate.outputs.publish == 'true' + runs-on: ubuntu-24.04 + timeout-minutes: 45 + permissions: + contents: write + packages: write + env: + PACKAGES_USER: ${{ secrets.HAUNTEDMC_PACKAGES_USERNAME }} + PACKAGES_TOKEN: ${{ secrets.HAUNTEDMC_PACKAGES_TOKEN }} + GH_TOKEN: ${{ github.token }} + PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY: 'true' + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Install pinned release CLI + run: gh extension install HauntedMC/gh-haunted-release --pin de43c102d81797001eef36fe45725744fdd22031 + - uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 + with: + distribution: temurin + java-version: '25' + cache: maven + server-id: github + server-username: PACKAGES_USER + server-password: PACKAGES_TOKEN + - name: Require Maven package credentials + run: test -n "$PACKAGES_USER" && test -n "$PACKAGES_TOKEN" + - id: preflight + name: Inspect published Maven coordinates + run: gh haunted-release published-state "${{ needs.gate.outputs.version }}" + - name: Verify release candidate with platform acceptance + env: + PHR_ACCEPTANCE_WORK_DIRECTORY: ${{ runner.temp }}/paperhotreloader-acceptance + run: ./mvnw -U -B -ntp -Prelease,platform-acceptance verify + - name: Audit distributable jar before publication + run: bash scripts/verify-artifact.sh + - name: Publish verified Maven package + if: steps.preflight.outputs.state != 'complete' + run: ./mvnw -B -ntp -DskipTests -Prelease deploy + - name: Resolve published Maven coordinates from a fresh repository + run: gh haunted-release verify-published "${{ needs.gate.outputs.version }}" + - name: Checksum distributable + run: sha256sum "target/PaperHotReloader-${{ needs.gate.outputs.version }}.jar" > "target/PaperHotReloader-${{ needs.gate.outputs.version }}.jar.sha256" + - name: Create release after package verification + run: | + gh release create "${{ needs.gate.outputs.tag }}" --target "$GITHUB_SHA" --title "PaperHotReloader ${{ needs.gate.outputs.tag }}" --generate-notes \ + "target/PaperHotReloader-${{ needs.gate.outputs.version }}.jar" \ + "target/PaperHotReloader-${{ needs.gate.outputs.version }}.jar.sha256" + - name: Upload acceptance logs on failure + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-platform-acceptance-logs + if-no-files-found: warn + path: ${{ runner.temp }}/paperhotreloader-acceptance/paper/paper.log diff --git a/.gitignore b/.gitignore index aa3034a..e66b6e6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ jars/ *.class *.jar -!gradle/wrapper/gradle-wrapper.jar .DS_Store + +# Maven and release tooling +target/ +.flattened-pom.xml +.mvn/timing.properties diff --git a/.mvn/maven.config b/.mvn/maven.config new file mode 100644 index 0000000..72fce39 --- /dev/null +++ b/.mvn/maven.config @@ -0,0 +1,2 @@ +--settings +.mvn/settings.xml diff --git a/.mvn/settings.xml b/.mvn/settings.xml new file mode 100644 index 0000000..cb9d6e9 --- /dev/null +++ b/.mvn/settings.xml @@ -0,0 +1,9 @@ + + + github${env.PACKAGES_USER}${env.PACKAGES_TOKEN} + hauntedmc-github-packages + centralhttps://repo.maven.apache.org/maven2githubhttps://maven.pkg.github.com/hauntedmc/*true + centralhttps://repo.maven.apache.org/maven2githubhttps://maven.pkg.github.com/hauntedmc/*true + + hauntedmc-github-packages + diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..222cba3 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,4 @@ +wrapperVersion=3.3.4 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip +distributionSha256Sum=0d7125e8c91097b36edb990ea5934e6c68b4440eef4ea96510a0f6815e7eeadb diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b45c7cf..f0b0cf0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Thanks for taking the time to contribute to PaperHotReloader. ## Before You Start -- Use Java 25 and Gradle. +- Use Java 25 and Maven. - Run the unit suite before opening a pull request. - Run the Paper acceptance suite for lifecycle, command, or watcher changes. @@ -13,7 +13,7 @@ Thanks for taking the time to contribute to PaperHotReloader. ```bash git clone https://github.com/HauntedMC/PaperHotReloader.git cd PaperHotReloader -./gradlew compileJava +./mvnw -B -ntp -DskipTests compile ``` ## Local Validation @@ -21,14 +21,14 @@ cd PaperHotReloader Minimum checks: ```bash -./gradlew test -shellcheck src/acceptance/run-acceptance.sh scripts/bump-version.sh +./mvnw -B -ntp test +shellcheck src/acceptance/run-acceptance.sh scripts/verify-artifact.sh tools/release/prepare-version.sh ``` Run the full Paper acceptance suite: ```bash -./gradlew acceptanceTest +./mvnw -B -ntp -Pplatform-acceptance verify ``` The acceptance task downloads the pinned Paper runtime, starts a temporary server, and builds temporary @@ -40,7 +40,7 @@ plugins. It requires `curl`, `java`, `javac`, `jar`, and `sha256sum`. 2. Include tests with behavior changes. 3. Update documentation for operator-visible changes. 4. Describe lifecycle, configuration, or compatibility impact in the pull request. -5. Do not include generated `build/` output. +5. Do not include generated `target/` output. ## Security diff --git a/README.md b/README.md index e398884..725b6a3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # PaperHotReloader -[![CI Quality](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci-tests-and-coverage.yml/badge.svg?branch=main)](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci-tests-and-coverage.yml) -[![Paper Acceptance](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci-tests-and-coverage.yml/badge.svg?branch=main)](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci-tests-and-coverage.yml) +[![CI](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/HauntedMC/PaperHotReloader/actions/workflows/ci.yml) [![Latest Release](https://img.shields.io/github/v/release/HauntedMC/PaperHotReloader?sort=semver)](https://github.com/HauntedMC/PaperHotReloader/releases/latest) [![Java 25](https://img.shields.io/badge/Java-25-007396?logo=openjdk&logoColor=white)](https://adoptium.net/) [![License](https://img.shields.io/github/license/HauntedMC/PaperHotReloader)](LICENSE) @@ -31,32 +30,17 @@ Hot-load, unload, reload, and watch Bukkit plugins without restarting a Paper se ## Build From Source -Build with Java 25 and the Paper 26.2 development bundle: +Use Java 25. HauntedPlatform is resolved from GitHub Packages; set `PACKAGES_USER` and `PACKAGES_TOKEN` (with `read:packages`) for a fresh local Maven cache. The committed `.mvn/settings.xml` reads these variables. ```bash -./gradlew clean build +./mvnw -B -ntp verify ``` -Output jar: `build/libs/PaperHotReloader-.jar` +Output jar: `target/PaperHotReloader-.jar` -## Release Workflow +## Release workflow -Create a semantic-version bump, commit, and release tag locally: - -```bash -scripts/bump-version.sh patch -``` - -Add `--push` to publish the branch and tag after the repository is reviewed: - -```bash -scripts/bump-version.sh minor --push -``` - -Run the fast unit suite with `./gradlew test`. `./gradlew acceptanceTest` follows the ServerFeatures -acceptance pattern: it downloads the pinned Paper 26.2 build, starts a disposable server, builds temporary -sample plugins, and verifies every PHR command plus dependency protection, dynamic reload, file watching, -and PHR's own restart. +From clean `main`, run `./tools/release/update-version patch --pr` to open a reviewed version PR. CI tests the PR; after merge, GitHub Actions publishes the Maven package, verifies that it resolves, and creates the tag and downloadable release jar with a SHA-256 checksum. See [release tooling](tools/release/README.md). ## Commands diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index 925fbdd..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,84 +0,0 @@ -import org.gradle.testing.jacoco.tasks.JacocoReport -import org.gradle.api.tasks.Exec - -plugins { - `java-library` - jacoco -} - -group = "nl.hauntedmc.paperhotreloader" -version = "1.0.4" - -val javaVersion = 25 -val paperApiVersion = "26.2.build.124-stable" -val acceptanceCompileClasspath by configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true -} - -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(javaVersion)) - withSourcesJar() -} - -repositories { - mavenCentral() - maven("https://repo.papermc.io/repository/maven-public/") -} - -dependencies { - // This plugin uses only the public Bukkit/Paper API. Unlike AIlex, it has no NMS classes to remap. - compileOnly("io.papermc.paper:paper-api:$paperApiVersion") - testImplementation("io.papermc.paper:paper-api:$paperApiVersion") - acceptanceCompileClasspath("io.papermc.paper:paper-api:$paperApiVersion") - - testImplementation(platform("org.junit:junit-bom:6.1.3")) - testImplementation("org.junit.jupiter:junit-jupiter") - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -tasks { - compileJava { - options.release.set(javaVersion) - options.encoding = Charsets.UTF_8.name() - } - - processResources { - val properties = mapOf("version" to project.version) - inputs.properties(properties) - filteringCharset = Charsets.UTF_8.name() - filesMatching("plugin.yml") { - expand(properties) - } - } - - test { - useJUnitPlatform() - testLogging { - events("passed", "skipped", "failed") - } - finalizedBy(jacocoTestReport) - } - - check { - dependsOn(jacocoTestReport) - } -} - -tasks.withType().configureEach { - reports { - xml.required.set(true) - html.required.set(true) - } -} - -val acceptanceTest = tasks.register("acceptanceTest") { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = "Boots Paper and verifies PHR against temporary sample plugins." - dependsOn(tasks.jar) - commandLine("bash", "src/acceptance/run-acceptance.sh") - doFirst { - environment("PHR_ARTIFACT", tasks.jar.get().archiveFile.get().asFile.absolutePath) - environment("PHR_PAPER_API_CLASSPATH", acceptanceCompileClasspath.asPath) - } -} diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 1d0c9cb..24bafe6 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -3,15 +3,15 @@ ## Local Workflow ```bash -./gradlew compileJava -./gradlew test -./gradlew check +./mvnw -B -ntp -DskipTests compile +./mvnw -B -ntp test +./mvnw -B -ntp verify ``` Use the platform acceptance task for changes to plugin loading, unloading, commands, or file watching: ```bash -./gradlew acceptanceTest +./mvnw -B -ntp -Pplatform-acceptance verify ``` The acceptance task is intentionally isolated: it builds temporary sample plugins and boots a disposable diff --git a/docs/TESTING.md b/docs/TESTING.md index 66621f6..37b26d2 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -5,26 +5,26 @@ Run deterministic tests with: ```bash -./gradlew test +./mvnw -B -ntp test ``` The suite covers plugin metadata parsing, dependency ordering, and command completion helpers. JaCoCo reports -are written to `build/reports/jacoco/test/html/index.html`. +are written to `target/site/jacoco/index.html`. ## Paper Platform Acceptance Run the real-server test with: ```bash -./gradlew acceptanceTest +./mvnw -B -ntp -Pplatform-acceptance verify ``` -It downloads pinned Paper 26.2 build 65 and verifies PHR against temporary sample plugins. The test exercises +It downloads pinned the HauntedPlatform-pinned Paper runtime and verifies PHR against temporary sample plugins. The test exercises `help`, configuration reload, listing and metadata commands, load, dependency protection, reload, unload, watch/unwatch, file-triggered reload, and PHR self-restart. For troubleshooting, preserve the temporary server directory: ```bash -PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY=true ./gradlew acceptanceTest +PHR_ACCEPTANCE_KEEP_WORK_DIRECTORY=true ./mvnw -B -ntp -Pplatform-acceptance verify ``` diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index 7c1e909..0000000 --- a/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.caching=true -org.gradle.parallel=true -org.gradle.jvmargs=-Dfile.encoding=UTF-8 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index eddabd2..0000000 Binary files a/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index ad7845b..0000000 --- a/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,9 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip -networkTimeout=10000 -retries=0 -retryBackOffMs=500 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew deleted file mode 100755 index 249efbb..0000000 --- a/gradlew +++ /dev/null @@ -1,248 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# gradlew start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh gradlew -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index a51ec4f..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,82 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem gradlew startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables, and ensure extensions are enabled -setlocal EnableExtensions - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -"%COMSPEC%" /c exit 1 - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -"%COMSPEC%" /c exit 1 - -:execute -@rem Setup the command line - - - -@rem Execute gradlew -@rem endlocal doesn't take effect until after the line is parsed and variables are expanded -@rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel - -:exitWithErrorLevel -@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts -"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..bd8896b --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.4 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..92450f9 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.4 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..89d323e --- /dev/null +++ b/pom.xml @@ -0,0 +1,161 @@ + + + 4.0.0 + + nl.hauntedmc.platform + haunted-application-parent + 2.0.0 + + + nl.hauntedmc.paperhotreloader + paperhotreloader + ${revision} + PaperHotReloader + Hot-load, unload, reload, and watch Paper plugins. + https://github.com/HauntedMC/PaperHotReloader + 2026 + + HauntedMC + https://www.hauntedmc.nl + + + + GNU Affero General Public License v3.0 + https://www.gnu.org/licenses/agpl-3.0.txt + repo + + + + + HauntedMC + HauntedMC + https://www.hauntedmc.nl + + + + scm:git:https://github.com/HauntedMC/PaperHotReloader.git + scm:git:ssh://git@github.com/HauntedMC/PaperHotReloader.git + https://github.com/HauntedMC/PaperHotReloader + v1.0.5 + + + GitHub + https://github.com/HauntedMC/PaperHotReloader/issues + + + GitHub Actions + https://github.com/HauntedMC/PaperHotReloader/actions + + + + github + GitHub Packages - PaperHotReloader + https://maven.pkg.github.com/HauntedMC/PaperHotReloader + + + + 1.0.5 + UTF-8 + 2026-09-25T00:00:00Z + + + + central + https://repo.maven.apache.org/maven2 + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + io.papermc.paper + paper-api + provided + + + org.junit.jupiter + junit-jupiter + test + + + + PaperHotReloader-${project.version} + + + src/main/resources + true + + + + + + release + + + + maven-source-plugin + + + sources + + jar-no-fork + + + + + + + + + platform-acceptance + + + + maven-dependency-plugin + + + acceptance-classpath + generate-test-resources + + build-classpath + + + test + ${project.build.directory}/acceptance-classpath.txt + + + + + + org.codehaus.mojo + exec-maven-plugin + + + run-platform-acceptance + verify + + exec + + + bash + + src/acceptance/run-acceptance.sh + + + ${project.build.directory}/${project.build.finalName}.jar + ${project.build.directory}/acceptance-classpath.txt + ${haunted.paper.runtime.version} + ${haunted.paper.runtime.build} + ${haunted.paper.runtime.sha256} + + + + + + + + + + diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh deleted file mode 100755 index 0ed80fb..0000000 --- a/scripts/bump-version.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -BUILD_FILE="build.gradle.kts" -PUSH_REMOTE="origin" -DO_PUSH=false - -usage() { - cat >&2 <<'EOF' -Usage: scripts/bump-version.sh [--push] [--remote ] - -Examples: - scripts/bump-version.sh patch - scripts/bump-version.sh minor --push - scripts/bump-version.sh major --push --remote origin -EOF -} - -if [[ $# -lt 1 ]]; then - usage - exit 1 -fi - -bump_type="$1" -shift - -if [[ "$bump_type" != "major" && "$bump_type" != "minor" && "$bump_type" != "patch" ]]; then - usage - exit 1 -fi - -while [[ $# -gt 0 ]]; do - case "$1" in - --push) - DO_PUSH=true - shift - ;; - --remote) - if [[ $# -lt 2 ]]; then - echo "--remote requires a value." >&2 - exit 1 - fi - PUSH_REMOTE="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" >&2 - usage - exit 1 - ;; - esac -done - -if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - echo "This script must be run inside a git repository." >&2 - exit 1 -fi - -if [[ ! -f "$BUILD_FILE" ]]; then - echo "$BUILD_FILE not found." >&2 - exit 1 -fi - -if [[ -n "$(git status --porcelain)" ]]; then - echo "Working tree is not clean. Commit or stash changes before bumping version." >&2 - exit 1 -fi - -current_version="$( - sed -nE 's/^[[:space:]]*version[[:space:]]*=[[:space:]]*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/p' "$BUILD_FILE" | head -n 1 -)" - -if [[ -z "$current_version" ]]; then - echo "Could not parse version from $BUILD_FILE." >&2 - exit 1 -fi - -if [[ ! "$current_version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then - echo "Project version must be a semantic version like 1.2.3." >&2 - exit 1 -fi - -major="${BASH_REMATCH[1]}" -minor="${BASH_REMATCH[2]}" -patch="${BASH_REMATCH[3]}" - -case "$bump_type" in - major) - major=$((major + 1)) - minor=0 - patch=0 - ;; - minor) - minor=$((minor + 1)) - patch=0 - ;; - patch) - patch=$((patch + 1)) - ;; -esac - -new_version="${major}.${minor}.${patch}" -new_tag="v${new_version}" - -if git rev-parse -q --verify "refs/tags/${new_tag}" >/dev/null 2>&1; then - echo "Tag ${new_tag} already exists locally." >&2 - exit 1 -fi - -if git ls-remote --exit-code --tags "$PUSH_REMOTE" "refs/tags/${new_tag}" >/dev/null 2>&1; then - echo "Tag ${new_tag} already exists on remote '${PUSH_REMOTE}'." >&2 - exit 1 -fi - -echo "Current version: ${current_version}" -echo "New version: ${new_version}" - -sed -i -E "s/^([[:space:]]*version[[:space:]]*=[[:space:]]*\")[0-9]+\.[0-9]+\.[0-9]+(\".*)$/\1${new_version}\2/" "$BUILD_FILE" - -git add "$BUILD_FILE" -git commit -m "Bump version to ${new_tag} for release" -git tag "$new_tag" - -if [[ "$DO_PUSH" == "true" ]]; then - current_branch="$(git rev-parse --abbrev-ref HEAD)" - git push "$PUSH_REMOTE" "$current_branch" - git push "$PUSH_REMOTE" "$new_tag" - echo "Version bump committed, tagged, and pushed to ${PUSH_REMOTE}." -else - echo "Version bump committed and tagged locally." - echo "Push when ready:" - echo " git push ${PUSH_REMOTE} && git push ${PUSH_REMOTE} ${new_tag}" -fi diff --git a/scripts/verify-artifact.sh b/scripts/verify-artifact.sh new file mode 100755 index 0000000..c4675c2 --- /dev/null +++ b/scripts/verify-artifact.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" +version="$(sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)<\/revision>.*/\1/p' pom.xml | head -n 1)" +[[ -n "$version" ]] || { echo 'Missing semantic Maven revision' >&2; exit 1; } +artifact="target/PaperHotReloader-$version.jar" +[[ -f "$artifact" ]] || { echo "Missing distributable $artifact" >&2; exit 1; } +entries="$(jar tf "$artifact")" +descriptor="$(unzip -p "$artifact" plugin.yml)" +grep -Fxq "version: '$version'" <<<"$descriptor" +grep -Fq 'nl/hauntedmc/paperhotreloader/PaperHotReloader.class' <<<"$entries" +if grep -Eq '^org/bukkit/|^io/papermc/paper/' <<<"$entries"; then + echo 'Plugin jar contains provided Paper classes' >&2 + exit 1 +fi +echo "Artifact audit passed: $artifact" diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 4fc824c..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "PaperHotReloader" diff --git a/src/acceptance/run-acceptance.sh b/src/acceptance/run-acceptance.sh index b0e90f2..c8a1bf5 100644 --- a/src/acceptance/run-acceptance.sh +++ b/src/acceptance/run-acceptance.sh @@ -2,10 +2,12 @@ set -euo pipefail root_directory="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -artifact="${PHR_ARTIFACT:-$root_directory/build/libs/PaperHotReloader-1.0.0.jar}" -paper_api_classpath="${PHR_PAPER_API_CLASSPATH:-}" -paper_url="https://fill-data.papermc.io/v1/objects/d290ee2a3ae92fea10afb0c74f797023d74cfd72cbcee44314e0c50d5551f2c6/paper-26.2-65.jar" -paper_sha256="d290ee2a3ae92fea10afb0c74f797023d74cfd72cbcee44314e0c50d5551f2c6" +artifact="${PHR_ARTIFACT:-}" +paper_api_classpath="$(cat "${PHR_PAPER_API_CLASSPATH:?Missing classpath file}")" +paper_version="${PHR_RUNTIME_VERSION:?Missing Paper runtime version}" +paper_build="${PHR_RUNTIME_BUILD:?Missing Paper runtime build}" +paper_sha256="${PHR_RUNTIME_SHA256:?Missing Paper runtime checksum}" +paper_url="https://fill-data.papermc.io/v1/objects/$paper_sha256/paper-$paper_version-$paper_build.jar" work_directory="${PHR_ACCEPTANCE_WORK_DIRECTORY:-$(mktemp -d)}" created_work_directory="${PHR_ACCEPTANCE_WORK_DIRECTORY:+false}" created_work_directory="${created_work_directory:-true}" @@ -89,11 +91,11 @@ mkdir -p "$work_directory/paper/plugins" "$work_directory/sample/classes" "$work curl --fail --silent --show-error --location --output "$work_directory/paper/paper.jar" "$paper_url" [[ "$(sha256sum "$work_directory/paper/paper.jar" | awk '{print $1}')" == "$paper_sha256" ]] \ - || fail "Downloaded Paper 26.2 build 65 checksum mismatch" + || fail "Downloaded Paper runtime checksum mismatch" cp "$artifact" "$work_directory/paper/plugins/PaperHotReloader.jar" sample_source="$root_directory/src/acceptance/sample-plugin/AcceptancePlugin.java" -javac --release 25 -cp "$paper_api_classpath" -d "$work_directory/sample/classes" "$sample_source" +"${JAVA_HOME:?Set JAVA_HOME to JDK 25}/bin/javac" --release 25 -cp "$paper_api_classpath" -d "$work_directory/sample/classes" "$sample_source" build_sample_plugin() { local destination=$1 descriptor=$2 marker=$3 @@ -116,7 +118,7 @@ build_sample_plugin \ printf '%s\n' 'eula=true' >"$work_directory/paper/eula.txt" printf '%s\n' 'server-port=0' 'level-type=minecraft:flat' >"$work_directory/paper/server.properties" mkfifo "$work_directory/paper/console.in" -(cd "$work_directory/paper" && exec java -Xms512M -Xmx1G -jar paper.jar --nogui paper.log 2>&1) & +(cd "$work_directory/paper" && exec "${JAVA_HOME:?Set JAVA_HOME to JDK 25}/bin/java" -Xms512M -Xmx1G -jar paper.jar --nogui paper.log 2>&1) & paper_pid=$! exec {paper_input_fd}>"$work_directory/paper/console.in" paper_log="$work_directory/paper/paper.log" diff --git a/tools/release/README.md b/tools/release/README.md new file mode 100644 index 0000000..b2733bc --- /dev/null +++ b/tools/release/README.md @@ -0,0 +1,5 @@ +# Version updates + +From clean, current `main`, run `./tools/release/update-version patch --pr` to prepare, push, and open a reviewed version PR in HauntedMC/PaperHotReloader. The pinned `gh-haunted-release` extension manages the worktree. Use `--dry-run` to inspect the next version. CI validates the PR; merging a version change publishes Maven packages and creates the GitHub release after verification. No local tag or direct push to `main` is needed. + +Install once: `gh extension install HauntedMC/gh-haunted-release --pin v1.0.3`. diff --git a/tools/release/prepare-version.sh b/tools/release/prepare-version.sh new file mode 100755 index 0000000..80c2ad6 --- /dev/null +++ b/tools/release/prepare-version.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" +[[ $# -eq 1 && "$1" =~ ^(major|minor|patch)$ ]] || { echo 'Usage: prepare-version.sh major|minor|patch' >&2; exit 2; } +python3 - "$1" <<'PYVERSION' +from pathlib import Path +from datetime import datetime, timezone +import re +import sys +kind = sys.argv[1] +pom = Path('pom.xml') +content = pom.read_text() +match = re.search(r'(\d+)\.(\d+)\.(\d+)', content) +if not match: + raise SystemExit('Missing semantic revision in pom.xml') +old = match.group(0).removeprefix('').removesuffix('') +parts = list(map(int, match.groups())) +index = {'major': 0, 'minor': 1, 'patch': 2}[kind] +parts[index] += 1 +for i in range(index + 1, 3): + parts[i] = 0 +new = '.'.join(map(str, parts)) +def replace(path, before, after): + value = path.read_text() + if value.count(before) != 1: + raise SystemExit(f'{path}: expected exactly one {before!r}') + path.write_text(value.replace(before, after)) +replace(pom, f'{old}', f'{new}') +replace(pom, f'v{old}', f'v{new}') +timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%dT00:00:00Z') +content = pom.read_text() +match = re.search(r'([^<]+)', content) +if not match: + raise SystemExit('Missing reproducible-build timestamp') +replace(pom, match.group(0), f'{timestamp}') +print(f'Prepared {old} -> {new}') +PYVERSION +git diff --check diff --git a/tools/release/project.toml b/tools/release/project.toml new file mode 100644 index 0000000..852d459 --- /dev/null +++ b/tools/release/project.toml @@ -0,0 +1,12 @@ +[project] +name = "PaperHotReloader" +repository = "HauntedMC/PaperHotReloader" +tool_version = "1.0.3" +mode = "bump" +prepare = "tools/release/prepare-version.sh" +verify = ["./mvnw", "-B", "-ntp", "verify"] +verify_before_pr = false + +[components.default] +pom = "pom.xml" +tag_prefix = "v" diff --git a/tools/release/update-version b/tools/release/update-version new file mode 100755 index 0000000..7ba2eb3 --- /dev/null +++ b/tools/release/update-version @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(git rev-parse --show-toplevel)" +exec gh haunted-release version "$@"