Skip to content

Commit 12c3845

Browse files
Copilotmsyyc
andauthored
Merge remote-tracking branch 'origin/main' into copilot/microsoft-12007-add-test-case-for-pull-5176
# Conflicts: # packages/http-client-python/package-lock.json # packages/http-client-python/package.json Co-authored-by: msyyc <70930885+msyyc@users.noreply.github.com>
2 parents 3dfd016 + a902c45 commit 12c3845

365 files changed

Lines changed: 5628 additions & 1045 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/compiler"
5+
---
6+
7+
Fix `tsp compile` failing with `INVALID_MODULE_EXPORT_TARGET` when the project is on a Windows network share (UNC path).
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/openapi3"
5+
---
6+
7+
[converter] Emit valid #deprecated directives for converted operation parameters.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/openapi3"
5+
---
6+
7+
Fix `@encode` on nullable properties and parameters in OpenAPI 3.1 and 3.2. The encoding is now applied to the `T` member of `anyOf: [T, { type: "null" }]` instead of being written next to `anyOf`. When the encoding changed the emitted type, like `unixTimestamp`, `seconds` or `@encode(string)` on a number, the old schema rejected every value. When the encoded type stayed a string, like `rfc7231` on `utcDateTime` or `base64url` on `bytes`, it only rejected `null`. `bytes | null` now also gets `contentEncoding` instead of `format`.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/openapi3"
5+
---
6+
7+
[converter] Escape quotes and line breaks in `@tagMetadata` strings generated from OpenAPI tags.

‎.chronus/changes/fix-required-etag-header-2026-09-14.md‎

Lines changed: 0 additions & 7 deletions
This file was deleted.

‎.chronus/changes/l0lawrence-single-union-aliases-2026-09-10-15-30-27.md‎

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: dependencies
3+
packages:
4+
- "@typespec/tspd"
5+
---
6+
7+
Upgrade API Extractor to 7.59.1 and API Extractor Model to 7.33.12.

