@kitsi/action 0.0.17 → 0.0.18

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.
Files changed (2) hide show
  1. package/dist/index.js +136 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -101569,7 +101569,7 @@ import { pathToFileURL } from "node:url";
101569
101569
  // ../core/dist/version.js
101570
101570
  var VERSION = "0.0.1";
101571
101571
  // ../core/dist/dsl/plan.js
101572
- var clonePlan = (plan, patch) => new PlanBuilder((() => {
101572
+ var clonePlan = (plan, patch) => {
101573
101573
  const next = {
101574
101574
  name: patch.name ?? plan.config.name,
101575
101575
  concurrency: patch.concurrency ?? plan.config.concurrency,
@@ -101578,6 +101578,9 @@ var clonePlan = (plan, patch) => new PlanBuilder((() => {
101578
101578
  const when = patch.when ?? plan.config.when;
101579
101579
  if (when)
101580
101580
  next.when = when;
101581
+ const maxConcurrency = patch.maxConcurrency ?? plan.config.maxConcurrency;
101582
+ if (maxConcurrency !== undefined)
101583
+ next.maxConcurrency = maxConcurrency;
101581
101584
  const matrix = patch.matrix ?? plan.config.matrix;
101582
101585
  if (matrix)
101583
101586
  next.matrix = matrix;
@@ -101587,8 +101590,8 @@ var clonePlan = (plan, patch) => new PlanBuilder((() => {
101587
101590
  const taskFactory = patch.taskFactory ?? plan.config.taskFactory;
101588
101591
  if (taskFactory)
101589
101592
  next.taskFactory = taskFactory;
101590
- return next;
101591
- })());
101593
+ return new PlanBuilder(next);
101594
+ };
101592
101595
 
101593
101596
  class PlanBuilder {
101594
101597
  config;
@@ -101600,6 +101603,8 @@ class PlanBuilder {
101600
101603
  };
101601
101604
  if (config.when)
101602
101605
  next.when = config.when;
101606
+ if (config.maxConcurrency !== undefined)
101607
+ next.maxConcurrency = config.maxConcurrency;
101603
101608
  if (config.matrix)
101604
101609
  next.matrix = config.matrix;
101605
101610
  if (config.params)
@@ -101611,6 +101616,9 @@ class PlanBuilder {
101611
101616
  when(predicate) {
101612
101617
  return clonePlan(this, { when: predicate });
101613
101618
  }
101619
+ maxConcurrency(value) {
101620
+ return clonePlan(this, { maxConcurrency: value });
101621
+ }
101614
101622
  concurrency = {
101615
101623
  group: (fn) => clonePlan(this, { concurrency: { ...this.config.concurrency, group: fn } }),
101616
101624
  cancelInProgress: (value) => clonePlan(this, { concurrency: { ...this.config.concurrency, cancelInProgress: value } })
@@ -104220,6 +104228,58 @@ class UseHandler {
104220
104228
  }
104221
104229
  }
104222
104230
 
104231
+ // ../core/dist/engine/executor/buffering-reporter.js
104232
+ class BufferingReporter {
104233
+ target;
104234
+ taskBuffers = new Map;
104235
+ enableBuffering;
104236
+ constructor(target, enableBuffering) {
104237
+ this.target = target ?? { emit: () => {} };
104238
+ this.enableBuffering = enableBuffering;
104239
+ }
104240
+ setBuffering(enabled) {
104241
+ this.enableBuffering = enabled;
104242
+ }
104243
+ emit(event, data) {
104244
+ if (!this.enableBuffering) {
104245
+ this.target.emit(event, data);
104246
+ return;
104247
+ }
104248
+ const taskName = this.extractTaskName(event, data);
104249
+ if (!taskName || event === "plan_start" || event === "plan_end") {
104250
+ this.target.emit(event, data);
104251
+ return;
104252
+ }
104253
+ if (event === "task_start") {
104254
+ this.taskBuffers.set(taskName, []);
104255
+ }
104256
+ const buffer = this.taskBuffers.get(taskName);
104257
+ if (buffer) {
104258
+ buffer.push({ event, data });
104259
+ } else {
104260
+ this.target.emit(event, data);
104261
+ }
104262
+ if (event === "task_end") {
104263
+ this.flush(taskName);
104264
+ }
104265
+ }
104266
+ extractTaskName(_event, data) {
104267
+ if (typeof data.task === "string") {
104268
+ return data.task;
104269
+ }
104270
+ return null;
104271
+ }
104272
+ flush(taskName) {
104273
+ const buffer = this.taskBuffers.get(taskName);
104274
+ if (!buffer)
104275
+ return;
104276
+ for (const bufferedEvent of buffer) {
104277
+ this.target.emit(bufferedEvent.event, bufferedEvent.data);
104278
+ }
104279
+ this.taskBuffers.delete(taskName);
104280
+ }
104281
+ }
104282
+
104223
104283
  // ../core/dist/engine/executor/command-runner.js
104224
104284
  import { spawn } from "node:child_process";
104225
104285
  class CommandRunner {
@@ -104438,6 +104498,7 @@ class PlanRunner {
104438
104498
  params;
104439
104499
  logger;
104440
104500
  failFast;
104501
+ maxConcurrencyOverride;
104441
104502
  reporter;
104442
104503
  signal;
104443
104504
  states = new Map;
@@ -104448,6 +104509,7 @@ class PlanRunner {
104448
104509
  opHandlers;
104449
104510
  cache;
104450
104511
  expandedTasks = null;
104512
+ taskPromises = new Map;
104451
104513
  constructor(plan2, opts) {
104452
104514
  this.plan = plan2;
104453
104515
  this.workspace = opts.workspace;
@@ -104456,7 +104518,8 @@ class PlanRunner {
104456
104518
  this.params = opts.params ?? {};
104457
104519
  this.logger = opts.logger;
104458
104520
  this.failFast = opts.failFast;
104459
- this.reporter = opts.reporter;
104521
+ this.maxConcurrencyOverride = opts.maxConcurrency;
104522
+ this.reporter = new BufferingReporter(opts.reporter, false);
104460
104523
  this.signal = opts.signal;
104461
104524
  this.resolver = new ValueResolver(this.states, this.params, this.secrets);
104462
104525
  this.commandRunner = new CommandRunner({
@@ -104512,15 +104575,22 @@ class PlanRunner {
104512
104575
  }
104513
104576
  const tasks = this.plan.expand();
104514
104577
  this.expandedTasks = tasks;
104578
+ const maxConcurrency = this.maxConcurrencyOverride ?? this.plan.config.maxConcurrency ?? 1;
104515
104579
  const results = {};
104516
- for (const task2 of tasks) {
104517
- this.checkSignal("plan");
104518
- const result = await this.executeTask(task2);
104519
- results[task2.config.name] = result;
104520
- if (result.status === "failed") {
104521
- if (this.failFast)
104522
- break;
104580
+ if (maxConcurrency === 1) {
104581
+ this.reporter.setBuffering(false);
104582
+ for (const task2 of tasks) {
104583
+ this.checkSignal("plan");
104584
+ const result = await this.executeTask(task2);
104585
+ results[task2.config.name] = result;
104586
+ if (result.status === "failed") {
104587
+ if (this.failFast)
104588
+ break;
104589
+ }
104523
104590
  }
104591
+ } else {
104592
+ this.reporter.setBuffering(true);
104593
+ await this.executeTasksParallel(tasks, maxConcurrency, results);
104524
104594
  }
104525
104595
  const status = Object.values(results).some((task2) => task2.status === "failed") ? "failed" : "success";
104526
104596
  this.reporter?.emit("plan_end", {
@@ -104535,6 +104605,46 @@ class PlanRunner {
104535
104605
  tasks: results
104536
104606
  };
104537
104607
  }
104608
+ async executeTasksParallel(tasks, maxConcurrency, results) {
104609
+ const limit = maxConcurrency === 0 ? Number.POSITIVE_INFINITY : maxConcurrency;
104610
+ const queue = [...tasks];
104611
+ const executing = [];
104612
+ let shouldStop = false;
104613
+ while (queue.length > 0 || executing.length > 0) {
104614
+ while (!shouldStop && executing.length < limit && queue.length > 0) {
104615
+ const task2 = queue.shift();
104616
+ if (!task2)
104617
+ break;
104618
+ const promise = (async () => {
104619
+ try {
104620
+ this.checkSignal("plan");
104621
+ const result = await this.executeTask(task2);
104622
+ results[task2.config.name] = result;
104623
+ if (result.status === "failed" && this.failFast) {
104624
+ shouldStop = true;
104625
+ }
104626
+ } catch (error2) {
104627
+ shouldStop = true;
104628
+ throw error2;
104629
+ }
104630
+ })();
104631
+ executing.push(promise);
104632
+ promise.finally(() => {
104633
+ const index = executing.indexOf(promise);
104634
+ if (index >= 0) {
104635
+ executing.splice(index, 1);
104636
+ }
104637
+ });
104638
+ }
104639
+ if (executing.length > 0) {
104640
+ await Promise.race(executing);
104641
+ }
104642
+ if (shouldStop) {
104643
+ break;
104644
+ }
104645
+ }
104646
+ await Promise.all(executing);
104647
+ }
104538
104648
  findTask(name) {
104539
104649
  const tasks = this.expandedTasks ?? this.plan.expand();
104540
104650
  return tasks.find((task2) => task2.config.name === name);
@@ -104545,12 +104655,26 @@ class PlanRunner {
104545
104655
  if (existing?.status === "success" || existing?.status === "failed") {
104546
104656
  return this.toResult(existing, undefined, this.cache.wasTaskCached(name));
104547
104657
  }
104658
+ const existingPromise = this.taskPromises.get(name);
104659
+ if (existingPromise) {
104660
+ if (this.inProgress.has(name)) {
104661
+ throw new Error(`Cycle detected while executing task "${name}"`);
104662
+ }
104663
+ return existingPromise;
104664
+ }
104548
104665
  if (this.inProgress.has(name)) {
104549
104666
  throw new Error(`Cycle detected while executing task "${name}"`);
104550
104667
  }
104668
+ const promise = this.executeTaskInternal(task2);
104669
+ this.taskPromises.set(name, promise);
104670
+ return promise;
104671
+ }
104672
+ async executeTaskInternal(task2) {
104673
+ const name = task2.config.name;
104551
104674
  this.inProgress.add(name);
104552
104675
  this.reporter?.emit("task_start", { task: name });
104553
104676
  const taskStart = Date.now();
104677
+ const existing = this.states.get(name);
104554
104678
  const state = existing ?? (() => {
104555
104679
  const next = {
104556
104680
  status: "pending",
@@ -104834,6 +104958,7 @@ var runPlan = async (registryOrPlan, planName, options = {}) => {
104834
104958
  params: options.params ?? {},
104835
104959
  logger,
104836
104960
  failFast: options.failFast ?? true,
104961
+ maxConcurrency: options.maxConcurrency,
104837
104962
  reporter: options.reporter,
104838
104963
  signal: options.signal,
104839
104964
  ...options.cache ? { cache: options.cache } : {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitsi/action",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "GitHub Action for running Kitsi pipelines",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",