From 19936722f9e9066b658e6f85ce4470ba136bdc7f Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Mon, 24 Aug 2026 22:08:20 +0000 Subject: [PATCH] Commence work on windows-x64 GUTDODP GUTDODP fix(java): avoid PowerShell Maven argument parsing Remove the redundant native download property so the Windows in-process job does not pass a dotted -D argument through PowerShell. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f fix(java): keep relative path test on workspace drive Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f fix(java): make coverage report portable Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f GUTDODP Plan for adding win32-x64 to release Prompt to implement win32 release feat(java): publish native classifiers from matching hosts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f build(deps-dev): bump hono from 4.12.32 to 4.13.1 in /test/harness (#2300) Bumps [hono](https://github.com/honojs/hono) from 4.12.32 to 4.13.1. - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.12.32...v4.13.1) --- updated-dependencies: - dependency-name: hono dependency-version: 4.13.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> fix(java): use workflow SHA for snapshot checkouts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f fix(java): use workflow SHA for publication test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f fix(java): stabilize Windows publishing checks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f ci(java): consolidate snapshot artifact summary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f ci(java): consolidate release artifact summary Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: e2a48f51-02fe-445b-a253-40776b09a44f GUTDODP Remove prompts --- .github/actions/java-test-report/action.yml | 3 +- .github/workflows/java-publish-maven.yml | 377 +++++++++++++--- .github/workflows/java-publish-snapshot.yml | 149 ++++++- .github/workflows/java-sdk-tests.yml | 136 +++++- java/README.md | 17 +- java/copilot-native/pom.xml | 228 +++++++++- .../create-native-classifier-test-fixture.mjs | 119 +++++ .../scripts/fetch-native.test.mjs | 72 +-- .../scripts/validate-local-publication.mjs | 108 +++++ .../scripts/validate-native-artifact.mjs | 338 ++++++++++++++ .../scripts/validate-native-artifact.test.mjs | 413 ++++++++++++++++++ .../scripts/validate-native-host.mjs | 33 +- .../scripts/validate-native-host.test.mjs | 35 ++ .../adr/adr-007-native-bundling-strategy.md | 6 +- java/sdk/pom.xml | 30 +- .../copilot/ffi/NativeRuntimeLoader.java | 6 +- .../java/com/github/copilot/e2e/RewindIT.java | 6 +- .../copilot/ffi/JnaNativeBindingTest.java | 8 +- .../copilot/ffi/NativeRuntimeLoaderTest.java | 61 +-- test/harness/package-lock.json | 5 +- 20 files changed, 1932 insertions(+), 218 deletions(-) create mode 100644 java/copilot-native/scripts/create-native-classifier-test-fixture.mjs create mode 100644 java/copilot-native/scripts/validate-local-publication.mjs create mode 100644 java/copilot-native/scripts/validate-native-artifact.mjs create mode 100644 java/copilot-native/scripts/validate-native-artifact.test.mjs diff --git a/.github/actions/java-test-report/action.yml b/.github/actions/java-test-report/action.yml index eedf053725..4ea110cb13 100644 --- a/.github/actions/java-test-report/action.yml +++ b/.github/actions/java-test-report/action.yml @@ -126,7 +126,8 @@ runs: if [ -n "$covered" ] && [ -n "$missed" ]; then local total=$((covered + missed)) if [ "$total" -gt 0 ]; then - echo "scale=1; $covered * 100 / $total" | bc + awk -v covered="$covered" -v total="$total" \ + 'BEGIN { printf "%.1f\n", covered * 100 / total }' else echo "0" fi diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml index 1744a697e1..609e7a468d 100644 --- a/.github/workflows/java-publish-maven.yml +++ b/.github/workflows/java-publish-maven.yml @@ -1,9 +1,6 @@ name: "Java Publish to Maven Central" env: - # Disable Husky Git hooks in CI to prevent local development hooks - # (e.g., pre-commit formatting checks) from running during automated - # workflows that perform git commits and pushes. HUSKY: 0 on: @@ -40,7 +37,7 @@ on: outputs: mavenPublished: description: "Whether the Java package was published to Maven Central" - value: ${{ jobs.publish-maven.outputs.published }} + value: ${{ jobs.deploy-maven.outputs.published }} secrets: JAVA_RELEASE_TOKEN: required: true @@ -56,8 +53,7 @@ on: required: true permissions: - contents: write - id-token: write + contents: read concurrency: group: publish-maven @@ -70,11 +66,6 @@ jobs: steps: - name: Verify JAVA_RELEASE_TOKEN can push to repository run: | - # JAVA_RELEASE_TOKEN is used by actions/checkout and for: - # - git push origin main (doc updates) - # - mvn release:prepare -DpushChanges=true (release commits + tags) - # - git revert + push (rollback on failure) - # It must have push (contents:write) permission on this repo. PUSH=$(gh api repos/${{ github.repository }} --jq '.permissions.push // false') if [ "$PUSH" != "true" ]; then echo "::error::JAVA_RELEASE_TOKEN lacks push permission on ${{ github.repository }}. It is required for pushing release commits and tags to main." @@ -84,17 +75,24 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_TOKEN }} - publish-maven: - name: Publish Java SDK to Maven Central + prepare-release: + name: Prepare Java release needs: preflight runs-on: ubuntu-latest + permissions: + contents: write defaults: run: shell: bash working-directory: ./java outputs: - version: ${{ steps.versions.outputs.release_version }} - published: ${{ steps.publish-maven.outcome == 'success' }} + release_version: ${{ steps.versions.outputs.release_version }} + development_version: ${{ steps.versions.outputs.development_version }} + release_tag: ${{ steps.release-identity.outputs.release_tag }} + tag_commit: ${{ steps.release-identity.outputs.tag_commit }} + pre_prepare_commit: ${{ steps.pre-prepare.outputs.pre_prepare_commit }} + post_prepare_commit: ${{ steps.release-identity.outputs.post_prepare_commit }} + docs_commit: ${{ steps.update-docs.outputs.docs_commit_sha }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -108,88 +106,66 @@ jobs: - uses: ./.github/actions/setup-copilot - - name: Set up JDK 25 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: "25" distribution: "microsoft" cache: "maven" - server-id: central - server-username: MAVEN_USERNAME - server-password: MAVEN_PASSWORD - gpg-private-key: ${{ secrets.JAVA_GPG_SECRET_KEY }} - gpg-passphrase: JAVA_GPG_PASSPHRASE - name: Determine versions id: versions - working-directory: ./java run: | CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) echo "Current pom.xml version: $CURRENT_VERSION" - # Determine release version if [ -n "${{ inputs.releaseVersion }}" ]; then RELEASE_VERSION="${{ inputs.releaseVersion }}" else - # Remove -SNAPSHOT suffix if present RELEASE_VERSION="${CURRENT_VERSION%-SNAPSHOT}" fi echo "Release version: $RELEASE_VERSION" - # Determine next development version if [ -n "${{ inputs.developmentVersion }}" ]; then DEV_VERSION="${{ inputs.developmentVersion }}" if [[ "$DEV_VERSION" != *-SNAPSHOT ]]; then - echo "::error::developmentVersion '${DEV_VERSION}' must end with '-SNAPSHOT' (e.g., '${DEV_VERSION}-SNAPSHOT'). The maven-release-plugin requires the next development version to be a snapshot." + echo "::error::developmentVersion '${DEV_VERSION}' must end with '-SNAPSHOT'." exit 1 fi else - # Split version: supports "0.1.32", "0.1.32-preview.0", "0.1.32-java.0", and "0.1.32-java-preview.0" formats - # Validate RELEASE_VERSION format explicitly to provide clear errors if ! echo "$RELEASE_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-(preview|(beta-)?java(-preview)?)\.[0-9]+)?$'; then - echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid. Expected format: M.M.P, M.M.P-preview.N, M.M.P-java.N, M.M.P-java-preview.N, M.M.P-beta-java.N, or M.M.P-beta-java-preview.N (e.g., 1.2.3, 1.2.3-preview.0, 1.2.3-java.0, 1.2.3-java-preview.0, 1.2.3-beta-java.0, or 1.2.3-beta-java-preview.0)." >&2 + echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid." >&2 exit 1 fi - # Extract the base M.M.P portion (before any qualifier) BASE_VERSION=$(echo "$RELEASE_VERSION" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+') QUALIFIER=$(echo "$RELEASE_VERSION" | sed "s|^${BASE_VERSION}||") IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION" - NEXT_PATCH=$((PATCH + 1)) - DEV_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}${QUALIFIER}-SNAPSHOT" + DEV_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))${QUALIFIER}-SNAPSHOT" fi - echo "Next development version: $DEV_VERSION" - echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT - echo "dev_version=$DEV_VERSION" >> $GITHUB_OUTPUT - - echo "### Version Summary" >> $GITHUB_STEP_SUMMARY - echo "- **Release version:** $RELEASE_VERSION" >> $GITHUB_STEP_SUMMARY - echo "- **Next development version:** $DEV_VERSION" >> $GITHUB_STEP_SUMMARY + echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT" + echo "development_version=$DEV_VERSION" >> "$GITHUB_OUTPUT" - name: Update documentation with release version id: update-docs - working-directory: ./java run: | VERSION="${{ steps.versions.outputs.release_version }}" - DEV_VERSION="${{ steps.versions.outputs.dev_version }}" + DEV_VERSION="${{ steps.versions.outputs.development_version }}" ./scripts/test-update-documentation-versions.sh ./scripts/update-documentation-versions.sh "$VERSION" "$DEV_VERSION" README.md sdk/jbang-example.java - - # Commit the documentation changes before release:prepare (requires clean working directory) git add README.md sdk/jbang-example.java git commit -m "docs: update version references to ${VERSION}" - - # Save the commit SHA for potential rollback - echo "docs_commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - + echo "docs_commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" git push origin main + - name: Record rollback base + id: pre-prepare + run: echo "pre_prepare_commit=$(git rev-parse HEAD^)" >> "$GITHUB_OUTPUT" + - name: Prepare Release - working-directory: ./java run: | mvn -B release:prepare \ -DreleaseVersion=${{ steps.versions.outputs.release_version }} \ - -DdevelopmentVersion=${{ steps.versions.outputs.dev_version }} \ + -DdevelopmentVersion=${{ steps.versions.outputs.development_version }} \ -DtagNameFormat=java/v@{project.version} \ -DpushChanges=true \ -Darguments="-DskipTests" @@ -198,38 +174,303 @@ jobs: MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }} JAVA_GPG_PASSPHRASE: ${{ secrets.JAVA_GPG_PASSPHRASE }} - - name: Perform Release and Deploy to Maven Central - id: publish-maven + - name: Record immutable release identity + id: release-identity + run: | + TAG="java/v${{ steps.versions.outputs.release_version }}" + TAG_COMMIT=$(git rev-parse "${TAG}^{commit}") + POST_PREPARE_COMMIT=$(git rev-parse HEAD) + echo "release_tag=$TAG" >> "$GITHUB_OUTPUT" + echo "tag_commit=$TAG_COMMIT" >> "$GITHUB_OUTPUT" + echo "post_prepare_commit=$POST_PREPARE_COMMIT" >> "$GITHUB_OUTPUT" + + build-windows-classifier: + name: Build Windows native classifier + needs: prepare-release + runs-on: windows-latest + permissions: + contents: read + defaults: + run: + shell: pwsh working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.prepare-release.outputs.release_tag }} + fetch-depth: 1 + persist-credentials: false + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - name: Build and validate win32-x64 classifier + run: | + $sourceCommit = git rev-parse HEAD + if ($sourceCommit -ne '${{ needs.prepare-release.outputs.tag_commit }}') { + throw "Checked out $sourceCommit instead of the prepared tag commit." + } + node copilot-native/scripts/validate-native-host.mjs win32-x64 + mvn -B -pl copilot-native package -DskipTests + $version = '${{ needs.prepare-release.outputs.release_version }}' + $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar" + $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar" + if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) { + throw "Expected Windows classifier was not produced: $jar" + } + node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) .. + node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar + $manifest = "copilot-native/target/win32-x64-$version.sha256" + $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant() + "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest + node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar)) + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: java-native-win32-x64-release-${{ github.run_id }}-${{ github.run_attempt }} + path: | + java/copilot-native/target/copilot-sdk-java-runtime-${{ needs.prepare-release.outputs.release_version }}-win32-x64.jar + java/copilot-native/target/win32-x64-${{ needs.prepare-release.outputs.release_version }}.sha256 + if-no-files-found: error + retention-days: 1 + + deploy-maven: + name: Deploy Java release to Maven Central + needs: [prepare-release, build-windows-classifier] + runs-on: ubuntu-latest + permissions: + contents: read + defaults: + run: + shell: bash + working-directory: ./java + outputs: + version: ${{ needs.prepare-release.outputs.release_version }} + published: ${{ steps.publish-maven.outcome == 'success' }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ needs.prepare-release.outputs.release_tag }} + fetch-depth: 1 + persist-credentials: false + + - uses: ./.github/actions/setup-copilot + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + server-id: central + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.JAVA_GPG_SECRET_KEY }} + gpg-passphrase: JAVA_GPG_PASSPHRASE + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: java-native-win32-x64-release-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/java-native-win32-x64 + + - name: Verify immutable source and Windows classifier + id: windows-artifact + run: | + SOURCE_COMMIT=$(git rev-parse HEAD) + if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then + echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit." + exit 1 + fi + VERSION="${{ needs.prepare-release.outputs.release_version }}" + ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-x64" + JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar" + MANIFEST="$ARTIFACT_DIRECTORY/win32-x64-$VERSION.sha256" + test -f "$JAR" + test -f "$MANIFEST" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + checksum "$JAR" "$MANIFEST" "$(basename "$JAR")" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + classifier win32-x64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE" + echo "windows_jar=$JAR" >> "$GITHUB_OUTPUT" + echo "windows_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT" + + - name: Build Linux classifier and deploy complete release + id: publish-maven run: | - mvn -B release:perform \ - -Dgoals="deploy" \ - -Darguments="-DskipTests -Prelease" + VERSION="${{ needs.prepare-release.outputs.release_version }}" + mvn -B deploy -DskipTests -Prelease -Dcopilot.native.libc=glibc \ + "-Dcopilot.native.external.win32.classifier.path=${{ steps.windows-artifact.outputs.windows_jar }}" + LINUX_JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-x64.jar" + test -f "$LINUX_JAR" + node copilot-native/scripts/validate-native-artifact.mjs \ + classifier linux-x64 "$LINUX_JAR" "$(basename "$LINUX_JAR")" .. + LINUX_SHA=$(sha256sum "$LINUX_JAR" | cut -d ' ' -f 1) + { + echo "### Maven Central Release" + echo "- **Version:** $VERSION" + echo "- **Source tag:** \`${{ needs.prepare-release.outputs.release_tag }}\`" + echo "- **Source commit:** \`${{ needs.prepare-release.outputs.tag_commit }}\`" + echo "- **Next development version:** ${{ needs.prepare-release.outputs.development_version }}" + echo "- **Repository:** Maven Central" + echo "" + echo "#### Published Native Classifiers" + echo "" + echo "| Classifier | Build runner | Artifact | SHA-256 | Status |" + echo "| --- | --- | --- | --- | --- |" + echo "| \`linux-x64\` | \`ubuntu-latest\` | \`$(basename "$LINUX_JAR")\` | \`$LINUX_SHA\` | Published |" + echo "| \`win32-x64\` | \`windows-latest\` | \`$(basename "${{ steps.windows-artifact.outputs.windows_jar }}")\` | \`${{ steps.windows-artifact.outputs.windows_sha }}\` | Published |" + } >> "$GITHUB_STEP_SUMMARY" env: MAVEN_USERNAME: ${{ secrets.JAVA_MAVEN_CENTRAL_USERNAME }} MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }} JAVA_GPG_PASSPHRASE: ${{ secrets.JAVA_GPG_PASSPHRASE }} - - name: Rollback documentation commit on failure - if: failure() && steps.update-docs.outputs.docs_commit_sha != '' + rollback-release: + name: Roll back failed Java release preparation + needs: [prepare-release, build-windows-classifier, deploy-maven] + if: ${{ failure() && needs.prepare-release.outputs.docs_commit != '' }} + runs-on: ubuntu-latest + permissions: + contents: write + defaults: + run: + shell: bash working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + token: ${{ secrets.JAVA_RELEASE_TOKEN }} + + - name: Safely revert release commits and tag + env: + DOCS_COMMIT: ${{ needs.prepare-release.outputs.docs_commit }} + PREPARE_RESULT: ${{ needs.prepare-release.result }} + PRE_PREPARE_COMMIT: ${{ needs.prepare-release.outputs.pre_prepare_commit }} + POST_PREPARE_COMMIT: ${{ needs.prepare-release.outputs.post_prepare_commit }} + RELEASE_TAG: java/v${{ needs.prepare-release.outputs.release_version }} + TAG_COMMIT: ${{ needs.prepare-release.outputs.tag_commit }} run: | - echo "Release failed, rolling back documentation commit..." - git revert --no-edit ${{ steps.update-docs.outputs.docs_commit_sha }} - git push origin main + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git fetch origin main --tags + MAIN_COMMIT=$(git rev-parse origin/main) + echo "Rollback inspection: main=$MAIN_COMMIT docs=$DOCS_COMMIT pre=$PRE_PREPARE_COMMIT post=$POST_PREPARE_COMMIT tag=$RELEASE_TAG" - # Also run Maven release:rollback to clean up any partial release state - mvn -B release:rollback || true + unsafe_rollback() { + echo "::error::Unsafe rollback state: $*" >&2 + exit 1 + } + + if ! git cat-file -e "${PRE_PREPARE_COMMIT}^{commit}"; then + unsafe_rollback "Recorded pre-prepare commit does not exist: $PRE_PREPARE_COMMIT" + fi + if ! git cat-file -e "${DOCS_COMMIT}^{commit}"; then + unsafe_rollback "Recorded documentation commit does not exist: $DOCS_COMMIT" + fi + if ! git merge-base --is-ancestor "$PRE_PREPARE_COMMIT" "$MAIN_COMMIT"; then + unsafe_rollback "Recorded pre-prepare commit is not an ancestor of main." + fi + if [ "$(git rev-parse "${DOCS_COMMIT}^")" != "$PRE_PREPARE_COMMIT" ]; then + unsafe_rollback "Documentation commit is not immediately based on the recorded pre-prepare commit." + fi + + mapfile -t ROLLBACK_COMMITS < <(git rev-list --reverse "$PRE_PREPARE_COMMIT..$MAIN_COMMIT") + FIRST_PARENT_COUNT=$(git rev-list --count --first-parent "$PRE_PREPARE_COMMIT..$MAIN_COMMIT") + if [ "${#ROLLBACK_COMMITS[@]}" -ne "$FIRST_PARENT_COUNT" ]; then + unsafe_rollback "Release range contains merged history." + fi + if [ "${#ROLLBACK_COMMITS[@]}" -lt 1 ] || [ "${#ROLLBACK_COMMITS[@]}" -gt 3 ]; then + unsafe_rollback "Expected one to three release-preparation commits after the recorded base; found ${#ROLLBACK_COMMITS[@]}." + fi + if [ "${ROLLBACK_COMMITS[0]}" != "$DOCS_COMMIT" ]; then + unsafe_rollback "The first commit after the recorded base is not the recorded documentation commit." + fi + + EXPECTED_PARENT="$PRE_PREPARE_COMMIT" + for COMMIT in "${ROLLBACK_COMMITS[@]}"; do + if git rev-parse -q --verify "${COMMIT}^2" >/dev/null; then + unsafe_rollback "Release range contains merge commit $COMMIT." + fi + if [ "$(git rev-parse "${COMMIT}^")" != "$EXPECTED_PARENT" ]; then + unsafe_rollback "Release range is not a linear continuation of the recorded base." + fi + EXPECTED_PARENT="$COMMIT" + done + + RELEASE_VERSION="${RELEASE_TAG#java/v}" + if [ "$(git log -1 --format=%s "$DOCS_COMMIT")" != "docs: update version references to $RELEASE_VERSION" ]; then + unsafe_rollback "Recorded documentation commit has an unexpected subject." + fi + + RELEASE_PREPARE_COMMIT="" + if [ "${#ROLLBACK_COMMITS[@]}" -ge 2 ]; then + RELEASE_PREPARE_COMMIT="${ROLLBACK_COMMITS[1]}" + if [ "$(git log -1 --format=%s "$RELEASE_PREPARE_COMMIT")" != "[maven-release-plugin] prepare release $RELEASE_TAG" ]; then + unsafe_rollback "Release-version commit has an unexpected subject." + fi + fi + if [ "${#ROLLBACK_COMMITS[@]}" -eq 3 ] && [ "$(git log -1 --format=%s "${ROLLBACK_COMMITS[2]}")" != "[maven-release-plugin] prepare for next development iteration" ]; then + unsafe_rollback "Development-version commit has an unexpected subject." + fi + + TAG_OBJECT=$(git ls-remote --refs origin "refs/tags/$RELEASE_TAG" | awk '{print $1}') + if [ -n "$TAG_OBJECT" ]; then + git fetch --no-tags origin "+refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG" + REMOTE_TAG_COMMIT=$(git rev-parse "${RELEASE_TAG}^{commit}") + if [ -z "$RELEASE_PREPARE_COMMIT" ] || [ "$REMOTE_TAG_COMMIT" != "$RELEASE_PREPARE_COMMIT" ]; then + unsafe_rollback "Release tag does not point to the guarded release-version commit." + fi + elif [ -n "$TAG_COMMIT" ]; then + unsafe_rollback "Recorded release tag is absent from the remote." + fi + + if [ "$PREPARE_RESULT" = "success" ]; then + if [ -z "$POST_PREPARE_COMMIT" ] || [ -z "$TAG_COMMIT" ]; then + unsafe_rollback "Successful preparation did not record its immutable release identity." + fi + if [ "$MAIN_COMMIT" != "$POST_PREPARE_COMMIT" ]; then + unsafe_rollback "main has advanced beyond the recorded post-prepare commit." + fi + if [ -z "$TAG_OBJECT" ] || [ "$REMOTE_TAG_COMMIT" != "$TAG_COMMIT" ]; then + unsafe_rollback "Remote release tag does not match the recorded tag commit." + fi + fi + + git checkout -B release-rollback "$MAIN_COMMIT" + git revert --no-edit "$PRE_PREPARE_COMMIT..$MAIN_COMMIT" + if [ -n "$TAG_OBJECT" ]; then + git push --atomic \ + "--force-with-lease=refs/heads/main:$MAIN_COMMIT" \ + "--force-with-lease=refs/tags/$RELEASE_TAG:$TAG_OBJECT" \ + origin HEAD:refs/heads/main ":refs/tags/$RELEASE_TAG" + else + git push \ + "--force-with-lease=refs/heads/main:$MAIN_COMMIT" \ + origin HEAD:refs/heads/main + fi + echo "Release preparation rollback completed after guarded history inspection." deploy-site: name: Deploy Documentation Site - needs: [preflight, publish-maven] - if: github.ref == 'refs/heads/main' + needs: [preflight, deploy-maven] + if: github.ref == 'refs/heads/main' && needs.deploy-maven.outputs.published == 'true' runs-on: ubuntu-latest steps: - name: Trigger site deployment on standalone repo run: | - VERSION="${{ needs.publish-maven.outputs.version }}" + VERSION="${{ needs.deploy-maven.outputs.version }}" TAG="java/v${VERSION}" PUBLISH_AS_LATEST=true if [ "${{ inputs.prerelease }}" = "true" ]; then @@ -241,7 +482,9 @@ jobs: -f version="${VERSION}" \ -f publish_as_latest="${PUBLISH_AS_LATEST}" \ -f monorepo_tag="${TAG}" - echo "### Site Deployment" >> $GITHUB_STEP_SUMMARY - echo "Triggered deploy-site.yml on github/copilot-sdk-java for version ${VERSION}" >> $GITHUB_STEP_SUMMARY + { + echo "### Site Deployment" + echo "Triggered deploy-site.yml on github/copilot-sdk-java for version ${VERSION}" + } >> "$GITHUB_STEP_SUMMARY" env: GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_GITHUB_TOKEN }} diff --git a/.github/workflows/java-publish-snapshot.yml b/.github/workflows/java-publish-snapshot.yml index 8c957627ff..588c565f55 100644 --- a/.github/workflows/java-publish-snapshot.yml +++ b/.github/workflows/java-publish-snapshot.yml @@ -16,8 +16,86 @@ concurrency: cancel-in-progress: false jobs: - publish-snapshot: + resolve-source: + name: Resolve immutable snapshot source + runs-on: ubuntu-latest + outputs: + source_sha: ${{ steps.source.outputs.sha }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false + + - id: source + shell: bash + run: | + SHA=$(git rev-parse HEAD) + echo "sha=$SHA" >> "$GITHUB_OUTPUT" + + build-windows-classifier: + name: Build Windows snapshot classifier + needs: resolve-source + runs-on: windows-latest + permissions: + contents: read + defaults: + run: + shell: pwsh + working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - name: Build and validate win32-x64 classifier + id: build + run: | + $sourceCommit = git rev-parse HEAD + if ($sourceCommit -ne '${{ needs.resolve-source.outputs.source_sha }}') { + throw "Checked out $sourceCommit instead of the resolved snapshot source." + } + node copilot-native/scripts/validate-native-host.mjs win32-x64 + mvn -B -pl copilot-native package -DskipTests + $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout" + $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar" + $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar" + if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) { + throw "Expected Windows classifier was not produced: $jar" + } + node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) .. + node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar + $manifest = "copilot-native/target/win32-x64-$version.sha256" + $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant() + "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest + node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar)) + "version=$version" | Add-Content $env:GITHUB_OUTPUT + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: java-native-win32-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} + path: | + java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-x64.jar + java/copilot-native/target/win32-x64-${{ steps.build.outputs.version }}.sha256 + if-no-files-found: error + retention-days: 1 + + deploy-snapshot: name: Publish SNAPSHOT to Maven Central + needs: [resolve-source, build-windows-classifier] runs-on: ubuntu-latest defaults: run: @@ -26,12 +104,13 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - fetch-depth: 0 + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false - uses: ./.github/actions/setup-copilot - - name: Set up JDK 25 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 with: java-version: "25" distribution: "microsoft" @@ -40,22 +119,64 @@ jobs: server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD - - name: Verify version is a SNAPSHOT - working-directory: ./java + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: java-native-win32-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/java-native-win32-x64 + + - name: Verify version, source, and Windows classifier + id: windows-artifact run: | + SOURCE_COMMIT=$(git rev-parse HEAD) + if [ "$SOURCE_COMMIT" != "${{ needs.resolve-source.outputs.source_sha }}" ]; then + echo "::error::Checked out $SOURCE_COMMIT instead of the resolved snapshot source." + exit 1 + fi VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) - echo "Publishing version: $VERSION" if [[ "$VERSION" != *"-SNAPSHOT" ]]; then - echo "ERROR: This workflow only publishes SNAPSHOT versions. Current version: $VERSION" + echo "::error::This workflow only publishes SNAPSHOT versions. Current version: $VERSION" exit 1 fi - echo "### Snapshot Publish" >> $GITHUB_STEP_SUMMARY - echo "- **Version:** $VERSION" >> $GITHUB_STEP_SUMMARY - echo "- **Repository:** Maven Central Snapshots" >> $GITHUB_STEP_SUMMARY + ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-x64" + JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar" + MANIFEST="$ARTIFACT_DIRECTORY/win32-x64-$VERSION.sha256" + test -f "$JAR" + test -f "$MANIFEST" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + checksum "$JAR" "$MANIFEST" "$(basename "$JAR")" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + classifier win32-x64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE" + echo "windows_jar=$JAR" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "windows_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT" - - name: Deploy Snapshot - working-directory: ./java - run: mvn -B deploy -DskipTests + - name: Build Linux classifier and deploy complete snapshot + run: | + VERSION="${{ steps.windows-artifact.outputs.version }}" + mvn -B deploy -DskipTests -Dcopilot.native.libc=glibc \ + "-Dcopilot.native.external.win32.classifier.path=${{ steps.windows-artifact.outputs.windows_jar }}" + LINUX_JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-x64.jar" + test -f "$LINUX_JAR" + node copilot-native/scripts/validate-native-artifact.mjs \ + classifier linux-x64 "$LINUX_JAR" "$(basename "$LINUX_JAR")" .. + LINUX_SHA=$(sha256sum "$LINUX_JAR" | cut -d ' ' -f 1) + { + echo "### Snapshot Publish" + echo "- **Version:** $VERSION" + echo "- **Source commit:** \`${{ needs.resolve-source.outputs.source_sha }}\`" + echo "- **Repository:** Maven Central Snapshots" + echo "" + echo "#### Published Native Classifiers" + echo "" + echo "| Classifier | Build runner | Artifact | SHA-256 | Status |" + echo "| --- | --- | --- | --- | --- |" + echo "| \`linux-x64\` | \`ubuntu-latest\` | \`$(basename "$LINUX_JAR")\` | \`$LINUX_SHA\` | Published |" + echo "| \`win32-x64\` | \`windows-latest\` | \`$(basename "${{ steps.windows-artifact.outputs.windows_jar }}")\` | \`${{ steps.windows-artifact.outputs.windows_sha }}\` | Published |" + } >> "$GITHUB_STEP_SUMMARY" env: MAVEN_USERNAME: ${{ secrets.JAVA_MAVEN_CENTRAL_USERNAME }} MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }} diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml index 918e6ce10f..3093e2eabd 100644 --- a/.github/workflows/java-sdk-tests.yml +++ b/.github/workflows/java-sdk-tests.yml @@ -18,12 +18,19 @@ permissions: jobs: java-sdk-inprocess: - name: "Java SDK InProcess Tests" + name: "Java SDK InProcess Tests (${{ matrix.classifier }})" if: github.event.repository.fork == false - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + classifier: linux-x64 + - os: windows-latest + classifier: win32-x64 + runs-on: ${{ matrix.os }} defaults: run: - shell: bash working-directory: ./java steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -40,13 +47,13 @@ jobs: with: node-version: 22 - - name: Validate Linux glibc native host - run: node copilot-native/scripts/validate-native-host.mjs linux-x64 + - name: Validate native host + run: node copilot-native/scripts/validate-native-host.mjs ${{ matrix.classifier }} - name: Run Java SDK tests (InProcess) env: CI: "true" - run: mvn clean verify -Pinprocess -Dcopilot.native.skip.download=false + run: mvn clean verify -Pinprocess - name: Generate Test Report Summary if: always() @@ -58,13 +65,128 @@ jobs: if: failure() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: java-test-results-inprocess + name: java-test-results-inprocess-${{ matrix.classifier }} path: | java/sdk/target/surefire-reports/ java/sdk/target/surefire-reports-isolated/ java/sdk/target/failsafe-reports/ retention-days: 7 + java-native-publication-windows: + name: "Java Native Publication Input (win32-x64)" + if: github.event.repository.fork == false + runs-on: windows-latest + permissions: + contents: read + outputs: + source_sha: ${{ steps.build.outputs.source_sha }} + version: ${{ steps.build.outputs.version }} + defaults: + run: + shell: pwsh + working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - name: Build and validate win32-x64 classifier + id: build + run: | + node copilot-native/scripts/validate-native-host.mjs win32-x64 + mvn -B -pl copilot-native package -DskipTests + $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout" + $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar" + $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar" + if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) { + throw "Expected Windows classifier was not produced: $jar" + } + node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) .. + node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar + $manifest = "copilot-native/target/win32-x64-$version.sha256" + $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant() + "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest + node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar)) + echo "source_sha=$(git rev-parse HEAD)" >> $env:GITHUB_OUTPUT + echo "version=$version" >> $env:GITHUB_OUTPUT + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: java-native-publication-win32-x64-${{ github.run_id }}-${{ github.run_attempt }} + path: | + java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-x64.jar + java/copilot-native/target/win32-x64-${{ steps.build.outputs.version }}.sha256 + if-no-files-found: error + retention-days: 1 + + java-native-publication-assembly: + name: "Java Native Publication Assembly" + if: github.event.repository.fork == false + needs: java-native-publication-windows + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + persist-credentials: false + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: java-native-publication-win32-x64-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ github.workspace }}/java/native-publication-input + + - name: Verify Windows input and deploy the complete local release + run: | + set -euo pipefail + test "$(git rev-parse HEAD)" = "${{ needs.java-native-publication-windows.outputs.source_sha }}" + VERSION="${{ needs.java-native-publication-windows.outputs.version }}" + INPUT_DIRECTORY="$GITHUB_WORKSPACE/java/native-publication-input" + WINDOWS_JAR="$INPUT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar" + WINDOWS_MANIFEST="$INPUT_DIRECTORY/win32-x64-$VERSION.sha256" + node copilot-native/scripts/validate-native-artifact.mjs \ + checksum "$WINDOWS_JAR" "$WINDOWS_MANIFEST" "$(basename "$WINDOWS_JAR")" + node copilot-native/scripts/validate-native-artifact.mjs \ + classifier win32-x64 "$WINDOWS_JAR" "$(basename "$WINDOWS_JAR")" .. + export GNUPGHOME="$GITHUB_WORKSPACE/java/copilot-native/target/local-publication-gpg" + rm -rf "$GNUPGHOME" + mkdir -p "$GNUPGHOME" + chmod 700 "$GNUPGHOME" + gpg --batch --pinentry-mode loopback --passphrase '' \ + --quick-generate-key 'Copilot SDK local validation ' rsa2048 sign 1d + LOCAL_REPOSITORY="$GITHUB_WORKSPACE/java/copilot-native/target/local-publication-repository" + rm -rf "$LOCAL_REPOSITORY" + mvn -B -pl copilot-native deploy -Prelease -DskipTests \ + -Dcopilot.native.libc=glibc \ + -Dcopilot.native.test.local.publication=true \ + "-Dcopilot.native.external.win32.classifier.path=$WINDOWS_JAR" \ + "-Dmaven.repo.local=$LOCAL_REPOSITORY" + node copilot-native/scripts/validate-local-publication.mjs \ + "$LOCAL_REPOSITORY" copilot-sdk-java-runtime "$VERSION" .. --signatures + java-sdk: name: "Java SDK Tests (JDK ${{ matrix.test-jdk }})" if: github.event.repository.fork == false diff --git a/java/README.md b/java/README.md index 6c6124e959..71126045a7 100644 --- a/java/README.md +++ b/java/README.md @@ -72,7 +72,7 @@ implementation 'com.github:copilot-sdk-java:1.0.13-preview.0-SNAPSHOT' ## In-process mode (experimental) -The SDK supports running the Copilot runtime **in-process** as a native library instead of spawning a separate CLI process. This eliminates process management overhead and simplifies deployment. In-process mode is currently experimental and only supported on **linux-x64**. +The SDK supports running the Copilot runtime **in-process** as a native library instead of spawning a separate CLI process. This eliminates process management overhead and simplifies deployment. In-process mode is currently experimental and supported on **linux-x64** (glibc) and **win32-x64**. Because in-process mode is experimental, see the [Using experimental APIs](#using-experimental-apis) section for how to opt in. @@ -88,13 +88,14 @@ Add both the SDK and the platform-specific native runtime to your project: copilot-sdk-java ${copilot.version} - + com.github copilot-sdk-java-runtime ${copilot.version} linux-x64 + net.java.dev.jna @@ -491,7 +492,7 @@ mvn jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test- Run native-runtime Maven commands from the `java` directory. Native packaging requires Node.js and npm in addition to JDK 25 and Maven because `copilot-native/scripts/fetch-native.mjs` retrieves the pinned npm runtime package. -Validated on a native Linux x64 glibc host: Maven activates the `native-linux-x64` profile on Linux `amd64` when `copilot.native.libc=glibc` is set. The build validates the host before downloading or packaging native files. The profile runs the native script tests, fetches the pinned `@github/copilot-linux-x64` package during `generate-resources`, packages the `linux-x64` classifier JAR during `package`, and verifies its native contents. An absent or explicitly false `copilot.native.skip.download` value preserves normal native packaging. Ensure npm can authenticate to the package registry before running the build. +On a native Linux x64 glibc host, Maven activates the `native-linux-x64` profile when `copilot.native.libc=glibc` is set. On Windows x64, Maven activates `native-win32-x64` automatically. The matching profile validates the host, runs the native script tests, fetches the pinned `@github/copilot-` package during `generate-resources`, packages the classifier JAR during `package`, and verifies its native contents. Ensure npm can authenticate to the package registry before running the build. Before opting in, validate that Node.js reports glibc for the build host: @@ -506,7 +507,13 @@ The `inprocess` test profile performs the same validation and native packaging a mvn -Pinprocess clean verify ``` -On macOS, Windows, Linux ARM64, Linux x64 musl, and other unsupported hosts, do not set `copilot.native.libc=glibc`. A normal build produces only the OS-neutral primary, sources, and Javadoc JARs; it does not run the Linux x64 native script tests, download or stage Linux native files, or produce a `linux-x64` classifier JAR. +On Windows PowerShell, initialize Java and run the same profile: + +```powershell +mvn -Pinprocess clean verify +``` + +On macOS, Linux ARM64, Linux x64 musl, and other unsupported hosts, do not set `copilot.native.libc=glibc`. A normal build produces only the OS-neutral primary, sources, and Javadoc JARs; it does not run native script tests, download or stage native files, or produce a platform classifier JAR. To build only the OS-neutral artifacts on any host, or override the glibc opt-in, disable native download and packaging: @@ -524,7 +531,7 @@ mvn clean verify -Dcopilot.native.libc=glibc mvn clean package -pl copilot-native -DskipTests -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=true ``` -On a supported Linux x64 host, the classifier JAR contains `native/linux-x64/runtime.node`, `native/linux-x64/platform.properties`, and `native/linux-x64/copilot`. The placeholder JAR remains OS-neutral and contains no native binaries. Unsupported hosts retain the placeholder-only behavior without producing a `-linux-x64.jar`. +On Linux x64, the classifier JAR contains `native/linux-x64/runtime.node`, `native/linux-x64/platform.properties`, and `native/linux-x64/copilot`. On Windows x64, it contains `native/win32-x64/runtime.node`, `native/win32-x64/platform.properties`, and `native/win32-x64/copilot.exe`. The placeholder JAR remains OS-neutral and contains no native binaries. Unsupported hosts retain the placeholder-only behavior. ## License diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index 775fad47da..15f52ea856 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -113,6 +113,7 @@ --test ${project.basedir}/scripts/fetch-native.test.mjs ${project.basedir}/scripts/validate-native-host.test.mjs + ${project.basedir}/scripts/validate-native-artifact.test.mjs @@ -125,7 +126,7 @@ @@ -232,6 +233,60 @@ + + + inprocess + + linux-x64 + copilot + + + + + org.codehaus.mojo + exec-maven-plugin + + + validate-native-host + validate + + + fetch-native + generate-resources + + + test-fetch-native + test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + jar-native + package + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + package + + + + + + - inprocess + native-win32-x64 + + + Windows + amd64 + + - linux-x64 - copilot + win32-x64 + copilot.exe @@ -350,6 +405,156 @@ + + + attach-external-win32-classifier + + + copilot.native.external.win32.classifier.path + + + + + + org.codehaus.mojo + exec-maven-plugin + + + validate-external-win32-classifier + validate + + exec + + + node + + ${project.basedir}/scripts/validate-native-artifact.mjs + classifier + win32-x64 + ${copilot.native.external.win32.classifier.path} + ${project.build.finalName}-win32-x64.jar + ${copilot.sdk.root} + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-external-win32-classifier + package + + attach-artifact + + + + + ${copilot.native.external.win32.classifier.path} + jar + win32-x64 + + + + + + + + + + + + attach-test-linux-classifier + + + copilot.native.test.linux.classifier.path + + + + + + org.codehaus.mojo + exec-maven-plugin + + + validate-test-linux-classifier + validate + + exec + + + node + + ${project.basedir}/scripts/validate-native-artifact.mjs + classifier + linux-x64 + ${copilot.native.test.linux.classifier.path} + ${project.build.finalName}-linux-x64.jar + ${copilot.sdk.root} + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-test-linux-classifier + package + + attach-artifact + + + + + ${copilot.native.test.linux.classifier.path} + jar + linux-x64 + + + + + + + + + + + + local-publication-validation + + + copilot.native.test.local.publication + true + + + + + + org.sonatype.central + central-publishing-maven-plugin + + true + + + + + ${copilot.sdk.root}/nodejs/node_modules/@github/copilot/npm-loader.js - - ${copilot.sdk.root}/nodejs/node_modules/@github/copilot-linux-x64/copilot false +