Require release versions for script uploads
CI / checks (push) Successful in 5m26s

This commit is contained in:
2026-10-12 18:46:57 -04:00
parent ffda04f276
commit 2f52dcb1ef
50 changed files with 871 additions and 113 deletions
+41 -19
View File
@@ -1,3 +1,4 @@
import { scriptVersionError } from '../../script-version.js';
import { putFileBlob, blobContent } from '../db/file-blobs.js';
import { createHash } from 'node:crypto';
import { db } from '../db/index.js';
@@ -73,6 +74,7 @@ export class ScriptError extends Error {
| 'baseline-exists'
| 'empty-overlay'
| 'invalid-release-version'
| 'release-version-conflict'
| 'too-large'
) {
super(message);
@@ -95,6 +97,14 @@ function normalizeReleaseVersion(value: string | null | undefined): string | nul
return value.trim() || null;
}
function releaseForKind(kind: ArtifactKind, value: string | null | undefined): string | null {
if (kind === 'script') {
const error = scriptVersionError(value);
if (error) throw new ScriptError(error, 'invalid-release-version');
}
return normalizeReleaseVersion(value);
}
export function getScript(id: number): ScriptRow | undefined {
return db().prepare('SELECT * FROM scripts WHERE id = ?').get(id) as ScriptRow | undefined;
}
@@ -273,7 +283,7 @@ export function createScript(input: CreateScriptInput): {
throw new ScriptError(`script "${input.name}" already exists`, 'name-taken');
const sha = sha256(input.content);
const releaseVersion = normalizeReleaseVersion(input.releaseVersion);
const releaseVersion = releaseForKind(kind, input.releaseVersion);
const size = input.content.byteLength;
const insertScript = db().prepare(
@@ -335,27 +345,39 @@ export function uploadNewVersion(
}
}
const latest = getLatestVersion(scriptId);
const nextVersion = (latest?.version ?? 0) + 1;
const release = releaseForKind(script.kind, releaseVersion);
const sha = sha256(content);
const release = normalizeReleaseVersion(releaseVersion);
return db().transaction(() => {
if (script.kind === 'script') {
const existing = listVersions(scriptId).filter((v) => v.release_version === release);
if (existing.some((v) => v.sha256 !== sha)) {
throw new ScriptError(
`Release ${release} already has different contents. Bump the script release version.`,
'release-version-conflict'
);
}
if (existing.length) return existing[0];
}
const latest = getLatestVersion(scriptId);
const nextVersion = (latest?.version ?? 0) + 1;
const res = db()
.prepare(
`INSERT INTO script_versions (script_id, version, sha256, size, content_hash, notes, kind, release_version)
const res = db()
.prepare(
`INSERT INTO script_versions (script_id, version, sha256, size, content_hash, notes, kind, release_version)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
)
.run(
scriptId,
nextVersion,
sha,
content.byteLength,
putFileBlob(content),
notes ?? null,
script.kind,
release
);
return getVersion(Number(res.lastInsertRowid))!;
)
.run(
scriptId,
nextVersion,
sha,
content.byteLength,
putFileBlob(content),
notes ?? null,
script.kind,
release
);
return getVersion(Number(res.lastInsertRowid))!;
})();
}
function validateScope(scopeType: ScopeType | null, scopeValue: string | null): void {