feat: create Docker context for Podman machine (#12126)

This commit is contained in:
Jeff MAURY 2025-11-05 14:22:28 +01:00 committed by GitHub
parent b838bbc0a5
commit b2b56cb648
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 591 additions and 6 deletions

View file

@ -37,5 +37,7 @@ export interface DockerContextInfo {
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface DockerContextParsingInfo extends Omit<DockerContextInfo, 'isCurrentContext'> {}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface DockerExtensionApi {}
export interface DockerExtensionApi {
createContext: (context: DockerContextParsingInfo) => Promise<void>;
removeContext: (context: string) => Promise<void>;
}

View file

@ -293,3 +293,46 @@ describe('getContexts', () => {
expect(contexts.find(c => c.name === 'bar')).toBeDefined();
});
});
describe('createContext', () => {
test('creating default context should throw exception', async () => {
let error: unknown;
try {
await dockerContextHandler.createContext({ name: 'default' } as unknown as DockerContextParsingInfo);
} catch (err: unknown) {
error = err;
}
expect(error).toBeDefined();
expect(error).toBeInstanceOf(Error);
});
test('creating non default context should write file', async () => {
const mkdirSpy = vi.spyOn(fs.promises, 'mkdir');
const writeFileSpy = vi.spyOn(fs.promises, 'writeFile');
await dockerContextHandler.createContext({
name: 'non-default',
metadata: { description: '' },
endpoints: { docker: { host: '' } },
} as unknown as DockerContextParsingInfo);
expect(mkdirSpy).toBeCalled();
expect(writeFileSpy).toBeCalled();
});
});
describe('removeContext', () => {
test('removing default context should be noop', async () => {
const rmSpy = vi.spyOn(fs.promises, 'rm');
await dockerContextHandler.removeContext('default');
// no rm folder
expect(rmSpy).not.toBeCalled();
});
test('removing non default context should delete folder', async () => {
const rmSpy = vi.spyOn(fs.promises, 'rm');
await dockerContextHandler.removeContext('non-default');
// no rm folder
expect(rmSpy).toBeCalled();
});
});

View file

@ -183,4 +183,40 @@ export class DockerContextHandler {
// write back the file with the current context and using tabs for indentation
await promises.writeFile(this.getDockerConfigPath(), JSON.stringify(config, null, '\t'));
}
async createContext(context: DockerContextParsingInfo): Promise<void> {
if (context.name === 'default') {
throw new Error('Cannot create a context with the name default');
}
// create the context directory
const dockerContextsMetaPath = join(this.#dockerConfig.getPath(), 'contexts', 'meta');
const sha256ContextName = createHash('sha256').update(context.name).digest('hex');
const dockerContextDirectory = join(dockerContextsMetaPath, sha256ContextName);
await promises.mkdir(dockerContextDirectory, { recursive: true });
// write the meta.json file
const metaFilePath = join(dockerContextDirectory, 'meta.json');
const meta = {
Name: context.name,
Metadata: {
Description: context.metadata.description,
},
Endpoints: {
docker: {
Host: context.endpoints.docker.host,
},
},
};
await promises.writeFile(metaFilePath, JSON.stringify(meta));
}
async removeContext(name: string): Promise<void> {
if (name !== 'default') {
// remove the context directory
const dockerContextsMetaPath = join(this.#dockerConfig.getPath(), 'contexts', 'meta');
const sha256ContextName = createHash('sha256').update(name).digest('hex');
const dockerContextDirectory = join(dockerContextsMetaPath, sha256ContextName);
await promises.rm(dockerContextDirectory, { recursive: true, force: true });
}
}
}

View file

@ -187,7 +187,8 @@ export async function activate(extensionContext: extensionApi.ExtensionContext):
}
});
return {
dummy: '',
createContext: dockerContextHandler.createContext.bind(dockerContextHandler),
removeContext: dockerContextHandler.removeContext.bind(dockerContextHandler),
};
}

View file

@ -0,0 +1,3 @@
# podman-docker-context extension
This extension creates Docker contexts for Podman connections (machines, remote,...).

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,30 @@
{
"name": "podman-docker-context",
"displayName": "Podman Docker Context",
"description": "Synchronize Docker Contexts with Podman",
"version": "1.23.0-next",
"icon": "icon.png",
"publisher": "podman-desktop",
"license": "Apache-2.0",
"engines": {
"podman-desktop": "^0.0.1"
},
"main": "./dist/extension.js",
"source": "./src/extension.ts",
"scripts": {
"build": "vite build && node ./scripts/build.js",
"test": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"watch": "vite build --watch"
},
"dependencies": {
"@podman-desktop/api": "workspace:*",
"@podman-desktop/docker-extension-api": "workspace:*"
},
"devDependencies": {
"adm-zip": "^0.5.16",
"mkdirp": "^3.0.1",
"vite": "^7.1.9",
"vitest": "^3.2.4"
}
}

