@@ -2,8 +2,12 @@ const core = require('@actions/core');
22const github = require ( '@actions/github' ) ;
33const AdmZip = require ( 'adm-zip' ) ;
44
5+ let verbose = false ;
6+ let log = null ;
7+ let debug = ( msg ) => { if ( verbose ) { log ( msg ) ; } }
8+
59async function analyze ( name , count , token , owner , repo , branch ) {
6- console . log ( `Analyzing ${ name } for the last ${ count } successful runs.\n` ) ;
10+ log ( `Analyzing ${ name } for the last ${ count } successful runs.\n` ) ;
711 const octokit = github . getOctokit ( token )
812
913 const runs = await octokit . rest . actions . listWorkflowRuns ( {
@@ -20,57 +24,55 @@ async function analyze(name, count, token, owner, repo, branch) {
2024 let wasUnknown = false ;
2125
2226 for ( const run of runs . data . workflow_runs ) {
23- if ( process . env . RUNNER_DEBUG )
24- console . log ( `Analyzing run ${ run . id } ...` ) ;
27+ debug ( `Analyzing run ${ run . id } ...` ) ;
2528
2629 const jobs = await octokit . rest . actions . listJobsForWorkflowRun ( {
2730 owner : owner ,
2831 repo : repo ,
2932 run_id : run . id ,
3033 } ) ;
3134
32- if ( process . env . RUNNER_DEBUG )
33- console . log ( `Found ${ jobs . data . jobs . length } jobs.` )
35+ debug ( `Found ${ jobs . data . jobs . length } jobs.` )
3436
3537 const artifacts = await octokit . rest . actions . listWorkflowRunArtifacts ( {
3638 owner : owner ,
3739 repo : repo ,
3840 run_id : run . id ,
3941 } ) ;
4042
41- if ( process . env . RUNNER_DEBUG )
42- console . log ( `${ artifacts . data . artifacts . length } artifacts...` )
43+ debug ( `${ artifacts . data . artifacts . length } artifacts...` )
4344
4445 for ( const job of jobs . data . jobs ) {
4546 if ( job . conclusion !== 'success' )
4647 continue ;
4748
48- if ( process . env . RUNNER_DEBUG ) {
49- console . log ( `${ job . name } ${ job . id } was successful...` ) ;
50- console . log ( `Downloading logs for job id ${ job . id } ...` ) ;
51- }
49+ debug ( `${ job . name } ${ job . id } was successful...` ) ;
50+ debug ( `Downloading logs for job id ${ job . id } ...` ) ;
5251
53- let log = null ;
52+ let workflowRunLog = null ;
5453 try {
55- log = await octokit . rest . actions . downloadJobLogsForWorkflowRun ( {
54+ workflowRunLog = await octokit . rest . actions . downloadJobLogsForWorkflowRun ( {
5655 owner : owner ,
5756 repo : repo ,
5857 job_id : job . id ,
5958 } ) ;
6059 } catch ( e ) {
61- if ( process . env . RUNNER_DEBUG )
62- console . log ( `Logs for the job ${ job . id } are not available.` ) ;
60+ debug ( `Logs for the job ${ job . id } are not available.` ) ;
6361 continue ;
6462 }
6563
66- const logUploadMatch = log . data . match ( / ^ .* C o n t a i n e r f o r a r t i f a c t \" ( .* - p e r m i s s i o n s - [ a - z 0 - 9 ] + ) \" s u c c e s s f u l l y c r e a t e d \. S t a r t i n g u p l o a d o f f i l e \( s \) $ / m) ;
67- if ( ! logUploadMatch )
64+ const logUploadMatch = workflowRunLog . data . match ( / ( [ ^ " ] + - p e r m i s s i o n s - [ a - z 0 - 9 ] { 32 } ) / m) ;
65+ if ( ! logUploadMatch ) {
66+ debug ( `Cannot find the magic string. Skipping.` ) ;
6867 continue ;
68+ }
6969 const artifactName = logUploadMatch [ 1 ] ;
70+ debug ( `Looking for artifactName ${ artifactName } ` ) ;
7071 const jobName = artifactName . split ( '-' ) . slice ( 0 , - 2 ) . join ( '-' ) ;
7172
7273 for ( const artifact of artifacts . data . artifacts ) {
7374 if ( artifact . name === artifactName ) {
75+ debug ( `Downloading artifact id ${ artifact . id } ` ) ;
7476 const download = await octokit . rest . actions . downloadArtifact ( {
7577 owner : owner ,
7678 repo : repo ,
@@ -110,62 +112,105 @@ async function analyze(name, count, token, owner, repo, branch) {
110112 return [ permissions , wasUnknown ] ;
111113}
112114
113- async function run ( name , count , token , owner , repo , branch ) {
115+ async function run ( token , name , count , owner , repo , branch , format ) {
114116 const [ permissions , wasUnknown ] = await analyze ( name , count , token , owner , repo , branch ) ;
115117
116118 let summary = core . summary . addHeading ( `Minimal required permissions for ${ name } :` ) ;
117- console . log ( `Minimal required permissions for ${ name } :` ) ;
119+ log ( `Minimal required permissions for ${ name } :` ) ;
118120
119121 if ( wasUnknown ) {
120122 summary . addRaw ( "\nAt least one call wasn't recognized. Some permissions are unknown. Check the workflow runs.\n" ) ;
121123 }
122124
123- if ( permissions . size === 0 ) {
124- summary = summary . addRaw ( 'No permissions logs were found.' ) ;
125- console . log ( 'No permissions logs were found.' ) ;
126- } else {
127- for ( const [ jobName , jobPermissions ] of permissions ) {
128- summary = summary . addHeading ( `${ jobName } :` , 2 ) ;
129- console . log ( `---------------------= ${ jobName } =---------------------` ) ;
130-
131- let codeBlock = '' ;
132- if ( jobPermissions . size === 0 ) {
133- codeBlock += 'permissions: {}' ;
134- console . log ( 'permissions: {}' ) ;
135- } else {
136- codeBlock += 'permissions:\n' ;
137- console . log ( 'permissions:' ) ;
138- for ( const [ kind , perm ] of jobPermissions ) {
139- codeBlock += ` ${ kind } : ${ perm } \n` ;
140- console . log ( ` ${ kind } : ${ perm } ` ) ;
125+ try {
126+ if ( permissions . size === 0 ) {
127+ summary = summary . addRaw ( 'No permissions logs were found.' ) ;
128+ throw new Error ( 'No permissions logs were found.' ) ;
129+ } else {
130+ let additionalIndent = '' ;
131+ if ( format )
132+ additionalIndent = ' ' ;
133+
134+ for ( const [ jobName , jobPermissions ] of permissions ) {
135+ summary = summary . addHeading ( `${ jobName } :` , 2 ) ;
136+ log ( `---------------------= ${ jobName } =---------------------` ) ;
137+ if ( format )
138+ console . log ( `${ jobName } :` ) ;
139+
140+ let codeBlock = '' ;
141+ if ( jobPermissions . size === 0 ) {
142+ codeBlock += `${ additionalIndent } permissions: {}` ;
143+ } else {
144+ codeBlock += `${ additionalIndent } permissions:\n` ;
145+ for ( const [ kind , perm ] of jobPermissions ) {
146+ codeBlock += `${ additionalIndent } ${ kind } : ${ perm } \n` ;
147+ }
141148 }
149+
150+ console . log ( codeBlock ) ; // write always
151+ summary = summary . addCodeBlock ( codeBlock , 'yaml' ) ;
142152 }
143-
144- summary = summary . addCodeBlock ( codeBlock , 'yaml' ) ;
145153 }
146- }
147-
148- if ( process . env . GITHUB_ACTIONS ) {
149- await summary . write ( ) ;
154+ } finally {
155+ if ( process . env . GITHUB_ACTIONS ) {
156+ await summary . write ( ) ;
157+ }
150158 }
151159}
152160
153- if ( ! process . env . GITHUB_ACTIONS && process . argv . length !== 7 ) {
154- console . log ( 'Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name>' ) ;
155- console . log ( 'For example: node index.js ci.yml 10 github actions-permissions main' ) ;
161+ function printUsageAndExit ( ) {
162+ console . log ( 'Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name> [--format yaml] [--verbose] ' ) ;
163+ console . log ( 'For example: node index.js ci.yml 10 github actions-permissions main --format yaml --verbose ' ) ;
156164 process . exit ( 1 ) ;
157165}
158166
167+ verbose = false ;
168+ log = console . log ;
169+
159170if ( process . env . GITHUB_ACTIONS ) {
160171 const name = core . getInput ( 'name' ) ;
161172 const count = core . getInput ( 'count' ) ;
162173 const token = core . getInput ( 'token' ) ;
174+ verbose = process . env . RUNNER_DEBUG ? true : false ;
175+ const branch = github . context . ref . split ( '/' ) . slice ( - 1 ) [ 0 ] ;
176+ const format = null ;
163177
164- run ( name , count , token , github . context . repo . owner , github . context . repo . repo , github . context . ref . split ( '/' ) . slice ( - 1 ) [ 0 ] ) . catch ( error => {
178+ run ( token , name , count , github . context . repo . owner , github . context . repo . repo , branch , format ) . catch ( error => {
165179 core . setFailed ( error . message ) ;
166180 } ) ;
167181} else {
168- run ( process . argv [ 2 ] , process . argv [ 3 ] , process . env . GITHUB_TOKEN , process . argv [ 4 ] , process . argv [ 5 ] , process . argv [ 6 ] ) . catch ( error => {
169- console . log ( `Error: ${ error . message } ` ) ;
182+ const args = process . argv . slice ( 2 ) ;
183+ const outputIndex = args . indexOf ( '--format' ) ;
184+ let format = null ;
185+
186+ if ( outputIndex !== - 1 ) {
187+ if ( outputIndex + 1 >= args . length ) {
188+ printUsageAndExit ( ) ;
189+ }
190+ format = args [ outputIndex + 1 ] ;
191+ if ( ! format || format !== 'yaml' ) {
192+ printUsageAndExit ( ) ;
193+ }
194+ args . splice ( outputIndex , 2 ) ; // Remove --output and its value from args
195+ }
196+
197+ const debugIndex = args . indexOf ( '--verbose' ) ;
198+ if ( debugIndex !== - 1 ) {
199+ verbose = true ;
200+ args . splice ( debugIndex , 1 ) ; // Remove --verbose from args
201+ }
202+
203+ if ( args . length !== 5 ) {
204+ printUsageAndExit ( ) ;
205+ }
206+
207+ const [ name , count , owner , repo , branch ] = args ;
208+ if ( format !== null ) {
209+ log = ( ) => { } ;
210+ }
211+
212+ run ( process . env . GITHUB_TOKEN , name , count , owner , repo , branch , format ) . catch ( error => {
213+ console . error ( `Error: ${ error . message } ` ) ;
214+ exit ( 2 ) ;
170215 } ) ;
171216}
0 commit comments