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",