diff --git a/packages/http-client-python/assets.json b/packages/http-client-python/assets.json
index 13cce63bc99..acc273622d4 100644
--- a/packages/http-client-python/assets.json
+++ b/packages/http-client-python/assets.json
@@ -2,5 +2,5 @@
"AssetsRepo": "l0lawrence/typespec-assets",
"AssetsRepoPrefixPath": "python",
"TagPrefix": "python/tests",
- "Tag": "python/tests_7870c7a975"
+ "Tag": "python/tests_481763e148"
}
diff --git a/packages/http-client-python/eng/scripts/ci/render-diff.ts b/packages/http-client-python/eng/scripts/ci/render-diff.ts
index 3c1f9496cd1..e1b8700782e 100644
--- a/packages/http-client-python/eng/scripts/ci/render-diff.ts
+++ b/packages/http-client-python/eng/scripts/ci/render-diff.ts
@@ -31,7 +31,7 @@ import { createRequire } from "module";
import { tmpdir } from "os";
import { dirname, join, resolve } from "path";
import pc from "picocolors";
-import { fileURLToPath } from "url";
+import { fileURLToPath, pathToFileURL } from "url";
import { parseArgs } from "util";
import { FLAVORS, readAssetsConfig, restoreFullBaseline } from "./assets.js";
@@ -47,6 +47,7 @@ const argv = parseArgs({
output: { type: "string", short: "o" },
generated: { type: "string", short: "g" },
title: { type: "string", short: "t" },
+ open: { type: "boolean" },
help: { type: "boolean", short: "h" },
},
});
@@ -61,6 +62,7 @@ ${pc.bold("Options:")}
-o, --output
Output directory (default: temp/diff-site).
-g, --generated Current generated dir (default: tests/generated).
-t, --title Title shown on the diff page.
+ --open Open the rendered diff in your default browser.
-h, --help Show this help.
`);
process.exit(0);
@@ -268,17 +270,41 @@ async function main(): Promise {
writeFileSync(join(OUTPUT_DIR, "summary.json"), JSON.stringify(summary, null, 2) + "\n");
writeSite(diffText, summary);
+ const indexPath = join(OUTPUT_DIR, "index.html");
console.log(
pc.green(
`Diff rendered to ${OUTPUT_DIR} ` +
`(${summary.filesChanged} files, +${summary.additions}/-${summary.deletions}).`,
),
);
+ // Print a clickable file:// URL so the page is one click away locally, and
+ // optionally pop it open in the default browser.
+ console.log(pc.cyan(`View it at ${pathToFileURL(indexPath).href}`));
+ if (argv.values.open) {
+ openInBrowser(indexPath);
+ }
} finally {
rmSync(workDir, { recursive: true, force: true });
}
}
+/** Opens a local file in the OS default browser; never fails the run. */
+function openInBrowser(target: string): void {
+ try {
+ if (process.platform === "win32") {
+ // `start` is a cmd builtin; the empty first arg is the window title so a
+ // path with spaces isn't mistaken for one.
+ execFileSync("cmd", ["/c", "start", "", target], { stdio: "ignore" });
+ } else if (process.platform === "darwin") {
+ execFileSync("open", [target], { stdio: "ignore" });
+ } else {
+ execFileSync("xdg-open", [target], { stdio: "ignore" });
+ }
+ } catch (err) {
+ console.warn(pc.yellow(`Could not open a browser automatically: ${err}`));
+ }
+}
+
interface FileDiff {
/** Display path (baseline/current prefixes stripped). */
path: string;
@@ -289,6 +315,35 @@ interface FileDiff {
status: "added" | "removed" | "modified";
}
+/**
+ * Rewrites a file chunk's diff header so both sides share the same path.
+ *
+ * The diff comes from `git diff --no-index baseline current`, so every header
+ * reads `a/baseline/` vs `b/current/`. Because those two paths
+ * differ only by the temp-dir prefix, diff2html mistakes every file for a
+ * RENAME (showing `{baseline → current}`). Stripping the `baseline/`/`current/`
+ * prefixes makes old === new path, so it renders as a normal modification.
+ */
+function normalizeChunkHeader(chunk: string): string {
+ const lines = chunk.split("\n");
+ for (let i = 0; i < lines.length; i++) {
+ const line = lines[i];
+ if (line.startsWith("@@")) break; // header is done once hunks begin
+ if (line.startsWith("diff --git ")) {
+ lines[i] = line.replace(/ a\/baseline\//g, " a/").replace(/ b\/current\//g, " b/");
+ } else if (line.startsWith("--- ")) {
+ lines[i] = line.replace(/^--- a\/baseline\//, "--- a/");
+ } else if (line.startsWith("+++ ")) {
+ lines[i] = line.replace(/^\+\+\+ b\/current\//, "+++ b/");
+ } else if (line.startsWith("rename from ")) {
+ lines[i] = line.replace(/^rename from baseline\//, "rename from ");
+ } else if (line.startsWith("rename to ")) {
+ lines[i] = line.replace(/^rename to current\//, "rename to ");
+ }
+ }
+ return lines.join("\n");
+}
+
/** Splits a `git diff --no-index` blob into one chunk per file. */
function splitDiffByFile(diffText: string): FileDiff[] {
const files: FileDiff[] = [];
@@ -323,7 +378,7 @@ function splitDiffByFile(diffText: string): FileDiff[] {
const display = strip(isAdded ? newPath : oldPath) || strip(newPath) || "(unknown)";
files.push({
path: display,
- chunk,
+ chunk: normalizeChunkHeader(chunk),
additions,
deletions,
status: isAdded ? "added" : isRemoved ? "removed" : "modified",
diff --git a/packages/http-client-python/generator/pygen/codegen/templates/version.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/version.py.jinja2
index 2086d0fc1d7..08faa5a6bb1 100644
--- a/packages/http-client-python/generator/pygen/codegen/templates/version.py.jinja2
+++ b/packages/http-client-python/generator/pygen/codegen/templates/version.py.jinja2
@@ -3,4 +3,5 @@
{{ code_model.license_header }}
{% endif %}
+# Generated by the TypeSpec Python emitter.
VERSION = "{{ code_model.options.get("package-version") }}"
diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json
index 0a2dcfb9c65..76d25e81c02 100644
--- a/packages/http-client-python/package.json
+++ b/packages/http-client-python/package.json
@@ -52,6 +52,8 @@
"regenerate": "tsx ./eng/scripts/ci/regenerate.ts",
"regenerate:push-assets": "tsx ./eng/scripts/ci/push-assets.ts",
"regenerate:render-diff": "tsx ./eng/scripts/ci/render-diff.ts",
+ "regenerate:diff": "tsx ./eng/scripts/ci/render-diff.ts --open",
+ "regenerate:review": "npm run regenerate && npm run regenerate:diff",
"ci": "npm run test:emitter && npm run ci:generated",
"ci:generated": "tsx ./eng/scripts/ci/run-tests.ts --generator --env=ci",
"change:version": "pnpm chronus version --ignore-policies --only @typespec/http-client-python",