145 lines
4.1 KiB
JavaScript
145 lines
4.1 KiB
JavaScript
import * as core from '@actions/core'
|
|
import * as exec from '@actions/exec'
|
|
import * as github from '@actions/github'
|
|
import { DefaultArtifactClient } from '@actions/artifact'
|
|
import * as fs from 'node:fs/promises'
|
|
|
|
/**
|
|
* The main function for the action.
|
|
*
|
|
* @returns {Promise<void>} Resolves when the action is complete.
|
|
*/
|
|
export async function run() {
|
|
try {
|
|
const comment = core.getBooleanInput('comment-on-pr', { required: false })
|
|
const upload = core.getBooleanInput('generate-artifact', {
|
|
required: false
|
|
})
|
|
const artifactName = core.getInput('artifact-name', { required: false })
|
|
const compare = core.getBooleanInput('do-comparison', { required: false })
|
|
const jobName = core.getInput('job-name', { required: false })
|
|
const baseBranch = core.getInput('base-branch', { required: false })
|
|
const system = core.getInput('system', { required: true })
|
|
|
|
if (!comment && !upload) {
|
|
core.error(
|
|
'Both comment-on-pr and generate-artifact were set to false, nothing to do, consider disabling the action instead'
|
|
)
|
|
core.setFailed('Neither commenting nor uploading a report ... why?')
|
|
return
|
|
}
|
|
|
|
const flakeInfo = JSON.parse(
|
|
await collectOutput('nix', ['flake', 'show', '--json'])
|
|
)
|
|
core.debug(`nix flake show --json: ${flakeInfo}`)
|
|
|
|
const report = await core.group('Generating size report', () =>
|
|
generateReport(flakeInfo, system)
|
|
)
|
|
|
|
if (upload) {
|
|
const artifact = new DefaultArtifactClient()
|
|
await fs.writeFile(artifactName, JSON.stringify(report))
|
|
const { id, size } = artifact.uploadArtifact(artifactName, [artifactName])
|
|
|
|
core.info(`Uploaded report ${artifactName} (${size}B) with id ${id}`)
|
|
}
|
|
|
|
// Done
|
|
if (!comment) {
|
|
return
|
|
}
|
|
|
|
// TODO: compare reports and create comment
|
|
} catch (error) {
|
|
// Fail the workflow run if an error occurs
|
|
if (error instanceof Error) core.setFailed(error.message)
|
|
}
|
|
}
|
|
|
|
async function generateReport(flakeInfo, system) {
|
|
const packages = getPackages(flakeInfo, system)
|
|
const hmConfigs = getKeys(flakeInfo, 'homeConfigurations')
|
|
const nixosConfigs = getKeys(flakeInfo, 'nixosConfigurations')
|
|
|
|
core.info(`packages: ${packages}`)
|
|
core.info(`homeConfigurations: ${hmConfigs}`)
|
|
core.info(`nixosConfigurations: ${nixosConfigs}`)
|
|
|
|
const pkgSizes = await core.group('Calculating size of packages', () =>
|
|
calculateSizeOf(packages, (pkg) => `.#${pkg}`)
|
|
)
|
|
|
|
const hmConfigsSizes = await core.group(
|
|
'Calculating size of Home-Manager Configurations',
|
|
() =>
|
|
calculateSizeOf(
|
|
hmConfigs,
|
|
(config) => `.#homeConfigurations.${config}.activationPackages`
|
|
)
|
|
)
|
|
|
|
const nixosConfigsSizes = await core.group(
|
|
'Calculating size of NixOS Configurations',
|
|
() =>
|
|
calculateSizeOf(
|
|
nixosConfigs,
|
|
(config) =>
|
|
`.#nixosConfigurations.${config}.config.system.build.toplevel`
|
|
)
|
|
)
|
|
|
|
return {
|
|
packages: pkgSizes,
|
|
nixosConfigurations: nixosConfigsSizes,
|
|
homeConfigurations: hmConfigsSizes
|
|
}
|
|
}
|
|
|
|
async function calculateSizeOf(names, nameToInstallable) {
|
|
let sizes = []
|
|
for (const name of names) {
|
|
sizes.push(await calculateSize(nameToInstallable(name)))
|
|
}
|
|
return sizes
|
|
}
|
|
|
|
/**
|
|
* Get the packages from a `nix flake show --json` blob
|
|
*
|
|
* @returns {Array<String>} the packages in the current flake for the specific system
|
|
*/
|
|
function getPackages(flakeInfo, system) {
|
|
return 'packages' in flakeInfo ? getKeys(flakeInfo.packages, system) : []
|
|
}
|
|
|
|
function getKeys(flakeInfo, key) {
|
|
return key in flakeInfo ? Object.keys(flakeInfo[key]) : []
|
|
}
|
|
|
|
async function calculateSize(installable) {
|
|
const path = await collectOutput('nix', [
|
|
'build',
|
|
'--print-out-paths',
|
|
installable
|
|
])
|
|
const data = JSON.parse(
|
|
await collectOutput('nix', ['path-info', '--closure-size', '--json', path])
|
|
)
|
|
data.path = path
|
|
|
|
return data
|
|
}
|
|
|
|
async function collectOutput(cmd, args) {
|
|
let output = ''
|
|
await exec.exec(cmd, args, {
|
|
listeners: {
|
|
stdout: (data) => {
|
|
output += data.toString()
|
|
}
|
|
}
|
|
})
|
|
return output
|
|
}
|