Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/java-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ jobs:
with:
node-version: 22

- name: Validate Linux glibc native host
run: node copilot-native/scripts/validate-native-host.mjs linux-x64

- name: Run Java SDK tests (InProcess)
env:
CI: "true"
run: mvn clean verify -Pinprocess
run: mvn clean verify -Pinprocess -Dcopilot.native.skip.download=false

- name: Generate Test Report Summary
if: always()
Expand Down Expand Up @@ -125,7 +128,9 @@ jobs:
if: matrix.test-jdk == '25'
env:
CI: "true"
run: mvn verify -Dskip.test.harness=true
run: |
node copilot-native/scripts/validate-native-host.mjs linux-x64
mvn verify -Dskip.test.harness=true -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=false

- name: Switch to JDK 17
if: matrix.test-jdk == '17'
Expand Down
39 changes: 39 additions & 0 deletions java/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,45 @@ mvn verify -Dskip.test.harness=true
mvn jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test-jdk-banner surefire:test failsafe:integration-test failsafe:verify jacoco:report@build-coverage-report-from-tests -Denforcer.skip=true
```

#### Development Setup for native embedding

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.

Before opting in, validate that Node.js reports glibc for the build host:

```bash
node copilot-native/scripts/validate-native-host.mjs linux-x64
mvn -pl copilot-native clean verify -Dcopilot.native.libc=glibc
```

The `inprocess` test profile performs the same validation and native packaging automatically, so the full in-process test command remains:

```bash
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.

To build only the OS-neutral artifacts on any host, or override the glibc opt-in, disable native download and packaging:

```bash
mvn -pl copilot-native clean package -DskipTests -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=true
```

The verified Linux x64 checks are:

```bash
node --test copilot-native/scripts/fetch-native.test.mjs copilot-native/scripts/validate-native-host.test.mjs
mvn -pl copilot-native help:active-profiles -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=false
mvn -pl copilot-native test -Dcopilot.native.libc=glibc
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`.

## License

MIT — see [LICENSE](sdk/LICENSE) for details.
187 changes: 165 additions & 22 deletions java/copilot-native/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<packaging>jar</packaging>

<name>GitHub Copilot SDK :: Java :: Native Runtime</name>
<description>Native runtime binaries for the GitHub Copilot Java SDK, published as per-platform classifier JARs</description>
<description>Native runtime artifacts for the GitHub Copilot Java SDK, with host-matched platform classifier JARs</description>
<url>https://github.com/github/copilot-sdk</url>

<scm>
Expand All @@ -35,12 +35,6 @@
its SHA-512 integrity hash.
-->
<copilot.sdk.root>${project.basedir}/../..</copilot.sdk.root>
<!--
Phase 4 hard invariant: linux-x64 is the only supported platform.
Additional classifiers are added in a later phase, each with its own
fetch execution and maven-jar-plugin execution.
-->
<copilot.native.classifier>linux-x64</copilot.native.classifier>
<copilot.native.staging>${project.build.directory}/native-staging</copilot.native.staging>
<!--
The native runtime is downloaded from npm; this module is published
Expand All @@ -64,20 +58,35 @@
</resources>
<plugins>
<!--
Download and verify the native runtime binary. The helper script
reads the pinned version and SHA-512 integrity hash for
@github/copilot-<classifier> from nodejs/package-lock.json,
runs `npm pack`, verifies the tarball against that hash, and
extracts runtime.node into the staging directory. Requires
Node.js and npm — already required for the Java E2E tests.
On a supported build host, download and verify the matching native
runtime binary. The helper script reads the pinned version and
SHA-512 integrity hash for @github/copilot-<classifier> from
nodejs/package-lock.json, runs `npm pack`, verifies the tarball
against that hash, and extracts runtime.node into the staging
directory. Requires Node.js and npm — already required for the
Java E2E tests.
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>fetch-native-linux-x64</id>
<phase>generate-resources</phase>
<id>validate-native-host</id>
<phase>none</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>node</executable>
<arguments>
<argument>${project.basedir}/scripts/validate-native-host.mjs</argument>
<argument>${copilot.native.classifier}</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>fetch-native</id>
<phase>none</phase>
<goals>
<goal>exec</goal>
</goals>
Expand All @@ -93,15 +102,17 @@
</execution>
<execution>
<id>test-fetch-native</id>
<phase>test</phase>
<phase>none</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<skip>${skipTests}</skip>
<executable>node</executable>
<arguments>
<argument>--test</argument>
<argument>${project.basedir}/scripts/fetch-native.test.mjs</argument>
<argument>${project.basedir}/scripts/validate-native-host.test.mjs</argument>
</arguments>
</configuration>
</execution>
Expand All @@ -118,8 +129,8 @@
native/<classifier>/platform.properties.
-->
<execution>
<id>jar-linux-x64</id>
<phase>package</phase>
<id>jar-native</id>
<phase>none</phase>
<goals>
<goal>jar</goal>
</goals>
Expand Down Expand Up @@ -173,7 +184,7 @@
<executions>
<execution>
<id>verify-native-jars</id>
<phase>package</phase>
<phase>none</phase>
<goals>
<goal>run</goal>
</goals>
Expand All @@ -193,10 +204,10 @@
<fail unless="native.properties.present" message="Classifier JAR is missing native/${copilot.native.classifier}/platform.properties" />
<condition property="native.cli.present">
<resourceexists>
<zipentry zipfile="${project.build.directory}/${project.build.finalName}-${copilot.native.classifier}.jar" name="native/${copilot.native.classifier}/copilot" />
<zipentry zipfile="${project.build.directory}/${project.build.finalName}-${copilot.native.classifier}.jar" name="native/${copilot.native.classifier}/${copilot.native.cli.filename}" />
</resourceexists>
</condition>
<fail unless="native.cli.present" message="Classifier JAR is missing native/${copilot.native.classifier}/copilot" />
<fail unless="native.cli.present" message="Classifier JAR is missing native/${copilot.native.classifier}/${copilot.native.cli.filename}" />
<condition property="placeholder.jar.polluted">
<resourceexists>
<zipentry zipfile="${project.build.directory}/${project.build.finalName}.jar" name="native/${copilot.native.classifier}/runtime.node" />
Expand All @@ -221,6 +232,124 @@
</build>

