@solana-epic/github-action 0.1.0-beta.1 → 0.2.0-beta.0

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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solana-epic/github-action",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.2.0-beta.0",
4
4
  "description": "Solana EPIC Upgrade Guard GitHub Action",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -16,11 +16,33 @@
16
16
  "dependencies": {
17
17
  "@actions/core": "^1.10.1",
18
18
  "@actions/github": "^6.0.0",
19
- "@solana-epic/diff-engine": "^0.1.0-beta.1",
20
- "@solana-epic/parser": "^0.1.0-beta.1"
19
+ "@solana-epic/cli": "^0.2.0-beta.0"
21
20
  },
22
21
  "devDependencies": {
23
22
  "esbuild": "^0.20.1",
24
23
  "typescript": "^5.3.3"
25
- }
24
+ },
25
+ "license": "MIT",
26
+ "homepage": "https://github.com/solana-epic/epic#readme",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/solana-epic/epic.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/solana-epic/epic/issues"
33
+ },
34
+ "keywords": [
35
+ "solana",
36
+ "anchor",
37
+ "security",
38
+ "static-analysis",
39
+ "audit",
40
+ "upgrade-safety",
41
+ "rust"
42
+ ],
43
+ "author": "Solana EPIC Team",
44
+ "files": [
45
+ "dist",
46
+ "action.yml"
47
+ ]
26
48
  }
