33declare (strict_types=1 );
44
55class Context {
6+ public string $ actor_id ;
67 public string $ pr_number ;
78 public string $ pr_sha ;
89 public string $ pr_ref ;
@@ -20,6 +21,7 @@ function get_context(): Context {
2021 $ context = new Context ;
2122
2223 $ env_mapping = [
24+ 'ACTOR_ID ' => 'actor_id ' ,
2325 'PR_DESCRIPTION ' => 'pr_description ' ,
2426 'PR_NUMBER ' => 'pr_number ' ,
2527 'PR_REF ' => 'pr_ref ' ,
@@ -167,21 +169,47 @@ function find_release_branches(string $target): array {
167169 return $ branches ;
168170}
169171
172+ function fetch_approvers (Context $ context ) {
173+ $ approver_ids = [$ context ->actor_id , ...preg_split ('(\n) ' , trim (run_command ("gh api \"/repos/php/php-src/pulls/22096/reviews \" --jq 'map(select(.state == \"APPROVED \" and .commit_id == \"800d8377b8abea7317ed2e47cb6bc0fd9e0909ea \") | .user.id)[]' " )->stdout ), flags: PREG_SPLIT_NO_EMPTY )];
174+ $ approvers = [];
175+ foreach ($ approver_ids as $ approver_id ) {
176+ $ id = $ user ['id ' ];
177+ $ login = $ user ['login ' ];
178+ $ user = json_decode (run_command ("gh api '/user/ $ id' " )->stdout , true , flags: JSON_THROW_ON_ERROR );
179+ $ name = $ user ['name ' ] ?? $ login ;
180+ $ email = $ user ['email ' ] ?? "{$ id }+ {$ login }@users.noreply.github.com " ;
181+ $ approvers [] = "$ name < $ email> " ;
182+ }
183+ return $ approvers ;
184+ }
185+
170186function merge_pr_into_target (Context $ context ): string {
171187 $ author = trim (run_command (['git ' , 'log ' , '-1 ' , '--format=%an <%ae> ' , $ context ->pr_first_sha ])->stdout );
172188 $ message = "{$ context ->pr_title } (GH- {$ context ->pr_number }) " ;
173189 $ description = wrap_commit_message ($ context ->pr_description );
174190
175191 $ co_authors = preg_split ('(\n) ' , trim (run_command ("git log --reverse --format='%an <%ae>' {$ context ->pr_first_sha }.. {$ context ->pr_sha } | sort | uniq -c | sort -rn | sed 's/^ *[0-9]* //' " )->stdout ), flags: PREG_SPLIT_NO_EMPTY );
176- $ co_authors = array_filter ($ co_authors , fn (string $ co_author ) => strcasecmp ($ co_author , $ author ) !== 0 && stripos ($ description , $ co_author ) === false );
192+ $ co_authors = array_map (fn (string $ co_author ) => 'Co-authored-by: ' . $ co_author , $ co_authors );
193+ $ co_authors = array_filter ($ co_authors , fn (string $ co_author ) => strcasecmp ($ co_author , 'Co-authored-by: ' . $ author ) !== 0 && stripos ($ description , $ co_author ) === false );
177194 if (count ($ co_authors )) {
178- $ co_authors = array_map (fn (string $ co_author ) => 'Co-authored-by: ' . $ co_author , $ co_authors );
179195 if ($ description !== '' ) {
180196 $ description .= "\n\n" ;
181197 }
182198 $ description .= implode ("\n" , $ co_authors );
183199 }
184200
201+ $ approvers = fetch_approvers ($ context );
202+ if (count ($ approvers )) {
203+ $ approvers = array_map (fn (string $ pr_approver ) => 'Signed-off-by: ' . $ pr_approver , $ approvers );
204+ $ approvers = array_filter ($ approvers , fn (string $ pr_approver ) => strcasecmp ($ pr_approver , 'Signed-off-by: ' . $ author ) !== 0 && stripos ($ description , $ pr_approver ) === false );
205+ if (count ($ approvers )) {
206+ if ($ description !== '' ) {
207+ $ description .= "\n\n" ;
208+ }
209+ $ description .= implode ("\n" , $ approvers );
210+ }
211+ }
212+
185213 run (['git ' , 'checkout ' , '-B ' , $ context ->target_ref , "refs/remotes/origin/ {$ context ->target_ref }" ]);
186214 run (['git ' , 'merge ' , '--squash ' , $ context ->pr_sha ],
187215 failure_message: "Failed to squash PR into {$ context ->target_ref }. " );
0 commit comments