mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 00:58:17 +08:00
138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import { createHash } from 'crypto';
|
|
import type {
|
|
ExecutionCommand,
|
|
ExecutionResourcePolicy,
|
|
ExecutionSpec,
|
|
} from './execution';
|
|
import { cloneExecutionSpec } from './executionSpec';
|
|
import {
|
|
assertPinnedTaskExecutionRevision,
|
|
type PinnedTaskExecutionRevision,
|
|
type TaskExecutionSpecTemplate,
|
|
} from './taskExecutionRevision';
|
|
|
|
export interface PinnedTaskExecutionRevisionRecord
|
|
extends PinnedTaskExecutionRevision {
|
|
contentDigest: string;
|
|
createdAtMs: number;
|
|
}
|
|
|
|
export class TaskExecutionRevisionCorruptError extends Error {
|
|
constructor(message: string) {
|
|
super(message);
|
|
this.name = 'TaskExecutionRevisionCorruptError';
|
|
}
|
|
}
|
|
|
|
function frozenCommand(command: ExecutionCommand): ExecutionCommand {
|
|
return command.kind === 'argv'
|
|
? Object.freeze({
|
|
kind: 'argv' as const,
|
|
file: command.file,
|
|
args: Object.freeze([...command.args]),
|
|
})
|
|
: Object.freeze({
|
|
kind: 'shell' as const,
|
|
command: command.command,
|
|
...(command.shell === undefined ? {} : { shell: command.shell }),
|
|
});
|
|
}
|
|
|
|
function frozenResourcePolicy(
|
|
policy: ExecutionResourcePolicy | undefined,
|
|
): ExecutionResourcePolicy | undefined {
|
|
if (policy === undefined) return undefined;
|
|
return Object.freeze({
|
|
...(policy.memoryBytes === undefined
|
|
? {}
|
|
: { memoryBytes: Object.freeze({ ...policy.memoryBytes }) }),
|
|
...(policy.cpuMillisPerSecond === undefined
|
|
? {}
|
|
: {
|
|
cpuMillisPerSecond: Object.freeze({
|
|
...policy.cpuMillisPerSecond,
|
|
}),
|
|
}),
|
|
...(policy.filesystemIsolation === undefined
|
|
? {}
|
|
: { filesystemIsolation: policy.filesystemIsolation }),
|
|
...(policy.networkIsolation === undefined
|
|
? {}
|
|
: { networkIsolation: policy.networkIsolation }),
|
|
});
|
|
}
|
|
|
|
function normalizedTemplate(
|
|
revision: PinnedTaskExecutionRevision,
|
|
): TaskExecutionSpecTemplate {
|
|
const spec: ExecutionSpec = cloneExecutionSpec({
|
|
runId: 'task-revision-normalization-run',
|
|
attemptId: 'task-revision-normalization-attempt',
|
|
projectId: revision.projectId,
|
|
taskId: revision.taskId,
|
|
taskRevision: revision.taskRevision,
|
|
command: revision.execution.command,
|
|
...(revision.execution.workingDirectory === undefined
|
|
? {}
|
|
: { workingDirectory: revision.execution.workingDirectory }),
|
|
environmentPolicy: revision.execution.environmentPolicy,
|
|
...(revision.execution.timeoutMs === undefined
|
|
? {}
|
|
: { timeoutMs: revision.execution.timeoutMs }),
|
|
terminationGraceMs: revision.execution.terminationGraceMs,
|
|
...(revision.execution.resourcePolicy === undefined
|
|
? {}
|
|
: { resourcePolicy: revision.execution.resourcePolicy }),
|
|
});
|
|
const resourcePolicy = frozenResourcePolicy(spec.resourcePolicy);
|
|
return Object.freeze({
|
|
command: frozenCommand(spec.command),
|
|
...(spec.workingDirectory === undefined
|
|
? {}
|
|
: { workingDirectory: spec.workingDirectory }),
|
|
environmentPolicy: spec.environmentPolicy,
|
|
...(spec.timeoutMs === undefined ? {} : { timeoutMs: spec.timeoutMs }),
|
|
terminationGraceMs: spec.terminationGraceMs,
|
|
...(resourcePolicy === undefined ? {} : { resourcePolicy }),
|
|
});
|
|
}
|
|
|
|
/** Removes unknown fields, deep-clones mutable values and freezes the result. */
|
|
export function normalizePinnedTaskExecutionRevision(
|
|
revision: PinnedTaskExecutionRevision,
|
|
): PinnedTaskExecutionRevision {
|
|
assertPinnedTaskExecutionRevision(revision);
|
|
return Object.freeze({
|
|
projectId: revision.projectId,
|
|
taskId: revision.taskId,
|
|
taskRevision: revision.taskRevision,
|
|
executorType: revision.executorType,
|
|
execution: normalizedTemplate(revision),
|
|
contextRef: revision.contextRef,
|
|
});
|
|
}
|
|
|
|
export function taskExecutionRevisionDigest(
|
|
revision: PinnedTaskExecutionRevision,
|
|
): string {
|
|
const normalized = normalizePinnedTaskExecutionRevision(revision);
|
|
return createHash('sha256')
|
|
.update(JSON.stringify(normalized), 'utf8')
|
|
.digest('hex');
|
|
}
|
|
|
|
export function createPinnedTaskExecutionRevisionRecord(
|
|
revision: PinnedTaskExecutionRevision,
|
|
createdAtMs: number,
|
|
): PinnedTaskExecutionRevisionRecord {
|
|
if (!Number.isSafeInteger(createdAtMs) || createdAtMs < 0) {
|
|
throw new RangeError('createdAtMs must be a non-negative safe integer');
|
|
}
|
|
const normalized = normalizePinnedTaskExecutionRevision(revision);
|
|
return Object.freeze({
|
|
...normalized,
|
|
contentDigest: taskExecutionRevisionDigest(normalized),
|
|
createdAtMs,
|
|
});
|
|
}
|