View file

@ -0,0 +1,46 @@
#!/usr/bin/env node
/**********************************************************************
* Copyright (C) 2022-2023 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
const AdmZip = require('adm-zip');
const path = require('path');
const packageJson = require('../package.json');
const fs = require('fs');
const { mkdirp } = require('mkdirp');
const destFile = path.resolve(__dirname, `../${packageJson.name}.cdix`);
const builtinDirectory = path.resolve(__dirname, '../builtin');
const unzippedDirectory = path.resolve(builtinDirectory, `${packageJson.name}.cdix`);
// remove the .cdix file before zipping
if (fs.existsSync(destFile)) {
fs.rmSync(destFile);
}
// remove the builtin folder before zipping
if (fs.existsSync(builtinDirectory)) {
fs.rmSync(builtinDirectory, { recursive: true, force: true });
}
const zip = new AdmZip();
zip.addLocalFolder(path.resolve(__dirname, '../'));
zip.writeZip(destFile);
// create unzipped built-in
mkdirp(unzippedDirectory).then(() => {
const unzip = new AdmZip(destFile);
unzip.extractAllTo(unzippedDirectory);
});

View file

@ -0,0 +1,197 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { ContainerProviderConnection, ProviderContainerConnection } from '@podman-desktop/api';
import { env, provider } from '@podman-desktop/api';
import type { DockerExtensionApi } from '@podman-desktop/docker-extension-api';
import { beforeEach, describe, expect, test, vi } from 'vitest';
import {
DockerContextSynchronizer,
toDescription,
toDockerContextName,
toEndpoint,
} from './docker-context-synchronizer.js';
vi.mock('@podman-desktop/api', async () => {
return {
env: {
isLinux: false,
isWindows: false,
isMac: false,
},
provider: {
onDidUpdateContainerConnection: vi.fn(),
onDidRegisterContainerConnection: vi.fn(),
getContainerConnections: vi.fn(() => []),
},
};
});
const DOCKER_CONNECTION = {
providerId: 'docker',
connection: {
type: 'docker',
name: 'docker',
endpoint: {
socketPath: '/var/run/docker.sock',
},
status: vi.fn(),
},
} as unknown as ProviderContainerConnection;
const PODMAN_CONNECTION1 = {
providerId: 'podman',
connection: {
type: 'podman',
name: 'podman-test-1',
endpoint: {
socketPath: '/var/run/podman1.sock',
},
status: vi.fn(),
},
} as unknown as ProviderContainerConnection;
const PODMAN_CONNECTION2 = {
providerId: 'podman',
connection: {
type: 'podman',
name: 'test-2',
endpoint: {
socketPath: '/var/run/podman2.sock',
},
status: vi.fn(),
},
} as unknown as ProviderContainerConnection;
const dockerExtensionAPI: DockerExtensionApi = {
createContext: vi.fn(),
removeContext: vi.fn(),
};
beforeEach(() => {
vi.resetAllMocks();
DOCKER_CONNECTION.connection.status = vi.fn().mockReturnValue('started');
PODMAN_CONNECTION1.connection.status = vi.fn().mockReturnValue('started');
PODMAN_CONNECTION2.connection.status = vi.fn().mockReturnValue('started');
});
class TestDockerContextSynchronizer extends DockerContextSynchronizer {
override async processUpdatedConnection(connection: ContainerProviderConnection): Promise<void> {
return super.processUpdatedConnection(connection);
}
}
describe('toDockerContextName', () => {
test.each(['Windows', 'Linux', 'MacOS'])('should return the name prefixed with podman- (%s)', platform => {
vi.mocked(env).isWindows = platform === 'Windows';
vi.mocked(env).isLinux = platform === 'Linux';
vi.mocked(env).isMac = platform === 'MacOS';
const name = toDockerContextName('foo');
expect(name).toBe(env.isWindows ? 'podman-foo' : 'podman');
});
test.each(['Windows', 'Linux', 'MacOS'])('should return the name (%s)', platform => {
vi.mocked(env).isWindows = platform === 'Windows';
vi.mocked(env).isLinux = platform === 'Linux';
vi.mocked(env).isMac = platform === 'MacOS';
const name = toDockerContextName('podman-foo');
expect(name).toBe(env.isWindows ? 'podman-foo' : 'podman');
});
});
describe('toDescription', () => {
test.each(['Windows', 'Linux', 'MacOS'])('should return the description (%s)', platform => {
vi.mocked(env).isWindows = platform === 'Windows';
vi.mocked(env).isLinux = platform === 'Linux';
vi.mocked(env).isMac = platform === 'MacOS';
const name = toDescription('foo');
expect(name).toBe(env.isWindows ? 'Podman machine foo' : 'Podman');
});
test.each(['Windows', 'Linux', 'MacOS'])('should also return the description (%s)', platform => {
vi.mocked(env).isWindows = platform === 'Windows';
vi.mocked(env).isLinux = platform === 'Linux';
vi.mocked(env).isMac = platform === 'MacOS';
const name = toDescription('podman-foo');
expect(name).toBe(env.isWindows ? 'Podman machine podman-foo' : 'Podman');
});
});
describe('toEndpoint', () => {
test('should return npipe on Windows', () => {
vi.mocked(env).isWindows = true;
const name = toEndpoint('foo');
expect(name).toBe('npipe://foo');
});
test('should return unix on Linux/MacOS', () => {
vi.mocked(env).isWindows = false;
const name = toEndpoint('podman-foo');
expect(name).toBe('unix://podman-foo');
});
});
describe('create contexts', () => {
test('should not create contexts if no connection', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([]);
const dockerContextSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).not.toHaveBeenCalled();
});
test('should not create contexts if only docker connections', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([DOCKER_CONNECTION]);
const dockerContextSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).not.toHaveBeenCalled();
});
test('should create context if single podman connection', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([PODMAN_CONNECTION1]);
const dockerContextSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).toHaveBeenCalledOnce();
});
test('should create context if podman and docker connections', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([DOCKER_CONNECTION, PODMAN_CONNECTION1]);
const dockerContextSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).toHaveBeenCalledOnce();
});
test('should create contexts if several podman connections', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([PODMAN_CONNECTION1, PODMAN_CONNECTION2]);
const dockerContextSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).toHaveBeenCalledTimes(2);
});
});
describe('delete context', () => {
test('should delete context if podman connection is removed', async () => {
vi.mocked(provider.getContainerConnections).mockReturnValue([PODMAN_CONNECTION1, PODMAN_CONNECTION2]);
const dockerContextSynchronizer = new TestDockerContextSynchronizer(dockerExtensionAPI);
await dockerContextSynchronizer.init();
expect(dockerExtensionAPI.createContext).toHaveBeenCalledTimes(2);
vi.mocked(PODMAN_CONNECTION2.connection.status).mockReturnValue('stopped');
await dockerContextSynchronizer.processUpdatedConnection(PODMAN_CONNECTION2.connection);
expect(dockerExtensionAPI.removeContext).toHaveBeenCalledTimes(1);
});
});

View file

@ -0,0 +1,81 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import type { ContainerProviderConnection, Disposable } from '@podman-desktop/api';
import { env, provider } from '@podman-desktop/api';
import type { DockerExtensionApi } from '@podman-desktop/docker-extension-api';
export function toDockerContextName(name: string): string {
return env.isWindows ? (name.startsWith('podman-') ? name : `podman-${name}`) : 'podman';
}
export function toDescription(name: string): string {
return env.isWindows ? `Podman machine ${name}` : 'Podman';
}
export function toEndpoint(socketPath: string): string {
return env.isWindows ? `npipe://${socketPath.replace(/\\/g, '/')}` : `unix://${socketPath}`;
}
export class DockerContextSynchronizer implements Disposable {
#disposable: Disposable[] = [];
constructor(private dockerExtensionAPI: DockerExtensionApi) {
this.#disposable.push(
provider.onDidUpdateContainerConnection(event => this.processUpdatedConnection(event.connection)),
);
this.#disposable.push(
provider.onDidRegisterContainerConnection(event => this.processUpdatedConnection(event.connection)),
);
}
async init(): Promise<void> {
const connections = provider
.getContainerConnections()
.filter(connection => connection.connection.status() === 'started');
for (const connection of connections) {
await this.processUpdatedConnection(connection.connection);
}
}
protected async processUpdatedConnection(connection: ContainerProviderConnection): Promise<void> {
if (connection.type === 'podman') {
if (connection.status() === 'started') {
try {
await this.dockerExtensionAPI.createContext({
name: toDockerContextName(connection.name),
metadata: { description: toDescription(connection.name) },
endpoints: { docker: { host: toEndpoint(connection.endpoint.socketPath) } },
});
} catch (error: unknown) {
console.warn(`Error creating Docker context for Podman machine ${connection.name}`, error);
}
} else if (connection.status() === 'stopped') {
const dockerContextName = toDockerContextName(connection.name);
try {
await this.dockerExtensionAPI.removeContext(dockerContextName);
} catch (error: unknown) {
console.warn(`Error removing Docker context ${dockerContextName}`, error);
}
}
}
}
dispose(): void {
this.#disposable.forEach(disposable => disposable.dispose());
}
}

View file

@ -0,0 +1,38 @@
/**********************************************************************
* Copyright (C) 2025 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import * as extensionApi from '@podman-desktop/api';
import type { DockerExtensionApi } from '@podman-desktop/docker-extension-api';
import { DockerContextSynchronizer } from './docker-context-synchronizer';
export async function activate(extensionContext: extensionApi.ExtensionContext): Promise<void> {
const dockerExtensionAPI = extensionApi.extensions.getExtension<DockerExtensionApi>('podman-desktop.docker');
if (!dockerExtensionAPI) {
throw new Error('Docker extension not found');
}
const dockerSynchronizer = new DockerContextSynchronizer(dockerExtensionAPI.exports);
extensionContext.subscriptions.push(dockerSynchronizer);
dockerSynchronizer.init().catch((err: unknown) => {
console.error('Error while initializing docker synchronizer', err);
});
}
export function deactivate(): void {
console.log('stopping docker context extension');
}

View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"strict": true,
"module": "esnext",
"lib": ["ES2017"],
"sourceMap": true,
"rootDir": "src",
"outDir": "dist",
"target": "esnext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"types": ["node"]
},
"include": ["src", "types/*.d.ts"]
}

View file

@ -0,0 +1,66 @@
/**********************************************************************
* Copyright (C) 2023-2025 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
***********************************************************************/
import { join } from 'path';
import { builtinModules } from 'module';
const PACKAGE_ROOT = __dirname;
/**
* @type {import('vite').UserConfig}
* @see https://vitejs.dev/config/
*/
const config = {
mode: process.env.MODE,
root: PACKAGE_ROOT,
envDir: process.cwd(),
resolve: {
alias: {
'/@/': join(PACKAGE_ROOT, 'src') + '/',
},
},
build: {
sourcemap: 'inline',
target: 'esnext',
outDir: 'dist',
assetsDir: '.',
minify: process.env.MODE === 'production' ? 'esbuild' : false,
lib: {
entry: 'src/extension.ts',
formats: ['cjs'],
},
rollupOptions: {
external: ['@podman-desktop/api', ...builtinModules.flatMap(p => [p, `node:${p}`])],
output: {
entryFileNames: '[name].js',
},
},
emptyOutDir: true,
reportCompressedSize: false,
},
test: {
globals: true,
environment: 'node',
include: ['src/**/*.{test,spec}.?(c|m)[jt]s?(x)'],
alias: {
'@podman-desktop/api': join(PACKAGE_ROOT, '..', '..', '__mocks__/@podman-desktop/api.js'),
},
},
};
export default config;

