refactor!: separate runtime releases from panel source

This commit is contained in:
2026-10-14 15:05:10 +08:00
parent 20635ffaec
commit a2dd6ee27d
51 changed files with 221 additions and 2017 deletions
-15
View File
@@ -1,15 +0,0 @@
#!/bin/bash
set -Eeuo pipefail
project_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
version=${1:-}
if [[ -z $version ]]; then
version=$(npm view @deepseek-ai/dsh@latest version --registry=https://registry.npmjs.org)
fi
if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo 'Expected an exact DSH version, not a dist-tag or arbitrary build argument.' >&2
exit 2
fi
tag="dsh-web:${version}-7"
printf 'Building local image %s (no push)\n' "$tag"
docker build --build-arg "DSH_VERSION=$version" --tag "$tag" "$project_dir/image"
docker image inspect --format '{{.Id}}' "$tag"
-1
View File
@@ -23,7 +23,6 @@ run go vet ./...
run go build ./...
run npm --prefix web test
run npm --prefix web run build
run node --test image/init-profile.test.mjs
run node --check scripts/test-panel.mjs
printf '\nAll acceptance checks passed. 所有测试与检查均通过。\n'
-1
View File
@@ -12,7 +12,6 @@ const commands = [
'go build ./...',
'npm --prefix web test',
'npm --prefix web run build',
'node --test image/init-profile.test.mjs',
'node --check scripts/test-panel.mjs',
];
-33
View File
@@ -1,33 +0,0 @@
import assert from 'node:assert/strict';
import {readFileSync} from 'node:fs';
const base=process.env.TEST_PANEL_URL;
assert.ok(base && process.env.TEST_PASSWORD_FILE);
let csrf='',cookie='';
async function req(path,method='GET',body){
const r=await fetch(base+path,{method,headers:{Cookie:cookie,Origin:base,'Content-Type':'application/json','X-CSRF-Token':csrf},body:body===undefined?undefined:JSON.stringify(body)});
if(path==='/api/login')cookie=r.headers.getSetCookie().map(v=>v.split(';')[0]).join('; ');
return {status:r.status,data:await r.json()};
}
const login=await req('/api/login','POST',{username:'admin',password:readFileSync(process.env.TEST_PASSWORD_FILE,'utf8').trim()});
assert.equal(login.status,200);csrf=login.data.csrf_token;
const before=(await req('/api/instances')).data;assert.equal(before.length,1,'run with exactly one retained test instance');
const intent=await req('/api/intents','POST',{name:'Design real intent',version:'0.2.0-rc.2'});assert.equal(intent.status,202);
let created;
try {
for(let n=0;n<60;n++){
const item=(await req('/api/intents')).data.find(x=>x.id===intent.data.id);
assert.notEqual(item.status,'failed');
if(item.instance_id){created=item.instance_id;break;}
await new Promise(r=>setTimeout(r,1000));
}
assert.ok(created,'intent must create a real instance');
const list=(await req('/api/instances')).data;assert.equal(list.length,2);assert.ok(list.every(i=>i.status==='running'));
const extra=await req('/api/instances','POST',{name:'must reject third',image_id:list[0].image_id});assert.equal(extra.status,409);
assert.equal((await req('/api/intents','POST',{name:'must reject extra reservation',version:'0.2.0-rc.2'})).status,409);
console.log('PASS persisted intent -> real runtime; two running instances; third create and reservation rejected');
} finally {
if(created)assert.equal((await req('/api/instances/'+created,'DELETE')).status,200);
else await req('/api/intents/'+intent.data.id,'DELETE');
}
assert.equal((await req('/api/instances')).data.length,1);
console.log('PASS deleted only this test runtime, retained data and prior instance');
+3 -7
View File
@@ -23,16 +23,12 @@ try {
assert.equal((await request('/api/me')).data.user.username,process.env.TEST_USERNAME || 'testadmin');
await request('/api/refresh','POST');
assert.equal((await request('/api/me')).data.user.username,process.env.TEST_USERNAME || 'testadmin');
let images=(await request('/api/images')).data;
const build=(await request('/api/builds','POST',{version:process.env.TEST_FROM_VERSION || images[0]?.version || '0.2.0-rc.2'})).data;
let buildSucceeded=false;
for(let n=0;n<120;n++){let job;try{job=(await request('/api/builds/'+encodeURIComponent(build.id))).data;}catch(e){if(!(e instanceof Error))throw e;await new Promise(r=>setTimeout(r,1000));continue;}if(job?.status==='failed')throw new Error('build/validation failed: '+job.error);if(job?.status==='success'){buildSucceeded=true;break;}await new Promise(r=>setTimeout(r,1000));}
assert.ok(buildSucceeded,'submitted build/validation job did not complete before timeout');
images=(await request('/api/images')).data.filter(i=>i.status==='available');assert.ok(images.length,'validated image required');
const images=(await request('/api/images')).data.filter(i=>i.status==='available' && i.selectable===true);
assert.ok(images.length,'prepare a validated, admitted image through the admin Git release flow before running this test');
const source=images.find(i=>!process.env.TEST_FROM_VERSION || i.version===process.env.TEST_FROM_VERSION);
const target=images.find(i=>!process.env.TEST_TO_VERSION || i.version===process.env.TEST_TO_VERSION);
assert.ok(source,'source image required');assert.ok(target,'target image required');
console.log('PASS local image reuse, persistent build task and runtime verification');
console.log('PASS validated and admitted local image selection');
instance=(await request('/api/instances','POST',{name:'Integration check',image_id:source.image_id || source.id,version:source.version})).data;
await request('/api/ws/mkdir','POST',{path:testDirectory});
const form=new FormData();form.set('path',testDirectory);form.set('file',new Blob(['hello workspace']), 'hello.txt');await request('/api/ws/upload','POST',form);