@jatingargiitk/archmap 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/action.yml +19 -3
- package/dist/action.js +19 -6
- package/dist/action.js.map +1 -1
- package/dist/chunk-QUFBKU64.js +915 -0
- package/dist/chunk-QUFBKU64.js.map +1 -0
- package/dist/cli.js +21 -12
- package/dist/cli.js.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
archmap commits a living map of your modules and flags every PR—human or AI—that adds a new dependency between them.
|
|
10
10
|
|
|
11
|
+

|
|
12
|
+
|
|
11
13
|
---
|
|
12
14
|
|
|
13
15
|
## Quick Start
|
|
@@ -21,17 +23,25 @@ This creates:
|
|
|
21
23
|
- ✅ `ARCHITECTURE.md` - Living architecture map ([see ours](./ARCHITECTURE.md))
|
|
22
24
|
- ✅ `archmap.lock.json` - Dependency lockfile
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
The generated workflow:
|
|
25
27
|
|
|
26
28
|
```yaml
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
name: archmap
|
|
30
|
+
|
|
31
|
+
on:
|
|
32
|
+
pull_request:
|
|
33
|
+
|
|
34
|
+
permissions:
|
|
35
|
+
contents: write
|
|
36
|
+
pull-requests: write
|
|
37
|
+
|
|
30
38
|
jobs:
|
|
31
39
|
archmap:
|
|
32
40
|
runs-on: ubuntu-latest
|
|
33
41
|
steps:
|
|
34
42
|
- uses: actions/checkout@v4
|
|
43
|
+
with:
|
|
44
|
+
fetch-depth: 0
|
|
35
45
|
- uses: jatingargiitk/archmap@v0
|
|
36
46
|
```
|
|
37
47
|
|
|
@@ -63,9 +73,7 @@ Think of it as snapshot testing, but for your architecture.
|
|
|
63
73
|
|
|
64
74
|
### After archmap
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
The PR gets a sticky comment showing the new dependency and file location. Now the reviewer knows exactly what changed architecturally, and the decision is explicit.
|
|
76
|
+
The PR gets a sticky comment showing the new dependency with the exact file and line number where the import was added. Now the reviewer knows exactly what changed architecturally, and the decision is explicit.
|
|
69
77
|
|
|
70
78
|
---
|
|
71
79
|
|
|
@@ -74,7 +82,7 @@ The PR gets a sticky comment showing the new dependency and file location. Now t
|
|
|
74
82
|
- ✅ **100% Deterministic** - Same input, same output. No LLM or API key required
|
|
75
83
|
- ✅ **Fast** - Parses with `web-tree-sitter` WASM (TS/JS + Python in v0.1)
|
|
76
84
|
- ✅ **Lockfile Diffing** - Git-friendly JSON lockfile tracks accepted edges
|
|
77
|
-
- ✅ **GitHub Action** - Sticky PR comments show
|
|
85
|
+
- ✅ **GitHub Action** - Sticky PR comments show added/removed edges with file locations
|
|
78
86
|
- ✅ **Optional Gate Mode** - Fail CI if unapproved edges are added
|
|
79
87
|
- ✅ **Deny Rules** - `deny: { from: "ui/**", to: "db/**" }`
|
|
80
88
|
- ✅ **Living Docs** - `ARCHITECTURE.md` with Mermaid diagram, auto-regenerated
|
|
@@ -166,7 +174,7 @@ modules:
|
|
|
166
174
|
|
|
167
175
|
### What about fork PRs?
|
|
168
176
|
|
|
169
|
-
|
|
177
|
+
Fork PRs cannot post comments with the default `GITHUB_TOKEN` due to GitHub security restrictions. The workflow will run the check successfully, but comment posting will be skipped. The same architecture diff is visible in the action logs.
|
|
170
178
|
|
|
171
179
|
### Does anything leave my runner?
|
|
172
180
|
|
package/action.yml
CHANGED
|
@@ -7,14 +7,30 @@ branding:
|
|
|
7
7
|
color: 'blue'
|
|
8
8
|
|
|
9
9
|
runs:
|
|
10
|
-
using: '
|
|
11
|
-
|
|
10
|
+
using: 'composite'
|
|
11
|
+
steps:
|
|
12
|
+
- name: Setup Node
|
|
13
|
+
uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: '20'
|
|
16
|
+
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
shell: bash
|
|
19
|
+
run: |
|
|
20
|
+
cd ${{ github.action_path }}
|
|
21
|
+
npm ci --omit=dev
|
|
22
|
+
|
|
23
|
+
- name: Run archmap
|
|
24
|
+
shell: bash
|
|
25
|
+
run: node ${{ github.action_path }}/dist/action.js
|
|
26
|
+
env:
|
|
27
|
+
INPUT_MODE: ${{ inputs.mode }}
|
|
28
|
+
GITHUB_TOKEN: ${{ inputs.github-token }}
|
|
12
29
|
|
|
13
30
|
inputs:
|
|
14
31
|
mode:
|
|
15
32
|
description: 'Mode: comment (default) or gate'
|
|
16
33
|
required: false
|
|
17
|
-
default: 'comment'
|
|
18
34
|
github-token:
|
|
19
35
|
description: 'GitHub token for posting comments'
|
|
20
36
|
required: false
|
package/dist/action.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
shouldFailCheck,
|
|
14
14
|
writeArchitectureFile,
|
|
15
15
|
writeLockFile
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QUFBKU64.js";
|
|
17
17
|
|
|
18
18
|
// src/action.ts
|
|
19
19
|
import * as fs from "fs/promises";
|
|
@@ -42,8 +42,19 @@ async function handlePullRequest(rootDir, currentGraph, currentLock, config, con
|
|
|
42
42
|
console.log("\u{1F4CA} Computing diff...");
|
|
43
43
|
let baseLock = null;
|
|
44
44
|
try {
|
|
45
|
+
const baseRef = `origin/${context.baseRef}`;
|
|
46
|
+
try {
|
|
47
|
+
execSync(`git rev-parse --verify ${baseRef}`, { cwd: rootDir, stdio: "ignore" });
|
|
48
|
+
} catch {
|
|
49
|
+
console.log(`Fetching base ref ${context.baseRef}...`);
|
|
50
|
+
try {
|
|
51
|
+
execSync(`git fetch --no-tags --depth=1 origin ${context.baseRef}`, { cwd: rootDir, stdio: "pipe" });
|
|
52
|
+
} catch (fetchError) {
|
|
53
|
+
console.log(`\u26A0\uFE0F Could not fetch base ref: ${fetchError.message}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
45
56
|
const baseLockContent = execSync(
|
|
46
|
-
`git show
|
|
57
|
+
`git show ${baseRef}:archmap.lock.json`,
|
|
47
58
|
{ cwd: rootDir, encoding: "utf-8" }
|
|
48
59
|
);
|
|
49
60
|
baseLock = JSON.parse(baseLockContent);
|
|
@@ -52,7 +63,7 @@ async function handlePullRequest(rootDir, currentGraph, currentLock, config, con
|
|
|
52
63
|
}
|
|
53
64
|
const diff = diffLocks(baseLock, currentLock, currentGraph);
|
|
54
65
|
const violations = checkRules(currentGraph.edges, config);
|
|
55
|
-
const commentBody = buildCommentBody(diff, violations, currentGraph);
|
|
66
|
+
const commentBody = buildCommentBody(diff, violations, currentGraph, context);
|
|
56
67
|
if (process.env.GITHUB_TOKEN && context.prNumber) {
|
|
57
68
|
await postOrUpdateComment(context, commentBody);
|
|
58
69
|
} else {
|
|
@@ -86,7 +97,7 @@ async function handlePush(rootDir, context) {
|
|
|
86
97
|
}
|
|
87
98
|
}
|
|
88
99
|
}
|
|
89
|
-
function buildCommentBody(diff, violations, _graph) {
|
|
100
|
+
function buildCommentBody(diff, violations, _graph, context) {
|
|
90
101
|
const lines = [];
|
|
91
102
|
lines.push("## \u{1F5FA}\uFE0F archmap");
|
|
92
103
|
lines.push("");
|
|
@@ -122,7 +133,9 @@ function buildCommentBody(diff, violations, _graph) {
|
|
|
122
133
|
for (const edge of diff.added) {
|
|
123
134
|
if (edge.locations && edge.locations.length > 0) {
|
|
124
135
|
const loc = edge.locations[0];
|
|
125
|
-
|
|
136
|
+
const [owner, repo] = context.repository.split("/");
|
|
137
|
+
const blobUrl = `https://github.com/${owner}/${repo}/blob/${context.sha}/${loc.file}#L${loc.line}`;
|
|
138
|
+
lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\` ([${loc.file}:${loc.line}](${blobUrl}))`);
|
|
126
139
|
} else {
|
|
127
140
|
lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\``);
|
|
128
141
|
}
|
|
@@ -326,7 +339,7 @@ async function loadConfig(rootDir) {
|
|
|
326
339
|
config = {};
|
|
327
340
|
}
|
|
328
341
|
const inputMode = process.env.INPUT_MODE;
|
|
329
|
-
if (inputMode === "comment" || inputMode === "gate") {
|
|
342
|
+
if (inputMode && (inputMode === "comment" || inputMode === "gate")) {
|
|
330
343
|
config.mode = inputMode;
|
|
331
344
|
}
|
|
332
345
|
return config;
|
package/dist/action.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/action.ts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { extractTypeScriptImports, extractPythonImports } from './parser/index.js';\nimport { resolveImports, loadTsConfig, findWorkspaces } from './resolver/index.js';\nimport { buildGraph } from './graph/index.js';\nimport { writeArchitectureFile, renderDeltaDiagram } from './render/index.js';\nimport { graphToLock, writeLockFile, diffLocks } from './lock/index.js';\nimport { checkRules, shouldFailCheck } from './rules/index.js';\nimport type { Config } from './types.js';\n\nasync function runAction(): Promise<void> {\n const githubContext = getGitHubContext();\n const rootDir = process.env.GITHUB_WORKSPACE || process.cwd();\n\n console.log('🔍 Analyzing architecture...');\n\n const config = await loadConfig(rootDir);\n const currentGraph = await analyzeRepo(rootDir, config);\n const currentLock = graphToLock(currentGraph);\n\n await writeArchitectureFile(\n path.join(rootDir, 'ARCHITECTURE.md'),\n currentGraph\n );\n\n await writeLockFile(path.join(rootDir, 'archmap.lock.json'), currentLock);\n\n if (githubContext.eventName === 'pull_request') {\n await handlePullRequest(rootDir, currentGraph, currentLock, config, githubContext);\n } else if (githubContext.eventName === 'push') {\n await handlePush(rootDir, githubContext);\n }\n}\n\nasync function handlePullRequest(\n rootDir: string,\n currentGraph: any,\n currentLock: any,\n config: Config,\n context: any\n): Promise<void> {\n console.log('📊 Computing diff...');\n\n let baseLock = null;\n try {\n const baseLockContent = execSync(\n `git show origin/${context.baseRef}:archmap.lock.json`,\n { cwd: rootDir, encoding: 'utf-8' }\n );\n baseLock = JSON.parse(baseLockContent);\n } catch {\n console.log('⚠️ No lock file found in base branch');\n }\n\n const diff = diffLocks(baseLock, currentLock, currentGraph);\n const violations = checkRules(currentGraph.edges, config);\n\n const commentBody = buildCommentBody(diff, violations, currentGraph);\n\n if (process.env.GITHUB_TOKEN && context.prNumber) {\n await postOrUpdateComment(context, commentBody);\n } else {\n console.log('Comment preview:');\n console.log(commentBody);\n }\n\n const shouldFail = shouldFailCheck(diff, violations, config);\n\n if (shouldFail) {\n console.log('❌ Check failed');\n process.exit(1);\n } else {\n console.log('✅ Check passed');\n }\n}\n\nasync function handlePush(rootDir: string, context: any): Promise<void> {\n if (context.ref === `refs/heads/${context.defaultBranch}`) {\n console.log('🔄 Committing updated architecture files...');\n\n try {\n execSync('git config user.name \"archmap[bot]\"', { cwd: rootDir });\n execSync('git config user.email \"archmap[bot]@users.noreply.github.com\"', { cwd: rootDir });\n execSync('git add ARCHITECTURE.md archmap.lock.json', { cwd: rootDir });\n \n try {\n execSync('git commit -m \"chore: update architecture map [skip ci]\"', { cwd: rootDir });\n execSync(`git push origin ${context.defaultBranch}`, { cwd: rootDir });\n console.log('✅ Architecture files updated');\n } catch {\n console.log('ℹ️ No changes to commit');\n }\n } catch (error: any) {\n console.log('⚠️ Could not commit changes:', error.message);\n }\n }\n}\n\nfunction buildCommentBody(diff: any, violations: any[], _graph: any): string {\n const lines: string[] = [];\n\n lines.push('## 🗺️ archmap');\n lines.push('');\n\n const summary = `**${diff.added.length > 0 ? '+' : ''}${diff.added.length} new edge${diff.added.length !== 1 ? 's' : ''}, ${diff.removed.length > 0 ? '-' : ''}${diff.removed.length} removed**`;\n lines.push(summary);\n lines.push('');\n\n if (violations.length > 0) {\n lines.push('### ⚠️ Rule Violations');\n lines.push('');\n for (const violation of violations) {\n lines.push(`- ${violation.message}`);\n lines.push(` - Rule: \\`${violation.rule}\\``);\n }\n lines.push('');\n }\n\n if (diff.added.length > 0 || diff.removed.length > 0) {\n const touchedModules = new Set<string>();\n for (const edge of diff.added) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n for (const edge of diff.removed) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n\n lines.push('### Changes');\n lines.push('');\n lines.push(renderDeltaDiagram(diff.added, diff.removed, touchedModules));\n lines.push('');\n\n if (diff.added.length > 0) {\n lines.push('#### Added Dependencies');\n lines.push('');\n for (const edge of diff.added) {\n // Show locations (B6)\n if (edge.locations && edge.locations.length > 0) {\n const loc = edge.locations[0];\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\` ([${loc.file}:${loc.line}](${loc.file}#L${loc.line}))`);\n } else {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n }\n lines.push('');\n }\n\n if (diff.removed.length > 0) {\n lines.push('#### Removed Dependencies');\n lines.push('');\n for (const edge of diff.removed) {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n lines.push('');\n }\n }\n\n if (diff.newCycles.length > 0) {\n lines.push('### ⚠️ New Cycles');\n lines.push('');\n for (const cycle of diff.newCycles) {\n lines.push(`- ${cycle.map((m: string) => `\\`${m}\\``).join(' → ')}`);\n }\n lines.push('');\n }\n\n if (diff.added.length === 0 && diff.removed.length === 0 && violations.length === 0) {\n lines.push('✅ No architectural changes detected');\n } else {\n lines.push('---');\n lines.push('');\n lines.push('To accept these changes, run:');\n lines.push('```bash');\n lines.push('npx @jatingargiitk/archmap accept');\n lines.push('git add ARCHITECTURE.md archmap.lock.json');\n lines.push('git commit -m \"chore: update architecture map\"');\n lines.push('```');\n }\n\n lines.push('');\n lines.push('<!-- archmap-comment -->');\n\n return lines.join('\\n');\n}\n\nasync function postOrUpdateComment(context: any, body: string): Promise<void> {\n const token = process.env.GITHUB_TOKEN;\n if (!token) return;\n\n const [owner, repo] = context.repository.split('/');\n const issueNumber = context.prNumber;\n\n try {\n const existingComments = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n 'User-Agent': 'archmap',\n },\n }\n );\n\n const comments = (await existingComments.json()) as any[];\n const existingComment = comments.find((c: any) =>\n c.body.includes('<!-- archmap-comment -->')\n );\n\n if (existingComment) {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Updated existing comment');\n } else {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Posted new comment');\n }\n } catch (error: any) {\n console.log('⚠️ Could not post comment:', error.message);\n }\n}\n\nfunction getGitHubContext(): any {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n let event = {};\n\n if (eventPath) {\n try {\n const eventData = readFileSync(eventPath, 'utf-8');\n event = JSON.parse(eventData);\n } catch {\n // Ignore\n }\n }\n\n return {\n eventName: process.env.GITHUB_EVENT_NAME || '',\n repository: process.env.GITHUB_REPOSITORY || '',\n ref: process.env.GITHUB_REF || '',\n sha: process.env.GITHUB_SHA || '',\n actor: process.env.GITHUB_ACTOR || '',\n workflow: process.env.GITHUB_WORKFLOW || '',\n defaultBranch: (event as any).repository?.default_branch || 'main',\n baseRef: (event as any).pull_request?.base?.ref || 'main',\n prNumber: (event as any).pull_request?.number,\n };\n}\n\nasync function analyzeRepo(rootDir: string, config: Config): Promise<any> {\n const files = await findSourceFiles(rootDir, config.ignore || []);\n\n const allImports = [];\n const allFiles: string[] = [];\n\n for (const file of files) {\n const fullPath = path.join(rootDir, file);\n const content = await fs.readFile(fullPath, 'utf-8');\n\n let imports;\n if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) {\n imports = await extractTypeScriptImports(file, content);\n } else if (file.endsWith('.py')) {\n imports = await extractPythonImports(file, content);\n } else {\n continue;\n }\n\n allImports.push(imports);\n allFiles.push(file);\n }\n\n const tsconfig = await loadTsConfig(rootDir);\n const workspaces = await findWorkspaces(rootDir);\n\n const resolved = await resolveImports(allImports, {\n rootDir,\n tsconfig: tsconfig || undefined,\n workspaces: workspaces.length > 0 ? workspaces : undefined,\n });\n\n const moduleConfig = config.modules || {};\n const graph = buildGraph(resolved, allFiles, moduleConfig);\n\n return graph;\n}\n\nasync function findSourceFiles(rootDir: string, ignore: string[]): Promise<string[]> {\n const files: string[] = [];\n const ignorePatterns = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/coverage/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/__tests__/**',\n ...ignore,\n ];\n\n async function walk(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(rootDir, fullPath);\n\n if (shouldIgnore(relativePath, ignorePatterns)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n if (isSourceFile(entry.name)) {\n files.push(relativePath);\n }\n }\n }\n }\n\n await walk(rootDir);\n return files;\n}\n\nfunction shouldIgnore(filePath: string, patterns: string[]): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n for (const pattern of patterns) {\n const regex = patternToRegex(pattern);\n if (regex.test(normalized)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction patternToRegex(pattern: string): RegExp {\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '___DOUBLESTAR___')\n .replace(/\\*/g, '[^/]*')\n .replace(/___DOUBLESTAR___/g, '.*');\n\n return new RegExp(`^${regexStr}$`);\n}\n\nfunction isSourceFile(name: string): boolean {\n return /\\.(ts|tsx|js|jsx|py)$/.test(name);\n}\n\nasync function loadConfig(rootDir: string): Promise<Config> {\n const configPath = path.join(rootDir, 'archmap.yml');\n\n let config: Config = {};\n try {\n const content = await fs.readFile(configPath, 'utf-8');\n const yaml = await import('js-yaml');\n config = yaml.load(content) as Config;\n } catch {\n config = {};\n }\n\n // Override with INPUT_MODE if provided (B7)\n const inputMode = process.env.INPUT_MODE;\n if (inputMode === 'comment' || inputMode === 'gate') {\n config.mode = inputMode;\n }\n\n return config;\n}\n\nrunAction().catch((error) => {\n console.error('Error:', error.message);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/action.ts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { extractTypeScriptImports, extractPythonImports } from './parser/index.js';\nimport { resolveImports, loadTsConfig, findWorkspaces } from './resolver/index.js';\nimport { buildGraph } from './graph/index.js';\nimport { writeArchitectureFile, renderDeltaDiagram } from './render/index.js';\nimport { graphToLock, writeLockFile, diffLocks } from './lock/index.js';\nimport { checkRules, shouldFailCheck } from './rules/index.js';\nimport type { Config } from './types.js';\n\nasync function runAction(): Promise<void> {\n const githubContext = getGitHubContext();\n const rootDir = process.env.GITHUB_WORKSPACE || process.cwd();\n\n console.log('🔍 Analyzing architecture...');\n\n const config = await loadConfig(rootDir);\n const currentGraph = await analyzeRepo(rootDir, config);\n const currentLock = graphToLock(currentGraph);\n\n await writeArchitectureFile(\n path.join(rootDir, 'ARCHITECTURE.md'),\n currentGraph\n );\n\n await writeLockFile(path.join(rootDir, 'archmap.lock.json'), currentLock);\n\n if (githubContext.eventName === 'pull_request') {\n await handlePullRequest(rootDir, currentGraph, currentLock, config, githubContext);\n } else if (githubContext.eventName === 'push') {\n await handlePush(rootDir, githubContext);\n }\n}\n\nasync function handlePullRequest(\n rootDir: string,\n currentGraph: any,\n currentLock: any,\n config: Config,\n context: any\n): Promise<void> {\n console.log('📊 Computing diff...');\n\n let baseLock = null;\n try {\n // Ensure we have the base ref, even in shallow clones\n const baseRef = `origin/${context.baseRef}`;\n try {\n execSync(`git rev-parse --verify ${baseRef}`, { cwd: rootDir, stdio: 'ignore' });\n } catch {\n // Base ref not found, try to fetch it\n console.log(`Fetching base ref ${context.baseRef}...`);\n try {\n execSync(`git fetch --no-tags --depth=1 origin ${context.baseRef}`, { cwd: rootDir, stdio: 'pipe' });\n } catch (fetchError: any) {\n console.log(`⚠️ Could not fetch base ref: ${fetchError.message}`);\n }\n }\n\n const baseLockContent = execSync(\n `git show ${baseRef}:archmap.lock.json`,\n { cwd: rootDir, encoding: 'utf-8' }\n );\n baseLock = JSON.parse(baseLockContent);\n } catch {\n console.log('⚠️ No lock file found in base branch');\n }\n\n const diff = diffLocks(baseLock, currentLock, currentGraph);\n const violations = checkRules(currentGraph.edges, config);\n\n const commentBody = buildCommentBody(diff, violations, currentGraph, context);\n\n if (process.env.GITHUB_TOKEN && context.prNumber) {\n await postOrUpdateComment(context, commentBody);\n } else {\n console.log('Comment preview:');\n console.log(commentBody);\n }\n\n const shouldFail = shouldFailCheck(diff, violations, config);\n\n if (shouldFail) {\n console.log('❌ Check failed');\n process.exit(1);\n } else {\n console.log('✅ Check passed');\n }\n}\n\nasync function handlePush(rootDir: string, context: any): Promise<void> {\n if (context.ref === `refs/heads/${context.defaultBranch}`) {\n console.log('🔄 Committing updated architecture files...');\n\n try {\n execSync('git config user.name \"archmap[bot]\"', { cwd: rootDir });\n execSync('git config user.email \"archmap[bot]@users.noreply.github.com\"', { cwd: rootDir });\n execSync('git add ARCHITECTURE.md archmap.lock.json', { cwd: rootDir });\n \n try {\n execSync('git commit -m \"chore: update architecture map [skip ci]\"', { cwd: rootDir });\n execSync(`git push origin ${context.defaultBranch}`, { cwd: rootDir });\n console.log('✅ Architecture files updated');\n } catch {\n console.log('ℹ️ No changes to commit');\n }\n } catch (error: unknown) {\n console.log('⚠️ Could not commit changes:', (error as Error).message);\n }\n }\n}\n\nfunction buildCommentBody(diff: any, violations: any[], _graph: any, context: any): string {\n const lines: string[] = [];\n\n lines.push('## 🗺️ archmap');\n lines.push('');\n\n const summary = `**${diff.added.length > 0 ? '+' : ''}${diff.added.length} new edge${diff.added.length !== 1 ? 's' : ''}, ${diff.removed.length > 0 ? '-' : ''}${diff.removed.length} removed**`;\n lines.push(summary);\n lines.push('');\n\n if (violations.length > 0) {\n lines.push('### ⚠️ Rule Violations');\n lines.push('');\n for (const violation of violations) {\n lines.push(`- ${violation.message}`);\n lines.push(` - Rule: \\`${violation.rule}\\``);\n }\n lines.push('');\n }\n\n if (diff.added.length > 0 || diff.removed.length > 0) {\n const touchedModules = new Set<string>();\n for (const edge of diff.added) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n for (const edge of diff.removed) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n\n lines.push('### Changes');\n lines.push('');\n lines.push(renderDeltaDiagram(diff.added, diff.removed, touchedModules));\n lines.push('');\n\n if (diff.added.length > 0) {\n lines.push('#### Added Dependencies');\n lines.push('');\n for (const edge of diff.added) {\n // Show locations (B6) with absolute blob URLs\n if (edge.locations && edge.locations.length > 0) {\n const loc = edge.locations[0];\n const [owner, repo] = context.repository.split('/');\n const blobUrl = `https://github.com/${owner}/${repo}/blob/${context.sha}/${loc.file}#L${loc.line}`;\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\` ([${loc.file}:${loc.line}](${blobUrl}))`);\n } else {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n }\n lines.push('');\n }\n\n if (diff.removed.length > 0) {\n lines.push('#### Removed Dependencies');\n lines.push('');\n for (const edge of diff.removed) {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n lines.push('');\n }\n }\n\n if (diff.newCycles.length > 0) {\n lines.push('### ⚠️ New Cycles');\n lines.push('');\n for (const cycle of diff.newCycles) {\n lines.push(`- ${cycle.map((m: string) => `\\`${m}\\``).join(' → ')}`);\n }\n lines.push('');\n }\n\n if (diff.added.length === 0 && diff.removed.length === 0 && violations.length === 0) {\n lines.push('✅ No architectural changes detected');\n } else {\n lines.push('---');\n lines.push('');\n lines.push('To accept these changes, run:');\n lines.push('```bash');\n lines.push('npx @jatingargiitk/archmap accept');\n lines.push('git add ARCHITECTURE.md archmap.lock.json');\n lines.push('git commit -m \"chore: update architecture map\"');\n lines.push('```');\n }\n\n lines.push('');\n lines.push('<!-- archmap-comment -->');\n\n return lines.join('\\n');\n}\n\nasync function postOrUpdateComment(context: any, body: string): Promise<void> {\n const token = process.env.GITHUB_TOKEN;\n if (!token) return;\n\n const [owner, repo] = context.repository.split('/');\n const issueNumber = context.prNumber;\n\n try {\n const existingComments = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n 'User-Agent': 'archmap',\n },\n }\n );\n\n const comments = (await existingComments.json()) as any[];\n const existingComment = comments.find((c: any) =>\n c.body.includes('<!-- archmap-comment -->')\n );\n\n if (existingComment) {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Updated existing comment');\n } else {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Posted new comment');\n }\n } catch (error: unknown) {\n console.log('⚠️ Could not post comment:', (error as Error).message);\n }\n}\n\nfunction getGitHubContext(): any {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n let event = {};\n\n if (eventPath) {\n try {\n const eventData = readFileSync(eventPath, 'utf-8');\n event = JSON.parse(eventData);\n } catch {\n // Ignore\n }\n }\n\n return {\n eventName: process.env.GITHUB_EVENT_NAME || '',\n repository: process.env.GITHUB_REPOSITORY || '',\n ref: process.env.GITHUB_REF || '',\n sha: process.env.GITHUB_SHA || '',\n actor: process.env.GITHUB_ACTOR || '',\n workflow: process.env.GITHUB_WORKFLOW || '',\n defaultBranch: (event as any).repository?.default_branch || 'main',\n baseRef: (event as any).pull_request?.base?.ref || 'main',\n prNumber: (event as any).pull_request?.number,\n };\n}\n\nasync function analyzeRepo(rootDir: string, config: Config): Promise<any> {\n const files = await findSourceFiles(rootDir, config.ignore || []);\n\n const allImports = [];\n const allFiles: string[] = [];\n\n for (const file of files) {\n const fullPath = path.join(rootDir, file);\n const content = await fs.readFile(fullPath, 'utf-8');\n\n let imports;\n if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) {\n imports = await extractTypeScriptImports(file, content);\n } else if (file.endsWith('.py')) {\n imports = await extractPythonImports(file, content);\n } else {\n continue;\n }\n\n allImports.push(imports);\n allFiles.push(file);\n }\n\n const tsconfig = await loadTsConfig(rootDir);\n const workspaces = await findWorkspaces(rootDir);\n\n const resolved = await resolveImports(allImports, {\n rootDir,\n tsconfig: tsconfig || undefined,\n workspaces: workspaces.length > 0 ? workspaces : undefined,\n });\n\n const moduleConfig = config.modules || {};\n const graph = buildGraph(resolved, allFiles, moduleConfig);\n\n return graph;\n}\n\nasync function findSourceFiles(rootDir: string, ignore: string[]): Promise<string[]> {\n const files: string[] = [];\n const ignorePatterns = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/coverage/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/__tests__/**',\n ...ignore,\n ];\n\n async function walk(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(rootDir, fullPath);\n\n if (shouldIgnore(relativePath, ignorePatterns)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n if (isSourceFile(entry.name)) {\n files.push(relativePath);\n }\n }\n }\n }\n\n await walk(rootDir);\n return files;\n}\n\nfunction shouldIgnore(filePath: string, patterns: string[]): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n for (const pattern of patterns) {\n const regex = patternToRegex(pattern);\n if (regex.test(normalized)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction patternToRegex(pattern: string): RegExp {\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '___DOUBLESTAR___')\n .replace(/\\*/g, '[^/]*')\n .replace(/___DOUBLESTAR___/g, '.*');\n\n return new RegExp(`^${regexStr}$`);\n}\n\nfunction isSourceFile(name: string): boolean {\n return /\\.(ts|tsx|js|jsx|py)$/.test(name);\n}\n\nasync function loadConfig(rootDir: string): Promise<Config> {\n const configPath = path.join(rootDir, 'archmap.yml');\n\n let config: Config = {};\n try {\n const content = await fs.readFile(configPath, 'utf-8');\n const yaml = await import('js-yaml');\n config = yaml.load(content) as Config;\n } catch {\n config = {};\n }\n\n // Override with INPUT_MODE if explicitly provided (B7)\n const inputMode = process.env.INPUT_MODE;\n if (inputMode && (inputMode === 'comment' || inputMode === 'gate')) {\n config.mode = inputMode;\n }\n\n return config;\n}\n\nrunAction().catch((error) => {\n console.error('Error:', error.message);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|