fix: route claudeqq notifications through k3s
This commit is contained in:
+88
-13
@@ -94,6 +94,7 @@ const providerGatewayWsEgressProxyUrl = "http://127.0.0.1:18789";
|
||||
const nativeK3sInstallVersion = "v1.34.1+k3s1";
|
||||
const nativeK3sImage = "rancher/k3s:v1.34.1-k3s1";
|
||||
const nativeK3sCtrAddress = "/run/k3s/containerd/containerd.sock";
|
||||
const unideskRepoUrl = "https://github.com/pikasTech/unidesk";
|
||||
|
||||
function isHelpArg(value: string | undefined): boolean {
|
||||
return value === "help" || value === "--help" || value === "-h";
|
||||
@@ -180,6 +181,16 @@ function repoSlug(repo: string): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
function isFullGitSha(value: string): boolean {
|
||||
return /^[0-9a-f]{40}$/iu.test(value);
|
||||
}
|
||||
|
||||
function isUnideskRepo(repo: string): boolean {
|
||||
const desiredSlug = repoSlug(repo);
|
||||
const unideskSlug = repoSlug(unideskRepoUrl);
|
||||
return desiredSlug !== null && desiredSlug === unideskSlug;
|
||||
}
|
||||
|
||||
function sshUrlForSlug(slug: string | null): string | null {
|
||||
if (slug === null) return null;
|
||||
const [host = "", ...pathParts] = slug.split("/");
|
||||
@@ -208,7 +219,7 @@ function providerSourceRepoUrl(repo: string): string {
|
||||
|
||||
function localResolvedCommitForDesired(desired: DeployManifestService): string | null {
|
||||
const desiredSlug = repoSlug(desired.repo);
|
||||
if (desiredSlug === null || desired.commitId.length !== 40) return null;
|
||||
if (desiredSlug === null || !isFullGitSha(desired.commitId)) return null;
|
||||
const localOrigin = runCommand(["git", "remote", "get-url", "origin"], repoRoot);
|
||||
const localOriginUrl = localOrigin.exitCode === 0 ? localOrigin.stdout.trim() : "";
|
||||
if (localOriginUrl.length === 0 || repoSlug(localOriginUrl) !== desiredSlug) return null;
|
||||
@@ -235,6 +246,7 @@ function runResolveGit(args: string[], cwd: string): ReturnType<typeof runComman
|
||||
function resolveDesiredCommit(desired: DeployManifestService): DeployManifestService {
|
||||
const localCommit = localResolvedCommitForDesired(desired);
|
||||
if (localCommit !== null) return { ...desired, commitId: localCommit };
|
||||
if (isFullGitSha(desired.commitId) && !isUnideskRepo(desired.repo)) return { ...desired, commitId: desired.commitId.toLowerCase() };
|
||||
|
||||
const cacheDir = repoResolveCacheDir(desired.repo);
|
||||
mkdirSync(cacheDir, { recursive: true });
|
||||
@@ -412,12 +424,32 @@ function targetExportDir(service: UniDeskMicroserviceConfig, runId: string): str
|
||||
|
||||
function targetWorkDir(service: UniDeskMicroserviceConfig): string {
|
||||
if (service.deployment.mode === "k3sctl-managed") return k3sDeployDir;
|
||||
if (targetIsMain(service) && service.repository.url === "https://github.com/pikasTech/unidesk") {
|
||||
if (targetIsMain(service) && service.repository.url === unideskRepoUrl) {
|
||||
return rootPath(".state", "deploy", "work", safeId(service.id));
|
||||
}
|
||||
return service.development.worktreePath;
|
||||
}
|
||||
|
||||
function sourceWorkDir(service: UniDeskMicroserviceConfig): string {
|
||||
if (service.deployment.mode === "k3sctl-managed" && service.repository.url !== unideskRepoUrl) {
|
||||
return `${remoteDeployRoot}/work/${safeId(service.id)}`;
|
||||
}
|
||||
return targetWorkDir(service);
|
||||
}
|
||||
|
||||
function sourceRootSubdir(service: UniDeskMicroserviceConfig): string {
|
||||
const claudeqqPrefix = "claudeqq/";
|
||||
if (service.id === "claudeqq" && service.repository.url !== unideskRepoUrl && service.repository.dockerfile.startsWith(claudeqqPrefix)) {
|
||||
return "claudeqq";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function sourceBuildContext(service: UniDeskMicroserviceConfig): string {
|
||||
const subdir = sourceRootSubdir(service);
|
||||
return subdir.length > 0 ? `${sourceWorkDir(service)}/${subdir}` : sourceWorkDir(service);
|
||||
}
|
||||
|
||||
function buildImageTag(service: UniDeskMicroserviceConfig): string {
|
||||
if (service.deployment.mode === "k3sctl-managed") return `unidesk-${service.id}:d601`;
|
||||
if (targetIsMain(service)) {
|
||||
@@ -438,12 +470,12 @@ function directComposeEnvFile(service: UniDeskMicroserviceConfig): string {
|
||||
}
|
||||
|
||||
function directBuildContextOverride(service: UniDeskMicroserviceConfig): string {
|
||||
if (targetIsMain(service) && service.repository.url === "https://github.com/pikasTech/unidesk") return targetWorkDir(service);
|
||||
if (targetIsMain(service) && service.repository.url === unideskRepoUrl) return targetWorkDir(service);
|
||||
return "";
|
||||
}
|
||||
|
||||
function directDockerfileOverride(service: UniDeskMicroserviceConfig): string {
|
||||
if (targetIsMain(service) && service.repository.url === "https://github.com/pikasTech/unidesk") return service.repository.dockerfile;
|
||||
if (targetIsMain(service) && service.repository.url === unideskRepoUrl) return service.repository.dockerfile;
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -453,8 +485,17 @@ function k8sManifestPath(service: UniDeskMicroserviceConfig): string {
|
||||
return composeFile.replace(/\.k3s\.json$/u, ".k8s.yaml");
|
||||
}
|
||||
|
||||
function sourceDockerfilePath(service: UniDeskMicroserviceConfig): string {
|
||||
const claudeqqPrefix = "claudeqq/";
|
||||
if (service.id === "claudeqq" && service.repository.url !== unideskRepoUrl && service.repository.dockerfile.startsWith(claudeqqPrefix)) {
|
||||
return service.repository.dockerfile.slice(claudeqqPrefix.length);
|
||||
}
|
||||
return service.repository.dockerfile;
|
||||
}
|
||||
|
||||
function sourceProxyPrelude(service: UniDeskMicroserviceConfig): string {
|
||||
if (targetIsMain(service)) return "";
|
||||
const strictHostKeyChecking = isUnideskRepo(service.repository.url) ? "yes" : "accept-new";
|
||||
return [
|
||||
`build_proxy=${shellQuote(providerGatewayWsEgressProxyUrl)}`,
|
||||
"export HTTP_PROXY=\"$build_proxy\" HTTPS_PROXY=\"$build_proxy\" ALL_PROXY=\"$build_proxy\"",
|
||||
@@ -521,7 +562,7 @@ while True:
|
||||
"UNIDESK_GIT_SSH_PROXY",
|
||||
"chmod 700 \"$git_ssh_proxy\"",
|
||||
"export UNIDESK_GIT_SSH_HTTP_PROXY=\"$build_proxy\"",
|
||||
"export GIT_SSH_COMMAND=\"ssh -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$HOME/.ssh/known_hosts -i $HOME/.ssh/id_ed25519 -o 'ProxyCommand=$git_ssh_proxy %h %p'\"",
|
||||
`export GIT_SSH_COMMAND="ssh -o BatchMode=yes -o IdentitiesOnly=yes -o StrictHostKeyChecking=${strictHostKeyChecking} -o UserKnownHostsFile=$HOME/.ssh/known_hosts -i $HOME/.ssh/id_ed25519 -o 'ProxyCommand=$git_ssh_proxy %h %p'"`,
|
||||
"curl -fsSI --max-time 20 -x \"$build_proxy\" https://github.com >/dev/null",
|
||||
"echo target_source_proxy=provider-gateway-ws-egress:$build_proxy",
|
||||
"echo target_build_proxy=provider-gateway-ws-egress:$build_proxy",
|
||||
@@ -541,7 +582,7 @@ function buildCachePrelude(dockerfileVariable: string): string[] {
|
||||
}
|
||||
|
||||
function prepareSourceScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, exportDir: string): string {
|
||||
if (targetIsMain(service) && desired.repo === "https://github.com/pikasTech/unidesk") {
|
||||
if (targetIsMain(service) && desired.repo === unideskRepoUrl) {
|
||||
return [
|
||||
"set -euo pipefail",
|
||||
`repo=${shellQuote(repoRoot)}`,
|
||||
@@ -577,11 +618,15 @@ function prepareSourceScript(service: UniDeskMicroserviceConfig, desired: Deploy
|
||||
}
|
||||
|
||||
function syncSourceScript(service: UniDeskMicroserviceConfig, exportDir: string): string {
|
||||
const workDir = targetWorkDir(service);
|
||||
const workDir = sourceWorkDir(service);
|
||||
const buildContext = sourceBuildContext(service);
|
||||
const dockerfilePath = sourceDockerfilePath(service);
|
||||
return [
|
||||
"set -euo pipefail",
|
||||
`export_dir=${shellQuote(exportDir)}`,
|
||||
`work_dir=${shellQuote(workDir)}`,
|
||||
`build_context=${shellQuote(buildContext)}`,
|
||||
`dockerfile_path=${shellQuote(dockerfilePath)}`,
|
||||
"mkdir -p \"$work_dir\"",
|
||||
[
|
||||
"rsync -a --delete",
|
||||
@@ -593,27 +638,51 @@ function syncSourceScript(service: UniDeskMicroserviceConfig, exportDir: string)
|
||||
"\"$export_dir/\"",
|
||||
"\"$work_dir/\"",
|
||||
].join(" "),
|
||||
`test -f "$work_dir/${service.repository.dockerfile}"`,
|
||||
"printf 'synced deploy worktree to %s\\n' \"$work_dir\"",
|
||||
"test -f \"$build_context/$dockerfile_path\"",
|
||||
"printf 'synced deploy worktree to %s\\nbuild_context=%s\\n' \"$work_dir\" \"$build_context\"",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function syncK8sControlManifestsScript(service: UniDeskMicroserviceConfig): string {
|
||||
if (service.deployment.mode !== "k3sctl-managed" || isUnideskRepo(service.repository.url)) return "";
|
||||
const manifests = [service.repository.composeFile, k8sManifestPath(service)];
|
||||
const commands = [
|
||||
"set -euo pipefail",
|
||||
`target_root=${shellQuote(targetWorkDir(service))}`,
|
||||
"mkdir -p \"$target_root\"",
|
||||
];
|
||||
for (const relativePath of manifests) {
|
||||
const absolutePath = rootPath(relativePath);
|
||||
if (!existsSync(absolutePath)) throw new Error(`${service.id} k3s control manifest missing: ${relativePath}`);
|
||||
const encoded = Buffer.from(readFileSync(absolutePath, "utf8"), "utf8").toString("base64");
|
||||
commands.push(
|
||||
`relative_path=${shellQuote(relativePath)}`,
|
||||
`target_file="$target_root/$relative_path"`,
|
||||
"mkdir -p \"$(dirname \"$target_file\")\"",
|
||||
`printf %s ${shellQuote(encoded)} | base64 -d > "$target_file"`,
|
||||
"printf 'synced_k3s_control_manifest=%s\\n' \"$target_file\"",
|
||||
);
|
||||
}
|
||||
return commands.join("\n");
|
||||
}
|
||||
|
||||
function buildImageScript(service: UniDeskMicroserviceConfig, desired: DeployManifestService, resolvedCommit: string): string {
|
||||
const image = buildImageTag(service);
|
||||
const workDir = targetWorkDir(service);
|
||||
const dockerfile = `${workDir}/${service.repository.dockerfile}`;
|
||||
const buildContext = sourceBuildContext(service);
|
||||
const dockerfilePath = sourceDockerfilePath(service);
|
||||
const dockerfile = `${buildContext}/${dockerfilePath}`;
|
||||
const labelArgs = [
|
||||
"--label", `unidesk.ai/service-id=${service.id}`,
|
||||
"--label", `unidesk.ai/source-repo=${desired.repo}`,
|
||||
"--label", `unidesk.ai/source-commit=${resolvedCommit}`,
|
||||
"--label", `unidesk.ai/dockerfile=${service.repository.dockerfile}`,
|
||||
"--label", `unidesk.ai/dockerfile=${dockerfilePath}`,
|
||||
];
|
||||
const commonArgs = [
|
||||
"--progress=plain",
|
||||
...labelArgs,
|
||||
"-t", image,
|
||||
"-f", dockerfile,
|
||||
workDir,
|
||||
buildContext,
|
||||
];
|
||||
const proxyBuildArgs = targetIsMain(service)
|
||||
? []
|
||||
@@ -1399,6 +1468,12 @@ async function applyOneService(config: UniDeskConfig, service: UniDeskMicroservi
|
||||
const sync = await step(config, service, "sync-source", syncSourceScript(service, exportDir), targetIsMain(service) ? repoRoot : "/home/ubuntu", 90_000, false);
|
||||
if (!pushStep(steps, sync)) return { ok: false, serviceId: service.id, startedAt, finishedAt: nowIso(), resolvedCommit, before, steps };
|
||||
|
||||
const controlManifestSyncScript = syncK8sControlManifestsScript(service);
|
||||
if (controlManifestSyncScript.length > 0) {
|
||||
const controlManifestSync = await step(config, service, "sync-k3s-control-manifests", controlManifestSyncScript, targetIsMain(service) ? repoRoot : "/home/ubuntu", 90_000, !targetIsMain(service));
|
||||
if (!pushStep(steps, controlManifestSync)) return { ok: false, serviceId: service.id, startedAt, finishedAt: nowIso(), resolvedCommit, before, steps };
|
||||
}
|
||||
|
||||
const buildScript = service.deployment.mode === "unidesk-direct"
|
||||
? buildDirectImageScript(service, desired, resolvedCommit)
|
||||
: buildImageScript(service, desired, resolvedCommit);
|
||||
|
||||
Reference in New Issue
Block a user