View file

@ -15,9 +15,10 @@
"scripts": {
"build": "pnpm run build:main && pnpm run build:preload && pnpm run build:preload-docker-extension && pnpm run build:preload-webview && npm run build:preload:types && pnpm run build:ui && pnpm run build:renderer && pnpm run build:extensions",
"build:main": "cd ./packages/main && vite build",
"build:extensions": "pnpm run build:extensions:compose && pnpm run build:extensions:docker && pnpm run build:extensions:lima && pnpm run build:extensions:podman && pnpm run build:extensions:kubecontext && pnpm run build:extensions:kind && pnpm run build:extensions:registries && pnpm run build:extensions:kubectl-cli",
"build:extensions": "pnpm run build:extensions:compose && pnpm run build:extensions:docker && pnpm run build:extensions:podman-docker-context && pnpm run build:extensions:lima && pnpm run build:extensions:podman && pnpm run build:extensions:kubecontext && pnpm run build:extensions:kind && pnpm run build:extensions:registries && pnpm run build:extensions:kubectl-cli",
"build:extensions:compose": "cd ./extensions/compose && pnpm run build",
"build:extensions:docker": "cd ./extensions/docker/packages/extension && pnpm run build",
"build:extensions:podman-docker-context": "cd extensions/podman-docker-context && pnpm run build",
"build:extensions:kubecontext": "cd ./extensions/kube-context && pnpm run build",
"build:extensions:kind": "cd ./extensions/kind && pnpm run build",
"build:extensions:lima": "cd ./extensions/lima && pnpm run build",
@ -63,10 +64,11 @@
"test:preload": "vitest --run --project=preload --passWithNoTests",
"test:preload-docker-extension": "vitest --run --project=preload-docker-extension --passWithNoTests",
"test:preload-webview": "vitest --run --project=preload-webview",
"test:extensions": "vitest --run --project=extensions --passWithNoTests && pnpm run test:extensions:compose && pnpm run test:extensions:kind && pnpm run test:extensions:docker && pnpm run test:extensions:lima && pnpm run test:extensions:kube && pnpm run test:extensions:podman && pnpm run test:extensions:registries && pnpm run test:extensions:kubectl-cli",
"test:extensions": "vitest --run --project=extensions --passWithNoTests && pnpm run test:extensions:compose && pnpm run test:extensions:kind && pnpm run test:extensions:docker && pnpm run test:extensions:podman-docker-context && pnpm run test:extensions:lima && pnpm run test:extensions:kube && pnpm run test:extensions:podman && pnpm run test:extensions:registries && pnpm run test:extensions:kubectl-cli",
"test:extensions:kind": "vitest --run --project=kind --passWithNoTests",
"test:extensions:compose": "vitest --run --project=compose --passWithNoTests",
"test:extensions:docker": "vitest --run --project=docker --passWithNoTests",
"test:extensions:podman-docker-context": "vitest --run --project=podman-docker-context --passWithNoTests",
"test:extensions:kube": "vitest --run --project=kube-context --passWithNoTests ",
"test:extensions:lima": "vitest --run --project=lima --passWithNoTests ",
"test:extensions:podman": "vitest --run --project=podman --passWithNoTests ",
@ -93,10 +95,11 @@
"typecheck:preload-webview": "tsc --noEmit -p packages/preload-webview/tsconfig.json",
"typecheck:renderer": "pnpm run build:preload:types && svelte-check --tsconfig packages/renderer/tsconfig.json",
"typecheck:ui": "pnpm run build:ui && pnpm run build:preload:types && svelte-check --tsconfig packages/ui/tsconfig.json",
"typecheck:extensions": "pnpm run typecheck:extensions:compose && pnpm run typecheck:extensions:kind && pnpm run typecheck:extensions:docker && pnpm run typecheck:extensions:lima && pnpm run typecheck:extensions:kube-context && pnpm run typecheck:extensions:podman && pnpm run typecheck:extensions:registries && pnpm run typecheck:extensions:kubectl-cli",
"typecheck:extensions": "pnpm run typecheck:extensions:compose && pnpm run typecheck:extensions:kind && pnpm run typecheck:extensions:docker && pnpm run typecheck:extensions:podman-docker-context && pnpm run typecheck:extensions:lima && pnpm run typecheck:extensions:kube-context && pnpm run typecheck:extensions:podman && pnpm run typecheck:extensions:registries && pnpm run typecheck:extensions:kubectl-cli",
"typecheck:extensions:kind": "tsc --noEmit --project extensions/kind",
"typecheck:extensions:compose": "tsc --noEmit --project extensions/compose",
"typecheck:extensions:docker": "tsc --noEmit --project extensions/docker/packages/extension",
"typecheck:extensions:podman-docker-context": "tsc --noEmit --project extensions/podman-docker-context",
"typecheck:extensions:kube-context": "tsc --noEmit --project extensions/kube-context",
"typecheck:extensions:lima": "tsc --noEmit --project extensions/lima",
"typecheck:extensions:podman": "tsc --noEmit --project extensions/podman/packages/extension",

View file

@ -480,6 +480,28 @@ importers:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.0)(jiti@2.6.1)(jsdom@27.1.0)(lightningcss@1.30.2)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(terser@5.36.0)(tsx@4.20.6)(yaml@2.8.1)
extensions/podman-docker-context:
dependencies:
'@podman-desktop/api':
specifier: workspace:*
version: link:../../packages/extension-api
'@podman-desktop/docker-extension-api':
specifier: workspace:*
version: link:../docker/packages/api
devDependencies:
adm-zip:
specifier: ^0.5.16
version: 0.5.16
mkdirp:
specifier: ^3.0.1
version: 3.0.1
vite:
specifier: ^7.1.9
version: 7.1.10(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.36.0)(tsx@4.20.6)(yaml@2.8.1)
vitest:
specifier: ^3.2.4
version: 3.2.4(@types/debug@4.1.12)(@types/node@24.7.2)(jiti@2.6.1)(jsdom@26.1.0)(lightningcss@1.30.1)(msw@2.11.5(@types/node@24.7.2)(typescript@5.9.3))(terser@5.36.0)(tsx@4.20.6)(yaml@2.8.1)
extensions/podman/packages/api:
dependencies:
'@podman-desktop/api':