package/dist/report.d.ts DELETED
@@ -1,4 +0,0 @@
1
- import type { DiffReport } from "@solana-epic/diff-engine";
2
- import type { config } from "@solana-epic/parser";
3
- export declare function generateCompactMarkdownReport(report: DiffReport, cfg: config.ResolvedEpicConfig, configChanged: boolean): string;
4
- //# sourceMappingURL=report.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAGlD,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,MAAM,CAAC,kBAAkB,EAC9B,aAAa,EAAE,OAAO,GACrB,MAAM,CAsIR"}
package/dist/report.js DELETED
@@ -1,133 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateCompactMarkdownReport = generateCompactMarkdownReport;
4
- const diff_engine_1 = require("@solana-epic/diff-engine");
5
- function generateCompactMarkdownReport(report, cfg, configChanged) {
6
- const lines = [];
7
- // Determine Overall Status
8
- const failSeverity = cfg.failOnSeverity;
9
- const severityOrder = ["SAFE", "MINOR", "MAJOR", "CRITICAL"];
10
- const thresholdIndex = severityOrder.indexOf(failSeverity);
11
- const reportSeverityIndex = severityOrder.indexOf(report.severity);
12
- const blocked = thresholdIndex !== -1 && reportSeverityIndex !== -1 && reportSeverityIndex >= thresholdIndex;
13
- const hasOverrides = report.findings.some(f => {
14
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
15
- return f.severity !== original;
16
- });
17
- // 1. Status Banner
18
- if (blocked) {
19
- lines.push("## 🔴 EPIC Guard: UPGRADE BLOCKED");
20
- lines.push("");
21
- lines.push(`Upgrade checks failed because layout changes exceed the **${failSeverity}** threshold.`);
22
- }
23
- else if (hasOverrides) {
24
- lines.push("## 🟡 EPIC Guard: APPROVED WITH OVERRIDES");
25
- lines.push("");
26
- lines.push("Upgrade checks passed with muted warnings. Custom overrides are active in `epic.toml`.");
27
- }
28
- else {
29
- lines.push("## 🟢 EPIC Guard: APPROVED");
30
- lines.push("");
31
- lines.push("Upgrade checks approved. No layout compatibility risks detected.");
32
- }
33
- lines.push("");
34
- // 2. Config Change Warning
35
- if (configChanged) {
36
- lines.push("> [!WARNING]");
37
- lines.push("> **UPGRADE CONFIGURATION GATE MODIFIED**");
38
- lines.push("> This Pull Request contains changes to `epic.toml` configuration rules.");
39
- lines.push("> Signers must audit the modifications below to ensure safety limits are not bypassed.");
40
- lines.push("");
41
- }
42
- // 3. Summary Table
43
- lines.push("### 📊 Upgrade Summary");
44
- lines.push("");
45
- lines.push("| Program | Account | Finding | Final Severity | Overridden? |");
46
- lines.push("| :--- | :--- | :--- | :--- | :--- |");
47
- if (report.findings.length === 0) {
48
- lines.push("| *N/A* | *All Accounts* | *No structural changes* | `SAFE` | No |");
49
- }
50
- else {
51
- for (const f of report.findings) {
52
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
53
- const isOverridden = f.severity !== original;
54
- lines.push(`| \`marginfi\` | \`${f.account}\` | \`${f.kind}\` | \`${f.severity}\` | ${isOverridden ? "✅ Yes" : "No"} |`);
55
- }
56
- }
57
- lines.push("");
58
- // 4. Detailed Findings
59
- if (report.findings.length > 0) {
60
- lines.push("### 🔍 Layout Findings");
61
- lines.push("");
62
- for (const f of report.findings) {
63
- const intel = (0, diff_engine_1.createUpgradeIntelligenceItem)(f);
64
- lines.push(`#### Struct \`${f.account}\` — **${f.kind}**`);
65
- lines.push("");
66
- lines.push(`* **Finding Type**: ${f.kind}`);
67
- if (f.field) {
68
- lines.push(`* **Field**: \`${f.field.name}\` (${f.field.oldType || "new"} ──► ${f.field.newType || "removed"})`);
69
- }
70
- lines.push(`* **Size Impact**: \`${f.oldSize}B\` ──► \`${f.newSize}B\` (${f.newSize - f.oldSize >= 0 ? "+" : ""}${f.newSize - f.oldSize} bytes)`);
71
- lines.push(`* **Risk Class**: ${intel.riskCategory}`);
72
- lines.push(`* **Severity**: \`${f.severity}\``);
73
- lines.push("");
74
- }
75
- }
76
- // 5. Applied Overrides Section
77
- const appliedOverrides = [];
78
- for (const f of report.findings) {
79
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
80
- if (f.severity !== original) {
81
- // Find override note
82
- let note = "No note provided.";
83
- for (const [name, program] of cfg.programs.entries()) {
84
- const match = program.overrides.find(o => {
85
- const accountMatch = o.account.toLowerCase() === f.account.toLowerCase();
86
- const findingMatch = o.finding.toUpperCase() === f.kind.toUpperCase();
87
- const fieldMatch = f.field && o.field && o.field.toLowerCase() === f.field.name.toLowerCase();
88
- return accountMatch && findingMatch && (fieldMatch || !o.field);
89
- });
90
- if (match) {
91
- note = match.note;
92
- break;
93
- }
94
- }
95
- appliedOverrides.push({
96
- account: f.account,
97
- finding: f.kind,
98
- field: f.field?.name,
99
- shift: `\`${original}\` ──► \`${f.severity}\``,
100
- note
101
- });
102
- }
103
- }
104
- if (appliedOverrides.length > 0) {
105
- lines.push("### 🔑 Applied Layout Overrides");
106
- lines.push("");
107
- lines.push("| Struct | Finding | Field | Severity Shift | Note / Safety Justification |");
108
- lines.push("| :--- | :--- | :--- | :--- | :--- |");
109
- for (const o of appliedOverrides) {
110
- lines.push(`| \`${o.account}\` | \`${o.finding}\` | \`${o.field || "global"}\` | ${o.shift} | ${o.note} |`);
111
- }
112
- lines.push("");
113
- }
114
- // 6. Recommended Actions
115
- lines.push("### 💡 Recommended Actions");
116
- lines.push("");
117
- if (report.findings.length === 0) {
118
- lines.push("* No action required. Layout upgrades are safe to proceed.");
119
- }
120
- else {
121
- const uniqueRecommendations = new Set();
122
- for (const f of report.findings) {
123
- const intel = (0, diff_engine_1.createUpgradeIntelligenceItem)(f);
124
- uniqueRecommendations.add(intel.recommendation);
125
- }
126
- for (const rec of uniqueRecommendations) {
127
- lines.push(`* ${rec}`);
128
- }
129
- }
130
- lines.push("");
131
- return lines.join("\n");
132
- }
133
- //# sourceMappingURL=report.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
package/src/github.ts DELETED
@@ -1,81 +0,0 @@
1
- import * as github from "@actions/github";
2
- import * as core from "@actions/core";
3
-
4
- export async function upsertPRComment(token: string, reportMarkdown: string): Promise<void> {
5
- const context = github.context;
6
-
7
- if (!context.payload.pull_request) {
8
- core.info("Not a pull request event. Skipping comment posting.");
9
- return;
10
- }
11
-
12
- const prNumber = context.payload.pull_request.number;
13
- const owner = context.repo.owner;
14
- const repo = context.repo.repo;
15
-
16
- const octokit = github.getOctokit(token);
17
-
18
- const commentHeader = "<!-- epic-upgrade-guard-comment -->";
19
- const bodyWithHeader = `${commentHeader}\n${reportMarkdown}`;
20
-
21
- core.info(`Searching for existing EPIC comments on PR #${prNumber}...`);
22
-
23
- const { data: comments } = await octokit.rest.issues.listComments({
24
- owner,
25
- repo,
26
- issue_number: prNumber,
27
- per_page: 100
28
- });
29
-
30
- const existingComment = comments.find((comment) => comment.body?.includes(commentHeader));
31
-
32
- if (existingComment) {
33
- core.info(`Found existing comment (ID: ${existingComment.id}). Updating it to avoid comment spam...`);
34
- await octokit.rest.issues.updateComment({
35
- owner,
36
- repo,
37
- comment_id: existingComment.id,
38
- body: bodyWithHeader
39
- });
40
- } else {
41
- core.info(`No existing comment found. Creating a new one...`);
42
- await octokit.rest.issues.createComment({
43
- owner,
44
- repo,
45
- issue_number: prNumber,
46
- body: bodyWithHeader
47
- });
48
- }
49
- }
50
-
51
- export async function checkIfConfigChanged(token: string): Promise<boolean> {
52
- const context = github.context;
53
-
54
- if (!context.payload.pull_request) {
55
- core.info("Not a pull request context. Skipping config change audit.");
56
- return false;
57
- }
58
-
59
- const prNumber = context.payload.pull_request.number;
60
- const owner = context.repo.owner;
61
- const repo = context.repo.repo;
62
- const octokit = github.getOctokit(token);
63
-
64
- try {
65
- core.info(`Auditing PR #${prNumber} files for modifications to epic.toml...`);
66
- const { data: files } = await octokit.rest.pulls.listFiles({
67
- owner,
68
- repo,
69
- pull_number: prNumber,
70
- per_page: 100
71
- });
72
-
73
- const isModified = files.some(file => file.filename.endsWith("epic.toml"));
74
- core.info(`Config modification audit: ${isModified ? "MODIFIED" : "UNMODIFIED"}`);
75
- return isModified;
76
- } catch (error) {
77
- core.warning(`Failed to list pull request files from GitHub API: ${error}`);
78
- return false;
79
- }
80
- }
81
-
package/src/index.ts DELETED
@@ -1,66 +0,0 @@
1
- import * as core from "@actions/core";
2
- import { compareAnchorPrograms } from "@solana-epic/diff-engine";
3
- import { config } from "@solana-epic/parser";
4
- import { upsertPRComment, checkIfConfigChanged } from "./github.js";
5
- import { generateCompactMarkdownReport } from "./report.js";
6
-
7
- async function run(): Promise<void> {
8
- try {
9
- const githubToken = core.getInput("github_token", { required: true });
10
- const oldPath = core.getInput("old_path", { required: true });
11
- const newPath = core.getInput("new_path", { required: true });
12
- const configPath = core.getInput("config_path") || undefined;
13
-
14
- core.info(`Running EPIC Upgrade Guard compare:`);
15
- core.info(`Old path: ${oldPath}`);
16
- core.info(`New path: ${newPath}`);
17
- if (configPath) {
18
- core.info(`Custom config path: ${configPath}`);
19
- }
20
-
21
- // Load epic.toml configuration
22
- let epicConfig: config.ResolvedEpicConfig;
23
- try {
24
- epicConfig = config.loadEpicConfig(configPath);
25
- core.info(`Loaded epic.toml settings. Fail severity threshold: ${epicConfig.failOnSeverity}`);
26
- } catch (err: any) {
27
- core.setFailed(`Failed to validate epic.toml configuration: ${err.message}`);
28
- return;
29
- }
30
-
31
- // Check if configuration changed in the pull request
32
- const configChanged = await checkIfConfigChanged(githubToken);
33
-
34
- // Run layout verification comparison
35
- const report = await compareAnchorPrograms(oldPath, newPath, epicConfig);
36
-
37
- const findingsCount = report.findings.length;
38
- core.setOutput("severity", report.severity);
39
- core.setOutput("findings_count", findingsCount.toString());
40
-
41
- core.info(`Diff completed. Severity: ${report.severity}, Findings: ${findingsCount}`);
42
-
43
- // Generate compact markdown report
44
- const markdownReport = generateCompactMarkdownReport(report, epicConfig, configChanged);
45
-
46
- // Upsert the comment on GitHub Pull Request
47
- await upsertPRComment(githubToken, markdownReport);
48
-
49
- // Gate verification
50
- const severityLevels = ["SAFE", "MINOR", "MAJOR", "CRITICAL"];
51
- const thresholdIndex = severityLevels.indexOf(epicConfig.failOnSeverity);
52
- const reportSeverityIndex = severityLevels.indexOf(report.severity);
53
-
54
- if (thresholdIndex !== -1 && reportSeverityIndex !== -1 && reportSeverityIndex >= thresholdIndex) {
55
- core.setFailed(`EPIC Guard Blocked: Upgrade severity is ${report.severity} (threshold: ${epicConfig.failOnSeverity}).`);
56
- } else {
57
- core.info("EPIC Guard approved upgrade.");
58
- }
59
- } catch (error) {
60
- const message = error instanceof Error ? error.message : String(error);
61
- core.setFailed(`EPIC Upgrade Guard failed: ${message}`);
62
- }
63
- }
64
-
65
- run();
66
-
package/src/report.ts DELETED
@@ -1,143 +0,0 @@
1
- import type { DiffReport } from "@solana-epic/diff-engine";
2
- import type { config } from "@solana-epic/parser";
3
- import { createUpgradeIntelligenceItem } from "@solana-epic/diff-engine";
4
-
5
- export function generateCompactMarkdownReport(
6
- report: DiffReport,
7
- cfg: config.ResolvedEpicConfig,
8
- configChanged: boolean
9
- ): string {
10
- const lines: string[] = [];
11
-
12
- // Determine Overall Status
13
- const failSeverity = cfg.failOnSeverity;
14
- const severityOrder = ["SAFE", "MINOR", "MAJOR", "CRITICAL"];
15
- const thresholdIndex = severityOrder.indexOf(failSeverity);
16
- const reportSeverityIndex = severityOrder.indexOf(report.severity);
17
- const blocked = thresholdIndex !== -1 && reportSeverityIndex !== -1 && reportSeverityIndex >= thresholdIndex;
18
-
19
- const hasOverrides = report.findings.some(f => {
20
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
21
- return f.severity !== original;
22
- });
23
-
24
- // 1. Status Banner
25
- if (blocked) {
26
- lines.push("## 🔴 EPIC Guard: UPGRADE BLOCKED");
27
- lines.push("");
28
- lines.push(`Upgrade checks failed because layout changes exceed the **${failSeverity}** threshold.`);
29
- } else if (hasOverrides) {
30
- lines.push("## 🟡 EPIC Guard: APPROVED WITH OVERRIDES");
31
- lines.push("");
32
- lines.push("Upgrade checks passed with muted warnings. Custom overrides are active in `epic.toml`.");
33
- } else {
34
- lines.push("## 🟢 EPIC Guard: APPROVED");
35
- lines.push("");
36
- lines.push("Upgrade checks approved. No layout compatibility risks detected.");
37
- }
38
- lines.push("");
39
-
40
- // 2. Config Change Warning
41
- if (configChanged) {
42
- lines.push("> [!WARNING]");
43
- lines.push("> **UPGRADE CONFIGURATION GATE MODIFIED**");
44
- lines.push("> This Pull Request contains changes to `epic.toml` configuration rules.");
45
- lines.push("> Signers must audit the modifications below to ensure safety limits are not bypassed.");
46
- lines.push("");
47
- }
48
-
49
- // 3. Summary Table
50
- lines.push("### 📊 Upgrade Summary");
51
- lines.push("");
52
- lines.push("| Program | Account | Finding | Final Severity | Overridden? |");
53
- lines.push("| :--- | :--- | :--- | :--- | :--- |");
54
-
55
- if (report.findings.length === 0) {
56
- lines.push("| *N/A* | *All Accounts* | *No structural changes* | `SAFE` | No |");
57
- } else {
58
- for (const f of report.findings) {
59
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
60
- const isOverridden = f.severity !== original;
61
- lines.push(`| \`marginfi\` | \`${f.account}\` | \`${f.kind}\` | \`${f.severity}\` | ${isOverridden ? "✅ Yes" : "No"} |`);
62
- }
63
- }
64
- lines.push("");
65
-
66
- // 4. Detailed Findings
67
- if (report.findings.length > 0) {
68
- lines.push("### 🔍 Layout Findings");
69
- lines.push("");
70
- for (const f of report.findings) {
71
- const intel = createUpgradeIntelligenceItem(f);
72
- lines.push(`#### Struct \`${f.account}\` — **${f.kind}**`);
73
- lines.push("");
74
- lines.push(`* **Finding Type**: ${f.kind}`);
75
- if (f.field) {
76
- lines.push(`* **Field**: \`${f.field.name}\` (${f.field.oldType || "new"} ──► ${f.field.newType || "removed"})`);
77
- }
78
- lines.push(`* **Size Impact**: \`${f.oldSize}B\` ──► \`${f.newSize}B\` (${f.newSize - f.oldSize >= 0 ? "+" : ""}${f.newSize - f.oldSize} bytes)`);
79
- lines.push(`* **Risk Class**: ${intel.riskCategory}`);
80
- lines.push(`* **Severity**: \`${f.severity}\``);
81
- lines.push("");
82
- }
83
- }
84
-
85
- // 5. Applied Overrides Section
86
- const appliedOverrides: Array<{ account: string; finding: string; field?: string; shift: string; note: string }> = [];
87
- for (const f of report.findings) {
88
- const original = f.kind === "FIELD_ADDED" ? "MAJOR" : "CRITICAL";
89
- if (f.severity !== original) {
90
- // Find override note
91
- let note = "No note provided.";
92
- for (const [name, program] of cfg.programs.entries()) {
93
- const match = program.overrides.find(o => {
94
- const accountMatch = o.account.toLowerCase() === f.account.toLowerCase();
95
- const findingMatch = o.finding.toUpperCase() === f.kind.toUpperCase();
96
- const fieldMatch = f.field && o.field && o.field.toLowerCase() === f.field.name.toLowerCase();
97
- return accountMatch && findingMatch && (fieldMatch || !o.field);
98
- });
99
- if (match) {
100
- note = match.note;
101
- break;
102
- }
103
- }
104
- appliedOverrides.push({
105
- account: f.account,
106
- finding: f.kind,
107
- field: f.field?.name,
108
- shift: `\`${original}\` ──► \`${f.severity}\``,
109
- note
110
- });
111
- }
112
- }
113
-
114
- if (appliedOverrides.length > 0) {
115
- lines.push("### 🔑 Applied Layout Overrides");
116
- lines.push("");
117
- lines.push("| Struct | Finding | Field | Severity Shift | Note / Safety Justification |");
118
- lines.push("| :--- | :--- | :--- | :--- | :--- |");
119
- for (const o of appliedOverrides) {
120
- lines.push(`| \`${o.account}\` | \`${o.finding}\` | \`${o.field || "global"}\` | ${o.shift} | ${o.note} |`);
121
- }
122
- lines.push("");
123
- }
124
-
125
- // 6. Recommended Actions
126
- lines.push("### 💡 Recommended Actions");
127
- lines.push("");
128
- if (report.findings.length === 0) {
129
- lines.push("* No action required. Layout upgrades are safe to proceed.");
130
- } else {
131
- const uniqueRecommendations = new Set<string>();
132
- for (const f of report.findings) {
133
- const intel = createUpgradeIntelligenceItem(f);
134
- uniqueRecommendations.add(intel.recommendation);
135
- }
136
- for (const rec of uniqueRecommendations) {
137
- lines.push(`* ${rec}`);
138
- }
139
- }
140
- lines.push("");
141
-
142
- return lines.join("\n");
143
- }
@@ -1,107 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { test } from "node:test";
3
- import { generateCompactMarkdownReport } from "../dist/report.js";
4
-
5
- // Helper to construct a mock config
6
- const createMockConfig = (failOnSeverity = "MAJOR") => ({
7
- compareMode: "ast",
8
- failOnSeverity,
9
- excludePaths: [],
10
- enforcePadding: false,
11
- programs: new Map([
12
- ["marginfi", {
13
- name: "marginfi",
14
- absolutePath: "/workspace/programs/marginfi",
15
- programId: "MFv2...",
16
- overrides: [
17
- {
18
- account: "Bank",
19
- finding: "PADDING_REPURPOSE",
20
- field: "reserved",
21
- action: "allow",
22
- note: "Replaced padding safely.",
23
- used: true
24
- }
25
- ]
26
- }]
27
- ])
28
- });
29
-
30
- test("report: generates approved status banner when clean", () => {
31
- const report = {
32
- oldProgramPath: "/old",
33
- newProgramPath: "/new",
34
- severity: "SAFE",
35
- findings: []
36
- };
37
- const config = createMockConfig();
38
-
39
- const md = generateCompactMarkdownReport(report, config, false);
40
- assert.match(md, /🟢 EPIC Guard: APPROVED/);
41
- assert.match(md, /Upgrade checks approved/);
42
- assert.match(md, /No structural changes/);
43
- assert.match(md, /No action required/);
44
- });
45
-
46
- test("report: generates overrides active banner when warnings are muted", () => {
47
- const report = {
48
- oldProgramPath: "/old",
49
- newProgramPath: "/new",
50
- severity: "SAFE",
51
- findings: [
52
- {
53
- severity: "SAFE", // Overridden from CRITICAL
54
- account: "Bank",
55
- kind: "PADDING_REPURPOSE",
56
- field: { name: "reserved" },
57
- oldSize: 100,
58
- newSize: 100
59
- }
60
- ]
61
- };
62
- const config = createMockConfig();
63
-
64
- const md = generateCompactMarkdownReport(report, config, false);
65
- assert.match(md, /🟡 EPIC Guard: APPROVED WITH OVERRIDES/);
66
- assert.match(md, /Applied Layout Overrides/);
67
- assert.match(md, /Replaced padding safely/);
68
- assert.match(md, /`CRITICAL` ──► `SAFE`/);
69
- });
70
-
71
- test("report: generates blocked status banner when threshold is exceeded", () => {
72
- const report = {
73
- oldProgramPath: "/old",
74
- newProgramPath: "/new",
75
- severity: "MAJOR",
76
- findings: [
77
- {
78
- severity: "MAJOR", // Not overridden
79
- account: "Bank",
80
- kind: "FIELD_ADDED",
81
- field: { name: "maker_rebate" },
82
- oldSize: 100,
83
- newSize: 108
84
- }
85
- ]
86
- };
87
- const config = createMockConfig("MAJOR"); // Will block since report severity is MAJOR
88
-
89
- const md = generateCompactMarkdownReport(report, config, false);
90
- assert.match(md, /🔴 EPIC Guard: UPGRADE BLOCKED/);
91
- assert.match(md, /Upgrade checks failed because layout changes exceed/);
92
- });
93
-
94
- test("report: injects warning banner when epic.toml changed", () => {
95
- const report = {
96
- oldProgramPath: "/old",
97
- newProgramPath: "/new",
98
- severity: "SAFE",
99
- findings: []
100
- };
101
- const config = createMockConfig();
102
-
103
- const md = generateCompactMarkdownReport(report, config, true); // configChanged = true
104
- assert.match(md, /🟢 EPIC Guard: APPROVED/);
105
- assert.match(md, /UPGRADE CONFIGURATION GATE MODIFIED/);
106
- assert.match(md, /This Pull Request contains changes to `epic.toml`/);
107
- });