Skip to content

Commit 2cc3231

Browse files
Update SDK snapshot for Copilot CLI 1.0.91-1
1 parent b6bb031 commit 2cc3231

25 files changed

Lines changed: 667 additions & 137 deletions

‎.github/workflows/block-remove-before-merge.yml‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,33 @@ on:
88
merge_group:
99

1010
permissions:
11-
pull-requests: read
11+
contents: read
1212

1313
jobs:
1414
check-paths:
1515
name: "No remove-before-merge directories"
1616
if: github.event_name == 'pull_request' && github.base_ref == 'main'
1717
runs-on: ubuntu-latest
1818
steps:
19+
- uses: actions/checkout@v7
20+
timeout-minutes: 4
21+
with:
22+
fetch-depth: 2
1923
- name: Check for remove-before-merge paths in PR
20-
env:
21-
GH_TOKEN: ${{ github.token }}
22-
PR_NUMBER: ${{ github.event.pull_request.number }}
23-
REPO: ${{ github.repository }}
2424
run: |
25-
FILES=$(gh api repos/$REPO/pulls/$PR_NUMBER/files --paginate --jq '.[].filename')
26-
BLOCKED=$(echo "$FILES" | grep -E '(^|/)[-a-zA-Z0-9_]+-remove-before-merge(/|$)' || true)
27-
if [ -n "$BLOCKED" ]; then
25+
# pull_request checks out the merge commit; its first parent is the base.
26+
git rev-parse --verify HEAD^2 >/dev/null 2>&1 || {
27+
echo "::error::Expected a pull request merge commit."
28+
exit 1
29+
}
30+
FILES_FILE=$(mktemp)
31+
trap 'rm -f "$FILES_FILE"' EXIT
32+
git diff --name-only -z HEAD^1 HEAD > "$FILES_FILE"
33+
if grep -zqE '(^|/)[-a-zA-Z0-9_]+-remove-before-merge(/|$)' "$FILES_FILE"; then
2834
echo "::error::This PR contains files under a 'remove-before-merge' directory. Remove them before merging."
2935
echo ""
3036
echo "Offending paths:"
31-
echo "$BLOCKED"
37+
grep -zE '(^|/)[-a-zA-Z0-9_]+-remove-before-merge(/|$)' "$FILES_FILE" | tr '\0' '\n'
3238
exit 1
3339
fi
3440
echo "No remove-before-merge paths found. ✅"

‎dotnet/src/Generated/Rpc.cs‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dotnet/test/ConnectionTokenTests.cs‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,19 @@ public async Task InitializeAsync()
3131

3232
public async Task DisposeAsync()
3333
{
34+
var errors = new List<Exception>();
3435
if (GoodClient is not null)
3536
{
36-
await GoodClient.ForceStopAsync();
37+
try { await GoodClient.ForceStopAsync(); }
38+
catch (Exception ex) { errors.Add(ex); }
3739
}
38-
39-
await Ctx.DisposeAsync();
40+
if (Ctx is not null)
41+
{
42+
try { await Ctx.DisposeAsync(); }
43+
catch (Exception ex) { errors.Add(ex); }
44+
}
45+
if (errors.Count == 1) throw errors[0];
46+
if (errors.Count > 1) throw new AggregateException(errors);
4047
}
4148
}
4249

‎dotnet/test/E2E/MultiClientCommandsElicitationE2ETests.cs‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ public async Task InitializeAsync()
3030

3131
public async Task DisposeAsync()
3232
{
33-
await Ctx.DisposeAsync();
33+
if (Ctx is not null)
34+
{
35+
await Ctx.DisposeAsync();
36+
}
3437
}
3538
}
3639

@@ -259,4 +262,3 @@ public async Task Capabilities_Changed_Fires_When_Elicitation_Provider_Disconnec
259262
"After elicitation provider disconnects, capability should be removed");
260263
}
261264
}
262-

‎dotnet/test/E2E/MultiClientE2ETests.cs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ public async Task InitializeAsync()
3333

3434
public async Task DisposeAsync()
3535
{
36-
await Ctx.DisposeAsync();
36+
if (Ctx is not null)
37+
{
38+
await Ctx.DisposeAsync();
39+
}
3740
}
3841
}
3942

‎dotnet/test/E2E/TelemetryExportE2ETests.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ private static int GetStatusCode(JsonElement entry)
205205
private static JsonElement AssertSpanWithOperation(IEnumerable<JsonElement> spans, string operationName)
206206
{
207207
var matchingSpan = spans.FirstOrDefault(span => GetStringAttribute(span, "gen_ai.operation.name") == operationName);
208-
Assert.NotEqual(JsonValueKind.Undefined, matchingSpan.ValueKind);
208+
Assert.True(matchingSpan.ValueKind != JsonValueKind.Undefined,
209+
$"Missing {operationName} span. Exported operations: {string.Join(", ", spans.Select(span => GetStringAttribute(span, "gen_ai.operation.name") ?? "<unnamed>"))}");
209210
return matchingSpan;
210211
}
211212

‎dotnet/test/Harness/ReplayProxy.cs‎

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,29 @@ public Task<string> StartAsync()
2727

