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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ docs/.validation/
# Java
java/target
java/sdk/target
java/copilot-native/target
java/smoke-test
java/.classpath
java/.project
Expand Down
214 changes: 214 additions & 0 deletions java/copilot-native/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) Microsoft Corporation. All rights reserved.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java-parent</artifactId>
<version>1.0.10-preview.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<groupId>com.github</groupId>
<artifactId>copilot-sdk-java-runtime</artifactId>
<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>

<properties>
<!--
Root of the monorepo. This module lives at java/copilot-native/, so
two levels up reaches the monorepo root. Used to locate
nodejs/package-lock.json, which pins both the runtime version and
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
to Maven Central alongside the sdk module.
-->
<maven.deploy.skip>false</maven.deploy.skip>
</properties>

<build>
<resources>
<!--
Placeholder resource for the primary JAR. Filtered so that
${project.version} is substituted. Deliberately contains no
native binaries: the primary JAR exists only to satisfy Maven
Central validation, which requires a main artifact.
-->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</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.
-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>fetch-native-linux-x64</id>
<phase>generate-resources</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>node</executable>
<arguments>
<argument>${project.basedir}/scripts/fetch-native.mjs</argument>
<argument>${copilot.sdk.root}</argument>
<argument>${copilot.native.staging}</argument>
<argument>${copilot.native.classifier}</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<!--
Additional classifier JAR containing
native/<classifier>/runtime.node and
native/<classifier>/platform.properties.
-->
<execution>
<id>jar-linux-x64</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classifier>${copilot.native.classifier}</classifier>
<classesDirectory>${copilot.native.staging}/${copilot.native.classifier}</classesDirectory>
<excludes>
<exclude>.version</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
<!--
Structural guards: assert that the classifier JAR carries the
native binary and that the placeholder JAR does not.
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>verify-native-jars</id>
<phase>package</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<condition property="native.binary.present">
<resourceexists>
<zipentry zipfile="${project.build.directory}/${project.build.finalName}-${copilot.native.classifier}.jar" name="native/${copilot.native.classifier}/runtime.node" />
</resourceexists>
</condition>
<fail unless="native.binary.present" message="Classifier JAR is missing native/${copilot.native.classifier}/runtime.node" />
<condition property="native.properties.present">
<resourceexists>
<zipentry zipfile="${project.build.directory}/${project.build.finalName}-${copilot.native.classifier}.jar" name="native/${copilot.native.classifier}/platform.properties" />
</resourceexists>
</condition>
<fail unless="native.properties.present" message="Classifier JAR is missing native/${copilot.native.classifier}/platform.properties" />
<condition property="placeholder.jar.polluted">
<resourceexists>
<zipentry zipfile="${project.build.directory}/${project.build.finalName}.jar" name="native/${copilot.native.classifier}/runtime.node" />
</resourceexists>
</condition>
<fail if="placeholder.jar.polluted" message="Placeholder primary JAR must not contain native binaries" />
</target>
</configuration>
</execution>
</executions>
</plugin>
<!--
Required by Maven Central: sources and javadoc artifacts. This
module has no Java sources, so both produce empty archives.
-->
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<!--
Skip the npm download when building offline or when only the
placeholder JAR is needed: mvn -Dcopilot.native.skip.download=true
-->
<profile>
<id>skip-native-download</id>
<activation>
<property>
<name>copilot.native.skip.download</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>jar-linux-x64</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>verify-native-jars</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
99 changes: 99 additions & 0 deletions java/copilot-native/scripts/fetch-native.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

/**
* Downloads the `runtime.node` native binary for a single platform classifier
* and stages it for packaging into a classifier JAR.
*
* Steps:
* 1. Read the pinned version and the SHA-512 `integrity` value for
* `@github/copilot-<classifier>` from `nodejs/package-lock.json`.
* 2. `npm pack` that exact version into the staging directory.
* 3. Verify the downloaded tarball against the `integrity` value.
* 4. Extract `package/prebuilds/<classifier>/runtime.node` to
* `<staging>/<classifier>/native/<classifier>/runtime.node`.
* 5. Write `<staging>/<classifier>/native/<classifier>/platform.properties`.
*
* Usage: node fetch-native.mjs <repoRoot> <stagingDir> <classifier>
*/

