PR Metrics is a cross-platform GitHub Action and Azure DevOps task that analyzes
pull request size and test coverage, updating PR titles with indicators (XS, S,
M, L, XL, 2XL, etc) and test coverage symbols (✔ or
The codebase uses a unique dual-platform architecture with dependency injection:
- Runners:
src/task/src/runners/- Abstracts GitHub Actions vs Azure Pipelines executionrunnerInvoker.tsroutes togitHubRunnerInvoker.tsorazurePipelinesRunnerInvoker.ts- Detection:
process.env.GITHUB_ACTIONdetermines platform
- Repos:
src/task/src/repos/- Abstracts GitHub vs Azure DevOps repository APIsreposInvoker.tsroutes togitHubReposInvoker.tsorazureReposInvoker.ts- Both extend
baseReposInvoker.ts, which provides shared API error handling (e.g., mapping 401/403/404 to access-error messages) tokenManager.tshandles workload identity federation as an alternative to Personal Access Tokens- Single codebase handles both GitHub and Azure DevOps repositories
- Entry Point:
src/task/index.ts- Simple DI container resolution and error handling - Main Logic:
src/task/src/pullRequestMetrics.ts- Orchestrates the workflow, callingcodeMetricsCalculator.updateDetails()andcodeMetricsCalculator.updateComments()in parallel viaPromise.all() - Metrics Engine:
src/task/src/metrics/codeMetricsCalculator.ts- Validates conditions, calculates PR metrics, and definesupdateDetails()andupdateComments() - Git Integration:
src/task/src/git/gitInvoker.ts- Executes Git commands for diff analysis (numstat-based metrics) - Diff Parsing:
src/task/src/git/octokitGitDiffParser.ts- Uses theparse-git-difflibrary to determine, for each file, the first diff hunk start line (a diff line suitable as a comment anchor) for precise comment placement on GitHub PRs
- Dependency Injection: Uses
tsyringewith@singleton()and@injectable()decorators - Interface Abstraction: All major components implement interfaces (e.g.,
ReposInvokerInterface,RunnerInvokerInterface) - Localization: Resource strings in the
src/task/Strings/resources.resjson/folder withrunnerInvoker.loc()calls
npm run build:debug # Builds with source maps for debugging
npm run build:release # Production build with minification
npm run build:package # Creates dist/ for GitHub Action
npm run lint # ESLint with autofix (strict TypeScript rules)
npm run test:fast # Quick test run during development- Production:
src/task/src/- Main source code - Tests:
src/task/tests/- Mirror source structure for test files - Interfaces: Separate
.d.tsfiles for all major interfaces - Build Outputs:
debug/,release/(git-ignored),dist/(committed for GitHub Action)
The core functionality relies on:
git diff --numstat --ignore-all-space origin/<target>...pull/<pull_request_id>/merge
- Strict ESLint: Flat config (
eslint.config.mjs) extendingstrictTypeCheckedandstylisticTypeCheckedpresets with extensive custom rules - Explicit Types:
@typescript-eslint/explicit-function-return-typeenforced - Member Ordering: Specific ordering enforced (fields → constructor → methods)
- ES Modules: Uses
.jsextensions in imports for Node.js ESM compatibility
- Framework: Mocha + ts-mockito + c8 for coverage + fast-check for property-based testing
- Pattern: Arrange-Act-Assert structure
- Coverage: Maintain high coverage rates (critical for edge cases)
- Test Files:
.spec.tsfiles mirror source structure;.property.spec.tsfiles contain property-based (fuzz) tests
- Early return pattern for validation failures
- Localized error messages via
runnerInvoker.loc() - Status setting:
setStatusFailed(),setStatusSkipped(),setStatusSucceeded()
PR_METRICS_ACCESS_TOKEN - The only token variable read by the source code.
Typically set to ${{ secrets.GITHUB_TOKEN }} in workflows or to an Azure
DevOps PAT. Can also be populated automatically by workload identity federation
via TokenManager.
- GitHub: Octokit for REST API calls
(
@octokit/plugin-rest-endpoint-methods) - Azure DevOps:
azure-devops-node-apifor TFS API calls - Git: Shell execution via runner abstraction
- GitHub Action:
action.ymldefines inputs/outputs - Azure Task:
src/task/task.jsondefines task metadata and inputs - Both configs must stay synchronized for input definitions
- Use
npm run test:fastfor rapid iteration during development - Debug builds include source maps; check
debug/folder - Manual testing instructions in
src/task/tests/manualTests/Instructions.md - Set
system.debug: truein pipelines for verbose logging - The localization system requires
locInitialize()before usingloc()calls