@@ -7,9 +7,24 @@ const board = require('../shared/project-board-client.cjs');
77 */
88module . exports = async function transitionStatus ( { github, context, core } ) {
99 const includeCurrentPullRequest = 'true' === ( process . env . INPUT_INCLUDE_CURRENT_PULL_REQUEST ?? '' ) . toLowerCase ( ) ;
10- const fromStatus = process . env . INPUT_FROM_STATUS ;
10+ const sourceStatuses = [
11+ ...( process . env . INPUT_FROM_STATUSES ?? '' ) . split ( ',' ) ,
12+ process . env . INPUT_FROM_STATUS ?? '' ,
13+ ]
14+ . map ( ( status ) => status . trim ( ) )
15+ . filter ( ( status , index , statuses ) => '' !== status && statuses . indexOf ( status ) === index ) ;
1116 const toStatus = process . env . INPUT_TO_STATUS ;
1217
18+ core . setOutput ( 'source-statuses' , sourceStatuses . join ( ',' ) ) ;
19+
20+ if ( 0 === sourceStatuses . length ) {
21+ core . info ( 'No source project statuses were provided. Skipping status transition.' ) ;
22+ core . setOutput ( 'moved-count' , '0' ) ;
23+ core . setOutput ( 'skipped-count' , '0' ) ;
24+
25+ return ;
26+ }
27+
1328 const project = await board . loadConfiguredProject (
1429 github ,
1530 context . repo . owner ,
@@ -18,6 +33,8 @@ module.exports = async function transitionStatus({ github, context, core }) {
1833
1934 if ( ! project ) {
2035 core . info ( 'No configured GitHub Project V2 was resolved. Skipping status transition.' ) ;
36+ core . setOutput ( 'moved-count' , '0' ) ;
37+ core . setOutput ( 'skipped-count' , '0' ) ;
2138
2239 return ;
2340 }
@@ -27,107 +44,93 @@ module.exports = async function transitionStatus({ github, context, core }) {
2744
2845 if ( ! statusField || ! targetOption ) {
2946 core . info ( `Project "${ project . title } " does not expose the expected target status "${ toStatus } ".` ) ;
47+ core . setOutput ( 'moved-count' , '0' ) ;
48+ core . setOutput ( 'skipped-count' , '0' ) ;
3049
3150 return ;
3251 }
3352
34- const result = await github . graphql (
35- `query($owner: String!, $repo: String!, $pullRequestNumber: Int!) {
36- repository(owner: $owner, name: $repo) {
37- issues(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, states: CLOSED) {
38- nodes {
39- number
40- projectItems(first: 20) {
41- nodes {
42- id
43- project {
44- ... on ProjectV2 {
45- id
46- }
47- }
48- fieldValues(first: 20) {
49- nodes {
50- __typename
51- ... on ProjectV2ItemFieldSingleSelectValue {
52- field {
53- ... on ProjectV2SingleSelectField {
54- name
55- }
56- }
57- name
53+ const loadProjectItems = async ( ) => {
54+ const items = [ ] ;
55+ let cursor = null ;
56+
57+ do {
58+ const result = await github . graphql (
59+ `query($project: ID!, $cursor: String) {
60+ node(id: $project) {
61+ ... on ProjectV2 {
62+ items(first: 100, after: $cursor) {
63+ pageInfo {
64+ hasNextPage
65+ endCursor
5866 }
59- }
60- }
61- }
62- }
63- }
64- }
65- pullRequests(first: 100, orderBy: {field: UPDATED_AT, direction: DESC}, states: [MERGED, CLOSED]) {
66- nodes {
67- number
68- projectItems(first: 20) {
69- nodes {
70- id
71- project {
72- ... on ProjectV2 {
73- id
74- }
75- }
76- fieldValues(first: 20) {
77- nodes {
78- __typename
79- ... on ProjectV2ItemFieldSingleSelectValue {
80- field {
81- ... on ProjectV2SingleSelectField {
82- name
67+ nodes {
68+ id
69+ content {
70+ __typename
71+ ... on Issue {
72+ number
73+ title
74+ url
75+ }
76+ ... on PullRequest {
77+ number
78+ title
79+ url
8380 }
8481 }
85- name
86- }
87- }
88- }
89- }
90- }
91- }
92- }
93- pullRequest(number: $pullRequestNumber) {
94- number
95- projectItems(first: 20) {
96- nodes {
97- id
98- project {
99- ... on ProjectV2 {
100- id
101- }
102- }
103- fieldValues(first: 20) {
104- nodes {
105- __typename
106- ... on ProjectV2ItemFieldSingleSelectValue {
107- field {
108- ... on ProjectV2SingleSelectField {
109- name
82+ fieldValues(first: 20) {
83+ nodes {
84+ __typename
85+ ... on ProjectV2ItemFieldSingleSelectValue {
86+ field {
87+ ... on ProjectV2SingleSelectField {
88+ name
89+ }
90+ }
91+ name
92+ }
93+ }
11094 }
11195 }
112- name
11396 }
11497 }
11598 }
116- }
117- }
118- }
119- }
120- }` ,
121- {
122- owner : context . repo . owner ,
123- repo : context . repo . repo ,
124- pullRequestNumber : context . payload . pull_request ?. number ?? 0 ,
125- } ,
126- ) ;
99+ }` ,
100+ {
101+ project : project . id ,
102+ cursor,
103+ } ,
104+ ) ;
105+
106+ const page = result . node ?. items ;
107+
108+ items . push ( ...( page ?. nodes ?? [ ] ) ) ;
109+ cursor = page ?. pageInfo ?. hasNextPage ? page . pageInfo . endCursor : null ;
110+ } while ( null !== cursor ) ;
111+
112+ return items ;
113+ } ;
114+
115+ const formatLabel = ( item ) => {
116+ const content = item . content ;
117+
118+ if ( 'Issue' === content ?. __typename ) {
119+ return `Issue #${ content . number } ` ;
120+ }
121+
122+ if ( 'PullRequest' === content ?. __typename ) {
123+ return `PR #${ content . number } ` ;
124+ }
125+
126+ return `Project item ${ item . id } ` ;
127+ } ;
127128
128129 const moveToStatus = async ( item , label ) => {
129- if ( ! item || board . getExistingFieldValue ( item , 'Status' ) !== fromStatus ) {
130- return ;
130+ const currentStatus = board . getExistingFieldValue ( item , 'Status' ) ;
131+
132+ if ( ! sourceStatuses . includes ( currentStatus ) ) {
133+ return false ;
131134 }
132135
133136 await board . updateSingleSelectField (
@@ -138,27 +141,29 @@ module.exports = async function transitionStatus({ github, context, core }) {
138141 targetOption . id ,
139142 ) ;
140143
141- core . info ( `${ label } moved to ${ toStatus } .` ) ;
144+ core . info ( `${ label } moved from ${ currentStatus } to ${ toStatus } .` ) ;
145+
146+ return true ;
142147 } ;
143148
144149 if ( includeCurrentPullRequest ) {
145- await moveToStatus (
146- board . findProjectItem ( result . repository . pullRequest ?. projectItems ?. nodes ?? [ ] , project . id ) ,
147- `Pull request #${ context . payload . pull_request . number } ` ,
148- ) ;
150+ core . info ( 'The include-current-pull-request input is kept for compatibility; project item pagination already includes the current pull request when it is on the board.' ) ;
149151 }
150152
151- for ( const pullRequest of result . repository . pullRequests . nodes ) {
152- await moveToStatus (
153- board . findProjectItem ( pullRequest . projectItems . nodes , project . id ) ,
154- `PR #${ pullRequest . number } ` ,
155- ) ;
156- }
153+ let movedCount = 0 ;
154+ let skippedCount = 0 ;
157155
158- for ( const issue of result . repository . issues . nodes ) {
159- await moveToStatus (
160- board . findProjectItem ( issue . projectItems . nodes , project . id ) ,
161- `Issue #${ issue . number } ` ,
162- ) ;
156+ for ( const item of await loadProjectItems ( ) ) {
157+ if ( await moveToStatus ( item , formatLabel ( item ) ) ) {
158+ movedCount ++ ;
159+
160+ continue ;
161+ }
162+
163+ skippedCount ++ ;
163164 }
165+
166+ core . info ( `${ movedCount } project item(s) moved to ${ toStatus } ; ${ skippedCount } inspected item(s) skipped.` ) ;
167+ core . setOutput ( 'moved-count' , String ( movedCount ) ) ;
168+ core . setOutput ( 'skipped-count' , String ( skippedCount ) ) ;
164169} ;
0 commit comments