From dce6effa584027a4f7db88593171f2db00062497 Mon Sep 17 00:00:00 2001 From: haseebmalik18 Date: Tue, 30 Jun 2026 14:56:10 -0400 Subject: [PATCH] populate reusedCode with detected USWDS and CMS Design System dependencies --- src/__tests__/unit/helper.test.ts | 216 +++++++++++++++++++++++++++++- src/gov-dependencies.ts | 59 ++++++++ src/helper.ts | 86 ++++++++++++ src/main.ts | 7 + 4 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 src/gov-dependencies.ts diff --git a/src/__tests__/unit/helper.test.ts b/src/__tests__/unit/helper.test.ts index 368607f3..04af7bbe 100644 --- a/src/__tests__/unit/helper.test.ts +++ b/src/__tests__/unit/helper.test.ts @@ -1,8 +1,29 @@ import { describe, it, expect, jest, beforeEach } from "@jest/globals"; -import { createHelpers } from "../../helper.js"; +import { + createHelpers, + parsePackageJSON, + parseRequirementsTxt, + mergeReusedCode, +} from "../../helper.js"; +import { + GOV_DEPENDENCIES, + USWDS, + USWDS_COMPILE, + CMS_DESIGN_SYSTEM, + CMS_DS_HEALTHCARE_GOV, +} from "../../gov-dependencies.js"; import { createMockDeps, createMockOctokit } from "../fixtures/mock-deps.js"; import { Dependencies } from "../../types/Dependencies.js"; +// returns a readFile mock that serves content by filepath and rejects otherwise +function readFileFrom(files: Record) { + return jest.fn((filepath: string) => + filepath in files + ? Promise.resolve(files[filepath]) + : Promise.reject(new Error("ENOENT")), + ); +} + describe("createHelpers - calculateMetaData", () => { let deps: Dependencies; @@ -203,4 +224,197 @@ describe("createHelpers - validateOnly", () => { expect(deps.setFailed).not.toHaveBeenCalled(); expect(deps.log.info).toHaveBeenCalledWith("code.json is valid!"); }); +}); + +describe("parsePackageJSON", () => { + it("collects dependencies and devDependencies", () => { + const content = JSON.stringify({ + dependencies: { uswds: "^3.0.0", react: "^18.0.0" }, + devDependencies: { jest: "^29.0.0" }, + }); + expect(parsePackageJSON(content)).toEqual(["uswds", "react", "jest"]); + }); + + it("handles missing dependency sections", () => { + expect(parsePackageJSON(JSON.stringify({ name: "x" }))).toEqual([]); + }); + + it("returns empty array for invalid JSON", () => { + expect(parsePackageJSON("not json {{{")).toEqual([]); + }); +}); + +describe("parseRequirementsTxt", () => { + it("strips version specifiers, extras, markers and comments", () => { + const content = [ + "uswds==3.0.0", + "requests>=2.0 # http client", + "django[argon2]~=4.2", + 'pytz; python_version < "3.9"', + "# a comment line", + "", + "-r other-requirements.txt", + "--hash=sha256:abc", + ].join("\n"); + + expect(parseRequirementsTxt(content)).toEqual([ + "uswds", + "requests", + "django", + "pytz", + ]); + }); +}); + +describe("createHelpers - detectReusedCode", () => { + it("matches a known gov dependency from package.json", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { "@uswds/uswds": "^3.0.0", react: "^18.0.0" }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]); + }); + + it("matches a known gov dependency from requirements.txt", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/requirements.txt": "uswds==3.0.0\nrequests==2.0", + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]); + }); + + it("matches multiple distinct gov dependencies in one manifest", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { + "@uswds/uswds": "^3.0.0", + "@cmsgov/design-system": "^14.0.0", + react: "^18.0.0", + }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([ + USWDS, + CMS_DESIGN_SYSTEM, + ]); + }); + + it("lists @uswds/compile as a separate entry from @uswds/uswds", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { "@uswds/uswds": "^3.0.0" }, + devDependencies: { "@uswds/compile": "^1.0.0" }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([ + USWDS, + USWDS_COMPILE, + ]); + }); + + it("lists each CMS dependency individually", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { "@cmsgov/design-system": "^14.0.0" }, + devDependencies: { "@cmsgov/ds-healthcare-gov": "^18.0.0" }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([ + CMS_DESIGN_SYSTEM, + CMS_DS_HEALTHCARE_GOV, + ]); + }); + + it("dedupes the same dependency found across both manifests", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { "@uswds/uswds": "^3.0.0" }, + }), + "/github/workspace/requirements.txt": "uswds==3.0.0", + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([USWDS]); + }); + + it("returns empty when no manifests are present", async () => { + const deps = createMockDeps(); + expect(await createHelpers(deps).detectReusedCode()).toEqual([]); + }); + + it("returns empty when no known gov dependencies are found", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { react: "^18.0.0" }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([]); + }); + + it("ignores dependency names that collide with Object prototype members", async () => { + const deps = createMockDeps({ + readFile: readFileFrom({ + "/github/workspace/package.json": JSON.stringify({ + dependencies: { constructor: "1.0.0", valueOf: "1.0.0" }, + }), + }), + }); + + expect(await createHelpers(deps).detectReusedCode()).toEqual([]); + }); +}); + +describe("mergeReusedCode", () => { + it("appends detected entries to existing ones", () => { + const existing = [{ name: "Other Gov Tool", URL: "https://example.gov" }]; + expect(mergeReusedCode(existing, [USWDS])).toEqual([...existing, USWDS]); + }); + + it("does not duplicate an entry already present by URL", () => { + const existing = [{ name: "USWDS (manual)", URL: USWDS.URL }]; + expect(mergeReusedCode(existing, [USWDS])).toEqual(existing); + }); + + it("does not duplicate an entry already present by name", () => { + const existing = [{ name: USWDS.name, URL: "https://old.example" }]; + expect(mergeReusedCode(existing, [USWDS])).toEqual(existing); + }); + + it("returns existing unchanged when nothing is detected", () => { + const existing = [{ name: "Gov Tool", URL: "https://example.gov" }]; + expect(mergeReusedCode(existing, [])).toEqual(existing); + }); + + it("tolerates a non-array existing value", () => { + expect(mergeReusedCode(undefined as any, [USWDS])).toEqual([USWDS]); + }); +}); + +describe("GOV_DEPENDENCIES integrity", () => { + const entries = Object.entries(GOV_DEPENDENCIES); + + it.each(entries)("%s has a lowercase key and a valid entry", (key, entry) => { + expect(key).toBe(key.toLowerCase()); + expect(entry.name.trim()).not.toBe(""); + expect(entry.URL).toMatch(/^https:\/\//); + }); }); \ No newline at end of file diff --git a/src/gov-dependencies.ts b/src/gov-dependencies.ts new file mode 100644 index 00000000..1547d7a2 --- /dev/null +++ b/src/gov-dependencies.ts @@ -0,0 +1,59 @@ +export interface ReusedCodeEntry { + name: string; + URL: string; +} + +// each dependency gets its own entry with the repository URL where the source code is hosted +export const USWDS: ReusedCodeEntry = { + name: "U.S. Web Design System (USWDS)", + URL: "https://github.com/uswds/uswds", +}; + +export const USWDS_COMPILE: ReusedCodeEntry = { + name: "USWDS Compile", + URL: "https://github.com/uswds/uswds-compile", +}; + +export const CMS_DESIGN_SYSTEM: ReusedCodeEntry = { + name: "CMS Design System", + URL: "https://github.com/CMSgov/design-system", +}; + +export const CMS_DS_HEALTHCARE_GOV: ReusedCodeEntry = { + name: "CMS Design System - HealthCare.gov", + URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-healthcare-gov", +}; + +export const CMS_DS_MEDICARE_GOV: ReusedCodeEntry = { + name: "CMS Design System - Medicare.gov", + URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-medicare-gov", +}; + +export const CMS_DS_CMS_GOV: ReusedCodeEntry = { + name: "CMS Design System - CMS.gov", + URL: "https://github.com/CMSgov/design-system/tree/main/packages/ds-cms-gov", +}; + +// keys are lowercased package names; each maps to its own unique entry +export const GOV_DEPENDENCIES: Record = { + uswds: USWDS, + "@uswds/uswds": USWDS, + "@uswds/compile": USWDS_COMPILE, + "@cmsgov/design-system": CMS_DESIGN_SYSTEM, + "@cmsgov/ds-healthcare-gov": CMS_DS_HEALTHCARE_GOV, + "@cmsgov/ds-medicare-gov": CMS_DS_MEDICARE_GOV, + "@cmsgov/ds-cms-gov": CMS_DS_CMS_GOV, +}; + +// npm names are lowercase and Python names are case-insensitive, so lowercasing suffices to match +export function normalizePackageName(name: string): string { + return name.trim().toLowerCase(); +} + +export function lookupGovDependency(name: string): ReusedCodeEntry | undefined { + // hasOwn guard so names like "constructor"/"__proto__" don't match inherited members + const key = normalizePackageName(name); + return Object.hasOwn(GOV_DEPENDENCIES, key) + ? GOV_DEPENDENCIES[key] + : undefined; +} diff --git a/src/helper.ts b/src/helper.ts index 0bd5ccab..521cf180 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -2,6 +2,7 @@ import { CodeJSON } from "./types/CodeJSONSchema.js"; import { BasicRepoInfo } from "./types/BasicRepoInfo.js"; import { validateCodeJSON } from "./zod-validation.js"; import { Dependencies } from "./types/Dependencies.js"; +import { ReusedCodeEntry, lookupGovDependency } from "./gov-dependencies.js"; const HOURS_PER_MONTH = 730.001; @@ -90,6 +91,39 @@ export function createHelpers(deps: Dependencies) { } } + //=============================================== + // Reused Code + //=============================================== + async function detectReusedCode(): Promise { + const [packageJSON, requirements] = await Promise.all([ + readManifest("/github/workspace/package.json"), + readManifest("/github/workspace/requirements.txt"), + ]); + + const names: string[] = []; + if (packageJSON) names.push(...parsePackageJSON(packageJSON)); + if (requirements) names.push(...parseRequirementsTxt(requirements)); + + const entries: ReusedCodeEntry[] = []; + const seen = new Set(); + for (const name of names) { + const entry = lookupGovDependency(name); + if (entry && !seen.has(entry.URL)) { + seen.add(entry.URL); + entries.push(entry); + } + } + return entries; + } + + async function readManifest(filepath: string): Promise { + try { + return await deps.readFile(filepath); + } catch { + return null; + } + } + async function getBaseBranch(): Promise { if (deps.branch) { return deps.branch; @@ -267,6 +301,8 @@ export function createHelpers(deps: Dependencies) { return { calculateMetaData, + detectReusedCode, + mergeReusedCode, getBaseBranch, validateOnly, validateCodeJSON, @@ -279,6 +315,56 @@ export function createHelpers(deps: Dependencies) { // export the type for convenience export type Helpers = ReturnType; +export function parsePackageJSON(content: string): string[] { + try { + const pkg = JSON.parse(content); + return [ + ...Object.keys(pkg.dependencies ?? {}), + ...Object.keys(pkg.devDependencies ?? {}), + ]; + } catch { + return []; + } +} + +// strips version specifiers, extras, markers and comments, leaving the bare package name +export function parseRequirementsTxt(content: string): string[] { + const names: string[] = []; + for (const rawLine of content.split(/\r?\n/)) { + const line = rawLine.split("#")[0].trim(); + if (!line || line.startsWith("-")) continue; + const match = line.match(/^[A-Za-z0-9._-]+/); + if (match) names.push(match[0]); + } + return names; +} + +// keeps existing entries (manual edits) and appends detected ones, de-duped by name and URL +export function mergeReusedCode( + existing: Array<{ name?: string; URL?: string }>, + detected: ReusedCodeEntry[], +): Array<{ name?: string; URL?: string }> { + const base = Array.isArray(existing) ? existing : []; + const merged = [...base]; + const seenURLs = new Set( + base.map((e) => e.URL?.toLowerCase()).filter(Boolean), + ); + const seenNames = new Set( + base.map((e) => e.name?.toLowerCase()).filter(Boolean), + ); + + for (const entry of detected) { + const url = entry.URL.toLowerCase(); + const name = entry.name.toLowerCase(); + if (seenURLs.has(url) || seenNames.has(name)) continue; + merged.push(entry); + seenURLs.add(url); + seenNames.add(name); + } + + return merged; +} + function bodyOfPR(): string { return ` ## Welcome to the Federal Open Source Community! diff --git a/src/main.ts b/src/main.ts index 0d0eeebf..0c74d746 100644 --- a/src/main.ts +++ b/src/main.ts @@ -136,6 +136,12 @@ async function getMetaData( tags?.push("archived"); } + // detect government-made dependencies and merge with any existing reusedCode + const reusedCode = helpers.mergeReusedCode( + existingCodeJSON?.reusedCode ?? [], + await helpers.detectReusedCode(), + ); + return { name: partialCodeJSON.name, description: description, @@ -158,6 +164,7 @@ async function getMetaData( feedbackMechanism, SBOM, contractNumber, + reusedCode, }; }