From fd0ba40d7320320f81b51c5dc8f3a1dc456cb659 Mon Sep 17 00:00:00 2001 From: Long Tran Date: Thu, 28 Nov 2024 09:53:10 +1100 Subject: [PATCH] fixes --- dist/index.js | 216 ++++++++++++++++++++++++++++++++++++++++++++-- package-lock.json | 13 +++ package.json | 1 + src/main.test.ts | 46 +++++++++- src/main.ts | 25 +++--- 5 files changed, 280 insertions(+), 21 deletions(-) diff --git a/dist/index.js b/dist/index.js index 8b91d61..ab90068 100644 --- a/dist/index.js +++ b/dist/index.js @@ -26871,6 +26871,208 @@ var require_graphology_traversal = __commonJS({ } }); +// node_modules/graphology-dag/has-cycle.js +var require_has_cycle = __commonJS({ + "node_modules/graphology-dag/has-cycle.js"(exports2, module2) { + var isGraph = require_is_graph(); + var WHITE = void 0; + var GREY = 0; + var BLACK = 1; + module2.exports = function hasCycle(graph) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/has-cycle: the given graph is not a valid graphology instance." + ); + if (graph.size === 0) + return false; + if (graph.selfLoopCount !== 0) + return true; + const labels = {}; + const stack = []; + function neighborCallback(neighbor) { + const neighborLabel = labels[neighbor]; + if (neighborLabel === WHITE) + stack.push(neighbor); + else if (neighborLabel === GREY) + return true; + return false; + } + return graph.someNode((node2) => { + if (labels[node2] === BLACK) + return false; + stack.push(node2); + while (stack.length !== 0) { + const current = stack[stack.length - 1]; + const currentLabel = labels[current]; + if (currentLabel !== GREY) { + labels[current] = GREY; + const shouldStop = graph.someOutboundNeighbor( + current, + neighborCallback + ); + if (shouldStop) + return true; + } else if (currentLabel === GREY) { + stack.pop(); + labels[current] = BLACK; + } + } + return false; + }); + }; + } +}); + +// node_modules/graphology-dag/will-create-cycle.js +var require_will_create_cycle = __commonJS({ + "node_modules/graphology-dag/will-create-cycle.js"(exports2, module2) { + var isGraph = require_is_graph(); + module2.exports = function willCreateCycle(graph, source, target) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/will-create-cycle: the given graph is not a valid graphology instance." + ); + source = "" + source; + target = "" + target; + if (source === target) + return true; + if (!graph.hasNode(source) || !graph.hasNode(target)) + return false; + if (graph.hasDirectedEdge(source, target)) + return false; + if (graph.hasDirectedEdge(target, source)) + return true; + const stack = graph.outNeighbors(target); + function push2(neighbor) { + stack.push(neighbor); + } + while (stack.length !== 0) { + const node2 = stack.pop(); + if (node2 === source) + return true; + graph.forEachOutNeighbor(node2, push2); + } + return false; + }; + } +}); + +// node_modules/graphology-dag/topological-sort.js +var require_topological_sort = __commonJS({ + "node_modules/graphology-dag/topological-sort.js"(exports2) { + var isGraph = require_is_graph(); + var FixedDeque = require_fixed_deque(); + function simpleInDegree(graph, node2) { + let degree = 0; + graph.forEachInNeighbor(node2, () => { + degree++; + }); + return degree; + } + function forEachNodeInTopologicalOrder(graph, callback) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/topological-sort: the given graph is not a valid graphology instance." + ); + if (graph.type === "undirected" || graph.undirectedSize !== 0) + throw new Error( + "graphology-dag/topological-sort: cannot work if graph is not directed." + ); + if (graph.order === 0) + return; + const queue = new FixedDeque(Array, graph.order); + const inDegrees = {}; + let total = 0; + graph.forEachNode((node2, attr) => { + const inDegree = graph.multi ? simpleInDegree(graph, node2) : graph.inDegree(node2); + if (inDegree === 0) { + queue.push([node2, attr, 0]); + } else { + inDegrees[node2] = inDegree; + total += inDegree; + } + }); + let currentGeneration = 0; + function neighborCallback(neighbor, attr) { + const neighborInDegree = --inDegrees[neighbor]; + total--; + if (neighborInDegree === 0) + queue.push([neighbor, attr, currentGeneration + 1]); + inDegrees[neighbor] = neighborInDegree; + } + while (queue.size !== 0) { + const [node2, attr, gen] = queue.shift(); + currentGeneration = gen; + callback(node2, attr, gen); + graph.forEachOutNeighbor(node2, neighborCallback); + } + if (total !== 0) + throw new Error( + "graphology-dag/topological-sort: given graph is not acyclic." + ); + } + function topologicalSort2(graph) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/topological-sort: the given graph is not a valid graphology instance." + ); + const sortedNodes = new Array(graph.order); + let i = 0; + forEachNodeInTopologicalOrder(graph, (node2) => { + sortedNodes[i++] = node2; + }); + return sortedNodes; + } + function forEachTopologicalGeneration(graph, callback) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/topological-generations: the given graph is not a valid graphology instance." + ); + if (graph.order === 0) + return; + let lastGenLevel = 0; + let lastGen = []; + forEachNodeInTopologicalOrder(graph, (node2, _, gen) => { + if (gen > lastGenLevel) { + callback(lastGen); + lastGenLevel = gen; + lastGen = []; + } + lastGen.push(node2); + }); + callback(lastGen); + } + function topologicalGenerations(graph) { + if (!isGraph(graph)) + throw new Error( + "graphology-dag/topological-generations: the given graph is not a valid graphology instance." + ); + const generations = []; + forEachTopologicalGeneration(graph, (generation) => { + generations.push(generation); + }); + return generations; + } + exports2.topologicalSort = topologicalSort2; + exports2.forEachNodeInTopologicalOrder = forEachNodeInTopologicalOrder; + exports2.topologicalGenerations = topologicalGenerations; + exports2.forEachTopologicalGeneration = forEachTopologicalGeneration; + } +}); + +// node_modules/graphology-dag/index.js +var require_graphology_dag = __commonJS({ + "node_modules/graphology-dag/index.js"(exports2) { + exports2.hasCycle = require_has_cycle(); + exports2.willCreateCycle = require_will_create_cycle(); + var sort = require_topological_sort(); + exports2.forEachNodeInTopologicalOrder = sort.forEachNodeInTopologicalOrder; + exports2.topologicalSort = sort.topologicalSort; + exports2.topologicalGenerations = sort.topologicalGenerations; + exports2.forEachTopologicalGeneration = sort.forEachTopologicalGeneration; + } +}); + // node_modules/extend/index.js var require_extend = __commonJS({ "node_modules/extend/index.js"(exports2, module2) { @@ -30716,6 +30918,7 @@ var core = __toESM(require_core()); var github = __toESM(require_github()); var import_graphology = __toESM(require_graphology_cjs()); var import_graphology_traversal = __toESM(require_graphology_traversal()); +var import_graphology_dag = __toESM(require_graphology_dag()); // node_modules/mdast-util-to-string/lib/index.js var emptyOptions = {}; @@ -42909,15 +43112,14 @@ async function main({ stackGraph2.dropNode(ref); } }); - return stackGraph2; + return import_graphology.DirectedGraph.from(stackGraph2.toJSON()); }; const getOutput = (graph) => { const lines = []; - graph.someNode((node2, attributes) => { - console.log(node2, attributes); - }); - (0, import_graphology_traversal.dfs)( + const rootRef = (0, import_graphology_dag.topologicalSort)(graph)[0]; + (0, import_graphology_traversal.dfsFromNode)( graph, + rootRef, (_, stackNode, depth) => { if (!stackNode.shouldPrint) return; @@ -42942,7 +43144,6 @@ async function main({ ); return lines.join("\n"); }; - const jobs = []; const stackGraph = getStackGraph(currentPullRequest); const shouldSkip = () => { const neighbors = stackGraph.neighbors(currentPullRequest.head.ref); @@ -42954,8 +43155,9 @@ async function main({ if (shouldSkip()) { return; } + const jobs = []; stackGraph.forEachNode((_, stackNode) => { - if (stackNode.type !== "pull-request" || !stackNode.shouldPrint || !stackNode.isCurrent) { + if (stackNode.type !== "pull-request" || !stackNode.shouldPrint) { return; } jobs.push(async () => { diff --git a/package-lock.json b/package-lock.json index 046f19e..1c80b02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", "graphology": "^0.25.4", + "graphology-dag": "^0.4.1", "graphology-traversal": "^0.3.1", "remark": "^15.0.1", "remark-gfm": "^4.0.0", @@ -4596,6 +4597,18 @@ "graphology-types": ">=0.24.0" } }, + "node_modules/graphology-dag": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/graphology-dag/-/graphology-dag-0.4.1.tgz", + "integrity": "sha512-3ch9oOAnHZDoT043vyg7ukmSkKJ505nFzaHaYOn0IF2PgGo5VtIavyVK4UpbIa4tli3hhGm1ZTdBsubTmaxu/w==", + "dependencies": { + "graphology-utils": "^2.4.1", + "mnemonist": "^0.39.0" + }, + "peerDependencies": { + "graphology-types": ">=0.19.0" + } + }, "node_modules/graphology-indices": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/graphology-indices/-/graphology-indices-0.17.0.tgz", diff --git a/package.json b/package.json index c9f504e..20a810a 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@actions/exec": "^1.1.1", "@actions/github": "^6.0.0", "graphology": "^0.25.4", + "graphology-dag": "^0.4.1", "graphology-traversal": "^0.3.1", "remark": "^15.0.1", "remark-gfm": "^4.0.0", diff --git a/src/main.test.ts b/src/main.test.ts index 6587973..ee304b2 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -1,5 +1,6 @@ import { describe, it, beforeEach, expect, vi } from 'vitest' -import { updateDescription } from './main' +import { main, updateDescription } from './main' +import type { Octokit } from './types' beforeEach(() => { vi.unstubAllEnvs() @@ -52,3 +53,46 @@ describe('updateDescription', () => { expect(actual).toEqual(expected) }) }) + +describe('main', () => { + it('should work', async () => { + await main({ + octokit: {} as unknown as Octokit, + currentPullRequest: { + number: 361, + head: { + ref: 'test-branch', + }, + base: { + ref: 'document-setup', + }, + state: 'open', + }, + pullRequests: [ + // { + // number: 360, + // head: { + // ref: 'document-setup', + // }, + // base: { + // ref: 'main', + // }, + // state: 'open', + // }, + { + number: 361, + head: { + ref: 'test-branch', + }, + base: { + ref: 'document-setup', + }, + state: 'open', + }, + ], + mainBranch: 'main', + perennialBranches: [], + skipSingleStacks: false, + }) + }) +}) diff --git a/src/main.ts b/src/main.ts index 88e2ffc..3946d1b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,8 @@ import * as core from '@actions/core' import * as github from '@actions/github' import { DirectedGraph } from 'graphology' -import { bfsFromNode, dfs, dfsFromNode } from 'graphology-traversal' +import { bfsFromNode,dfsFromNode } from 'graphology-traversal' +import { topologicalSort} from 'graphology-dag' import type { PullRequest, Context, StackNodeAttributes } from './types' import { remark } from './remark' @@ -97,18 +98,20 @@ export async function main({ } }) - return stackGraph + return DirectedGraph.from(stackGraph.toJSON()) } const getOutput = (graph: DirectedGraph) => { const lines: string[] = [] - graph.someNode((node, attributes) => { - console.log(node, attributes) - }) + // `dfs` is bugged and doesn't traverse in topological order. + // `dfsFromNode` does, so we'll do the topological sort ourselves + // start traversal from the root. + const rootRef = topologicalSort(graph)[0] - dfs( + dfsFromNode( graph, + rootRef, (_, stackNode, depth) => { if (!stackNode.shouldPrint) return @@ -141,8 +144,6 @@ export async function main({ return lines.join('\n') } - const jobs: Array<() => Promise> = [] - const stackGraph = getStackGraph(currentPullRequest) const shouldSkip = () => { @@ -162,12 +163,10 @@ export async function main({ return } + const jobs: Array<() => Promise> = [] + stackGraph.forEachNode((_, stackNode) => { - if ( - stackNode.type !== 'pull-request' || - !stackNode.shouldPrint || - !stackNode.isCurrent - ) { + if (stackNode.type !== 'pull-request' || !stackNode.shouldPrint) { return }