Skip to content

Commit b58b6dc

Browse files
authored
Merge branch 'main' into copilot/fix-regenerate-typespec-python-tests
2 parents dbcc82a + b33d8a5 commit b58b6dc

1 file changed

Lines changed: 69 additions & 0 deletions

File tree

eng/tools/azure-sdk-tools/packaging_tools/sdk_generator.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,72 @@ def execute_func_with_timeout(func, timeout: int = 900) -> Any:
5151
return multiprocessing.Pool(processes=1).apply_async(func).get(timeout)
5252

5353

54+
# Hard-coded name of the optional post-emitter script. If a service team places a
55+
# script with this name in the generated package folder (sdk/<service>/azure-*),
56+
# it will be executed after code generation.
57+
POST_EMITTER_SCRIPT_NAME = "PostEmitter.ps1"
58+
59+
60+
def run_post_emitter_script(sdk_code_path: str) -> None:
61+
"""Run the optional post-emitter PowerShell script for a package, if present.
62+
63+
When a script whose name matches ``POST_EMITTER_SCRIPT_NAME`` exists directly
64+
inside the generated package folder (``sdk/<service>/azure-*``), it is executed
65+
after code generation so service teams can run custom post-processing on the
66+
generated SDK. The script's stdout/stderr are captured and logged so they appear
67+
in the pipeline output. Failures are logged but never fail the overall generation.
68+
"""
69+
package_folder = Path(sdk_code_path).resolve()
70+
script_path = package_folder / POST_EMITTER_SCRIPT_NAME
71+
72+
# Run the script only when it exists; otherwise this is a no-op.
73+
if not script_path.is_file():
74+
_LOGGER.info(f"[POST-EMITTER] Skip running post-emitter script since file {script_path} was not found.")
75+
return
76+
77+
pwsh = shutil.which("pwsh") or shutil.which("powershell")
78+
if not pwsh:
79+
_LOGGER.warning(
80+
f"[POST-EMITTER] Found {script_path} but no PowerShell executable (pwsh/powershell) is available; skipping."
81+
)
82+
return
83+
84+
_LOGGER.info(f"[POST-EMITTER] Running post-emitter script: {script_path}")
85+
post_emitter_start_time = time.time()
86+
try:
87+
process = subprocess.run(
88+
[
89+
pwsh,
90+
"-NonInteractive",
91+
"-NoProfile",
92+
"-ExecutionPolicy",
93+
"Bypass",
94+
"-File",
95+
str(script_path),
96+
],
97+
cwd=str(package_folder),
98+
capture_output=True,
99+
text=True,
100+
timeout=600,
101+
)
102+
if process.stdout:
103+
_LOGGER.info(f"[POST-EMITTER] stdout:\n{process.stdout}")
104+
if process.stderr:
105+
_LOGGER.warning(f"[POST-EMITTER] stderr:\n{process.stderr}")
106+
if process.returncode != 0:
107+
_LOGGER.warning(f"[POST-EMITTER] Script {script_path} exited with non-zero code {process.returncode}.")
108+
else:
109+
_LOGGER.info(f"[POST-EMITTER] Script {script_path} completed successfully.")
110+
except subprocess.TimeoutExpired:
111+
_LOGGER.warning(f"[POST-EMITTER] Script {script_path} timed out after 600 seconds.")
112+
except Exception as e:
113+
_LOGGER.warning(f"[POST-EMITTER] Fail to run script {script_path}: {str(e)}")
114+
finally:
115+
_LOGGER.info(
116+
f"[POST-EMITTER] post-emitter script cost time: {int(time.time() - post_emitter_start_time)} seconds"
117+
)
118+
119+
54120
# return relative path like: network/azure-mgmt-network
55121
def extract_sdk_folder(python_md: List[str]) -> str:
56122
pattern = ["$(python-sdks-folder)", "azure-mgmt-"]
@@ -242,6 +308,9 @@ def main(generate_input, generate_output):
242308
readme_or_tsp=readme_or_tsp,
243309
)
244310

311+
# Run optional post-emitter script provided by the service team
312+
run_post_emitter_script(sdk_code_path)
313+
245314
# Generate ApiView
246315
if data.get("runMode") in ["spec-pull-request"]:
247316
apiview_start_time = time.time()

0 commit comments

Comments
 (0)