workdir: report current selector and ref

This commit is contained in:
2026-07-31 23:48:46 +09:00
parent 32d3f8f75e
commit 06a98fe30c
15 changed files with 395 additions and 86 deletions
@@ -0,0 +1,61 @@
import type { WorkingDirectorySummary } from "../sidebar/types.ts";
import { formatCurrentWorkdirRevision } from "./workdir-revision.ts";
declare const Deno: {
test(name: string, fn: () => Promise<void> | void): void;
};
function assertEquals<T>(actual: T, expected: T): void {
if (actual !== expected) {
throw new Error(`Expected ${String(expected)}, got ${String(actual)}`);
}
}
function workdir(
current_selector: string | null,
current_ref: string | null,
): WorkingDirectorySummary {
return {
working_directory_id: "workdir-1",
repository_id: "repository-1",
current_selector,
current_ref,
materializer_kind: "local_git_worktree",
status: "active",
cleanup_target: {
kind: "local_git_worktree",
working_directory_id: "workdir-1",
repository_id: "repository-1",
},
};
}
Deno.test("Git detached Workdir shows only its current ref", () => {
assertEquals(
formatCurrentWorkdirRevision(
workdir(null, "0123456789abcdef0123456789abcdef01234567"),
"git",
),
"0123456789ab",
);
});
Deno.test("Git Workdir with a selector shows selector at current ref", () => {
assertEquals(
formatCurrentWorkdirRevision(
workdir("feature/current", "fedcba9876543210fedcba9876543210fedcba98"),
"git",
),
"feature/current@fedcba987654",
);
});
Deno.test("non-Git Workdir does not receive Git hash formatting", () => {
assertEquals(
formatCurrentWorkdirRevision(
workdir("snapshot", "revision-value"),
"archive",
),
"snapshot · revision-value",
);
});
@@ -0,0 +1,22 @@
import type { WorkingDirectorySummary } from "../sidebar/types.ts";
export function formatCurrentWorkdirRevision(
workdir: WorkingDirectorySummary,
repositoryProvider: string | null | undefined,
): string {
const selector = workdir.current_selector?.trim() || null;
const reference = workdir.current_ref?.trim() || null;
if (repositoryProvider?.toLowerCase() === "git") {
const hash = reference ? shortGitHash(reference) : null;
if (selector && hash) return `${selector}@${hash}`;
return selector ?? hash ?? "—";
}
if (selector && reference) return `${selector} · ${reference}`;
return selector ?? reference ?? "—";
}
function shortGitHash(reference: string): string {
return reference.length > 12 ? reference.slice(0, 12) : reference;
}
@@ -110,7 +110,7 @@
</span>
<span class="item-meta">
worker {worker.worker_id} · {worker.profile ? `${worker.profile} · ` : ''}{worker.state} · 🖥 {worker.host_id}
{worker.working_directory ? ` · wd:${worker.working_directory.repository_id}@${worker.working_directory.resolved_commit.slice(0, 8)}` : ''}
{worker.working_directory?.current_ref ? ` · wd:${worker.working_directory.repository_id}@${worker.working_directory.current_ref.slice(0, 8)}` : ''}
</span>
</a>
</li>
@@ -128,10 +128,11 @@ export type WorkingDirectoryOccupancy = {
export type WorkingDirectorySummary = {
working_directory_id: string;
repository_id: string;
requested_selector?: string | null;
creation_selector?: string | null;
creation_ref?: string | null;
current_selector?: string | null;
current_ref?: string | null;
materializer_kind: string;
resolved_commit: string;
resolved_tree?: string | null;
status: string;
cleanliness?: string | null;
primary_worker_id?: number | null;
@@ -50,9 +50,11 @@ const options: WorkerLaunchOptionsResponse = {
{
working_directory_id: "wd-1-repo",
repository_id: "repo",
requested_selector: "HEAD",
creation_selector: "HEAD",
creation_ref: "0123456789abcdef",
current_selector: null,
current_ref: "0123456789abcdef",
materializer_kind: "local_git_worktree",
resolved_commit: "0123456789abcdef",
status: "active",
cleanliness: "clean",
primary_worker_id: null,
@@ -159,7 +161,7 @@ Deno.test("defaultWorkerLaunchForm preserves a Ticket repository target", () =>
...options.working_directories[0],
working_directory_id: "ticket-workdir",
repository_id: "ticket-repo",
requested_selector: "work/ticket",
creation_selector: "work/ticket",
},
],
},
@@ -55,7 +55,8 @@ export function defaultWorkerLaunchForm(
Boolean(current.working_directory_repository_id) &&
directory.repository_id === current.working_directory_repository_id &&
(!current.working_directory_selector ||
directory.requested_selector === current.working_directory_selector)
(directory.current_selector ?? directory.creation_selector) ===
current.working_directory_selector)
) ?? availableWorkingDirectories.find((directory) =>
Boolean(current.working_directory_repository_id) &&
directory.repository_id === current.working_directory_repository_id
@@ -1,6 +1,7 @@
<script lang="ts">
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
import { workspaceApiPath } from '$lib/workspace/api/http';
import { formatCurrentWorkdirRevision } from '$lib/workspace/settings/workdir-revision';
import type {
CleanupWorkdirCandidate,
RuntimeCleanupExecutionResponse,
@@ -23,12 +24,13 @@
workdirs = data.workdirs?.items ?? [];
});
function commitLabel(workdir: WorkingDirectorySummary): string {
return workdir.resolved_commit ? workdir.resolved_commit.slice(0, 12) : '—';
function repositoryProvider(workdir: WorkingDirectorySummary): string | null {
return data.repositories?.items.find((repository) => repository.id === workdir.repository_id)
?.provider ?? null;
}
function selectorLabel(workdir: WorkingDirectorySummary): string {
return workdir.requested_selector ?? 'HEAD';
function currentRevision(workdir: WorkingDirectorySummary): string {
return formatCurrentWorkdirRevision(workdir, repositoryProvider(workdir));
}
function cleanupCandidate(workdir: WorkingDirectorySummary): CleanupWorkdirCandidate | undefined {
@@ -125,8 +127,7 @@
<tr>
<th>Workdir</th>
<th>Repository</th>
<th>Selector</th>
<th>Commit</th>
<th>Revision</th>
<th>Status</th>
<th>Cleanliness</th>
<th>Occupied by</th>
@@ -139,8 +140,7 @@
<tr>
<td><code>{workdir.working_directory_id}</code></td>
<td>{workdir.repository_id}</td>
<td>{selectorLabel(workdir)}</td>
<td><code>{commitLabel(workdir)}</code></td>
<td><code>{currentRevision(workdir)}</code></td>
<td>{workdir.status}</td>
<td>{workdir.cleanliness ?? 'unknown'}</td>
<td>
@@ -2,6 +2,7 @@
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
import { workspaceApiPath } from '$lib/workspace/api/http';
import { workerConsoleHref } from '$lib/workspace/console/model';
import { formatCurrentWorkdirRevision } from '$lib/workspace/settings/workdir-revision';
import { canOpenWorkerConsole } from '$lib/workspace/sidebar/workers';
import type { CleanupWorkerCandidate, RuntimeCleanupExecutionResponse, RuntimeCleanupPlanResponse, Worker } from '$lib/workspace/sidebar/types';
import type { PageProps } from './$types';
@@ -145,9 +146,9 @@
function workerDirectory(worker: Worker): string {
const directory = worker.working_directory;
if (!directory) return '—';
const selector = directory.requested_selector ?? 'HEAD';
const commit = directory.resolved_commit ? directory.resolved_commit.slice(0, 12) : null;
return commit ? `${directory.repository_id} · ${selector} · ${commit}` : `${directory.repository_id} · ${selector}`;
const provider = data.repositories?.items.find((repository) => repository.id === directory.repository_id)
?.provider;
return `${directory.repository_id} · ${formatCurrentWorkdirRevision(directory, provider)}`;
}
</script>
@@ -2,12 +2,14 @@
import { goto } from '$app/navigation';
import { untrack } from 'svelte';
import { workspaceApiPath } from '$lib/workspace/api/http';
import { formatCurrentWorkdirRevision } from '$lib/workspace/settings/workdir-revision';
import { buildBrowserCreateWorkerRequest, defaultWorkerLaunchForm } from '$lib/workspace/sidebar/worker-launch';
import type {
BrowserCreateWorkerResponse,
BrowserWorkingDirectoryCreateResponse,
Diagnostic,
WorkerLaunchOptionsResponse,
WorkingDirectorySummary,
} from '$lib/workspace/sidebar/types';
import type { PageProps } from './$types';
@@ -22,6 +24,12 @@
diagnostics?: Diagnostic[];
};
function workdirOptionLabel(directory: WorkingDirectorySummary): string {
const provider = data.repositories?.items.find((repository) => repository.id === directory.repository_id)
?.provider;
return `${directory.repository_id} · ${formatCurrentWorkdirRevision(directory, provider)}`;
}
let { data }: PageProps = $props();
let workspaceId = $derived(data.workspaceId);
const ticketContext = untrack(() => data.ticketContext);
@@ -296,7 +304,7 @@
<option value="" disabled>Select workdir</option>
{#each availableWorkingDirectories as directory}
<option value={directory.working_directory_id}>
{directory.repository_id} · {directory.requested_selector ?? 'HEAD'}
{workdirOptionLabel(directory)}
</option>
{/each}
<option value={NEW_WORKING_DIRECTORY_VALUE}>New workdir…</option>