2828
async Task<string> StartCoreAsync()
2929
{
30-
string filename;
31-
string args;
32-
33-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
34-
{
35-
filename = "cmd.exe";
36-
args = "/c npm.cmd run start";
37-
38-
}
39-
else
40-
{
41-
filename = "npm";
42-
args = "run start";
43-
}
44-
30+
// Spawn the server directly so a stalled npm/cmd wrapper cannot strand startup or cleanup.
4531
var startInfo = new ProcessStartInfo
4632
{
47-
FileName = filename,
33+
FileName = "node",
4834
WorkingDirectory = Path.Join(FindRepoRoot(), "test", "harness"),
49-
Arguments = args,
35+
Arguments = "--import tsx server.ts",
5036
UseShellExecute = false,
5137
RedirectStandardOutput = true,
5238
RedirectStandardError = true,
5339
CreateNoWindow = true,
5440
};
5541

56-
_process = new Process { StartInfo = startInfo };
42+
var process = new Process { StartInfo = startInfo };
5743

5844
var tcs = new TaskCompletionSource<string>();
5945
var errorOutput = new StringBuilder();
46+
var startupTimer = Stopwatch.StartNew();
47+
string CapturedErrors()
48+
{
49+
lock (errorOutput) return errorOutput.ToString();
50+
}
6051

61-
_process.OutputDataReceived += (_, e) =>
52+
process.OutputDataReceived += (_, e) =>
6253
{
6354
if (e.Data == null) return;
6455
var match = Regex.Match(e.Data, @"Listening: (?<url>http://[^\s]+)\s+(?<metadata>\{.*\})$");
@@ -98,28 +89,39 @@ async Task<string> StartCoreAsync()
9889
tcs.TrySetResult(match.Groups["url"].Value);
9990
};
10091

101-
_process.ErrorDataReceived += (_, e) =>
92+
process.ErrorDataReceived += (_, e) =>
10293
{
10394
if (e.Data == null) return;
104-
errorOutput.AppendLine(e.Data);
95+
lock (errorOutput) errorOutput.AppendLine(e.Data);
10596
Console.Error.WriteLine(e.Data);
10697
};
10798

108-
_process.Start();
109-
_process.BeginOutputReadLine();
110-
_process.BeginErrorReadLine();
111-
_ = _process.WaitForExitAsync().ContinueWith(_ =>
99+
try
100+
{
101+
process.Start();
102+
}
103+
catch
104+
{
105+
process.Dispose();
106+
throw;
107+
}
108+
_process = process;
109+
process.BeginOutputReadLine();
110+
process.BeginErrorReadLine();
111+
_ = process.WaitForExitAsync().ContinueWith(_ =>
112112
{
113-
if (_process?.ExitCode is int exitCode && exitCode != 0)
113+
if (!tcs.Task.IsCompleted)
114114
{
115-
tcs.TrySetException(new Exception($"Proxy exited with code {_process.ExitCode}: {errorOutput}"));
115+
tcs.TrySetException(new Exception($"Proxy exited before listening with code {process.ExitCode}: {CapturedErrors()}"));
116116
}
117117
});
118118

119119
// Use longer timeout on Windows due to slower process startup
120120
var timeoutSeconds = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 30 : 10;
121121
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds));
122-
cts.Token.Register(() => tcs.TrySetException(new TimeoutException("Timeout waiting for proxy")));
122+
cts.Token.Register(() => tcs.TrySetException(new TimeoutException(
123+
$"Timeout waiting for proxy after {startupTimer.ElapsedMilliseconds}ms " +
124+
$"(exited: {process.HasExited}; stderr: {CapturedErrors()})")));
123125

124126
return await tcs.Task;
125127
}

‎dotnet/test/Unit/E2ETestFixtureTests.cs‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.ComponentModel;
66
using System.Diagnostics;
77
using System.Reflection;
8+
using GitHub.Copilot.Test.E2E;
89
using GitHub.Copilot.Test.Harness;
910
using Xunit;
1011

@@ -116,4 +117,29 @@ public void Shared_Client_Preserves_Tcp_Connection_For_OutOfProcess_Tests()
116117

117118
Assert.Equal(E2ETestFixture.SharedTcpConnectionToken, connection.ConnectionToken);
118119
}
120+
121+
[Fact]
122+
public async Task Dispose_Derived_Fixtures_Without_Initialized_Context_Does_Not_Throw()
123+
{
124+
await new MultiClientTestFixture().DisposeAsync();
125+
await new MultiClientCommandsElicitationFixture().DisposeAsync();
126+
await new ConnectionTokenTestFixture().DisposeAsync();
127+
}
128+
129+
[Fact]
130+
public async Task Replay_Proxy_Starts_With_Connect_Metadata_And_Stops()
131+
{
132+
var proxy = new ReplayProxy();
133+
try
134+
{
135+
var url = await proxy.StartAsync();
136+
Assert.StartsWith("http://", url);
137+
Assert.False(string.IsNullOrWhiteSpace(proxy.ConnectProxyUrl));
138+
Assert.False(string.IsNullOrWhiteSpace(proxy.CaFilePath));
139+
}
140+
finally
141+
{
142+
await proxy.StopAsync(skipWritingCache: true);
143+
}
144+
}
119145
}

‎dotnet/test/Unit/JsonRpcTests.cs‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ public async Task JsonRpc_Cancels_Incoming_Handler_Token_When_Connection_Dispose
126126
await WaitForCancellationAsync(handlerToken);
127127
}
128128

129+
[Fact]
130+
public async Task JsonRpc_Awaits_Declared_Task_Handler_With_Generic_Runtime_Task()
131+
{
132+
using var pair = JsonRpcReflectionPair.Create();
133+
pair.Server.SetLocalRpcMethod(
134+
"genericRuntimeTask",
135+
(Func<SingleObjectRequest, Task>)(_ => Task.FromResult(1)),
136+
singleObjectParam: true);
137+
pair.StartListening();
138+
139+
Assert.Null(await pair.Client.InvokeAsync<object?>(
140+
"genericRuntimeTask",
141+
[new SingleObjectRequest { Value = "value" }]));
142+
}
143+
129144
[Fact]
130145
public async Task JsonRpc_Dispose_Completes_Cleanup_When_Cancellation_Callback_Throws()
131146
{

‎go/rpc/zrpc.go‎

Lines changed: 14 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)