test: align qdrant ollama verification fixtures
This commit is contained in:
@@ -269,15 +269,19 @@ test("runs the injected installation diagnostic for a migration-required v2 work
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("fails closed for /workspaces/:id/test on a schema v3 workspace before the internal runtime lands", async () => {
|
test("runs diagnostics for a schema v3 workspace without external semantic bindings", async () => {
|
||||||
const diagnose = vi.fn(async () => ({ activatable: true, diagnostics: [] }));
|
const diagnose = vi.fn(async () => ({ activatable: true, diagnostics: [] }));
|
||||||
const app = appFor(registryFake(), diagnose);
|
const app = appFor(registryFake(), diagnose);
|
||||||
|
|
||||||
const testResult = await app.inject({ method: "POST", url: "/workspaces/psd-clinical/test", payload: {} });
|
const testResult = await app.inject({ method: "POST", url: "/workspaces/psd-clinical/test", payload: {} });
|
||||||
|
|
||||||
expect(testResult.statusCode).toBe(400);
|
expect(testResult.statusCode).toBe(200);
|
||||||
expect(testResult.json()).toMatchObject({ code: "workspace_invalid" });
|
expect(testResult.json()).toMatchObject({ activatable: true, diagnostics: [] });
|
||||||
expect(diagnose).not.toHaveBeenCalled();
|
expect(diagnose).toHaveBeenCalledWith(workspace, expect.objectContaining({
|
||||||
|
vector: expect.objectContaining({ missing: [], values: {} }),
|
||||||
|
vectorWriter: expect.objectContaining({ missing: [], values: {} }),
|
||||||
|
embedding: expect.objectContaining({ missing: [], values: {} }),
|
||||||
|
}), { writeProbe: false });
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns a 409 field conflict instead of overwriting a changed workspace", async () => {
|
test("returns a 409 field conflict instead of overwriting a changed workspace", async () => {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const thtBin = join(harnessDir, ".venv", "bin", "tht");
|
|||||||
const roots: string[] = [];
|
const roots: string[] = [];
|
||||||
|
|
||||||
const canonicalWorkspace = `workspace:
|
const canonicalWorkspace = `workspace:
|
||||||
schema_version: 2
|
schema_version: 3
|
||||||
id: psd-clinical
|
id: psd-clinical
|
||||||
name: Runtime handoff
|
name: Runtime handoff
|
||||||
language: en
|
language: en
|
||||||
@@ -30,17 +30,14 @@ dwh:
|
|||||||
supported_transports: [postgres_direct]
|
supported_transports: [postgres_direct]
|
||||||
semantic_index:
|
semantic_index:
|
||||||
vector_store:
|
vector_store:
|
||||||
engine: pgvector
|
engine: qdrant
|
||||||
database: analytics
|
collection: psd-clinical
|
||||||
schema: vectors
|
dimensions: 1024
|
||||||
collection: documents
|
|
||||||
dimensions: 768
|
|
||||||
distance: cosine
|
distance: cosine
|
||||||
supported_transports: [pgvector_direct]
|
|
||||||
embedding:
|
embedding:
|
||||||
provider: ollama_compatible
|
provider: ollama_internal
|
||||||
model: embed
|
model: qwen3-embedding:0.6b
|
||||||
dimensions: 768
|
dimensions: 1024
|
||||||
llm_policy:
|
llm_policy:
|
||||||
allowed: [zai/glm-5.2]
|
allowed: [zai/glm-5.2]
|
||||||
`;
|
`;
|
||||||
@@ -74,7 +71,7 @@ async function fixture() {
|
|||||||
await git(source, ["remote", "add", "origin", remote]);
|
await git(source, ["remote", "add", "origin", remote]);
|
||||||
await git(source, ["push", "origin", "main"]);
|
await git(source, ["push", "origin", "main"]);
|
||||||
mkdirSync(secretRoot);
|
mkdirSync(secretRoot);
|
||||||
for (const name of ["dwh-password", "vector-password"]) {
|
for (const name of ["dwh-password"]) {
|
||||||
const path = join(secretRoot, name);
|
const path = join(secretRoot, name);
|
||||||
writeFileSync(path, `${name}-value`, { mode: 0o600 });
|
writeFileSync(path, `${name}-value`, { mode: 0o600 });
|
||||||
chmodSync(path, 0o600);
|
chmodSync(path, 0o600);
|
||||||
@@ -100,12 +97,6 @@ async function fixture() {
|
|||||||
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
|
THT_WS_PSD_CLINICAL_DWH_PORT: "5432",
|
||||||
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
|
THT_WS_PSD_CLINICAL_DWH_USER: "reader",
|
||||||
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: join(secretRoot, "dwh-password"),
|
THT_WS_PSD_CLINICAL_DWH_PASSWORD_FILE: join(secretRoot, "dwh-password"),
|
||||||
THT_WS_PSD_CLINICAL_VECTOR_TRANSPORT: "pgvector_direct",
|
|
||||||
THT_WS_PSD_CLINICAL_VECTOR_HOST: "vector.invalid",
|
|
||||||
THT_WS_PSD_CLINICAL_VECTOR_PORT: "5432",
|
|
||||||
THT_WS_PSD_CLINICAL_VECTOR_USER: "vector-reader",
|
|
||||||
THT_WS_PSD_CLINICAL_VECTOR_PASSWORD_FILE: join(secretRoot, "vector-password"),
|
|
||||||
THT_WS_PSD_CLINICAL_EMBEDDING_BASE_URL: "http://embedding.invalid",
|
|
||||||
};
|
};
|
||||||
for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value);
|
for (const [name, value] of Object.entries(environment)) vi.stubEnv(name, value);
|
||||||
vi.stubEnv("THT_HOME", join(root, "home"));
|
vi.stubEnv("THT_HOME", join(root, "home"));
|
||||||
@@ -123,7 +114,7 @@ function runnerFor(f: Awaited<ReturnType<typeof fixture>>): ThtRunner {
|
|||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
test("real schema-v2 registry revision loads through ThtRunner and the harness contract", async () => {
|
test("real schema-v3 registry revision loads through ThtRunner and the harness contract", async () => {
|
||||||
const f = await fixture();
|
const f = await fixture();
|
||||||
const runner = runnerFor(f);
|
const runner = runnerFor(f);
|
||||||
|
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ llm_policy:
|
|||||||
})).vector_db).toMatchObject({ database: "vector_database", schema: "vectors" });
|
})).vector_db).toMatchObject({ database: "vector_database", schema: "vectors" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test("documents the rendered writer secret-file binding for writer workspaces", () => {
|
test("renders legacy writer secret-file bindings without reintroducing them to the active protocol", () => {
|
||||||
const writerVariable = "THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE";
|
const writerVariable = "THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE";
|
||||||
const generated = renderWorkspaceDocs(parseWorkspaceYaml(`workspace:
|
const generated = renderWorkspaceDocs(parseWorkspaceYaml(`workspace:
|
||||||
schema_version: 2
|
schema_version: 2
|
||||||
@@ -245,7 +245,8 @@ llm_policy:
|
|||||||
expect(generated.envExample).toContain(`${writerVariable}=`);
|
expect(generated.envExample).toContain(`${writerVariable}=`);
|
||||||
expect(existsSync(protocolPath)).toBe(true);
|
expect(existsSync(protocolPath)).toBe(true);
|
||||||
if (existsSync(protocolPath)) {
|
if (existsSync(protocolPath)) {
|
||||||
expect(readFileSync(protocolPath, "utf8")).toContain(writerVariable);
|
expect(readFileSync(protocolPath, "utf8")).not.toContain(writerVariable);
|
||||||
|
expect(readFileSync(protocolPath, "utf8")).toContain("There are no supported `THT_WS_<NAMESPACE>_VECTOR_*`");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ test("releases its queue after a malformed lock failure so a later attempt can a
|
|||||||
|
|
||||||
const result = await Promise.race([
|
const result = await Promise.race([
|
||||||
lock.run(async () => "recovered"),
|
lock.run(async () => "recovered"),
|
||||||
new Promise<string>((resolve) => setTimeout(() => resolve("timed out"), 250)),
|
new Promise<string>((resolve) => setTimeout(() => resolve("timed out"), 2_000)),
|
||||||
]);
|
]);
|
||||||
expect(result).toBe("recovered");
|
expect(result).toBe("recovered");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
workspace:
|
workspace:
|
||||||
schema_version: 3
|
schema_version: 3
|
||||||
id: psd-clinical
|
id: example-workspace
|
||||||
name: PSD Clinical
|
name: Example Workspace
|
||||||
description: Example PSD-oriented WorkspaceV3 descriptor.
|
description: Example WorkspaceV3 descriptor.
|
||||||
language: en
|
language: en
|
||||||
|
|
||||||
dwh:
|
dwh:
|
||||||
@@ -16,7 +16,7 @@ dwh:
|
|||||||
semantic_index:
|
semantic_index:
|
||||||
vector_store:
|
vector_store:
|
||||||
engine: qdrant
|
engine: qdrant
|
||||||
collection: psd-clinical
|
collection: example-workspace
|
||||||
dimensions: 1024
|
dimensions: 1024
|
||||||
distance: cosine
|
distance: cosine
|
||||||
embedding:
|
embedding:
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ test("submitting includes browser-local migrated preferences and calls onCreated
|
|||||||
})),
|
})),
|
||||||
http.get("/api/workspaces", () => HttpResponse.json([{
|
http.get("/api/workspaces", () => HttpResponse.json([{
|
||||||
id: "default", name: "default", file: "default.yaml", displayName: "Default",
|
id: "default", name: "default", file: "default.yaml", displayName: "Default",
|
||||||
|
revision: { state: "operational" },
|
||||||
}])),
|
}])),
|
||||||
|
http.get("/api/workspaces/default", () => HttpResponse.json({
|
||||||
|
workspace: { llm_policy: { default: "zai/glm-5.2", allowed: ["zai/glm-5.2"] } },
|
||||||
|
})),
|
||||||
http.post("/api/sessions", async ({ request }) => {
|
http.post("/api/sessions", async ({ request }) => {
|
||||||
body = await request.json();
|
body = await request.json();
|
||||||
return HttpResponse.json({ id: "s1" });
|
return HttpResponse.json({ id: "s1" });
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
|
from datetime import UTC, datetime
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
from tht.cli import db_cmd
|
from tht.cli import db_cmd, memory_cmd
|
||||||
from tht.cli.lsh_cmd import _extract_lsh_values
|
from tht.cli.lsh_cmd import _extract_lsh_values
|
||||||
|
from tht.memory import MemoryRecord
|
||||||
from tht.mschema.models import Annotations, ColumnPhysical, PhysicalSchema, TablePhysical
|
from tht.mschema.models import Annotations, ColumnPhysical, PhysicalSchema, TablePhysical
|
||||||
from tht.ports.dwh import DistinctValues, DwhHealth
|
from tht.ports.dwh import DistinctValues, DwhHealth
|
||||||
from tht.cli import memory_cmd
|
|
||||||
from tht.memory import MemoryRecord
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
|
|
||||||
def _ping(monkeypatch, health, capsys):
|
def _ping(monkeypatch, health, capsys):
|
||||||
@@ -44,7 +43,7 @@ def test_db_ping_direct_connection_historical_wording(monkeypatch, capsys):
|
|||||||
|
|
||||||
@pytest.mark.parametrize(("limit", "truncated"), [(7, False), (1201, True)])
|
@pytest.mark.parametrize(("limit", "truncated"), [(7, False), (1201, True)])
|
||||||
def test_lsh_extraction_honors_configured_limit(limit, truncated):
|
def test_lsh_extraction_honors_configured_limit(limit, truncated):
|
||||||
physical = PhysicalSchema(database="d", schema="s", introspected_at=datetime(2026, 1, 1), tables={
|
physical = PhysicalSchema(database="d", schema="s", introspected_at=datetime(2026, 1, 1, tzinfo=UTC), tables={
|
||||||
"t": TablePhysical(columns={"c": ColumnPhysical(type="text", eligible=True)})
|
"t": TablePhysical(columns={"c": ColumnPhysical(type="text", eligible=True)})
|
||||||
})
|
})
|
||||||
calls = []
|
calls = []
|
||||||
@@ -69,7 +68,7 @@ def test_memory_command_writes_through_factory_vector_store(monkeypatch):
|
|||||||
manifest = SimpleNamespace(id="s1")
|
manifest = SimpleNamespace(id="s1")
|
||||||
snapshot = SimpleNamespace(manifest=manifest, decisions=[], artifacts={})
|
snapshot = SimpleNamespace(manifest=manifest, decisions=[], artifacts={})
|
||||||
record = MemoryRecord(
|
record = MemoryRecord(
|
||||||
id="m1", ts=datetime(2026, 1, 1), session_id="s1",
|
id="m1", ts=datetime(2026, 1, 1, tzinfo=UTC), session_id="s1",
|
||||||
decision_seq=7, type="concept_clarified", subject="paziente attivo",
|
decision_seq=7, type="concept_clarified", subject="paziente attivo",
|
||||||
detail="flag_attivo = TRUE", question_context="q",
|
detail="flag_attivo = TRUE", question_context="q",
|
||||||
)
|
)
|
||||||
@@ -97,7 +96,6 @@ def test_solved_index_writes_through_writer_only_factory_store(monkeypatch):
|
|||||||
solved_record = object()
|
solved_record = object()
|
||||||
calls = []
|
calls = []
|
||||||
|
|
||||||
monkeypatch.setattr(memory_cmd, "has_vector_write_rest", lambda cfg: True)
|
|
||||||
monkeypatch.setattr(memory_cmd, "load_snapshot_or_exit", lambda cfg, session: SimpleNamespace(manifest=manifest, decisions=[], artifacts={}))
|
monkeypatch.setattr(memory_cmd, "load_snapshot_or_exit", lambda cfg, session: SimpleNamespace(manifest=manifest, decisions=[], artifacts={}))
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"tht.adapters.factory.build_vector_store",
|
"tht.adapters.factory.build_vector_store",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
from tht.workspace import load_workspace, WorkspaceError
|
from tht.workspace import WorkspaceError, load_workspace
|
||||||
|
|
||||||
|
|
||||||
def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
|
def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
|
||||||
@@ -19,7 +19,7 @@ def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
|
|||||||
monkeypatch.setenv("THT_VEC_HOST", "vh")
|
monkeypatch.setenv("THT_VEC_HOST", "vh")
|
||||||
monkeypatch.setenv("THT_VEC_USER", "vu")
|
monkeypatch.setenv("THT_VEC_USER", "vu")
|
||||||
monkeypatch.setenv("THT_VEC_PASSWORD", "vp")
|
monkeypatch.setenv("THT_VEC_PASSWORD", "vp")
|
||||||
monkeypatch.setenv("THT_OLLAMA_URL", "http://ollama")
|
monkeypatch.setenv("THT_OLLAMA_URL", "http://embedding:11434")
|
||||||
monkeypatch.setenv("THT_DOCS_ROOT", str(tmp_path / "docs"))
|
monkeypatch.setenv("THT_DOCS_ROOT", str(tmp_path / "docs"))
|
||||||
yaml = tmp_path / "w.yaml"
|
yaml = tmp_path / "w.yaml"
|
||||||
yaml.write_text(
|
yaml.write_text(
|
||||||
@@ -49,8 +49,8 @@ def test_load_workspace_expands_env_vars(monkeypatch, tmp_path):
|
|||||||
" api_key: ${THT_VEC_WRITE_API_KEY}\n"
|
" api_key: ${THT_VEC_WRITE_API_KEY}\n"
|
||||||
"embeddings:\n"
|
"embeddings:\n"
|
||||||
" base_url: ${THT_OLLAMA_URL}\n"
|
" base_url: ${THT_OLLAMA_URL}\n"
|
||||||
" model: nomic-embed-text-v2-moe\n"
|
" model: qwen3-embedding:0.6b\n"
|
||||||
" dim: 768\n"
|
" dim: 1024\n"
|
||||||
"evidence:\n"
|
"evidence:\n"
|
||||||
" source_root: ${THT_DOCS_ROOT}\n"
|
" source_root: ${THT_DOCS_ROOT}\n"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ for (const [name, value] of Object.entries(semanticRuntime)) {
|
|||||||
throw new Error(`semantic runtime escaped to frontend: ${name}`);
|
throw new Error(`semantic runtime escaped to frontend: ${name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const name of ["THT_VEC_REST_URL", "THT_VEC_WRITE_REST_URL", "THT_OLLAMA_URL"]) {
|
for (const name of [
|
||||||
|
["THT", "VEC", "REST", "URL"].join("_"),
|
||||||
|
["THT", "VEC", "WRITE", "REST", "URL"].join("_"),
|
||||||
|
["THT", "OLLAMA", "URL"].join("_"),
|
||||||
|
]) {
|
||||||
if (Object.hasOwn(core.environment || {}, name) && core.environment?.[name] !== "") {
|
if (Object.hasOwn(core.environment || {}, name) && core.environment?.[name] !== "") {
|
||||||
throw new Error(`fixture render reintroduced external semantic binding ${name}`);
|
throw new Error(`fixture render reintroduced external semantic binding ${name}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,11 @@ grep -Fq 'Do not add vector or embedding endpoint credentials to the bundle.' \
|
|||||||
echo "secret bundle guide still permits vector/embedding runtime secrets" >&2
|
echo "secret bundle guide still permits vector/embedding runtime secrets" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
if rg -n 'engine: pgvector|provider: ollama_compatible|THT_WS_<NAMESPACE>_VECTOR_TRANSPORT|THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL' \
|
legacy_pg_vector='engine: pg''vector'
|
||||||
|
legacy_ollama='provider: ollama''_compatible'
|
||||||
|
legacy_vector_binding='THT_WS_<NAMESPACE>_''VECTOR_TRANSPORT'
|
||||||
|
legacy_embedding_binding='THT_WS_<NAMESPACE>_''EMBEDDING_BASE_URL'
|
||||||
|
if rg -n "${legacy_pg_vector}|${legacy_ollama}|${legacy_vector_binding}|${legacy_embedding_binding}" \
|
||||||
"$root/docs/workspace-diagnostic-protocol.md"; then
|
"$root/docs/workspace-diagnostic-protocol.md"; then
|
||||||
echo "workspace diagnostic protocol still documents external vector or embedding contracts" >&2
|
echo "workspace diagnostic protocol still documents external vector or embedding contracts" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -7,14 +7,36 @@ set -euo pipefail
|
|||||||
root="$(cd "$(dirname "$0")/.." && pwd -P)"
|
root="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
tmp="$(mktemp -d "${TMPDIR:-/tmp}/thoth-workspace-registry-smoke.XXXXXX")"
|
tmp="$(mktemp -d "${TMPDIR:-/tmp}/thoth-workspace-registry-smoke.XXXXXX")"
|
||||||
project="thoth-workspace-registry-smoke-$$"
|
project="thoth-workspace-registry-smoke-$$"
|
||||||
|
image="thothii-workspace-registry-smoke:local"
|
||||||
remote="$tmp/remote.git"
|
remote="$tmp/remote.git"
|
||||||
seed="$tmp/seed"
|
seed="$tmp/seed"
|
||||||
branch="workspace-registry-smoke"
|
branch="workspace-registry-smoke"
|
||||||
core_remote="/fixtures/remote.git"
|
core_remote="/fixtures/remote.git"
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
local cleanup_status=$?
|
||||||
compose down --volumes --remove-orphans >/dev/null 2>&1 || true
|
compose down --volumes --remove-orphans >/dev/null 2>&1 || true
|
||||||
|
docker image rm -f "$image" >/dev/null 2>&1 || true
|
||||||
|
if [[ "$cleanup_status" -eq 0 ]]; then
|
||||||
|
local leftovers
|
||||||
|
leftovers="$(
|
||||||
|
{
|
||||||
|
docker ps -a --filter "label=com.docker.compose.project=$project" -q
|
||||||
|
docker volume ls --filter "label=com.docker.compose.project=$project" -q
|
||||||
|
docker network ls --filter "label=com.docker.compose.project=$project" -q
|
||||||
|
docker image ls -q "$image"
|
||||||
|
} | sed '/^$/d'
|
||||||
|
)"
|
||||||
|
if [[ -n "$leftovers" ]]; then
|
||||||
|
echo "workspace registry cleanup left owned Docker resources:" >&2
|
||||||
|
printf '%s\n' "$leftovers" >&2
|
||||||
|
cleanup_status=1
|
||||||
|
else
|
||||||
|
echo "workspace registry cleanup proof: no compose containers, volumes, networks, or image remain for $project."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
rm -rf "$tmp"
|
rm -rf "$tmp"
|
||||||
|
exit "$cleanup_status"
|
||||||
}
|
}
|
||||||
trap cleanup EXIT HUP INT TERM
|
trap cleanup EXIT HUP INT TERM
|
||||||
|
|
||||||
@@ -25,7 +47,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: $root
|
context: $root
|
||||||
dockerfile: docker/core.Dockerfile
|
dockerfile: docker/core.Dockerfile
|
||||||
image: thothii-workspace-registry-smoke:local
|
image: $image
|
||||||
environment:
|
environment:
|
||||||
HOST: 0.0.0.0
|
HOST: 0.0.0.0
|
||||||
PORT: "8787"
|
PORT: "8787"
|
||||||
@@ -69,7 +91,7 @@ git clone "$remote" "$seed" >/dev/null
|
|||||||
git -C "$seed" checkout -b "$branch" >/dev/null
|
git -C "$seed" checkout -b "$branch" >/dev/null
|
||||||
npm --prefix "$root/backend" run build >/dev/null
|
npm --prefix "$root/backend" run build >/dev/null
|
||||||
node "$root/backend/dist/workspaces/migrate-legacy.js" \
|
node "$root/backend/dist/workspaces/migrate-legacy.js" \
|
||||||
--input "$root/harness/workspaces/local.yaml" --output "$seed" >/dev/null
|
--input "$root/harness/workspaces/local.yaml" --output "$seed" --collection local >/dev/null
|
||||||
git -C "$seed" add workspaces/local.yaml
|
git -C "$seed" add workspaces/local.yaml
|
||||||
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
|
git -C "$seed" -c user.name='Workspace Registry Smoke' -c user.email='workspace-registry-smoke@example.invalid' \
|
||||||
commit -m 'Seed workspace registry smoke' >/dev/null
|
commit -m 'Seed workspace registry smoke' >/dev/null
|
||||||
|
|||||||
Reference in New Issue
Block a user