22
33declare (strict_types=1 );
44
5+ class Context {
6+ public string $ github_output ;
7+ public string $ pr_number ;
8+ public string $ pr_sha ;
9+ public string $ pr_ref ;
10+ public string $ pr_repo_url ;
11+ public string $ pr_title ;
12+ public string $ pr_description ;
13+ public string $ target_sha ;
14+ public string $ target_ref ;
15+ /** @var list<string> */
16+ public array $ release_branches ;
17+ public string $ pr_first_sha ;
18+ }
19+
20+ function get_context (): Context {
21+ $ context = new Context ;
22+
23+ $ env_mapping = [
24+ 'PR_DESCRIPTION ' => 'pr_description ' ,
25+ 'PR_NUMBER ' => 'pr_number ' ,
26+ 'PR_REF ' => 'pr_ref ' ,
27+ 'PR_REPO_URL ' => 'pr_repo_url ' ,
28+ 'PR_SHA ' => 'pr_sha ' ,
29+ 'PR_TITLE ' => 'pr_title ' ,
30+ 'TARGET_REF ' => 'target_ref ' ,
31+ 'TARGET_SHA ' => 'target_sha ' ,
32+ ];
33+
34+ foreach ($ env_mapping as $ env_name => $ prop_name ) {
35+ $ value = getenv ($ env_name );
36+ if ($ value === false ) {
37+ throw new InvalidArgumentException ("Missing env var $ env_name " );
38+ }
39+ $ context ->{$ prop_name } = $ value ;
40+ }
41+
42+ $ context ->release_branches = find_release_branches ($ context ->target_ref );
43+ $ context ->pr_first_sha = trim (run_command ("git log --reverse --format=%H {$ context ->target_sha }.. {$ context ->pr_sha } | head -n1 " )->stdout );
44+
45+ return $ context ;
46+ }
47+
548class ProcessResult {
649 public int $ status = 0 ;
750 public string $ stdout = '' ;
@@ -19,6 +62,9 @@ function run_command(string|array $cmd, ?string $failure_message = 'Unexpected e
1962 fwrite (STDERR , "::group:: {$ cmd }\n" );
2063
2164 $ process_handle = proc_open ($ cmd , $ descriptor_spec , $ pipes );
65+ if ($ process_handle === false ) {
66+ throw new RuntimeException ("Failed to execute command ` $ cmd` " );
67+ }
2268
2369 $ stdin = $ pipes [0 ];
2470 $ stdout = $ pipes [1 ];
@@ -122,20 +168,20 @@ function find_release_branches(string $target): array {
122168 return $ branches ;
123169}
124170
125- function merge_pr_into_target (string $ pr_sha , string $ pr_first_sha , string $ target , string $ message, string $ description ): string {
126- $ author = trim (run_command (['git ' , 'log ' , '-1 ' , '--format=%an <%ae> ' , $ pr_first_sha ])->stdout );
171+ function merge_pr_into_target (Context $ context , string $ message ): string {
172+ $ author = trim (run_command (['git ' , 'log ' , '-1 ' , '--format=%an <%ae> ' , $ context -> pr_first_sha ])->stdout );
127173
128- run (['git ' , 'checkout ' , '-B ' , $ target , "refs/remotes/origin/ $ target " ]);
129- run (['git ' , 'merge ' , '--squash ' , $ pr_sha ],
130- failure_message: "Failed to squash PR into $ target . " );
131- run (['git ' , 'commit ' , "--author= $ author " , '-m ' , $ message , '-m ' , wrap_commit_message ($ description )]);
174+ run (['git ' , 'checkout ' , '-B ' , $ context -> target_ref , "refs/remotes/origin/ { $ context -> target_ref } " ]);
175+ run (['git ' , 'merge ' , '--squash ' , $ context -> pr_sha ],
176+ failure_message: "Failed to squash PR into { $ context -> target_ref } . " );
177+ run (['git ' , 'commit ' , "--author= $ author " , '-m ' , $ message , '-m ' , wrap_commit_message ($ context -> pr_description )]);
132178 $ squashed_sha = trim ((string ) shell_exec ('git rev-parse HEAD ' ));
133179
134180 return $ squashed_sha ;
135181}
136182
137- /** @param list<string> $branches */
138- function merge_upwards ( array $ branches): void {
183+ function merge_upwards ( Context $ context ): void {
184+ $ branches = $ context -> release_branches ;
139185 for ($ i = 1 ; $ i < count ($ branches ); $ i ++) {
140186 $ prev = $ branches [$ i - 1 ];
141187 $ current = $ branches [$ i ];
@@ -151,8 +197,8 @@ enum PushPrBranchResult {
151197 case RemoteRejected;
152198}
153199
154- function push_pr_branch (string $ url , string $ branch , string $ new_commit, string $ expected_commit ): PushPrBranchResult {
155- $ result = run_command (['git ' , 'push ' , "--force-with-lease= $ branch : $ expected_commit " , $ url , "$ new_commit:refs/heads/ $ branch " ], failure_message: null );
200+ function push_pr_branch (Context $ context , string $ new_commit ): PushPrBranchResult {
201+ $ result = run_command (['git ' , 'push ' , "--force-with-lease= { $ context -> pr_ref } : { $ context -> pr_sha } " , $ context -> pr_repo_url , "$ new_commit:refs/heads/ { $ context -> pr_ref } " ], failure_message: null );
156202 if ($ result ->status === 0 ) {
157203 return PushPrBranchResult::Success;
158204 } else if (preg_match ('(\[rejected\]) ' , $ result ->stderr )) {
@@ -162,13 +208,12 @@ function push_pr_branch(string $url, string $branch, string $new_commit, string
162208 }
163209}
164210
165- /** @param list<string> $branches */
166- function push_release_branches (array $ branches ): bool {
167- return try_run (['git ' , 'push ' , '--atomic ' , 'origin ' , ...$ branches ]);
211+ function push_release_branches (Context $ context ): bool {
212+ return try_run (['git ' , 'push ' , '--atomic ' , 'origin ' , ...$ context ->release_branches ]);
168213}
169214
170- function revert_pr_branch (string $ url , string $ branch , string $ new_commit , string $ expected_commit ): void {
171- run_command (['git ' , 'push ' , "--force-with-lease= $ branch : $ expected_commit " , $ url , "$ new_commit :refs/heads/ $ branch " ],
215+ function revert_pr_branch (Context $ context , string $ expected_commit ): void {
216+ run_command (['git ' , 'push ' , "--force-with-lease= { $ context -> pr_ref } : $ expected_commit " , $ context -> pr_repo_url , "{ $ context -> pr_sha } :refs/heads/ { $ context -> pr_ref } " ],
172217 failure_message: 'Failed to push release branches. Reverting PR branch also failed. ' );
173218}
174219
@@ -201,40 +246,30 @@ function wrap_commit_message(string $message, int $width = 80): string {
201246}
202247
203248function main (): int {
204- $ target_sha = getenv ('TARGET_SHA ' );
205- $ target_ref = getenv ('TARGET_REF ' );
206- $ pr_number = getenv ('PR_NUMBER ' );
207- $ pr_sha = getenv ('PR_SHA ' );
208- $ pr_ref = getenv ('PR_REF ' );
209- $ pr_repo_url = getenv ('PR_REPO_URL ' );
210- $ pr_title = getenv ('PR_TITLE ' );
211- $ pr_description = getenv ('PR_DESCRIPTION ' );
212249 $ github_output = getenv ('GITHUB_OUTPUT ' );
250+ if ($ github_output === false ) {
251+ throw new InvalidArgumentException ('Missing env var GITHUB_OUTPUT ' );
252+ }
213253
214254 try {
215- $ release_branches = find_release_branches ($ target_ref );
216- $ pr_first_sha = trim (run_command ("git log --reverse --format=%H $ target_sha.. $ pr_sha | head -n1 " )->stdout );
255+ $ context = get_context ();
217256
218- $ squashed_sha = merge_pr_into_target ($ pr_sha , $ pr_first_sha , $ target_ref , " $ pr_title (GH- $ pr_number) " , $ pr_description );
219- merge_upwards ($ release_branches );
220- $ push_pr_branch_result = push_pr_branch ($ pr_repo_url , $ pr_ref , $ squashed_sha, $ pr_sha );
257+ $ squashed_sha = merge_pr_into_target ($ context , "{ $ context -> pr_title } (GH- { $ context -> pr_number } ) " );
258+ merge_upwards ($ context );
259+ $ push_pr_branch_result = push_pr_branch ($ context , $ squashed_sha );
221260 if ($ push_pr_branch_result === PushPrBranchResult::Rejected) {
222261 throw new RuntimeException ('PR branch diverged. ' );
223262 } else if ($ push_pr_branch_result === PushPrBranchResult::RemoteRejected) {
224- if ($ github_output !== false ) {
225- // Contributor likely unchecked the "Allow edits by maintainers"
226- // checkbox. Resume and close PR manually.
227- file_put_contents ($ github_output , "close_pr=1 \n" , FILE_APPEND );
228- }
263+ // Contributor likely unchecked the "Allow edits by maintainers"
264+ // checkbox. Resume and close PR manually.
265+ file_put_contents ($ github_output , "close_pr=1 \n" , FILE_APPEND );
229266 }
230- if (!push_release_branches ($ release_branches )) {
231- revert_pr_branch ($ pr_repo_url , $ pr_ref , $ pr_sha , $ squashed_sha );
267+ if (!push_release_branches ($ context )) {
268+ revert_pr_branch ($ context , $ squashed_sha );
232269 throw new RuntimeException ('Failed to push release branches. ' );
233270 }
234271 } catch (Throwable $ e ) {
235- if ($ github_output !== false ) {
236- file_put_contents ($ github_output , "fail_reason<<EOF \n{$ e ->getMessage ()}\nEOF \n" , FILE_APPEND );
237- }
272+ file_put_contents ($ github_output , "fail_reason<<EOF \n{$ e ->getMessage ()}\nEOF \n" , FILE_APPEND );
238273 fwrite (STDERR , "::error:: {$ e ->getMessage ()}\n" );
239274 return 1 ;
240275 }
0 commit comments