Skip to content
Open
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
51 changes: 51 additions & 0 deletions .github/actions/bundle-analysis/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: "Bundle Size Analysis"
description: "Analyses esbuild bundle sizes, compares with baseline, and posts a sticky PR comment with the results."

inputs:
baseline-metafiles-dir:
description: "Path to directory containing baseline metafiles organised as <lambda-name>/meta.json"
required: true
current-metafiles-dir:
description: "Path to directory containing current metafiles organised as <lambda-name>/meta.json"
required: true
threshold:
description: "Variance threshold percentage — changes within ±threshold are marked as unchanged"
required: false
default: "5"
pr-number:
description: "The pull request number to post the comment on"
required: true
github-token:
description: "GitHub token with pull-requests: write scope"
required: true

runs:
using: "composite"
steps:
- name: "Run bundle analysis"
shell: bash
env:
BASELINE_DIR: ${{ inputs.baseline-metafiles-dir }}
CURRENT_DIR: ${{ inputs.current-metafiles-dir }}
OUTPUT_DIR: /tmp/bundle-analysis-output
THRESHOLD: ${{ inputs.threshold }}
TOOL_DIR: ${{ github.action_path }}/../../../tools/bundle-analysis
run: ${{ github.action_path }}/bundle-analysis.sh

- name: "Write detailed report to job summary"
shell: bash
run: cat /tmp/bundle-analysis-output/detailed-report.md >> "$GITHUB_STEP_SUMMARY"

- name: "Substitute report link in summary"
shell: bash
run: |
report_link="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
sed -i "s|{{REPORT_LINK}}|${report_link}|g" /tmp/bundle-analysis-output/summary.md

- name: "Post or update PR comment"
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
with:
header: bundle-analysis
number: ${{ inputs.pr-number }}
GITHUB_TOKEN: ${{ inputs.github-token }}
path: /tmp/bundle-analysis-output/summary.md
27 changes: 27 additions & 0 deletions .github/actions/bundle-analysis/bundle-analysis.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

set -euo pipefail

function main() {
if [[ -z "${TOOL_DIR:-}" ]]; then
echo "ERROR: TOOL_DIR is not set" >&2
exit 1
fi

if [[ ! -d "${TOOL_DIR}" ]]; then
echo "ERROR: Tool directory does not exist: ${TOOL_DIR}" >&2
exit 1
fi

echo "Installing bundle-analysis dependencies..."
(cd "${TOOL_DIR}" && npm ci)

echo "Running bundle analysis..."
(cd "${TOOL_DIR}" && npm run analyse -- \
--baseline-dir="${BASELINE_DIR}" \
--current-dir="${CURRENT_DIR}" \
--output-dir="${OUTPUT_DIR}" \
--threshold="${THRESHOLD}")
}

main
65 changes: 65 additions & 0 deletions tools/bundle-analysis/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import js from "@eslint/js";
import jest from "eslint-plugin-jest";
import security from "eslint-plugin-security";
import sonarjs from "eslint-plugin-sonarjs";
import unicorn from "eslint-plugin-unicorn";
import globals from "globals";
import tseslint from "typescript-eslint";

export default tseslint.config(
{
ignores: [
"**/coverage/**",
"**/.build/**",
"**/node_modules/**",
"**/dist/**",
],
},
{
files: ["**/*.{js,mjs,cjs,ts}"],
extends: [js.configs.recommended, ...tseslint.configs.recommended],
languageOptions: {
globals: globals.node,
},
},
{
files: ["**/*.ts"],
languageOptions: {
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: import.meta.dirname,
},
},
},
{
files: ["**/*.{js,mjs,cjs,ts}"],
extends: [security.configs.recommended],
},
{
files: ["**/*.{js,mjs,cjs,ts}"],
plugins: { sonarjs },
rules: sonarjs.configs.recommended.rules,
},
{
files: ["**/*.{js,mjs,cjs,ts}"],
extends: [unicorn.configs.recommended],
rules: {
"unicorn/prevent-abbreviations": "off",
"unicorn/no-null": "off",
"unicorn/no-useless-undefined": "off",
"unicorn/prefer-module": "off",
},
},
{
files: ["**/__tests__/**", "**/*.test.*", "**/*.spec.*"],
extends: [jest.configs["flat/recommended"]],
rules: {
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-explicit-any": "off",
},
},
);
29 changes: 29 additions & 0 deletions tools/bundle-analysis/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Config } from "jest";

const jestConfig: Config = {
preset: "ts-jest",
clearMocks: true,
silent: true,
collectCoverage: true,
collectCoverageFrom: ["src/**/*.ts", "!src/__tests__/**", "!src/types.ts"],
coverageDirectory: "./.reports/unit/coverage",
coverageProvider: "v8",
coveragePathIgnorePatterns: ["/__tests__/", "/node_modules/"],
transform: { "^.+\\.ts$": "ts-jest" },
testPathIgnorePatterns: [".build"],
testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
testEnvironment: "node",
moduleNameMapper: {
"^src/(.*)$": "<rootDir>/src/$1",
},
coverageThreshold: {
global: {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
};

export default jestConfig;
Loading