[ts-http-runtime] add azure diff check tool (#31285)

### Packages impacted by this PR

- `@typespec/ts-http-runtime`

### Describe the problem that is addressed by this PR

We have had issues with the unbranded Core package becoming out of sync
with the Azure packages. This PR adds a report which is a diff between
the Azure Core packages and the unbranded package and causes the lint
step to fail if there are any differences between Azure and unbranded
that do not appear in the report. Running the `lint:fix` script will
update the diff report. This will mean that:

- Making an Azure Core change without a corresponding change to
unbranded package, either in the source code or in the diff report, will
result in a CI failure
- Changes to the diff report can be reviewed so that any differences
between Azure and unbranded implementations of a feature can be easily
compared

Why use the lint script (and lint:fix)? This is already run by CI, so by
using it we don't need to make any changes to pipeline steps to get this
working. This script is a one-off and so I just want to do the bare
minimum to get things working 😄
This commit is contained in:
Timo van Veenendaal 2024-10-21 14:44:06 -07:00 коммит произвёл GitHub
Родитель 0b6bbeab42
Коммит 31b76c94f5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
4 изменённых файлов: 2802 добавлений и 4 удалений

Просмотреть файл

@ -25104,7 +25104,7 @@ packages:
dev: false
file:projects/ts-http-runtime.tgz:
resolution: {integrity: sha512-MD9tMIfpTC9zx97D7IF1ZXzk/qijL6n58lc0C8cfeQ7y8DQHUx4TGBLW43aDOABJrMO9Oo3M3PCVGS2yVfkOpA==, tarball: file:projects/ts-http-runtime.tgz}
resolution: {integrity: sha512-wtRhN//7rN8fYN7MR3Gm1g2YY0+GxVwAGSx4aLNkxM7zSNggwYOBwqmT8fDlDSkiewqYbW5jHNWOtwMd4mRecA==, tarball: file:projects/ts-http-runtime.tgz}
name: '@rush-temp/ts-http-runtime'
version: 0.0.0
dependencies:
@ -25119,6 +25119,7 @@ packages:
playwright: 1.48.1
rimraf: 5.0.10
tslib: 2.8.0
tsx: 4.19.1
typescript: 5.6.3
vitest: 2.1.3(@types/node@18.19.57)(@vitest/browser@2.1.3)
transitivePeerDependencies:

Просмотреть файл

@ -62,8 +62,8 @@
"integration-test": "npm run integration-test:node && npm run integration-test:browser",
"integration-test:browser": "echo skipped",
"integration-test:node": "echo skipped",
"lint": "eslint package.json api-extractor.json src test",
"lint:fix": "eslint package.json api-extractor.json src test --fix --fix-type [problem,suggestion]",
"lint": "tsx scripts/azure-diff.ts; eslint package.json api-extractor.json src test",
"lint:fix": "tsx scripts/azure-diff.ts --update && eslint package.json api-extractor.json src test --fix --fix-type [problem,suggestion]",
"pack": "npm pack 2>&1",
"test": "npm run clean && dev-tool run build-package && npm run unit-test:node && dev-tool run bundle && npm run unit-test:browser && npm run integration-test",
"test:browser": "npm run clean && npm run unit-test:browser && npm run integration-test:browser",
@ -108,7 +108,8 @@
"playwright": "^1.41.2",
"rimraf": "^5.0.5",
"typescript": "~5.6.2",
"vitest": "^2.0.5"
"vitest": "^2.0.5",
"tsx": "^4.19.1"
},
"tshy": {
"exports": {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,156 @@
import { parseArgs } from "node:util";
import fs from "node:fs/promises";
import { spawn, SpawnOptions } from "node:child_process";
import { exit } from "node:process";
import path from "node:path";
/**
* Mapping of locations of Azure source files to their location in the unbranded package.
*/
const AZURE_SOURCES = {
// core-rest-pipeline is placed at the root of the unbranded package; this also covers subfolders of
// core-rest-pipeline including policies/ and retryStrategies/.
"../core-rest-pipeline/src/": "./src/",
"../core-util/src/": "./src/util/",
"../core-auth/src/": "./src/auth/",
"../abort-controller/src/": "./src/abort-controller/",
"../core-client-rest/src/": "./src/client/",
"../core-tracing/src/": "./src/tracing/",
"../logger/src/": "./src/logger/",
};
const {
values: {
update = false,
"output-path": outputPath = "./review/azure-core-comparison.diff",
verbose = false,
},
} = parseArgs({
options: {
update: {
type: "boolean",
},
"output-path": {
type: "string",
short: "o",
},
verbose: {
type: "boolean",
short: "v",
},
},
});
interface RunResult {
exitCode?: number;
signal?: NodeJS.Signals;
stdout: string;
}
/**
* Runs the given CLI command and captures the output
*/
async function run(
commandAndArgs: [string, ...string[]],
options: SpawnOptions = {},
): Promise<RunResult> {
let stdout = "";
const [command, ...args] = commandAndArgs;
const proc = spawn(command, args, options);
return new Promise((resolve, reject) => {
proc.on("error", reject);
proc.on("exit", (exitCode, signal) => {
resolve({
exitCode: exitCode ?? undefined,
signal: signal ?? undefined,
stdout,
});
});
proc.stdout?.on("data", (data) => {
stdout += data.toString();
if (verbose) {
process.stdout.write(data);
}
});
if (verbose) {
proc.stderr?.on("data", (data) => process.stderr.write(data));
}
});
}
/**
* Creates a diff representing code changes between the Azure core packages and the unbranded package.
*
* This is done by using a temp folder/git repo. The Azure core files are copied into the temp folder following
* the mappings defined in AZURE_SOURCES. This is then committed, and then the temp folder is cleaned out and the unbranded
* Core is copied in. The diff (as reported by `git diff`) between the committed Azure core files and the unbranded replacement
* is returned.
*/
async function calculatePatchFileContents(): Promise<string> {
const tmpDir = await fs.mkdtemp(".azure-diff-tool");
try {
// Initialize temp git repo
await run(["git", "init"], { cwd: tmpDir });
// Copy over the Azure Core files to where are expected to be in the unbranded Core package
for (const [azurePath, newLocation] of Object.entries(AZURE_SOURCES)) {
await fs.cp(azurePath, path.join(tmpDir, newLocation), { recursive: true });
}
// Commit the Azure files, then remove everything so that they can be replaced by the unbranded files
await run(["git", "add", "."], { cwd: tmpDir });
await run(["git", "commit", "-m", "Placeholder commit"], { cwd: tmpDir });
await fs.rm(path.join(tmpDir, "src"), { recursive: true, force: true });
// Copy the unbranded files into the temp repo
await fs.cp("./src/", path.join(tmpDir, "src"), { recursive: true });
// Staging the unbranded files and using `git diff --staged` means that the diff will also show files
// that have been removed and moved.
await run(["git", "add", "."], { cwd: tmpDir });
const { stdout } = await run(
["git", "diff", "--ignore-all-space", "--staged", "--find-renames"],
{ cwd: tmpDir },
);
return stdout;
} finally {
await fs.rm(tmpDir, { recursive: true, force: true });
}
}
async function main(): Promise<void> {
const expectedContent = await calculatePatchFileContents();
if (update) {
await fs.writeFile(outputPath, expectedContent, "utf-8");
} else {
const content = await fs.readFile(outputPath, "utf-8");
if (content === expectedContent) {
console.log("✅ No changes to Azure to unbranded diff report");
} else {
console.error("❌ There have been changes to the Azure-unbranded diff report.");
console.error(
" This happens when you make a change to Azure Core without making the same change in the unbranded Core package.",
);
console.error(" To fix:");
console.error(
" - Run `rushx lint:fix` in the ts-http-runtime package to update review/azure-core-comparison.diff, then have a look and see what's changed.",
);
console.error(" - Apply your Core changes to the ts-http-runtime package as appropriate.");
console.error(
" - Run `rushx lint:fix` again package to update the diff report, and commit the changes.",
);
exit(1);
}
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});