@@ -42,6 +42,59 @@ const workflowURL = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOS
4242
4343const getCommit = memoize ( GithubUtils . octokit . git . getCommit ) ;
4444
45+ /**
46+ * Process staging deploy comments for a list of PRs
47+ */
48+ async function commentStagingDeployPRs (
49+ prList : number [ ] ,
50+ repoName : string ,
51+ recentTags : Awaited < ReturnType < typeof GithubUtils . octokit . repos . listTags > > [ 'data' ] ,
52+ getDeployMessage : ( deployer : string , deployVerb : string , prTitle ?: string ) => string ,
53+ ) {
54+ for ( const prNumber of prList ) {
55+ try {
56+ const { data : pr } = await GithubUtils . octokit . pulls . get ( {
57+ owner : CONST . GITHUB_OWNER ,
58+ repo : repoName ,
59+ pull_number : prNumber ,
60+ } ) ;
61+
62+ // Find the deployer: either the merger, or for CPs, the tag creator
63+ const isCP = pr . labels . some ( ( { name : labelName } ) => labelName === CONST . LABELS . CP_STAGING ) ;
64+ let deployer = pr . merged_by ?. login ;
65+ if ( isCP ) {
66+ for ( const tag of recentTags ) {
67+ const { data : commit } = await getCommit ( {
68+ owner : CONST . GITHUB_OWNER ,
69+ repo : repoName ,
70+ commit_sha : tag . commit . sha ,
71+ } ) ;
72+ const prNumForCPMergeCommit = commit . message . match ( / M e r g e p u l l r e q u e s t # ( \d + ) [ \S \s ] * \( c h e r r y p i c k e d f r o m c o m m i t .* \) / ) ;
73+ if ( prNumForCPMergeCommit ?. at ( 1 ) === String ( prNumber ) ) {
74+ const cpActor = commit . message . match ( / .* \( c h e r r y - p i c k e d t o .* b y ( .* ) \) / ) ?. at ( 1 ) ;
75+ if ( cpActor ) {
76+ deployer = cpActor ;
77+ }
78+ break ;
79+ }
80+ }
81+ }
82+
83+ const title = pr . title ;
84+ const deployMessage = deployer ? getDeployMessage ( deployer , isCP ? 'Cherry-picked' : 'Deployed' , title ) : '' ;
85+ await commentPR ( prNumber , deployMessage , repoName ) ;
86+ } catch ( error ) {
87+ if ( ( error as RequestError ) . status === 404 ) {
88+ console . log ( `Unable to comment on ${ repoName } PR #${ prNumber } . GitHub responded with 404.` ) ;
89+ } else if ( repoName === CONST . MOBILE_EXPENSIFY_REPO && process . env . GITHUB_REPOSITORY !== 'Expensify/App' ) {
90+ console . warn ( `Unable to comment on ${ repoName } PR #${ prNumber } from forked repository. This is expected.` ) ;
91+ } else {
92+ throw error ;
93+ }
94+ }
95+ }
96+ }
97+
4598async function run ( ) {
4699 const prList = ( ActionUtils . getJSONInput ( 'PR_LIST' , { required : true } ) as string [ ] ) . map ( ( num ) => Number . parseInt ( num , 10 ) ) ;
47100 const mobileExpensifyPRList = ( ActionUtils . getJSONInput ( 'MOBILE_EXPENSIFY_PR_LIST' , { required : false } ) as string [ ] | undefined ) ?. map ( ( num ) => Number . parseInt ( num , 10 ) ) ?? [ ] ;
@@ -109,124 +162,34 @@ async function run() {
109162 return ;
110163 }
111164
112- const { data : recentTags } = await GithubUtils . octokit . repos . listTags ( {
165+ const { data : appRecentTags } = await GithubUtils . octokit . repos . listTags ( {
113166 owner : CONST . GITHUB_OWNER ,
114167 repo : CONST . APP_REPO ,
115168 per_page : 100 ,
116169 } ) ;
117170
118- for ( const prNumber of prList ) {
119- /*
120- * Determine who the deployer for the PR is. The "deployer" for staging deploys is:
121- * 1. For regular staging deploys, the person who merged the PR.
122- * 2. For CPs, the person who committed the cherry-picked commit (not necessarily the author of the commit).
123- */
171+ // Only fetch Mobile-Expensify tags if there are Mobile-Expensify PRs
172+ let mobileExpensifyRecentTags : typeof appRecentTags = [ ] ;
173+ if ( mobileExpensifyPRList . length > 0 ) {
124174 try {
125- const { data : pr } = await GithubUtils . octokit . pulls . get ( {
126- owner : CONST . GITHUB_OWNER ,
127- repo : CONST . APP_REPO ,
128- pull_number : prNumber ,
129- } ) ;
130-
131- // Check for the CP Staging label on the issue to see if it was cherry-picked
132- const isCP = pr . labels . some ( ( { name : labelName } ) => labelName === CONST . LABELS . CP_STAGING ) ;
133-
134- // Determine the deployer. For most PRs it will be whoever merged the PR.
135- // For CPs it will be whoever created the tag for the PR (i.e: whoever triggered the CP)
136- let deployer = pr . merged_by ?. login ;
137- if ( isCP ) {
138- for ( const tag of recentTags ) {
139- const { data : commit } = await getCommit ( {
140- owner : CONST . GITHUB_OWNER ,
141- repo : CONST . APP_REPO ,
142- commit_sha : tag . commit . sha ,
143- } ) ;
144- const prNumForCPMergeCommit = commit . message . match ( / M e r g e p u l l r e q u e s t # ( \d + ) [ \S \s ] * \( c h e r r y p i c k e d f r o m c o m m i t .* \) / ) ;
145- if ( prNumForCPMergeCommit ?. at ( 1 ) === String ( prNumber ) ) {
146- const cpActor = commit . message . match ( / .* \( c h e r r y - p i c k e d t o .* b y ( .* ) \) / ) ?. at ( 1 ) ;
147- if ( cpActor ) {
148- deployer = cpActor ;
149- }
150- break ;
151- }
152- }
153- }
154-
155- const title = pr . title ;
156- const deployMessage = deployer ? getDeployMessage ( deployer , isCP ? 'Cherry-picked' : 'Deployed' , title ) : '' ;
157- await commentPR ( prNumber , deployMessage ) ;
158- } catch ( error ) {
159- if ( ( error as RequestError ) . status === 404 ) {
160- console . log ( `Unable to comment on PR #${ prNumber } . GitHub responded with 404.` ) ;
161- } else {
162- throw error ;
163- }
164- }
165- }
166-
167- // Handle Mobile-Expensify PRs for staging deploys
168- // Note: We'll need to fetch tags from Mobile-Expensify repo for cherry-pick detection
169- let mobileExpensifyRecentTags : typeof recentTags = [ ] ;
170- try {
171- const response = await GithubUtils . octokit . repos . listTags ( {
172- owner : CONST . GITHUB_OWNER ,
173- repo : CONST . MOBILE_EXPENSIFY_REPO ,
174- per_page : 100 ,
175- } ) ;
176- mobileExpensifyRecentTags = response . data ;
177- } catch ( error ) {
178- if ( process . env . GITHUB_REPOSITORY !== 'Expensify/App' ) {
179- console . warn ( 'Unable to fetch Mobile-Expensify tags from forked repository. This is expected.' ) ;
180- } else {
181- console . error ( 'Failed to fetch Mobile-Expensify tags:' , error ) ;
182- }
183- }
184-
185- for ( const prNumber of mobileExpensifyPRList ) {
186- try {
187- const { data : pr } = await GithubUtils . octokit . pulls . get ( {
175+ const response = await GithubUtils . octokit . repos . listTags ( {
188176 owner : CONST . GITHUB_OWNER ,
189177 repo : CONST . MOBILE_EXPENSIFY_REPO ,
190- pull_number : prNumber ,
178+ per_page : 100 ,
191179 } ) ;
192-
193- // Check for the CP Staging label on the issue to see if it was cherry-picked
194- const isCP = pr . labels . some ( ( { name : labelName } ) => labelName === CONST . LABELS . CP_STAGING ) ;
195-
196- // Determine the deployer. For most PRs it will be whoever merged the PR.
197- // For CPs it will be whoever created the tag for the PR (i.e: whoever triggered the CP)
198- let deployer = pr . merged_by ?. login ;
199- if ( isCP ) {
200- for ( const tag of mobileExpensifyRecentTags ) {
201- const { data : commit } = await getCommit ( {
202- owner : CONST . GITHUB_OWNER ,
203- repo : CONST . MOBILE_EXPENSIFY_REPO ,
204- commit_sha : tag . commit . sha ,
205- } ) ;
206- const prNumForCPMergeCommit = commit . message . match ( / M e r g e p u l l r e q u e s t # ( \d + ) [ \S \s ] * \( c h e r r y p i c k e d f r o m c o m m i t .* \) / ) ;
207- if ( prNumForCPMergeCommit ?. at ( 1 ) === String ( prNumber ) ) {
208- const cpActor = commit . message . match ( / .* \( c h e r r y - p i c k e d t o .* b y ( .* ) \) / ) ?. at ( 1 ) ;
209- if ( cpActor ) {
210- deployer = cpActor ;
211- }
212- break ;
213- }
214- }
215- }
216-
217- const title = pr . title ;
218- const deployMessage = deployer ? getDeployMessage ( deployer , isCP ? 'Cherry-picked' : 'Deployed' , title ) : '' ;
219- await commentPR ( prNumber , deployMessage , CONST . MOBILE_EXPENSIFY_REPO ) ;
180+ mobileExpensifyRecentTags = response . data ;
220181 } catch ( error ) {
221- if ( ( error as RequestError ) . status === 404 ) {
222- console . log ( `Unable to comment on Mobile-Expensify PR #${ prNumber } . GitHub responded with 404.` ) ;
223- } else if ( process . env . GITHUB_REPOSITORY !== 'Expensify/App' ) {
224- console . warn ( `Unable to comment on Mobile-Expensify PR #${ prNumber } from forked repository. This is expected.` ) ;
182+ if ( process . env . GITHUB_REPOSITORY !== 'Expensify/App' ) {
183+ console . warn ( 'Unable to fetch Mobile-Expensify tags from forked repository. This is expected.' ) ;
225184 } else {
226- throw error ;
185+ console . error ( 'Failed to fetch Mobile-Expensify tags:' , error ) ;
227186 }
228187 }
229188 }
189+
190+ // Comment on the PRs
191+ await commentStagingDeployPRs ( prList , CONST . APP_REPO , appRecentTags , getDeployMessage ) ;
192+ await commentStagingDeployPRs ( mobileExpensifyPRList , CONST . MOBILE_EXPENSIFY_REPO , mobileExpensifyRecentTags , getDeployMessage ) ;
230193}
231194
232195if ( require . main === module ) {
0 commit comments