import { createHash } from 'node:crypto';
import { execFileSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';

const [repoRoot, stagingDir, classifier] = process.argv.slice(2);

if (!repoRoot || !stagingDir || !classifier) {
console.error('Usage: node fetch-native.mjs <repoRoot> <stagingDir> <classifier>');
process.exit(1);
}

const lockPath = path.join(repoRoot, 'nodejs', 'package-lock.json');
const packageName = `@github/copilot-${classifier}`;
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
const entry = lock.packages?.[`node_modules/${packageName}`];

if (!entry?.version || !entry?.integrity) {
console.error(`Could not find version/integrity for ${packageName} in ${lockPath}`);
process.exit(1);
}

const { version, integrity } = entry;
if (!integrity.startsWith('sha512-')) {
console.error(`Unsupported integrity algorithm for ${packageName}: ${integrity}`);
process.exit(1);
}

const outDir = path.join(stagingDir, classifier);
const resourceDir = path.join(outDir, 'native', classifier);
const runtimePath = path.join(resourceDir, 'runtime.node');
const stampPath = path.join(outDir, '.version');

// Idempotence: skip the download when the staged binary already matches.
// The stamp stores version + integrity + binary digest to ensure a corrupted
// binary or lockfile integrity change is detected.
if (fs.existsSync(runtimePath) && fs.existsSync(stampPath)) {
const stampLines = fs.readFileSync(stampPath, 'utf8').trim().split('\n');
const stampVersion = stampLines[0] || '';
const stampIntegrity = stampLines[1] || '';
const stampBinaryDigest = stampLines[2] || '';
const currentBinaryDigest = `sha512-${createHash('sha512').update(fs.readFileSync(runtimePath)).digest('base64')}`;
if (stampVersion === version && stampIntegrity === integrity && stampBinaryDigest === currentBinaryDigest) {
console.log(`${packageName}@${version} already staged at ${runtimePath}`);
process.exit(0);
}
}

fs.rmSync(outDir, { recursive: true, force: true });
fs.mkdirSync(resourceDir, { recursive: true });

console.log(`Downloading ${packageName}@${version} ...`);
const packOutput = execFileSync('npm', ['pack', `${packageName}@${version}`, '--pack-destination', outDir], {
encoding: 'utf8',
shell: process.platform === 'win32',
});
const tarballName = packOutput.trim().split('\n').pop().trim();
const tarballPath = path.join(outDir, tarballName);

const actual = `sha512-${createHash('sha512').update(fs.readFileSync(tarballPath)).digest('base64')}`;
if (actual !== integrity) {
console.error(`Integrity verification failed for ${tarballPath}`);
console.error(` expected: ${integrity}`);
console.error(` actual: ${actual}`);
process.exit(1);
}
console.log(`Integrity verified (${integrity.slice(0, 20)}...).`);

const memberPath = `package/prebuilds/${classifier}/runtime.node`;
execFileSync('tar', ['-xzf', tarballPath, '-C', outDir, memberPath], { stdio: 'inherit' });
fs.renameSync(path.join(outDir, memberPath), runtimePath);
fs.rmSync(path.join(outDir, 'package'), { recursive: true, force: true });
fs.rmSync(tarballPath, { force: true });

fs.writeFileSync(path.join(resourceDir, 'platform.properties'), `classifier=${classifier}\nversion=${version}\n`);
const binaryDigest = `sha512-${createHash('sha512').update(fs.readFileSync(runtimePath)).digest('base64')}`;
fs.writeFileSync(stampPath, `${version}\n${integrity}\n${binaryDigest}\n`);

console.log(`Staged ${runtimePath}`);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Placeholder marker for the primary (classifier-less) artifact of
# com.github:copilot-sdk-java-runtime.
#
# The real native binaries ship in per-platform classifier JARs
# (e.g. copilot-sdk-java-runtime-<version>-linux-x64.jar) under
# native/<classifier>/runtime.node. This primary JAR exists only to satisfy
# Maven Central's requirement for a main artifact and intentionally contains
# no native binaries.
#
# This file is processed by Maven resource filtering.
placeholder=true
version=${project.version}
6 changes: 3 additions & 3 deletions java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@

<modules>
<module>sdk</module>
<module>copilot-native</module>
<!--
copilot-native and copilot-native-all are added in later tasks (4.7+).
Uncomment when the module directories and POMs are created.
copilot-native-all (the monolithic uber-JAR) is added in a later task.
Uncomment when the module directory and POM are created.
-->
<!-- <module>copilot-native</module> -->
<!-- <module>copilot-native-all</module> -->
</modules>

Expand Down
Loading