@@ -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
55121def 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