Skip to content

Commit 05b331b

Browse files
committed
Keep default runtime bundles SEA-free
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3f5c3c30-0cf7-4b9a-9a3b-2b01ca073015
1 parent 289a768 commit 05b331b

13 files changed

Lines changed: 334 additions & 332 deletions

File tree

‎dotnet/test/E2E/RpcExtensionsLoadedE2ETests.cs‎

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@ namespace GitHub.Copilot.Test.E2E;
2525
public class RpcExtensionsLoadedE2ETests(E2ETestFixture fixture, ITestOutputHelper output)
2626
: E2ETestBase(fixture, "rpc_extensions_loaded", output)
2727
{
28-
// TODO(PR #2395): Temporarily disabled while this PR transitions managed out-of-process SDK
29-
// launches to the Rust-only flow. Re-enable when that flow provides the Node extension
30-
// subprocess lifecycle required by the EXTENSIONS controller.
31-
private const string RustOnlyFlowSkipReason =
32-
"Temporarily disabled for the Rust-only out-of-process transition in PR #2395";
33-
3428
/// <summary>
3529
/// Extension subprocess startup involves Node fork + SDK resolver + JSON-RPC
3630
/// handshake. Empirically this completes in well under a second on Windows,
@@ -169,7 +163,7 @@ await TestHelper.WaitForConditionAsync(
169163
return lastSeen!;
170164
}
171165

172-
[Theory(Skip = RustOnlyFlowSkipReason)]
166+
[Theory]
173167
[InlineData("user")]
174168
[InlineData("project")]
175169
public async Task Discovers_Loads_And_Reports_Running_Extension(string sourceValue)
@@ -212,7 +206,7 @@ public async Task Discovers_Loads_And_Reports_Running_Extension(string sourceVal
212206
Assert.True(ext.Pid > 0);
213207
}
214208

215-
[Fact(Skip = RustOnlyFlowSkipReason)]
209+
[Fact]
216210
public async Task Disable_Then_Enable_Cycles_Extension_Status()
217211
{
218212
var extName = CreateUserExtension();
@@ -240,7 +234,7 @@ public async Task Disable_Then_Enable_Cycles_Extension_Status()
240234
Assert.NotNull(reEnabled.Pid);
241235
}
242236

243-
[Fact(Skip = RustOnlyFlowSkipReason)]
237+
[Fact]
244238
public async Task Reload_Picks_Up_Extension_Added_After_Session_Create()
245239
{
246240
// Start the session BEFORE writing the extension so the initial discovery sees nothing.
@@ -274,7 +268,7 @@ await TestHelper.WaitForConditionAsync(
274268
Assert.Equal(ExtensionSource.User, ext.Source);
275269
}
276270

277-
[Fact(Skip = RustOnlyFlowSkipReason)]
271+
[Fact]
278272
public async Task Failed_Extension_Reports_Failed_Status()
279273
{
280274
// Write an extension whose body throws synchronously at import time.
@@ -302,7 +296,7 @@ public async Task Failed_Extension_Reports_Failed_Status()
302296
Assert.Equal(ExtensionSource.User, ext.Source);
303297
}
304298

305-
[Fact(Skip = RustOnlyFlowSkipReason)]
299+
[Fact]
306300
public async Task Multiple_Extensions_Are_Discovered_Independently()
307301
{
308302
var ext1Name = CreateUserExtension(prefix: "multi-a");
@@ -326,7 +320,7 @@ public async Task Multiple_Extensions_Are_Discovered_Independently()
326320
Assert.Equal(pids.Count, pids.Distinct().Count());
327321
}
328322

329-
[Fact(Skip = RustOnlyFlowSkipReason)]
323+
[Fact]
330324
public async Task Reload_Preserves_Disabled_State_Across_Calls()
331325
{
332326
var extName = CreateUserExtension(prefix: "persistent-disable");

‎java/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ mvn clean verify -Dcopilot.native.libc=glibc
560560
mvn clean package -pl copilot-native -DskipTests -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=true
561561
```
562562

563-
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`. On Apple Silicon macOS, it contains `native/darwin-arm64/runtime.node`, `native/darwin-arm64/platform.properties`, and `native/darwin-arm64/copilot`. The placeholder JAR remains OS-neutral and contains no native binaries. Unsupported hosts retain the placeholder-only behavior.
563+
Each classifier JAR includes `runtime.node`, `platform.properties`, and `copilot-runtime` (or `copilot-runtime.exe`) under its `native/<classifier>` directory. It does not contain the legacy `copilot` SEA. The placeholder JAR remains OS-neutral and contains no native binaries. Unsupported hosts retain the placeholder-only behavior.
564564

565565
## License
566566

‎java/copilot-native/pom.xml‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,7 @@
126126
<!--
127127
Additional classifier JAR containing
128128
native/<classifier>/runtime.node,
129-
native/<classifier>/copilot-runtime or copilot-runtime.exe,
130-
native/<classifier>/copilot or copilot.exe, and
129+
native/<classifier>/copilot-runtime or copilot-runtime.exe, and
131130
native/<classifier>/platform.properties.
132131
-->
133132
<execution>
@@ -204,12 +203,6 @@
204203
</resourceexists>
205204
</condition>
206205
<fail unless="native.properties.present" message="Classifier JAR is missing native/${copilot.native.classifier}/platform.properties" />
207-
<condition property="native.cli.present">
208-
<resourceexists>
209-
<zipentry zipfile="${project.build.directory}/${project.build.finalName}-${copilot.native.classifier}.jar" name="native/${copilot.native.classifier}/${copilot.native.cli.filename}" />
210-
</resourceexists>
211-
</condition>
212-
<fail unless="native.cli.present" message="Classifier JAR is missing native/${copilot.native.classifier}/${copilot.native.cli.filename}" />
213206
<condition property="native.wrapper.filename" value="copilot-runtime.exe" else="copilot-runtime">
214207
<matches string="${copilot.native.classifier}" pattern="^win32-" />
215208
</condition>
@@ -253,7 +246,6 @@
253246
<id>inprocess</id>
254247
<properties>
255248
<copilot.native.classifier>linux-x64</copilot.native.classifier>
256-
<copilot.native.cli.filename>copilot</copilot.native.cli.filename>
257249
</properties>
258250
<build>
259251
<plugins>
@@ -316,7 +308,6 @@
316308
</activation>
317309
<properties>
318310
<copilot.native.classifier>linux-x64</copilot.native.classifier>
319-
<copilot.native.cli.filename>copilot</copilot.native.cli.filename>
320311
</properties>
321312
<build>
322313
<plugins>
@@ -371,7 +362,6 @@
371362
</activation>
372363
<properties>
373364
<copilot.native.classifier>win32-x64</copilot.native.classifier>
374-
<copilot.native.cli.filename>copilot.exe</copilot.native.cli.filename>
375365
</properties>
376366
<build>
377367
<plugins>
@@ -426,7 +416,6 @@
426416
</activation>
427417
<properties>
428418
<copilot.native.classifier>darwin-arm64</copilot.native.classifier>
429-
<copilot.native.cli.filename>copilot</copilot.native.cli.filename>
430419
</properties>
431420
<build>
432421
<plugins>

‎java/copilot-native/scripts/fetch-native.mjs‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ const excludedTopLevel = new Set([
2727
'app.js',
2828
'assets',
2929
'changelog.json',
30+
'copilot',
31+
'copilot.exe',
3032
'copilot-sdk',
3133
'foundry-local-sdk',
3234
'index.js',
@@ -70,32 +72,32 @@ const outDir = path.join(stagingDir, classifier);
7072
const resourceDir = path.join(outDir, 'native', classifier);
7173
const runtimePath = path.join(resourceDir, 'runtime.node');
7274
const isWindows = classifier.startsWith('win32');
73-
const cliFilename = isWindows ? 'copilot.exe' : 'copilot';
74-
const cliPath = path.join(resourceDir, cliFilename);
7575
const wrapperFilename = isWindows ? 'copilot-runtime.exe' : 'copilot-runtime';
7676
const wrapperPath = path.join(resourceDir, wrapperFilename);
7777
const inventoryPath = path.join(resourceDir, 'runtime-assets.list');
7878
const platformPropertiesPath = path.join(resourceDir, 'platform.properties');
7979
const expectedPlatformProperties = `classifier=${classifier}\nversion=${version}\n`;
80+
const stagingSchema = 'hostless-runtime-v2';
8081
const stampPath = path.join(outDir, '.version');
8182

8283
// Idempotence: skip the download only when every required staged artifact
8384
// matches the package identity recorded in the stamp.
8485
if (
8586
fs.existsSync(runtimePath) &&
8687
fs.existsSync(wrapperPath) &&
87-
fs.existsSync(cliPath) &&
8888
fs.existsSync(inventoryPath) &&
8989
fs.existsSync(platformPropertiesPath) &&
9090
fs.existsSync(stampPath)
9191
) {
9292
const stampLines = fs.readFileSync(stampPath, 'utf8').trim().split('\n');
93-
const stampVersion = stampLines[0] || '';
94-
const stampIntegrity = stampLines[1] || '';
95-
const stampTreeDigest = stampLines[2] || '';
93+
const stampSchema = stampLines[0] || '';
94+
const stampVersion = stampLines[1] || '';
95+
const stampIntegrity = stampLines[2] || '';
96+
const stampTreeDigest = stampLines[3] || '';
9697
const currentTreeDigest = digestTree(resourceDir);
9798
const currentPlatformProperties = fs.readFileSync(platformPropertiesPath, 'utf8');
9899
if (
100+
stampSchema === stagingSchema &&
99101
stampVersion === version &&
100102
stampIntegrity === integrity &&
101103
stampTreeDigest === currentTreeDigest &&
@@ -162,12 +164,12 @@ fs.writeFileSync(inventoryPath, `${inventory.join('\n')}\n`);
162164

163165
fs.rmSync(tarballPath, { force: true });
164166

165-
if (!fs.existsSync(runtimePath) || !fs.existsSync(wrapperPath) || !fs.existsSync(cliPath)) {
166-
throw new Error(`Package ${packageName}@${version} is missing the CLI or runtime wrapper pair`);
167+
if (!fs.existsSync(runtimePath) || !fs.existsSync(wrapperPath)) {
168+
throw new Error(`Package ${packageName}@${version} is missing the runtime wrapper pair`);
167169
}
168170
fs.writeFileSync(platformPropertiesPath, expectedPlatformProperties);
169171
const treeDigest = digestTree(resourceDir);
170-
fs.writeFileSync(stampPath, `${version}\n${integrity}\n${treeDigest}\n`);
172+
fs.writeFileSync(stampPath, `${stagingSchema}\n${version}\n${integrity}\n${treeDigest}\n`);
171173

172174
console.log(`Staged ${runtimePath}`);
173175

‎java/copilot-native/scripts/fetch-native.test.mjs‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,34 @@ import test from 'node:test';
1414
const version = '1.0.79';
1515
const integrity = 'sha512-test-integrity';
1616
const runtimeContent = 'runtime content';
17-
const cliContent = 'cli content';
1817
const wrapperContent = 'wrapper content';
18+
const stagingSchema = 'hostless-runtime-v2';
1919
const scriptPath = fileURLToPath(new URL('./fetch-native.mjs', import.meta.url));
2020

2121
for (const classifier of ['linux-x64', 'win32-x64', 'darwin-arm64']) {
22-
test(`${classifier}: missing CLI does not use incremental fast path`, (t) => {
22+
test(`${classifier}: complete hostless artifacts use incremental fast path without a CLI`, (t) => {
2323
const fixture = createFixture(t, classifier);
24-
fs.rmSync(fixture.cliPath);
2524

2625
const result = runScript(fixture);
2726

28-
assertRestagingAttempted(fixture, result);
27+
assert.equal(result.status, 0, result.stderr);
28+
assert.match(result.stdout, /already staged/);
29+
assert.equal(fs.existsSync(fixture.npmMarkerPath), false);
2930
});
3031

31-
test(`${classifier}: stale CLI does not use incremental fast path`, (t) => {
32+
test(`${classifier}: missing runtime wrapper does not use incremental fast path`, (t) => {
3233
const fixture = createFixture(t, classifier);
33-
fs.writeFileSync(fixture.cliPath, 'stale CLI content');
34+
fs.rmSync(fixture.wrapperPath);
3435

3536
const result = runScript(fixture);
3637

3738
assertRestagingAttempted(fixture, result);
3839
});
3940

40-
test(`${classifier}: missing runtime wrapper does not use incremental fast path`, (t) => {
41+
test(`${classifier}: legacy staging schema does not use incremental fast path`, (t) => {
4142
const fixture = createFixture(t, classifier);
42-
fs.rmSync(fixture.wrapperPath);
43+
const stampPath = path.join(fixture.stagingDir, classifier, '.version');
44+
fs.writeFileSync(stampPath, fs.readFileSync(stampPath, 'utf8').replace(stagingSchema, 'hostless-runtime-v1'));
4345

4446
const result = runScript(fixture);
4547

@@ -82,7 +84,7 @@ test('stages retained package assets and excludes CLI-only content', (t) => {
8284
fs.mkdirSync(path.join(packageRoot, 'prebuilds', classifier), { recursive: true });
8385
fs.mkdirSync(path.join(packageRoot, 'ripgrep', 'bin', classifier), { recursive: true });
8486
fs.mkdirSync(path.join(packageRoot, 'definitions'), { recursive: true });
85-
fs.writeFileSync(path.join(packageRoot, 'copilot'), cliContent);
87+
fs.writeFileSync(path.join(packageRoot, 'copilot'), 'excluded');
8688
fs.writeFileSync(path.join(packageRoot, 'prebuilds', classifier, 'runtime.node'), runtimeContent);
8789
fs.writeFileSync(path.join(packageRoot, 'prebuilds', classifier, 'copilot-runtime'), wrapperContent);
8890
fs.writeFileSync(path.join(packageRoot, 'ripgrep', 'bin', classifier, 'rg'), 'ripgrep content');
@@ -116,6 +118,7 @@ test('stages retained package assets and excludes CLI-only content', (t) => {
116118
assert.equal(fs.readFileSync(path.join(resourceDir, 'ripgrep', 'bin', classifier, 'rg'), 'utf8'), 'ripgrep content');
117119
assert.equal(fs.readFileSync(path.join(resourceDir, 'definitions', 'future.json'), 'utf8'), '{}');
118120
assert.equal(fs.existsSync(path.join(resourceDir, 'app.js')), false);
121+
assert.equal(fs.existsSync(path.join(resourceDir, 'copilot')), false);
119122
assert.equal(fs.existsSync(path.join(resourceDir, 'LICENSE.md')), false);
120123
assert.equal(fs.existsSync(path.join(resourceDir, 'README.md')), false);
121124
assert.match(fs.readFileSync(path.join(resourceDir, 'runtime-assets.list'), 'utf8'), /ripgrep\/bin\/linux-x64\/rg/);
@@ -144,7 +147,6 @@ function createFixture(t, classifier) {
144147
);
145148

146149
const runtimePath = path.join(resourceDir, 'runtime.node');
147-
const cliPath = path.join(resourceDir, classifier.startsWith('win32') ? 'copilot.exe' : 'copilot');
148150
const wrapperPath = path.join(
149151
resourceDir,
150152
classifier.startsWith('win32') ? 'copilot-runtime.exe' : 'copilot-runtime',
@@ -154,17 +156,16 @@ function createFixture(t, classifier) {
154156
const inventoryPath = path.join(resourceDir, 'runtime-assets.list');
155157
fs.mkdirSync(path.dirname(ripgrepPath), { recursive: true });
156158
fs.writeFileSync(runtimePath, runtimeContent);
157-
fs.writeFileSync(cliPath, cliContent);
158159
fs.writeFileSync(wrapperPath, wrapperContent);
159160
fs.writeFileSync(ripgrepPath, 'ripgrep content');
160161
fs.writeFileSync(
161162
inventoryPath,
162-
`644\truntime.node\n755\tcopilot\n755\tcopilot-runtime\n755\tripgrep/bin/${classifier}/rg\n`,
163+
`644\truntime.node\n755\tcopilot-runtime\n755\tripgrep/bin/${classifier}/rg\n`,
163164
);
164165
fs.writeFileSync(platformPropertiesPath, `classifier=${classifier}\nversion=${version}\n`);
165166
fs.writeFileSync(
166167
path.join(stagingDir, classifier, '.version'),
167-
`${version}\n${integrity}\n${digestTree(resourceDir)}\n`,
168+
`${stagingSchema}\n${version}\n${integrity}\n${digestTree(resourceDir)}\n`,
168169
);
169170

170171
const fakeNpmPath = path.join(fakeBinDir, process.platform === 'win32' ? 'npm.cmd' : 'npm');
@@ -182,7 +183,6 @@ function createFixture(t, classifier) {
182183
fakeBinDir,
183184
npmMarkerPath,
184185
runtimePath,
185-
cliPath,
186186
wrapperPath,
187187
ripgrepPath,
188188
platformPropertiesPath,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.e2e;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertNotNull;
9+
10+
import org.junit.jupiter.api.Test;
11+
12+
import com.github.copilot.AllowCopilotExperimental;
13+
import com.github.copilot.CopilotClient;
14+
import com.github.copilot.rpc.CopilotClientOptions;
15+
import com.github.copilot.rpc.PingResponse;
16+
import com.github.copilot.rpc.RuntimeConnection;
17+
18+
/**
19+
* Failsafe smoke test for the managed out-of-process runtime wrapper.
20+
*/
21+
@AllowCopilotExperimental
22+
@RequireInProcess
23+
class OutOfProcessTransportIT {
24+
25+
@Test
26+
void shouldStartPingAndStopOverStdio() throws Exception {
27+
CopilotClientOptions options = new CopilotClientOptions().setConnection(RuntimeConnection.forStdio());
28+
try (CopilotClient client = new CopilotClient(options)) {
29+
client.start().get();
30+
31+
PingResponse pong = client.ping("wrapper message").get();
32+
assertEquals("pong: wrapper message", pong.message());
33+
assertNotNull(pong.timestamp());
34+
35+
client.stop().get();
36+
}
37+
}
38+
}

‎nodejs/src/runtimeArtifacts.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const EXCLUDED_TOP_LEVEL = new Set([
2323
"app.js",
2424
"assets",
2525
"changelog.json",
26+
"copilot",
27+
"copilot.exe",
2628
"copilot-sdk",
2729
"foundry-local-sdk",
2830
"index.js",

0 commit comments

Comments
 (0)