Creating a Podman Desktop extension
This tutorial covers the following end-to-end tasks required to create and run a Podman Desktop extension:
- Initializing an extension
- Writing the extension entry point
- Build dependencies
- Running the extension
- Verifying the extension's features
You can write an extension in TypeScript
or JavaScript
. You can simplify extension creation by specifying two entry points: activate()
and deactivate()
from within the extension.
All Podman Desktop functionalities are communicated entirely through the API. The extension you create interacts with the Podman Desktop API through the @podman-desktop/api
package. The type definition of the @podman-desktop/api
API is hosted as part of the npm
package.
Before you begin
Make sure you have:
- Installed Podman Desktop.
- A running Podman machine.
- A developer role.
- Installed
JavaScript
orTypeScript
. - A clone of the Podman Desktop repository on your local machine.
Initializing an extension
Create a package.json
file to initialize your extension.
-
Create a subdirectory, such as
foobar
in theextensions
directory of the Podman Desktop repository. -
Initialize a
package.json
file and add it to the subdirectory.{}
-
Add
TypeScript
and the Podman Desktop API to the development dependencies:"devDependencies": {
"@podman-desktop/api": "latest",
"typescript": "latest",
"vite": "latest"
}, -
Add the required metadata:
"name": "my-extension",
"displayName": "My Hello World extension",
"description": "How to write my first extension",
"version": "0.0.1",
"icon": "icon.png",
"publisher": "benoitf", -
Add the Podman Desktop version to run the extension:
"engines": {
"podman-desktop": "latest"
}, -
Add the main entry point:
"main": "./dist/extension.js"
-
Add a
Hello World
command contribution:"contributes": {
"commands": [
{
"command": "my.first.command",
"title": "My First Extension: Hello World"
}
]
} -
Validate the complete
package.json
file manually:Example: The complete
package.json
file{
"devDependencies": {
"@podman-desktop/api": "latest",
"typescript": "latest",
"vite": "latest"
},
"name": "my-extension",
"displayName": "My Hello World extension",
"description": "How to write my first extension",
"version": "0.0.1",
"icon": "icon.png",
"publisher": "benoitf",
"engines": {
"podman-desktop": "latest"
},
"scripts": {
"build": "vite build",
"test": "vitest run --coverage",
"test:watch": "vitest watch --coverage",
"watch": "vite build --watch"
},
"main": "./dist/extension.js",
"contributes": {
"commands": [
{
"command": "my.first.command",
"title": "My First Extension: Hello World"
}
]
}
} -
Add an
icon.png
file to the subdirectory.
Writing the extension entry point
-
Create a
src/extension.ts
file in the subdirectory. -
Import the Podman Desktop API into the file:
import * as podmanDesktopAPI from '@podman-desktop/api';
-
Use one of the following ways to expose the
activate
function:-
Synchronous (sequential execution of tasks)
export function activate(): void;
-
Asynchronous (parallel execution of tasks)
export async function activate(): Promise<void>;
-
-
Optional: Add an extension context to the
activate
function by enabling the extension to register disposable resources:export async function activate(extensionContext: podmanDesktopAPI.ExtensionContext): Promise<void> {}
-
Add the features of the extension to the file. This sample extension:
- Registers the command referenced in the
package.json
file. - Displays an option for the user to select values from the dropdown list.
- Displays a pop-up message with the values selected by the user.
- Creates an item in the status bar to run the command.
import * as podmanDesktopAPI from '@podman-desktop/api';
export async function activate(extensionContext: podmanDesktopAPI.ExtensionContext): Promise<void> {
// register the command referenced in package.json file
const myFirstCommand = podmanDesktopAPI.commands.registerCommand('my.first.command', async () => {
// display a choice to the user for selecting some values
const result = await podmanDesktopAPI.window.showQuickPick(['un', 'deux', 'trois'], {
canPickMany: true, // user can select more than one choice
});
// display an information message with the user choice
await podmanDesktopAPI.window.showInformationMessage(`The choice was: ${result}`);
});
// create an item in the status bar to run our command
// it will stick on the left of the status bar
const item = podmanDesktopAPI.window.createStatusBarItem(podmanDesktopAPI.StatusBarAlignLeft, 100);
item.text = 'My first command';
item.command = 'my.first.command';
item.show();
// register disposable resources to it's removed when you deactivte the extension
extensionContext.subscriptions.push(myFirstCommand);
extensionContext.subscriptions.push(item);
} - Registers the command referenced in the
-
Optional: Use one of the following ways to expose the
deactivate
function:-
Synchronous
export function deactivate(): void;
-
Asynchronous
export async function deactivate(): Promise<void>;
noteThe above example is not a full representation of every functionality an extension can be used for. You can expand the internal Podman Desktop functionalities, such as creating a new provider and adding new commands. See our API documentation for more information.
-
Build dependencies
You can build this extension by configuring TypeScript
and Vite
.
- Create a file named
tsconfig.json
with the following content in the subdirectory:
{
"compilerOptions": {
"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"]
}
- Create a file named
vite.config.js
with the following content in the subdirectory:
/**********************************************************************
* Copyright (C) 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
***********************************************************************/
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,
},
};
export default config;
Running the extension
-
Stop the Podman Desktop application if it runs in the background.
-
Run the following command from your clone of the Podman Desktop repository:
pnpm watch --extension-folder <path-to-your-extension>
The extension compiles and generates the output in the
dist
folder of the subdirectory.noteYou can also run and verify the extension directly from the Podman Desktop UI.
Verifying the extension's features
-
Click Extensions in the left navigation pane.
-
Search the created
My Hello World
extension in the list. The extension isACTIVE
. -
Verify the features of the extension:
-
Click the
My first command
item in the status bar. A dropdown list opens. -
Select a value from the dropdown list.
-
Click OK. A pop-up notifying the selected value opens.
-
Click OK.
noteIf you have created a webview extension, you can access the console of the extension:
- Right-click the extension icon in the left navigation pane.
- Select Open Devtools of the webview.