From 2fb3b27dcd96dfce3054964837b850b4b0a6b0a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:07:57 +0000 Subject: [PATCH 1/3] Initial plan From dfa43a50373755d11d9139c6212466986e07b52a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Aug 2026 21:20:13 +0000 Subject: [PATCH 2/3] Add copilot-native module with linux-x64 classifier JAR Co-authored-by: edburns <75821+edburns@users.noreply.github.com> --- .gitignore | 1 + java/copilot-native/pom.xml | 208 ++++++++++++++++++ java/copilot-native/scripts/fetch-native.mjs | 89 ++++++++ .../native/lib/copilot-runtime.properties | 12 + java/pom.xml | 6 +- 5 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 java/copilot-native/pom.xml create mode 100644 java/copilot-native/scripts/fetch-native.mjs create mode 100644 java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties diff --git a/.gitignore b/.gitignore index 714dd47f52..4d039ac903 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ docs/.validation/ # Java java/target java/sdk/target +java/copilot-native/target java/smoke-test java/.classpath java/.project diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml new file mode 100644 index 0000000000..52292bf916 --- /dev/null +++ b/java/copilot-native/pom.xml @@ -0,0 +1,208 @@ + + + + 4.0.0 + + + com.github + copilot-sdk-java-parent + 1.0.10-preview.0-SNAPSHOT + ../pom.xml + + + com.github + copilot-sdk-java-runtime + jar + + GitHub Copilot SDK :: Java :: Native Runtime + Native runtime binaries for the GitHub Copilot Java SDK, published as per-platform classifier JARs + + + + ${project.basedir}/../.. + + linux-x64 + ${project.build.directory}/native-staging + + false + + + + + + + src/main/resources + true + + + + + + org.codehaus.mojo + exec-maven-plugin + + + fetch-native-linux-x64 + generate-resources + + exec + + + node + + ${project.basedir}/scripts/fetch-native.mjs + ${copilot.sdk.root} + ${copilot.native.staging} + ${copilot.native.classifier} + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + jar-linux-x64 + package + + jar + + + ${copilot.native.classifier} + ${copilot.native.staging}/${copilot.native.classifier} + + .version + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + package + + run + + + + + + + + + + + + + + + + + + + + + + + org.sonatype.central + central-publishing-maven-plugin + true + + central + true + + + + + + + + + skip-native-download + + + copilot.native.skip.download + true + + + + + + org.codehaus.mojo + exec-maven-plugin + + true + + + + org.apache.maven.plugins + maven-jar-plugin + + + jar-linux-x64 + none + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + none + + + + + + + + diff --git a/java/copilot-native/scripts/fetch-native.mjs b/java/copilot-native/scripts/fetch-native.mjs new file mode 100644 index 0000000000..3a71d4329d --- /dev/null +++ b/java/copilot-native/scripts/fetch-native.mjs @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * 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-` 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//runtime.node` to + * `//native//runtime.node`. + * 5. Write `//native//platform.properties`. + * + * Usage: node fetch-native.mjs + */ + +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 '); + 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. +if (fs.existsSync(runtimePath) && fs.existsSync(stampPath) && fs.readFileSync(stampPath, 'utf8').trim() === version) { + 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`); +fs.writeFileSync(stampPath, `${version}\n`); + +console.log(`Staged ${runtimePath}`); diff --git a/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties new file mode 100644 index 0000000000..0f32308984 --- /dev/null +++ b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties @@ -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--linux-x64.jar) under +# native//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} diff --git a/java/pom.xml b/java/pom.xml index efffeca927..9b4a92294c 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -38,11 +38,11 @@ sdk + copilot-native - From d216d21de2908f45af44115de1b8feb755729829 Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Mon, 3 Aug 2026 21:48:08 +0000 Subject: [PATCH 3/3] Address Copilot review: harden cache stamp and add platform.properties assertion - Persist integrity hash and binary digest in the .version stamp file so corrupted binaries or lockfile integrity changes are detected on cache hit. - Add zipentry assertion for platform.properties in the classifier JAR structural guard. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- java/copilot-native/pom.xml | 6 ++++++ java/copilot-native/scripts/fetch-native.mjs | 18 ++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index 52292bf916..de57c313fe 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -132,6 +132,12 @@ + + + + + + diff --git a/java/copilot-native/scripts/fetch-native.mjs b/java/copilot-native/scripts/fetch-native.mjs index 3a71d4329d..18449badfb 100644 --- a/java/copilot-native/scripts/fetch-native.mjs +++ b/java/copilot-native/scripts/fetch-native.mjs @@ -52,9 +52,18 @@ const runtimePath = path.join(resourceDir, 'runtime.node'); const stampPath = path.join(outDir, '.version'); // Idempotence: skip the download when the staged binary already matches. -if (fs.existsSync(runtimePath) && fs.existsSync(stampPath) && fs.readFileSync(stampPath, 'utf8').trim() === version) { - console.log(`${packageName}@${version} already staged at ${runtimePath}`); - process.exit(0); +// 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 }); @@ -84,6 +93,7 @@ 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`); -fs.writeFileSync(stampPath, `${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}`);