From 8a2653d7576c6c756f0252506bb20162610c787c Mon Sep 17 00:00:00 2001 From: gdlol <.> Date: Thu, 1 Oct 2026 02:17:52 +0000 Subject: [PATCH] Fix dotnet zsh completion --- features/dotnet/Install/Completions.cs | 2 +- features/dotnet/config.json | 2 +- scripts/verify/dotnet/lifecycle.ts | 25 +++++++++++++++++++++++++ scripts/verify/dotnet/scenarios.ts | 13 ++++++++++--- scripts/verify/dotnet/verify.ts | 1 + 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/features/dotnet/Install/Completions.cs b/features/dotnet/Install/Completions.cs index 318332d..f6e16c2 100644 --- a/features/dotnet/Install/Completions.cs +++ b/features/dotnet/Install/Completions.cs @@ -9,7 +9,7 @@ internal static class Completions private static readonly (string Shell, string Path)[] Targets = [ ("bash", "/usr/share/bash-completion/completions/dotnet"), - ("zsh", "/usr/share/zsh/site-functions/_dotnet"), + ("zsh", "/usr/local/share/zsh/site-functions/_dotnet"), ("fish", "/usr/share/fish/vendor_completions.d/dotnet.fish"), ]; diff --git a/features/dotnet/config.json b/features/dotnet/config.json index ed7f42c..ed76e36 100644 --- a/features/dotnet/config.json +++ b/features/dotnet/config.json @@ -1,5 +1,5 @@ { - "revision": 2, + "revision": 3, "components": { "sdk": { "versions": { diff --git a/scripts/verify/dotnet/lifecycle.ts b/scripts/verify/dotnet/lifecycle.ts index d6e8585..f3c61eb 100644 --- a/scripts/verify/dotnet/lifecycle.ts +++ b/scripts/verify/dotnet/lifecycle.ts @@ -1,3 +1,4 @@ +import { createHash } from "node:crypto"; import { cp, mkdir, writeFile } from "node:fs/promises"; import path, { posix } from "node:path"; @@ -57,6 +58,17 @@ const workspaceAt = (root: string, name: string): Workspace => { return { path: path.join(root, directory), containerPath: posix.join("/workspaces", directory) }; }; +const workspaceImageName = (workspace: Workspace): string => { + const folderHash = createHash("sha256").update(workspace.path).digest("hex"); + return `vsc-${path.basename(workspace.path)}-${folderHash}`; +}; + +const workspaceImages = async (workspace: Workspace): Promise => { + const name = workspaceImageName(workspace); + const { stdout } = await $$docker`docker images ${["--format", "{{.Repository}}:{{.Tag}}"]}`; + return stdout.split("\n").filter((tag) => tag.startsWith(name)); +}; + export const imagesModes = ["build", "pull"] as const; export type ImagesMode = (typeof imagesModes)[number]; @@ -151,6 +163,11 @@ const buildContainer = async (workspace: Workspace): Promise => { const startContainer = async (workspace: Workspace): Promise => { await project$$`devcontainer up ${["--remove-existing-container", ...["--workspace-folder", workspace.path]]}`; + if ((await workspaceImages(workspace)).length === 0) { + throw new Error( + `devcontainer up ${workspace.path}: no images matching ${workspaceImageName(workspace)}*; the devcontainer CLI folder image naming may have changed`, + ); + } }; const execInContainer = async (workspace: Workspace, command: Command): Promise => { @@ -205,6 +222,13 @@ const removeImage = async (tag: string): Promise => { await project$$`docker image rm ${tag}`; }; +const removeWorkspaceImages = async (workspace: Workspace): Promise => { + const tags = await workspaceImages(workspace); + if (tags.length > 0) { + await project$$`docker image rm ${tags}`; + } +}; + export const lifecycle = { provision, buildTestImage, @@ -217,4 +241,5 @@ export const lifecycle = { expectBuildFailure, removeContainer, removeImage, + removeWorkspaceImages, } as const; diff --git a/scripts/verify/dotnet/scenarios.ts b/scripts/verify/dotnet/scenarios.ts index f311a24..adddb23 100644 --- a/scripts/verify/dotnet/scenarios.ts +++ b/scripts/verify/dotnet/scenarios.ts @@ -1,3 +1,4 @@ +// spell-checker:ignore compinit import path from "node:path"; import type { Component } from "@/scripts/tasks/features/dotnet/generateConfig.js"; @@ -80,7 +81,7 @@ const definitions: Record = { const completionFiles = [ "/usr/share/bash-completion/completions/dotnet", - "/usr/share/zsh/site-functions/_dotnet", + "/usr/local/share/zsh/site-functions/_dotnet", "/usr/share/fish/vendor_completions.d/dotnet.fish", ]; @@ -94,7 +95,7 @@ const completionsAbsent: Exec = { expect: { stdout: ["absent"] }, }; -const completionRegistration: Exec = { +const bashCompletionRegistration: Exec = { command: [ "bash", "-lc", @@ -103,6 +104,11 @@ const completionRegistration: Exec = { expect: { stdout: ["complete -F _dotnet dotnet"] }, }; +const zshCompletionRegistration: Exec = { + command: ["zsh", "-f", "-c", "autoload -Uz compinit; compinit -D; print -r -- ${_comps[dotnet]:-NONE}"], + expect: { stdout: ["_dotnet"] }, +}; + const installResidue: Exec = { command: [ "sh", @@ -193,7 +199,8 @@ const sdkBehavior = ({ selection, workspace }: ScenarioContext): ScenarioBehavio }, }, completionsPresent, - completionRegistration, + bashCompletionRegistration, + zshCompletionRegistration, installResidue, tmpPreserved, ], diff --git a/scripts/verify/dotnet/verify.ts b/scripts/verify/dotnet/verify.ts index 1212472..9aa0d1d 100644 --- a/scripts/verify/dotnet/verify.ts +++ b/scripts/verify/dotnet/verify.ts @@ -163,6 +163,7 @@ export const verify = async (argv: string[]): Promise => { console.log(">>> verify dotnet: cleanup"); for (const workspace of workspaces) { await lifecycle.removeContainer(workspace); + await lifecycle.removeWorkspaceImages(workspace); } if (values.rmi) { for (const image of testImages.values()) {