diff --git a/.chronus/changes/fix-ci-readme-regeneration-2026-5-14.md b/.chronus/changes/fix-ci-readme-regeneration-2026-5-14.md new file mode 100644 index 00000000000..a5fd88faafd --- /dev/null +++ b/.chronus/changes/fix-ci-readme-regeneration-2026-5-14.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Fix CI on main by deleting README.md files during baseline reset so regeneration recreates them; extend unit test to assert README.md exists for every generated SDK package (azure & unbranded). diff --git a/packages/http-client-python/eng/scripts/ci/regenerate-common.ts b/packages/http-client-python/eng/scripts/ci/regenerate-common.ts index f21c4013616..529270f1d1d 100644 --- a/packages/http-client-python/eng/scripts/ci/regenerate-common.ts +++ b/packages/http-client-python/eng/scripts/ci/regenerate-common.ts @@ -732,16 +732,23 @@ export async function prepareBaselineOfGeneratedCode(generatedFolder: string): P rmSync(tempDir, { recursive: true, force: true }); } - // Delete a couple of fully-generated package folders so regeneration has to - // recreate them from scratch (smoke test of full-emit path). - const targetsToDelete = [ - join(testsGeneratedDir, "azure", "authentication-http-custom"), - join(testsGeneratedDir, "unbranded", "encode-array"), - ]; - for (const target of targetsToDelete) { - if (existsSync(target)) { - console.log(pc.dim(`Deleting ${target}`)); - rmSync(target, { recursive: true, force: true }); + // Smoke test the full-emit path: delete a couple of fully-generated package + // folders and every README.md so regeneration has to recreate them. + const deleteIfExists = (path: string) => { + if (!existsSync(path)) return; + console.log(pc.dim(`Deleting ${path}`)); + rmSync(path, { recursive: true, force: true }); + }; + + deleteIfExists(join(testsGeneratedDir, "azure", "authentication-http-custom")); + deleteIfExists(join(testsGeneratedDir, "unbranded", "encode-array")); + + if (existsSync(testsGeneratedDir)) { + const entries = await readdir(testsGeneratedDir, { recursive: true, withFileTypes: true }); + for (const entry of entries) { + if (entry.isFile() && entry.name === "README.md") { + deleteIfExists(join(entry.parentPath, entry.name)); + } } } } diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index b4edd33cca2..a6c894ec804 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -26,7 +26,7 @@ } }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "scripts": { "clean": "rimraf ./dist ./temp ./venv ./tests/generated", diff --git a/packages/http-client-python/tests/mock_api/shared/unittests/test_readme.py b/packages/http-client-python/tests/mock_api/shared/unittests/test_readme.py new file mode 100644 index 00000000000..133f680d9e7 --- /dev/null +++ b/packages/http-client-python/tests/mock_api/shared/unittests/test_readme.py @@ -0,0 +1,46 @@ +from pathlib import Path + +import pytest + +GENERATED_DIR = Path(__file__).parent / "../../../generated" +FLAVORS = ("azure", "unbranded") + +# Folders that exist in the azure-sdk-for-python baseline checkout but are not +# regenerated by the emitter, so their README.md is never (re)written. Skip +# them to keep this test focused on real generated packages. +# - "azure/service-multiple-services": listed in SKIP_SPECS in +# eng/scripts/ci/regenerate-common.ts (no emitter run for this spec). +# - "azure/azure-client-generator-core-client-initialization": orphan parent +# folder; only its sub-specs (.../default, .../individually, +# .../individuallyParent) are actually emitted as packages. +SKIP_PACKAGES = { + ("azure", "service-multiple-services"), + ("azure", "azure-client-generator-core-client-initialization"), +} + + +def _package_dirs(): + base = GENERATED_DIR.resolve() + dirs = [] + for flavor in FLAVORS: + flavor_dir = base / flavor + if not flavor_dir.is_dir(): + continue + for p in sorted(flavor_dir.iterdir()): + if not p.is_dir(): + continue + if (flavor, p.name) in SKIP_PACKAGES: + continue + dirs.append(p) + return dirs + + +@pytest.mark.parametrize( + "package_dir", + _package_dirs(), + ids=lambda p: f"{p.parent.name}/{p.name}", +) +def test_readme_exists(package_dir: Path): + """Each generated SDK package folder (azure & unbranded) must contain a README.md.""" + readme_path = package_dir / "README.md" + assert readme_path.is_file(), f"README.md not found at {readme_path}"