Spaces:
Running
Running
Deploy a623efc to Docker Space
Browse filesSource: MisonL/gpt-image-playground-customer@a623efcc28ea071e4a51a2753173829a0ee01002
public/hf-space-deploy-marker.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
{
|
| 2 |
"schema_version": 1,
|
| 3 |
-
"local_sha": "
|
| 4 |
-
"created_at": "2026-06-
|
|
|
|
| 5 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"schema_version": 1,
|
| 3 |
+
"local_sha": "a623efcc28ea071e4a51a2753173829a0ee01002",
|
| 4 |
+
"created_at": "2026-06-21T06:16:41.993Z",
|
| 5 |
+
"deploy_id": "83a9c54d-c4df-43fc-8045-97fe4feadb16"
|
| 6 |
}
|
scripts/deploy-hf-space.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
#!/usr/bin/env node
|
| 2 |
|
| 3 |
import { spawnSync } from 'node:child_process';
|
|
|
|
| 4 |
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
| 5 |
import { tmpdir } from 'node:os';
|
| 6 |
import { join } from 'node:path';
|
|
@@ -141,15 +142,34 @@ export function findRemoteDeletePaths(localFiles, remoteFiles) {
|
|
| 141 |
return [...remoteFiles].filter((filename) => !localFiles.has(filename)).sort();
|
| 142 |
}
|
| 143 |
|
| 144 |
-
export function buildDeployMarker(localSha, createdAt = new Date()) {
|
| 145 |
if (!/^[0-9a-f]{40}$/.test(String(localSha || ''))) throw new Error('localSha must be a full git commit SHA.');
|
|
|
|
|
|
|
|
|
|
| 146 |
return {
|
| 147 |
schema_version: 1,
|
| 148 |
local_sha: localSha,
|
| 149 |
-
created_at: createdAt.toISOString()
|
|
|
|
| 150 |
};
|
| 151 |
}
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
export function buildDeployMarkerRouteSource(marker) {
|
| 154 |
const markerJson = JSON.stringify(marker);
|
| 155 |
return `import { NextResponse } from 'next/server';
|
|
@@ -184,10 +204,10 @@ function readLocalGitFilesWithDeployMarker() {
|
|
| 184 |
return files;
|
| 185 |
}
|
| 186 |
|
| 187 |
-
function uploadSourceTree(sourceDir,
|
| 188 |
-
writeDeployMarker(sourceDir,
|
| 189 |
const deletePaths = findRemoteDeletePaths(readLocalGitFilesWithDeployMarker(), readRemoteFilePaths());
|
| 190 |
-
const output = runText('hf', buildUploadArgs({ sourceDir, localSha, repoSlug: readRepositorySlug(), deletePaths }));
|
| 191 |
return extractUploadCommitSha(output);
|
| 192 |
}
|
| 193 |
|
|
@@ -196,26 +216,39 @@ function readSpaceInfo() {
|
|
| 196 |
return parseJsonPayload(output, 'hf spaces info');
|
| 197 |
}
|
| 198 |
|
| 199 |
-
async function waitForRunning(spaceCommitSha,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
let lastStage = 'unknown';
|
| 201 |
let lastSha = 'unknown';
|
| 202 |
-
|
| 203 |
-
|
|
|
|
| 204 |
lastStage = info.runtime?.stage || 'unknown';
|
| 205 |
lastSha = info.sha || info.runtime?.raw?.sha || 'unknown';
|
| 206 |
-
|
| 207 |
if (lastStage === 'RUNNING' && lastSha === spaceCommitSha) {
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
}
|
| 210 |
-
await
|
| 211 |
}
|
| 212 |
-
const marker = await
|
| 213 |
return {
|
| 214 |
stage: lastStage,
|
| 215 |
sha: lastSha,
|
| 216 |
management_status: 'runtime_stage_not_running',
|
| 217 |
service_marker_verified: true,
|
| 218 |
-
warning: `Space did not reach RUNNING for ${spaceCommitSha}; last stage=${lastStage} sha=${lastSha}`,
|
| 219 |
marker
|
| 220 |
};
|
| 221 |
}
|
|
@@ -224,16 +257,9 @@ async function fetchJson(path) {
|
|
| 224 |
return fetchJsonWithTimeout(new URL(path, HF_SPACE_URL), { timeoutMs: PUBLIC_ENDPOINT_TIMEOUT_MS });
|
| 225 |
}
|
| 226 |
|
| 227 |
-
async function verifyDeployMarker(
|
| 228 |
const marker = await fetchJson(`${DEPLOY_MARKER_SERVICE_PATH}?t=${Date.now()}`);
|
| 229 |
-
|
| 230 |
-
if (marker.local_sha !== localSha) {
|
| 231 |
-
throw new Error(`deploy marker local_sha mismatch: expected ${localSha}, received ${marker.local_sha || 'missing'}.`);
|
| 232 |
-
}
|
| 233 |
-
if (typeof marker.created_at !== 'string' || !marker.created_at.trim()) {
|
| 234 |
-
throw new Error('deploy marker created_at was missing.');
|
| 235 |
-
}
|
| 236 |
-
return marker;
|
| 237 |
}
|
| 238 |
|
| 239 |
async function verifyPublicEndpoints() {
|
|
@@ -263,10 +289,11 @@ async function deploy() {
|
|
| 263 |
assertCleanGitWorktree();
|
| 264 |
runText('hf', ['auth', 'whoami']);
|
| 265 |
const localSha = runText('git', ['rev-parse', 'HEAD']);
|
|
|
|
| 266 |
const sourceDir = prepareSourceTree();
|
| 267 |
try {
|
| 268 |
-
const spaceCommitSha = uploadSourceTree(sourceDir,
|
| 269 |
-
const runtime = await waitForRunning(spaceCommitSha,
|
| 270 |
const verification = await verifyPublicEndpoints();
|
| 271 |
console.log(
|
| 272 |
JSON.stringify(
|
|
|
|
| 1 |
#!/usr/bin/env node
|
| 2 |
|
| 3 |
import { spawnSync } from 'node:child_process';
|
| 4 |
+
import { randomUUID } from 'node:crypto';
|
| 5 |
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
| 6 |
import { tmpdir } from 'node:os';
|
| 7 |
import { join } from 'node:path';
|
|
|
|
| 142 |
return [...remoteFiles].filter((filename) => !localFiles.has(filename)).sort();
|
| 143 |
}
|
| 144 |
|
| 145 |
+
export function buildDeployMarker(localSha, createdAt = new Date(), deployId = randomUUID()) {
|
| 146 |
if (!/^[0-9a-f]{40}$/.test(String(localSha || ''))) throw new Error('localSha must be a full git commit SHA.');
|
| 147 |
+
if (typeof deployId !== 'string' || !deployId.trim() || /[\r\n]/.test(deployId)) {
|
| 148 |
+
throw new Error('deployId must be a non-empty single-line string.');
|
| 149 |
+
}
|
| 150 |
return {
|
| 151 |
schema_version: 1,
|
| 152 |
local_sha: localSha,
|
| 153 |
+
created_at: createdAt.toISOString(),
|
| 154 |
+
deploy_id: deployId
|
| 155 |
};
|
| 156 |
}
|
| 157 |
|
| 158 |
+
export function assertDeployMarkerMatches(marker, expectedMarker) {
|
| 159 |
+
if (!marker || typeof marker !== 'object') throw new Error('deploy marker response was not an object.');
|
| 160 |
+
if (marker?.schema_version !== expectedMarker.schema_version) throw new Error('deploy marker schema_version mismatch.');
|
| 161 |
+
if (marker.local_sha !== expectedMarker.local_sha) {
|
| 162 |
+
throw new Error(`deploy marker local_sha mismatch: expected ${expectedMarker.local_sha}, received ${marker.local_sha || 'missing'}.`);
|
| 163 |
+
}
|
| 164 |
+
if (marker.created_at !== expectedMarker.created_at) {
|
| 165 |
+
throw new Error(`deploy marker created_at mismatch: expected ${expectedMarker.created_at}, received ${marker.created_at || 'missing'}.`);
|
| 166 |
+
}
|
| 167 |
+
if (marker.deploy_id !== expectedMarker.deploy_id) {
|
| 168 |
+
throw new Error(`deploy marker deploy_id mismatch: expected ${expectedMarker.deploy_id}, received ${marker.deploy_id || 'missing'}.`);
|
| 169 |
+
}
|
| 170 |
+
return marker;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
export function buildDeployMarkerRouteSource(marker) {
|
| 174 |
const markerJson = JSON.stringify(marker);
|
| 175 |
return `import { NextResponse } from 'next/server';
|
|
|
|
| 204 |
return files;
|
| 205 |
}
|
| 206 |
|
| 207 |
+
function uploadSourceTree(sourceDir, deployMarker) {
|
| 208 |
+
writeDeployMarker(sourceDir, deployMarker);
|
| 209 |
const deletePaths = findRemoteDeletePaths(readLocalGitFilesWithDeployMarker(), readRemoteFilePaths());
|
| 210 |
+
const output = runText('hf', buildUploadArgs({ sourceDir, localSha: deployMarker.local_sha, repoSlug: readRepositorySlug(), deletePaths }));
|
| 211 |
return extractUploadCommitSha(output);
|
| 212 |
}
|
| 213 |
|
|
|
|
| 216 |
return parseJsonPayload(output, 'hf spaces info');
|
| 217 |
}
|
| 218 |
|
| 219 |
+
export async function waitForRunning(spaceCommitSha, deployMarker, options = {}) {
|
| 220 |
+
const attempts = options.attempts || STATUS_POLL_ATTEMPTS;
|
| 221 |
+
const intervalMs = options.intervalMs ?? STATUS_POLL_INTERVAL_MS;
|
| 222 |
+
const readInfo = options.readInfo || readSpaceInfo;
|
| 223 |
+
const verifyMarker = options.verifyMarker || verifyDeployMarker;
|
| 224 |
+
const sleep = options.sleep || delay;
|
| 225 |
+
const log = options.log || console.log;
|
| 226 |
let lastStage = 'unknown';
|
| 227 |
let lastSha = 'unknown';
|
| 228 |
+
let lastMarkerError = 'unknown';
|
| 229 |
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
| 230 |
+
const info = readInfo();
|
| 231 |
lastStage = info.runtime?.stage || 'unknown';
|
| 232 |
lastSha = info.sha || info.runtime?.raw?.sha || 'unknown';
|
| 233 |
+
log(`attempt=${attempt} stage=${lastStage} sha=${lastSha}`);
|
| 234 |
if (lastStage === 'RUNNING' && lastSha === spaceCommitSha) {
|
| 235 |
+
try {
|
| 236 |
+
const marker = await verifyMarker(deployMarker);
|
| 237 |
+
return { stage: lastStage, sha: lastSha, service_marker_verified: true, marker };
|
| 238 |
+
} catch (error) {
|
| 239 |
+
lastMarkerError = error instanceof Error ? error.message : String(error);
|
| 240 |
+
log(`attempt=${attempt} marker_status=not_ready error=${lastMarkerError}`);
|
| 241 |
+
}
|
| 242 |
}
|
| 243 |
+
await sleep(intervalMs);
|
| 244 |
}
|
| 245 |
+
const marker = await verifyMarker(deployMarker);
|
| 246 |
return {
|
| 247 |
stage: lastStage,
|
| 248 |
sha: lastSha,
|
| 249 |
management_status: 'runtime_stage_not_running',
|
| 250 |
service_marker_verified: true,
|
| 251 |
+
warning: `Space did not reach RUNNING with a matching service marker for ${spaceCommitSha}; last stage=${lastStage} sha=${lastSha} marker_error=${lastMarkerError}`,
|
| 252 |
marker
|
| 253 |
};
|
| 254 |
}
|
|
|
|
| 257 |
return fetchJsonWithTimeout(new URL(path, HF_SPACE_URL), { timeoutMs: PUBLIC_ENDPOINT_TIMEOUT_MS });
|
| 258 |
}
|
| 259 |
|
| 260 |
+
async function verifyDeployMarker(expectedMarker) {
|
| 261 |
const marker = await fetchJson(`${DEPLOY_MARKER_SERVICE_PATH}?t=${Date.now()}`);
|
| 262 |
+
return assertDeployMarkerMatches(marker, expectedMarker);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
}
|
| 264 |
|
| 265 |
async function verifyPublicEndpoints() {
|
|
|
|
| 289 |
assertCleanGitWorktree();
|
| 290 |
runText('hf', ['auth', 'whoami']);
|
| 291 |
const localSha = runText('git', ['rev-parse', 'HEAD']);
|
| 292 |
+
const deployMarker = buildDeployMarker(localSha);
|
| 293 |
const sourceDir = prepareSourceTree();
|
| 294 |
try {
|
| 295 |
+
const spaceCommitSha = uploadSourceTree(sourceDir, deployMarker);
|
| 296 |
+
const runtime = await waitForRunning(spaceCommitSha, deployMarker);
|
| 297 |
const verification = await verifyPublicEndpoints();
|
| 298 |
console.log(
|
| 299 |
JSON.stringify(
|
scripts/deploy-hf-space.test.mjs
CHANGED
|
@@ -3,12 +3,14 @@ import { describe, it } from 'node:test';
|
|
| 3 |
|
| 4 |
import {
|
| 5 |
GIT_ARCHIVE_MAX_BUFFER_BYTES,
|
|
|
|
| 6 |
buildDeployMarker,
|
| 7 |
buildDeployMarkerRouteSource,
|
| 8 |
buildUploadArgs,
|
| 9 |
extractUploadCommitSha,
|
| 10 |
findRemoteDeletePaths,
|
| 11 |
-
parseRepositorySlug
|
|
|
|
| 12 |
} from './deploy-hf-space.mjs';
|
| 13 |
|
| 14 |
describe('HF Space deploy script', () => {
|
|
@@ -129,28 +131,121 @@ describe('HF Space deploy script', () => {
|
|
| 129 |
});
|
| 130 |
|
| 131 |
it('builds a non-secret deploy marker for service-side deployment verification', () => {
|
| 132 |
-
const marker = buildDeployMarker(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
assert.deepEqual(marker, {
|
| 135 |
schema_version: 1,
|
| 136 |
local_sha: '3333333333333333333333333333333333333333',
|
| 137 |
-
created_at: '2026-06-20T10:00:00.000Z'
|
|
|
|
| 138 |
});
|
| 139 |
assert.doesNotMatch(JSON.stringify(marker), /key|token|secret|password/i);
|
| 140 |
});
|
| 141 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
it('rejects short deploy marker commit shas before upload', () => {
|
| 143 |
assert.throws(() => buildDeployMarker('3333333'), /full git commit SHA/);
|
| 144 |
});
|
| 145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
it('builds a no-store API route for service-side deploy marker verification', () => {
|
| 147 |
-
const marker = buildDeployMarker(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
const source = buildDeployMarkerRouteSource(marker);
|
| 149 |
|
| 150 |
assert.match(source, /NextResponse\.json/);
|
| 151 |
assert.match(source, /Cache-Control/);
|
| 152 |
assert.match(source, /no-store/);
|
| 153 |
assert.match(source, /4444444444444444444444444444444444444444/);
|
|
|
|
| 154 |
assert.doesNotMatch(source, /key|token|secret|password/i);
|
| 155 |
});
|
| 156 |
|
|
|
|
| 3 |
|
| 4 |
import {
|
| 5 |
GIT_ARCHIVE_MAX_BUFFER_BYTES,
|
| 6 |
+
assertDeployMarkerMatches,
|
| 7 |
buildDeployMarker,
|
| 8 |
buildDeployMarkerRouteSource,
|
| 9 |
buildUploadArgs,
|
| 10 |
extractUploadCommitSha,
|
| 11 |
findRemoteDeletePaths,
|
| 12 |
+
parseRepositorySlug,
|
| 13 |
+
waitForRunning
|
| 14 |
} from './deploy-hf-space.mjs';
|
| 15 |
|
| 16 |
describe('HF Space deploy script', () => {
|
|
|
|
| 131 |
});
|
| 132 |
|
| 133 |
it('builds a non-secret deploy marker for service-side deployment verification', () => {
|
| 134 |
+
const marker = buildDeployMarker(
|
| 135 |
+
'3333333333333333333333333333333333333333',
|
| 136 |
+
new Date('2026-06-20T10:00:00.000Z'),
|
| 137 |
+
'deploy-333'
|
| 138 |
+
);
|
| 139 |
|
| 140 |
assert.deepEqual(marker, {
|
| 141 |
schema_version: 1,
|
| 142 |
local_sha: '3333333333333333333333333333333333333333',
|
| 143 |
+
created_at: '2026-06-20T10:00:00.000Z',
|
| 144 |
+
deploy_id: 'deploy-333'
|
| 145 |
});
|
| 146 |
assert.doesNotMatch(JSON.stringify(marker), /key|token|secret|password/i);
|
| 147 |
});
|
| 148 |
|
| 149 |
+
it('builds a unique deploy marker id when none is provided', () => {
|
| 150 |
+
const first = buildDeployMarker('3333333333333333333333333333333333333333', new Date('2026-06-20T10:00:00.000Z'));
|
| 151 |
+
const second = buildDeployMarker('3333333333333333333333333333333333333333', new Date('2026-06-20T10:00:00.000Z'));
|
| 152 |
+
|
| 153 |
+
assert.match(first.deploy_id, /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
|
| 154 |
+
assert.notEqual(first.deploy_id, second.deploy_id);
|
| 155 |
+
});
|
| 156 |
+
|
| 157 |
it('rejects short deploy marker commit shas before upload', () => {
|
| 158 |
assert.throws(() => buildDeployMarker('3333333'), /full git commit SHA/);
|
| 159 |
});
|
| 160 |
|
| 161 |
+
it('rejects unsafe deploy marker ids before upload', () => {
|
| 162 |
+
assert.throws(
|
| 163 |
+
() =>
|
| 164 |
+
buildDeployMarker(
|
| 165 |
+
'3333333333333333333333333333333333333333',
|
| 166 |
+
new Date('2026-06-20T10:00:00.000Z'),
|
| 167 |
+
'deploy\n333'
|
| 168 |
+
),
|
| 169 |
+
/deployId must be/
|
| 170 |
+
);
|
| 171 |
+
});
|
| 172 |
+
|
| 173 |
+
it('rejects stale deploy markers from an older same-commit deployment', () => {
|
| 174 |
+
const expected = buildDeployMarker(
|
| 175 |
+
'3333333333333333333333333333333333333333',
|
| 176 |
+
new Date('2026-06-20T10:00:00.000Z'),
|
| 177 |
+
'deploy-current'
|
| 178 |
+
);
|
| 179 |
+
const stale = buildDeployMarker(
|
| 180 |
+
'3333333333333333333333333333333333333333',
|
| 181 |
+
new Date('2026-06-20T09:00:00.000Z'),
|
| 182 |
+
'deploy-stale'
|
| 183 |
+
);
|
| 184 |
+
|
| 185 |
+
assert.throws(() => assertDeployMarkerMatches(stale, expected), /created_at mismatch/);
|
| 186 |
+
assert.deepEqual(assertDeployMarkerMatches(expected, expected), expected);
|
| 187 |
+
});
|
| 188 |
+
|
| 189 |
+
it('rejects deploy markers with a stale deploy id even when the source commit and timestamp match', () => {
|
| 190 |
+
const expected = buildDeployMarker(
|
| 191 |
+
'3333333333333333333333333333333333333333',
|
| 192 |
+
new Date('2026-06-20T10:00:00.000Z'),
|
| 193 |
+
'deploy-current'
|
| 194 |
+
);
|
| 195 |
+
const stale = {
|
| 196 |
+
...expected,
|
| 197 |
+
deploy_id: 'deploy-stale'
|
| 198 |
+
};
|
| 199 |
+
|
| 200 |
+
assert.throws(() => assertDeployMarkerMatches(stale, expected), /deploy_id mismatch/);
|
| 201 |
+
});
|
| 202 |
+
|
| 203 |
+
it('waits for the public service marker after management reports the new Space commit running', async () => {
|
| 204 |
+
const marker = buildDeployMarker(
|
| 205 |
+
'5555555555555555555555555555555555555555',
|
| 206 |
+
new Date('2026-06-20T12:00:00.000Z'),
|
| 207 |
+
'deploy-555'
|
| 208 |
+
);
|
| 209 |
+
let verifyCount = 0;
|
| 210 |
+
|
| 211 |
+
const runtime = await waitForRunning('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', marker, {
|
| 212 |
+
attempts: 2,
|
| 213 |
+
intervalMs: 0,
|
| 214 |
+
readInfo: () => ({
|
| 215 |
+
runtime: { stage: 'RUNNING' },
|
| 216 |
+
sha: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
| 217 |
+
}),
|
| 218 |
+
verifyMarker: async () => {
|
| 219 |
+
verifyCount += 1;
|
| 220 |
+
if (verifyCount === 1) throw new Error('deploy marker deploy_id mismatch');
|
| 221 |
+
return marker;
|
| 222 |
+
},
|
| 223 |
+
sleep: async () => {},
|
| 224 |
+
log: () => {}
|
| 225 |
+
});
|
| 226 |
+
|
| 227 |
+
assert.equal(verifyCount, 2);
|
| 228 |
+
assert.deepEqual(runtime, {
|
| 229 |
+
stage: 'RUNNING',
|
| 230 |
+
sha: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
| 231 |
+
service_marker_verified: true,
|
| 232 |
+
marker
|
| 233 |
+
});
|
| 234 |
+
});
|
| 235 |
+
|
| 236 |
it('builds a no-store API route for service-side deploy marker verification', () => {
|
| 237 |
+
const marker = buildDeployMarker(
|
| 238 |
+
'4444444444444444444444444444444444444444',
|
| 239 |
+
new Date('2026-06-20T11:00:00.000Z'),
|
| 240 |
+
'deploy-444'
|
| 241 |
+
);
|
| 242 |
const source = buildDeployMarkerRouteSource(marker);
|
| 243 |
|
| 244 |
assert.match(source, /NextResponse\.json/);
|
| 245 |
assert.match(source, /Cache-Control/);
|
| 246 |
assert.match(source, /no-store/);
|
| 247 |
assert.match(source, /4444444444444444444444444444444444444444/);
|
| 248 |
+
assert.match(source, /deploy-444/);
|
| 249 |
assert.doesNotMatch(source, /key|token|secret|password/i);
|
| 250 |
});
|
| 251 |
|
src/app/api/deploy-marker/route.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { NextResponse } from 'next/server';
|
| 2 |
|
| 3 |
-
const deployMarker = {"schema_version":1,"local_sha":"
|
| 4 |
|
| 5 |
export const dynamic = 'force-dynamic';
|
| 6 |
|
|
|
|
| 1 |
import { NextResponse } from 'next/server';
|
| 2 |
|
| 3 |
+
const deployMarker = {"schema_version":1,"local_sha":"a623efcc28ea071e4a51a2753173829a0ee01002","created_at":"2026-06-21T06:16:41.993Z","deploy_id":"83a9c54d-c4df-43fc-8045-97fe4feadb16"} as const;
|
| 4 |
|
| 5 |
export const dynamic = 'force-dynamic';
|
| 6 |
|