Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alpha 2 actions - completed actions plus some others #89

Merged
merged 10 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cli/actions/actions/basic-reporting/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: 'Basic Reporting'
description: 'Generates basic reports for companies plus wrapping README.md files in the repository'
inputs:
github-token:
description: 'GitHub token'
required: true
runs:
using: 'node20'
main: 'index.js'
200 changes: 200 additions & 0 deletions cli/actions/actions/basic-reporting/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
const github = require('@actions/github')
const core = require('@actions/core')

async function readObjects (fileName) {
const token = core.getInput('github-token', { required: true })
const octokit = github.getOctokit(token)

const { data: file } = await octokit.rest.repos.getContent({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
path: fileName,
ref: 'main'
})

// check to see if the file is empty and if it is return an empty object
if (file.size === 0) {
return []
}

// Return the file content as an object
return JSON.parse(Buffer.from(file.content, 'base64').toString())
}

async function readWorkflows () {
const token = core.getInput('github-token', { required: true })
const octokit = github.getOctokit(token)

const workflows = await octokit.rest.actions.listWorkflowRunsForRepo({
owner: github.context.repo.owner,
repo: github.context.repo.repo
})

const workflowList = []
let totalRunTimeThisMonth = 0
for (const workflow of workflows.data.workflow_runs) {
// Get the current month
const currentMonth = new Date().getMonth()

// Compute the runtime and if the time is less than 60s round it to 1m
const runTime = Math.ceil((new Date(workflow.updated_at) - new Date(workflow.created_at)) / 1000 / 60) < 1 ? 1 : Math.ceil((new Date(workflow.updated_at) - new Date(workflow.created_at)) / 1000 / 60)

// If the month of the workflow is not the current month, then skip it
if (new Date(workflow.updated_at).getMonth() !== currentMonth) {
continue
}
totalRunTimeThisMonth += runTime

// Add the workflow to the workflowList
workflowList.push({
run_time_minutes: runTime,
path: workflow.path,
name: workflow.path.replace('.github/workflows/', '').replace('.yml', ''),
run_time_name: workflow.name,
conclusion: workflow.conclusion,
created_at: workflow.created_at,
updated_at: workflow.updated_at,
html_url: workflow.html_url
})
}

// Sort the worflowList to put the most recent workflows first
workflowList.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))

// log the length of the workflowList
console.log(`Number of workflows: ${workflowList.length}`)

return {allWorkFlows: workflowList, runTime: totalRunTimeThisMonth}
}

async function readBranches () {
const token = core.getInput('github-token', { required: true })
const octokit = github.getOctokit(token)

let { data: branches } = await octokit.rest.repos.listBranches({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
})

// Loop through the branches to obtain the latest commit comment, author, and date added to the branch, and then add them to the branches array
for (const branch of branches) {
const { data: commit } = await octokit.rest.repos.getBranch({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
branch: branch.name
})
branch.commit = commit.commit.sha
branch.author = commit.commit.author.name
branch.date = commit.commit.author.date
}

return branches
}

// Create a function that compares the current list of companies and the associated markdown files for each company present in the repository. Should there be a markdown file for a company that is not in the current list of companies, then the file should be deleted.
async function reconcileCompanyFiles (companies) {
const token = core.getInput('github-token', { required: true })
const octokit = github.getOctokit(token)

// Get the list of company files
const { data: companyFiles } = await octokit.rest.repos.getContent({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
path: 'Companies',
ref: 'main'
})
// Filter in only the markdown files that aren't the README.md
const companyFilesFiltered = companyFiles.filter(file => file.name.endsWith('.md') && file.name !== 'README.md')

// Using the supplied companies create a new object with the key being the company markdown and the value being the company name
let markdownToCompanies = companies.reduce((acc, company) => {
const markdown = company.name.replace(/[\s,.\?!]/g, '')
acc[`${markdown}.md`] = company.name
return acc
} , {})

// Check to see if if each companyFilesFiltered is in the markdownToCompanies object, if it isn't then delete the file
const deleteFiles = []
for (const file of companyFilesFiltered) {
if (!markdownToCompanies[file.name]) {
await octokit.rest.repos.deleteFile({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
path: `Companies/${file.name}`,
message: `Delete ${file.name} report`,
branch: 'main'
})
// Add the file to the deleteFiles array
deleteFiles.push(file.name)
}
}
// Return the deleteFiles array
return deleteFiles
}

async function saveReports (reports, inputs) {
// Reconile the company files
const prunedMarkdowns = await reconcileCompanyFiles(inputs.companies)

const token = core.getInput('github-token', { required: true })
const octokit = github.getOctokit(token)

// Create or update each file in a single commit
for (const report of reports) {
// Get the sha of the file if it exists
try {
const { data: file } = await octokit.rest.repos.getContent({
owner: github.context.repo.owner,
repo: github.context.repo.repo,
path: report.path,
ref: 'main'
})
// If the file exists then get the sha
if (file) {
report.sha = file.sha
}
} catch (error) {
// If the file doesn't exist then set the sha to null
report.sha = null
}

// Create or update the file
// NOTE: The file must be encoded as a base64 string
// NOTE: Unsure how to handle the case where the file exists vs doesn't exist

// If the file doesn't exist then create then report.sha will be null, so create the object to create the file without the sha
let octokitContent = {
owner: github.context.repo.owner,
repo: github.context.repo.repo,
// If the file exists then update it, otherwise create it
path: report.path,
message: `Update ${report.name} report`,
content: Buffer.from(report.content).toString('base64'),
sha: report.sha, // The blob SHA of the file being replaced
branch: 'main',
committer: {
name: github.context.actor,
email: `${github.context.actor}@users.noreply.github.com`
},
author: {
name: github.context.actor,
email: `${github.context.actor}@users.noreply.github.com`
}
}

// if report.sha is null delete the sha property from the object
if (!report.sha) {
delete octokitContent.sha
}

await octokit.rest.repos.createOrUpdateFileContents(octokitContent)
}
}


module.exports = {
readObjects,
readWorkflows,
readBranches,
saveReports
}
53 changes: 53 additions & 0 deletions cli/actions/actions/basic-reporting/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Load the modules
const { readObjects, readBranches, readWorkflows, saveReports } = require('./github.js')
const { createCompaniesReport, createCompanyReports, createMainReport } = require('./reports.js')


// Create the run function that creates the reports
async function run () {
// Define inputs
const inputs = {
companies: await readObjects('/Companies/Companies.json'),
interactions: await readObjects('/Interactions/Interactions.json'),
branches: await readBranches(),
workflows: await readWorkflows()
}

// If there are no companies then return
if (inputs.companies.length === 0) {
return
}

// Define reports
const reports = {
companies: `Companies/README.md`,
company: `Companies/`,
main: `README.md`,
}

// Create the company files
const companyFiles = await createCompanyReports(inputs.companies, inputs.interactions, reports)
// Create the companies file
const companiesFile = createCompaniesReport(inputs.companies)
// Create the main file
const mainFile = createMainReport(inputs)
// Create the reports array
const markdownReports = [
{
name: 'Companies',
path: reports.companies,
content: companiesFile
},
{
name: 'Main',
path: reports.main,
content: mainFile
},
...companyFiles
]

// Write the reports
await saveReports(markdownReports, inputs)
}

run()
Loading