‎.github/skills/http-client-python-bump-and-release/SKILL.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ npm install -g npm-check-updates
7070
npm run change:version
7171
```
7272

73+
Verify that the script updated both the emitter version in `package.json` and `CHANGELOG.md`. If neither file was updated:
74+
- Increment the third component of the emitter version in `package.json` (for example, `a.b.c` to `a.b.(c+1)`).
75+
- Add this entry near the top of `CHANGELOG.md`, immediately after the changelog title:
76+
77+
```markdown
78+
## <new emitter version>
79+
80+
### Bump dependencies
81+
82+
- Bump dependencies of `@typespec/*` and `@azure-tools/*` to latest versions
83+
```
84+
85+
- Verify that the version in `package.json` matches the new `CHANGELOG.md` heading.
86+
7387
8. Build and commit:
7488

7589
```bash
@@ -84,3 +98,16 @@ npm install -g npm-check-updates
8498
```
8599

86100
10. Create PR with title `[python] release new version` and no description.
101+
102+
## Post-Process Review
103+
104+
1. After the pull request is created, review the complete release run: commands executed, command output, manual interventions, validation results, changed files, commit, push, cleanup, and PR creation.
105+
2. Update this skill only when the completed run provides a concrete finding and a strong reason for the change, such as:
106+
- A documented step failed or produced an incorrect result.
107+
- The agent needed an undocumented manual correction to complete the release.
108+
- A step was ambiguous enough to create a real risk or delay.
109+
- A command was unnecessary or could be replaced by a demonstrably safer or more reliable command.
110+
3. Do not change the skill based on preference, speculation, or an unobserved edge case. For every update, record the observed evidence and explain why the edit improves future releases.
111+
4. Keep any skill update minimal, validate its formatting and frontmatter, and keep it separate from the completed release PR unless the user explicitly asks to include it there.
112+
5. If the review finds no evidence-backed improvement, leave the skill unchanged and state that no update was warranted.
113+
6. Return the branch name, released package version, `@typespec/http-client-python` version, commit hash, pull request URL, review findings, and any skill update to the user.

‎.github/workflows/check-agentic-workflows.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1818

1919
- name: Install gh-aw
20-
uses: github/gh-aw/actions/setup-cli@bde367913adeb3132f0a171594c88a17f4b7d08c # v0.88.7
20+
uses: github/gh-aw/actions/setup-cli@97b9a7c376bb60bd7cbcf348a5a6349b52e266ff # v0.89.14
2121
with:
2222
version: v0.88.7
2323

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
name: "python / emitter diff commenter"
2+
3+
# The artifact is untrusted data. Never check out, build, or execute PR code here.
4+
# Resolve the PR from trusted workflow_run metadata, not artifact fields.
5+
on:
6+
workflow_run:
7+
workflows: ["python / emitter diff"]
8+
types:
9+
- completed
10+
11+
permissions:
12+
actions: read
13+
pull-requests: write
14+
15+
concurrency:
16+
group: python-emitter-diff-comment-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
17+
cancel-in-progress: false
18+
19+
jobs:
20+
commenter:
21+
if: github.event.workflow_run.event == 'pull_request'
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Resolve PR and artifact
25+
id: resolve
26+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
27+
with:
28+
script: |
29+
const { owner, repo } = context.repo;
30+
const run = context.payload.workflow_run;
31+
if (run.path !== '.github/workflows/ci-emitter-diff-python.yml') {
32+
throw new Error('Unexpected producer workflow path.');
33+
}
34+
if (run.conclusion === 'cancelled' || run.conclusion === 'skipped') {
35+
core.info(`Skipping ${run.conclusion} producer run.`);
36+
return;
37+
}
38+
const headSha = run.head_sha;
39+
const headBranch = run.head_branch;
40+
const headRepo = run.head_repository?.full_name;
41+
const headOwner = run.head_repository?.owner?.login;
42+
if (!headSha || !headBranch || !headRepo || !headOwner) {
43+
throw new Error('workflow_run is missing trusted head repository information.');
44+
}
45+
// Fork runs have empty pull_requests; commit association lookup also
46+
// misses fork commits that have not been merged into the base repo.
47+
const prs = await github.paginate(github.rest.pulls.list, {
48+
owner, repo, state: 'open', head: `${headOwner}:${headBranch}`, per_page: 100,
49+
});
50+
const matches = prs.filter((pr) =>
51+
pr.state === 'open' &&
52+
pr.head.sha === headSha &&
53+
pr.head.ref === headBranch &&
54+
pr.head.repo?.full_name === headRepo &&
55+
pr.base.repo?.full_name === `${owner}/${repo}`
56+
);
57+
if (matches.length !== 1) {
58+
core.info(`Skipping stale, closed, missing, or ambiguous PR: found ${matches.length} exact matches.`);
59+
return;
60+
}
61+
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
62+
owner, repo, run_id: run.id, per_page: 100,
63+
});
64+
const comments = artifacts.filter((artifact) => artifact.name === 'emitter-diff-python-comment');
65+
if (comments.length === 0 && run.conclusion !== 'success') {
66+
core.warning('Failed producer run has no comment artifact; see the producer run for the tool/build error.');
67+
return;
68+
}
69+
if (comments.length !== 1 || comments[0].expired) {
70+
throw new Error('Expected one unexpired comment artifact from the producer run.');
71+
}
72+
core.setOutput('pr', String(matches[0].number));
73+
core.setOutput('artifact', String(comments[0].id));
74+
75+
- name: Download comment artifact
76+
if: steps.resolve.outputs.artifact != ''
77+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
78+
with:
79+
artifact-ids: ${{ steps.resolve.outputs.artifact }}
80+
merge-multiple: true
81+
run-id: ${{ github.event.workflow_run.id }}
82+
github-token: ${{ secrets.GITHUB_TOKEN }}
83+
path: ${{ runner.temp }}/emitter-diff-comment
84+
85+
- name: Post PR comment
86+
if: steps.resolve.outputs.artifact != ''
87+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
88+
env:
89+
PR_NUMBER: ${{ steps.resolve.outputs.pr }}
90+
with:
91+
script: |
92+
const fs = require('node:fs');
93+
const path = require('node:path');
94+
const { owner, repo } = context.repo;
95+
const run = context.payload.workflow_run;
96+
const marker = '<!-- emitter-diff-python -->';
97+
const issue_number = Number(process.env.PR_NUMBER);
98+
if (!Number.isSafeInteger(issue_number) || issue_number <= 0 ||
99+
!Number.isSafeInteger(run.id) || !Number.isSafeInteger(run.run_attempt)) {
100+
throw new Error('Invalid trusted PR or run identifier.');
101+
}
102+
const file = path.join(process.env.RUNNER_TEMP, 'emitter-diff-comment', 'comment.json');
103+
const stat = fs.lstatSync(file);
104+
if (!stat.isFile() || stat.size > 16384) {
105+
throw new Error('Comment artifact must be a regular JSON file of at most 16 KiB.');
106+
}
107+
const payload = JSON.parse(fs.readFileSync(file, 'utf8'));
108+
if (!payload || typeof payload.body !== 'string' ||
109+
!payload.body.trim() || payload.body.length > 8000) {
110+
throw new Error('Comment body must be a nonempty string of at most 8000 characters.');
111+
}
112+
// Force both control markers from trusted values before the untrusted body.
113+
const body = `${marker}\n<!-- emitter-diff-python-run: ${run.id}:${run.run_attempt} -->\n${payload.body}`;
114+
const comments = await github.paginate(github.rest.issues.listComments, {
115+
owner, repo, issue_number, per_page: 100,
116+
});
117+
const owned = comments.filter((comment) =>
118+
comment.user?.type === 'Bot' &&
119+
comment.user?.login === 'github-actions[bot]' &&
120+
comment.body?.startsWith(marker + '\n')
121+
);
122+
if (owned.length > 1) {
123+
throw new Error('Multiple bot-owned emitter diff comments; refusing an ambiguous update.');
124+
}
125+
const hit = owned[0];
126+
const stamp = hit?.body.match(/^<!-- emitter-diff-python -->\n<!-- emitter-diff-python-run: (\d+):(\d+) -->/);
127+
if (stamp && (Number(stamp[1]) > run.id ||
128+
(Number(stamp[1]) === run.id && Number(stamp[2]) > run.run_attempt))) {
129+
core.info('Skipping older run; a newer emitter diff comment is already present.');
130+
return;
131+
}
132+
// Recheck after download/pagination, immediately before writing.
133+
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: issue_number });
134+
if (pr.state !== 'open' || pr.head.sha !== run.head_sha ||
135+
pr.head.ref !== run.head_branch ||
136+
pr.head.repo?.full_name !== run.head_repository.full_name ||
137+
pr.base.repo?.full_name !== `${owner}/${repo}`) {
138+
core.info('Skipping PR that closed or changed head while preparing the comment.');
139+
return;
140+
}
141+
if (hit) {
142+
await github.rest.issues.updateComment({ owner, repo, comment_id: hit.id, body });
143+
} else {
144+
await github.rest.issues.createComment({ owner, repo, issue_number, body });
145+
}

0 commit comments

Comments
 (0)