|
5 | 5 | parsers, |
6 | 6 | reusable, |
7 | 7 | Workflow, |
| 8 | + WorkflowJob, |
8 | 9 | } from '@circleci/circleci-config-sdk'; |
9 | 10 | import { CustomCommand } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Commands/exports/Reusable'; |
10 | 11 | import { CustomParameter } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters'; |
@@ -372,48 +373,86 @@ const Actions: StoreActions = { |
372 | 373 | commands: config.commands || [], |
373 | 374 | }; |
374 | 375 |
|
| 376 | + const nodeWidth = 250; // Make this dynamic |
| 377 | + const nodeHeight = 60; // Make this dynamic |
| 378 | + |
375 | 379 | state.workflows = config.workflows.map(({ name, jobs }) => { |
376 | | - const nodeWidth = 200; // Make this dynamic |
| 380 | + const jobTable: Record<string, WorkflowJob> = {}; |
| 381 | + |
| 382 | + jobs.forEach((workflowJob) => { |
| 383 | + const jobName = workflowJob.parameters.name || workflowJob.job.name; |
| 384 | + jobTable[jobName] = workflowJob; |
| 385 | + }); |
| 386 | + |
377 | 387 | const elements: Elements = []; |
378 | | - const requireTable: Record<ElementId, ElementId[]> = {}; |
| 388 | + const columns: Array<number> = []; |
| 389 | + const solved: Record<ElementId, number> = {}; |
| 390 | + |
| 391 | + const solve = (workflowJob: WorkflowJob) => { |
| 392 | + const jobName = workflowJob.parameters.name || workflowJob.job.name; |
| 393 | + |
| 394 | + if (solved[jobName] !== undefined) { |
| 395 | + return solved[jobName]; |
| 396 | + } |
| 397 | + |
| 398 | + let column = 0; |
| 399 | + |
| 400 | + if (workflowJob.parameters.requires) { |
| 401 | + let greatestColumn = 0; |
| 402 | + |
| 403 | + workflowJob.parameters.requires.forEach((requiredJob) => { |
| 404 | + let requiredJobColumn = 0; |
| 405 | + |
| 406 | + if (solved[requiredJob] === undefined) { |
| 407 | + requiredJobColumn = solve(jobTable[requiredJob]); |
| 408 | + } else { |
| 409 | + requiredJobColumn = solved[requiredJob]; |
| 410 | + } |
| 411 | + |
| 412 | + greatestColumn = Math.max(greatestColumn, requiredJobColumn); |
| 413 | + |
| 414 | + // add connection line |
| 415 | + elements.push({ |
| 416 | + id: v4(), |
| 417 | + source: requiredJob, |
| 418 | + target: jobName, |
| 419 | + type: 'requires', |
| 420 | + sourceHandle: `${requiredJob}_source`, |
| 421 | + targetHandle: `${jobName}_target`, |
| 422 | + animated: false, |
| 423 | + style: { stroke: '#A3A3A3', strokeWidth: '2px' }, |
| 424 | + }); |
| 425 | + }); |
379 | 426 |
|
380 | | - // Build workflow and prep requirement connection generation |
381 | | - jobs.forEach((workflowJob, i) => { |
382 | | - const jobName = workflowJob.job.name; |
| 427 | + column = greatestColumn + 1; |
| 428 | + } |
| 429 | + |
| 430 | + if (columns.length > column) { |
| 431 | + columns[column]++; |
| 432 | + } else { |
| 433 | + columns.push(1); |
| 434 | + } |
383 | 435 |
|
| 436 | + const row = columns[column] * nodeHeight; |
| 437 | + |
| 438 | + // add job node |
384 | 439 | elements.push({ |
385 | 440 | id: jobName, |
386 | 441 | data: { job: workflowJob.job, parameters: workflowJob.parameters }, |
387 | 442 | connectable: true, |
388 | 443 | dragHandle: '.node', |
389 | 444 | type: 'jobs', |
390 | | - position: { x: i * nodeWidth, y: 0 }, |
| 445 | + position: { x: column * nodeWidth, y: row }, |
391 | 446 | }); |
392 | 447 |
|
393 | | - workflowJob.parameters.requires?.forEach((requiredJob) => { |
394 | | - if (!requireTable[requiredJob]) { |
395 | | - requireTable[requiredJob] = [jobName]; |
396 | | - return; |
397 | | - } |
| 448 | + solved[jobName] = column; |
398 | 449 |
|
399 | | - requireTable[requiredJob].push(jobName); |
400 | | - }); |
401 | | - }); |
| 450 | + return column; |
| 451 | + }; |
402 | 452 |
|
403 | | - // Generate connections |
404 | | - Object.entries(requireTable).forEach(([jobName, requiredBy]) => { |
405 | | - requiredBy.forEach((requiredId) => { |
406 | | - elements.push({ |
407 | | - id: v4(), |
408 | | - source: jobName, |
409 | | - target: requiredId, |
410 | | - type: 'requires', |
411 | | - sourceHandle: `${jobName}_source`, |
412 | | - targetHandle: `${requiredId}_target`, |
413 | | - animated: false, |
414 | | - style: { stroke: '#A3A3A3', strokeWidth: '2px' }, |
415 | | - }); |
416 | | - }); |
| 453 | + // Build workflow and prep requirement connection generation |
| 454 | + jobs.forEach((workflowJob) => { |
| 455 | + solve(workflowJob); |
417 | 456 | }); |
418 | 457 |
|
419 | 458 | return { |
|
0 commit comments