@@ -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 ( {
@@ -19,65 +23,55 @@ async function analyze(name, count, token, owner, repo, branch) {
1923 let permissions = new Map ( ) ;
2024
2125 for ( const run of runs . data . workflow_runs ) {
22- if ( process . env . RUNNER_DEBUG )
23- console . log ( `Analyzing run ${ run . id } ...` ) ;
26+ debug ( `Analyzing run ${ run . id } ...` ) ;
2427
2528 const jobs = await octokit . rest . actions . listJobsForWorkflowRun ( {
2629 owner : owner ,
2730 repo : repo ,
2831 run_id : run . id ,
2932 } ) ;
3033
31- if ( process . env . RUNNER_DEBUG )
32- console . log ( `Found ${ jobs . data . jobs . length } jobs.` )
34+ debug ( `Found ${ jobs . data . jobs . length } jobs.` )
3335
3436 const artifacts = await octokit . rest . actions . listWorkflowRunArtifacts ( {
3537 owner : owner ,
3638 repo : repo ,
3739 run_id : run . id ,
3840 } ) ;
3941
40- if ( process . env . RUNNER_DEBUG )
41- console . log ( `${ artifacts . data . artifacts . length } artifacts...` )
42+ debug ( `${ artifacts . data . artifacts . length } artifacts...` )
4243
4344 for ( const job of jobs . data . jobs ) {
4445 if ( job . conclusion !== 'success' )
4546 continue ;
4647
47- if ( process . env . RUNNER_DEBUG ) {
48- console . log ( `${ job . name } ${ job . id } was successful...` ) ;
49- console . log ( `Downloading logs for job id ${ job . id } ...` ) ;
50- }
48+ debug ( `${ job . name } ${ job . id } was successful...` ) ;
49+ debug ( `Downloading logs for job id ${ job . id } ...` ) ;
5150
52- let log = null ;
51+ let workflowRunLog = null ;
5352 try {
54- log = await octokit . rest . actions . downloadJobLogsForWorkflowRun ( {
53+ workflowRunLog = await octokit . rest . actions . downloadJobLogsForWorkflowRun ( {
5554 owner : owner ,
5655 repo : repo ,
5756 job_id : job . id ,
5857 } ) ;
5958 } catch ( e ) {
60- if ( process . env . RUNNER_DEBUG )
61- console . log ( `Logs for the job ${ job . id } are not available.` ) ;
59+ debug ( `Logs for the job ${ job . id } are not available.` ) ;
6260 continue ;
6361 }
6462
65- const logUploadMatch = log . data . match ( / ( [ ^ " ] + - p e r m i s s i o n s - [ a - z 0 - 9 ] { 32 } ) / m) ;
63+ const logUploadMatch = workflowRunLog . data . match ( / ( [ ^ " ] + - p e r m i s s i o n s - [ a - z 0 - 9 ] { 32 } ) / m) ;
6664 if ( ! logUploadMatch ) {
67- if ( process . env . RUNNER_DEBUG ) {
68- console . log ( `Cannot find the magic string. Skipping.` ) ;
69- }
65+ debug ( `Cannot find the magic string. Skipping.` ) ;
7066 continue ;
7167 }
7268 const artifactName = logUploadMatch [ 1 ] ;
73- if ( process . env . RUNNER_DEBUG )
74- console . log ( `Looking for artifactName ${ artifactName } ` ) ;
69+ debug ( `Looking for artifactName ${ artifactName } ` ) ;
7570 const jobName = artifactName . split ( '-' ) . slice ( 0 , - 2 ) . join ( '-' ) ;
7671
7772 for ( const artifact of artifacts . data . artifacts ) {
7873 if ( artifact . name === artifactName ) {
79- if ( process . env . RUNNER_DEBUG )
80- console . log ( `Downloading artifact id ${ artifact . id } ` ) ;
74+ debug ( `Downloading artifact id ${ artifact . id } ` ) ;
8175 const download = await octokit . rest . actions . downloadArtifact ( {
8276 owner : owner ,
8377 repo : repo ,
@@ -112,58 +106,101 @@ async function analyze(name, count, token, owner, repo, branch) {
112106 return permissions ;
113107}
114108
115- async function run ( name , count , token , owner , repo , branch ) {
109+ async function run ( token , name , count , owner , repo , branch , format ) {
116110 const permissions = await analyze ( name , count , token , owner , repo , branch ) ;
117111
118112 let summary = core . summary . addHeading ( `Minimal required permissions for ${ name } :` ) ;
119- console . log ( `Minimal required permissions for ${ name } :` ) ;
120-
121- if ( permissions . size === 0 ) {
122- summary = summary . addRaw ( 'No permissions logs were found.' ) ;
123- console . log ( 'No permissions logs were found.' ) ;
124- } else {
125- for ( const [ jobName , jobPermissions ] of permissions ) {
126- summary = summary . addHeading ( `${ jobName } :` , 2 ) ;
127- console . log ( `---------------------= ${ jobName } =---------------------` ) ;
128-
129- let codeBlock = '' ;
130- if ( jobPermissions . size === 0 ) {
131- codeBlock += 'permissions: {}' ;
132- console . log ( 'permissions: {}' ) ;
133- } else {
134- codeBlock += 'permissions:\n' ;
135- console . log ( 'permissions:' ) ;
136- for ( const [ kind , perm ] of jobPermissions ) {
137- codeBlock += ` ${ kind } : ${ perm } \n` ;
138- console . log ( ` ${ kind } : ${ perm } ` ) ;
113+ log ( `Minimal required permissions for ${ name } :` ) ;
114+
115+ try {
116+ if ( permissions . size === 0 ) {
117+ summary = summary . addRaw ( 'No permissions logs were found.' ) ;
118+ throw new Error ( 'No permissions logs were found.' ) ;
119+ } else {
120+ let additionalIndent = '' ;
121+ if ( format )
122+ additionalIndent = ' ' ;
123+
124+ for ( const [ jobName , jobPermissions ] of permissions ) {
125+ summary = summary . addHeading ( `${ jobName } :` , 2 ) ;
126+ log ( `---------------------= ${ jobName } =---------------------` ) ;
127+ if ( format )
128+ console . log ( `${ jobName } :` ) ;
129+
130+ let codeBlock = '' ;
131+ if ( jobPermissions . size === 0 ) {
132+ codeBlock += `${ additionalIndent } permissions: {}` ;
133+ } else {
134+ codeBlock += `${ additionalIndent } permissions:\n` ;
135+ for ( const [ kind , perm ] of jobPermissions ) {
136+ codeBlock += `${ additionalIndent } ${ kind } : ${ perm } \n` ;
137+ }
139138 }
139+
140+ console . log ( codeBlock ) ; // write always
141+ summary = summary . addCodeBlock ( codeBlock , 'yaml' ) ;
140142 }
141-
142- summary = summary . addCodeBlock ( codeBlock , 'yaml' ) ;
143143 }
144- }
145-
146- if ( process . env . GITHUB_ACTIONS ) {
147- await summary . write ( ) ;
144+ } finally {
145+ if ( process . env . GITHUB_ACTIONS ) {
146+ await summary . write ( ) ;
147+ }
148148 }
149149}
150150
151- if ( ! process . env . GITHUB_ACTIONS && process . argv . length !== 7 ) {
152- console . log ( 'Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name>' ) ;
153- console . log ( 'For example: node index.js ci.yml 10 github actions-permissions main' ) ;
151+ function printUsageAndExit ( ) {
152+ console . log ( 'Usage: node index.js <number_of_the_last_runs> <github_owner> <repo_name> <branch_name> [--format yaml] [--verbose] ' ) ;
153+ console . log ( 'For example: node index.js ci.yml 10 github actions-permissions main --format yaml --verbose ' ) ;
154154 process . exit ( 1 ) ;
155155}
156156
157+ verbose = false ;
158+ log = console . log ;
159+
157160if ( process . env . GITHUB_ACTIONS ) {
158161 const name = core . getInput ( 'name' ) ;
159162 const count = core . getInput ( 'count' ) ;
160163 const token = core . getInput ( 'token' ) ;
164+ verbose = process . env . RUNNER_DEBUG ? true : false ;
165+ const branch = github . context . ref . split ( '/' ) . slice ( - 1 ) [ 0 ] ;
166+ const format = null ;
161167
162- run ( name , count , token , github . context . repo . owner , github . context . repo . repo , github . context . ref . split ( '/' ) . slice ( - 1 ) [ 0 ] ) . catch ( error => {
168+ run ( token , name , count , github . context . repo . owner , github . context . repo . repo , branch , format ) . catch ( error => {
163169 core . setFailed ( error . message ) ;
164170 } ) ;
165171} else {
166- run ( process . argv [ 2 ] , process . argv [ 3 ] , process . env . GITHUB_TOKEN , process . argv [ 4 ] , process . argv [ 5 ] , process . argv [ 6 ] ) . catch ( error => {
167- console . log ( `Error: ${ error . message } ` ) ;
172+ const args = process . argv . slice ( 2 ) ;
173+ const outputIndex = args . indexOf ( '--format' ) ;
174+ let format = null ;
175+
176+ if ( outputIndex !== - 1 ) {
177+ if ( outputIndex + 1 >= args . length ) {
178+ printUsageAndExit ( ) ;
179+ }
180+ format = args [ outputIndex + 1 ] ;
181+ if ( ! format || ! [ 'yaml' ] . includes ( format ) {
182+ printUsageAndExit ( ) ;
183+ }
184+ args . splice ( outputIndex , 2 ) ; // Remove --output and its value from args
185+ }
186+
187+ const debugIndex = args . indexOf ( '--verbose' ) ;
188+ if ( debugIndex !== - 1 ) {
189+ verbose = true ;
190+ args . splice ( debugIndex , 1 ) ; // Remove --verbose from args
191+ }
192+
193+ if ( args . length !== 5 ) {
194+ printUsageAndExit ( ) ;
195+ }
196+
197+ const [ name , count , owner , repo , branch ] = args ;
198+ if ( format !== null ) {
199+ log = ( ) => { } ;
200+ }
201+
202+ run ( process . env . GITHUB_TOKEN , name , count , owner , repo , branch , format ) . catch ( error => {
203+ console . error ( `Error: ${ error . message } ` ) ;
204+ exit ( 2 ) ;
168205 } ) ;
169206}
0 commit comments