@@ -19,6 +19,7 @@ const parseInputs = () => {
1919 rootIssue,
2020 accessToken: "",
2121 sectionTitle: "Spec graph",
22+ dryRun: false
2223 };
2324};
2425exports.parseInputs = parseInputs;
@@ -96,8 +97,8 @@ class GraphBuilder {
9697 getGraph() {
9798 const graphNodes = Array.from(this.nodes.values());
9899 const vertices = graphNodes.map(x => x.value).filter((x) => x !== null);
99- const startNode = mermaid_node_1.MermaidNode.getStartNode ();
100- const finishNode = mermaid_node_1.MermaidNode.getFinishNode ();
100+ const startNode = mermaid_node_1.MermaidNode.createStartNode ();
101+ const finishNode = mermaid_node_1.MermaidNode.createFinishNode ();
101102 const edgesFromStartNode = graphNodes
102103 .filter(x => x.predecessors.length === 0)
103104 .map(x => ({ from: startNode, to: x.value }));
@@ -130,49 +131,58 @@ exports.IssueContentParser = void 0;
130131const utils_1 = __nccwpck_require__(918);
131132class IssueContentParser {
132133 extractIssueTasklist(issue) {
133- var _a;
134- const issueContent = (_a = issue.body) !== null && _a !== void 0 ? _a : "";
135- const issueContentLines = issueContent.split("\n");
136- return issueContentLines
137- .filter(x => x.startsWith("- [ ] "))
134+ var _a, _b;
135+ const contentLines = (_b = (_a = issue.body) === null || _a === void 0 ? void 0 : _a.split("\n")) !== null && _b !== void 0 ? _b : [];
136+ return contentLines
137+ .filter(x => this.isTaskListLine(x))
138138 .map(x => (0, utils_1.parseIssueUrl)(x))
139139 .filter((x) => x !== null);
140140 }
141141 extractIssueDependencies(issue) {
142- var _a;
143- const issueContent = (_a = issue.body) !== null && _a !== void 0 ? _a : "";
144- const issueContentLines = issueContent.split("\n");
145- return issueContentLines
146- .filter(x => x.startsWith("Depends on"))
147- .map(x => x.split(",").map(y => (0, utils_1.parseIssueUrl)(y)))
142+ var _a, _b;
143+ const contentLines = (_b = (_a = issue.body) === null || _a === void 0 ? void 0 : _a.split("\n")) !== null && _b !== void 0 ? _b : [];
144+ return contentLines
145+ .filter(x => this.isDependencyLine(x))
146+ .map(x => (0, utils_1.parseIssuesUrls)(x))
148147 .flat()
149148 .filter((x) => x !== null);
150149 }
151150 replaceIssueContent(issue, sectionTitle, newSectionContent) {
152- var _a;
153- const content = (_a = issue.body) !== null && _a !== void 0 ? _a : "";
154- const contentLines = content.split("\n");
155- const sectionStartIndex = contentLines.findIndex(line => this.isLineMarkdownHeader(line, sectionTitle));
151+ var _a, _b;
152+ const contentLines = (_b = (_a = issue.body) === null || _a === void 0 ? void 0 : _a.split("\n")) !== null && _b !== void 0 ? _b : [];
153+ const sectionStartIndex = contentLines.findIndex(x => this.isMarkdownHeaderLine(x, sectionTitle));
156154 if (sectionStartIndex === -1) {
157- throw "" ;
155+ throw `Markdown header '${sectionTitle}' is not found in issue body:\n ${issue.body}` ;
158156 }
159- const sectionEndIndex = contentLines.findIndex((line, lineIndex ) => lineIndex > sectionStartIndex && this.isLineMarkdownHeader(line ));
157+ const sectionEndIndex = contentLines.findIndex((x, index ) => index > sectionStartIndex && this.isMarkdownHeaderLine(x ));
160158 return [
161- ...contentLines.slice(0, sectionStartIndex),
159+ ...contentLines.slice(0, sectionStartIndex + 1 ),
162160 newSectionContent,
163- ...contentLines.slice(sectionEndIndex),
161+ "",
162+ ...contentLines.slice(sectionEndIndex !== -1 ? sectionEndIndex : contentLines.length),
164163 ].join("\n");
165164 }
166- isLineMarkdownHeader(line, sectionTitle) {
167- if (!line.startsWith("#")) {
165+ isMarkdownHeaderLine(str, sectionTitle) {
166+ if (!str.startsWith("#")) {
167+ return false;
168+ }
169+ const trimmedLine = str.replace(/^#+/, "").trim();
170+ if (!trimmedLine) {
168171 return false;
169172 }
170173 if (!sectionTitle) {
171174 return true;
172175 }
173- const trimmedLine = line.replace(/^#+/, "").trim();
174176 return trimmedLine.toLowerCase() === sectionTitle.toLocaleLowerCase();
175177 }
178+ isTaskListLine(str) {
179+ return str.startsWith("- [ ] ");
180+ }
181+ isDependencyLine(str) {
182+ const dependencyLinePrefixes = ["Dependencies: ", "Predecessors: ", "Depends on ", "Depends on: "];
183+ const formattedLine = str.toLowerCase();
184+ return dependencyLinePrefixes.some(x => formattedLine.startsWith(x.toLowerCase()));
185+ }
176186}
177187exports.IssueContentParser = IssueContentParser;
178188
@@ -223,19 +233,32 @@ const run = async () => {
223233 const mermaidRender = new mermaid_render_1.MermaidRender();
224234 const rootIssue = await githubApiClient.getIssue(config.rootIssue);
225235 const rootIssueTasklist = issueContentParser.extractIssueTasklist(rootIssue);
236+ core.info(`Found ${rootIssueTasklist.length} work items in task list.`);
237+ core.info("Building dependency graph...");
226238 const graphBuilder = new graph_builder_1.GraphBuilder();
227239 for (const issueRef of rootIssueTasklist) {
228240 const issue = await githubApiClient.getIssue(issueRef);
229- const issueDetails = mermaid_node_1.MermaidNode.fromGitHubIssue(issue);
230- graphBuilder.addIssue(issueRef, issueDetails);
241+ const issueDetails = mermaid_node_1.MermaidNode.createFromGitHubIssue(issue);
231242 const issueDependencies = issueContentParser.extractIssueDependencies(issue);
243+ graphBuilder.addIssue(issueRef, issueDetails);
232244 issueDependencies.forEach(x => graphBuilder.addDependency(x, issueRef));
233245 }
234246 const graph = graphBuilder.getGraph();
235247 const renderedContent = mermaidRender.render(graph);
236- console.log(renderedContent);
248+ core.startGroup("Mermaid diagram");
249+ core.info(renderedContent);
250+ core.endGroup();
237251 const updatedIssueContent = issueContentParser.replaceIssueContent(rootIssue, config.sectionTitle, renderedContent);
252+ core.startGroup("Updated issue content");
253+ core.info(updatedIssueContent);
254+ core.endGroup();
255+ if (config.dryRun) {
256+ console.log("Action is run in dry-run mode. Root issue won't be updated");
257+ return;
258+ }
259+ core.info("Updating root issue...");
238260 await githubApiClient.updateIssueContent(config.rootIssue, updatedIssueContent);
261+ core.info("Root issue is updated.");
239262 }
240263 catch (error) {
241264 if (error instanceof Error) {
@@ -263,7 +286,7 @@ class MermaidNode {
263286 this.status = status;
264287 this.url = url;
265288 }
266- static fromGitHubIssue (issue) {
289+ static createFromGitHubIssue (issue) {
267290 return new MermaidNode(`issue${issue.id}`, issue.title, MermaidNode.getNodeStatusFromGitHubIssue(issue), issue.html_url);
268291 }
269292 static getNodeStatusFromGitHubIssue(issue) {
@@ -275,10 +298,10 @@ class MermaidNode {
275298 }
276299 return "notstarted";
277300 }
278- static getStartNode () {
301+ static createStartNode () {
279302 return new MermaidNode("start", "Start", "notstarted");
280303 }
281- static getFinishNode () {
304+ static createFinishNode () {
282305 return new MermaidNode("finish", "Finish", "notstarted");
283306 }
284307}
@@ -304,7 +327,7 @@ ${this.renderLegendSection()}
304327${this.renderIssuesSection(graph.vertices)}
305328${this.renderDependencies(graph.edges)}
306329\`\`\`
307- `;
330+ `;
308331 }
309332 renderCssSection() {
310333 return `
@@ -315,7 +338,7 @@ classDef started fill:#fae17d,color:#000;
315338classDef completed fill:#ccffd8,color:#000;
316339
317340%% </CSS>
318- `;
341+ `;
319342 }
320343 renderLegendSection() {
321344 return `
@@ -330,7 +353,7 @@ subgraph legend["Legend"]
330353end
331354
332355%% </Legend>
333- `;
356+ `;
334357 }
335358 renderIssuesSection(issues) {
336359 const renderedGraphIssues = issues.map(x => this.renderIssue(x)).join("\n\n");
340363${renderedGraphIssues}
341364
342365%% </Issues>
343- `;
366+ `;
344367 }
345368 renderIssue(issue) {
346369 let result = `${issue.nodeId}("${issue.title}"):::${issue.status};`;
@@ -357,7 +380,7 @@ ${renderedGraphIssues}
357380${renderedDependencies}
358381
359382%% </Dependencies>
360- `;
383+ `;
361384 }
362385 renderDependency(dependency) {
363386 return `${dependency.from.nodeId} --> ${dependency.to.nodeId};`;
@@ -374,10 +397,11 @@ exports.MermaidRender = MermaidRender;
374397"use strict";
375398
376399Object.defineProperty(exports, "__esModule", ({ value: true }));
377- exports.parseIssueUrl = void 0;
400+ exports.parseIssuesUrls = exports. parseIssueUrl = void 0;
378401const issueUrlRegex = /github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)/i;
379- const parseIssueUrl = (issueUrl) => {
380- const found = issueUrl.match(issueUrlRegex);
402+ const issueUrlsRegex = new RegExp(issueUrlRegex, "ig");
403+ const parseIssueUrl = (str) => {
404+ const found = str.match(issueUrlRegex);
381405 if (!found) {
382406 return null;
383407 }
@@ -388,6 +412,18 @@ const parseIssueUrl = (issueUrl) => {
388412 };
389413};
390414exports.parseIssueUrl = parseIssueUrl;
415+ const parseIssuesUrls = (str) => {
416+ const result = [];
417+ for (const match of str.matchAll(issueUrlsRegex)) {
418+ result.push({
419+ repoOwner: match[1],
420+ repoName: match[2],
421+ issueNumber: parseInt(match[3]),
422+ });
423+ }
424+ return result;
425+ };
426+ exports.parseIssuesUrls = parseIssuesUrls;
391427
392428
393429/***/ }),
0 commit comments