Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/fix-ci-readme-regeneration-2026-5-14.md
Original file line number Diff line number Diff line change
@@ -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).
27 changes: 17 additions & 10 deletions packages/http-client-python/eng/scripts/ci/regenerate-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
}
2 changes: 1 addition & 1 deletion packages/http-client-python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}
},
"engines": {
"node": ">=20.0.0"
"node": ">=22.0.0"
},
"scripts": {
"clean": "rimraf ./dist ./temp ./venv ./tests/generated",
Expand Down
Original file line number Diff line number Diff line change
@@ -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}"
Loading