Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const run = async (): Promise<Result<void, Failure[]>> => {
options.failureMode,
abort
);
const result = await executor.execute(config.value);
const result = await executor.getExecution(config.value).execute();
if (!result.ok) {
return result;
}
Expand Down
35 changes: 23 additions & 12 deletions src/execution/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,41 @@ export type FailureMode = 'no-new' | 'continue' | 'kill';
* A single execution of a specific script.
*/
export abstract class BaseExecution<T extends ScriptConfig> {
protected readonly script: T;
protected readonly executor: Executor;
protected readonly logger: Logger;
protected readonly _config: T;
protected readonly _executor: Executor;
protected readonly _logger: Logger;
private _fingerprint?: Promise<ExecutionResult>;

protected constructor(script: T, executor: Executor, logger: Logger) {
this.script = script;
this.executor = executor;
this.logger = logger;
constructor(config: T, executor: Executor, logger: Logger) {
this._config = config;
this._executor = executor;
this._logger = logger;
}

/**
* Execute this script and return its fingerprint. Cached, so safe to call
* multiple times.
*/
execute(): Promise<ExecutionResult> {
return (this._fingerprint ??= this._execute());
}

protected abstract _execute(): Promise<ExecutionResult>;

/**
* Execute all of this script's dependencies.
*/
protected async executeDependencies(): Promise<
protected async _executeDependencies(): Promise<
Result<Array<[ScriptReference, Fingerprint]>, Failure[]>
> {
// Randomize the order we execute dependencies to make it less likely for a
// user to inadvertently depend on any specific order, which could indicate
// a missing edge in the dependency graph.
shuffle(this.script.dependencies);
shuffle(this._config.dependencies);

const dependencyResults = await Promise.all(
this.script.dependencies.map((dependency) => {
return this.executor.execute(dependency.config);
this._config.dependencies.map((dependency) => {
return this._executor.getExecution(dependency.config).execute();
})
);
const results: Array<[ScriptReference, Fingerprint]> = [];
Expand All @@ -64,7 +75,7 @@ export abstract class BaseExecution<T extends ScriptConfig> {
errors.add(error);
}
} else {
results.push([this.script.dependencies[i].config, result.value]);
results.push([this._config.dependencies[i].config, result.value]);
}
}
if (errors.size > 0) {
Expand Down
20 changes: 5 additions & 15 deletions src/execution/no-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,23 @@ import {BaseExecution} from './base.js';
import {Fingerprint} from '../fingerprint.js';

import type {ExecutionResult} from './base.js';
import type {Executor} from '../executor.js';
import type {NoCommandScriptConfig} from '../config.js';
import type {Logger} from '../logging/logger.js';

/**
* Execution for a {@link NoCommandScriptConfig}.
*/
export class NoCommandScriptExecution extends BaseExecution<NoCommandScriptConfig> {
static execute(
script: NoCommandScriptConfig,
executor: Executor,
logger: Logger
): Promise<ExecutionResult> {
return new NoCommandScriptExecution(script, executor, logger)._execute();
}

private async _execute(): Promise<ExecutionResult> {
const dependencyFingerprints = await this.executeDependencies();
protected override async _execute(): Promise<ExecutionResult> {
const dependencyFingerprints = await this._executeDependencies();
if (!dependencyFingerprints.ok) {
return dependencyFingerprints;
}
const fingerprint = await Fingerprint.compute(
this.script,
this._config,
dependencyFingerprints.value
);
this.logger.log({
script: this.script,
this._logger.log({
script: this._config,
type: 'success',
reason: 'no-command',
});
Expand Down
20 changes: 7 additions & 13 deletions src/execution/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,23 @@ import {BaseExecution} from './base.js';
import {Fingerprint} from '../fingerprint.js';

import type {ExecutionResult} from './base.js';
import type {Executor} from '../executor.js';
import type {ServiceScriptConfig} from '../config.js';
import type {Logger} from '../logging/logger.js';

/**
* Execution for a {@link ServiceScriptConfig}.
*/
export class ServiceScriptExecution extends BaseExecution<ServiceScriptConfig> {
static execute(
script: ServiceScriptConfig,
executor: Executor,
logger: Logger
): Promise<ExecutionResult> {
return new ServiceScriptExecution(script, executor, logger)._execute();
}

private async _execute(): Promise<ExecutionResult> {
const dependencyFingerprints = await this.executeDependencies();
/**
* Note `execute` is a bit of a misnomer here, because we don't actually
* execute the command at this stage in the case of services.
*/
protected override async _execute(): Promise<ExecutionResult> {
const dependencyFingerprints = await this._executeDependencies();
if (!dependencyFingerprints.ok) {
return dependencyFingerprints;
}
const fingerprint = await Fingerprint.compute(
this.script,
this._config,
dependencyFingerprints.value
);
return {ok: true, value: fingerprint};
Expand Down
Loading