<profiles>
<!--
Bind native packaging only when the build host matches an implemented
classifier. Add sibling profiles as additional platforms are supported.
-->
<profile>
<id>native-linux-x64</id>
<activation>
<os>
<name>Linux</name>
<arch>amd64</arch>
</os>
Comment thread
edburns marked this conversation as resolved.
<property>
<name>copilot.native.libc</name>
<value>glibc</value>
</property>
Comment thread
edburns marked this conversation as resolved.
</activation>
<properties>
<copilot.native.classifier>linux-x64</copilot.native.classifier>
<copilot.native.cli.filename>copilot</copilot.native.cli.filename>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>validate-native-host</id>
<phase>validate</phase>
</execution>
<execution>
<id>fetch-native</id>
<phase>generate-resources</phase>
</execution>
<execution>
<id>test-fetch-native</id>
<phase>test</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>jar-native</id>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>verify-native-jars</id>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!--
The SDK's inprocess test profile requires this module's classifier
artifact. Bind the same guarded native lifecycle so `-Pinprocess`
works without a separate libc property. Host validation fails
before download or packaging on non-glibc and unsupported hosts.
-->
<profile>
<id>inprocess</id>
<properties>
<copilot.native.classifier>linux-x64</copilot.native.classifier>
<copilot.native.cli.filename>copilot</copilot.native.cli.filename>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>validate-native-host</id>
<phase>validate</phase>
</execution>
<execution>
<id>fetch-native</id>
<phase>generate-resources</phase>
</execution>
<execution>
<id>test-fetch-native</id>
<phase>test</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>jar-native</id>
<phase>package</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>verify-native-jars</id>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<!--
Skip the npm download when building offline or when only the
placeholder JAR is needed: mvn -Dcopilot.native.skip.download=true
Expand All @@ -241,13 +370,27 @@
<configuration>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>validate-native-host</id>
<phase>none</phase>
</execution>
<execution>
<id>fetch-native</id>
<phase>none</phase>
</execution>
<execution>
<id>test-fetch-native</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>jar-linux-x64</id>
<id>jar-native</id>
<phase>none</phase>
</execution>
</executions>
Expand Down
55 changes: 55 additions & 0 deletions java/copilot-native/scripts/validate-native-host.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import { pathToFileURL } from "node:url";

export function validateNativeHost(classifier, host) {
if (classifier !== "linux-x64") {
throw new Error(`Unsupported native build classifier: ${classifier}`);
}
if (host.platform !== "linux" || host.arch !== "x64") {
throw new Error(
`Native ${classifier} packaging requires Linux x64; detected ${host.platform}-${host.arch}`,
);
}
if (!host.glibcVersionRuntime) {
throw new Error(
`Native ${classifier} packaging requires glibc; musl and unknown libc hosts are unsupported`,
);
}

return `Validated native build host: ${classifier} (glibc ${host.glibcVersionRuntime})`;
}

export function detectNativeHost() {
const report = process.report?.getReport();
return {
platform: process.platform,
arch: process.arch,
glibcVersionRuntime: report?.header?.glibcVersionRuntime,
};
}

function main() {
const [classifier] = process.argv.slice(2);
if (!classifier) {
console.error("Usage: node validate-native-host.mjs <classifier>");
process.exitCode = 1;
return;
}

try {
console.log(validateNativeHost(classifier, detectNativeHost()));
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
}

if (
process.argv[1] &&
import.meta.url === pathToFileURL(process.argv[1]).href
) {
main();
}
Loading
Loading