@@ -47266,63 +47266,98 @@ const generateScope = async ({ octokit, orgs, scope, maxRequestInParallel }) =>
4726647266 return newScope
4726747267}
4726847268
47269- const generateScores = async ({ scope, database: currentDatabase, maxRequestInParallel, reportTagsEnabled, renderBadge, reportTool }) => {
47269+ /**
47270+ * Parse local Scorecard results (Scorecard JSON v2 format) into the
47271+ * internal score format used by scorecard-monitor.
47272+ * @param {Array} results - Array of Scorecard JSON v2 result objects
47273+ * @returns {Array} - Array of {score, date, commit, platform, org, repo}
47274+ */
47275+ const parseLocalResults = (results) => {
47276+ return results.map((x) => {
47277+ const parts = x.repo.name.split('/')
47278+ return {
47279+ score: x.score,
47280+ date: x.date,
47281+ commit: x.repo.commit,
47282+ platform: parts[0],
47283+ org: parts[1],
47284+ repo: parts[2]
47285+ }
47286+ })
47287+ }
47288+
47289+ const generateScores = async ({ scope, database: currentDatabase, maxRequestInParallel, reportTagsEnabled, renderBadge, reportTool, localResultsPath }) => {
4727047290 // @TODO: Improve deep clone logic
4727147291 const database = JSON.parse(JSON.stringify(currentDatabase))
47272- const platform = 'github.com'
4727347292
47274- // @TODO: End the action if there are no projects in scope?
47293+ let rawScores = []
4727547294
47276- const orgs = Object.keys(scope[platform])
47277- core.debug(`Total Orgs/Users in scope: ${orgs.length}`)
47295+ if (localResultsPath) {
47296+ // Local results mode: read scores from a Scorecard JSON v2 file
47297+ const { readFile } = (__nccwpck_require__(7147).promises)
47298+ const content = await readFile(localResultsPath, 'utf8')
47299+ const results = JSON.parse(content)
47300+ rawScores = parseLocalResults(Array.isArray(results) ? results : [results])
47301+ core.debug(`Loaded ${rawScores.length} scores from local results file: ${localResultsPath}`)
47302+ } else {
47303+ // API mode: fetch scores from the public Scorecard API
47304+ const platform = 'github.com'
4727847305
47279- // Structure Projects
47280- const projects = []
47306+ // @TODO: End the action if there are no projects in scope?
4728147307
47282- orgs.forEach((org) => {
47283- const repos = scope[platform][org].included
47284- repos.forEach((repo) => projects.push({ org, repo }))
47285- })
47308+ const orgs = Object.keys(scope[platform])
47309+ core.debug(`Total Orgs/Users in scope: ${orgs.length}`)
4728647310
47287- core.debug(`Total Projects in scope: ${projects.length}`)
47311+ // Structure Projects
47312+ const projects = []
4728847313
47289- const chunks = chunkArray(projects, maxRequestInParallel)
47290- core.debug(`Total chunks: ${chunks.length}`)
47314+ orgs.forEach((org) => {
47315+ const repos = scope[platform][org].included
47316+ repos.forEach((repo) => projects.push({ org, repo }))
47317+ })
4729147318
47292- const scores = []
47319+ core.debug(`Total Projects in scope: ${projects.length}`)
4729347320
47294- for (let index = 0; index < chunks.length; index++) {
47295- const chunk = chunks[index]
47296- core.debug(`Processing chunk ${index + 1}/${chunks.length}`)
47321+ const chunks = chunkArray(projects, maxRequestInParallel)
47322+ core.debug(`Total chunks: ${chunks.length}`)
4729747323
47298- const chunkScores = await Promise.all(chunk.map(async ({ org, repo }) => {
47299- const { score, date, commit } = await getProjectScore({ platform, org, repo })
47300- core.debug(`Got project score for ${platform }/${org}/${repo}: ${score} (${date}) `)
47324+ for (let index = 0; index < chunks.length; index++) {
47325+ const chunk = chunks[index]
47326+ core.debug(`Processing chunk ${index + 1 }/${chunks.length} `)
4730147327
47302- const storedScore = getScore({ database, platform, org, repo })
47328+ const chunkScores = await Promise.all(chunk.map(async ({ org, repo }) => {
47329+ const { score, date, commit } = await getProjectScore({ platform, org, repo })
47330+ core.debug(`Got project score for ${platform}/${org}/${repo}: ${score} (${date})`)
47331+ return { score, date, commit, platform, org, repo }
47332+ }))
4730347333
47304- const scoreData = { platform, org, repo, score, date, commit }
47305- // If no stored score then record if score is different then:
47306- if (!storedScore || storedScore.score !== score) {
47307- saveScore({ database, platform, org, repo, score, date, commit })
47308- }
47334+ rawScores.push(...chunkScores)
47335+ }
47336+ }
4730947337
47310- // Add previous score and date if available to the report
47311- if (storedScore) {
47312- scoreData.prevScore = storedScore.score
47313- scoreData.prevDate = storedScore.date
47314- scoreData.prevCommit = storedScore.commit
47338+ // Common path: enrich scores with database history
47339+ const scores = rawScores.map(({ score, date, commit, platform, org, repo }) => {
47340+ const storedScore = getScore({ database, platform, org, repo })
47341+ const scoreData = { platform, org, repo, score, date, commit }
4731547342
47316- if (storedScore. score !== score) {
47317- scoreData.currentDiff = parseFloat(( score - storedScore.score).toFixed(1))
47318- }
47319- }
47343+ // If no stored score then record if score is different then:
47344+ if (!storedScore || storedScore.score !== score) {
47345+ saveScore({ database, platform, org, repo, score, date, commit })
47346+ }
4732047347
47321- return scoreData
47322- }))
47348+ // Add previous score and date if available to the report
47349+ if (storedScore) {
47350+ scoreData.prevScore = storedScore.score
47351+ scoreData.prevDate = storedScore.date
47352+ scoreData.prevCommit = storedScore.commit
4732347353
47324- scores.push(...chunkScores)
47325- }
47354+ if (storedScore.score !== score) {
47355+ scoreData.currentDiff = parseFloat((score - storedScore.score).toFixed(1))
47356+ }
47357+ }
47358+
47359+ return scoreData
47360+ })
4732647361
4732747362 core.debug('All the scores are already collected')
4732847363
@@ -49452,9 +49487,10 @@ async function run () {
4945249487 // Context
4945349488 const context = github.context
4945449489 // Inputs
49455- const scopePath = core.getInput('scope', { required: true } )
49490+ const scopePath = core.getInput('scope')
4945649491 const databasePath = core.getInput('database', { required: true })
4945749492 const reportPath = core.getInput('report', { required: true })
49493+ const localResultsPath = core.getInput('local-results-path')
4945849494 // Options
4945949495 const maxRequestInParallel = parseInt(core.getInput('max-request-in-parallel') || 10)
4946049496 const generateIssue = normalizeBoolean(core.getInput('generate-issue'))
@@ -49494,23 +49530,28 @@ async function run () {
4949449530 let scope = { 'github.com': {} }
4949549531 let originalReportContent = ''
4949649532
49497- // check if scope exists
49498- core.info('Checking if scope file exists...')
49499- const existScopeFile = existsSync(scopePath)
49500- if (!existScopeFile && !discoveryEnabled) {
49501- throw new Error('Scope file does not exist and discovery is not enabled')
49502- }
49533+ // In local results mode, scope is discovered from the results file
49534+ if (!localResultsPath) {
49535+ // check if scope exists
49536+ core.info('Checking if scope file exists...')
49537+ const existScopeFile = existsSync(scopePath)
49538+ if (!existScopeFile && !discoveryEnabled) {
49539+ throw new Error('Scope file does not exist and discovery is not enabled')
49540+ }
4950349541
49504- // Use scope file if it exists
49505- if (existScopeFile) {
49506- core.debug('Scope file exists, using it...')
49507- scope = await readFile(scopePath, 'utf8').then(content => JSON.parse(content))
49508- validateScopeIntegrity(scope)
49509- }
49542+ // Use scope file if it exists
49543+ if (existScopeFile) {
49544+ core.debug('Scope file exists, using it...')
49545+ scope = await readFile(scopePath, 'utf8').then(content => JSON.parse(content))
49546+ validateScopeIntegrity(scope)
49547+ }
4951049548
49511- if (discoveryEnabled) {
49512- core.info(`Starting discovery for the organizations ${discoveryOrgs}...`)
49513- scope = await generateScope({ octokit, orgs: discoveryOrgs, scope, maxRequestInParallel })
49549+ if (discoveryEnabled) {
49550+ core.info(`Starting discovery for the organizations ${discoveryOrgs}...`)
49551+ scope = await generateScope({ octokit, orgs: discoveryOrgs, scope, maxRequestInParallel })
49552+ }
49553+ } else {
49554+ core.info(`Using local results from: ${localResultsPath}`)
4951449555 }
4951549556
4951649557 // Check if database exists and load it
@@ -49529,7 +49570,7 @@ async function run () {
4952949570
4953049571 // PROCESS
4953149572 core.info('Generating scores...')
49532- const { reportContent, issueContent, database: newDatabaseState } = await generateScores({ scope, database, maxRequestInParallel, reportTagsEnabled, renderBadge, reportTool })
49573+ const { reportContent, issueContent, database: newDatabaseState } = await generateScores({ scope, database, maxRequestInParallel, reportTagsEnabled, renderBadge, reportTool, localResultsPath })
4953349574
4953449575 core.info('Checking database changes...')
4953549576 const hasChanges = isDifferent(database, newDatabaseState)
0 commit comments