Compare commits
2 commits
291da10d66
...
8925f3da6d
Author | SHA1 | Date | |
---|---|---|---|
8925f3da6d | |||
1b14f63169 |
21 changed files with 215062 additions and 110 deletions
|
@ -2,7 +2,7 @@
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
[*.{nix,json,sh}]
|
[*.{nix,json,sh,js}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
||||||
check:
|
check:
|
||||||
- treefmt
|
- treefmt
|
||||||
steps:
|
steps:
|
||||||
- uses: "https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" # v4
|
- uses: 'https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # v4
|
||||||
- name: Run checks
|
- name: Run checks
|
||||||
run: |
|
run: |
|
||||||
nix --version
|
nix --version
|
||||||
|
@ -18,11 +18,12 @@ jobs:
|
||||||
runs-on: nixos
|
runs-on: nixos
|
||||||
needs: check
|
needs: check
|
||||||
steps:
|
steps:
|
||||||
- uses: "https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" # v4
|
- uses: 'https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # v4
|
||||||
- run: nix --version
|
- run: nix --version
|
||||||
- name: Create Size Report
|
- name: Create Size Report
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
|
system: x86_64-linux
|
||||||
# Create a comment on the associated PR
|
# Create a comment on the associated PR
|
||||||
comment-on-pr: ${{ github.ref_name != 'main' }}
|
comment-on-pr: ${{ github.ref_name != 'main' }}
|
||||||
# Generate artifacts on main (to speed up comparisons)
|
# Generate artifacts on main (to speed up comparisons)
|
||||||
|
|
|
@ -7,7 +7,7 @@ jobs:
|
||||||
check-renovaterc:
|
check-renovaterc:
|
||||||
runs-on: nixos
|
runs-on: nixos
|
||||||
steps:
|
steps:
|
||||||
- uses: "https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" # v4
|
- uses: 'https://git.salame.cl/actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # v4
|
||||||
- name: Validate renovaterc.json
|
- name: Validate renovaterc.json
|
||||||
run: |
|
run: |
|
||||||
nix --version
|
nix --version
|
||||||
|
|
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
dist/** -diff linguist-generated=true
|
103
.gitignore
vendored
103
.gitignore
vendored
|
@ -1 +1,104 @@
|
||||||
result*
|
result*
|
||||||
|
|
||||||
|
# Dependency directory
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# OS metadata
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore built ts files
|
||||||
|
__tests__/runner/*
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea
|
||||||
|
*.code-workspace
|
||||||
|
|
5
.prettierignore
Normal file
5
.prettierignore
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.DS_Store
|
||||||
|
.licenses/
|
||||||
|
dist/
|
||||||
|
node_modules/
|
||||||
|
coverage/
|
15
.prettierrc.yml
Normal file
15
.prettierrc.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# See: https://prettier.io/docs/en/configuration
|
||||||
|
printWidth: 80
|
||||||
|
tabWidth: 2
|
||||||
|
useTabs: false
|
||||||
|
semi: false
|
||||||
|
singleQuote: true
|
||||||
|
quoteProps: as-needed
|
||||||
|
jsxSingleQuote: false
|
||||||
|
trailingComma: none
|
||||||
|
bracketSpacing: true
|
||||||
|
bracketSameLine: true
|
||||||
|
arrowParens: always
|
||||||
|
proseWrap: always
|
||||||
|
htmlWhitespaceSensitivity: css
|
||||||
|
endOfLine: lf
|
22
README.md
22
README.md
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
Use `nix path-info` to query the size of flake outputs and produce a report.
|
Use `nix path-info` to query the size of flake outputs and produce a report.
|
||||||
|
|
||||||
This repost can be posted to a PR (as formatted markdown) and/or uploaded as a workflow artifact.
|
This repost can be posted to a PR (as formatted markdown) and/or uploaded as a
|
||||||
|
workflow artifact.
|
||||||
|
|
||||||
Requires `nix`, `jq`, `curl`, `sed`, `gunzip`, `tar` and `coreutils` to be in the runner's path.
|
Requires `nix`, `jq`, `curl`, `sed`, `gunzip`, `tar` and `coreutils` to be in
|
||||||
|
the runner's path.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
@ -39,23 +41,29 @@ For more details see the [action.yaml](./action.yml) file.
|
||||||
**Definitions:**
|
**Definitions:**
|
||||||
|
|
||||||
- `Name`: the name of the package/configuration.
|
- `Name`: the name of the package/configuration.
|
||||||
- `Size`: the closure size (size on disk/NAR size + all transitive dependencies).
|
- `Size`: the closure size (size on disk/NAR size + all transitive
|
||||||
|
dependencies).
|
||||||
- `NAR Size`: the size of the build output (package without the dependencies).
|
- `NAR Size`: the size of the build output (package without the dependencies).
|
||||||
- `[NAR] Size Change`: the amount changed compared to the main branch.
|
- `[NAR] Size Change`: the amount changed compared to the main branch.
|
||||||
|
|
||||||
**Tips on reading this data:**
|
**Tips on reading this data:**
|
||||||
|
|
||||||
- For NixOS configurations you generally care only about the `Size` (closure size/size on disk).
|
- For NixOS configurations you generally care only about the `Size` (closure
|
||||||
|
size/size on disk).
|
||||||
- Reduce the `Size` by disabling unneeded services/default packages.
|
- Reduce the `Size` by disabling unneeded services/default packages.
|
||||||
- For Packages you care about both the `Size` and the `NAR Size`.
|
- For Packages you care about both the `Size` and the `NAR Size`.
|
||||||
- Reduce the `NAR Size` by reducing the size of the build outputs, e.g. don't copy unnecessary data to the $out dir, optimize binaries for size, etc.
|
- Reduce the `NAR Size` by reducing the size of the build outputs, e.g. don't
|
||||||
|
copy unnecessary data to the $out dir, optimize binaries for size, etc.
|
||||||
- Reduce the `Size` by reducing the dependencies (e.g. `buildInputs`).
|
- Reduce the `Size` by reducing the dependencies (e.g. `buildInputs`).
|
||||||
- Don't worry too much about size, some dependencies are deduplicated, e.g. `glibc` adds ~40MiB to the `Size`, but is generally shared by ~every binary on the system, so, chances are, you are already including it from somewhere else and statically linking with e.g. `musl` is not gonna improve things.
|
- Don't worry too much about size, some dependencies are deduplicated, e.g.
|
||||||
|
`glibc` adds ~40MiB to the `Size`, but is generally shared by ~every binary
|
||||||
|
on the system, so, chances are, you are already including it from somewhere
|
||||||
|
else and statically linking with e.g. `musl` is not gonna improve things.
|
||||||
|
|
||||||
# NixOS Configurations
|
# NixOS Configurations
|
||||||
|
|
||||||
| Name | Size | Size Change | NAR Size | NAR Size Change |
|
| Name | Size | Size Change | NAR Size | NAR Size Change |
|
||||||
|------|-----:|------------:|---------:|----------------:|
|
| -------- | ----: | ----------: | -------: | --------------: |
|
||||||
| `gemini` | 11Gi | -2.4Mi | 28Ki | 0 |
|
| `gemini` | 11Gi | -2.4Mi | 28Ki | 0 |
|
||||||
| `leo` | 1.6Gi | 0 | 25Ki | 0 |
|
| `leo` | 1.6Gi | 0 | 25Ki | 0 |
|
||||||
| `libra` | 9.4Gi | -2.4Mi | 28Ki | 0 |
|
| `libra` | 9.4Gi | -2.4Mi | 28Ki | 0 |
|
||||||
|
|
96
action.yml
96
action.yml
|
@ -32,6 +32,9 @@ inputs:
|
||||||
|
|
||||||
This is a no-op in case no PR is associated with the current branch.
|
This is a no-op in case no PR is associated with the current branch.
|
||||||
default: 'true'
|
default: 'true'
|
||||||
|
system:
|
||||||
|
description: |
|
||||||
|
The nix system name to query the packages of (e.g. x86_64-linux)
|
||||||
# Generate workflow artifact
|
# Generate workflow artifact
|
||||||
generate-artifact:
|
generate-artifact:
|
||||||
description: Export the generated markdown document as a workflow artifact.
|
description: Export the generated markdown document as a workflow artifact.
|
||||||
|
@ -65,94 +68,5 @@ inputs:
|
||||||
default: ${{ github.base_ref }}
|
default: ${{ github.base_ref }}
|
||||||
outputs:
|
outputs:
|
||||||
runs:
|
runs:
|
||||||
using: 'composite'
|
using: node20
|
||||||
steps:
|
main: dist/index.js
|
||||||
- name: Find PR (if it exists)
|
|
||||||
id: pr-number
|
|
||||||
if: inputs.comment-on-pr == 'true'
|
|
||||||
run: |
|
|
||||||
. "$GITHUB_ACTION_PATH/scripts/utils.sh"
|
|
||||||
|
|
||||||
log 'Determine head_ref'
|
|
||||||
# For push & tag events it'll bet GITHUB_REF_NAME, for pull_request events it'll be GITHUB_HEAD_REF
|
|
||||||
head_ref=${GITHUB_REF_NAME:-$GITHUB_HEAD_REF}
|
|
||||||
|
|
||||||
log "Get PR number for $head_ref"
|
|
||||||
prs=$(curl -X 'GET' \
|
|
||||||
"$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls?state=open&sort=recentupdate" \
|
|
||||||
-H "Authorization: token $GITHUB_TOKEN" \
|
|
||||||
-H 'Accept: application/json')
|
|
||||||
|
|
||||||
pr_number=$(echo "$prs" |
|
|
||||||
jq --arg head_ref "$head_ref" '.[] | select(.head.ref == $head_ref) | .number')
|
|
||||||
|
|
||||||
# This seems to create the file???
|
|
||||||
log "GITHUB_OUTPUT=$GITHUB_OUTPUT"
|
|
||||||
log "$(ls -l "$GITHUB_OUTPUT")"
|
|
||||||
|
|
||||||
# Protect against running before a PR is made or if it is triggered on the main branch
|
|
||||||
if [ -z "$pr_number" ]; then
|
|
||||||
warn "No PR created for this commit"
|
|
||||||
echo "pr-number=" >> "$GIHUB_OUTPUT"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
log "Retrieved index: $pr_number"
|
|
||||||
log "Expected PR URL: $GITHUB_SERVER_URL/$GITHUB_REPOSITORY/pulls/$pr_number"
|
|
||||||
|
|
||||||
echo "pr-number=$pr_number" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: Find previous comment (if present)
|
|
||||||
# We want to generate a comment, and we we able to fin the PR number
|
|
||||||
if: inputs.comment-on-pr == 'true' && steps.pr-number.outputs.pr-number != ''
|
|
||||||
id: find-comment
|
|
||||||
uses: https://github.com/peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
|
|
||||||
with:
|
|
||||||
issue-number: ${{ steps.pr-number.outputs.pr-number }}
|
|
||||||
direction: first
|
|
||||||
body-includes: "<!-- AUTOGENERATED by nix-flake-outputs-size action -->"
|
|
||||||
- name: Create report
|
|
||||||
if: inputs.comment-on-pr == 'true' || inputs.generate-artifact == 'true'
|
|
||||||
env:
|
|
||||||
PR_ID: ${{ steps.pr-number.outputs.pr-number }}
|
|
||||||
COMMENT: ${{ inputs.comment-on-pr }}
|
|
||||||
COMMENT_ID: ${{ steps.find-comment.outputs.comment-id }}
|
|
||||||
ARTIFACT_NAME: ${{ inputs.artifact-name }}
|
|
||||||
DO_COMPARISON: ${{ inputs.do-comparison }}
|
|
||||||
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
||||||
JOB_NAME: ${{ inputs.job-name }}
|
|
||||||
run: |
|
|
||||||
. "$GITHUB_ACTION_PATH/scripts/utils.sh"
|
|
||||||
|
|
||||||
# Input validation
|
|
||||||
if [ "$DO_COMPARISON" = 'true' ] && [ -z "$JOB_NAME" ]; then
|
|
||||||
error 'job-name should be set if you want to generate a comparison report'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create Size Report
|
|
||||||
"$GITHUB_ACTION_PATH/scripts/create-report.sh" report.json
|
|
||||||
|
|
||||||
# Nothing else to do
|
|
||||||
if [ "$COMMENT" != 'true' ]; then exit 0; fi
|
|
||||||
|
|
||||||
# Try to do a comparison report
|
|
||||||
if [ "$DO_COMPARISON" = 'true' ]; then
|
|
||||||
if "$GITHUB_ACTION_PATH/scripts/retrieve-old-report.sh" && [ -f old-report.json ]; then
|
|
||||||
log "Reporting on sizes and comparing to sizes in $HEAD_BRANCH"
|
|
||||||
|
|
||||||
"$GITHUB_ACTION_PATH/scripts/comment_on_pr.sh" report.json old-report.json
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
error 'Failed to do comparison, fallback to posting the report without them'
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Just report values
|
|
||||||
log 'Reporting on sizes'
|
|
||||||
"$GITHUB_ACTION_PATH/scripts/comment_on_pr.sh" report.json
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: https://git.salame.cl/actions/upload-artifact@v4
|
|
||||||
if: inputs.generate-artifact == 'true'
|
|
||||||
with:
|
|
||||||
path: report.json
|
|
||||||
name: ${{ inputs.artifact-name }}
|
|
||||||
|
|
202419
dist/index.js
generated
vendored
Normal file
202419
dist/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/index.js.map
generated
vendored
Normal file
1
dist/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
61
eslint.config.mjs
Normal file
61
eslint.config.mjs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// See: https://eslint.org/docs/latest/use/configure/configuration-files
|
||||||
|
|
||||||
|
import { fixupPluginRules } from '@eslint/compat'
|
||||||
|
import { FlatCompat } from '@eslint/eslintrc'
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import _import from 'eslint-plugin-import'
|
||||||
|
import jest from 'eslint-plugin-jest'
|
||||||
|
import prettier from 'eslint-plugin-prettier'
|
||||||
|
import globals from 'globals'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = path.dirname(__filename)
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all
|
||||||
|
})
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: ['**/coverage', '**/dist', '**/linter', '**/node_modules']
|
||||||
|
},
|
||||||
|
...compat.extends(
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:jest/recommended',
|
||||||
|
'plugin:prettier/recommended'
|
||||||
|
),
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
import: fixupPluginRules(_import),
|
||||||
|
jest,
|
||||||
|
prettier
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest,
|
||||||
|
Atomics: 'readonly',
|
||||||
|
SharedArrayBuffer: 'readonly'
|
||||||
|
},
|
||||||
|
|
||||||
|
ecmaVersion: 2023,
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
camelcase: 'off',
|
||||||
|
'eslint-comments/no-use': 'off',
|
||||||
|
'eslint-comments/no-unused-disable': 'off',
|
||||||
|
'i18n-text/no-en': 'off',
|
||||||
|
'import/no-namespace': 'off',
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-shadow': 'off',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'prettier/prettier': 'error'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -26,10 +26,12 @@
|
||||||
# Setup formatters
|
# Setup formatters
|
||||||
treefmt = {
|
treefmt = {
|
||||||
# Ignore images
|
# Ignore images
|
||||||
settings.global.excludes = [ "*.png" ];
|
settings.global.excludes = [
|
||||||
|
"*.png"
|
||||||
|
"dist/*"
|
||||||
|
];
|
||||||
projectRootFile = "flake.nix";
|
projectRootFile = "flake.nix";
|
||||||
programs = {
|
programs = {
|
||||||
mdformat.enable = true;
|
|
||||||
nixfmt.enable = true;
|
nixfmt.enable = true;
|
||||||
shfmt.enable = true;
|
shfmt.enable = true;
|
||||||
shellcheck.enable = true;
|
shellcheck.enable = true;
|
||||||
|
|
30
jest.config.js
Normal file
30
jest.config.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// See: https://jestjs.io/docs/configuration
|
||||||
|
|
||||||
|
/** @type {import('jest').Config} */
|
||||||
|
const jestConfig = {
|
||||||
|
clearMocks: true,
|
||||||
|
collectCoverage: true,
|
||||||
|
collectCoverageFrom: ['./src/**'],
|
||||||
|
coverageDirectory: './coverage',
|
||||||
|
coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
|
||||||
|
coverageReporters: ['json-summary', 'text', 'lcov'],
|
||||||
|
// Uncomment the below lines if you would like to enforce a coverage threshold
|
||||||
|
// for your action. This will fail the build if the coverage is below the
|
||||||
|
// specified thresholds.
|
||||||
|
// coverageThreshold: {
|
||||||
|
// global: {
|
||||||
|
// branches: 100,
|
||||||
|
// functions: 100,
|
||||||
|
// lines: 100,
|
||||||
|
// statements: 100
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
moduleFileExtensions: ['js'],
|
||||||
|
reporters: ['default'],
|
||||||
|
testEnvironment: 'node',
|
||||||
|
testMatch: ['**/*.test.js'],
|
||||||
|
testPathIgnorePatterns: ['/dist/', '/node_modules/'],
|
||||||
|
verbose: true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default jestConfig
|
12141
package-lock.json
generated
Normal file
12141
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
66
package.json
Normal file
66
package.json
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"name": "nix-flake-outputs-size-report",
|
||||||
|
"description": "Use 'nix-path-info' to query the size of outputs and produce a markdown report",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": "Jalil David Salamé Messina",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"homepage": "https://git.salame.cl/jalil/nix-flake-outputs-size#readme",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://git.salame.cl/jalil/nix-flake-outputs-size.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://git.salame.cl/jalil/nix-flake-outputs-size/issues"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"actions"
|
||||||
|
],
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"bundle": "npm run format:write && npm run package",
|
||||||
|
"ci-test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
|
||||||
|
"coverage": "npx make-coverage-badge --output-path ./badges/coverage.svg",
|
||||||
|
"format:write": "npx prettier --write .",
|
||||||
|
"format:check": "npx prettier --check .",
|
||||||
|
"lint": "npx eslint .",
|
||||||
|
"local-action": "npx @github/local-action . src/main.js .env",
|
||||||
|
"package": "npx rollup --config rollup.config.js",
|
||||||
|
"package:watch": "npm run package -- --watch",
|
||||||
|
"test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 npx jest",
|
||||||
|
"all": "npm run format:write && npm run lint && npm run test && npm run coverage && npm run package"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/artifact": "^2.3.2",
|
||||||
|
"@actions/core": "^1.11.1",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/github": "^6.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/compat": "^1.3.1",
|
||||||
|
"@github/local-action": "^3.2.1",
|
||||||
|
"@jest/globals": "^30.0.4",
|
||||||
|
"@rollup/plugin-commonjs": "^28.0.6",
|
||||||
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
|
"eslint": "^9.30.1",
|
||||||
|
"eslint-config-prettier": "^10.1.5",
|
||||||
|
"eslint-plugin-import": "^2.32.0",
|
||||||
|
"eslint-plugin-jest": "^29.0.1",
|
||||||
|
"eslint-plugin-prettier": "^5.5.1",
|
||||||
|
"jest": "^30.0.4",
|
||||||
|
"make-coverage-badge": "^1.2.0",
|
||||||
|
"prettier": "^3.6.2",
|
||||||
|
"prettier-eslint": "^16.4.2",
|
||||||
|
"rollup": "^4.44.2"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "*"
|
||||||
|
}
|
||||||
|
}
|
11
report.json
Normal file
11
report.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "hello",
|
||||||
|
"size": 33159640,
|
||||||
|
"narSize": 234680
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nixosConfigurations": [],
|
||||||
|
"homeConfigurations": []
|
||||||
|
}
|
18
rollup.config.js
Normal file
18
rollup.config.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// See: https://rollupjs.org/introduction/
|
||||||
|
|
||||||
|
import commonjs from '@rollup/plugin-commonjs'
|
||||||
|
import json from '@rollup/plugin-json'
|
||||||
|
import { nodeResolve } from '@rollup/plugin-node-resolve'
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
input: 'src/index.js',
|
||||||
|
output: {
|
||||||
|
esModule: true,
|
||||||
|
file: 'dist/index.js',
|
||||||
|
format: 'es',
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
plugins: [commonjs(), json(), nodeResolve({ preferBuiltins: true })]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
|
@ -74,6 +74,7 @@ curl -X 'GET' \
|
||||||
tar -zvx --strip-components=1 -C "$old"
|
tar -zvx --strip-components=1 -C "$old"
|
||||||
endgroup
|
endgroup
|
||||||
|
|
||||||
(cd "$old" && "$GITHUB_ACTION_PATH/scripts/create-report.sh" old-report.json)
|
repo_dir=$PWD
|
||||||
|
(cd "$old" && "$GITHUB_ACTION_PATH/scripts/create-report.sh" "$repo_dir"/old-report.json)
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
8
src/index.js
Normal file
8
src/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/**
|
||||||
|
* The entrypoint for the action. This file simply imports and runs the action's
|
||||||
|
* main logic.
|
||||||
|
*/
|
||||||
|
import { run } from './main.js'
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
run()
|
145
src/main.js
Normal file
145
src/main.js
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
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.trim()
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue