diff --git a/.chocolatey/.gitignore b/.chocolatey/.gitignore deleted file mode 100644 index a7aeaaa13d..0000000000 --- a/.chocolatey/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.nupkg diff --git a/.chocolatey/podman-desktop/podman-desktop.nuspec b/.chocolatey/podman-desktop/podman-desktop.nuspec deleted file mode 100644 index e39f4471f8..0000000000 --- a/.chocolatey/podman-desktop/podman-desktop.nuspec +++ /dev/null @@ -1,45 +0,0 @@ - - - - - podman-desktop - 1.20.2 - Podman Desktop - Florent Benoit - https://github.com/containers - https://github.com/containers/podman-desktop/blob/main/LICENSE - http://www.podman-desktop.io/ - https://cdn.jsdelivr.net/gh/containers/podman-desktop@v0.9.1/buildResources/icon.png - false - -## Containers and Kubernetes for application developers - -### Build, run and manage containers: -- Build images from Containerfile or Dockerfile. -- Pull images from remote registries. -- Start, Stop, Restart containers and pods. -- Easily get a terminal in your container. -- Inspect container logs. -- Push images to OCI registries. -- Deploy and test images on Kubernetes. - -### Multiple configuration options -- Manage OCI registries; add, edit, or delete registries. -- Configure your proxy settings (work in progress.) -- Configure CPU, memory, and disk of Podman machines (work in progress.) -- Handle multiple container engines at the same time (Podman, Docker, Lima...) - -### You can also bring new features with Podman Desktop plug-ins or Docker Desktop extensions - - Manage Podman and other container engines from a single UI and tray. - https://github.com/containers/podman-desktop/releases/expanded_assets/v1.20.2 - 2022 Red Hat, Inc - podman desktop podman-machine containers ui - https://github.com/containers/podman-desktop/tree/main/.chocolatey - https://github.com/containers/podman-desktop - https://podman-desktop.io/docs/intro - - - - - diff --git a/.chocolatey/podman-desktop/tools/chocolateyInstall.ps1 b/.chocolatey/podman-desktop/tools/chocolateyInstall.ps1 deleted file mode 100644 index a81b1feafe..0000000000 --- a/.chocolatey/podman-desktop/tools/chocolateyInstall.ps1 +++ /dev/null @@ -1,16 +0,0 @@ -$ErrorActionPreference = 'Stop' - -$packageArgs = @{ - packageName = 'podman-desktop' - fileType = 'exe' - softwareName = 'PodmanDesktop' - - url64bit = 'https://github.com/podman-desktop/podman-desktop/releases/download/v1.20.2/podman-desktop-1.20.2-setup.exe' - checksumType = 'sha256' - checksum64 = 'f6a2e9a48a1685515fa6d401834afe2a535cd928999ccf9da07218c595a5926f' - - silentArgs = '/S' - validExitCodes = @(0) -} - -Install-ChocolateyPackage @packageArgs diff --git a/.chocolatey/podman-desktop/tools/chocolateyUninstall.ps1 b/.chocolatey/podman-desktop/tools/chocolateyUninstall.ps1 deleted file mode 100644 index 14d7e82975..0000000000 --- a/.chocolatey/podman-desktop/tools/chocolateyUninstall.ps1 +++ /dev/null @@ -1,27 +0,0 @@ -$ErrorActionPreference = 'Stop'; - -$packageName = 'podman-desktop' - -$uninstalled = $false -[array]$key = Get-UninstallRegistryKey -SoftwareName 'PodmanDesktop' - -if ($key.Count -eq 1) { - $key | % { - $packageArgs = @{ - packageName = $packageName - fileType = 'EXE' - silentArgs = '/S' - validExitCodes = @(0) - file = "$($_.UninstallString.Trim('"'))" - } - - Uninstall-ChocolateyPackage @packageArgs - } -} elseif ($key.Count -eq 0) { - Write-Warning "$packageName has already been uninstalled by other means." -} elseif ($key.Count -gt 1) { - Write-Warning "$key.Count matches found!" - Write-Warning "To prevent accidental data loss, no programs will be uninstalled." - Write-Warning "Please alert package maintainer the following keys were matched:" - $key | % {Write-Warning "- $_.DisplayName"} -} diff --git a/.chocolatey/podman-desktop/update.ps1 b/.chocolatey/podman-desktop/update.ps1 deleted file mode 100644 index be1a42589c..0000000000 --- a/.chocolatey/podman-desktop/update.ps1 +++ /dev/null @@ -1,32 +0,0 @@ -import-module au - -$version = $env:VERSION -$releases = 'https://github.com/containers/podman-desktop/releases/expanded_assets/v' + $version - -function global:au_SearchReplace { - @{ - ".\tools\chocolateyInstall.ps1" = @{ - "(?i)(^\s*url64bit\s*=\s*)('.*')" = "`$1'$($Latest.URL64)'" - "(?i)(^\s*checksum64\s*=\s*)('.*')" = "`$1'$($Latest.Checksum64)'" - } - ".\podman-desktop.nuspec" = @{ - "\.+" = "$($Latest.Version)" - "\.+" = "$($Latest.ReleaseNotes)" - } - } -} - -function global:au_GetLatest { - $download_page = Invoke-WebRequest -Uri $releases - - $url64 = $download_page.links | ? href -match '-setup.exe$' | % href | select -First 1 - $version = (Split-Path ( Split-Path $url64 ) -Leaf).Substring(1) - - @{ - URL64 = 'https://github.com' + $url64 - Version = $version - ReleaseNotes = $releases - } -} - -update -ChecksumFor 64 diff --git a/.clomonitor.yml b/.clomonitor.yml deleted file mode 100644 index 4944f89af4..0000000000 --- a/.clomonitor.yml +++ /dev/null @@ -1,21 +0,0 @@ -# -# 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 - -# Checks exemptions -exemptions: - - check: artifacthub_badge # Check identifier (see https://github.com/cncf/clomonitor/blob/main/docs/checks.md#exemptions) - reason: "Podman Desktop is a desktop application and does not offer Cloud Native packages." diff --git a/.devcontainer/.parent/Containerfile b/.devcontainer/.parent/Containerfile deleted file mode 100644 index dd84414874..0000000000 --- a/.devcontainer/.parent/Containerfile +++ /dev/null @@ -1,60 +0,0 @@ -FROM quay.io/fedora/fedora:37 - -# install Node.js + yarn -ENV NODE_VERSION 16.18.1 -RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" && \ - tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 && \ - rm "node-v$NODE_VERSION-linux-x64.tar.gz" && \ - npm install -g yarn - -RUN dnf -y update && \ - yum -y reinstall shadow-utils && \ - yum install -y git \ - # dependencies for Podman Desktop - nss \ - atk \ - at-spi2-atk \ - cups-libs \ - gtk3 \ - # for remote Display - fluxbox \ - tigervnc-server \ - xorg-x11-fonts-Type1 \ - novnc \ - supervisor \ - xdpyinfo \ - # for podman - podman \ - fuse-overlayfs --exclude container-selinux \ - xterm && \ - rm -rf /var/cache /var/log/dnf* /var/log/yum.* -COPY entrypoint.sh /entrypoint.sh -COPY supervisord.conf /etc/supervisord.conf -COPY fluxbox /usr/share/fluxbox/init -RUN useradd -u 1000 podman-desktop && echo podman-desktop:10000:5000 > /etc/subuid && echo podman-desktop:10000:5000 > /etc/subgid - -# Disable telemetry -RUN mkdir -p /home/podman-desktop/.local/share/containers/podman-desktop/configuration && \ - echo '{"telemetry.enabled": false, "telemetry.check": true}' > /home/podman-desktop/.local/share/containers/podman-desktop/configuration/settings.json - -# expose port for VNC -EXPOSE 8080 - -# initialize conf files -ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf -ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman-desktop/.config/containers/containers.conf - -# set permissions -RUN chown podman-desktop:podman-desktop -R /home/podman-desktop && chmod 644 /etc/containers/containers.conf && \ - mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers /var/lib/shared/vfs-images /var/lib/shared/vfs-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock; touch /var/lib/shared/vfs-images/images.lock; touch /var/lib/shared/vfs-layers/layers.lock && \ - mkdir -p /run/user/1000 && chown podman-desktop:podman-desktop /run/user/1000 - - -ENV _CONTAINERS_USERNS_CONFIGURED="" - -# socket path for podman -ENV XDG_RUNTIME_DIR=/run/user/1000 - -ENTRYPOINT [ "/entrypoint.sh" ] -USER podman-desktop -CMD ["tail", "-f", "/dev/null"] \ No newline at end of file diff --git a/.devcontainer/.parent/entrypoint.sh b/.devcontainer/.parent/entrypoint.sh deleted file mode 100755 index 66722d7fb2..0000000000 --- a/.devcontainer/.parent/entrypoint.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -# -# Copyright (C) 2022 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 - -# Ensure $HOME exists when starting -if [ ! -d "${HOME}" ]; then - mkdir -p "${HOME}" -fi - -# Setup $PS1 for a consistent and reasonable prompt -if [ -w "${HOME}" ] && [ ! -f "${HOME}"/.bashrc ]; then - echo "PS1='\s-\v \w \$ '" > "${HOME}"/.bashrc -fi - -# Add current (arbitrary) user to /etc/passwd and /etc/group -if ! whoami > /dev/null 2>&1; then - if [ -w /etc/passwd ]; then - echo "update passwd file" - echo "${USER_NAME:-user}:x:$(id -u):0:${USER_NAME:-user} user:${HOME}:/bin/bash" >> /etc/passwd - echo "${USER_NAME:-user}:x:$(id -u):" >> /etc/group - fi -fi -/usr/bin/supervisord -c /etc/supervisord.conf diff --git a/.devcontainer/.parent/fluxbox b/.devcontainer/.parent/fluxbox deleted file mode 100644 index d182474012..0000000000 --- a/.devcontainer/.parent/fluxbox +++ /dev/null @@ -1,13 +0,0 @@ -! If you're looking for settings to configure, they won't be saved here until -! you change something in the fluxbox configuration menu. - -session.menuFile: ~/.fluxbox/menu -session.keyFile: ~/.fluxbox/keys -! Change the default theme -session.styleFile: /usr/share/fluxbox/styles/bora_blue -session.configVersion: 13 -session.screen0.toolbar.widthPercent: 100 -session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S -session.screen0.toolbar.tools: prevworkspace, workspacename, nextworkspace, clock, prevwindow, nextwindow, iconbar, systemtray -! disable toolbar -session.screen0.toolbar.visible: false diff --git a/.devcontainer/.parent/supervisord.conf b/.devcontainer/.parent/supervisord.conf deleted file mode 100644 index e94804d025..0000000000 --- a/.devcontainer/.parent/supervisord.conf +++ /dev/null @@ -1,18 +0,0 @@ -[supervisord] -nodaemon=true -logfile = /home/podman-desktop/supervisord.log -pidfile = /home/podman-desktop/supervisord.pid - -[program:tigervnc] -environment=DISPLAY=":0" -command=vncserver :0 -geometry 1920x1080 -depth 24 -SecurityTypes None -user=podman-desktop -autorestart=false -priority=300 - -[program:windowmanager] -environment=DISPLAY=":0" -command=fluxbox -user=podman-desktop -autorestart=true -priority=350 diff --git a/.devcontainer/Containerfile b/.devcontainer/Containerfile deleted file mode 100644 index c0b136dc7a..0000000000 --- a/.devcontainer/Containerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM quay.io/podman-desktop/devcontainer-parent:next - diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index b1d5fbe7e0..0000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "Podman Desktop", - "build": { - "dockerfile": "Containerfile" - }, - // do not use root - "containerUser": "podman-desktop", - // Add the IDs of extensions you want installed when the container is created. - "extensions": ["svelte.svelte-vscode", "bradlc.vscode-tailwindcss"], - "features": {}, - "runArgs": [ - "--cap-add=sys_admin", - "--security-opt", - "seccomp=unconfined", - "--device", - "/dev/fuse", - "--security-opt", - "label=disable", - "--security-opt", - "apparmor=unconfined" - ], - "postStartCommand": "${containerWorkspaceFolder}/.devcontainer/postStartCommand.sh", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", - "workspaceFolder": "/workspace", - "onCreateCommand": "${containerWorkspaceFolder}/.devcontainer/onCreateCommand.sh", - "updateContentCommand": "${containerWorkspaceFolder}/.devcontainer/updateContentCommand.sh", - "remoteEnv": { - "DISPLAY": ":0" - }, - "portsAttributes": { - "9000": { - "label": "vnc", - "onAutoForward": "openPreview" - }, - "3000": { - "label": "website" - } - }, - "hostRequirements": { - "memory": "8gb" - } -} diff --git a/.devcontainer/onCreateCommand.sh b/.devcontainer/onCreateCommand.sh deleted file mode 100755 index f34bba60e1..0000000000 --- a/.devcontainer/onCreateCommand.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2022 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 - -# This script is run inside the container after the repo has been cloned. -# Launch yarn to install dependencies and build the project. -yarn - -# build podman-desktop -MODE=production yarn run build && yarn run electron-builder build --linux --dir --config .electron-builder.config.cjs diff --git a/.devcontainer/postStartCommand.sh b/.devcontainer/postStartCommand.sh deleted file mode 100755 index 6cbd9d15fd..0000000000 --- a/.devcontainer/postStartCommand.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2022 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 - -# Need to start all services -/usr/bin/supervisord -c /etc/supervisord.conf & - -# wait X server to be ready or after 2mn exit -echo "Waiting for X server to be ready" -timeout 120 bash -c 'until xdpyinfo -display :0 &> /dev/null; do printf "."; sleep 1; done' - -# launch podman desktop -echo "Launching Podman Desktop" -cd dist/linux-unpacked/&& ./podman-desktop & - - -# Launch the 9000 redirect after 20 seconds -sleep 20 -websockify --web=/usr/share/novnc localhost:9000 localhost:5900 & - -# launch the website rendering -echo "Launching Website" -cd website && yarn start diff --git a/.devcontainer/updateContentCommand.sh b/.devcontainer/updateContentCommand.sh deleted file mode 100755 index ba3fe69ea8..0000000000 --- a/.devcontainer/updateContentCommand.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2022 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 - -# Do nothing for now (maybe need to use postStartCommand.sh) diff --git a/.electron-builder.config.cjs b/.electron-builder.config.cjs index 94555c45be..8dc7093319 100644 --- a/.electron-builder.config.cjs +++ b/.electron-builder.config.cjs @@ -70,8 +70,8 @@ async function addElectronFuses(context) { * @see https://www.electron.build/configuration/configuration */ const config = { - productName: 'Podman Desktop', - appId: 'io.podman_desktop.PodmanDesktop', + productName: 'Kortex', + appId: 'dev.kortex-hub.Kortex', directories: { output: 'dist', buildResources: 'buildResources', @@ -82,58 +82,27 @@ const config = { const DEFAULT_ASSETS = []; context.packager.config.extraResources = DEFAULT_ASSETS; - // universal build, add both pkg files - // this is hack to avoid issue https://github.com/electron/universal/issues/36 - if ( - context.appOutDir.endsWith('mac-universal-x64-temp') || - context.appOutDir.endsWith('mac-universal-arm64-temp') - ) { - context.packager.config.extraResources = DEFAULT_ASSETS; - context.packager.config.extraResources.push( - 'extensions/podman/packages/extension/assets/podman-installer-macos-universal*.pkg', - ); - return; - } - - if (context.arch === Arch.arm64 && context.electronPlatformName === 'darwin') { - context.packager.config.extraResources.push( - 'extensions/podman/packages/extension/assets/podman-installer-macos-aarch64-*.pkg', - ); - context.packager.config.extraResources.push('extensions/podman/packages/extension/assets/podman-image-arm64.zst'); - } - - if (context.arch === Arch.x64 && context.electronPlatformName === 'darwin') { - context.packager.config.extraResources.push( - 'extensions/podman/packages/extension/assets/podman-installer-macos-amd64-*.pkg', - ); - context.packager.config.extraResources.push('extensions/podman/packages/extension/assets/podman-image-x64.zst'); - } - if (context.electronPlatformName === 'win32') { // add the win-ca package context.packager.config.extraResources.push({ from: 'node_modules/win-ca/lib/roots.exe', to: 'win-ca/roots.exe', }); - // add podman installer - context.packager.config.extraResources.push('extensions/podman/packages/extension/assets/podman-*.exe'); - } - if (context.arch === Arch.x64 && context.electronPlatformName === 'win32') { - context.packager.config.extraResources.push('extensions/podman/packages/extension/assets/podman-image-x64.zst'); - } - if (context.arch === Arch.arm64 && context.electronPlatformName === 'win32') { - context.packager.config.extraResources.push('extensions/podman/packages/extension/assets/podman-image-arm64.zst'); } + context.packager.config.extraResources.push({ + from: 'packages/main/src/chat/db/migrations/', + to: 'chat/db/migrations', + }); }, afterPack: async context => { await addElectronFuses(context); }, files: ['packages/**/dist/**', 'extensions/**/builtin/*.cdix/**', 'packages/main/src/assets/**'], portable: { - artifactName: `podman-desktop${artifactNameSuffix}-\${version}-\${arch}.\${ext}`, + artifactName: `kortex${artifactNameSuffix}-\${version}-\${arch}.\${ext}`, }, nsis: { - artifactName: `podman-desktop${artifactNameSuffix}-\${version}-setup-\${arch}.\${ext}`, + artifactName: `kortex${artifactNameSuffix}-\${version}-setup-\${arch}.\${ext}`, oneClick: false, }, win: { @@ -186,21 +155,21 @@ const config = { '--talk-name=org.freedesktop.Flatpak', ], useWaylandFlags: 'false', - artifactName: 'podman-desktop-${version}.${ext}', + artifactName: 'kortex-${version}.${ext}', runtimeVersion: '24.08', branch: 'main', files: [ - ['.flatpak-appdata.xml', '/share/metainfo/io.podman_desktop.PodmanDesktop.metainfo.xml'], - ['buildResources/icon-512x512.png', '/share/icons/hicolor/512x512/apps/io.podman_desktop.PodmanDesktop.png'], + ['.flatpak-appdata.xml', '/share/metainfo/dev.kortex.Kortex.metainfo.xml'], + ['buildResources/icon-1024x1024.png', '/share/icons/hicolor/1024x1024/apps/io.podman_desktop.PodmanDesktop.png'], ], }, linux: { category: 'Development', - icon: './buildResources/icon-512x512.png', + icon: './buildResources/icon-1024x1024.png', target: ['flatpak', { target: 'tar.gz', arch: ['x64', 'arm64'] }], }, mac: { - artifactName: `podman-desktop${artifactNameSuffix}-\${version}-\${arch}.\${ext}`, + artifactName: `kortex${artifactNameSuffix}-\${version}-\${arch}.\${ext}`, hardenedRuntime: true, entitlements: './node_modules/electron-builder-notarize/entitlements.mac.inherit.plist', target: { @@ -229,8 +198,8 @@ const config = { ], }, protocols: { - name: 'Podman Desktop', - schemes: ['podman-desktop'], + name: 'Kortex', + schemes: ['kortex'], role: 'Editor', }, publish: { diff --git a/.flatpak-appdata.xml b/.flatpak-appdata.xml index 9ebcf8e29f..9fdf86f5fc 100644 --- a/.flatpak-appdata.xml +++ b/.flatpak-appdata.xml @@ -1,154 +1,24 @@ - io.podman_desktop.PodmanDesktop - Podman Desktop + dev.kortex_hub.Kortex + Kortex Apache-2.0 Red Hat, Inc. - Manage Podman and other container engines from a single UI + Kortex CC0-1.0 - https://podman-desktop.io/ - https://github.com/containers/podman-desktop/issues - https://podman-desktop.io/docs/intro + https://TODO + https://TODO + https://TODO -

Podman Desktop is an open source graphical tool enabling you to seamlessly work with containers and Kubernetes from your local environment.

-

Build, run and manage containers:

-
    -
  • Build images from Containerfile or Dockerfile.
  • -
  • Pull images from remote registries.
  • -
  • Start, Stop, Restart containers and pods.
  • -
  • Easily get a terminal in your container.
  • -
  • Inspect container logs.
  • -
  • Push images to OCI registries.
  • -
  • Deploy and test images on Kubernetes.
  • -
-

Multiple configuration options

-
    -
  • Manage OCI registries; add, edit, or delete registries.
  • -
  • Configure your proxy settings (work in progress.)
  • -
  • Configure CPU, memory, and disk of Podman machines (work in progress.)
  • -
  • Handle multiple container engines at the same time (Podman, Docker, Lima...)
  • -
-

You can also bring new features with Podman Desktop plug-ins or Docker Desktop extensions.

- io.podman_desktop.PodmanDesktop.desktop + dev.kortex_hub.Kortex - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/dashboard.png - Main dashboard - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/dashboard.png - Main dashboard - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/containers.png - List view of running containers - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/containers.png - List view of running containers - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/pods.png - List view of running pods - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/pods.png - List view of running pods - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/images.png - List view of downloaded images - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/images.png - List view of downloaded images - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/volumes.png - List view of existing volumes - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/volumes.png - List view of existing volumes - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/kubernetes.png - Overview of Kubernetes clusters - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/kubernetes.png - Overview of Kubernetes clusters - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/extensions.png - List of supported extensions - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/extensions.png - List of supported extensions - - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/light/settings.png - Overview of supported customizations - - - https://raw.githubusercontent.com/containers/podman-desktop-media/8786db2a59e62d7bebbe4602c0475a1cadf2f54c/screenshots/linux/dark/settings.png - Overview of supported customizations - fbenoit_at_redhat_com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/.flatpak.desktop b/.flatpak.desktop index 806f83519a..11586f9ebe 100644 --- a/.flatpak.desktop +++ b/.flatpak.desktop @@ -1,10 +1,10 @@ [Desktop Entry] -Name=Podman Desktop +Name=Kortex Exec=run.sh %U Terminal=false Type=Application -Icon=io.podman_desktop.PodmanDesktop -StartupWMClass=Podman Desktop +Icon=dev.kortex_hub.Kortex +StartupWMClass=Kortex Categories=Development; -Keywords=container;pod;image;volume;kubernetes;docker;kind;kubectl; -X-Flatpak=io.podman_desktop.PodmanDesktop +Keywords= +X-Flatpak=dev.kortex_hub.Kortex diff --git a/.github/ISSUE_TEMPLATE/UX_Template.yml b/.github/ISSUE_TEMPLATE/UX_Template.yml deleted file mode 100644 index 990d853228..0000000000 --- a/.github/ISSUE_TEMPLATE/UX_Template.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: UX Request -description: UX Request Form -labels: [UX/UI Issue, Graphic design] -projects: ["podman-desktop/4"] - -body: - - type: markdown - attributes: - value: | - Before opening a UX request, please search for existing issues. - - --- - - - type: textarea - id: UX-description - attributes: - label: UX Description - description: Describe the request - validations: - required: true - - - type: dropdown - id: request-type - attributes: - label: Request type - description: "What type of request is this?" - options: - - "A logo design" - - "An icon" - - "An infographic/chart" - - "A template or design for printed materials" - - "Swag design" - - "Graphic design not covered by the above" - - "UX analysis/suggestions for improvement" - - "User research" - - "User testing" - - "Application mockups/designs" - - "Website mockups/designs" - - "Something else UX-related" - - - - type: textarea - id: Contacts - attributes: - label: Primary Contact - description: Who is the contact the design team can speak with about this issue? - - - type: textarea - id: Deadline - attributes: - label: Deadline for request - description: When do you need this? If this is for an event, please let us know the date of the event and any lead time you need to get materials produced. diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 7270288ee5..0000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Bug 🐞 -description: Report a bug report -labels: [kind/bug 🐞] -projects: ["podman-desktop/4"] - -body: - - type: markdown - attributes: - value: | - Before opening a bug report, please search for the behaviour in the existing issues. - - --- - - Thank you for taking the time to file a bug report. To address this bug as fast as possible, we need some information. - - - type: textarea - id: bug-description - attributes: - label: Bug description - description: What happened? - validations: - required: true - - - type: input - id: os - attributes: - label: Operating system - description: "Which operating system are you on? Please provide the version as well. If you are on a Mac, please specify Apple silicon or Intel." - placeholder: "macOS Ventura 13.4 (Arm), Windows 11" - validations: - required: true - - - type: dropdown - id: install - attributes: - label: Installation Method - description: "How did you install Podman Desktop ?" - options: - - "Installer from website/GitHub releases" - - "Brew (macOS)" - - "Chocolatey (Windows)" - - "Flathub (Linux)" - - "Scoop (Windows)" - - "Winget (Windows)" - - "Other" - - - type: dropdown - id: version - attributes: - label: Version - description: What version of the software are you running? - options: - - "1.19.2" - - "1.20.0" - - "next (development version)" - - "1.19.1" - - "1.18.0" - - "1.17.0" - - "1.16.2" - - "1.16.1" - - "1.16.0" - - "1.15.x" - - "1.14.x" - - "1.13.x" - - "1.12.x" - - "1.11.x" - - "1.10.x" - - "1.9.x" - - "1.8.x" - - "1.7.x" - - "1.6.x" - - "1.5.x" - - "1.4.x" - - "1.3.x" - - "1.2.x" - - "1.1.x" - - "1.0.x" - - "0.x" - validations: - required: true - - - type: textarea - id: steps - attributes: - label: Steps to reproduce - description: What steps do we need to take to reproduce this error? - - - type: textarea - id: logs - attributes: - label: Relevant log output - description: If applicable, provide relevant log output. - render: shell - - - type: textarea - id: additional-context - attributes: - label: Additional context - description: Add any other context or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0cec..0000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml deleted file mode 100644 index f3250d2153..0000000000 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Enhancement ✨ -description: Suggest an enhancement to an existing feature -labels: [kind/enhancement ✨] -projects: ["podman-desktop/4"] - -body: - - type: markdown - attributes: - value: | - Before opening a new enhancement, please search for potential existing issues. - - --- - - Thank you for taking the time to file an enhancement request, we appreciate and value your time to help the project! - - - type: textarea - id: problem - attributes: - label: Is your enhancement related to a problem? Please describe - description: A clear and concise description of what the problem is. - placeholder: I'm always frustrated when [...] - validations: - required: true - - - type: textarea - id: solution - attributes: - label: Describe the solution you'd like - description: A clear and concise description of what you want to happen. - validations: - required: true - - - type: textarea - id: alternatives - attributes: - label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. - - - type: textarea - id: additional-context - attributes: - label: Additional context - description: Add any other context or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/epic.yml b/.github/ISSUE_TEMPLATE/epic.yml deleted file mode 100644 index 5c79b8ab5a..0000000000 --- a/.github/ISSUE_TEMPLATE/epic.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Epic ⚡ -description: A high-level feature -labels: [kind/epic ⚡] -projects: ["podman-desktop/4","podman-desktop/7"] - -body: - - type: markdown - attributes: - value: | - Epics are normally created by the development team, to group a set of related features and plan work across multiple sprints. - The features this epic includes are referenced with the text of the epic. - - - type: textarea - id: domain - attributes: - label: Epic domain - description: A clear and concise description of the feature area or domain that this epic will address. - placeholder: Podman Desktop should support [...] - validations: - required: true - - - type: textarea - id: additional-context - attributes: - label: Additional context - description: Add any other context or screenshots here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index d22d237160..0000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Feature 💡 -description: Suggest an idea for this project -labels: [kind/feature 💡] -projects: ["podman-desktop/4"] - -body: - - type: markdown - attributes: - value: | - Before opening a feature request, please search for potential existing issues. - - --- - - Thank you for taking the time to file a feature request, we appreciate and value your time to help the project! - - - type: textarea - id: problem - attributes: - label: Is your feature request related to a problem? Please describe - description: A clear and concise description of what the problem is. - placeholder: I'm always frustrated when [...] - validations: - required: true - - - type: textarea - id: solution - attributes: - label: Describe the solution you'd like - description: A clear and concise description of what you want to happen. - validations: - required: true - - - type: textarea - id: alternatives - attributes: - label: Describe alternatives you've considered - description: A clear and concise description of any alternative solutions or features you've considered. - - - type: textarea - id: additional-context - attributes: - label: Additional context - description: Add any other context or screenshots here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 095ddb1160..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,18 +0,0 @@ -### What does this PR do? - -### Screenshot / video of UI - - - -### What issues does this PR fix or reference? - - - -### How to test this PR? - - - -- [ ] Tests are covering the bug fix or the new feature diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5218f27a5c..1f629f5c77 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,69 +1,61 @@ -# Set update schedule for GitHub Actions +# +# 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 version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" - # check at 3am UTC - time: "03:00" - open-pull-requests-limit: 20 - - package-ecosystem: "npm" - directory: "/" + directory: "/" # adjust if your package.json is in a subfolder schedule: - interval: daily - # check at 3am UTC - time: "03:00" - open-pull-requests-limit: 20 + interval: "daily" + # Only allow updates for these specific packages + allow: + - dependency-name: "@ai-sdk/*" + dependency-type: "all" + - dependency-name: "@modelcontextprotocol/sdk" + dependency-type: "all" + - dependency-name: "@oslojs/*" + dependency-type: "all" + - dependency-name: "@types/better-sqlite3" + dependency-type: "all" + - dependency-name: "@types/mime-types" + dependency-type: "all" + - dependency-name: "@types/ms" + dependency-type: "all" + - dependency-name: "ai" + dependency-type: "all" + - dependency-name: "better-sqlite3" + dependency-type: "all" + - dependency-name: "bcrypt-ts" + dependency-type: "all" + - dependency-name: "drizzle-orm" + dependency-type: "all" + - dependency-name: "mime-types" + dependency-type: "all" + - dependency-name: "ms" + dependency-type: "all" + - dependency-name: "neverflow" + dependency-type: "all" + - dependency-name: "openapi-typescript" + dependency-type: "all" + - dependency-name: "random-words" + dependency-type: "all" groups: - argosci: - applies-to: version-updates - patterns: - - "@argos-ci/*" - storybook: - applies-to: version-updates - patterns: - - "@storybook/*" - - "storybook" - - "storybook-dark-mode" - commitlint: - applies-to: version-updates - patterns: - - "@commitlint/*" - docusaurus: - applies-to: version-updates - patterns: - - "@docusaurus/*" - eslint: + ai-sdk: applies-to: version-updates patterns: - - "eslint" - - "@eslint/*" - fortawesome: - applies-to: version-updates - patterns: - - "@fortawesome/*" - typescript-eslint: - applies-to: version-updates - patterns: - - "@typescript-eslint/*" - - "typescript-eslint" - tailwindcss: - applies-to: version-updates - patterns: - - "@tailwindcss/*" - - "tailwindcss" - vitest: - applies-to: version-updates - patterns: - - "@vitest/*" - - "vitest" - typedoc: - applies-to: version-updates - patterns: - - "typedoc" - - "typedoc-plugin-markdown" - - "docusaurus-plugin-typedoc" - + - "@ai-sdk/mcp" + - "ai" diff --git a/.github/semantic.yml b/.github/semantic.yml deleted file mode 100644 index 857c9eff59..0000000000 --- a/.github/semantic.yml +++ /dev/null @@ -1,3 +0,0 @@ -enabled: true -titleAndCommits: true -allowMergeCommits: true diff --git a/.github/workflows/argos.yaml b/.github/workflows/argos.yaml deleted file mode 100644 index abdf098412..0000000000 --- a/.github/workflows/argos.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# -# Copyright (C) 2023-2024 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 - -name: Argos CI Screenshots - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - 'package.json' - - 'pnpm-lock.yaml' - - 'storybook/**' - - 'website/**' - - 'website-argos/**' - pull_request: - branches: - - main - paths: - - '.github/workflows/argos.yaml' - - 'package.json' - - 'pnpm-lock.yaml' - - 'storybook/**' - - 'website/**' - - 'website-argos/**' - -permissions: - contents: read - -jobs: - take-screenshots: - name: take screenshots - runs-on: ubuntu-22.04 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run website - run: pnpm website:build - - - name: Install Playwright browsers - working-directory: website-argos - run: pnpm playwright install --with-deps chromium - - - name: Take screenshots with Playwright - run: pnpm website:screenshots - - - name: Upload screenshots to Argos - continue-on-error: true - working-directory: website-argos - run: pnpm upload - env: - ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} diff --git a/.github/workflows/codecov-next.yaml b/.github/workflows/codecov-next.yaml deleted file mode 100644 index 68bb34e1db..0000000000 --- a/.github/workflows/codecov-next.yaml +++ /dev/null @@ -1,57 +0,0 @@ -# -# 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 - -name: Publish codecov report from main branch - -on: - workflow_dispatch: - push: - branches: - - main - -permissions: - contents: read - -jobs: - codecov: - name: Run tests and push coverage result - runs-on: ubuntu-24.04 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run unit tests - run: pnpm test:unit:coverage - - - name: publish codecov report - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 - with: - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/daily-testing-build.yaml b/.github/workflows/daily-testing-build.yaml deleted file mode 100644 index 81e7364df0..0000000000 --- a/.github/workflows/daily-testing-build.yaml +++ /dev/null @@ -1,163 +0,0 @@ -# -# Copyright (C) 2024 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 - -name: Daily testing build -run-name: Testing build of Podman Desktop from Main branch - -on: - workflow_dispatch: - schedule: - - cron: '0 1 * * *' - -env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - DEBUG: electron-builder - -permissions: - contents: read - -jobs: - tag: - name: Tagging - runs-on: ubuntu-24.04 - outputs: - githubTag: ${{ steps.TAG_UTIL.outputs.githubTag }} - desktopVersion: ${{ steps.TAG_UTIL.outputs.desktopVersion }} - releaseId: ${{ steps.create_release.outputs.id }} - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - fetch-depth: 0 - - - name: Generate tag utilities - id: TAG_UTIL - run: | - CURRENT_DAY=$(date +'%Y%m%d') - SHORT_SHA1=$(git rev-parse --short HEAD) - # grab the version from the package.json - PODMAN_DEKSTOP_VERSION=$(jq -r '.version' package.json) - # remove the -next from the version - STRIPPED_VERSION=${PODMAN_DEKSTOP_VERSION%-next} - TAG_PATTERN=${STRIPPED_VERSION}-$(date +'%Y%m%d%H%M')-${SHORT_SHA1} - echo "githubTag=v$TAG_PATTERN" >> ${GITHUB_OUTPUT} - echo "desktopVersion=$TAG_PATTERN" >> ${GITHUB_OUTPUT} - # check for tag existence - exit the workflow - echo "Checking if tag exists: $(git rev-parse -q --verify "$githubTag")" - if [ git rev-parse -q --verify "$githubTag" ]; then - echo "Tag '$githubTag' exists, skipping..." - exit 1; - else - echo "Tag '$githubTag' does not exist yet, continue" - fi - - - name: Create a Tag, commit and push to testing-prereleases - run: | - # quite heavy solution, we might only consider crating a tag, but not actually pushing whole state of the repo - echo "Setting github.actor: ${{ github.actor }} and id: ${{ github.actor_id }}" - git config --local user.name ${{ github.actor }} - git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" package.json - find extensions/* -maxdepth 2 -name "package.json" | xargs -I {} sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" {} - # change the repository field to be the prerelease repository in package.json file - sed -i "s#\"repository\":\ \"\(.*\)\",#\"repository\":\ \"https://github.com/podman-desktop/testing-prereleases\",#g" package.json - cat package.json - git add package.json extensions/*/package.json - # get rid of .github/workflows - requires additional permissions - rm -rf .github/workflows/* - git add .github/ - git commit -m "chore: tag ${{ steps.TAG_UTIL.outputs.githubTag }}" - echo "Tagging with ${{ steps.TAG_UTIL.outputs.githubTag }}" - git tag ${{ steps.TAG_UTIL.outputs.githubTag }} - # push tag to the prereleases repository - git remote add prereleases https://github.com/podman-desktop/testing-prereleases - git push prereleases ${{ steps.TAG_UTIL.outputs.githubTag }} - - - name: Create Release - id: create_release - uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1.18.0 - with: - tag: ${{ steps.TAG_UTIL.outputs.githubTag }} - name: ${{ steps.TAG_UTIL.outputs.githubTag }} - draft: true - prerelease: true - owner: podman-desktop - repo: testing-prereleases - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - - build: - name: Build / ${{ matrix.os }} - needs: tag - runs-on: ${{ matrix.os }} - env: - ELECTRON_ENABLE_INSPECT: true - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-24.04, windows-2025, macos-15] - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: podman-desktop/testing-prereleases - ref: ${{ needs.tag.outputs.githubTag }} - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Install Flatpak dependencies - if: startsWith(matrix.os, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install flatpak -y - sudo apt-get install flatpak-builder -y - sudo apt-get install elfutils -y - flatpak remote-add --if-not-exists flathub --user https://flathub.org/repo/flathub.flatpakrepo - flatpak install flathub --no-static-deltas --user -y org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 - - - name: Run Build and Compile on all platforms - timeout-minutes: 60 - run: pnpm compile:next - - release: - needs: [tag, build] - name: Release - runs-on: ubuntu-24.04 - steps: - - name: id - run: echo the release id is ${{ needs.tag.outputs.releaseId }} - - - name: Publish release - uses: StuYarrow/publish-release@01f2a1365bacd77bad861873a7fdf274ab49eefd # v1.1.2 - env: - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - with: - id: ${{ needs.tag.outputs.releaseId }} - repo: testing-prereleases - owner: podman-desktop diff --git a/.github/workflows/downloads-count.yaml b/.github/workflows/downloads-count.yaml deleted file mode 100644 index 3abfd2f56d..0000000000 --- a/.github/workflows/downloads-count.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# -# 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 - -# Grab downloads - -name: Downloads Count - -on: - schedule: - - cron: '0 0 * * *' - -permissions: - contents: read - -jobs: - - get-all-releases-count: - runs-on: ubuntu-24.04 - - steps: - - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - id: process-downloads - with: - script: | - const resp = await github.paginate('GET /repos/{owner}/{repo}/releases{?per_page,page}', { - owner: 'containers', - repo: 'podman-desktop', - per_page: 100 - }, (response) => response.data.map(({ tag_name, assets }) => ({ tag_name, assets }))); - - const releases = resp; - - // Keep only tag_name and asset.name and asset.download_count from releases - releases.forEach((release) => { release.assets = release.assets.map(({ name, download_count }) => ({ name, download_count })) }) - - let totalMac = 0; - let totalWin = 0; - let totalLinux = 0; - - // count the total number of downloads for each release for mac,windows,linux assests - releases.forEach((release) => { - release.assets = release.assets.filter((asset) => { - if (asset.name.endsWith(".dmg") || (asset.name.endsWith("arm64.zip") || asset.name.endsWith("x64.zip") || asset.name.endsWith("universal.zip")) ){ - totalMac += asset.download_count; - } else if (asset.name.endsWith(".exe")){ - totalWin += asset.download_count; - } else if (asset.name.endsWith(".tar.gz") || asset.name.endsWith(".flatpak")){ - totalLinux+=asset.download_count; - } - }) - }) - - let totalDownloads = totalMac + totalWin + totalLinux; - - // Export results as environment variables - core.exportVariable('totalDownloads', totalDownloads); - core.exportVariable('totalMac', totalMac); - core.exportVariable('totalWin', totalWin); - core.exportVariable('totalLinux', totalLinux); - - - name: send-event-4 - run: | - curl https://api.segment.io/v1/track \ - -X POST \ - -H 'Content-Type: application/json' \ - -d '{ - "userId": "gh-action", - "event": "download-count", - "properties": { - "mac": "${{ env.totalMac }}", - "windows": "${{ env.totalWin }}", - "linux": "${{ env.totalLinux }}", - "total": "${{ env.totalDownloads }}" - } - }' \ - -u ${{ secrets.SEGMENT_WRITE_KEY }} diff --git a/.github/workflows/e2e-kubernetes-main.yaml b/.github/workflows/e2e-kubernetes-main.yaml deleted file mode 100644 index 02199ec295..0000000000 --- a/.github/workflows/e2e-kubernetes-main.yaml +++ /dev/null @@ -1,156 +0,0 @@ -# -# Copyright (C) 2023-2024 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 - -name: e2e-kubernetes-tests-main - -on: - push: - branches: [main] - - workflow_dispatch: - inputs: - organization: - default: 'podman-desktop' - description: 'Organization of the Podman Desktop repository' - type: string - required: true - repositoryName: - default: 'podman-desktop' - description: 'Podman Desktop repository name' - type: string - required: true - branch: - default: 'main' - description: 'Podman Desktop repo branch' - type: string - required: true - kind_provider: - description: 'Kind cluster provider type' - type: choice - options: - - docker - - podman - required: true - -permissions: - contents: read - -jobs: - e2e-tests: - name: Run All E2E tests - runs-on: ubuntu-24.04 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - permissions: - checks: write # required for mikepenz/action-junit-report - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} - ref: ${{ github.event.inputs.branch }} - if: github.event_name == 'workflow_dispatch' - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - if: github.event_name == 'push' - - - name: Set the default provider type variable - env: - DEFAULT_KIND_PROVIDER: 'docker' - run: | - echo "KIND_PROVIDER=${{ github.event.inputs.kind_provider || env.DEFAULT_KIND_PROVIDER }}" >> $GITHUB_ENV - - - name: Update podman - run: | - echo "ubuntu version from kubic repository to install podman we need (v5)" - ubuntu_version='23.10' - echo "Add unstable kubic repo into list of available sources and get the repo key" - sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" - curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - - echo "Updating all dependencies..." - sudo apt-get update -qq - echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}" - sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1 - echo "install criu manually from static location" - curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb - echo "installing/update podman package..." - sudo apt-get -qq -y install podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \ - sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \ - curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \ - sudo apt-get update && \ - sudo apt-get -y install podman; } - podman version - - - name: Revert unprivileged user namespace restrictions in Ubuntu 24.04 - run: | - # allow unprivileged user namespace - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - - - name: Set cgroup_manager to 'cgroupfs' instead of systemd - run: | - mkdir -p ~/.config/containers - cat <> ~/.config/containers/containers.conf - [engine] - cgroup_manager="cgroupfs" - EOT - podman info - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run E2E kubernetes tests in Production Mode - env: - PODMANDESKTOP_CI_BOT_TOKEN: ${{ secrets.PODMANDESKTOP_CI_BOT_TOKEN }} - TEST_PODMAN_MACHINE: 'true' - SKIP_KIND_INSTALL: 'true' - KIND_PROVIDER_GHA: ${{ env.KIND_PROVIDER }} - ELECTRON_ENABLE_INSPECT: true - run: | - echo "Compiling the Podman Desktop in production mode" - pnpm compile:current --linux dir - path=$(realpath ./dist/linux-unpacked/podman-desktop) - echo "Podman Desktop built binary: $path" - export PODMAN_DESKTOP_BINARY_PATH=$path - pnpm test:e2e:k8s - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: k8s-e2e-tests - path: | - ./tests/**/output/ - !./tests/**/traces/raw diff --git a/.github/workflows/e2e-main.yaml b/.github/workflows/e2e-main.yaml deleted file mode 100644 index 248442569f..0000000000 --- a/.github/workflows/e2e-main.yaml +++ /dev/null @@ -1,338 +0,0 @@ -# -# Copyright (C) 2023-2024 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 - -name: e2e-tests-main - -on: - push: - branches: [main] - - workflow_dispatch: - inputs: - organization: - default: 'podman-desktop' - description: 'Organization of the Podman Desktop repository' - type: string - required: true - repositoryName: - default: 'podman-desktop' - description: 'Podman Desktop repository name' - type: string - required: true - npm_target: - default: 'test:e2e' - description: 'The npm target to run tests. Use "test:e2e:all" to run all test suites, including Kubernetes tests.' - type: string - required: true - branch: - default: 'main' - description: 'Podman Desktop repo branch' - type: string - required: true - kind_provider: - description: 'Kind cluster provider type' - type: choice - options: - - docker - - podman - required: false - update_with_extensions: - description: 'Installation extensions before update' - type: choice - options: - - 'false' - - 'true' - required: false - -permissions: - contents: read - -jobs: - e2e-tests: - name: Run All E2E tests - runs-on: ubuntu-24.04 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - permissions: - checks: write # required for mikepenz/action-junit-report - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} - ref: ${{ github.event.inputs.branch }} - if: github.event_name == 'workflow_dispatch' - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - if: github.event_name == 'push' - - - name: Set the default env. variables - env: - DEFAULT_NPM_TARGET: 'test:e2e' - DEFAULT_KIND_PROVIDER: 'docker' - run: | - echo "NPM_TARGET=${{ github.event.inputs.npm_target || env.DEFAULT_NPM_TARGET }}" >> $GITHUB_ENV - echo "KIND_PROVIDER=${{ github.event.inputs.kind_provider || env.DEFAULT_KIND_PROVIDER }}" >> $GITHUB_ENV - - - name: Update podman - run: | - echo "ubuntu version from kubic repository to install podman we need (v5)" - ubuntu_version='23.10' - echo "Add unstable kubic repo into list of available sources and get the repo key" - sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" - curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - - echo "Updating all dependencies..." - sudo apt-get update -qq - echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}" - sudo apt-get install -qq libprotobuf32t64 python3-protobuf libnet1 - echo "install criu manually from static location" - curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb - echo "installing/update podman package..." - sudo apt-get -qq -y install podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \ - sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \ - curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \ - sudo apt-get update && \ - sudo apt-get -y install podman; } - podman version - - - name: Revert unprivileged user namespace restrictions in Ubuntu 24.04 - run: | - # allow unprivileged user namespace - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - - # Enables multi-architecture support for building multi-arch manifests - - name: Install QEMU for Multi-Arch - run: | - sudo apt install -y qemu-user-static - - - name: Set cgroup_manager to 'cgroupfs' instead of systemd - run: | - mkdir -p ~/.config/containers - cat <> ~/.config/containers/containers.conf - [engine] - cgroup_manager="cgroupfs" - EOT - podman info - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run E2E tests in Production Mode - env: - PODMANDESKTOP_CI_BOT_TOKEN: ${{ secrets.PODMANDESKTOP_CI_BOT_TOKEN }} - TEST_PODMAN_MACHINE: 'true' - SKIP_KIND_INSTALL: 'true' - KIND_PROVIDER_GHA: ${{ env.KIND_PROVIDER }} - ELECTRON_ENABLE_INSPECT: true - run: | - echo "Compiling the Podman Desktop in production mode" - pnpm compile:current --linux dir - path=$(realpath ./dist/linux-unpacked/podman-desktop) - echo "Podman Desktop built binary: $path" - export PODMAN_DESKTOP_BINARY=$path - pnpm ${{ env.NPM_TARGET }} - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: e2e-tests - path: | - ./tests/**/output/ - !./tests/**/traces/raw - - win-update-e2e-test: - name: win update e2e tests - ${{ matrix.installation }} - runs-on: windows-2025 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - permissions: - checks: write # required for mikepenz/action-junit-report - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - installation: ['vanilla', 'custom-extensions'] - exclude: - - installation: ${{ (github.event.inputs.update_with_extensions && github.event.inputs.update_with_extensions == 'true') && 'N/A' || 'custom-extensions' }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} - ref: ${{ github.event.inputs.branch }} - if: github.event_name == 'workflow_dispatch' - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - if: github.event_name == 'push' - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Adjust/Downgrade local podman desktop version Windows - run: | - $version="1.15.0" - jq --arg version "$version" '.version = $version' package.json | Out-File -FilePath package.json_tmp - Move-Item -Path package.json_tmp -Destination package.json -Force - - - name: Build Podman Desktop locally with electron updater included - env: - ELECTRON_ENABLE_INSPECT: true - run: | - pnpm compile:current --win nsis - $path=('./dist/win-unpacked/Podman Desktop.exe' | resolve-path).ProviderPath - echo $path - echo ("PODMAN_DESKTOP_BINARY=" + $path) >> $env:GITHUB_ENV - - - name: Run E2E Update test - env: - INSTALLATION_TYPE: ${{ matrix.installation }} - run: | - echo "${{ env.PODMAN_DESKTOP_BINARY }}" - echo "${{ env.INSTALLATION_TYPE }}" - pnpm test:e2e:update:run - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: update-e2e-test-${{ matrix.installation }} - path: | - ./tests/**/output/ - !./tests/**/traces/raw - - mac-update-e2e-test: - name: mac update e2e tests - runs-on: macos-15 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: ${{ github.event.inputs.organization }}/${{ github.event.inputs.repositoryName }} - ref: ${{ github.event.inputs.branch }} - if: github.event_name == 'workflow_dispatch' - - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - if: github.event_name == 'push' - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Adjust/Downgrade local podman desktop version - run: | - version="1.15.0" - jq --arg version "$version" '.version = $version' package.json > package.json_tmp - mv package.json_tmp package.json - - - name: Build Podman Desktop locally with electron updater included - env: - ELECTRON_ENABLE_INSPECT: true - run: | - pnpm compile:current --mac dmg - dmgPath=$(realpath $(find ./dist -name "*.dmg")) - echo "DMG Path: $dmgPath" - # extract the dmg - hdiutil attach $dmgPath - pdVolumePath=$(find /Volumes -name *1.15.0*) - echo "PD Volume path: $pdVolumePath" - sudo cp -R "$pdVolumePath/Podman Desktop.app" /Applications - # codesign the extracted app - appPath="/Applications/Podman Desktop.app" - sudo codesign --force --deep --sign - "$appPath" - codesign --verify --deep --verbose=2 "$appPath" - binaryPath="$appPath/Contents/MacOS/Podman Desktop" - echo "PODMAN_DESKTOP_BINARY=$binaryPath" >> $GITHUB_ENV - - - name: Run E2E Update test - env: - UPDATE_PODMAN_DESKTOP: true - run: | - echo "${{ env.PODMAN_DESKTOP_BINARY }}" - pnpm test:e2e:update:run - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: mac-update-e2e-test - path: | - ./tests/**/output/ - !./tests/**/traces/raw diff --git a/.github/workflows/merge-group.yaml b/.github/workflows/merge-group.yaml deleted file mode 100644 index 55f6201302..0000000000 --- a/.github/workflows/merge-group.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# 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 - -name: merge-queue -on: - merge_group: - -permissions: - contents: read - -jobs: - dco: - name: DCO - runs-on: ubuntu-24.04 - steps: - - run: echo "This DCO job runs on merge_queue event and does not do anything" - semantic: - name: Semantic PR - runs-on: ubuntu-24.04 - steps: - - run: echo "This DCO job runs on merge_queue event and does not do anything" diff --git a/.github/workflows/next-build.yaml b/.github/workflows/next-build.yaml index 2ee680cf1e..ab6d7e59cd 100644 --- a/.github/workflows/next-build.yaml +++ b/.github/workflows/next-build.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2022-2024 Red Hat, Inc. +# Copyright (C) 2022-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. @@ -39,13 +39,13 @@ jobs: if: github.event.repository.fork == false outputs: githubTag: ${{ steps.TAG_UTIL.outputs.githubTag}} - desktopVersion: ${{ steps.TAG_UTIL.outputs.desktopVersion}} + kortexVersion: ${{ steps.TAG_UTIL.outputs.kortexVersion}} releaseId: ${{ steps.create_release.outputs.id}} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} + token: ${{ secrets.KORTEX_BOT_TOKEN }} fetch-depth: 0 - name: Generate tag utilities id: TAG_UTIL @@ -53,40 +53,40 @@ jobs: CURRENT_DAY=$(date +'%Y%m%d') SHORT_SHA1=$(git rev-parse --short HEAD) # grab the version from the package.json - PODMAN_DEKSTOP_VERSION=$(jq -r '.version' package.json) + KORTEX_VERSION=$(jq -r '.version' package.json) # remove the -next from the version - STRIPPED_VERSION=${PODMAN_DEKSTOP_VERSION%-next} - TAG_PATTERN=${STRIPPED_VERSION}-$(date +'%Y%m%d%H%M')-${SHORT_SHA1} + STRIPPED_VERSION=${KORTEX_VERSION%-next} + TAG_PATTERN=${STRIPPED_VERSION}-next.$(date +'%Y%m%d%H%M')-${SHORT_SHA1} echo "githubTag=v$TAG_PATTERN" >> ${GITHUB_OUTPUT} - echo "desktopVersion=$TAG_PATTERN" >> ${GITHUB_OUTPUT} + echo "kortexVersion=$TAG_PATTERN" >> ${GITHUB_OUTPUT} - name: tag run: | git config --local user.name ${{ github.actor }} git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" package.json - find extensions/* -maxdepth 2 -name "package.json" | xargs -I {} sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" {} + sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.kortexVersion }}\",#g" package.json + find extensions/* -maxdepth 2 -name "package.json" | xargs -I {} sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.kortexVersion }}\",#g" {} # change the repository field to be the prerelease repository in package.json file - sed -i "s#\"repository\":\ \"\(.*\)\",#\"repository\":\ \"https://github.com/podman-desktop/prereleases\",#g" package.json + sed -i "s#\"repository\":\ \"\(.*\)\",#\"repository\":\ \"https://github.com/kortex-hub/prereleases\",#g" package.json cat package.json git add package.json extensions/*/package.json git commit -m "chore: tag ${{ steps.TAG_UTIL.outputs.githubTag }}" echo "Tagging with ${{ steps.TAG_UTIL.outputs.githubTag }}" git tag ${{ steps.TAG_UTIL.outputs.githubTag }} # push tag to the prereleases repository - git remote add prereleases https://github.com/podman-desktop/prereleases + git remote add prereleases https://github.com/kortex-hub/prereleases git push prereleases ${{ steps.TAG_UTIL.outputs.githubTag }} - name: Create Release id: create_release - uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1.18.0 + uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 with: tag: ${{ steps.TAG_UTIL.outputs.githubTag }} name: ${{ steps.TAG_UTIL.outputs.githubTag }} draft: true prerelease: true - owner: containers - repo: podman-desktop-prereleases - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} + owner: kortex-hub + repo: prereleases + token: ${{ secrets.KORTEX_BOT_TOKEN }} build: name: Build / ${{ matrix.os }} @@ -98,9 +98,9 @@ jobs: os: [windows-2025, ubuntu-24.04, macos-15] timeout-minutes: 60 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - repository: podman-desktop/prereleases + repository: kortex-hub/prereleases ref: ${{ needs.tag.outputs.githubTag}} - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 @@ -108,18 +108,18 @@ jobs: with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.x" - - name: setup telemetry key for production - run: | - sed -i -r -e "s/SEGMENT_KEY = '.*'/SEGMENT_KEY = '${{ secrets.SEGMENT_WRITE_KEY }}'/" packages/main/src/plugin/telemetry/telemetry.ts + #- name: setup telemetry key for production + # run: | + # sed -i -r -e "s/SEGMENT_KEY = '.*'/SEGMENT_KEY = '${{ secrets.SEGMENT_WRITE_KEY }}'/" packages/main/src/plugin/telemetry/telemetry.ts - name: Execute pnpm run: pnpm install @@ -143,21 +143,21 @@ jobs: echo "APPLE_APP_SPECIFIC_PASSWORD=${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}" >> $GITHUB_ENV echo "APPLE_TEAM_ID=${{secrets.APPLE_TEAM_ID}}" >> $GITHUB_ENV - - name: Install Azure SignTool on Windows - if: startsWith(matrix.os, 'windows') - run: | - dotnet tool install --global AzureSignTool --version 3.0.0 - echo "AZURE_KEY_VAULT_CERTIFICATE=${{secrets.AZURE_KEY_VAULT_CERTIFICATE}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_CLIENT_ID=${{secrets.AZURE_KEY_VAULT_CLIENT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_SECRET=${{secrets.AZURE_KEY_VAULT_SECRET}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_TENANT_ID=${{secrets.AZURE_KEY_VAULT_TENANT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_URL=${{secrets.AZURE_KEY_VAULT_URL}}" | Out-File -FilePath $env:GITHUB_ENV -Append + #- name: Install Azure SignTool on Windows + # if: startsWith(matrix.os, 'windows') + # run: | + # dotnet tool install --global AzureSignTool --version 3.0.0 + # echo "AZURE_KEY_VAULT_CERTIFICATE=${{secrets.AZURE_KEY_VAULT_CERTIFICATE}}" | Out-File -FilePath $env:GITHUB_ENV -Append + # echo "AZURE_KEY_VAULT_CLIENT_ID=${{secrets.AZURE_KEY_VAULT_CLIENT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append + # echo "AZURE_KEY_VAULT_SECRET=${{secrets.AZURE_KEY_VAULT_SECRET}}" | Out-File -FilePath $env:GITHUB_ENV -Append + # echo "AZURE_KEY_VAULT_TENANT_ID=${{secrets.AZURE_KEY_VAULT_TENANT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append + # echo "AZURE_KEY_VAULT_URL=${{secrets.AZURE_KEY_VAULT_URL}}" | Out-File -FilePath $env:GITHUB_ENV -Append - name: Run Build timeout-minutes: 40 run: pnpm compile:next env: - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} + GITHUB_TOKEN: ${{ secrets.KORTEX_BOT_TOKEN }} release: needs: [tag, build] @@ -170,11 +170,11 @@ jobs: - name: Publish release uses: StuYarrow/publish-release@01f2a1365bacd77bad861873a7fdf274ab49eefd # v1.1.2 env: - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} + GITHUB_TOKEN: ${{ secrets.KORTEX_BOT_TOKEN }} with: id: ${{ needs.tag.outputs.releaseId}} - repo: podman-desktop-prereleases - owner: podman-desktop + repo: prereleases + owner: kortex-hub publish: needs: [tag, release] @@ -183,50 +183,22 @@ jobs: permissions: id-token: write steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 with: - node-version: 22 + node-version: 24 cache: 'pnpm' - name: Execute pnpm run: pnpm install - - name: Set-up npmjs auth token - run: printf "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}\n" >> ~/.npmrc - - - name: Publish API to npmjs + - name: Publish @kortex-app/api to npmjs run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/extension-api/package.json + echo "Using version ${{ needs.tag.outputs.kortexVersion }}" + sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.kortexVersion }}\",#g" packages/extension-api/package.json cd packages/extension-api && pnpm publish --provenance --tag next --no-git-checks --access public - - - name: Publish Webview API to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/webview-api/package.json - cd packages/webview-api && pnpm publish --provenance --tag next --no-git-checks --access public - - - name: Publish ui/svelte to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/ui/package.json - cd packages/ui && pnpm build && pnpm publish --provenance --tag next --no-git-checks --access public - - - name: Publish tests-playwright to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" tests/playwright/package.json - cd tests/playwright && pnpm build && pnpm publish --provenance --tag next --no-git-checks --access public - - - name: Publish Podman Extension API to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" extensions/podman/packages/api/package.json - cd extensions/podman/packages/api - pnpm publish --provenance --tag next --no-git-checks --access public diff --git a/.github/workflows/nightly-e2e.yaml b/.github/workflows/nightly-e2e.yaml new file mode 100644 index 0000000000..0b0435ead0 --- /dev/null +++ b/.github/workflows/nightly-e2e.yaml @@ -0,0 +1,172 @@ +# +# 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 + +name: nightly e2e tests + +on: + schedule: + - cron: '0 2 * * *' + workflow_dispatch: + inputs: + platform: + description: 'Platform to run tests on' + required: false + default: 'all' + type: choice + options: + - all + - windows + - macos + +jobs: + windows-e2e-tests: + name: ${{ matrix.os }} - ${{ matrix.mode }} e2e tests + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + if: ${{ github.event_name == 'schedule' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'windows' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + strategy: + fail-fast: false + matrix: + os: [windows-2025, windows-11-arm] + mode: [prod, dev] + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda + name: Install pnpm + with: + run_install: false + + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 + with: + node-version: 22 + cache: 'pnpm' + + - name: Execute pnpm + run: pnpm install + + - name: Run Smoke E2E tests in ${{ matrix.mode == 'production' && 'Production' || 'Development' }} Mode + shell: bash + run: | + if [ ${{ matrix.mode }} == 'prod' ]; then + echo "Compiling Kortex in production mode" + export ELECTRON_ENABLE_INSPECT=true + pnpm compile:current --win dir + # Determine binary path based on OS + case "${{ matrix.os }}" in + *arm*) + BINARY_PATH="./dist/win-arm64-unpacked/Kortex.exe" + ;; + *) + BINARY_PATH="./dist/win-unpacked/Kortex.exe" + ;; + esac + path=$(realpath $BINARY_PATH) + echo "Kortex built binary: $path" + export KORTEX_BINARY=$path + fi + pnpm test:e2e + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + if: always() + with: + annotate_only: true + fail_on_failure: true + include_passed: true + detailed_summary: true + require_tests: true + report_paths: '**/*results.xml' + + - name: Upload test artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: results-e2e-${{ matrix.os }}-${{ matrix.mode }} + path: tests/playwright/output/** + retention-days: 30 + + macos-e2e-tests: + name: ${{ matrix.os }} - ${{ matrix.mode }} e2e tests + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + if: ${{ github.event_name == 'schedule' || github.event.inputs.platform == 'all' || github.event.inputs.platform == 'macos' }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + strategy: + fail-fast: false + matrix: + os: [macos-15-intel, macos-26] + mode: [prod, dev] + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda + name: Install pnpm + with: + run_install: false + + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 + with: + node-version: 22 + cache: 'pnpm' + + - name: Execute pnpm + run: pnpm install + + - name: Run Smoke E2E tests in ${{ matrix.mode == 'production' && 'Production' || 'Development' }} Mode + shell: bash + run: | + if [ ${{ matrix.mode }} == 'prod' ]; then + echo "Compiling Kortex in production mode" + export ELECTRON_ENABLE_INSPECT=true + pnpm compile:current --mac dir + # Determine binary path based on OS architecture + case "${{ matrix.os }}" in + *intel*) + BINARY_PATH="./dist/mac/Kortex.app/Contents/MacOS/Kortex" + ;; + *) + BINARY_PATH="./dist/mac-arm64/Kortex.app/Contents/MacOS/Kortex" + ;; + esac + path=$(realpath $BINARY_PATH) + echo "Kortex built binary: $path" + export KORTEX_BINARY=$path + fi + pnpm test:e2e + + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + if: always() + with: + annotate_only: true + fail_on_failure: true + include_passed: true + detailed_summary: true + require_tests: true + report_paths: '**/*results.xml' + + - name: Upload test artifacts + uses: actions/upload-artifact@v4 + if: always() + with: + name: results-e2e-${{ matrix.os }}-${{ matrix.mode }} + path: tests/playwright/output/** + retention-days: 30 diff --git a/.github/workflows/pr-check.yaml b/.github/workflows/pr-check.yaml index b54c50548c..ad32da8f14 100644 --- a/.github/workflows/pr-check.yaml +++ b/.github/workflows/pr-check.yaml @@ -1,5 +1,5 @@ # -# Copyright (C) 2022-2024 Red Hat, Inc. +# Copyright (C) 2022-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. @@ -38,14 +38,14 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -63,22 +63,22 @@ jobs: - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-installer-x64 - path: ./dist/podman-desktop*-setup-x64.exe + path: ./dist/kortex-*-setup-x64.exe - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-installer-arm64 - path: ./dist/podman-desktop*-setup-arm64.exe + path: ./dist/kortex-*-setup-arm64.exe - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-exe-x64 - path: ./dist/podman-desktop*-next-x64.exe + path: ./dist/kortex*-next-x64.exe - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: windows-exe-arm64 - path: ./dist/podman-desktop*-next-arm64.exe + path: ./dist/kortex*-next-arm64.exe linux: if: ${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} @@ -88,14 +88,14 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -122,12 +122,12 @@ jobs: - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: linux - path: ./dist/podman-desktop-*.tar.gz + path: ./dist/kortex-*.tar.gz - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: flatpak - path: ./dist/podman-desktop-*.flatpak + path: ./dist/kortex-*.flatpak darwin: if: ${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} @@ -137,14 +137,14 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -162,51 +162,15 @@ jobs: - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: macos-x64-dmg - path: ./dist/podman-desktop-*x64.dmg + path: ./dist/kortex-*x64.dmg - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: macos-arm64-dmg - path: ./dist/podman-desktop-*arm64.dmg + path: ./dist/kortex-*arm64.dmg - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: macos-universal-dmg - path: ./dist/podman-desktop-*universal.dmg - - website-build: - if: ${{ (contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft) && github.event.pull_request.base.ref == 'main' }} - name: build website - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run website - run: pnpm website:build - - - name: Store pull request details for publish-cloudflare - run: | - echo "${{ github.event.number }}" > PR_NUMBER - echo "${{ github.event.pull_request.head.sha }}" > PR_SHA - - - name: Upload artifact website-content - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: website-content - path: | - website/build - PR_NUMBER - PR_SHA + path: ./dist/kortex-*universal.dmg lint-format: if: ${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} @@ -214,13 +178,13 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 40 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -248,13 +212,13 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 40 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -269,7 +233,7 @@ jobs: run: pnpm svelte:check unit-tests: - if: ${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} + if: false #${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} name: unit tests / ${{ matrix.os }} runs-on: ${{ matrix.os }} timeout-minutes: 40 @@ -278,13 +242,13 @@ jobs: matrix: os: [windows-2025, ubuntu-24.04, macos-15] steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -305,9 +269,7 @@ jobs: # publish codecov report if linux - name: publish codecov report if: ${{ matrix.os=='ubuntu-24.04' }} - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 - with: - token: ${{ secrets.CODECOV_TOKEN }} + uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 smoke-e2e-tests: if: ${{ contains(github.event.pull_request.labels.*.name, 'area/ci') || !github.event.pull_request.draft }} @@ -317,54 +279,14 @@ jobs: matrix: mode: [production, development] steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: Update podman to 5.x - run: | - echo "ubuntu version from kubic repository to install podman we need (v5)" - ubuntu_version='23.10' - echo "Add unstable kubic repo into list of available sources and get the repo key" - sudo sh -c "echo 'deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" - curl -L "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - - echo "Updating all dependencies..." - sudo apt-get update -qq - echo "install necessary dependencies for criu package which is not part of ${ubuntu_version}" - sudo apt-get install --allow-unauthenticated -qq libprotobuf32t64 python3-protobuf libnet1 - echo "install criu manually from static location" - curl -sLO http://archive.ubuntu.com/ubuntu/pool/universe/c/criu/criu_3.16.1-2_amd64.deb && sudo dpkg -i criu_3.16.1-2_amd64.deb - echo "installing/update podman package..." - sudo apt-get -qq -y install --allow-unauthenticated podman || { echo "Start fallback steps for podman nightly installation from a static mirror" && \ - sudo sh -c "echo 'deb http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \ - curl -L "http://ftp.lysator.liu.se/pub/opensuse/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_${ubuntu_version}/Release.key" | sudo apt-key add - && \ - sudo apt-get --allow-insecure-repositories update && \ - sudo apt-get --allow-unauthenticated -y install podman; } - podman version - - - name: Revert unprivileged user namespace restrictions in Ubuntu 24.04 - run: | - # allow unprivileged user namespace - sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 - - # Enables multi-architecture support for building multi-arch manifests - - name: Install QEMU for Multi-Arch - run: | - sudo apt install -y qemu-user-static - - - name: Set cgroup_manager to 'cgroupfs' instead of systemd - run: | - mkdir -p ~/.config/containers - cat <> ~/.config/containers/containers.conf - [engine] - cgroup_manager="cgroupfs" - EOT - podman info + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 name: Install pnpm with: run_install: false - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 22 cache: 'pnpm' @@ -375,14 +297,14 @@ jobs: - name: Run Smoke E2E tests in ${{ matrix.mode == 'production' && 'Production' || 'Development' }} Mode run: | if [ ${{ matrix.mode }} == 'production' ]; then - echo "Compiling the Podman Desktop in production mode" + echo "Compiling Kortex in production mode" export ELECTRON_ENABLE_INSPECT=true pnpm compile:current --linux dir - path=$(realpath ./dist/linux-unpacked/podman-desktop) - echo "Podman Desktop built binary: $path" - export PODMAN_DESKTOP_BINARY=$path + path=$(realpath ./dist/linux-unpacked/kortex) + echo "Kortex built binary: $path" + export KORTEX_BINARY=$path fi - pnpm test:e2e:smoke + pnpm test:e2e - name: Publish Test Report uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 @@ -392,7 +314,7 @@ jobs: include_passed: true detailed_summary: true annotate_only: true - require_tests: true + require_tests: true report_paths: '**/*results.xml' - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 @@ -411,7 +333,7 @@ jobs: pnpm_lock_changed: ${{ steps.pnpm_changed.outputs.PNPM_LOCK_CHANGED }} steps: - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 2 @@ -427,190 +349,3 @@ jobs: else echo "PNPM_LOCK_CHANGED=false" >> $GITHUB_OUTPUT fi - - win-update-e2e-test: - name: win update e2e tests - needs: detect_pnpm_changes - strategy: - fail-fast: false - matrix: - os: [windows-2025] - if: contains(github.event.pull_request.labels.*.name, 'area/update') || needs.detect_pnpm_changes.outputs.pnpm_lock_changed == 'true' - runs-on: ${{ matrix.os }} - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Adjust/Downgrade local podman desktop version Windows - run: | - $version="1.20.0" - jq --arg version "$version" '.version = $version' package.json | Out-File -FilePath package.json_tmp - Move-Item -Path package.json_tmp -Destination package.json -Force - - - name: Build Podman Desktop locally with electron updater included, allow prereleases - env: - ELECTRON_ENABLE_INSPECT: true - run: | - (Get-Content packages/main/src/plugin/updater.ts).Replace('autoUpdater.autoDownload = false;', 'autoUpdater.autoDownload = false;autoUpdater.allowPrerelease=true;') | Set-Content packages/main/src/plugin/updater.ts - pnpm compile:current --win nsis - $runnerArch=$env:RUNNER_ARCH - $unpackedPath = "dist/win-unpacked" - if ($runnerArch -eq 'ARM64') { - $unpackedPath = "dist/win-arm64-unpacked" - } - echo ("PD_DIST_PATH=" + $unpackedPath) >> $env:GITHUB_ENV - $path=("./$unpackedPath/Podman Desktop.exe" | resolve-path).ProviderPath - echo $path - echo ("PODMAN_DESKTOP_BINARY=" + $path) >> $env:GITHUB_ENV - - - name: Manually set testing-prereleases as update target - run: | - echo "Replace app-update.yml repo to a testing-prerelease, which are more stable update target then the prerelease" - (Get-Content "$Env:PD_DIST_PATH/resources/app-update.yml").Replace('repo: podman-desktop', 'repo: testing-prereleases') | Set-Content "$Env:PD_DIST_PATH/resources/app-update.yml" - echo "Show app-update.yml after replace..." - cat "$Env:PD_DIST_PATH/resources/app-update.yml" - - - name: Run E2E Update test - run: | - echo "${{ env.PODMAN_DESKTOP_BINARY }}" - pnpm test:e2e:update:run - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: ${{ matrix.os }}-update-e2e-test - path: | - ./tests/**/output/ - !./tests/**/traces/raw - - mac-update-e2e-test: - name: mac update e2e tests - needs: detect_pnpm_changes - if: contains(github.event.pull_request.labels.*.name, 'area/update') || needs.detect_pnpm_changes.outputs.pnpm_lock_changed == 'true' - runs-on: macos-15 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Adjust/Downgrade local podman desktop version - run: | - testingVersion="1.20.0" - actualVersion=$(jq -r '.version' package.json) - echo "Current version: $actualVersion, testing version: $testingVersion" - version=$testingVersion - echo "PD_VERSION=$version" >> $GITHUB_ENV - jq --arg version "$version" '.version = $version' package.json > package.json_tmp - mv package.json_tmp package.json - - - name: Build Podman Desktop locally and allow prereleases on update - env: - ELECTRON_ENABLE_INSPECT: true - run: | - echo "Allow Prerelease when updating, setting it inline in the code" - sed -i '' '/autoUpdater.autoDownload = false;/a \ - autoUpdater.allowPrerelease = true; \ - ' packages/main/src/plugin/updater.ts - pnpm compile:current --mac dmg - dmgPath=$(realpath $(find ./dist -name "*.dmg")) - echo "DMG Path: $dmgPath" - # extract the dmg - hdiutil attach $dmgPath - pdVolumePath=$(find /Volumes -name "*${{ env.PD_VERSION }}*") - echo "PD Volume path: $pdVolumePath" - sudo cp -R "$pdVolumePath/Podman Desktop.app" /Applications - # codesign the extracted app - appPath="/Applications/Podman Desktop.app" - sudo codesign --force --deep --sign - "$appPath" - codesign --verify --deep --verbose=2 "$appPath" - binaryPath="$appPath/Contents/MacOS/Podman Desktop" - echo "PD_APP_PATH=$appPath" >> $GITHUB_ENV - echo "PODMAN_DESKTOP_BINARY=$binaryPath" >> $GITHUB_ENV - - - name: Manually set testing-prereleases as update target - run: | - echo "Replace app-update.yml repo to a testing-prerelease" - sudo sed -i '' 's/repo: podman-desktop/repo: testing-prereleases/' "$PD_APP_PATH/Contents/Resources/app-update.yml" - cat "$PD_APP_PATH/Contents/Resources/app-update.yml" - - - name: Run E2E Update test - env: - UPDATE_PODMAN_DESKTOP: true - run: | - echo "${{ env.PODMAN_DESKTOP_BINARY }}" - pnpm test:e2e:update:run - - - name: Publish Test Report - uses: mikepenz/action-junit-report@3585e9575db828022551b4231f165eb59a0e74e3 # v5.6.2 - if: always() # always run even if the previous step fails - with: - fail_on_failure: true - include_passed: true - detailed_summary: true - annotate_only: true - require_tests: true - report_paths: '**/*results.xml' - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: mac-update-e2e-test - path: | - ./tests/**/output/ - !./tests/**/traces/raw - - update-e2e-test: - name: update E2E test - runs-on: ubuntu-24.04 - needs: [win-update-e2e-test, mac-update-e2e-test] - if: | - always() - steps: - - name: Evaluate the Windows and Mac E2E Update test results - run: | - echo "Windows updater result: ${{ needs.win-update-e2e-test.result }}" - echo "Mac updater result: ${{ needs.mac-update-e2e-test.result }}" - if [[ "${{ needs.win-update-e2e-test.result }}" = "failure" || "${{ needs.mac-update-e2e-test.result }}" = "failure" ]]; then - echo "Windows or Mac udpater test failed..." - exit 1 - else - echo "Windows and Mac updater test succeeded or was skipped..." - fi diff --git a/.github/workflows/publish-flathub.yaml b/.github/workflows/publish-flathub.yaml deleted file mode 100644 index c2504a1f37..0000000000 --- a/.github/workflows/publish-flathub.yaml +++ /dev/null @@ -1,146 +0,0 @@ -# -# Copyright (C) 2024-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 - -name: Publish update to flathub.org - -on: - workflow_dispatch: - inputs: - version: - description: 'release version like 1.14.2' - required: true - repository_dispatch: - types: [ publish-to-flathub ] - -permissions: - contents: read - -jobs: - - update-flathub: - permissions: - contents: write # for Git to git push - name: Publish Podman Desktop to flathub - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: 'flathub/io.podman_desktop.PodmanDesktop' - ref: 'master' - # Change the token to the one that has write access to the fork repository - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - - - name: Set version - id: VERSION - run: | - version="" - if [ "${{ github.event_name }}" == "repository_dispatch" ] - then - version="${{ github.event.client_payload.version }}" - else - version="${{ github.event.inputs.version }}" - fi - # strip out the prefix v if it's there - if [[ $version == v* ]]; then - version="${version:1}" - fi - echo "desktopVersion=$version" >> ${GITHUB_OUTPUT} - - name: Update the yaml file - run: | - # Grab the source code of Podman Desktop - export PODMAN_DESKTOP_SOURCE_URL="https://github.com/containers/podman-desktop/archive/refs/tags/v${{ steps.VERSION.outputs.desktopVersion }}.tar.gz" - wget ${PODMAN_DESKTOP_SOURCE_URL} -qO podman-desktop-source.tgz - # compute shasum256 and take the first column of the text - export PODMAN_DESKTOP_SOURCE_SHASUM=$(shasum -a 256 podman-desktop-source.tgz | awk '{print $1}') - - echo "podman Desktop source .tgz file sha256 is ${PODMAN_DESKTOP_SOURCE_SHASUM}" - - # Grab the pnpm cache for arm64 - export PNPM_STORE_CACHE_ARM64_URL="https://github.com/containers/podman-desktop/releases/download/v${{ steps.VERSION.outputs.desktopVersion }}/store-cache-pnpm-arm64.tgz" - wget ${PNPM_STORE_CACHE_ARM64_URL} -qO podman-desktop-pnpm-arm64.tgz - # compute shasum256 - export PODMAN_DESKTOP_PNPM_ARM64_SHASUM=$(shasum -a 256 podman-desktop-pnpm-arm64.tgz | awk '{print $1}') - echo "podman Desktop pnpm arm64 .tgz file sha256 is ${PODMAN_DESKTOP_PNPM_ARM64_SHASUM}" - - # Grab the pnpm cache for amd64 - export PNPM_STORE_CACHE_AMD64_URL="https://github.com/containers/podman-desktop/releases/download/v${{ steps.VERSION.outputs.desktopVersion }}/store-cache-pnpm-amd64.tgz" - wget ${PNPM_STORE_CACHE_AMD64_URL} -qO podman-desktop-pnpm-amd64.tgz - # compute shasum256 - export PODMAN_DESKTOP_PNPM_AMD64_SHASUM=$(shasum -a 256 podman-desktop-pnpm-amd64.tgz | awk '{print $1}') - echo "podman Desktop pnpm amd64 .tgz file sha256 is ${PODMAN_DESKTOP_PNPM_AMD64_SHASUM}" - - # find the version of electron being used by podman desktop - wget "https://raw.githubusercontent.com/podman-desktop/podman-desktop/refs/tags/v${{ steps.VERSION.outputs.desktopVersion }}/package.json" -qO podman-desktop-package.json - # set the version of electron - ELECTRON_VERSION=$(jq -r '.devDependencies.electron' podman-desktop-package.json) - echo "Found electron ${ELECTRON_VERSION}" - - # electron root url - export ELECTRON_BASE_URL="https://github.com/electron/electron/releases/download/v${ELECTRON_VERSION}" - export ELECTRON_BASE_URL_SHA=$(echo -n "$ELECTRON_BASE_URL" | sha256sum | cut -d ' ' -f 1) - export ELECTRON_CACHE_DIRECTORY="electron-cache/${ELECTRON_BASE_URL_SHA}" - export ELECTRON_CACHE_DIRECTORY_SLASH="${ELECTRON_CACHE_DIRECTORY}/" - - # download electron binary (amd64) - export ELECTRON_AMD64_URL="${ELECTRON_BASE_URL}/electron-v${ELECTRON_VERSION}-linux-x64.zip" - wget ${ELECTRON_AMD64_URL} -qO electron-x64.zip - # compute shasum - export ELECTRON_AMD64_SHASUM=$(shasum -a 256 electron-x64.zip | awk '{print $1}') - echo "electron x64 zip sha256 is ${ELECTRON_AMD64_SHASUM}" - - # download electron binary (arm64) - export ELECTRON_ARM64_URL="${ELECTRON_BASE_URL}/electron-v${ELECTRON_VERSION}-linux-arm64.zip" - wget ${ELECTRON_ARM64_URL} -qO electron-arm64.zip - # compute shasum - export ELECTRON_ARM64_SHASUM=$(shasum -a 256 electron-arm64.zip | awk '{print $1}') - echo "electron arm64 zip sha256 is ${ELECTRON_ARM64_SHASUM}" - - # copy original yaml file - cp io.podman_desktop.PodmanDesktop.yml io.podman_desktop.PodmanDesktop.ori - - # update the url and sha256 for the electron binaries for amd64 and arm64 - yq -i '(.modules[].sources[] | select(.url == "*electron*x64*")) += {"url":strenv(ELECTRON_AMD64_URL), "dest":strenv(ELECTRON_CACHE_DIRECTORY_SLASH), "sha256":strenv(ELECTRON_AMD64_SHASUM)}' io.podman_desktop.PodmanDesktop.yml - yq -i '(.modules[].sources[] | select(.url == "*electron*arm64*")) += {"url":strenv(ELECTRON_ARM64_URL), "dest":strenv(ELECTRON_CACHE_DIRECTORY_SLASH), "sha256":strenv(ELECTRON_ARM64_SHASUM)}' io.podman_desktop.PodmanDesktop.yml - - # update the pnpm store url/sha256 - yq -i '(.modules[].sources[] | select(.url == "*store-cache-pnpm*amd64*")) += {"url":strenv(PNPM_STORE_CACHE_AMD64_URL), "sha256":strenv(PODMAN_DESKTOP_PNPM_AMD64_SHASUM)}' io.podman_desktop.PodmanDesktop.yml - yq -i '(.modules[].sources[] | select(.url == "*store-cache-pnpm*arm64*")) += {"url":strenv(PNPM_STORE_CACHE_ARM64_URL), "sha256":strenv(PODMAN_DESKTOP_PNPM_ARM64_SHASUM)}' io.podman_desktop.PodmanDesktop.yml - - # update the source url/sha256 of podman desktop - yq -i '(.modules[].sources[] | select(.url == "*podman-desktop*refs*tags*")) += {"url":strenv(PODMAN_DESKTOP_SOURCE_URL), "sha256":strenv(PODMAN_DESKTOP_SOURCE_SHASUM)}' io.podman_desktop.PodmanDesktop.yml - - # update the version of the cache for the env variable - sed -i -E 's|^( *- export ELECTRON_CACHE="\$\(\s*pwd\s*\)/)[^"]+(".*)|\1'"$ELECTRON_CACHE_DIRECTORY"'\2|' io.podman_desktop.PodmanDesktop.yml - - env: - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - PODMAN_DESKTOP_VERSION: ${{ steps.VERSION.outputs.desktopVersion }} - - name: Create Pull Request to update flathub - run: | - git config --local user.name ${{ github.actor }} - git config --local user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" - export LOCAL_BRANCH_NAME="update-to-${{ steps.VERSION.outputs.desktopVersion }}" - git checkout -b "${LOCAL_BRANCH_NAME}" - git add io.podman_desktop.PodmanDesktop.yml - export TITLE="feat: update Podman Desktop to version ${{ steps.VERSION.outputs.desktopVersion }}" - git commit -m "${TITLE}" - git remote add podman-desktop-bot https://github.com/podman-desktop-bot/io.podman_desktop.PodmanDesktop - git push podman-desktop-bot "+${LOCAL_BRANCH_NAME}" - gh pr create --title "${TITLE}" --body "${TITLE}" -H "podman-desktop-bot:${LOCAL_BRANCH_NAME}" - - env: - GH_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} diff --git a/.github/workflows/publish-to-chocolatey.yaml b/.github/workflows/publish-to-chocolatey.yaml deleted file mode 100644 index 6f66116bc9..0000000000 --- a/.github/workflows/publish-to-chocolatey.yaml +++ /dev/null @@ -1,116 +0,0 @@ -# -# Copyright (C) 2022 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 - -name: Publish update to Chocolatey - -on: - workflow_dispatch: - inputs: - version: - description: 'release version like 0.0.6' - required: true - force: - description: 'Force the push of the package' - required: false - default: 'false' - repository_dispatch: - types: [ publish-to-chocolatey ] - -permissions: - contents: read - -jobs: - - version: - name: Extracting version - runs-on: ubuntu-24.04 - outputs: - desktopVersion: ${{ steps.VERSION.outputs.desktopVersion}} - - steps: - - name: set version - id: VERSION - run: | - version="" - if [ "${{ github.event_name }}" == "repository_dispatch" ] - then - version="${{ github.event.client_payload.version }}" - else - version="${{ github.event.inputs.version }}" - fi - # strip out the prefix v if it's there - if [[ $version == v* ]]; then - version="${version:1}" - fi - echo "desktopVersion=$version" >> ${GITHUB_OUTPUT} - - winget-bump: - permissions: - contents: write # for Git to git push - name: Update Chocolatey - needs: version - runs-on: windows-2025 - defaults: - run: - shell: powershell - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Install the Chocolatey Automatic Package Updater Module - run: choco install au -y - - name: Build the Podman Desktop chocolatey Package by making sure we're using the latest release - working-directory: ./.chocolatey/podman-desktop - run: | - if ($env:automaticUpdateForce -eq 'true') { - $au_Force = $true; ./update.ps1 - } else { - ./update.ps1 - } - cat podman-desktop.nuspec - env: - au_Push: ${{ github.event.inputs.force }} - automaticUpdateForce: ${{ github.event.inputs.force }} - VERSION: ${{ needs.version.outputs.desktopVersion }} - - name: Create the PR to bump the version in the main branch - run: | - git config --local user.name ${{ github.actor }} - git config --local user.email "fbenoit@redhat.com" - $bumpedBranchName = "update-chocolatey-for-${{ needs.version.outputs.desktopVersion }}" - git checkout -b $bumpedBranchName - git add .chocolatey - git commit --signoff -m "chore: Update chocolatey packages for ${{ needs.version.outputs.desktopVersion }}" - git push origin "$bumpedBranchName" - New-Item -Path . -Name "pr-title" -ItemType "file" -Value "${{ needs.version.outputs.desktopVersion }} has been released`n`nUpdate Chocolatey packages for ${{ needs.version.outputs.desktopVersion }}" - $pullRequestUrl = gh pr create --title "chore: Update Chocolatey package to ${{ needs.version.outputs.desktopVersion }}" --body-file ./pr-title --head "$bumpedBranchName" --base "main" -R "containers/podman-desktop" - echo "📢 Pull request created: $pullRequestUrl" - echo "➡️ Flag the PR as being ready for review" - gh pr ready "$pullRequestUrl" - echo "🔅 Mark the PR as being ok to be merged automatically" - gh pr merge "$pullRequestUrl" --auto --rebase - env: - GH_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - - name: Publish Package to Chocolatey - run: | - if ($env:automaticUpdateForce -eq 'true') { - $au_Force = $true; Push-Package - } else { - Push-Package - } - working-directory: ./.chocolatey/podman-desktop - env: - api_key: ${{ secrets.CHOCOLATEY_API_KEY }} - automaticUpdateForce: ${{ github.event.inputs.force }} diff --git a/.github/workflows/publish-to-podman_io.yaml b/.github/workflows/publish-to-podman_io.yaml deleted file mode 100644 index 1634c24025..0000000000 --- a/.github/workflows/publish-to-podman_io.yaml +++ /dev/null @@ -1,101 +0,0 @@ -# -# 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 - -name: Publish update to podman.io website - -on: - workflow_dispatch: - inputs: - version: - permissions: - contents: write # for Git to git push - description: 'release version like 1.1.0' - required: true - repository_dispatch: - types: [ publish-to-podman_io ] - -permissions: - contents: read - -jobs: - - version: - name: Extracting version - runs-on: ubuntu-24.04 - outputs: - desktopVersion: ${{ steps.VERSION.outputs.desktopVersion}} - - steps: - - name: set version - id: VERSION - run: | - version="" - if [ "${{ github.event_name }}" == "repository_dispatch" ] - then - version="${{ github.event.client_payload.version }}" - else - version="${{ github.event.inputs.version }}" - fi - # strip out the prefix v if it's there - if [[ $version == v* ]]; then - version="${version:1}" - fi - echo "desktopVersion=$version" >> ${GITHUB_OUTPUT} - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - repository: containers/podman.io - ref: refs/heads/main - token: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - - - - name: Replace the content of the file - run: | - # Replace the version in static/data/global.ts file (replace the xxx value) - # export const LATEST_DESKTOP_VERSION = 'xxx'; - sed -i "s/export const LATEST_DESKTOP_VERSION = '.*';/export const LATEST_DESKTOP_VERSION = '${{ steps.VERSION.outputs.desktopVersion }}';/g" static/data/global.ts - # Replace the version of the blog post - # export const LATEST_DESKTOP_DOWNLOAD_URL = 'https://podman-desktop.io/blog/podman-desktop-release-xxx'; - # need to strip out the last digit after the last . to get the blog post version (so for example 1.4.1 becomes 1.4) - blogPostVersion=$(echo ${{ steps.VERSION.outputs.desktopVersion }} | sed 's/\.[^.]*$//') - sed -i "s/export const LATEST_DESKTOP_DOWNLOAD_URL = '.*';/export const LATEST_DESKTOP_DOWNLOAD_URL = 'https:\/\/podman-desktop.io\/blog\/podman-desktop-release-$blogPostVersion';/g" static/data/global.ts - echo "New content of the file:" - cat static/data/global.ts - - - name: Create the PR to bump the version in the main branch - run: | - git config --local user.name ${{ github.actor }} - git config --local user.email "fbenoit@redhat.com" - bumpedBranchName="update-podman-desktop-io-for-v${{ steps.VERSION.outputs.desktopVersion }}" - echo "branch name: $bumpedBranchName" - git checkout -b $bumpedBranchName - echo "adding the file to the commit" - git add static/data/global.ts - echo "commiting the file" - git commit --signoff -m "chore: Update Podman Desktop version to v${{ steps.VERSION.outputs.desktopVersion }}" - git remote -v - git remote add podman-desktop-bot https://github.com/podman-desktop-bot/podman.io - git push podman-desktop-bot "+$bumpedBranchName" - printf "Podman Desktop v${{ steps.VERSION.outputs.desktopVersion }} has been released 🎉\nUpdate website for v${{ steps.VERSION.outputs.desktopVersion }}" > ./pr-title - pullRequestUrl=$(gh pr create --title "chore: Update Podman Desktop version to v${{ steps.VERSION.outputs.desktopVersion }}" --body-file ./pr-title --head "podman-desktop-bot:$bumpedBranchName" --base "main" -R "containers/podman.io") - echo "📢 Pull request created: $pullRequestUrl" - echo "➡️ Flag the PR as being ready for review" - gh pr ready "$pullRequestUrl" - echo "🔅 Mark the PR as being ok to be merged automatically" - gh pr merge "$pullRequestUrl" --auto --rebase - env: - GH_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} diff --git a/.github/workflows/publish-to-winget.yaml b/.github/workflows/publish-to-winget.yaml deleted file mode 100644 index 6971d374da..0000000000 --- a/.github/workflows/publish-to-winget.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright (C) 2022 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 - -name: Publish update to Winget - -on: - workflow_dispatch: - inputs: - version: - description: 'release version like 0.0.6' - required: true - repository_dispatch: - types: [ publish-to-winget ] - -permissions: - contents: read - -jobs: - - version: - name: Extracting version - runs-on: ubuntu-24.04 - outputs: - desktopVersion: ${{ steps.VERSION.outputs.desktopVersion}} - - steps: - - name: set version - id: VERSION - run: | - version="" - if [ "${{ github.event_name }}" == "repository_dispatch" ] - then - version="${{ github.event.client_payload.version }}" - else - version="${{ github.event.inputs.version }}" - fi - # strip out the prefix v if it's there - if [[ $version == v* ]]; then - version="${version:1}" - fi - echo "desktopVersion=$version" >> ${GITHUB_OUTPUT} - - winget-bump: - name: Update Winget - needs: version - runs-on: windows-2025 - defaults: - run: - shell: powershell - steps: - - name: Create winget PR - run: | - Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe - .\wingetcreate.exe update RedHat.Podman-Desktop -u $Env:URL_X64 $Env:URL_ARM64 -v $Env:VERSION -t $Env:TOKEN --submit - env: - TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - VERSION: ${{ needs.version.outputs.desktopVersion }} - URL_X64: ${{ format('https://github.com/containers/podman-desktop/releases/download/v{0}/podman-desktop-{0}-setup-x64.exe|x64', needs.version.outputs.desktopVersion) }} - URL_ARM64: ${{ format('https://github.com/containers/podman-desktop/releases/download/v{0}/podman-desktop-{0}-setup-arm64.exe|arm64', needs.version.outputs.desktopVersion) }} diff --git a/.github/workflows/publish-website-pr-cloudflare.yaml b/.github/workflows/publish-website-pr-cloudflare.yaml deleted file mode 100644 index b43eabd654..0000000000 --- a/.github/workflows/publish-website-pr-cloudflare.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# -# 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 - -# NOTE: Because this worklow is using secrets, it cannot run directly on a pull-request workflow, which is running in the context of the forked repository. - -name: Publish pull request content on cloudflare pages - -on: - workflow_run: - workflows: - - 'pr-check' - types: - - completed - -permissions: - contents: read - deployments: write - -jobs: - publish: - name: Publish content of the website pull request on cloudflare pages - runs-on: ubuntu-24.04 - steps: - - name: Download website content artifact - uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 - with: - workflow: ${{ github.event.workflow_run.workflow_id }} - name: website-content - path: content - allow_forks: true - - - name: Deploy to Cloudflare Pages - uses: AdrianGonz97/refined-cf-pages-action@45ea15c1c69a7bce8ffbb79ba114daa2b52f0cff # v1.3.0 - with: - apiToken: ${{ secrets.CLOUDFLARE_AUTH_TOKEN }} - accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - githubToken: ${{ secrets.GITHUB_TOKEN }} - projectName: podman-desktop-website-pr - deploymentName: Website Preview - comment: false - directory: content/website/build diff --git a/.github/workflows/release-generate-sbom.yaml b/.github/workflows/release-generate-sbom.yaml deleted file mode 100644 index dd9066fc46..0000000000 --- a/.github/workflows/release-generate-sbom.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# -# 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 - -name: Generate sbom for release - -on: - workflow_dispatch: - inputs: - version: - description: 'Release version for sbom generation (e.g. 0.0.1)' - required: true - -env: - RELEASE_TAG: v${{ inputs.version }} - -jobs: - generate-sbom: - name: Generate SBOM file - runs-on: ubuntu-24.04 - permissions: - actions: read - contents: write - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ env.RELEASE_TAG }} - - - name: Generate sbom file - uses: anchore/sbom-action@v0 - with: - path: ./ - output-file: ./podman-desktop-sbom.spdx.json - artifact-name: podman-desktop-sbom.spdx.json - - - name: Upload sbom file to release - uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./podman-desktop-sbom.spdx.json - tag: ${{ env.RELEASE_TAG }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index dee02f04c0..0000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,310 +0,0 @@ -# -# Copyright (C) 2022-2024 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 - -name: release - -on: - workflow_dispatch: - inputs: - version: - description: 'Version to release' - required: true - branch: - description: 'Branch to use for the release' - required: true - default: main -env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - DEBUG: electron-builder - -permissions: - contents: read - -jobs: - - tag: - name: Tagging - runs-on: ubuntu-24.04 - permissions: - contents: write - outputs: - githubTag: ${{ steps.TAG_UTIL.outputs.githubTag}} - desktopVersion: ${{ steps.TAG_UTIL.outputs.desktopVersion}} - releaseId: ${{ steps.create_release.outputs.id}} - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ github.event.inputs.branch }} - - name: Generate tag utilities - id: TAG_UTIL - run: | - TAG_PATTERN=${{ github.event.inputs.version }} - echo "githubTag=v$TAG_PATTERN" >> ${GITHUB_OUTPUT} - echo "desktopVersion=$TAG_PATTERN" >> ${GITHUB_OUTPUT} - - - name: tag - run: | - git config --local user.name ${{ github.actor }} - - # Add the new version in package.json file - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" package.json - find extensions/* -maxdepth 5 -name "package.json" | xargs -I {} sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ steps.TAG_UTIL.outputs.desktopVersion }}\",#g" {} - git add package.json extensions/*/package.json extensions/*/packages/extension/package.json - - # Update the issue template with the new version and move old version below - nextVersionLineNumber=$(grep -n "next (development version)" .github/ISSUE_TEMPLATE/bug_report.yml | cut -d ":" -f 1 | head -n 1) - currentVersionItem=$(sed "$(expr ${nextVersionLineNumber} - 1)q;d" .github/ISSUE_TEMPLATE/bug_report.yml) - newVersionItem=$(echo "$currentVersionItem" | sed -r -e "s|\".*\"|\"${{ steps.TAG_UTIL.outputs.desktopVersion }}\"|") - # delete the lines before the next version line - sed -i "$(expr ${nextVersionLineNumber} - 1)d" .github/ISSUE_TEMPLATE/bug_report.yml - # insert the version being tagged - sed -i "$(expr ${nextVersionLineNumber} - 1)i\\${newVersionItem}" .github/ISSUE_TEMPLATE/bug_report.yml - sed -i "$(expr ${nextVersionLineNumber} + 1)i\\${currentVersionItem}" .github/ISSUE_TEMPLATE/bug_report.yml - # add the changes - git add .github/ISSUE_TEMPLATE/bug_report.yml - - # Add the new version in the appdata.xml file - xmlReleasesLineNumber=$(grep -n "" .flatpak-appdata.xml | cut -d ":" -f 1 | head -n 1) - newFlatpakReleaseItem=" " - sed -i "$(expr ${xmlReleasesLineNumber} + 1)i\\${newFlatpakReleaseItem}" .flatpak-appdata.xml - git add .flatpak-appdata.xml - - # commit the changes - git commit -m "chore: 🥁 tagging ${{ steps.TAG_UTIL.outputs.githubTag }} 🥳" - echo "Tagging with ${{ steps.TAG_UTIL.outputs.githubTag }}" - git tag ${{ steps.TAG_UTIL.outputs.githubTag }} - git push origin ${{ steps.TAG_UTIL.outputs.githubTag }} - - name: Create Release - id: create_release - uses: ncipollo/release-action@bcfe5470707e8832e12347755757cec0eb3c22af # v1.18.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag: ${{ steps.TAG_UTIL.outputs.githubTag }} - name: ${{ steps.TAG_UTIL.outputs.githubTag }} - draft: true - prerelease: true - - name: Create the PR to bump the version in the main branch (only if we're tagging from main branch) - if: ${{ github.event.inputs.branch == 'main' }} - run: | - git config --local user.name ${{ github.actor }} - git config --local user.email "fbenoit@redhat.com" - CURRENT_VERSION=$(echo "${{ steps.TAG_UTIL.outputs.desktopVersion }}") - tmp=${CURRENT_VERSION%.*} - minor=${tmp#*.} - bumpedVersion=${CURRENT_VERSION%%.*}.$((minor + 1)).0 - bumpedBranchName="bump-to-${bumpedVersion}" - git checkout -b "${bumpedBranchName}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${bumpedVersion}-next\",#g" package.json - find extensions/* -maxdepth 5 -name "package.json" | xargs -I {} sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${bumpedVersion}-next\",#g" {} - git add package.json extensions/*/package.json extensions/*/packages/extension/package.json - git commit -s --amend -m "chore: bump version to ${bumpedVersion}" - git push origin "${bumpedBranchName}" - echo -e "📢 Bump version to ${bumpedVersion}\n\n${{ steps.TAG_UTIL.outputs.desktopVersion }} has been released.\n\n Time to switch to the new ${bumpedVersion} version 🥳" > /tmp/pr-title - pullRequestUrl=$(gh pr create --title "chore: 📢 Bump version to ${bumpedVersion}" --body-file /tmp/pr-title --head "${bumpedBranchName}" --base "main") - echo "📢 Pull request created: ${pullRequestUrl}" - echo "➡️ Flag the PR as being ready for review" - gh pr ready "${pullRequestUrl}" - echo "🔅 Mark the PR as being ok to be merged automatically" - gh pr merge "${pullRequestUrl}" --auto --rebase - env: - GITHUB_TOKEN: ${{ secrets.PODMAN_DESKTOP_BOT_TOKEN }} - - build: - name: Build / ${{ matrix.os }} ${{ matrix.airgap == 'true' && '(Air Gap)' || '' }} - needs: tag - permissions: - contents: write - runs-on: ${{ matrix.os }} - strategy: - matrix: - include: - - os: "windows-2025" - - os: "windows-2025" - airgap: "true" - - os: "macos-15" - - os: "macos-15" - airgap: "true" - - os: "ubuntu-24.04" - timeout-minutes: 60 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ needs.tag.outputs.githubTag}} - - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: "3.x" - - - name: setup telemetry key for production - run: | - sed -i -r -e "s/SEGMENT_KEY = '.*'/SEGMENT_KEY = '${{ secrets.SEGMENT_WRITE_KEY }}'/" packages/main/src/plugin/telemetry/telemetry.ts - - - name: Execute pnpm - run: pnpm install - - - name: Install flatpak on Linux - if: ${{ matrix.os=='ubuntu-24.04' }} - run: | - sudo apt-get update - sudo apt-get install flatpak -y - sudo apt-get install flatpak-builder -y - sudo apt-get install elfutils -y - flatpak remote-add --if-not-exists flathub --user https://flathub.org/repo/flathub.flatpakrepo - flatpak install flathub --no-static-deltas --user -y org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 - - name: Set macOS environment variables - if: startsWith(matrix.os, 'macos') - run: | - echo "CSC_LINK=${{secrets.CSC_LINK}}" >> $GITHUB_ENV - echo "CSC_KEY_PASSWORD=${{secrets.CSC_KEY_PASSWORD}}" >> $GITHUB_ENV - echo "APPLE_ID=${{secrets.APPLE_ID}}" >> $GITHUB_ENV - echo "APPLE_APP_SPECIFIC_PASSWORD=${{secrets.APPLE_APP_SPECIFIC_PASSWORD}}" >> $GITHUB_ENV - echo "APPLE_TEAM_ID=${{secrets.APPLE_TEAM_ID}}" >> $GITHUB_ENV - - - name: Install Azure SignTool on Windows - if: startsWith(matrix.os, 'windows') - run: | - dotnet tool install --global AzureSignTool --version 3.0.0 - echo "AZURE_KEY_VAULT_CERTIFICATE=${{secrets.AZURE_KEY_VAULT_CERTIFICATE}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_CLIENT_ID=${{secrets.AZURE_KEY_VAULT_CLIENT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_SECRET=${{secrets.AZURE_KEY_VAULT_SECRET}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_TENANT_ID=${{secrets.AZURE_KEY_VAULT_TENANT_ID}}" | Out-File -FilePath $env:GITHUB_ENV -Append - echo "AZURE_KEY_VAULT_URL=${{secrets.AZURE_KEY_VAULT_URL}}" | Out-File -FilePath $env:GITHUB_ENV -Append - - - name: Build & Publish artifacts - timeout-minutes: 40 - env: - AIRGAP_DOWNLOAD: ${{ matrix.airgap == 'true' && '1' || '' }} - run: pnpm compile:next - - release: - needs: [tag, build] - name: Release - permissions: - contents: write - runs-on: ubuntu-24.04 - steps: - - name: id - run: echo the release id is ${{ needs.tag.outputs.releaseId}} - - - name: Publish release - uses: StuYarrow/publish-release@01f2a1365bacd77bad861873a7fdf274ab49eefd # v1.1.2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - id: ${{ needs.tag.outputs.releaseId}} - - publish: - needs: [tag, release] - name: Publish - runs-on: ubuntu-24.04 - permissions: - id-token: write - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Set-up npmjs auth token - run: printf "//registry.npmjs.org/:_authToken=${{ secrets.NPM_AUTH_TOKEN }}\n" >> ~/.npmrc - - - name: Publish API to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/extension-api/package.json - cd packages/extension-api && pnpm publish --provenance --tag latest --no-git-checks --access public - - - name: Publish Webview API to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/webview-api/package.json - cd packages/webview-api && pnpm publish --provenance --tag latest --no-git-checks --access public - - - name: Publish ui/svelte to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" packages/ui/package.json - cd packages/ui && pnpm build && pnpm publish --provenance --tag latest --no-git-checks --access public - - - name: Publish tests-playwright to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" tests/playwright/package.json - cd tests/playwright && pnpm build && pnpm publish --provenance --tag latest --no-git-checks --access public - - - name: Publish Podman Extension API to npmjs - run: | - echo "Using version ${{ needs.tag.outputs.desktopVersion }}" - sed -i "s#version\":\ \"\(.*\)\",#version\":\ \"${{ needs.tag.outputs.desktopVersion }}\",#g" extensions/podman/packages/api/package.json - cd extensions/podman/packages/api - pnpm publish --provenance --tag latest --no-git-checks --access public - - # publish the pnpm store for flathub builds - pnpm-store: - needs: [tag, release] - name: pnpm-store-${{ matrix.arch }} - runs-on: ubuntu-24.04 - permissions: - contents: write - strategy: - matrix: - arch: [amd64, arm64] - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ needs.tag.outputs.githubTag}} - - - name: Set up QEMU - uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - - name: create the pnpm store from the dependencies - run: | - podman run --platform linux/${{ matrix.arch }} -v $(pwd):/project --rm -it --entrypoint=sh node:22 -c "cd /project && npm install -g corepack@latest && corepack enable pnpm && COREPACK_ENABLE_DOWNLOAD_PROMPT=0 pnpm install --frozen-lockfile --store-dir pnpm-store" - # now the store is in the pnpm-store directory - # create a tarball of the store - echo "Creating the archive store-cache-pnpm-${{ matrix.arch }}.tgz" - tar -czf store-cache-pnpm-${{ matrix.arch }}.tgz pnpm-store - - - name: Upload binaries to release - uses: svenstaro/upload-release-action@81c65b7cd4de9b2570615ce3aad67a41de5b1a13 # v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: store-cache-pnpm-${{ matrix.arch }}.tgz - tag: ${{ needs.tag.outputs.githubTag }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml deleted file mode 100644 index a7fae3ecfa..0000000000 --- a/.github/workflows/scorecard.yml +++ /dev/null @@ -1,75 +0,0 @@ -# -# 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 - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '43 7 * * 1' - push: - branches: [ "main" ] - -# Declare default permissions as read only. -permissions: read-all - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-24.04 - # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. - if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - # Uncomment the permissions below if installing in a private repository. - # contents: read - # actions: read - - steps: - - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 - with: - results_file: results.sarif - results_format: sarif - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard (optional). - # Commenting out will disable upload of results to your repo's Code Scanning dashboard - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5 - with: - sarif_file: results.sarif diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml deleted file mode 100644 index 5ee11287f5..0000000000 --- a/.github/workflows/stale.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# -# Copyright (C) 2024 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 - -name: stale - -on: - schedule: - - cron: "5 0 * * *" - workflow_dispatch: - -permissions: - contents: read - -jobs: - stale: - permissions: - issues: write # for actions/stale to close stale issues - pull-requests: write # for actions/stale to close stale PRs - - runs-on: ubuntu-24.04 - steps: - - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - # Please refer to https://github.com/actions/stale/ - # limit to 1000 operations per run - operations-per-run: 1000 - days-before-issue-stale: 180 - days-before-issue-close: 30 - stale-issue-label: lifecycle/stale - exempt-issue-labels: lifecycle/frozen - stale-issue-message: > - This issue has been automatically marked as stale because it has not had - activity in the last 6 months. It will be closed in 30 days if no further activity occurs. Please - feel free to leave a comment if you believe the issue is still relevant. - Thank you for your contributions! - close-issue-message: > - This issue has been automatically closed because it has not had any further - activity in the last 30 days. Thank you for your contributions! - days-before-pr-stale: 180 - days-before-pr-close: 14 - stale-pr-label: lifecycle/stale - exempt-pr-labels: lifecycle/frozen - stale-pr-message: > - This pull request has been automatically marked as stale because it has not had - activity in the last 6 months. It will be closed in 2 weeks if no further activity occurs. Please - feel free to give a status update or ping for review. Thank you for your contributions! - close-pr-message: > - This pull request has been automatically closed because it has not had any further - activity in the last 2 weeks. Thank you for your contributions! - remove-stale-when-updated: true diff --git a/.github/workflows/website-next.yaml b/.github/workflows/website-next.yaml deleted file mode 100644 index a75580fe3e..0000000000 --- a/.github/workflows/website-next.yaml +++ /dev/null @@ -1,80 +0,0 @@ -# -# Copyright (C) 2022 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 - -name: Publish Website - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - 'package.json' - - 'pnpm-lock.yaml' - - 'storybook/**' - - 'website/**' - - 'packages/extension-api/src/**' - -env: - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - -permissions: - contents: read - -jobs: - deploy: - permissions: - contents: write # for peaceiris/actions-gh-pages to push pages branch - name: Build and deploy website - runs-on: ubuntu-24.04 - # disable on forks as secrets are not available - if: github.event.repository.fork == false - timeout-minutes: 60 - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ needs.tag.outputs.githubTag}} - - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 - name: Install pnpm - with: - run_install: false - - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: 22 - cache: 'pnpm' - - - name: Execute pnpm - run: pnpm install - - - name: Run Build - timeout-minutes: 20 - run: pnpm website:build - - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - # Build output to publish to the `gh-pages` branch: - publish_dir: ./website/build - # The following lines assign commit authorship to the official - # GH-Actions bot for deploys to `gh-pages` branch: - # https://github.com/actions/checkout/issues/13#issuecomment-724415212 - # The GH actions bot is used by default if you didn't specify the two fields. - # You can swap them out with your own user credentials. - user_name: github-actions[bot] - user_email: 41898282+github-actions[bot]@users.noreply.github.com diff --git a/.gitignore b/.gitignore index 1517cd3bd1..a63a730232 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ dist /storybook/storybook-static/ yarn.lock extensions/podman/assets/ +.claude +output/ diff --git a/ADOPTERS.md b/ADOPTERS.md deleted file mode 100644 index ba3521293e..0000000000 --- a/ADOPTERS.md +++ /dev/null @@ -1,25 +0,0 @@ -# Podman Desktop Adopters - -Thank you to all the individuals, organizations, and projects that are using Podman Desktop! - -### Contribution Guidelines - -If you're using Podman Desktop and want to add your organization to this list submit a pull request with your change. In your pull request, please include the following information: - -- **Name/Organization/Project name:** Clearly identify the adopter. -- **Status:** Provide a status of your usage of the solution: ![production](https://img.shields.io/badge/-production-blue?style=flat), ![testing](https://img.shields.io/badge/-development%20&%20testing-green?style=flat). -- **More Information:** Eventually include a link that points to more information about your usage of the solution. - -## Organizations using Podman Desktop - -Below is a list of organizations that are using Podman Desktop. - -| Organization | Status | More Information | -| -------------------------------- | ----------------------------------------------------------------------- | ---------------- | -| [SAMPLE Acme](https://acme.com/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | -| [Amadeus](https://amadeus.com/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Migration of 2k+ developers' container engines to Podman Desktop in Jan 2024. Regular collaboration with the Podman Desktop engineering team. -| [École et observatoire des Sciences de la Terre](https://eost.unistra.fr/) | ![production](https://img.shields.io/badge/-production-blue?style=flat) | Used to improve security on our developer workstations | - -## Solutions built with Podman Desktop - -Below is a list of solutions where Podman Desktop is being used or referenced. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..f2fbf5fb1c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,271 @@ +# AGENTS.md + +This file provides guidance to AI code assistants when working with code in this repository. + +## Project Overview + +Kortex is an Electron-based desktop application built with Svelte that provides AI-powered container and Kubernetes management capabilities. It integrates with multiple AI providers (Gemini, OpenAI-compatible services, OpenShift AI) and supports flow execution through providers like Goose. The application also implements the Model Context Protocol (MCP) for AI agent integration. + +## Core Architecture + +### Multi-Process Electron Application + +Kortex follows the standard Electron architecture with three main processes: + +- **Main Process** (`packages/main`): Node.js backend handling system integration, extension loading, container/Kubernetes operations, and business logic +- **Renderer Process** (`packages/renderer`): Svelte-based UI running in the browser context +- **Preload Scripts** (`packages/preload` and `packages/preload-webview`): Bridge layer for secure IPC communication between main and renderer + +### Plugin System and Dependency Injection + +The core plugin system is implemented in `packages/main/src/plugin/index.ts` using Inversify for dependency injection. Key components include: + +- **PluginSystem class**: Orchestrates extension loading, configuration registry, and all core services +- **Extension Loader** (`ExtensionLoader`): Manages extension lifecycle (loading, starting, stopping, uninstalling) +- **Container**: Inversify DI container binding all major registries and managers as singletons + +All major services are registered as singletons in the DI container during initialization: + +- `ProviderRegistry`: Manages inference, container, and Kubernetes providers +- `ContainerProviderRegistry`: Handles Docker/Podman container operations +- `KubernetesClient`: Kubernetes cluster management +- `MCPManager` and `MCPRegistry`: Model Context Protocol integration +- `FlowManager`: Manages flow execution with providers like Goose +- `ChatManager`: AI chat functionality +- `ConfigurationRegistry`: Settings and configuration management + +### Extensions + +Extensions are located in the `extensions/` directory and follow a standard structure: + +- Each extension has a `package.json` with `main` pointing to `./dist/extension.js` +- Extensions must declare `engines.kortex` version compatibility +- Extensions can contribute: + - Inference providers (Gemini, OpenAI-compatible, OpenShift AI) + - Flow providers (Goose) + - MCP registries + - Configuration properties +- Extensions are built using Vite and export a standard activation API + +Available built-in extensions: + +- `gemini`: Google Gemini AI provider integration +- `goose`: Goose flow execution provider +- `mcp-registries`: MCP server registries +- `openai-compatible`: OpenAI-compatible API support +- `openshift-ai`: OpenShift AI platform integration + +### Extension API + +Extensions interact with Kortex through `@kortex-app/api` (`packages/extension-api`), which provides TypeScript definitions for: + +- Provider registration (inference, container, Kubernetes, VM, flow) +- Configuration management +- Command and menu contributions +- UI components (webviews, views, status bar items) +- Authentication providers +- MCP server integration + +## Development Commands + +### Setup and Installation + +```bash +# Install dependencies +pnpm install + +# Start in watch/development mode +pnpm watch +``` + +### Building + +```bash +# Build entire application +pnpm run build + +# Build individual packages +pnpm run build:main # Main process +pnpm run build:renderer # UI/renderer +pnpm run build:preload # Preload scripts +pnpm run build:extensions # All extensions + +# Build specific extension +cd extensions/gemini && pnpm run build +``` + +### Testing + +```bash +# Run all tests (unit + e2e) +pnpm test + +# Unit tests only +pnpm run test:unit + +# Unit tests with coverage +pnpm run test:unit:coverage + +# Run tests for specific packages +pnpm run test:main # Main process tests +pnpm run test:renderer # Renderer tests +pnpm run test:preload # Preload tests +pnpm run test:extensions # Extension tests + +# E2E tests with Playwright +pnpm run test:e2e # Build and run e2e tests +pnpm run test:e2e:run # Run e2e tests only (must build first) +pnpm run test:e2e:report # Show Playwright test report + +# Watch mode for development +pnpm run test:watch +``` + +### Code Quality + +```bash +# Format code +pnpm run format:check # Check formatting +pnpm run format:fix # Fix formatting issues + +# Linting +pnpm run lint:check # Check for linting issues +pnpm run lint:fix # Fix linting issues + +# Type checking +pnpm run typecheck # Check all packages +pnpm run typecheck:main # Main process only +pnpm run typecheck:renderer # Renderer only +pnpm run svelte:check # Svelte component type checking +``` + +### Building Distributables + +```bash +# Development build (directory output, no packaging) +pnpm run compile + +# Production build for release +pnpm run compile:next # With auto-publishing +pnpm run compile:current # Current version +pnpm run compile:pull-request # Without publishing +``` + +## Key Technical Details + +### Workspace Structure + +This is a pnpm monorepo with workspaces defined in `pnpm-workspace.yaml`: + +- `packages/*`: Core application packages (main, renderer, preload, extension-api, api, webview-api) +- `extensions/*`: Extension packages +- `scripts`: Build and utility scripts +- `tests/playwright`: E2E test suite + +### IPC Communication Pattern + +The main/renderer communication follows a structured pattern: + +- Main process exposes handlers via `ipcHandle()` in `packages/main/src/plugin/index.ts` +- Handlers follow naming convention: `:` (e.g., `container-provider-registry:listContainers`) +- Renderer invokes via exposed preload APIs +- Events are sent to renderer via `apiSender.send()` for real-time updates + +### Test File Organization + +Tests are co-located with source files: + +- Unit tests: `*.spec.ts` files alongside source code +- E2E tests: Located in `tests/playwright/src/` +- Test configuration: `vitest.config.js` at root defines workspace test projects +- 627 total test files across the codebase + +### Extension Lifecycle + +1. **Discovery**: `ExtensionLoader` scans `extensions/` directory +2. **Initialization**: Extensions are loaded via `ExtensionLoader.init()` +3. **Activation**: Extensions start when `ExtensionLoader.start()` is called after system ready +4. **Runtime**: Extensions can be started/stopped/removed dynamically through registry +5. **Development**: Use `extension-development-folders` API to load extensions from custom paths + +### Configuration System + +Configuration is managed through `ConfigurationRegistry`: + +- Properties defined in extension's `package.json` under `contributes.configuration.properties` +- Scopes: `DEFAULT`, `ContainerProviderConnection`, `KubernetesProviderConnection`, `InferenceProviderConnection`, `InferenceProviderConnectionFactory` +- Configuration can be experimental and requires explicit enablement +- Values stored and retrieved with dot notation (e.g., `gemini.factory.apiKey`) + +### Flow Execution + +Flows are AI-powered automation workflows: + +- Providers register flow capabilities through `ProviderRegistry` +- `FlowManager` coordinates flow generation, execution, and lifecycle +- Flows can be deployed to Kubernetes (YAML generation) +- Goose extension provides flow runtime capabilities +- MCP servers can be connected to flows for tool access + +### MCP (Model Context Protocol) Integration + +- `MCPRegistry`: Manages MCP registry sources (like mcp-get.com) +- `MCPManager`: Handles remote MCP server connections and lifecycle +- `MCPIPCHandler`: IPC bridge for MCP operations +- MCP servers provide tools that can be accessed by AI models +- Credentials and setup stored securely via `SafeStorageRegistry` + +## Important Patterns + +### Working with Containers and Pods + +Container operations go through `ContainerProviderRegistry`: + +- List containers: `container-provider-registry:listContainers` +- Container lifecycle: `startContainer`, `stopContainer`, `deleteContainer` +- Image operations: `pullImage`, `buildImage`, `deleteImage` +- Pod operations: `createPod`, `startPod`, `stopPod`, `removePod` +- All operations require `engineId` parameter identifying the container engine + +### Kubernetes Operations + +Kubernetes operations use `KubernetesClient`: + +- Context management: `getContexts()`, `setContext()`, `deleteContext()` +- Resource operations: CRUD operations for pods, deployments, services, etc. +- Port forwarding: `createPortForward()`, `deletePortForward()` +- Exec into containers: `execIntoContainer()` with stdin/stdout callbacks +- Apply YAML: `applyResourcesFromFile()`, `applyResourcesFromYAML()` + +### Task Management + +Long-running operations should use `TaskManager`: + +```typescript +const task = taskManager.createTask({ + title: 'Operation description', + action: { + name: 'Navigate to result', + execute: () => navigationManager.navigateToResource(), + }, +}); +// Set task.status = 'success' or task.error on completion +``` + +### Creating New Extensions + +1. Create directory in `extensions/` +2. Add `package.json` with required fields: + - `engines.kortex`: Version compatibility + - `main`: Entry point (`./dist/extension.js`) + - `contributes`: Configuration, commands, etc. +3. Create `src/extension.ts` with `activate()` function +4. Add extension to build in root `package.json` scripts +5. Build using Vite: `vite build` + +### Security Considerations + +- External URLs require user confirmation (handled in `setupSecurityRestrictionsOnLinks`) +- Safe storage for credentials via `SafeStorageRegistry` +- Configuration scopes limit exposure of sensitive data +- Preload scripts enforce security boundaries between processes diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..f2fbf5fb1c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,271 @@ +# AGENTS.md + +This file provides guidance to AI code assistants when working with code in this repository. + +## Project Overview + +Kortex is an Electron-based desktop application built with Svelte that provides AI-powered container and Kubernetes management capabilities. It integrates with multiple AI providers (Gemini, OpenAI-compatible services, OpenShift AI) and supports flow execution through providers like Goose. The application also implements the Model Context Protocol (MCP) for AI agent integration. + +## Core Architecture + +### Multi-Process Electron Application + +Kortex follows the standard Electron architecture with three main processes: + +- **Main Process** (`packages/main`): Node.js backend handling system integration, extension loading, container/Kubernetes operations, and business logic +- **Renderer Process** (`packages/renderer`): Svelte-based UI running in the browser context +- **Preload Scripts** (`packages/preload` and `packages/preload-webview`): Bridge layer for secure IPC communication between main and renderer + +### Plugin System and Dependency Injection + +The core plugin system is implemented in `packages/main/src/plugin/index.ts` using Inversify for dependency injection. Key components include: + +- **PluginSystem class**: Orchestrates extension loading, configuration registry, and all core services +- **Extension Loader** (`ExtensionLoader`): Manages extension lifecycle (loading, starting, stopping, uninstalling) +- **Container**: Inversify DI container binding all major registries and managers as singletons + +All major services are registered as singletons in the DI container during initialization: + +- `ProviderRegistry`: Manages inference, container, and Kubernetes providers +- `ContainerProviderRegistry`: Handles Docker/Podman container operations +- `KubernetesClient`: Kubernetes cluster management +- `MCPManager` and `MCPRegistry`: Model Context Protocol integration +- `FlowManager`: Manages flow execution with providers like Goose +- `ChatManager`: AI chat functionality +- `ConfigurationRegistry`: Settings and configuration management + +### Extensions + +Extensions are located in the `extensions/` directory and follow a standard structure: + +- Each extension has a `package.json` with `main` pointing to `./dist/extension.js` +- Extensions must declare `engines.kortex` version compatibility +- Extensions can contribute: + - Inference providers (Gemini, OpenAI-compatible, OpenShift AI) + - Flow providers (Goose) + - MCP registries + - Configuration properties +- Extensions are built using Vite and export a standard activation API + +Available built-in extensions: + +- `gemini`: Google Gemini AI provider integration +- `goose`: Goose flow execution provider +- `mcp-registries`: MCP server registries +- `openai-compatible`: OpenAI-compatible API support +- `openshift-ai`: OpenShift AI platform integration + +### Extension API + +Extensions interact with Kortex through `@kortex-app/api` (`packages/extension-api`), which provides TypeScript definitions for: + +- Provider registration (inference, container, Kubernetes, VM, flow) +- Configuration management +- Command and menu contributions +- UI components (webviews, views, status bar items) +- Authentication providers +- MCP server integration + +## Development Commands + +### Setup and Installation + +```bash +# Install dependencies +pnpm install + +# Start in watch/development mode +pnpm watch +``` + +### Building + +```bash +# Build entire application +pnpm run build + +# Build individual packages +pnpm run build:main # Main process +pnpm run build:renderer # UI/renderer +pnpm run build:preload # Preload scripts +pnpm run build:extensions # All extensions + +# Build specific extension +cd extensions/gemini && pnpm run build +``` + +### Testing + +```bash +# Run all tests (unit + e2e) +pnpm test + +# Unit tests only +pnpm run test:unit + +# Unit tests with coverage +pnpm run test:unit:coverage + +# Run tests for specific packages +pnpm run test:main # Main process tests +pnpm run test:renderer # Renderer tests +pnpm run test:preload # Preload tests +pnpm run test:extensions # Extension tests + +# E2E tests with Playwright +pnpm run test:e2e # Build and run e2e tests +pnpm run test:e2e:run # Run e2e tests only (must build first) +pnpm run test:e2e:report # Show Playwright test report + +# Watch mode for development +pnpm run test:watch +``` + +### Code Quality + +```bash +# Format code +pnpm run format:check # Check formatting +pnpm run format:fix # Fix formatting issues + +# Linting +pnpm run lint:check # Check for linting issues +pnpm run lint:fix # Fix linting issues + +# Type checking +pnpm run typecheck # Check all packages +pnpm run typecheck:main # Main process only +pnpm run typecheck:renderer # Renderer only +pnpm run svelte:check # Svelte component type checking +``` + +### Building Distributables + +```bash +# Development build (directory output, no packaging) +pnpm run compile + +# Production build for release +pnpm run compile:next # With auto-publishing +pnpm run compile:current # Current version +pnpm run compile:pull-request # Without publishing +``` + +## Key Technical Details + +### Workspace Structure + +This is a pnpm monorepo with workspaces defined in `pnpm-workspace.yaml`: + +- `packages/*`: Core application packages (main, renderer, preload, extension-api, api, webview-api) +- `extensions/*`: Extension packages +- `scripts`: Build and utility scripts +- `tests/playwright`: E2E test suite + +### IPC Communication Pattern + +The main/renderer communication follows a structured pattern: + +- Main process exposes handlers via `ipcHandle()` in `packages/main/src/plugin/index.ts` +- Handlers follow naming convention: `:` (e.g., `container-provider-registry:listContainers`) +- Renderer invokes via exposed preload APIs +- Events are sent to renderer via `apiSender.send()` for real-time updates + +### Test File Organization + +Tests are co-located with source files: + +- Unit tests: `*.spec.ts` files alongside source code +- E2E tests: Located in `tests/playwright/src/` +- Test configuration: `vitest.config.js` at root defines workspace test projects +- 627 total test files across the codebase + +### Extension Lifecycle + +1. **Discovery**: `ExtensionLoader` scans `extensions/` directory +2. **Initialization**: Extensions are loaded via `ExtensionLoader.init()` +3. **Activation**: Extensions start when `ExtensionLoader.start()` is called after system ready +4. **Runtime**: Extensions can be started/stopped/removed dynamically through registry +5. **Development**: Use `extension-development-folders` API to load extensions from custom paths + +### Configuration System + +Configuration is managed through `ConfigurationRegistry`: + +- Properties defined in extension's `package.json` under `contributes.configuration.properties` +- Scopes: `DEFAULT`, `ContainerProviderConnection`, `KubernetesProviderConnection`, `InferenceProviderConnection`, `InferenceProviderConnectionFactory` +- Configuration can be experimental and requires explicit enablement +- Values stored and retrieved with dot notation (e.g., `gemini.factory.apiKey`) + +### Flow Execution + +Flows are AI-powered automation workflows: + +- Providers register flow capabilities through `ProviderRegistry` +- `FlowManager` coordinates flow generation, execution, and lifecycle +- Flows can be deployed to Kubernetes (YAML generation) +- Goose extension provides flow runtime capabilities +- MCP servers can be connected to flows for tool access + +### MCP (Model Context Protocol) Integration + +- `MCPRegistry`: Manages MCP registry sources (like mcp-get.com) +- `MCPManager`: Handles remote MCP server connections and lifecycle +- `MCPIPCHandler`: IPC bridge for MCP operations +- MCP servers provide tools that can be accessed by AI models +- Credentials and setup stored securely via `SafeStorageRegistry` + +## Important Patterns + +### Working with Containers and Pods + +Container operations go through `ContainerProviderRegistry`: + +- List containers: `container-provider-registry:listContainers` +- Container lifecycle: `startContainer`, `stopContainer`, `deleteContainer` +- Image operations: `pullImage`, `buildImage`, `deleteImage` +- Pod operations: `createPod`, `startPod`, `stopPod`, `removePod` +- All operations require `engineId` parameter identifying the container engine + +### Kubernetes Operations + +Kubernetes operations use `KubernetesClient`: + +- Context management: `getContexts()`, `setContext()`, `deleteContext()` +- Resource operations: CRUD operations for pods, deployments, services, etc. +- Port forwarding: `createPortForward()`, `deletePortForward()` +- Exec into containers: `execIntoContainer()` with stdin/stdout callbacks +- Apply YAML: `applyResourcesFromFile()`, `applyResourcesFromYAML()` + +### Task Management + +Long-running operations should use `TaskManager`: + +```typescript +const task = taskManager.createTask({ + title: 'Operation description', + action: { + name: 'Navigate to result', + execute: () => navigationManager.navigateToResource(), + }, +}); +// Set task.status = 'success' or task.error on completion +``` + +### Creating New Extensions + +1. Create directory in `extensions/` +2. Add `package.json` with required fields: + - `engines.kortex`: Version compatibility + - `main`: Entry point (`./dist/extension.js`) + - `contributes`: Configuration, commands, etc. +3. Create `src/extension.ts` with `activate()` function +4. Add extension to build in root `package.json` scripts +5. Build using Vite: `vite build` + +### Security Considerations + +- External URLs require user confirmation (handled in `setupSecurityRestrictionsOnLinks`) +- Safe storage for credentials via `SafeStorageRegistry` +- Configuration scopes limit exposure of sensitive data +- Preload scripts enforce security boundaries between processes diff --git a/CODE-GUIDELINES.md b/CODE-GUIDELINES.md deleted file mode 100644 index f2d60ebba3..0000000000 --- a/CODE-GUIDELINES.md +++ /dev/null @@ -1,171 +0,0 @@ -# Guidelines for Podman Desktop Code - -## Production code - -## Unit tests code - -### Use `vi.mocked`, not a generic `myFunctionMock` - -If you define a mock with `const myFunctionMock = vi.fn();` its type is `Mock`, which is a generic type. - -For example, do not write this, or Typescript won't be able to detect that you passed an object instead of a string to `mockResolvedValue`: - -```ts -const windowMethodMock = vi.fn(); - -Object.defineProperty(global, 'window', { - value: { - windowMethod: windowMethodMock, - }, -}); - -test('...', () => { - windowMethodMock.mockResolvedValue({ msg: 'a string' }); // here, Typescript is not able to detect that the type is wrong -}); -``` - -Instead, you can write `vi.mocked(window.windowMethod).mock...`, and Typescript will check that you correctly pass a string to `mockResolvedValue`: - -```ts -Object.defineProperty(global, 'window', { - value: { - windowMethod: vi.fn(), - }, -}); - -test('...', () => { - vi.mocked(window.windowMethod).mockResolvedValue('a string'); -}); -``` - -### Mock complete modules, spy on parts of module for specific tests - -When testing a module, you have to decide for each imported module if you mock the entire module or if you spy on specific functions of the module -for specific tests and keep the real implementation for the other functions. - -System modules (`node:fs`, etc) are most generally mocked, so you are sure that unit tests are executed in isolation of the system. For internal modules, -it's up to you to decide if you want to mock them or not, depending on the coverage you want for the unit tests. - -#### Mock a complete module - -Mock completely an imported module with `vi.mock('/path/to/module)`, and define mock implementation for each test with `vi.mocked(function).mock...()`. - -Use `vi.resetAllMocks()` in the top-level `beforeEach` to reset all mocks to a no-op function returning `undefined` before to start each test. - -```ts -import { existsSync } from 'node:fs'; -import { beforeEach, describe, expect, test, vi } from 'vitest'; - -// completely mock the fs module, to be sure to -// run the tests in complete isolation from the filesystem -vi.mock('node:fs'); - -beforeEach(() => { - vi.resetAllMocks(); -}); - -describe('the file exists', () => { - beforeEach(() => { - vi.mocked(existsSync).mockReturnValue(true); - }); - - test('file exists', () => { - // existsSync is mocked to return true - expect(codeCheckingIfFileExists('/file/not/found')).toBeTruthy(); - }); -}); - -describe('the file does not exist', () => { - beforeEach(() => { - vi.mocked(existsSync).mockReturnValue(false); - }); - - test('root does not exists', () => { - // existsSync is mocked to return false - expect(codeCheckingIfFileExists('/')).toBeFalsy(); - }); -}); - -test('file existence is not defined', () => { - // a no-op mock returning undefined is called - expect(codeCheckingIfFileExists('/file/not/found')).toBeUndefined(); -}); -``` - -#### Spy on a function for a specific test - -When you want to mock only one or a small number of functions of a module (for example a function of the module you are testing, or a function of an helper module from which you want to use real implementation for some functions) for a particular test, you can use `vi.spyOn(module, 'function')` to mock only `function` and keep the original implementation for the rest of the module. - -To be sure that the spied function is restored to its original implementation for the other tests, use `vi.restoreAllMocks()` in the top-level `beforeEach`. - -```ts -// helpers.ts -export function f1(): boolean { - return true; -} - -// mymodule.ts -import { f1 } from './helpers.js'; - -export class MyModuleToTest { - f2(): boolean { - return f1(); - } -} - -// mymodule.spec.ts -import { beforeEach, describe, expect, test, vi } from 'vitest'; -import { MyModuleToTest } from './mymodule.js'; -import * as helpers from './helpers.js'; - -let myModuleToTest: MyModuleToTest; - -beforeEach(() => { - myModuleToTest = new MyModuleToTest(); - - // restore f1 to its original implementation - vi.restoreAllMocks(); -}); - -describe('f1 returns false', () => { - beforeEach(() => { - vi.spyOn(helpers, 'f1').mockReturnValue(false); - }); - - test('f2 returns false', () => { - expect(myModuleToTest.f2()).toBeFalsy(); - expect(helpers.f1).toHaveBeenCalledOnce(); - }); -}); - -test('f2 returns true', () => { - // use the original implementation of f1 - expect(myModuleToTest.f2()).toBeTruthy(); - // this won't work, as f1 is not spied for this test - // expect(helpers.f1).toHaveBeenCalledOnce(); -}); -``` - -### screen.getBy vs screen.queryBy - -Calling `element = screen.getBy...` throws an error if no element is found. -For this reason, it is not necessary to call `expect(element).toBeInTheDocument()`, as the assertion -has already been done as part of `screen.getBy...`. - -It is necessary to use `element = screen.queryBy...` followed by `expect(element).not.toBeInTheDocument()` -when checking if a component does NOT exist, as this call does not throw any error, -but returns a `null` value if the element is not found. - -### Testing style attribute - -When we need to ensure a given style is applied to an HTMLElement, we should be using [tohavestyle](https://github.com/testing-library/jest-dom?tab=readme-ov-file#tohavestyle) - -#### Examples - -```ts -const { getByText } = render(); - -const text = getByText('text in the page'); -// [Good] -expect(text).toHaveStyle({ color: '#FFFFF'}); -``` diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md deleted file mode 100644 index 4209b95ddc..0000000000 --- a/CODE-OF-CONDUCT.md +++ /dev/null @@ -1,3 +0,0 @@ -## The Podman Desktop Project Community Code of Conduct - -The Podman Desktop project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/CODEOWNERS b/CODEOWNERS index 311b86d793..ad356dd5d0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,10 +1,18 @@ -# Default Owners -* @podman-desktop/reviewers @benoitf - -# website -website/** @podman-desktop/reviewers @slemeur @cdrage @benoitf @Firewall -website/docs/** @podman-desktop/reviewers @shipsing @slemeur @cdrage @benoitf @Firewall - -# playwright tests -tests/playwright/** @podman-desktop/reviewers @podman-desktop/qe-reviewers @benoitf +# +# 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 +* @kortex-hub/reviewers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 94a4b74b08..0000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,460 +0,0 @@ -# Contributing to Podman Desktop - -

- Podman Desktop -

- -We'd love to have you join the community! Below summarizes the processes -that we follow. - -## Topics - -- [Reporting Issues](#reporting-issues) -- [Providing Extensions](#providing-extensions) -- [Working On Issues](#working-on-issues) -- [Contributing](#contributing) -- [Continuous Integration](#continuous-integration) -- [Submitting Pull Requests](#submitting-pull-requests) -- [Communication](#communication) -- [Code Architecture](#code-architecture) -- [Maintainer Tasks](#maintainer-tasks) -- [Website Contributions](#website-contributions) - -## Reporting Issues - -Before opening an issue, check the backlog of -[open issues](https://github.com/containers/podman-desktop/issues) -to see if someone else has already reported it. - -If so, feel free to add -your scenario, or additional information, to the discussion. Or simply -"subscribe" to it to be notified when it is updated. - -If you find a new issue with the project we'd love to hear about it! The most -important aspect of a bug report is that it includes enough information for -us to reproduce it. So, please include as much detail as possible and try -to remove the extra stuff that doesn't really relate to the issue itself. -The easier it is for us to reproduce it, the faster it'll be fixed! - -Please don't include any private/sensitive information in your issue! - -## Providing Extensions - -Some of the best features of Podman Desktop aren't even in this repository! -Podman Desktop provides a set of API that expands its capabilities -by installing [extensions](https://podman-desktop.io/extensions). - -Extensions add support for new container engines, command line tools, -Kubernetes providers, or add elements to the UI like actions, badges, or views. -You can create your own extension and contribute it to the catalog for -others to use. - -See the [extension documentation](https://podman-desktop.io/docs/extensions) on our website for more information. - -## Working On Issues - -Often issues will be assigned to someone, to be worked on at a later time. - -If you are a member of the [Containers](https://github.com/containers) organization, -self-assign the issue with the `status/in-progress` label. - -If you can not set the label: add a quick comment in the issue asking that -the `status/in-progress` label to be set and a maintainer will label it. - -## Contributing - -This section describes how to start a contribution to Podman Desktop. - -### Prerequisites: Prepare your environment - -You can develop on either: `Windows`, `macOS` or `Linux`. - -Requirements: - -- [Node.js 22+](https://nodejs.org/en/) -- [pnpm v9.x](https://pnpm.io/installation) (`corepack enable pnpm`) - -Optional Linux requirements: - -- [Flatpak builder, runtime, and SDK, version 24.08](https://docs.flatpak.org/en/latest/first-build.html) - ```sh - flatpak remote-add --if-not-exists flathub --user https://flathub.org/repo/flathub.flatpakrepo - flatpak install --user flathub org.flatpak.Builder org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 - ``` -- GNU C and C++ compiler - Fedora/RHEL - ```sh - dnf install gcc-c++ - ``` - Ubuntu/Debian - ```sh - apt-get install build-essential - ``` - -On Windows: - -- [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2015-2017-2019-and-2022) - -### Step 1. Fork and clone Podman Desktop - -Clone and fork the project. - -Fork the repo using GitHub site and then clone the directory: - -```sh -git clone https://github.com//podman-desktop && cd podman-desktop -``` - -### Step 2. Install dependencies - -Fetch all dependencies using the command `pnpm`: - -```sh -pnpm install -``` - -### Step 3. Start in watch mode - -Run the application in watch mode: - -```sh -pnpm watch -``` - -The dev environment will track all files changes and reload the application respectively. - -### Step 4. Write and run tests - -Write tests! Please try to write some unit tests when submitting your PR. - -Run the unit and component tests using `pnpm`: - -```sh -pnpm test:unit -``` - -Depending on to what part of project you contribute to, you can specify to run tests for the given module only, ie., if you are working on extensions, you can run the tests for extensions and have faster feedback: - -```sh -pnpm test:extensions -``` - -or if you are contributing to a particular extension, you can call: - -```sh -pnpm test:extensions:compose -``` - -This will show a test results for restricted amount of tests: - -``` - ✓ src/os.spec.ts (3) - ✓ src/detect.spec.ts (10) 518ms - ✓ src/compose-github-releases.spec.ts (10) - ✓ src/compose-extension.spec.ts (16) - ✓ src/compose-wrapper-generator.spec.ts (4) - - Test Files 5 passed (5) - Tests 43 passed (43) - Start at 17:17:07 - Duration 1.27s (transform 562ms, setup 0ms, collect 1.25s, tests 587ms) -``` - -Check the npm script tasks in our `package.json` for more options. - -### Step 5. Run E2E tests - -In case of adding new feature, it is always suitable to make sure we do not bring any new regression. For this purpose we are using the E2E tests. They can be built and run using `pnpm` with a variety of options: - -- For all the tests: - -```sh -pnpm test:e2e -``` - -- For the smoke tests: - -```sh -pnpm test:e2e:smoke -``` - -- For the extension tests: - -```sh -pnpm test:e2e:extension -``` - -You can find more specific options on the [package.json](https://github.com/containers/podman-desktop/blob/main/package.json) file, under 'scripts'. - -However, there are some things that you have to take into account: - -- In order to make the tests pass you have to either: - - Remove `settings.json` from `~/.local/share/containers/podman-desktop/configuration/` or, - - Remove the objects with keys `"welcome.version"` and `"telemetry.*"` from the file, if you do not want to lose your settings -- Some of the tests can only be executed in certain operating systems. If your execution is skipping a test, this is very likely the reason why. -- If you want to execute the tests outside of the repository, you can find a setup guide in this [README](https://github.com/containers/podman-desktop/tree/main/tests/playwright#podman-desktop-playwright-tests). - -Finally, after executing the E2E tests, you can check the results in your browser with: - -```sh -pnpm exec playwright show-report tests/playwright/output/html-results -``` - -In case of an error, you can find more information that can help you debug in the `podman-desktop/tests/playwright/output` folder. You have the video repetitions on `videos`, captures of the application failing the test on `screenshots`, and the traces of the execution on `traces`. The latter ones can be opened with `npx playwright show-trace **_NOTE:_** macOS and Windows create binaries while Linux will create a `.flatpak`. Make sure your flatpak dependencies are installed for successful compiling on Linux. - -> **_macOS NOTE:_** On macOS the `dist/` folder will contain folders for `arm64` and `universal` `.app` files. Ignore these and use the `.app` file in the `dist/mac/` folder for testing. - -## Submitting Pull Requests - -### Process - -Whether it is a large patch or a one-line bug fix, make sure you explain in detail what's changing! - -Make sure you include the issue in your PR! For example, say: `Closes #XXX`. - -PRs will be approved by an [approver][owners] listed in [`CODEOWNERS`](CODEOWNERS). - -We typically require one approval for code as well as documentation-related PR's. If it is a large code-related PR, proof of review / testing (a video / screenshot) is required. - -**Avoid enabling auto-merge** until the PR has undergone sufficient reviews and contributors have been given ample time for assessment. A maintainer will review the PR prior to the final merge. It's common for code PRs to require up to a week before merging due to reasons such as ongoing releases or dependencies on other PRs. Additionally, documentation PRs might take a few days for integration. - -Some tips for the PR process: - -- No PR too small! Feel free to open a PR against tests, bugs, new features, docs, etc. -- Make sure you include as much information as possible in your PR so maintainers can understand. -- Try to break up larger PRs into smaller ones for easier reviewing -- Any additional code changes should be in a new commit so we can see what has changed between reviews. -- Squash your commits into logical pieces of work. - -### Use the correct commit message semantics - -We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. - -Some examples for correct titles would be: - -- `fix: prevent racing of requests` -- `chore: drop support for Node 6` -- `docs: add quickstart guide` - -For Podman Desktop we use the following types: - -- `fix:` A bug fix -- `chore:` Very small change / insignificant impact -- `docs:` Documentation only changes (ex. website) -- `build:` Changes that affect the build system -- `ci:` Changes to the CI (ex. GitHub actions) -- `feat:` A new feature -- `perf:` A code change that improves performance -- `refactor:` A code change that neither fixes a bug nor adds a feature -- `style:` Changes that affect the formatting, but not the ability of the code -- `test:` Adding missing tests / new tests - -Title formatting: - -``` -[optional scope]: - -[optional body] - -[optional footer(s)] -``` - -### Sign your PRs - -The sign-off is a line at the end of the explanation for the patch. Your -signature certifies that you wrote the patch or otherwise have the right to pass -it on as an open-source patch. - -Then you just add a line to every git commit message: - - Signed-off-by: Joe Smith - -Legal name must be used (no pseudonyms or anonymous contributions) - -If you set your `user.name` and `user.email` git configs, you can sign your -commit automatically with `git commit -s`. - -### Skipping Jobs for Draft Pull Requests on GitHub - -When creating a pull request in **draft mode** on GitHub, all CI/CD jobs are **skipped by default**. This behavior is intentional to avoid triggering unnecessary workflows while the pull request is still in progress. - -#### Triggering Jobs in Draft Mode - -If you want to run jobs for a pull request in **draft mode**, you need to manually apply the `area/ci` label to the pull request. Applying this label signals the CI system to execute the associated workflows, even though the pull request remains in draft. - -#### Steps to Trigger Jobs in Draft Mode - -1. Open your pull request in draft mode. -2. Navigate to the **Labels** section in the right-hand sidebar. -3. Apply the `area/ci` label. - -This action will trigger the configured CI/CD workflows for your draft pull request. - -#### Example Scenario - -- **Without the `area/ci` label**: No jobs will run for your draft pull request. -- **With the `area/ci` label**: Jobs will be triggered, allowing you to validate your work in progress. - -This ensures that CI resources are used efficiently while still providing flexibility for testing during the draft stage. - -### Review process - -1. Submit your PR -2. Reviewers are assigned by GitHub to two Podman Desktop developers -3. PR's require 1 LGTM / Approval (2 if it's a large code change) - -> **_NOTE:_** Confirm that your PR works on macOS, Windows and Linux if it's a significant change (not a UI improvement) - -> **_NOTE:_** If your PR hasn't been merged in an appropriate amount of time, ping the two developers assigned to the issue with `@` - -## Continuous Integration - -All pull requests and branch-merges automatically run: - -- Format and lint checking -- Cross-platform builds (Windows, macOS, Linux) -- Unit test (Linux) -- E2E tests (Linux, triggered by PR check, do not prevent merging of the PR in case of instability) - -You can follow these jobs in Github Actions https://github.com/containers/podman-desktop/actions - -## Communication - -For bugs/feature requests please [file issues](https://github.com/containers/podman-desktop/issues/new/choose) - -Discussions are possible using Github Discussions https://github.com/containers/podman-desktop/discussions/ - -## Code Architecture - -### Frameworks and tooling - -Within Podman Desktop, we use the following frameworks and tools to build the desktop application: - -- [Electron](https://www.electronjs.org/): In order to deploy cross-platform to multiple operating systems. -- [Svelte](https://svelte.dev/): The reactive UI/UX framework for building the interface. -- [Tailwind CSS](https://tailwindcss.com/): A utility-first CSS framework for the UI/UX framework. -- [Vite](https://vitejs.dev/): Dev tooling for rapid development, debugging and deployment. - -> **_NOTE:_** We also use TypeScript instead of JavaScript for strongly typed programming language development. - -### Testing - -Within Podman Desktop, we use the following for testing: - -- [Vitest](https://vitest.dev/): Unit tests - Written as `spec.ts` files. -- [Testing Library](https://testing-library.com/): Component tests - Utilities and best practices for writing component tests. -- [Playwright](https://playwright.dev/): Integration and E2E tests. - -### Folders - -Below are brief descriptions on the architecture on each folder of Podman Desktop and how it's organized. - -If you're unsure where to add code (renderer, UI, extensions, plugins) see the below TLDR: - -- `__mocks__/`: Mock packages for Vitest. -- `buildResources`: Podman Desktop logo location / build resources for electron -- `extensions`: We separate functionality into separate "extensions" to keep Podman Desktop modular. Here you'll find extensions such as Kubernetes, CRC, Podman and Docker functionality that Podman Desktop interacts with and integrates into the API (see `packages/extension-api`). Examples include `extensions/crc`, `extensions/podman`, `extensions/docker`. -- `packages/extension-api`: The extension API for extensions such as `extensions/podman` to interact with the Podman Desktop GUI. This API acts as a "middleware" to the main Electron functionality such as displaying notifications, progress messages, configuration changes, etc. -- `packages/main`: Electron process code that is responsible for creating the app's main windows, setting up system events and communicating with other processes -- `packages/preload`: Electron code that runs before the page gets rendered. Typically has access to APIs and used to setup communication processes between the main and renderer code. -- `packages/preload-docker-extension`: Electron preload code specific to the Docker Desktop extension. -- `packages/renderer`: Electron code that runs in the renderer process. The renderer runs separate to the main process and is responsible for typically rendering the main pages of Podman Desktop. Typically, this is where you find the `.svelte` code that renders the main Podman Desktop UI. -- `scripts`: Scripts Podman Desktop requires such as `pnpm watch` functionality and updating Electron vendorered modules. -- `tests`: Contains e2e tests for Podman Desktop. -- `types`: Additional types required for TypeScript. -- `website`: The documentation as well as [Podman Desktop website](https://podman-desktop.io) developed in [Docusaurus](https://docusaurus.io). -- `node_modules`: Location for Node.JS packages / dependencies. - -> **_NOTE:_** Each `extension` folder is a separately packaged module. If there are any issues with loading, make sure your module is packaged correctly. - -### UI colors - -Colors in Podman Desktop are now managed by a [`color-registry.ts`](https://github.com/containers/podman-desktop/blob/main/packages/main/src/plugin/color-registry.ts) file in order to easily switch between light and dark mode. - -When contributing a UI component to Podman Desktop that is colorized, you must go through some steps to figure out what color to use and how to reference it. - -Steps: - -1. Open the [`color-registry.ts`](https://github.com/containers/podman-desktop/blob/main/packages/main/src/plugin/color-registry.ts) file. -2. Figure out which color category from the `initColors()` function. -3. Use the referenced color with the format `[var(--pd-)]` - -Example: - -1. Choose what UI component you want to add: Ex. I want to add a new primary button. -2. Look under `initColors()` and pick `this.initButton()` and scroll down to `protected initButton()`. -3. Pick a color. I want to use the the "primary" button. So I will pick: `${button}primary-bg`. -4. Scroll up and note the `const` below `protected initButton()` which is `const button = 'button-';` -5. The color can be referenced with `[var(--pd-button-primary-bg)]`. The `[var(--pd-` portion will always be consistent when refering to a color variable. -6. For example: - -```ts - diff --git a/packages/renderer/src/lib/chat/components/McpsToInstallToast.svelte b/packages/renderer/src/lib/chat/components/McpsToInstallToast.svelte new file mode 100644 index 0000000000..4fb1f3f7b3 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/McpsToInstallToast.svelte @@ -0,0 +1,34 @@ + + +
+

The following MCPs are required to use this suggestion:

+
    + {#each mcpsToInstall as mcp (mcp.serverId)} +
  • + +
  • + {/each} +
+
diff --git a/packages/renderer/src/lib/chat/components/NoModelsAvailable.svelte b/packages/renderer/src/lib/chat/components/NoModelsAvailable.svelte new file mode 100644 index 0000000000..d6a5a23f0d --- /dev/null +++ b/packages/renderer/src/lib/chat/components/NoModelsAvailable.svelte @@ -0,0 +1,23 @@ + + +
+

No AI Models Available

+

You need to configure at least one AI model to start chatting.

+ + {#each $providerInfos as providerInfo (providerInfo.id)} + {#if providerInfo.inferenceProviderConnectionCreation} +
+ + {providerInfo.name} + +
+ {/if} + {/each} +
diff --git a/packages/renderer/src/lib/chat/components/app-sidebar.svelte b/packages/renderer/src/lib/chat/components/app-sidebar.svelte new file mode 100644 index 0000000000..529ef89b19 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/app-sidebar.svelte @@ -0,0 +1,66 @@ + + +{#if context.open} + + + +
+ { + context.setOpenMobile(false); + }} + class="flex flex-row items-center gap-3" + > + + Chatbot + + + + + {#snippet child({ props })} + + {/snippet} + + New Chat + +
+
+
+ + + +
+{/if} diff --git a/packages/renderer/src/lib/chat/components/auth-form.svelte b/packages/renderer/src/lib/chat/components/auth-form.svelte new file mode 100644 index 0000000000..bc26fcf66e --- /dev/null +++ b/packages/renderer/src/lib/chat/components/auth-form.svelte @@ -0,0 +1,89 @@ + + + + +
+
+ + + +
+ +
+ + + +
+ + {@render submitButton({ pending, success: !!form?.success })} + {@render children()} +
diff --git a/packages/renderer/src/lib/chat/components/chat-header.svelte b/packages/renderer/src/lib/chat/components/chat-header.svelte new file mode 100644 index 0000000000..419969d43d --- /dev/null +++ b/packages/renderer/src/lib/chat/components/chat-header.svelte @@ -0,0 +1,113 @@ + + +
+ + + {#if !sidebar.open || (innerWidth.current ?? 768) < 768} + + + {#snippet child({ props })} + + {/snippet} + + New Chat + + {/if} + + {#if !readonly} + + +
+ + {#if noMcps} +
+ +
+ {/if} +
+ {/if} + + + +
diff --git a/packages/renderer/src/lib/chat/components/chat.svelte b/packages/renderer/src/lib/chat/components/chat.svelte new file mode 100644 index 0000000000..a4be67e391 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/chat.svelte @@ -0,0 +1,144 @@ + + +
+ {#if hasModels} + + {/if} +
+ {#if hasModels} +
+ +
+ {#if !readonly} + + {/if} + +
+ + {:else} + + {/if} +
+
+ + + diff --git a/packages/renderer/src/lib/chat/components/code-block.svelte b/packages/renderer/src/lib/chat/components/code-block.svelte new file mode 100644 index 0000000000..e2ba7fb974 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/code-block.svelte @@ -0,0 +1,31 @@ + + +{#if inline} + + {@render children?.()} + +{:else} +
+
+          {@render children?.()}
+        
+
+{/if} diff --git a/packages/renderer/src/lib/chat/components/icons/PenToSquareIcon.svelte b/packages/renderer/src/lib/chat/components/icons/PenToSquareIcon.svelte new file mode 100644 index 0000000000..2f40e860cc --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/PenToSquareIcon.svelte @@ -0,0 +1,12 @@ + + + diff --git a/packages/renderer/src/lib/chat/components/icons/arrow-up.svelte b/packages/renderer/src/lib/chat/components/icons/arrow-up.svelte new file mode 100644 index 0000000000..7c5361342b --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/arrow-up.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/check-circle-fill.svelte b/packages/renderer/src/lib/chat/components/icons/check-circle-fill.svelte new file mode 100644 index 0000000000..749f801e18 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/check-circle-fill.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/chevron-down.svelte b/packages/renderer/src/lib/chat/components/icons/chevron-down.svelte new file mode 100644 index 0000000000..3f28c089a5 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/chevron-down.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/chevron-up.svelte b/packages/renderer/src/lib/chat/components/icons/chevron-up.svelte new file mode 100644 index 0000000000..e405e24f6a --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/chevron-up.svelte @@ -0,0 +1,24 @@ + + + diff --git a/packages/renderer/src/lib/chat/components/icons/globe.svelte b/packages/renderer/src/lib/chat/components/icons/globe.svelte new file mode 100644 index 0000000000..52d9171643 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/globe.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/loader.svelte b/packages/renderer/src/lib/chat/components/icons/loader.svelte new file mode 100644 index 0000000000..d0f93378fc --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/loader.svelte @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/lock.svelte b/packages/renderer/src/lib/chat/components/icons/lock.svelte new file mode 100644 index 0000000000..134dea753c --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/lock.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/message.svelte b/packages/renderer/src/lib/chat/components/icons/message.svelte new file mode 100644 index 0000000000..ac0b830ce4 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/message.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/more-horizontal.svelte b/packages/renderer/src/lib/chat/components/icons/more-horizontal.svelte new file mode 100644 index 0000000000..d06930e963 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/more-horizontal.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/panel-left.svelte b/packages/renderer/src/lib/chat/components/icons/panel-left.svelte new file mode 100644 index 0000000000..cc95b3ba28 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/panel-left.svelte @@ -0,0 +1,26 @@ + + + diff --git a/packages/renderer/src/lib/chat/components/icons/paperclip.svelte b/packages/renderer/src/lib/chat/components/icons/paperclip.svelte new file mode 100644 index 0000000000..e7b0a92f97 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/paperclip.svelte @@ -0,0 +1,29 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/pencil-edit.svelte b/packages/renderer/src/lib/chat/components/icons/pencil-edit.svelte new file mode 100644 index 0000000000..16241af90b --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/pencil-edit.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/plus.svelte b/packages/renderer/src/lib/chat/components/icons/plus.svelte new file mode 100644 index 0000000000..9597190370 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/plus.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/share.svelte b/packages/renderer/src/lib/chat/components/icons/share.svelte new file mode 100644 index 0000000000..ac130076b3 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/share.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/sidebar-left.svelte b/packages/renderer/src/lib/chat/components/icons/sidebar-left.svelte new file mode 100644 index 0000000000..68eecc04fb --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/sidebar-left.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/sparkles.svelte b/packages/renderer/src/lib/chat/components/icons/sparkles.svelte new file mode 100644 index 0000000000..b807562001 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/sparkles.svelte @@ -0,0 +1,34 @@ + + + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/stop.svelte b/packages/renderer/src/lib/chat/components/icons/stop.svelte new file mode 100644 index 0000000000..5c9d44eab3 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/stop.svelte @@ -0,0 +1,22 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/icons/trash.svelte b/packages/renderer/src/lib/chat/components/icons/trash.svelte new file mode 100644 index 0000000000..41b63c98ff --- /dev/null +++ b/packages/renderer/src/lib/chat/components/icons/trash.svelte @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/renderer/src/lib/chat/components/ipc-chat-transport.ts b/packages/renderer/src/lib/chat/components/ipc-chat-transport.ts new file mode 100644 index 0000000000..13fc67773b --- /dev/null +++ b/packages/renderer/src/lib/chat/components/ipc-chat-transport.ts @@ -0,0 +1,65 @@ +import type { ChatRequestOptions, ChatTransport, UIMessage, UIMessageChunk } from 'ai'; + +import type { ModelInfo } from '/@/lib/chat/components/model-info'; + +import { currentChatId } from '../state/current-chat-id.svelte.js'; + +interface Dependencies { + getModel: () => ModelInfo; + getMCP: () => Array; +} + +export class IPCChatTransport implements ChatTransport { + constructor(private readonly dependencies: Dependencies) {} + + async sendMessages( + options: { + trigger: 'submit-message' | 'regenerate-message'; + chatId: string; + messageId: string | undefined; + messages: T[]; + abortSignal: AbortSignal | undefined; + } & ChatRequestOptions, + ): Promise> { + const uiMessages = JSON.parse(JSON.stringify(options.messages)); + const model = this.dependencies.getModel(); + console.log('Selected model', model); + + const mcp = this.dependencies.getMCP(); + + return new ReadableStream({ + async start(controller): Promise { + const { providerId, connectionName, label } = model; + await window.inferenceStreamText( + { + chatId: options.chatId, + providerId, + connectionName, + modelId: label, + mcp, + messages: uiMessages, + }, + (chunk: UIMessageChunk) => { + console.log('IPCChatTransport->chunk:', chunk); + controller.enqueue(chunk); + }, + (error: unknown) => { + console.error('Error during inferenceStreamText:', error); + controller.error(error); + }, + () => { + console.log('IPCChatTransport: Stream completed'); + controller.close(); + }, + ); + currentChatId.value = options.chatId; + }, + }); + } + + reconnectToStream(options: { chatId: string } & ChatRequestOptions): Promise | null> { + //FIX ME: not implemented + console.log('reconnecting to stream with options', options); + return Promise.resolve(null); + } +} diff --git a/packages/renderer/src/lib/chat/components/markdown/index.ts b/packages/renderer/src/lib/chat/components/markdown/index.ts new file mode 100644 index 0000000000..d7aef19c8b --- /dev/null +++ b/packages/renderer/src/lib/chat/components/markdown/index.ts @@ -0,0 +1 @@ +export { default as Markdown } from './renderer.svelte'; diff --git a/packages/renderer/src/lib/chat/components/markdown/renderer.svelte b/packages/renderer/src/lib/chat/components/markdown/renderer.svelte new file mode 100644 index 0000000000..bbcd027b7b --- /dev/null +++ b/packages/renderer/src/lib/chat/components/markdown/renderer.svelte @@ -0,0 +1,89 @@ + + + + {#snippet ol(props)} + {@const { children, ...rest } = props} +
    + {@render children?.()} +
+ {/snippet} + {#snippet ul(props)} + {@const { children, ...rest } = props} +
    + {@render children?.()} +
+ {/snippet} + {#snippet li(props)} + {@const { children, ...rest } = props} +
  • + {@render children?.()} +
  • + {/snippet} + + {#snippet strong(props)} + {@const { children, ...rest } = props} + + {@render children?.()} + + {/snippet} + {#snippet a(props)} + {@const { children, ...rest } = props} + + {@render children?.()} + + {/snippet} + + {#snippet h1(props)} + {@const { children, ...rest } = props} +

    + {@render children?.()} +

    + {/snippet} + {#snippet h2(props)} + {@const { children, ...rest } = props} +

    + {@render children?.()} +

    + {/snippet} + {#snippet h3(props)} + {@const { children, ...rest } = props} +

    + {@render children?.()} +

    + {/snippet} + {#snippet h4(props)} + {@const { children, ...rest } = props} +

    + {@render children?.()} +

    + {/snippet} + {#snippet h5(props)} + {@const { children, ...rest } = props} +
    + {@render children?.()} +
    + {/snippet} + {#snippet h6(props)} + {@const { children, ...rest } = props} +
    + {@render children?.()} +
    + {/snippet} + {#snippet code(props)} + + {/snippet} +
    diff --git a/packages/renderer/src/lib/chat/components/mcp-messages.spec.ts b/packages/renderer/src/lib/chat/components/mcp-messages.spec.ts new file mode 100644 index 0000000000..f841815e1e --- /dev/null +++ b/packages/renderer/src/lib/chat/components/mcp-messages.spec.ts @@ -0,0 +1,107 @@ +/********************************************************************** + * 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 '@testing-library/jest-dom/vitest'; + +import type { UIMessage } from '@ai-sdk/svelte'; +import { render, screen } from '@testing-library/svelte'; +import type { DynamicToolUIPart } from 'ai'; +import { describe, expect, test } from 'vitest'; + +import MCPMessages from './mcp-messages.svelte'; + +function dynamicTool(toolName: string, id: string): DynamicToolUIPart { + return { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: id, + toolName, + input: { foo: id }, + output: { bar: id }, + } as DynamicToolUIPart; +} + +describe('mcp-messages.svelte', () => { + test('shows empty state when no assistant dynamic-tool parts are present', () => { + const messages: UIMessage[] = [ + { id: 'u1', role: 'user', parts: [{ type: 'text', text: 'Hello' }] }, + { id: 'a1', role: 'assistant', parts: [{ type: 'text', text: 'Hi!' }] }, + // dynamic-tool but from user: must be ignored + { id: 'u2', role: 'user', parts: [dynamicTool('Ignored Tool', 't0')] }, + ]; + + render(MCPMessages, { messages }); + + // Header should always be present + expect(screen.getByText('MCP')).toBeInTheDocument(); + + // Empty state should be visible + expect(screen.getByText('No MCP activity yet.')).toBeInTheDocument(); + }); + + test('renders one entry per assistant message that contains dynamic-tool parts', async () => { + const msgWithOneTool: UIMessage = { + id: 'a1', + role: 'assistant', + parts: [dynamicTool('Tool A', 't1')], + }; + + const msgWithTwoTools: UIMessage = { + id: 'a2', + role: 'assistant', + parts: [dynamicTool('Tool B1', 't2'), dynamicTool('Tool B2', 't3')], + }; + + const assistantWithoutTools: UIMessage = { + id: 'a3', + role: 'assistant', + parts: [{ type: 'text', text: 'no tools here' }], + }; + + const systemMessage: UIMessage = { + id: 's1', + role: 'system', + parts: [dynamicTool('System Tool', 'tX')], // should be ignored because not assistant + }; + + const messages = [ + { id: 'u1', role: 'user', parts: [{ type: 'text', text: 'Hello' }] } satisfies UIMessage, + msgWithOneTool, + msgWithTwoTools, + assistantWithoutTools, + systemMessage, + ]; + + render(MCPMessages, { messages }); + + // Empty state should not be present + expect(screen.queryByText('No MCP activity yet.')).not.toBeInTheDocument(); + + // The component wraps each ToolParts in a specific container div; count them + //const entryContainers = container.querySelectorAll('.rounded-md.bg-background.p-2.ring-1.ring-border'); + const entryContainers = await screen.findAllByRole('row'); + expect(entryContainers.length).toBe(2); // only a1 and a2 contain dynamic-tool parts + + // Ensure tool names from ToolParts appear (verifies children rendered) + expect(screen.getByText('Tool A', { exact: true })).toBeInTheDocument(); + expect(screen.getByText('Tool B1', { exact: true })).toBeInTheDocument(); + expect(screen.getByText('Tool B2', { exact: true })).toBeInTheDocument(); + + // Assistant without tools and non-assistant dynamic tools should not contribute entries + expect(screen.queryByText('System Tool')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/renderer/src/lib/chat/components/mcp-messages.svelte b/packages/renderer/src/lib/chat/components/mcp-messages.svelte new file mode 100644 index 0000000000..6db3caf578 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/mcp-messages.svelte @@ -0,0 +1,83 @@ + + +{#if open} + +{:else} + + +{/if} diff --git a/packages/renderer/src/lib/chat/components/mcp-selector.svelte b/packages/renderer/src/lib/chat/components/mcp-selector.svelte new file mode 100644 index 0000000000..9f85f3664c --- /dev/null +++ b/packages/renderer/src/lib/chat/components/mcp-selector.svelte @@ -0,0 +1,86 @@ + + + (open = val)}> + + {#snippet child({ props })} + + {/snippet} + + + {#if $mcpRemoteServerInfos.length === 0} + + No MCP available + + {/if} + + {#each $mcpRemoteServerInfos as mcpRemoteServerInfo (mcpRemoteServerInfo.id)} + s.id === mcpRemoteServerInfo.id)} + > +
    +
    {mcpRemoteServerInfo.name}
    +
    + +
    + +
    +
    + {/each} +
    +
    +
    diff --git a/packages/renderer/src/lib/chat/components/message-reasoning.svelte b/packages/renderer/src/lib/chat/components/message-reasoning.svelte new file mode 100644 index 0000000000..b098984fd7 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/message-reasoning.svelte @@ -0,0 +1,69 @@ + + +
    + {#if loading} +
    +
    Reasoning
    +
    + +
    +
    + {:else} +
    +
    Reasoned for a few seconds
    + + +
    { + expanded = !expanded; + }} + > + +
    +
    + {/if} + + {#if expanded} +
    + +
    + {/if} +
    diff --git a/packages/renderer/src/lib/chat/components/messages.svelte b/packages/renderer/src/lib/chat/components/messages.svelte new file mode 100644 index 0000000000..2403a4108c --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages.svelte @@ -0,0 +1,65 @@ + + +
    + {#if mounted && messages.length === 0} + + {/if} + + {#each messages as message (message.id)} + + {/each} + + {#if loading && messages.length > 0 && messages[messages.length - 1].role === 'user'} + + {/if} + +
    +
    diff --git a/packages/renderer/src/lib/chat/components/messages/ExportIcon.svelte b/packages/renderer/src/lib/chat/components/messages/ExportIcon.svelte new file mode 100644 index 0000000000..b1208da9e9 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/ExportIcon.svelte @@ -0,0 +1,12 @@ + + + diff --git a/packages/renderer/src/lib/chat/components/messages/overview.svelte b/packages/renderer/src/lib/chat/components/messages/overview.svelte new file mode 100644 index 0000000000..aec99668c8 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/overview.svelte @@ -0,0 +1,14 @@ + + +
    +
    +

    + +

    + +
    +
    diff --git a/packages/renderer/src/lib/chat/components/messages/preview-message.svelte b/packages/renderer/src/lib/chat/components/messages/preview-message.svelte new file mode 100644 index 0000000000..f41c4e1cea --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/preview-message.svelte @@ -0,0 +1,165 @@ + + +
    + +
    + {#if message.role === 'assistant'} +
    +
    + +
    +
    + {/if} + +
    + {#if message.role === 'assistant'} + + {#if tools.length > 0} + + {/if} + {/if} + + {#if message.parts.filter(part => part.type === 'file').length > 0} +
    + {#each message.parts.filter(part => part.type === 'file') as attachment (attachment.url)} + + {/each} +
    + {/if} + + {#each message.parts as part, i (`${message.id}-${i}`)} + {@const { type } = part} + {#if type === 'reasoning'} + + {:else if type === 'text'} + {#if mode === 'view'} +
    + {#if message.role === 'user' && !readonly} + + + {#snippet child({ props })} + + {/snippet} + + Edit message + + {/if} +
    + +
    +
    + {:else if mode === 'edit'} +
    +
    + + + +
    + {/if} + + + + {/if} + {/each} + + + +
    +
    +
    diff --git a/packages/renderer/src/lib/chat/components/messages/thinking-message.svelte b/packages/renderer/src/lib/chat/components/messages/thinking-message.svelte new file mode 100644 index 0000000000..9cca02b153 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/thinking-message.svelte @@ -0,0 +1,27 @@ + + +
    +
    +
    + +
    + +
    +
    Hmm...
    +
    +
    +
    diff --git a/packages/renderer/src/lib/chat/components/messages/tool-parts.spec.ts b/packages/renderer/src/lib/chat/components/messages/tool-parts.spec.ts new file mode 100644 index 0000000000..26fe3f2062 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/tool-parts.spec.ts @@ -0,0 +1,167 @@ +/********************************************************************** + * 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 '@testing-library/jest-dom/vitest'; + +import { render, screen } from '@testing-library/svelte'; +import userEvent from '@testing-library/user-event'; +import type { DynamicToolUIPart } from 'ai'; +import { describe, expect, test, vi } from 'vitest'; + +import ToolParts from './tool-parts.svelte'; + +function matches(content: string, val: unknown): boolean { + try { + return JSON.stringify(JSON.parse(content)) === JSON.stringify(val); + } catch (e: unknown) { + return false; + } +} + +vi.mock('svelte/transition', () => ({ + slide: (): { delay: number; duration: number } => ({ + delay: 0, + duration: 0, + }), +})); + +describe('tool-parts.svelte', () => { + test('renders tool sections collapsed by default and toggles to show input/output details', async () => { + const tools: Array = [ + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't1', + toolName: 'Tool Text', + input: { a: 1 }, + output: { content: [{ type: 'text', text: 'Hello world' }] }, + }, + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't2', + toolName: 'Tool Image', + input: { b: 2 }, + output: { content: [{ type: 'image', data: 'AAA', mimeType: 'image/png' }] }, + }, + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't3', + toolName: 'Tool Resource', + input: { c: 3 }, + output: { + content: [ + { + type: 'resource', + resource: { uri: 'file:///tmp/readme.txt', mimeType: 'text/plain', text: 'Some text here' }, + }, + ], + }, + }, + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't4', + toolName: 'Tool Result', + input: { d: 4 }, + output: { toolResult: { ok: true } }, + }, + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't5', + toolName: 'Tool Non-MCP', + input: { e: 5 }, + output: 'plain', + }, + { + type: 'dynamic-tool', + state: 'output-available', + toolCallId: 't6', + toolName: 'Tool Error', + input: { f: 6 }, + output: { isError: true, content: [] }, + }, + ]; + + const user = userEvent.setup(); + + render(ToolParts, { tools: tools }); + + // Verify headers are present + for (const t of tools) { + const element = screen.getByText(t.toolName); + expect(element).toBeDefined(); + expect(element).toBeInTheDocument(); + } + + // By default no input JSONs should be visible + for (const t of tools) { + expect(screen.queryByText(content => matches(content, t.input))).not.toBeInTheDocument(); + } + + // Helper to click the toggle for a given tool name + async function toggle(name: string): Promise { + const title = screen.getByText(name); + const row = title.parentElement as HTMLElement | null; + expect(row).toBeTruthy(); + const toggleEl = row!.querySelector('.cursor-pointer') as HTMLElement | null; + expect(toggleEl).toBeTruthy(); + await user.click(toggleEl!); + } + + // Expand Tool Text and assert text content rendering + await toggle('Tool Text'); + expect(screen.getByText('Input')).toBeInTheDocument(); + expect(screen.getByText(content => matches(content, { a: 1 }))).toBeInTheDocument(); + // Output renders a Text block + expect(screen.getByText('Text')).toBeInTheDocument(); + expect(screen.getByText('Hello world')).toBeInTheDocument(); + + // Collapse Tool Text and ensure input JSON disappears + await toggle('Tool Text'); + expect(screen.queryByText(content => matches(content, { a: 1 }))).not.toBeInTheDocument(); + + // Expand Tool Image and assert image rendering + await toggle('Tool Image'); + expect(screen.getByText(content => matches(content, { b: 2 }))).toBeInTheDocument(); + const img = screen.getByAltText('MCP image') as HTMLImageElement; + expect(img).toBeInTheDocument(); + expect(img.src).toContain('data:image/png;base64,AAA'); + + // Expand Tool Resource and assert resource details + await toggle('Tool Resource'); + expect(screen.getAllByText('Resource').length).toBeGreaterThan(0); + expect(screen.getByText('file:///tmp/readme.txt')).toBeInTheDocument(); + // The resource also renders an inner Text section with the provided text + expect(screen.getByText('Some text here')).toBeInTheDocument(); + + // Expand Tool Result and assert toolResult branch + await toggle('Tool Result'); + expect(screen.getByText(content => matches(content, { ok: true }))).toBeInTheDocument(); + + // Expand Tool Non-MCP and assert raw JSON of output + await toggle('Tool Non-MCP'); + // JSON.stringify('plain') => "plain" + expect(screen.getByText('"plain"')).toBeInTheDocument(); + + // Expand Tool Error and check error message appears + await toggle('Tool Error'); + expect(screen.getByText('MCP reported an error')).toBeInTheDocument(); + }); +}); diff --git a/packages/renderer/src/lib/chat/components/messages/tool-parts.svelte b/packages/renderer/src/lib/chat/components/messages/tool-parts.svelte new file mode 100644 index 0000000000..194afd2e87 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/messages/tool-parts.svelte @@ -0,0 +1,165 @@ + + + +
    + {#each tools as tool (tool.toolCallId)} +
    + + +
    {tool.toolName}
    + + +
    + +
    +
    + + {#if expanded.has(tool.toolCallId)} +
    + Input + + {JSON.stringify(tool.input, null, 2)} + + + Output + {#if isMcpCallToolResult(tool.output)} + {#if isRecord(tool.output) && 'isError' in tool.output && tool.output.isError} +
    MCP reported an error
    + {/if} + + {#if isRecord(tool.output) && Array.isArray(tool.output.content)} +
    + {#each tool.output.content as item, idx (idx)} + {#if isMcpTextContent(item)} +
    +
    Text
    +
    {formatText(item.text)}
    +
    + {:else if isMcpImageContent(item)} +
    +
    Image ({item.mimeType})
    + MCP {item.mimeType} content +
    + {:else if isMcpResourceContent(item)} +
    +
    Resource
    +
    URI: {item.resource.uri}
    + {#if item.resource.mimeType} +
    MIME: {item.resource.mimeType}
    + {/if} + {#if 'text' in item.resource && item.resource.text} +
    +
    Text
    + {formatText(item.resource.text)} +
    + {:else if 'blob' in item.resource && item.resource.blob} +
    Binary blob provided.
    + {/if} +
    + {:else} + {JSON.stringify(item, null, 2)} + {/if} + {/each} +
    + {:else if isRecord(tool.output) && 'toolResult' in tool.output} + + {JSON.stringify(tool.output.toolResult, null, 2)} + + {:else} + + {JSON.stringify(tool.output, null, 2)} + + {/if} + {:else} + + {JSON.stringify(tool.output, null, 2)} + + {/if} +
    + {/if} + + {/each} +
    + diff --git a/packages/renderer/src/lib/chat/components/model-info.ts b/packages/renderer/src/lib/chat/components/model-info.ts new file mode 100644 index 0000000000..5173a74b72 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/model-info.ts @@ -0,0 +1,23 @@ +/********************************************************************** + * 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 + ***********************************************************************/ + +export interface ModelInfo { + providerId: string; + connectionName: string; + label: string; +} diff --git a/packages/renderer/src/lib/chat/components/model-selector.svelte b/packages/renderer/src/lib/chat/components/model-selector.svelte new file mode 100644 index 0000000000..c112b0f50f --- /dev/null +++ b/packages/renderer/src/lib/chat/components/model-selector.svelte @@ -0,0 +1,97 @@ + + + (open = val)}> + + {#snippet child({ props })} + + {/snippet} + + + {#each groups.entries() as [key, mModels] (key)} + + + + {key} + + {#each mModels as model (model.label)} + +
    +
    {model.label}
    +
    + +
    + +
    +
    + {/each} + +
    + {/each} +
    +
    diff --git a/packages/renderer/src/lib/chat/components/multimodal-input.svelte b/packages/renderer/src/lib/chat/components/multimodal-input.svelte new file mode 100644 index 0000000000..8e03b32c4e --- /dev/null +++ b/packages/renderer/src/lib/chat/components/multimodal-input.svelte @@ -0,0 +1,203 @@ + + +
    + {#if mounted && chatClient.messages.length === 0 && attachments.length === 0} + + {/if} + + {#if attachments.length > 0} +
    + {#each attachments as attachment (attachment.url)} + + {/each} + +
    + {/if} + + diff --git a/packages/renderer/src/lib/chat/components/ui/tooltip/index.ts b/packages/renderer/src/lib/chat/components/ui/tooltip/index.ts new file mode 100644 index 0000000000..3fd33fb1ad --- /dev/null +++ b/packages/renderer/src/lib/chat/components/ui/tooltip/index.ts @@ -0,0 +1,31 @@ +import { Tooltip as TooltipPrimitive } from 'bits-ui'; +import type { ComponentProps } from 'svelte'; + +import Content from './tooltip-content.svelte'; +import Trigger from './tooltip-trigger.svelte'; + +const Root = TooltipPrimitive.Root; +const Provider = TooltipPrimitive.Provider; +const Portal = TooltipPrimitive.Portal; + +type TooltipProps = ComponentProps; +type TooltipTriggerProps = ComponentProps; +type TooltipProviderProps = ComponentProps; +type TooltipContentProps = ComponentProps; + +export { + Content, + Portal, + Provider, + Root, + // + Root as Tooltip, + Content as TooltipContent, + type TooltipContentProps, + type TooltipProps, + Provider as TooltipProvider, + type TooltipProviderProps, + Trigger as TooltipTrigger, + type TooltipTriggerProps, + Trigger, +}; diff --git a/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-content.svelte b/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-content.svelte new file mode 100644 index 0000000000..a24ec168a5 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-content.svelte @@ -0,0 +1,48 @@ + + + + + {@render children?.()} + + {#snippet child({ props })} +
    + {/snippet} +
    +
    +
    diff --git a/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-trigger.svelte b/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-trigger.svelte new file mode 100644 index 0000000000..16086d4ef4 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/ui/tooltip/tooltip-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/packages/renderer/src/lib/chat/components/visibility-selector.svelte b/packages/renderer/src/lib/chat/components/visibility-selector.svelte new file mode 100644 index 0000000000..51d0539073 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/visibility-selector.svelte @@ -0,0 +1,85 @@ + + + (open = val)}> + + {#snippet child({ props })} + + {/snippet} + + + + {#each visibilities as visibility (visibility.id)} + { + chatHistory.updateVisibility(chat.id, visibility.id).catch((err: unknown) => { + console.error('Failed to update chat visibility', err); + }); + open = false; + }} + class="group/item flex flex-row items-center justify-between gap-4" + data-active={visibility.id === chatFromHistory?.visibility} + > +
    + {visibility.label} +
    + {visibility.description} +
    +
    +
    + +
    +
    + {/each} +
    +
    diff --git a/packages/renderer/src/lib/chat/components/visibility-type.ts b/packages/renderer/src/lib/chat/components/visibility-type.ts new file mode 100644 index 0000000000..a5620b0047 --- /dev/null +++ b/packages/renderer/src/lib/chat/components/visibility-type.ts @@ -0,0 +1 @@ +export type VisibilityType = 'private' | 'public'; diff --git a/packages/renderer/src/lib/chat/hooks/chat-history.svelte.ts b/packages/renderer/src/lib/chat/hooks/chat-history.svelte.ts new file mode 100644 index 0000000000..11af58b253 --- /dev/null +++ b/packages/renderer/src/lib/chat/hooks/chat-history.svelte.ts @@ -0,0 +1,75 @@ +import { getContext, setContext } from 'svelte'; +import { toast } from 'svelte-sonner'; + +import type { VisibilityType } from '/@/lib/chat/components/visibility-type'; +import type { Chat } from '/@api/chat/schema.js'; + +const contextKey = Symbol('ChatHistory'); + +export class ChatHistory { + #loading = $state(false); + #revalidating = $state(false); + chats = $state([]); + + get loading(): boolean { + return this.#loading; + } + + get revalidating(): boolean { + return this.#revalidating; + } + + constructor(chatsPromise: Promise) { + this.#loading = true; + this.#revalidating = true; + chatsPromise + .then(chats => (this.chats = chats)) + .finally(() => { + this.#loading = false; + this.#revalidating = false; + }) + .catch((error: unknown) => { + console.error('Failed to load chat history', error); + }); + } + + getChatDetails = (chatId: string): Chat | undefined => { + return this.chats.find(c => c.id === chatId); + }; + + updateVisibility = async (chatId: string, visibility: VisibilityType): Promise => { + const chat = this.chats.find(c => c.id === chatId); + if (chat) { + chat.visibility = visibility; + } + const res = await fetch('/api/chat/visibility', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ chatId, visibility }), + }); + if (!res.ok) { + toast.error('Failed to update chat visibility'); + // try reloading data from source in case another competing mutation caused an issue + await this.refetch(); + } + }; + + setContext(): void { + setContext(contextKey, this); + } + + async refetch(): Promise { + this.#revalidating = true; + try { + this.chats = await window.inferenceGetChats(); + } finally { + this.#revalidating = false; + } + } + + static fromContext(): ChatHistory { + return getContext(contextKey); + } +} diff --git a/packages/renderer/src/lib/chat/hooks/is-mobile.svelte.ts b/packages/renderer/src/lib/chat/hooks/is-mobile.svelte.ts new file mode 100644 index 0000000000..8742f9f805 --- /dev/null +++ b/packages/renderer/src/lib/chat/hooks/is-mobile.svelte.ts @@ -0,0 +1,9 @@ +import { MediaQuery } from 'svelte/reactivity'; + +import { BREAKPOINTS } from '/@/lib/chat/utils/constants'; + +export class IsMobile extends MediaQuery { + constructor() { + super(`max-width: ${BREAKPOINTS.md.value - 1}${BREAKPOINTS.md.unit}`); + } +} diff --git a/packages/renderer/src/lib/chat/hooks/local-storage.svelte.ts b/packages/renderer/src/lib/chat/hooks/local-storage.svelte.ts new file mode 100644 index 0000000000..0fb61a7cc9 --- /dev/null +++ b/packages/renderer/src/lib/chat/hooks/local-storage.svelte.ts @@ -0,0 +1,39 @@ +/* eslint-disable import/no-duplicates */ +import { on } from 'svelte/events'; +import { createSubscriber } from 'svelte/reactivity'; +/* eslint-enable simple-import-sort/imports */ + +export class LocalStorage { + #key: string; + #defaultValue: T; + #subscribe: () => void; + + constructor(key: string, defaultValue: T) { + this.#key = key; + this.#defaultValue = defaultValue; + + this.#subscribe = createSubscriber(update => { + const off = on(window, 'storage', event => { + if (event.key === this.#key) { + update(); + } + }); + + return off; + }); + } + + get value(): T { + this.#subscribe(); + const storedValue = localStorage.getItem(this.#key); + return storedValue === null ? this.#defaultValue : JSON.parse(storedValue); + } + + set value(v: T) { + localStorage.setItem(this.#key, JSON.stringify(v)); + } + + delete(): void { + localStorage.removeItem(this.#key); + } +} diff --git a/packages/renderer/src/lib/chat/hooks/lock.ts b/packages/renderer/src/lib/chat/hooks/lock.ts new file mode 100644 index 0000000000..ba20e0fc35 --- /dev/null +++ b/packages/renderer/src/lib/chat/hooks/lock.ts @@ -0,0 +1,17 @@ +import { getContext, setContext } from 'svelte'; + +export class Lock { + locked = false; +} + +const lockKey = (key: string): symbol => Symbol.for(`lock:${key}`); + +export function getLock(key: string): Lock { + const k = lockKey(key); + let lock = getContext(k); + if (!lock) { + lock = new Lock(); + setContext(k, lock); + } + return lock; +} diff --git a/packages/renderer/src/lib/chat/hooks/selected-model.svelte.ts b/packages/renderer/src/lib/chat/hooks/selected-model.svelte.ts new file mode 100644 index 0000000000..a710170227 --- /dev/null +++ b/packages/renderer/src/lib/chat/hooks/selected-model.svelte.ts @@ -0,0 +1,14 @@ +import { SynchronizedCookie } from '/@/lib/chat/utils/reactivity.svelte'; + +/** + * @deprecated + */ +export class SelectedModel extends SynchronizedCookie { + constructor(value: string) { + super('selected-model', value); + } + + static fromContext(): SelectedModel { + return super.fromContext('selected-model'); + } +} diff --git a/packages/renderer/src/lib/chat/route/CustomChat.svelte b/packages/renderer/src/lib/chat/route/CustomChat.svelte new file mode 100644 index 0000000000..2ec0c8b6dd --- /dev/null +++ b/packages/renderer/src/lib/chat/route/CustomChat.svelte @@ -0,0 +1,57 @@ + + +{#if selectedChatModel} +
    + + + {#await chatMessagesPromise} + Loading + {:then { chat, messages }} + sidebarCollapsed.value = !open}> + + + + + + {/await} + +
    +{/if} + diff --git a/packages/renderer/src/lib/chat/route/chat-layout.svelte b/packages/renderer/src/lib/chat/route/chat-layout.svelte new file mode 100644 index 0000000000..745b418aad --- /dev/null +++ b/packages/renderer/src/lib/chat/route/chat-layout.svelte @@ -0,0 +1,16 @@ + + + + + {@render children?.()} + diff --git a/packages/renderer/src/lib/chat/route/main-chat-layout.svelte b/packages/renderer/src/lib/chat/route/main-chat-layout.svelte new file mode 100644 index 0000000000..9b8baf3b15 --- /dev/null +++ b/packages/renderer/src/lib/chat/route/main-chat-layout.svelte @@ -0,0 +1,12 @@ + + + + + {@render children()} + diff --git a/packages/renderer/src/lib/chat/state/current-chat-id.svelte.ts b/packages/renderer/src/lib/chat/state/current-chat-id.svelte.ts new file mode 100644 index 0000000000..fa14f347c5 --- /dev/null +++ b/packages/renderer/src/lib/chat/state/current-chat-id.svelte.ts @@ -0,0 +1 @@ +export const currentChatId = $state<{ value: string | undefined }>({ value: undefined }); diff --git a/packages/renderer/src/lib/chat/state/flow-creation-data.svelte.ts b/packages/renderer/src/lib/chat/state/flow-creation-data.svelte.ts new file mode 100644 index 0000000000..d0a5469aca --- /dev/null +++ b/packages/renderer/src/lib/chat/state/flow-creation-data.svelte.ts @@ -0,0 +1,15 @@ +import type { ModelInfo } from '/@/lib/chat/components/model-info'; +import type { FlowGenerationParameters } from '/@api/chat/flow-generation-parameters-schema'; +import type { MCPRemoteServerInfo } from '/@api/mcp/mcp-server-info'; + +export interface FlowCreationData extends FlowGenerationParameters { + model: ModelInfo; + mcp: MCPRemoteServerInfo[]; +} + +/** + * A state to temporarily hold the data for a new flow + * when navigating from a chat session to the creation page. + * It's set to `undefined` after being read to prevent stale data. + */ +export const flowCreationData = $state<{ value: FlowCreationData | undefined }>({ value: undefined }); diff --git a/packages/renderer/src/lib/chat/state/sidebar-collapsed.svelte.ts b/packages/renderer/src/lib/chat/state/sidebar-collapsed.svelte.ts new file mode 100644 index 0000000000..04634223a9 --- /dev/null +++ b/packages/renderer/src/lib/chat/state/sidebar-collapsed.svelte.ts @@ -0,0 +1 @@ +export const sidebarCollapsed = $state({ value: true }); diff --git a/packages/renderer/src/lib/chat/utils/chat.ts b/packages/renderer/src/lib/chat/utils/chat.ts new file mode 100644 index 0000000000..a27e393693 --- /dev/null +++ b/packages/renderer/src/lib/chat/utils/chat.ts @@ -0,0 +1,53 @@ +import type { Attachment } from '@ai-sdk/ui-utils'; +import type { AssistantModelMessage, FileUIPart, ToolModelMessage, UIMessage } from 'ai'; + +import type { Document, Message } from '/@api/chat/schema.js'; + +export function convertToUIMessages(messages: Array): Array { + return messages.map(message => ({ + id: message.id, + parts: message.parts as UIMessage['parts'], + role: message.role as UIMessage['role'], + // Note: content will soon be deprecated in @ai-sdk/react + content: '', + createdAt: message.createdAt, + experimental_attachments: (message.attachments as Array) ?? [], + })); +} + +export function getMostRecentUserMessage(messages: Array): UIMessage | undefined { + const userMessages = messages.filter(message => message.role === 'user'); + return userMessages.at(-1); +} + +export function getDocumentTimestampByIndex(documents: Array, index: number): Date { + if (!documents) { + return new Date(); + } + if (index > documents.length) { + return new Date(); + } + + return documents[index].createdAt; +} + +type ResponseMessageWithoutId = ToolModelMessage | AssistantModelMessage; +type ResponseMessage = ResponseMessageWithoutId & { id: string }; + +export function getTrailingMessageId({ messages }: { messages: Array }): string | null { + const trailingMessage = messages.at(-1); + + if (!trailingMessage) { + return null; + } + + return trailingMessage.id; +} + +export function fileUIPart2Attachment(part: FileUIPart): Attachment { + return { + name: part.filename ?? part.url.substring(part.url.lastIndexOf('/') + 1), + contentType: part.mediaType, + url: part.url, + }; +} diff --git a/packages/renderer/src/lib/chat/utils/constants.ts b/packages/renderer/src/lib/chat/utils/constants.ts new file mode 100644 index 0000000000..62bb23adb2 --- /dev/null +++ b/packages/renderer/src/lib/chat/utils/constants.ts @@ -0,0 +1,24 @@ +export const BREAKPOINTS = { + sm: { + unit: 'px', + value: 640, + }, + md: { + unit: 'px', + value: 768, + }, + lg: { + unit: 'px', + value: 1024, + }, + xl: { + unit: 'px', + value: 1280, + }, + '2xl': { + unit: 'px', + value: 1536, + }, +} as const; + +export const allowAnonymousChats = true; diff --git a/packages/renderer/src/lib/chat/utils/reactivity.svelte.ts b/packages/renderer/src/lib/chat/utils/reactivity.svelte.ts new file mode 100644 index 0000000000..61ef6d8209 --- /dev/null +++ b/packages/renderer/src/lib/chat/utils/reactivity.svelte.ts @@ -0,0 +1,53 @@ +import { getContext, setContext } from 'svelte'; + +export class Box { + value = $state() as T; + + constructor(value: T) { + this.value = value; + } +} + +/** + * Expects there to be a route at `/api/synchronized-cookie/:key` that sets a cookie with the given key/value. + * That handler is responsible for validating the cookie value and setting the cookie with the given key. + * This uses fire-and-forget logic for setting the cookie, optimistically updating local state. + */ +export class SynchronizedCookie { + #contextKey: symbol; + #key: string; + #value = $state()!; + + constructor(key: string, value: string) { + this.#key = key; + this.#value = value; + this.#contextKey = Symbol.for(`SynchronizedCookie:${key}`); + } + + get key(): string { + return this.#key; + } + + get value(): string { + return this.#value; + } + + set value(v: string) { + fetch(`/api/synchronized-cookie/${this.#key}`, { + method: 'POST', + body: JSON.stringify({ value: v }), + headers: { + 'Content-Type': 'application/json', + }, + }).catch(console.error); + this.#value = v; + } + + setContext(): void { + setContext(this.#contextKey, this); + } + + static fromContext(key: string): SynchronizedCookie { + return getContext(Symbol.for(`SynchronizedCookie:${key}`)); + } +} diff --git a/packages/renderer/src/lib/chat/utils/shadcn.ts b/packages/renderer/src/lib/chat/utils/shadcn.ts new file mode 100644 index 0000000000..f546a382ed --- /dev/null +++ b/packages/renderer/src/lib/chat/utils/shadcn.ts @@ -0,0 +1,13 @@ +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChild = T extends { child?: any } ? Omit : T; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type WithoutChildren = T extends { children?: any } ? Omit : T; +export type WithoutChildrenOrChild = WithoutChildren>; +export type WithElementRef = T & { ref?: U | null }; diff --git a/packages/renderer/src/lib/chat/utils/types.ts b/packages/renderer/src/lib/chat/utils/types.ts new file mode 100644 index 0000000000..76c97ee0aa --- /dev/null +++ b/packages/renderer/src/lib/chat/utils/types.ts @@ -0,0 +1,9 @@ +import type { Snippet } from 'svelte'; + +export type WithElementRef = T & { + ref?: U | null; +}; + +export type WithElementRefAndChild = WithElementRef & { + child?: Snippet<[{ props: T }]>; +}; diff --git a/packages/renderer/src/lib/compose/ComposeDetails.spec.ts b/packages/renderer/src/lib/compose/ComposeDetails.spec.ts index 0eac31e926..2dd6324090 100644 --- a/packages/renderer/src/lib/compose/ComposeDetails.spec.ts +++ b/packages/renderer/src/lib/compose/ComposeDetails.spec.ts @@ -18,7 +18,7 @@ import '@testing-library/jest-dom/vitest'; -import type { ContainerInspectInfo } from '@podman-desktop/api'; +import type { ContainerInspectInfo } from '@kortex-app/api'; import { fireEvent, render, screen } from '@testing-library/svelte'; /* eslint-disable import/no-duplicates */ import { tick } from 'svelte'; diff --git a/packages/renderer/src/lib/container/ContainerExport.spec.ts b/packages/renderer/src/lib/container/ContainerExport.spec.ts index 9c400e4d4c..9e7ff2bf49 100644 --- a/packages/renderer/src/lib/container/ContainerExport.spec.ts +++ b/packages/renderer/src/lib/container/ContainerExport.spec.ts @@ -18,7 +18,7 @@ import '@testing-library/jest-dom/vitest'; -import type { Uri } from '@podman-desktop/api'; +import type { Uri } from '@kortex-app/api'; import { render, screen } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import { tick } from 'svelte'; diff --git a/packages/renderer/src/lib/container/ContainerInfoUI.ts b/packages/renderer/src/lib/container/ContainerInfoUI.ts index b309aa05d8..11b62f266f 100644 --- a/packages/renderer/src/lib/container/ContainerInfoUI.ts +++ b/packages/renderer/src/lib/container/ContainerInfoUI.ts @@ -16,7 +16,7 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import type { Port } from '@podman-desktop/api'; +import type { Port } from '@kortex-app/api'; // type of groups export enum ContainerGroupInfoTypeUI { diff --git a/packages/renderer/src/lib/container/CreateContainerFromExistingImage.spec.ts b/packages/renderer/src/lib/container/CreateContainerFromExistingImage.spec.ts index 78eb21490b..243e689005 100644 --- a/packages/renderer/src/lib/container/CreateContainerFromExistingImage.spec.ts +++ b/packages/renderer/src/lib/container/CreateContainerFromExistingImage.spec.ts @@ -18,7 +18,7 @@ import '@testing-library/jest-dom/vitest'; -import type { ImageInfo, ProviderStatus } from '@podman-desktop/api'; +import type { ImageInfo, ProviderStatus } from '@kortex-app/api'; import { render, screen, waitFor, within } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import { tick } from 'svelte'; @@ -28,7 +28,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { providerInfos } from '/@/stores/providers'; import type { ImageSearchResult } from '/@api/image-registry'; -import type { ProviderContainerConnectionInfo, ProviderInfo } from '/@api/provider-info'; +import { ProviderConnectionType, type ProviderContainerConnectionInfo, type ProviderInfo } from '/@api/provider-info'; import CreateContainerFromExistingImage from './CreateContainerFromExistingImage.svelte'; @@ -91,6 +91,7 @@ vi.mock('tinro', () => { const pStatus: ProviderStatus = 'started'; const pInfo: ProviderContainerConnectionInfo = { name: 'test', + connectionType: ProviderConnectionType.CONTAINER, displayName: 'test', status: 'started', endpoint: { diff --git a/packages/renderer/src/lib/container/container-utils.ts b/packages/renderer/src/lib/container/container-utils.ts index 3726e2c06a..1634c36664 100644 --- a/packages/renderer/src/lib/container/container-utils.ts +++ b/packages/renderer/src/lib/container/container-utils.ts @@ -16,7 +16,7 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import type { Port } from '@podman-desktop/api'; +import type { Port } from '@kortex-app/api'; import { ContainerIcon } from '@podman-desktop/ui-svelte/icons'; // eslint-disable-next-line unicorn/prefer-node-protocol import { Buffer } from 'buffer'; diff --git a/packages/renderer/src/lib/context/contextKey.ts b/packages/renderer/src/lib/context/contextKey.ts index b7d65dda0a..6e0d2343b1 100644 --- a/packages/renderer/src/lib/context/contextKey.ts +++ b/packages/renderer/src/lib/context/contextKey.ts @@ -28,7 +28,7 @@ /* eslint-disable sonarjs/single-character-alternation */ /* eslint-disable sonarjs/updated-loop-counter */ /* eslint-disable sonarjs/function-return-type */ -import type { Event } from '@podman-desktop/api'; +import type { Event } from '@kortex-app/api'; import type { IDisposable } from '/@api/disposable.js'; diff --git a/packages/renderer/src/lib/dashboard/DashboardPage.svelte b/packages/renderer/src/lib/dashboard/DashboardPage.svelte index d2978cee2d..c213aca5e1 100644 --- a/packages/renderer/src/lib/dashboard/DashboardPage.svelte +++ b/packages/renderer/src/lib/dashboard/DashboardPage.svelte @@ -3,10 +3,8 @@ import { NavPage } from '@podman-desktop/ui-svelte'; import { SvelteMap } from 'svelte/reactivity'; import ProviderConfiguring from '/@/lib/dashboard/ProviderConfiguring.svelte'; -import ExtensionBanners from '/@/lib/recommendation/ExtensionBanners.svelte'; import { providerInfos } from '../../stores/providers'; -import LearningCenter from '../learning-center/LearningCenter.svelte'; import NotificationsBox from './NotificationsBox.svelte'; import ProviderConfigured from './ProviderConfigured.svelte'; import type { InitializationContext } from './ProviderInitUtils'; @@ -16,7 +14,6 @@ import ProviderNotInstalled from './ProviderNotInstalled.svelte'; import ProviderReady from './ProviderReady.svelte'; import ProviderStarting from './ProviderStarting.svelte'; import ProviderStopped from './ProviderStopped.svelte'; -import ReleaseNotesBox from './ReleaseNotesBox.svelte'; const providerInitContexts = new SvelteMap(); @@ -39,15 +36,16 @@ function getInitializationContext(id: string): InitializationContext { } - + {#snippet content()}
    + {#if providersReady.length > 0} {#each providersReady as providerReady (providerReady.internalId)} diff --git a/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts index c99371e2d6..6acb062c42 100644 --- a/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts +++ b/packages/renderer/src/lib/dashboard/NewContentOnDashboardBadge.spec.ts @@ -17,7 +17,7 @@ ***********************************************************************/ import '@testing-library/jest-dom/vitest'; -import type { ProviderStatus } from '@podman-desktop/api'; +import type { ProviderStatus } from '@kortex-app/api'; import { render, screen } from '@testing-library/svelte'; import { tick } from 'svelte'; import { router } from 'tinro'; @@ -26,7 +26,7 @@ import { expect, test } from 'vitest'; import { notificationQueue } from '/@/stores/notifications'; import { providerInfos } from '/@/stores/providers'; import type { NotificationCard } from '/@api/notification'; -import type { ProviderContainerConnectionInfo, ProviderInfo } from '/@api/provider-info'; +import { ProviderConnectionType, type ProviderContainerConnectionInfo, type ProviderInfo } from '/@api/provider-info'; import NewContentOnDashboardBadge from './NewContentOnDashboardBadge.svelte'; @@ -47,6 +47,7 @@ const notification1: NotificationCard = { const pStatus: ProviderStatus = 'started'; const pInfo: ProviderContainerConnectionInfo = { name: 'test', + connectionType: ProviderConnectionType.CONTAINER, displayName: 'test', status: 'started', endpoint: { diff --git a/packages/renderer/src/lib/dashboard/NotificationCardItem.spec.ts b/packages/renderer/src/lib/dashboard/NotificationCardItem.spec.ts deleted file mode 100644 index 88c1d7f1f6..0000000000 --- a/packages/renderer/src/lib/dashboard/NotificationCardItem.spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -/********************************************************************** - * 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 '@testing-library/jest-dom/vitest'; - -import { faCircleExclamation, faCircleInfo, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; -import { fireEvent, render, screen } from '@testing-library/svelte'; -import { beforeAll, expect, test, vi } from 'vitest'; - -import type { NotificationCard } from '/@api/notification'; - -import NotificationCardItem from './NotificationCardItem.svelte'; - -const removeNotificationMock = vi.fn(); - -beforeAll(() => { - Object.defineProperty(window, 'removeNotification', { value: removeNotificationMock }); -}); - -test('Expect notification card to show notification title, description and close button', async () => { - const notification: NotificationCard = { - id: 1, - extensionId: 'extension', - title: 'title', - body: 'description', - type: 'info', - }; - render(NotificationCardItem, { - notification, - }); - - const titleDiv = screen.getByLabelText('Notification title'); - const descriptionDiv = screen.getByLabelText('Notification description'); - expect(titleDiv).toBeInTheDocument(); - expect(descriptionDiv).toBeInTheDocument(); - expect(titleDiv.textContent).toEqual('title'); - expect(descriptionDiv.textContent).toContain('description'); - - const deleteButton = screen.getByRole('button', { name: 'Delete notification 1' }); - expect(deleteButton).toBeInTheDocument(); - expect(deleteButton).toHaveAttribute('title', 'Delete notification'); - - await fireEvent.click(deleteButton); - - expect(removeNotificationMock).toBeCalled(); -}); - -test('Test info notification style and icon', () => { - const notification: NotificationCard = { - id: 1, - extensionId: 'extension', - title: 'Info notification title', - body: 'Info notification description', - type: 'info', - }; - const { getByTitle } = render(NotificationCardItem, { - notification, - }); - const iconTitle = getByTitle('Notification icon', { exact: false }); - const icon = iconTitle.parentElement; - // check icon shape - const pdIconPath = icon?.querySelector('path')?.getAttribute('d'); - const faIconPath = faCircleInfo.icon[4]; // index 4 is the actual path as per FA IconDefinition - expect(pdIconPath).toBe(faIconPath); - // check icon color - expect(icon).toHaveClass('text-[var(--pd-state-info)]'); - // check icon title contains the text from nested title element - expect(iconTitle.textContent).toBe('Notification icon - info'); - // check region top border - expect(screen.getByRole('region', { name: 'id: 1' })).toHaveClass('border-[var(--pd-state-info)]'); -}); - -test('Test warning notification style and icon', () => { - const notification: NotificationCard = { - id: 1, - extensionId: 'extension', - title: 'Warning notification title', - body: 'Warning notification description', - type: 'warn', - }; - const { getByTitle } = render(NotificationCardItem, { - notification, - }); - const iconTitle = getByTitle('Notification icon', { exact: false }); - const icon = iconTitle.parentElement; - // check icon shape - const pdIconPath = icon?.querySelector('path')?.getAttribute('d'); - const faIconPath = faExclamationTriangle.icon[4]; // index 4 is the actual path as per FA IconDefinition - expect(pdIconPath).toBe(faIconPath); - // check icon color - expect(icon).toHaveClass('text-[var(--pd-state-warning)]'); - // check icon title - expect(iconTitle.textContent).toBe('Notification icon - warn'); - // check region top border - expect(screen.getByRole('region', { name: 'id: 1' })).toHaveClass('border-[var(--pd-state-warning)]'); -}); - -test('Test error notification style and icon', () => { - const notification: NotificationCard = { - id: 1, - extensionId: 'extension', - title: 'Error notification title', - body: 'Error notification description', - type: 'error', - }; - const { getByTitle } = render(NotificationCardItem, { - notification, - }); - const iconTitle = getByTitle('Notification icon', { exact: false }); - const icon = iconTitle.parentElement; - // check icon shape - const pdIconPath = icon?.querySelector('path')?.getAttribute('d'); - const faIconPath = faCircleExclamation.icon[4]; // index 4 is the actual path as per FA IconDefinition - expect(pdIconPath).toBe(faIconPath); - // check icon color - expect(icon).toHaveClass('text-[var(--pd-state-error)]'); - // check icon title - expect(iconTitle.textContent).toBe('Notification icon - error'); - // check region top border - expect(screen.getByRole('region', { name: 'id: 1' })).toHaveClass('border-[var(--pd-state-error)]'); -}); diff --git a/packages/renderer/src/lib/dashboard/ProviderCard.spec.ts b/packages/renderer/src/lib/dashboard/ProviderCard.spec.ts index ab3f9df2c6..2e1c248b30 100644 --- a/packages/renderer/src/lib/dashboard/ProviderCard.spec.ts +++ b/packages/renderer/src/lib/dashboard/ProviderCard.spec.ts @@ -18,12 +18,12 @@ import '@testing-library/jest-dom/vitest'; -import type { ProviderImages } from '@podman-desktop/api'; +import type { ProviderImages } from '@kortex-app/api'; import { render, screen } from '@testing-library/svelte'; import { tick } from 'svelte'; import { expect, test } from 'vitest'; -import type { ProviderInfo } from '/@api/provider-info'; +import { type ProviderInfo } from '/@api/provider-info'; import ProviderCard from './ProviderCard.svelte'; @@ -49,6 +49,13 @@ test('Expect provider region', async () => { images: {} as ProviderImages, installationSupport: false, cleanupSupport: false, + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); @@ -78,6 +85,13 @@ test('Expect provider name', async () => { images: {} as ProviderImages, installationSupport: false, cleanupSupport: false, + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); @@ -108,6 +122,13 @@ test('Expect provider icon', async () => { images: { icon: 'test.png' } as ProviderImages, installationSupport: false, cleanupSupport: false, + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); @@ -142,7 +163,13 @@ test('Expect no provider version', async () => { images: {} as ProviderImages, installationSupport: false, cleanupSupport: false, - // no version + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); @@ -173,6 +200,13 @@ test('Expect provider version', async () => { installationSupport: false, version: '1.2.3', cleanupSupport: false, + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); @@ -203,6 +237,13 @@ test('Expect provider state', async () => { images: {} as ProviderImages, installationSupport: false, cleanupSupport: false, + inferenceConnections: [], + ragConnections: [], + flowConnections: [], + inferenceProviderConnectionCreation: false, + inferenceProviderConnectionInitialization: false, + ragProviderConnectionCreation: false, + ragProviderConnectionInitialization: false, }; render(ProviderCard, { provider }); diff --git a/packages/renderer/src/lib/dashboard/ProviderDetectionChecksButton.svelte b/packages/renderer/src/lib/dashboard/ProviderDetectionChecksButton.svelte index 5944fd825e..1ff5f771ba 100644 --- a/packages/renderer/src/lib/dashboard/ProviderDetectionChecksButton.svelte +++ b/packages/renderer/src/lib/dashboard/ProviderDetectionChecksButton.svelte @@ -1,6 +1,6 @@ + + + + +{#if showModal} + +{/if} diff --git a/packages/renderer/src/lib/flows/FlowCreate.svelte b/packages/renderer/src/lib/flows/FlowCreate.svelte new file mode 100644 index 0000000000..e399475467 --- /dev/null +++ b/packages/renderer/src/lib/flows/FlowCreate.svelte @@ -0,0 +1,193 @@ + + + + {#snippet content()} +
    + {#if $isFlowConnectionAvailable} +
    +
    +
    You can create a flow using this form by selecting a model, one or several tools (from MCP servers) + and specifying instructions.
    +
    A flow can also be created by exporting a chat session. All information's on this page will then automatically be filled.
    +
    The export feature in the chat window is available through the icon
    +
    + {#if error} + + {/if} + +
    +
    + Flow Name + +
    + + +
    + Description + + +
    +
    +
    +
    +
    + + + +
    +
    +

    Knowledges

    +
    + +
    + +
    +

    Knowledge Bases

    + +
    + + +
    +
    + + + + +
    +
    + + +
    + All + Available +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + StatusEnvironment NameDatabaseChunkerSourcesActions
    + + + + + + + + + + + podman-knowledges + + +
    + Milvus +
    +
    + +
    + Docling +
    +
    + 23 + +
    + + +
    +
    + + + + + + + + + + + openshift-knowledges + + +
    + ChromaDB +
    +
    + +
    + HF Sentence Transformers +
    +
    + 47 + +
    + + +
    +
    + + + + + + + + + + + docker-knowledges + + +
    + Milvus +
    +
    + +
    + HF Sentence Transformers +
    +
    + 0 + +
    + + +
    +
    + + + + + + + + + + + kubernetes-knowledges + + +
    + ChromaDB +
    +
    + +
    + Docling +
    +
    + 15 + +
    + + +
    +
    +
    +
    + + +
    +
    +

    Knowledges

    +
    + +
    + +
    + + + + + + + +
    +
    +
    + + + + + + + +
    +
    +

    podman-knowledges

    +
    + RunningMilvus + Docling +
    +
    +
    + +
    + + +
    +
    +
    + + + + + +
    +
    +
    +

    Environment Details

    +
    + Name + podman-knowledges +
    +
    + Status + Running +
    +
    + Created + 2 days ago +
    +
    + Last Updated + 5 hours ago +
    +
    + +
    +

    Configuration

    +
    + Vector Store + Milvus +
    +
    + Chunker + Docling +
    +
    + Sources + 23 files +
    +
    + Total Vectors + 1,247 +
    +
    +
    +
    + + +
    + +
    + + + + + +
    Drag & Drop files here
    +
    or click to browse • Supports PDF, TXT, MD, DOCX
    + +
    + + +
    +
    +

    Uploaded Sources (23)

    +
    + +
    +
    +
    + + + + +
    +
    +

    podman-documentation.pdf

    +
    2.3 MB • Uploaded 2 hours ago • 45 chunks
    +
    +
    +
    + + +
    +
    + +
    +
    +
    + + + + +
    +
    +

    getting-started-guide.md

    +
    156 KB • Uploaded 1 day ago • 12 chunks
    +
    +
    +
    + + +
    +
    + +
    +
    +
    + + + + +
    +
    +

    api-reference.pdf

    +
    1.8 MB • Uploaded 3 days ago • 32 chunks
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +

    Database Information

    +
    + Type + Milvus +
    +
    + Version + 2.3.2 +
    +
    + Host + localhost:19530 +
    +
    + Collection + podman_knowledges +
    +
    + +
    +

    Storage Statistics

    +
    + Total Vectors + 1,247 +
    +
    + Dimensions + 768 +
    +
    + Index Type + IVF_FLAT +
    +
    + Disk Usage + 124 MB +
    +
    +
    +
    + + +
    +
    +
    +

    Model Information

    +
    + Type + Docling +
    +
    + Version + 1.2.0 +
    +
    + Processing + Advanced OCR + Layout +
    +
    + Output Format + Structured chunks +
    +
    + +
    +

    Configuration

    +
    + Chunk Size + 512 tokens +
    +
    + Overlap + 50 tokens +
    +
    + Language + Auto-detect +
    +
    + OCR Enabled + Yes +
    +
    +
    +
    +
    +
    + + + + + + + diff --git a/recommendations.json b/recommendations.json index 430286a010..0f7a344684 100644 --- a/recommendations.json +++ b/recommendations.json @@ -1,25 +1,4 @@ { - "extensions": [ - { - "extensionId": "redhat.ai-lab", - "publishDate": "2024-05-07T13:25:00.000Z", - "title": "Supercharge your apps with Podman AI Lab!", - "description": "Want to see what AI can do for your app, but not sure where to start?", - "when": "provider.podman.status === 'ready'", - "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAYAAAAIACAYAAACLn7v6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAE7WlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDI0LTA0LTA1VDEwOjI5OjE2KzAyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyNC0wNC0wNVQxMDozMzoxNyswMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyNC0wNC0wNVQxMDozMzoxNyswMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6YzQwMWQwZDktNjQyZS04MDQxLWE0YjktNjkwMjVlZTc3MmNiIiB4bXBNTTpEb2N1bWVudElEPSJ4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", - "thumbnail": "data:image/png;base64,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", - "background": { - "light": "data:image/png;base64,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", - "dark": "data:image/png;base64,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" - } - } - ], - "registries": [ - { - "id": "registry.redhat.io", - "name": "Red Hat Authentication", - "extensionId": "redhat.redhat-authentication", - "errors": ["is denied", "Please login to the Red Hat Registry"] - } - ] + "extensions": [], + "registries": [] } diff --git a/scripts/watch.mjs b/scripts/watch.mjs index c1b992df03..1013c2e08c 100644 --- a/scripts/watch.mjs +++ b/scripts/watch.mjs @@ -120,42 +120,6 @@ const setupMainPackageWatcher = ({ config: { server, extensions } }) => { }); }; -const setupUiPackageWatcher = () => { - const logger = createLogger(LOG_LEVEL, { - prefix: '[ui]', - }); - - /** @type {ChildProcessWithoutNullStreams | null} */ - let spawnProcess = null; - - if (spawnProcess !== null) { - spawnProcess.off('exit', process.exit); - spawnProcess.kill('SIGINT'); - spawnProcess = null; - } - - const dirname = join(__dirname, '..', 'node_modules', '.bin'); - const exe = 'svelte-package'.concat(process.platform === 'win32' ? '.cmd' : ''); - const newPath = `${process.env.PATH}${delimiter}${dirname}`; - spawnProcess = spawn(exe, ['-w'], { - cwd: './packages/ui/', - env: { PATH: newPath, ...process.env }, - shell: process.platform === 'win32', - }); - - spawnProcess.stdout.on('data', d => d.toString().trim() && logger.warn(d.toString(), { timestamp: true })); - spawnProcess.stderr.on('data', d => { - const data = d.toString().trim(); - if (!data) return; - const mayIgnore = stderrFilterPatterns.some(r => r.test(data)); - if (mayIgnore) return; - logger.error(data, { timestamp: true }); - }); - - // Stops the watch script when the application has been quit - spawnProcess.on('exit', process.exit); -}; - /** * Start or restart App when source files are changed * @param {{ws: import('vite').WebSocketServer}} WebSocketServer @@ -178,25 +142,6 @@ const setupPreloadPackageWatcher = ({ ws }) => }, }); -const setupPreloadDockerExtensionPackageWatcher = ({ ws }) => - getWatcher({ - name: 'reload-page-on-preload-docker-extension-package-change', - configFile: 'packages/preload-docker-extension/vite.config.js', - writeBundle() { - // Generating exposedInMainWorld.d.ts when preload package is changed. - generateAsync({ - input: 'packages/preload-docker-extension/tsconfig.json', - output: 'packages/preload-docker-extension/exposedInDockerExtension.d.ts', - }); - - if (ws) { - ws.send({ - type: 'full-reload', - }); - } - }, - }); - const setupPreloadWebviewPackageWatcher = ({ ws }) => getWatcher({ name: 'reload-page-on-preload-webview-package-change', @@ -281,9 +226,7 @@ const setupExtensionApiWatcher = name => { setupExtensionApiWatcher(extension); } await setupPreloadPackageWatcher(viteDevServer); - await setupPreloadDockerExtensionPackageWatcher(viteDevServer); await setupPreloadWebviewPackageWatcher(viteDevServer); - await setupUiPackageWatcher(); await setupMainPackageWatcher(viteDevServer); } catch (e) { console.error(e); diff --git a/storybook/.storybook/main.css b/storybook/.storybook/main.css deleted file mode 100644 index 911f6e88a7..0000000000 --- a/storybook/.storybook/main.css +++ /dev/null @@ -1,2 +0,0 @@ -@import 'tailwindcss'; -@config '../tailwind.config.js'; diff --git a/storybook/.storybook/main.ts b/storybook/.storybook/main.ts deleted file mode 100644 index 36e42de3d6..0000000000 --- a/storybook/.storybook/main.ts +++ /dev/null @@ -1,48 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { StorybookConfig } from '@storybook/svelte-vite'; -import { join, dirname } from 'node:path'; - -/** - * This function is used to resolve the absolute path of a package. - * It is needed in projects that use Yarn PnP or are set up within a monorepo. - */ -function getAbsolutePath(value: string): any { - return dirname(require.resolve(join(value, 'package.json'))); -} - -const config: StorybookConfig = { - stories: ['../src/stories/**/*.mdx', '../src/stories/**/*.stories.@(js|jsx|ts|tsx|svelte)'], - addons: [ - // Do not use getAbsolutePath - getAbsolutePath('@storybook/addon-links'), - '@storybook/addon-svelte-csf', - getAbsolutePath('@storybook/addon-docs'), - ], - typescript: { - check: true, - }, - framework: { - name: getAbsolutePath('@storybook/svelte-vite'), - options: {}, - }, - docs: {}, -}; - -export default config; diff --git a/storybook/.storybook/preview.ts b/storybook/.storybook/preview.ts deleted file mode 100644 index 516f92005a..0000000000 --- a/storybook/.storybook/preview.ts +++ /dev/null @@ -1,87 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { Preview } from '@storybook/svelte-vite'; -import { createElement } from 'react'; -import { themes } from 'storybook/theming'; -import { DocsContainer } from '@storybook/addon-docs/blocks'; -import './main.css'; -import './themes.css'; - -let theme: 'dark' | 'light' = 'light'; - -// Theme logic here (runs in iframe context) -const applyTheme = () => { - try { - const isEmbedded = window.parent !== window; - let parentTheme: string | undefined; - if (isEmbedded) { - parentTheme = window.parent.document.documentElement.dataset.theme; - if (parentTheme === 'dark' || parentTheme === 'light') { - theme = parentTheme; - } - } - if (!parentTheme) { - const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - theme = isDark ? 'dark' : 'light'; - } - document.body.classList.remove('dark', 'light'); - document.body.classList.add(theme); - } catch (err) { - console.warn('Theme detection failed:', err); - } -}; -applyTheme(); - -// Watch for changes if embedded -if (window.parent !== window) { - try { - const observer = new MutationObserver(applyTheme); - observer.observe(window.parent.document.documentElement, { - attributes: true, - attributeFilter: ['data-theme'], - }); - - // Disconnect on iframe unload - window.parent.addEventListener('unload', () => { - observer.disconnect(); - }); - } catch (err) { - console.warn('Could not observe parent for theme changes:', err); - } -} - -const preview: Preview = { - parameters: { - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/i, - }, - }, - docs: { - container: props => { - const currentProps = { ...props }; - currentProps.theme = theme === 'light' ? themes.light : themes.dark; - return createElement(DocsContainer, currentProps); - }, - }, - }, -}; - -export default preview; diff --git a/storybook/package.json b/storybook/package.json deleted file mode 100644 index b9cd472fd9..0000000000 --- a/storybook/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "storybook", - "version": "0.0.1", - "type": "module", - "license": "Apache-2.0", - "scripts": { - "dev": "cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook dev -p 6006", - "build": "cross-env STORYBOOK_DISABLE_TELEMETRY=1 storybook build" - }, - "peerDependencies": { - "svelte": "^5.0.0", - "svelte-fa": "^4.0.0" - }, - "dependencies": { - "@fortawesome/fontawesome-free": "^7.0.0", - "@fortawesome/free-brands-svg-icons": "^7.0.0", - "@fortawesome/free-regular-svg-icons": "^7.0.0", - "@fortawesome/free-solid-svg-icons": "^7.0.0", - "@podman-desktop/ui-svelte": "workspace:*", - "eslint": "^9.32.0", - "svelte-fa": "^4.0.4" - }, - "devDependencies": { - "@storybook/addon-docs": "^9.1.0", - "@storybook/addon-links": "^9.1.0", - "@storybook/addon-svelte-csf": "5.0.4", - "@storybook/svelte-vite": "^9.1.0", - "@sveltejs/package": "^2.4.0", - "@sveltejs/vite-plugin-svelte": "6.1.0", - "@tailwindcss/vite": "^4.1.11", - "@tsconfig/svelte": "^5.0.2", - "@typescript-eslint/eslint-plugin": "^8.38.0", - "autoprefixer": "^10.4.21", - "cross-env": "10.0.0", - "eslint-plugin-storybook": "9.1.0", - "eslint-plugin-svelte": "^3.11.0", - "postcss": "^8.5.6", - "postcss-load-config": "^6.0.1", - "react": "18.2.0", - "react-dom": "18.3.1", - "storybook": "^9.0.14", - "svelte": "5.37.3", - "svelte-check": "^4.3.0", - "tailwindcss": "^4.1.11", - "typescript": "^5.8.3", - "vite": "^7.0.6", - "vitest": "^3.2.4" - } -} diff --git a/storybook/src/stories/Checkbox.stories.svelte b/storybook/src/stories/Checkbox.stories.svelte deleted file mode 100644 index 1b042fdb67..0000000000 --- a/storybook/src/stories/Checkbox.stories.svelte +++ /dev/null @@ -1,114 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - {args.content} -{/snippet} - - - - - - - - - - - - diff --git a/storybook/src/stories/Dropdown.stories.svelte b/storybook/src/stories/Dropdown.stories.svelte deleted file mode 100644 index e1081af2d3..0000000000 --- a/storybook/src/stories/Dropdown.stories.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} -
    - - - - - -
    -{/snippet} - - - - - - - - - - {#snippet left()} - Selected value:  - {/snippet} - - diff --git a/storybook/src/stories/DropdownMenu.stories.svelte b/storybook/src/stories/DropdownMenu.stories.svelte deleted file mode 100644 index 9ab8904c85..0000000000 --- a/storybook/src/stories/DropdownMenu.stories.svelte +++ /dev/null @@ -1,37 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} -
    - - {#each args.items as item, index (index)} - - {/each} - -
    -{/snippet} - - diff --git a/storybook/src/stories/ErrorMessage.stories.svelte b/storybook/src/stories/ErrorMessage.stories.svelte deleted file mode 100644 index 0c5db8719b..0000000000 --- a/storybook/src/stories/ErrorMessage.stories.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - - - - - diff --git a/storybook/src/stories/Expandable.stories.svelte b/storybook/src/stories/Expandable.stories.svelte deleted file mode 100644 index aec3e78973..0000000000 --- a/storybook/src/stories/Expandable.stories.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - -{#snippet template({ ...args }: Args, _context: StoryContext)} - {args.children} -{/snippet} - - - - - {#snippet title()} - Title - {/snippet} - Children - - diff --git a/storybook/src/stories/Link.stories.svelte b/storybook/src/stories/Link.stories.svelte deleted file mode 100644 index ded96030f1..0000000000 --- a/storybook/src/stories/Link.stories.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - {args.content} -{/snippet} - - - - diff --git a/storybook/src/stories/StatusIcon.stories.svelte b/storybook/src/stories/StatusIcon.stories.svelte deleted file mode 100644 index fa3e609eaa..0000000000 --- a/storybook/src/stories/StatusIcon.stories.svelte +++ /dev/null @@ -1,69 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - {args.status ? `Status for ${args.status}` : ''} -{/snippet} - - - - - - - - - - - - diff --git a/storybook/src/stories/Tab.stories.svelte b/storybook/src/stories/Tab.stories.svelte deleted file mode 100644 index 96bbbec0e1..0000000000 --- a/storybook/src/stories/Tab.stories.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - -{#snippet template({ ...args }: Args, _context: StoryContext)} - -{/snippet} - - - - diff --git a/storybook/src/stories/Table.stories.svelte b/storybook/src/stories/Table.stories.svelte deleted file mode 100644 index 02f943582d..0000000000 --- a/storybook/src/stories/Table.stories.svelte +++ /dev/null @@ -1,83 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} -
    -{/snippet} - - - - diff --git a/storybook/src/stories/Tooltip.stories.svelte b/storybook/src/stories/Tooltip.stories.svelte deleted file mode 100644 index e7129cdb8e..0000000000 --- a/storybook/src/stories/Tooltip.stories.svelte +++ /dev/null @@ -1,135 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} -
    - Move mouse over the star icon to see the tooltip - - - -
    -{/snippet} - - - - - - - - - - - - - - - - - - diff --git a/storybook/src/stories/button/Button.stories.svelte b/storybook/src/stories/button/Button.stories.svelte deleted file mode 100644 index 90f3c36c02..0000000000 --- a/storybook/src/stories/button/Button.stories.svelte +++ /dev/null @@ -1,72 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - - - - - - - - - - - diff --git a/storybook/src/stories/button/CloseButton.stories.svelte b/storybook/src/stories/button/CloseButton.stories.svelte deleted file mode 100644 index 163eafb3c2..0000000000 --- a/storybook/src/stories/button/CloseButton.stories.svelte +++ /dev/null @@ -1,30 +0,0 @@ - - -{#snippet template({ _children }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/src/stories/icon/ContainerIcon.stories.svelte b/storybook/src/stories/icon/ContainerIcon.stories.svelte deleted file mode 100644 index 89562dd637..0000000000 --- a/storybook/src/stories/icon/ContainerIcon.stories.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/src/stories/icon/StarIcon.stories.svelte b/storybook/src/stories/icon/StarIcon.stories.svelte deleted file mode 100644 index e2a2ee26eb..0000000000 --- a/storybook/src/stories/icon/StarIcon.stories.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/src/stories/input/Input.stories.svelte b/storybook/src/stories/input/Input.stories.svelte deleted file mode 100644 index 94ba84c944..0000000000 --- a/storybook/src/stories/input/Input.stories.svelte +++ /dev/null @@ -1,98 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - {args.content} -{/snippet} - - - - - - - - - - - - diff --git a/storybook/src/stories/progress/LinearProgress.stories.svelte b/storybook/src/stories/progress/LinearProgress.stories.svelte deleted file mode 100644 index 3b8d390db8..0000000000 --- a/storybook/src/stories/progress/LinearProgress.stories.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/src/stories/progress/Spinner.stories.svelte b/storybook/src/stories/progress/Spinner.stories.svelte deleted file mode 100644 index 90908e24e5..0000000000 --- a/storybook/src/stories/progress/Spinner.stories.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/src/stories/screen/EmptyScreen.stories.svelte b/storybook/src/stories/screen/EmptyScreen.stories.svelte deleted file mode 100644 index 856e0528e6..0000000000 --- a/storybook/src/stories/screen/EmptyScreen.stories.svelte +++ /dev/null @@ -1,84 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - - - - - - - diff --git a/storybook/src/stories/screen/FilteredEmptyScreen.stories.svelte b/storybook/src/stories/screen/FilteredEmptyScreen.stories.svelte deleted file mode 100644 index 9f2e774de7..0000000000 --- a/storybook/src/stories/screen/FilteredEmptyScreen.stories.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - -{#snippet template({ _children, ...args }: Args, _context: StoryContext)} - -{/snippet} - - diff --git a/storybook/tailwind.config.js b/storybook/tailwind.config.js deleted file mode 100644 index 8b47a47a0f..0000000000 --- a/storybook/tailwind.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import config from '../tailwind.config.cjs'; - -/** @type {import('tailwindcss').Config} */ -module.exports = { - ...config, - content: ['./src/**/*.{svelte,ts,css}', '../packages/ui/**/*.{svelte,ts,css}'], -}; diff --git a/storybook/tsconfig.json b/storybook/tsconfig.json deleted file mode 100644 index 43af456d64..0000000000 --- a/storybook/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "@tsconfig/svelte/tsconfig.json", - "compilerOptions": { - "target": "esnext", - "module": "esnext", - "strict": true, - "resolveJsonModule": true, - "preserveValueImports": false, - "baseUrl": ".", - "paths": { - "/@/*": ["./src/*"] - }, - /** - * Typecheck JS in `.svelte` and `.js` files by default. - * Disable checkJs if you'd like to use dynamic types in JS. - * Note that setting allowJs false does not prevent the use - * of JS in `.svelte` files. - */ - "allowJs": true, - "checkJs": true - }, - "include": ["src/**/*.d.ts", ".storybook", "src/**/*.ts", "src/**/*.svelte"] -} diff --git a/storybook/vite.config.js b/storybook/vite.config.js deleted file mode 100644 index dbc811370c..0000000000 --- a/storybook/vite.config.js +++ /dev/null @@ -1,69 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 - ***********************************************************************/ - -/* eslint-env node */ -import { join } from 'path'; -import * as path from 'path'; -import { svelte } from '@sveltejs/vite-plugin-svelte'; -import { svelteTesting } from '@testing-library/svelte/vite'; -import tailwindcss from '@tailwindcss/vite'; - -import { defineConfig } from 'vite'; -import { fileURLToPath } from 'url'; - -let filename = fileURLToPath(import.meta.url); -const PACKAGE_ROOT = path.dirname(filename); - -// https://vitejs.dev/config/ -export default defineConfig({ - mode: process.env.MODE, - root: PACKAGE_ROOT, - resolve: { - alias: { - '/@/': join(PACKAGE_ROOT, 'src') + '/', - }, - }, - plugins: [tailwindcss(), svelte(), svelteTesting()], - test: { - include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - globals: true, - environment: 'jsdom', - alias: [{ find: '@testing-library/svelte', replacement: '@testing-library/svelte/svelte5' }], - deps: { - inline: ['moment'], - }, - }, - base: '', - server: { - fs: { - strict: true, - }, - }, - build: { - sourcemap: true, - outDir: 'dist', - assetsDir: '.', - lib: { - entry: 'src/lib/index.ts', - formats: ['es'], - }, - - emptyOutDir: true, - reportCompressedSize: false, - }, -}); diff --git a/tailwind-color-palette.json b/tailwind-color-palette.json index bd11da422f..cf68bfebe6 100644 --- a/tailwind-color-palette.json +++ b/tailwind-color-palette.json @@ -26,16 +26,16 @@ "900": "#0f0f11" }, "dustypurple": { - "50": "#f2f2fb", - "100": "#e7e8f8", - "200": "#d3d3f2", - "300": "#b9b8e9", - "400": "#a09adf", - "500": "#8f81d3", - "600": "#8772c7", - "700": "#6d57ab", - "800": "#59498a", - "900": "#4a406f" + "50": "#f2f7fb", + "100": "#e4eff8", + "200": "#ccdff0", + "300": "#b1cce4", + "400": "#97b6d7", + "500": "#84a2c9", + "600": "#7893bc", + "700": "#5f779c", + "800": "#4c5f7c", + "900": "#424e65" }, "fuschia": { "50": "#fdf2ff", @@ -75,16 +75,16 @@ "900": "#193b20" }, "purple": { - "50": "#f7f3ff", - "100": "#efe9fe", - "200": "#e2d6fe", - "300": "#bfa7f6", - "400": "#ad8bfa", - "500": "#8b5cf6", - "600": "#6d48bf", - "700": "#6234b1", - "800": "#4d2d87", - "900": "#37255d" + "50": "#f3f8ff", + "100": "#e0f0ff", + "200": "#b8dafe", + "300": "#8ac2fd", + "400": "#5da9fb", + "500": "#318df8", + "600": "#1f6fd1", + "700": "#1957a3", + "800": "#134179", + "900": "#0d2c52" }, "red": { "50": "#fff4f1", diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 3996afda1c..b3933d80e7 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -24,6 +24,7 @@ module.exports = { 'packages/renderer/index.html', 'packages/renderer/src/**/*.{svelte,ts,css}', 'packages/ui/src/**/*.{svelte,ts,css}', + 'node_modules/@podman-desktop/ui-svelte/dist/**/*.{svelte,ts,css}', ], darkMode: 'class', theme: { @@ -56,6 +57,17 @@ module.exports = { 700: tailwindColors.violet[700], }, }, + extend: { + keyframes: { + fadeIn: { + '0%': { opacity: 0, transform: 'translateY(10px)' }, + '100%': { opacity: 1, transform: 'translateY(0)' }, + }, + }, + animation: { + 'fade-in': 'fadeIn 2s ease-out forwards', + }, + }, }, plugins: [], }; diff --git a/tests/.gitignore b/tests/.gitignore deleted file mode 100644 index 793c39798d..0000000000 --- a/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -output/ -*.tgz \ No newline at end of file diff --git a/tests/playwright/README.md b/tests/playwright/README.md deleted file mode 100644 index 7ead4d776c..0000000000 --- a/tests/playwright/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Podman Desktop Playwright Tests - -This document contains information on how to use the E2E testing framework of Podman Desktop on different scenarios. -This is particularly useful if you want to execute the tests from a Podman Desktop extension or you want to develop your own. - -Prerequisites: - -- Have Node.js 22 installed (ideally using `nvm`) -- Uses pnpm v10, can be installed via `npm install -g pnpm@10` or you can get it [here](https://pnpm.io/installation) -- Have a clone of the Podman Desktop repo (you can get it [here](https://github.com/containers/podman-desktop/tree/main)) - -## Core functionality how the E2E tests works - -Podman Desktop E2E tests use the Playwright testing framework libraries to develop the tests and POM (page object model) for the Podman Desktop application. The test runner also relies on playwright code. - -At this moment the electron support is marked as [experimental](https://playwright.dev/docs/api/class-electron). - -In order to be able to reuse and share the core e2e tests libraries and definitions we created `@podman-desktop/tests-playwright` [npm package](https://www.npmjs.com/package/@podman-desktop/tests-playwright). - -The project uses `devDependency` to `@playwright/test` for access to playwright function, assertions, etc., and also to an `electron` package, which brings the ability to run Podman Desktop application (which is electron based) when running e2e tests. - -### Running E2E tests in development or production mode - -We distinguish between two operational modes to run the tests with: development and production. In both cases, we need to have access to Electron binary which is used to [launch the Podman Desktop application](https://playwright.dev/docs/api/class-electron#electron-launch). Above functionality is handled by `@podman-desktop/tests-playwright` `Runner` class (`podman-desktop-runner.ts`). The runner is then shipped as a Playwright fixture and takes care of all the setup for E2E tests. - -Multiple scenarios may occur: - -1. Running E2E tests from `podman-desktop` repository in DEVELOPMENT mode: We do not need to set any paths to electron binary, `Runner` uses default configuration and grabs electron from `/node_modules/.bin/electron` which is installed via electron dependency postinstall script during `pnpm install`. -2. Running E2 tests from `podman-desktop` repository in PRODUCTION mode: We need to set `PODMAN_DESKTOP_BINARY` environment variable when running the tests from `podman-desktop` repository with the previously built or downloaded Podman Desktop electron app. Simply point to a `podman-desktop` binary or executable. -3. Running E2E tests in external repository in DEVELOPMENT mode: Set `PODMAN_DESKTOP_ARGS` and point to the previously built `podman-desktop` repository. We are using electron binary from `podman-desktop` repository the same way as in point 1. -4. Running E2E tests in external repository in PRODUCTION mode: Set `PODMAN_DESKTOP_BINARY` environment variable and point to a downloaded/built Podman Desktop application which is also main electron binary that is used. - -This approach is beneficial in the way that we do not need to have the `electron` package set as a dependency in external repositories. - -If you set `PODMAN_DESKTOP_BINARY` and `PODMAN_DESKTOP_ARGS` at the same time, the runner will throw an error since you cannot really run tests in both mode at the same time. - -You can get testing-enabled Podman Desktop binaries and installation files from [testing-prereleases repository](https://github.com/podman-desktop/testing-prereleases). - -## Usage of @podman-desktop/tests-playwright to develop the podman-desktop tests in the Podman Desktop repository - -This section explains how to develop your own E2E tests in the Podman Desktop repository. - -Steps: - -1. Get into the Podman Desktop repo folder (should be named `podman-desktop`) -2. Install its local dependencies by executing `pnpm install` -3. Build the application's tests with `pnpm test:e2e:build` -4. Now you can implement your E2E tests in the `tests/playwright` folder -5. Execute `pnpm test:e2e` in order to run them -6. When the tests finish you will find the test artifacts and logs available under `./tests/playwright/output` (this directory can be modified in the `playwright.config.ts` file) - -## Usage of @podman-desktop/tests-playwright in an external repository in DEVELOPMENT mode - -This section explains how to add the npm package to a repository external to podman-desktop. - -Steps: - -1. Get into the Podman Desktop repo folder (should be named `podman-desktop`) -2. Install its local dependencies by executing `pnpm install` -3. Implement your changes to the E2E tests library (optional) -4. Build the application and tests with `pnpm test:e2e:build` -5. Set an environment variable by executing: `export PODMAN_DESKTOP_ARGS="/path/to/podman-desktop"` (the current directory) [¹] -6. Get into YOUR repository and update the `package.json` file to have the following contents: - -- Under `devDependencies`: - - `"@podman-desktop/tests-playwright": "next",`[²] - - `"@playwright/test": "^1.48.1"` (or the current latest) -- Under `scripts`: - - `"test:e2e:setup": "xvfb-maybe --auto-servernum --server-args='-screen 0 1280x960x24' --"` [³] - - `"test:e2e": "cross-env npm run test:e2e:setup npx playwright test tests/src"` - -7. Execute `pnpm install`, which should extract the contents of the previously built Podman Desktop into the `node_modules` folder in your repository -8. Write your E2E tests on your repository, which may use your changes to `@podman-desktop/tests-playwright` from step 3 (optional) -9. Run your E2E tests by executing `pnpm test:e2e` - -### Running in e2e tests in PRODUCTION mode - -You can skip steps 1 through 4 and set `PODMAN_DESKTOP_BINARY` if you have compiled it or downloaded before and want to run in PRODUCTION mode. - -[¹] Remember that environment variables defined this way only work on the terminal they were defined and only for as long as the terminal is active. - -Using the value `next` works for running tests locally, but for remote executions, specify the latest version of the `@podman-desktop/tests-playwright` package. Check the “Versions” tab here to find the latest version. This version will be written into the `pnpm-lock.yaml` file; to ensure you use the latest version in the future, force an update with `pnpm add -D @podman-desktop/tests-playwright@next` (use the `-w` flag in monorepos to install at the workspace root). - -[³] If your project does not already have the `xvfb-maybe` dependency, you'll need to add it as well. - -## How to develop and test @podman-desktop/tests-playwright locally - -This section references how to use @podman-desktop/tests-playwright generated archive file for local development - -1. Get into the Podman Desktop repo folder (should be named `podman-desktop`) -2. Get into the `tests/playwright` folder and install its local dependencies by executing `pnpm install` -3. Implement your changes to the E2E tests library (it can be found under `tests/playwright`) -4. Create the local package with `pnpm run package` which also builds the code. This will produce a `.tgz`archive -5. Set the environment variable by executing: `export PODMAN_DESKTOP_ARGS="path/to/podman-desktop"` (not the current directory, two folders up) -6. In YOUR repository, update the `package.json` file to have the following contents under `devDependencies`: - `"@podman-desktop/tests-playwright": "file:../podman-desktop/tests/playwright/podman-desktop-1.20.0-next.tgz"` -7. Execute `pnpm install`, which should extract the contents of the previously built Podman Desktop package into the `node_modules` folder in your repository -8. Now, the changes you made to `@podman-desktop/tests-playwright` should be available in your project. If the process was successful, you should be able to find the classes you added in the `index.d.ts` file under `node_modules/@podman-desktop/tests-playwright/dist` -9. You might need to remove `node_modules` to force new installation diff --git a/tests/playwright/package.json b/tests/playwright/package.json index c05555f207..c65f8ea02d 100644 --- a/tests/playwright/package.json +++ b/tests/playwright/package.json @@ -1,31 +1,20 @@ { - "name": "@podman-desktop/tests-playwright", - "version": "1.9.0-next", - "description": "Playwright-based testing libraries for Podman Desktop and its extensions", + "name": "@kortex/tests-playwright", + "version": "0.0.1-next", "type": "module", - "repository": "https://github.com/podman-desktop/podman-desktop", - "publishConfig": { - "provenance": true, - "access": "public" - }, - "main": "dist/index.js", - "files": [ - "dist", - "!dist/specs/*", - "!dist/setupFiles/extended-hooks.*" - ], - "license": "Apache-2.0", + "private": true, "scripts": { - "build": "vite build", - "package": "npm run build && npm link && npm pack", - "test": "echo \"Error: no test specified\" && exit 1", - "publish:next": "pnpm publish --registry=https://registry.npmjs.org/ --no-git-tag-version --new-version 0.0.1-\"$(date +%s)\"" + "build": "tsc", + "test": "playwright test", + "test:debug": "playwright test --debug", + "test:headed": "playwright test --headed", + "test:ui": "playwright test --ui", + "report": "playwright show-report ./output/tests", + "postinstall": "playwright install chromium" }, "devDependencies": { - "@playwright/test": "1.54.2", + "@playwright/test": "^1.54.2", "@types/node": "^22", - "electron": "37.2.5", - "typescript": "^5.8.3", - "vitest": "^3.2.4" + "typescript": "5.9.2" } } diff --git a/tests/playwright/playwright.config.ts b/tests/playwright/playwright.config.ts new file mode 100644 index 0000000000..67d36d779b --- /dev/null +++ b/tests/playwright/playwright.config.ts @@ -0,0 +1,84 @@ +/********************************************************************** + * 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 { PlaywrightTestConfig } from '@playwright/test'; + +import type { ResourceId } from './src/model/core/types'; + +const config: PlaywrightTestConfig & { + projects?: Array<{ + use?: { resource?: ResourceId }; + [key: string]: unknown; + }>; +} = { + testDir: './src', + timeout: 180_000, + + workers: 1, + + reporter: [ + ['html', { outputFolder: './output/html-report' }], + ['json', { outputFile: './output/test-results.json' }], + ['junit', { outputFile: './output/junit-results.xml' }], + ['list'], + ], + + use: { + actionTimeout: 15_000, + screenshot: 'only-on-failure', + video: 'retain-on-failure', + trace: 'retain-on-failure', + }, + + preserveOutput: 'always', + + projects: [ + { + name: 'Kortex-App-Core', + testMatch: ['**/*.spec.ts'], + testIgnore: ['**/provider-specs/*.spec.ts'], + }, + { + name: 'Gemini-Provider', + testMatch: ['**/provider-specs/*.spec.ts'], + use: { + resource: 'gemini', + }, + testIgnore: process.env.GEMINI_API_KEY ? [] : ['**/*'], // Skip if GEMINI_API_KEY is not set + }, + { + name: 'OpenAI-Provider', + testMatch: ['**/provider-specs/*.spec.ts'], + use: { + resource: 'openai', + }, + testIgnore: process.env.OPENAI_API_KEY ? [] : ['**/*'], // Skip if OPENAI_API_KEY is not set + }, + { + name: 'OpenShift-AI-Provider', + testMatch: ['**/provider-specs/*.spec.ts'], + use: { + resource: 'openshift-ai', + }, + testIgnore: ['**/*'], // Disabled until OpenShift AI resource creation is implemented + }, + ], + + outputDir: './output/test-results', +}; + +export default config; diff --git a/tests/playwright/resources/alphine-hello/alphine-hello.containerfile b/tests/playwright/resources/alphine-hello/alphine-hello.containerfile deleted file mode 100644 index f4f607e717..0000000000 --- a/tests/playwright/resources/alphine-hello/alphine-hello.containerfile +++ /dev/null @@ -1,24 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -FROM ghcr.io/linuxcontainers/alpine -RUN echo $PATH -COPY echo_and_run.sh /usr/bin/ -RUN ls -al /usr/bin/echo_and_run.sh -ENTRYPOINT ["echo_and_run.sh"] - \ No newline at end of file diff --git a/tests/playwright/resources/alphine-hello/echo_and_run.sh b/tests/playwright/resources/alphine-hello/echo_and_run.sh deleted file mode 100644 index afd901672a..0000000000 --- a/tests/playwright/resources/alphine-hello/echo_and_run.sh +++ /dev/null @@ -1,22 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -#!/bin/sh -echo "Hello There!" -/bin/sh - \ No newline at end of file diff --git a/tests/playwright/resources/compose.yaml b/tests/playwright/resources/compose.yaml deleted file mode 100644 index 5e12465ba5..0000000000 --- a/tests/playwright/resources/compose.yaml +++ /dev/null @@ -1,11 +0,0 @@ -services: - backend: - image: quay.io/podman-desktop-demo/podify-demo-backend:v1 - ports: - - '6379:6379' - frontend: - image: quay.io/podman-desktop-demo/podify-demo-frontend:v1 - ports: - - '5001:5001' - depends_on: - - backend diff --git a/tests/playwright/resources/kube/foobar/Containerfile b/tests/playwright/resources/kube/foobar/Containerfile deleted file mode 100644 index 7dd7fddc60..0000000000 --- a/tests/playwright/resources/kube/foobar/Containerfile +++ /dev/null @@ -1 +0,0 @@ -FROM docker.io/library/nginx:alpine diff --git a/tests/playwright/resources/kube/play-yaml-build-test.yaml b/tests/playwright/resources/kube/play-yaml-build-test.yaml deleted file mode 100644 index c2bb77f18b..0000000000 --- a/tests/playwright/resources/kube/play-yaml-build-test.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: play-yaml-build-test -spec: - containers: - - name: container - image: foobar - imagePullPolicy: Never - ports: - - name: http - containerPort: 80 diff --git a/tests/playwright/resources/kubernetes/test-configmap-resource.yaml b/tests/playwright/resources/kubernetes/test-configmap-resource.yaml deleted file mode 100644 index 341da6a1b1..0000000000 --- a/tests/playwright/resources/kubernetes/test-configmap-resource.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: test-configmap-resource -data: - configmap-username: test-configmap - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-cronjob-resource.yaml b/tests/playwright/resources/kubernetes/test-cronjob-resource.yaml deleted file mode 100644 index 4207b28e73..0000000000 --- a/tests/playwright/resources/kubernetes/test-cronjob-resource.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -apiVersion: batch/v1 -kind: CronJob -metadata: - name: test-cronjob-resource -spec: - schedule: "*/1 * * * *" - successfulJobsHistoryLimit: 1 - jobTemplate: - spec: - template: - spec: - containers: - - name: test-cronjob-container - image: busybox - command: - - /bin/sh - - -c - - "echo Hello from CronJob!" - restartPolicy: OnFailure - diff --git a/tests/playwright/resources/kubernetes/test-deployment-resource.yaml b/tests/playwright/resources/kubernetes/test-deployment-resource.yaml deleted file mode 100644 index 23d3442e56..0000000000 --- a/tests/playwright/resources/kubernetes/test-deployment-resource.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: test-deployment-resource - labels: - app: test-deployment-resource -spec: - replicas: 3 - selector: - matchLabels: - app: test-deployment-resource - template: - metadata: - labels: - app: test-deployment-resource - spec: - containers: - - name: test-deployment-resource - image: ghcr.io/podmandesktop-ci/nginx - ports: - - containerPort: 80 - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-image-push.yaml b/tests/playwright/resources/kubernetes/test-image-push.yaml deleted file mode 100644 index 7e81c10e2c..0000000000 --- a/tests/playwright/resources/kubernetes/test-image-push.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -apiVersion: v1 -kind: Pod -metadata: - name: test-image-push-pod -spec: - containers: - - name: test-image-push - image: ghcr.io/linuxcontainers/alpine - imagePullPolicy: Never - command: ["sleep", "3600"] - diff --git a/tests/playwright/resources/kubernetes/test-ingress-resource.yaml b/tests/playwright/resources/kubernetes/test-ingress-resource.yaml deleted file mode 100644 index 80f7ba6e65..0000000000 --- a/tests/playwright/resources/kubernetes/test-ingress-resource.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: test-ingress-resource -spec: - rules: - - http: - paths: - - pathType: Prefix - path: / - backend: - service: - name: test-service-resource - port: - number: 8080 - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-kind-config-file.yaml b/tests/playwright/resources/kubernetes/test-kind-config-file.yaml deleted file mode 100644 index ca96b57e8f..0000000000 --- a/tests/playwright/resources/kubernetes/test-kind-config-file.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -kind: Cluster -apiVersion: kind.x-k8s.io/v1alpha4 -name: test-cluster diff --git a/tests/playwright/resources/kubernetes/test-pod-configmaps-secrets.yaml b/tests/playwright/resources/kubernetes/test-pod-configmaps-secrets.yaml deleted file mode 100644 index de3cef45fb..0000000000 --- a/tests/playwright/resources/kubernetes/test-pod-configmaps-secrets.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: test-pod-configmaps-secrets -spec: - containers: - - name: test-app-configmap-secret - image: quay.io/podman-desktop-demo/podify-demo-backend:v1 - envFrom: - - configMapRef: - name: test-configmap-resource - - secretRef: - name: test-secret-resource - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-pod-pvcs.yaml b/tests/playwright/resources/kubernetes/test-pod-pvcs.yaml deleted file mode 100644 index 811606582d..0000000000 --- a/tests/playwright/resources/kubernetes/test-pod-pvcs.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - name: test-pod-pvcs -spec: - containers: - - name: test-app-pvc - image: quay.io/podman-desktop-demo/podify-demo-backend:v1 - volumeMounts: - - name: config-volume - mountPath: /etc/config - volumes: - - name: config-volume - persistentVolumeClaim: - claimName: test-pvc-resource - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-pvc-resource.yaml b/tests/playwright/resources/kubernetes/test-pvc-resource.yaml deleted file mode 100644 index bc23cdda7b..0000000000 --- a/tests/playwright/resources/kubernetes/test-pvc-resource.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: test-pvc-resource -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 1Gi - storageClassName: standard - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-secret-resource.yaml b/tests/playwright/resources/kubernetes/test-secret-resource.yaml deleted file mode 100644 index c057a58b22..0000000000 --- a/tests/playwright/resources/kubernetes/test-secret-resource.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: test-secret-resource -type: Opaque -stringData: - secret-username: "test-secret" - \ No newline at end of file diff --git a/tests/playwright/resources/kubernetes/test-service-resource.yaml b/tests/playwright/resources/kubernetes/test-service-resource.yaml deleted file mode 100644 index a8deb81c88..0000000000 --- a/tests/playwright/resources/kubernetes/test-service-resource.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# /********************************************************************** -# * 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 -# ***********************************************************************/ - -apiVersion: v1 -kind: Service -metadata: - name: test-service-resource -spec: - type: ClusterIP - selector: - app: test-deployment-resource - ports: - - port: 8080 - targetPort: 80 - \ No newline at end of file diff --git a/tests/playwright/resources/primary-podify-demo.yaml b/tests/playwright/resources/primary-podify-demo.yaml deleted file mode 100644 index f146388b6e..0000000000 --- a/tests/playwright/resources/primary-podify-demo.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Save the output of this file and use kubectl create -f to import -# it into Kubernetes. -# -# Created with podman-4.8.1 -apiVersion: v1 -kind: Pod -metadata: - creationTimestamp: "2023-12-12T12:45:23Z" - labels: - app: podify-demo-pod - name: podify-demo-pod -spec: - containers: - - env: - - name: HOSTNAME - value: d6ee99a0905d - - name: TERM - value: xterm - - name: HOME - value: /home/redis - image: quay.io/podman-desktop-demo/podify-demo-backend:v1 - resources: - limits: - cpu: 1 - memory: 500Mi - automountServiceAccountToken: false - name: backend-podified - ports: - - containerPort: 5007 - hostPort: 5007 - securityContext: - runAsNonRoot: true - - env: - - name: TERM - value: xterm - - name: HOME - value: /home/frontend - - name: HOSTNAME - value: 4daf7345fc17 - image: quay.io/podman-desktop-demo/podify-demo-frontend:v1 - resources: - limits: - cpu: 1 - memory: 500Mi - automountServiceAccountToken: false - name: frontend-podified - securityContext: - runAsNonRoot: true \ No newline at end of file diff --git a/tests/playwright/resources/test-containerfile b/tests/playwright/resources/test-containerfile deleted file mode 100644 index 2e6743ea2b..0000000000 --- a/tests/playwright/resources/test-containerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM ghcr.io/linuxcontainers/alpine -CMD ["/bin/sh", "-c", "echo 'Image built!'"] \ No newline at end of file diff --git a/tests/playwright/resources/test-kube-config b/tests/playwright/resources/test-kube-config deleted file mode 100644 index 47a534c727..0000000000 --- a/tests/playwright/resources/test-kube-config +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (C) 2024 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 - -apiVersion: v1 -kind: Config -clusters: - - name: context-1-cluster - cluster: - server: context-1-server - insecure-skip-tls-verify: false - - name: context-2-cluster - cluster: - server: context-2-server - insecure-skip-tls-verify: false - - name: context-3-cluster - cluster: - server: context-3-server - insecure-skip-tls-verify: false -users: - - name: context-1-user - user: - token: sha256~abcdefg - - name: context-2-user - user: - token: sha256~abcdefg - - name: context-3-user - user: - token: sha256~abcdefg -contexts: - - name: context-1 - context: - cluster: context-1-cluster - name: context-1 - user: context-1-user - namespace: context-1-namespace - - name: context-2 - context: - cluster: context-2-cluster - name: context-2 - user: context-2-user - - name: context-3 - context: - cluster: context-3-cluster - name: context-3 - user: context-3-user -preferences: {} -current-context: context-1 - diff --git a/tests/playwright/src/fixtures/electron-app.ts b/tests/playwright/src/fixtures/electron-app.ts new file mode 100644 index 0000000000..93162a1876 --- /dev/null +++ b/tests/playwright/src/fixtures/electron-app.ts @@ -0,0 +1,244 @@ +/********************************************************************** + * 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 + ***********************************************************************/ + +/** biome-ignore-all lint/correctness/noEmptyPattern: Playwright fixture pattern requires empty object when no dependencies are needed */ +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { _electron as electron, type ElectronApplication, type Page, test as base } from '@playwright/test'; +import { TIMEOUTS } from 'src/model/core/types'; +import { NavigationBar } from 'src/model/navigation/navigation'; +import { ChatPage } from 'src/model/pages/chat-page'; +import { ExtensionsPage } from 'src/model/pages/extensions-page'; +import { FlowsPage } from 'src/model/pages/flows-page'; +import { McpPage } from 'src/model/pages/mcp-page'; +import { SettingsPage } from 'src/model/pages/settings-page'; + +import { waitForAppReady } from '../utils/app-ready'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const DEVTOOLS_URL_PREFIX = 'devtools://'; + +export interface ElectronFixtures { + electronApp: ElectronApplication; + page: Page; + navigationBar: NavigationBar; + settingsPage: SettingsPage; + flowsPage: FlowsPage; + mcpPage: McpPage; + extensionsPage: ExtensionsPage; + chatPage: ChatPage; +} + +export const test = base.extend({ + // eslint-disable-next-line no-empty-pattern + electronApp: async ({}, use): Promise => { + let electronApp: ElectronApplication | undefined; + + try { + electronApp = await launchElectronApp(); + await use(electronApp); + } finally { + if (electronApp) { + try { + await closeAllWindows(electronApp); + await electronApp.close(); + } catch (error) { + console.error('Error closing Electron app:', error); + try { + await electronApp.close(); + } catch { + // Ignore errors during forced close + } + } + } + } + }, + + page: async ({ electronApp }, use): Promise => { + const page = await getFirstPage(electronApp); + await use(page); + }, + + navigationBar: async ({ page }, use): Promise => { + const navigationBar = new NavigationBar(page); + await use(navigationBar); + }, + + settingsPage: async ({ page }, use): Promise => { + const settingsPage = new SettingsPage(page); + await use(settingsPage); + }, + + flowsPage: async ({ page }, use): Promise => { + const flowsPage = new FlowsPage(page); + await use(flowsPage); + }, + + mcpPage: async ({ page }, use): Promise => { + const mcpPage = new McpPage(page); + await use(mcpPage); + }, + + extensionsPage: async ({ page }, use): Promise => { + const extensionsPage = new ExtensionsPage(page); + await use(extensionsPage); + }, + + chatPage: async ({ page }, use): Promise => { + const chatPage = new ChatPage(page); + await use(chatPage); + }, +}); + +function isDevToolsWindow(url: string): boolean { + return url.startsWith(DEVTOOLS_URL_PREFIX); +} + +function filterNonDevToolsWindows(windows: Page[]): Page[] { + return windows.filter(w => !isDevToolsWindow(w.url())); +} + +export async function getDevModeWindow( + electronApp: ElectronApplication, + retries = TIMEOUTS.MAX_RETRIES, +): Promise { + let lastError: Error | unknown; + + for (let attempt = 0; attempt <= retries; attempt++) { + try { + const existingWindows = filterNonDevToolsWindows(electronApp.windows()); + if (existingWindows.length > 0) { + return existingWindows[0]; + } + + return await electronApp.waitForEvent('window', { + timeout: TIMEOUTS.NON_DEVTOOLS_WINDOW, + predicate: page => !isDevToolsWindow(page.url()), + }); + } catch (error) { + lastError = error; + if (attempt < retries) { + const delay = TIMEOUTS.RETRY_DELAY * (attempt + 1); + console.warn( + `Failed to get dev window (attempt ${attempt + 1}/${retries + 1}), retrying in ${delay}ms:`, + error, + ); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } + } + + console.error('Failed to get dev window after retries, falling back to firstWindow:', lastError); + const allWindows = electronApp.windows(); + if (allWindows.length > 0) { + return allWindows[0]; + } + + throw new Error(`Failed to get any window from Electron app: ${lastError}`); +} + +function prepareElectronEnv(): Record { + const electronEnv: Record = {}; + + for (const [key, value] of Object.entries(process.env)) { + if (value !== undefined && typeof value === 'string') { + electronEnv[key] = value; + } + } + // Remove Electron-specific variables that shouldn't be passed + delete electronEnv.ELECTRON_RUN_AS_NODE; + + return electronEnv; +} + +function createLaunchConfig(): Parameters[0] { + const isProductionMode = !!process.env.KORTEX_BINARY; + const electronEnv = prepareElectronEnv(); + + if (isProductionMode) { + const executablePath = process.env.KORTEX_BINARY; + if (!executablePath) { + throw new Error('KORTEX_BINARY environment variable is set but empty'); + } + + return { + executablePath, + args: ['--no-sandbox'], + env: electronEnv, + }; + } + + // Development mode + return { + args: ['.', '--no-sandbox'], + env: { + ...electronEnv, + ELECTRON_IS_DEV: '1', + }, + cwd: resolve(__dirname, '../../../..'), + }; +} + +export async function launchElectronApp(): Promise { + const launchConfig = createLaunchConfig(); + + try { + const electronApp = await electron.launch(launchConfig); + + return electronApp; + } catch (error) { + console.error('Failed to launch Electron app:', error); + throw new Error(`Failed to launch Electron app: ${error instanceof Error ? error.message : String(error)}`); + } +} + +export async function getFirstPage(electronApp: ElectronApplication): Promise { + const isProductionMode = !!process.env.KORTEX_BINARY; + let page: Page; + + try { + if (isProductionMode) { + page = await electronApp.firstWindow({ timeout: TIMEOUTS.DEFAULT }); + } else { + page = await getDevModeWindow(electronApp); + } + } catch (error) { + const allWindows = electronApp.windows(); + if (allWindows.length > 0) { + const nonDevToolsWindows = filterNonDevToolsWindows(allWindows); + page = nonDevToolsWindows.length > 0 ? nonDevToolsWindows[0] : allWindows[0]; + console.warn('Using fallback window selection after error:', error); + } else { + throw new Error(`Failed to get first page: ${error instanceof Error ? error.message : String(error)}`); + } + } + + await page.waitForLoadState('load', { timeout: TIMEOUTS.PAGE_LOAD }); + await waitForAppReady(page); + + return page; +} + +export async function closeAllWindows(electronApp: ElectronApplication): Promise { + const windows = electronApp.windows(); + await Promise.allSettled(windows.map(window => window.close().catch(() => {}))); +} + +export { expect } from '@playwright/test'; diff --git a/tests/playwright/src/fixtures/provider-fixtures.ts b/tests/playwright/src/fixtures/provider-fixtures.ts new file mode 100644 index 0000000000..539e0177a1 --- /dev/null +++ b/tests/playwright/src/fixtures/provider-fixtures.ts @@ -0,0 +1,158 @@ +/********************************************************************** + * 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 + ***********************************************************************/ + +/** biome-ignore-all lint/correctness/noEmptyPattern: Playwright fixture pattern requires empty object when no dependencies are needed */ +import type { ElectronApplication, Page } from '@playwright/test'; + +import { MCP_SERVERS, type MCPServerId, PROVIDERS, type ResourceId } from '../model/core/types'; +import { NavigationBar } from '../model/navigation/navigation'; +import { type ElectronFixtures, getFirstPage, launchElectronApp, test as base } from './electron-app'; + +interface WorkerFixtures { + workerElectronApp: ElectronApplication; + workerPage: Page; + workerNavigationBar: NavigationBar; + resource: ResourceId; + resourceSetup: void; + mcpServers: MCPServerId[]; + mcpSetup: void; +} + +export const test = base.extend({ + resource: [ + 'gemini' as ResourceId, + { + scope: 'worker', + option: true, + }, + ], + + mcpServers: [ + ['github'] as MCPServerId[], + { + scope: 'worker', + option: true, + }, + ], + + workerElectronApp: [ + // eslint-disable-next-line no-empty-pattern + async ({}, use): Promise => { + const electronApp = await launchElectronApp(); + await use(electronApp); + await electronApp.close(); + }, + { scope: 'worker' }, + ], + + workerPage: [ + async ({ workerElectronApp }, use): Promise => { + const page = await getFirstPage(workerElectronApp); + await use(page); + }, + { scope: 'worker' }, + ], + + workerNavigationBar: [ + async ({ workerPage }, use): Promise => { + const navigationBar = new NavigationBar(workerPage); + await use(navigationBar); + }, + { scope: 'worker' }, + ], + + resourceSetup: [ + async ({ workerNavigationBar, resource }, use): Promise => { + try { + const provider = PROVIDERS[resource]; + const credentials = requireEnvVar(provider.envVarName); + const settingsPage = await workerNavigationBar.navigateToSettingsPage(); + await settingsPage.createResource(resource, credentials); + await use(); + } finally { + await safeCleanup(async () => { + const settingsPage = await workerNavigationBar.navigateToSettingsPage(); + await settingsPage.deleteResource(resource); + }, `Failed to delete ${resource} resource`); + } + }, + { scope: 'worker', auto: true }, + ], + + mcpSetup: [ + async ({ workerNavigationBar, mcpServers }, use): Promise => { + const configuredServers: Array<{ id: MCPServerId; serverName: string }> = []; + + try { + const mcpPage = await workerNavigationBar.navigateToMCPPage(); + + for (const id of mcpServers) { + const server = MCP_SERVERS[id]; + const token = requireEnvVar(server.envVarName); + + try { + configuredServers.push({ id, serverName: server.serverName }); + await mcpPage.createServer(server.serverName, token); + } catch (error) { + console.warn(`MCP setup skipped for ${id}:`, error); + throw error; + } + } + + await use(); + } finally { + if (configuredServers.length > 0) { + const mcpPage = await workerNavigationBar.navigateToMCPPage(); + + await Promise.allSettled( + configuredServers.map(({ id, serverName }) => + safeCleanup(() => mcpPage.deleteServer(serverName), `Failed to delete ${id} MCP server`), + ), + ); + } + } + }, + { scope: 'worker', auto: false }, + ], + + electronApp: async ({ workerElectronApp }, use): Promise => { + await use(workerElectronApp); + }, + + page: async ({ workerPage }, use): Promise => { + await use(workerPage); + }, +}); + +function requireEnvVar(envVarName: string): string { + const value = process.env[envVarName]; + if (!value) { + throw new Error(`${envVarName} environment variable is not set`); + } + return value; +} + +async function safeCleanup(operation: () => Promise, errorMessage: string): Promise { + try { + await operation(); + } catch (error) { + console.error(`${errorMessage}:`, error); + } +} + +export { expect } from '@playwright/test'; diff --git a/tests/playwright/src/globalSetup/global-setup.ts b/tests/playwright/src/globalSetup/global-setup.ts deleted file mode 100644 index 3133f5718e..0000000000 --- a/tests/playwright/src/globalSetup/global-setup.ts +++ /dev/null @@ -1,45 +0,0 @@ -/********************************************************************** - * 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 { removeFolderIfExists } from '../utility/cleanup'; - -let setupCalled = false; -let teardownCalled = false; - -export async function setup(): Promise { - if (!setupCalled) { - // remove all previous testing output files - // Junit reporter output file is created before we can clean up output folders - // It is not possible to remove junit output file because it is opened by the process already, at least on windows - if (!process.env.CI && !process.env.SKIP_REMOVE_FOLDER) { - await removeFolderIfExists('tests/output'); - } else { - console.log( - `On CI, skipping before All tests/output cleanup, see https://github.com/containers/podman-desktop/issues/5460`, - ); - } - setupCalled = true; - } -} - -export async function teardown(): Promise { - if (!teardownCalled) { - // here comes teardown logic - teardownCalled = true; - } -} diff --git a/tests/playwright/src/index.ts b/tests/playwright/src/index.ts deleted file mode 100644 index 2dddbe0e5c..0000000000 --- a/tests/playwright/src/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 - ***********************************************************************/ - -// export core modules -export * from './globalSetup/global-setup'; -export * from './runner/podman-desktop-runner'; -export * from './runner/runner-options'; -export * from './setupFiles/setup-registry'; -export * from './utility/auth-utils'; -export * from './utility/cleanup'; -export * from './utility/cluster-operations'; -export * from './utility/fixtures'; -export * from './utility/kubernetes'; -export * from './utility/operations'; -export * from './utility/platform'; -export * from './utility/wait'; - -// exports Podman Desktop Page Object Module -export * from './model/core/extensions'; -export * from './model/core/operations'; -export * from './model/core/platforms'; -export * from './model/core/states'; -export * from './model/core/types'; -export * from './model/pages/authentication-page'; -export * from './model/pages/base-page'; -export * from './model/pages/build-image-page'; -export * from './model/pages/cluster-creation-base-page'; -export * from './model/pages/command-palette'; -export * from './model/pages/compose-onboarding/compose-failed-page'; -export * from './model/pages/compose-onboarding/compose-local-install-page'; -export * from './model/pages/compose-onboarding/compose-onboarding-page'; -export * from './model/pages/compose-onboarding/compose-version-page'; -export * from './model/pages/compose-onboarding/compose-wide-install-page'; -export * from './model/pages/container-details-page'; -export * from './model/pages/containers-page'; -export * from './model/pages/create-kind-cluster-page'; -export * from './model/pages/create-machine-page'; -export * from './model/pages/create-pod-page'; -export * from './model/pages/dashboard-page'; -export * from './model/pages/deploy-to-kubernetes-page'; -export * from './model/pages/details-page'; -export * from './model/pages/docker-compatibility-page'; -export * from './model/pages/experimental-page'; -export * from './model/pages/extension-card-page'; -export * from './model/pages/extension-catalog-card-page'; -export * from './model/pages/extension-details-page'; -export * from './model/pages/extensions-page'; -export * from './model/pages/forms/machine-creation-form'; -export * from './model/pages/image-details-page'; -export * from './model/pages/image-edit-page'; -export * from './model/pages/images-page'; -export * from './model/pages/kubernetes-bar'; -export * from './model/pages/kubernetes-context-page'; -export * from './model/pages/kubernetes-resource-details-page'; -export * from './model/pages/kubernetes-resource-page'; -export * from './model/pages/main-page'; -export * from './model/pages/onboarding-page'; -export * from './model/pages/play-kube-yaml-page'; -export * from './model/pages/podman-machine-details-page'; -export * from './model/pages/podman-onboarding-page'; -export * from './model/pages/pods-details-page'; -export * from './model/pages/pods-page'; -export * from './model/pages/preferences-page'; -export * from './model/pages/pull-image-page'; -export * from './model/pages/registries-page'; -export * from './model/pages/resource-card-page'; -export * from './model/pages/resource-cli-card-page'; -export * from './model/pages/resource-connection-card-page'; -export * from './model/pages/resource-details-page'; -export * from './model/pages/resources-page'; -export * from './model/pages/run-image-page'; -export * from './model/pages/settings-bar'; -export * from './model/pages/settings-page'; -export * from './model/pages/tasks-page'; -export * from './model/pages/troubleshooting-page'; -export * from './model/pages/welcome-page'; -export * from './model/workbench/navigation'; -export * from './model/workbench/status-bar'; diff --git a/tests/playwright/src/model/core/extensions.ts b/tests/playwright/src/model/core/extensions.ts deleted file mode 100644 index 3633a11eda..0000000000 --- a/tests/playwright/src/model/core/extensions.ts +++ /dev/null @@ -1,197 +0,0 @@ -/********************************************************************** - * 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 - ***********************************************************************/ - -// Extension locators definition -export interface ExtensionType { - extensionName: string; - extensionFullName: string; - extensionLabel: string; - extensionFullLabel: string; -} - -// Catalog/External extensions -export const minikubeExtension: ExtensionType = { - extensionName: 'minikube', - extensionFullName: 'minikube extension', - extensionLabel: 'minikube', - extensionFullLabel: 'podman-desktop.minikube', -}; - -export const podmanAILabExtension: ExtensionType = { - extensionName: 'Podman AI Lab', - extensionFullName: 'Podman AI Lab extension', - extensionLabel: 'ai-lab', - extensionFullLabel: 'redhat.ai-lab', -}; - -export const extensionsPackExtension: ExtensionType = { - extensionName: 'Red Hat Extension Pack', - extensionFullName: 'Red Hat Extension Pack extension', - extensionLabel: 'redhat-pack', - extensionFullLabel: 'redhat.redhat-pack', -}; - -export const bootcExtension: ExtensionType = { - extensionName: 'Bootable Container', - extensionFullName: 'Bootable Container extension', - extensionLabel: 'bootc', - extensionFullLabel: 'redhat.bootc', -}; - -export const developerSandboxExtension: ExtensionType = { - extensionName: 'Developer Sandbox', - extensionFullName: 'Developer Sandbox extension', - extensionLabel: 'redhat-sandbox', - extensionFullLabel: 'redhat.redhat-sandbox', -}; - -export const imageLayersExplorerExtension: ExtensionType = { - extensionName: 'Image Layers Explorer', - extensionFullName: 'Image Layers Explorer extension', - extensionLabel: 'layers-explorer', - extensionFullLabel: 'podman-desktop.layers-explorer', -}; - -export const podmanQuadletExtension: ExtensionType = { - extensionName: 'Podman Quadlet', - extensionFullName: 'Podman Quadlet extension', - extensionLabel: 'quadlet', - extensionFullLabel: 'podman-desktop.quadlet', -}; - -export const ssoExtension: ExtensionType = { - extensionName: 'Red Hat Authentication', - extensionFullName: 'Red Hat Authentication extension', - extensionLabel: 'redhat-authentication', - extensionFullLabel: 'redhat.redhat-authentication', -}; - -export const openshiftCheckerExtension: ExtensionType = { - extensionName: 'Red Hat OpenShift Checker', - extensionFullName: 'Red Hat OpenShift Checker extension', - extensionLabel: 'openshift-checker', - extensionFullLabel: 'redhat.openshift-checker', -}; - -export const openshiftLocalExtension: ExtensionType = { - extensionName: 'Red Hat OpenShift Local', - extensionFullName: 'Red Hat OpenShift Local extension', - extensionLabel: 'openshift-local', - extensionFullLabel: 'redhat.openshift-local', -}; - -// external contributor -export const headlampExtension: ExtensionType = { - extensionName: 'Headlamp', - extensionFullName: 'Headlamp extension', - extensionLabel: 'Headlamp', - extensionFullLabel: 'Headlamp', -}; - -// Built-in extensions -export const composeExtension: ExtensionType = { - extensionName: 'Compose', - extensionFullName: 'Compose extension', - extensionLabel: 'compose', - extensionFullLabel: 'podman-desktop.compose', -}; - -export const dockerExtension: ExtensionType = { - extensionName: 'Docker', - extensionFullName: 'Docker extension', - extensionLabel: 'docker', - extensionFullLabel: 'podman-desktop.docker', -}; - -export const kindExtension: ExtensionType = { - extensionName: 'Kind', - extensionFullName: 'Kind extension', - extensionLabel: 'kind', - extensionFullLabel: 'podman-desktop.kind', -}; - -export const kubeContextExtension: ExtensionType = { - extensionName: 'Kube Context', - extensionFullName: 'Kube Context extension', - extensionLabel: 'kube-context', - extensionFullLabel: 'podman-desktop.kube-context', -}; - -export const kubectlCLIExtension: ExtensionType = { - extensionName: 'kubectl CLI', - extensionFullName: 'kubectl CLI extension', - extensionLabel: 'podman-desktop.kubectl-cli', - extensionFullLabel: 'podman-desktop.kubectl-cli', -}; - -export const limaExtension: ExtensionType = { - extensionName: 'Lima', - extensionFullName: 'Lima extension', - extensionLabel: 'lima', - extensionFullLabel: 'podman-desktop.lima', -}; - -export const podmanExtension: ExtensionType = { - extensionName: 'Podman', - extensionFullName: 'Podman extension', - extensionLabel: 'podman', - extensionFullLabel: 'podman-desktop.podman', -}; - -export const registriesExtension: ExtensionType = { - extensionName: 'Registries', - extensionFullName: 'Registries extension', - extensionLabel: 'registries', - extensionFullLabel: 'podman-desktop.registries', -}; - -export const openshiftDockerExtension: ExtensionType = { - extensionName: 'OpenShift', - extensionFullName: 'OpenShift extension', - extensionLabel: 'OpenShift', - extensionFullLabel: 'OpenShift', -}; - -export const extensionsBuiltInList = [ - composeExtension, - dockerExtension, - kindExtension, - kubeContextExtension, - kubectlCLIExtension, - podmanAILabExtension, - registriesExtension, -]; -export const extensionsExternalList = [ - minikubeExtension, - podmanAILabExtension, - extensionsPackExtension, - bootcExtension, - developerSandboxExtension, - imageLayersExplorerExtension, - podmanQuadletExtension, - ssoExtension, - openshiftCheckerExtension, - openshiftLocalExtension, -]; -export const extensionsInstallationSmokeList = [ - developerSandboxExtension, - openshiftLocalExtension, - openshiftCheckerExtension, - openshiftDockerExtension, -]; -export const extensionsAllExternalList = [...extensionsExternalList, headlampExtension]; diff --git a/tests/playwright/src/model/core/operations.ts b/tests/playwright/src/model/core/operations.ts deleted file mode 100644 index fb0babf980..0000000000 --- a/tests/playwright/src/model/core/operations.ts +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 - ***********************************************************************/ - -export enum ResourceElementActions { - Start = 'Start', - Restart = 'Restart', - Stop = 'Stop', - Delete = 'Delete', -} - -export enum PlayYamlRuntime { - Kubernetes, - Podman, -} diff --git a/tests/playwright/src/model/core/platforms.ts b/tests/playwright/src/model/core/platforms.ts deleted file mode 100644 index 4122ec49f1..0000000000 --- a/tests/playwright/src/model/core/platforms.ts +++ /dev/null @@ -1,23 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 - ***********************************************************************/ - -export const enum ArchitectureType { - Default = 'default', - AMD64 = 'amd64', - ARM64 = 'arm64', -} diff --git a/tests/playwright/src/model/core/states.ts b/tests/playwright/src/model/core/states.ts deleted file mode 100644 index 77a0d1e89b..0000000000 --- a/tests/playwright/src/model/core/states.ts +++ /dev/null @@ -1,76 +0,0 @@ -/********************************************************************** - * 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 - ***********************************************************************/ - -export enum ContainerState { - Starting = 'STARTING', - Stopping = 'STOPPING', - Running = 'RUNNING', - Error = 'ERROR', - Exited = 'EXITED', - Deleting = 'DELETING', - Created = 'CREATED', - Paused = 'PAUSED', - Stopped = 'STOPPED', - Unknown = 'UNKNOWN', -} - -export enum PodState { - Created = 'CREATED', - Running = 'RUNNING', - Stopped = 'STOPPED', - Exited = 'EXITED', - Dead = 'DEAD', - Starting = 'STARTING', - Stopping = 'STOPPING', - Deleting = 'DELETING', - Restarting = 'RESTARTING', - Degraded = 'DEGRADED', - Paused = 'PAUSED', - Unknown = 'UNKNOWN', -} - -export enum VolumeState { - Used = 'USED', - Unused = 'UNUSED', -} - -export enum ResourceElementState { - Running = 'RUNNING', - Off = 'OFF', - Starting = 'STARTING', -} - -export enum KubernetesResourceState { - Starting = 'STARTING', - Running = 'RUNNING', - Stopped = 'STOPPED', - Unknown = 'UNKNOWN', - Succeeded = 'SUCCEEDED', -} -export enum ExtensionState { - Disabled = 'DISABLED', - Active = 'ACTIVE', - Running = 'RUNNING', - NotInstalled = 'NOT-INSTALLED', - Downloadable = 'DOWNLOADABLE', -} - -export enum ImageState { - Used = 'USED', - Unused = 'UNUSED', -} diff --git a/tests/playwright/src/model/core/types.ts b/tests/playwright/src/model/core/types.ts index 09990e7aa2..b52cac14c4 100644 --- a/tests/playwright/src/model/core/types.ts +++ b/tests/playwright/src/model/core/types.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (C) 2024 Red Hat, Inc. + * 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. @@ -16,71 +16,147 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import type { PlayYamlRuntime } from './operations'; +export interface FlowParameters { + description?: string; + model?: string; + mcpServer?: string; + prompt?: string; + instruction?: string; +} + +export const builtInExtensions = [ + { name: 'Default MCP Registries', locator: 'kortex.mcp-registries' }, + { name: 'Gemini', locator: 'kortex.gemini' }, + { name: 'goose', locator: 'kortex.goose' }, + { name: 'OpenAI Compatible', locator: 'kortex.openai-compatible' }, + { name: 'OpenShift AI', locator: 'kortex.openshift-ai' }, +] as const; + +export type ExtensionLocator = (typeof builtInExtensions)[number]['locator']; -export interface ContainerInteractiveParams { - interactive?: boolean; - attachTerminal?: boolean; - attachVolumeName?: string; - attachVolumePath?: string; +export enum Button { + STOP = 'Stop', + START = 'Start', + DELETE = 'Delete', } -export interface KindClusterOptions { - configFilePath?: string; - providerType?: string; - httpPort?: string; - httpsPort?: string; - useIngressController?: boolean; - containerImage?: string; +export enum State { + ACTIVE = 'ACTIVE', + DISABLED = 'DISABLED', } -export interface DeployPodOptions { - useKubernetesServices?: boolean; - useRestrictedSecurityContext?: boolean; - useKubernetesIngress?: boolean; - containerExposedPort?: string; - isOpenShiftCluster?: boolean; - useOpenShiftRoutes?: boolean; +export const BADGE_TEXT = 'built-in Extension' as const; + +export enum BadgeType { + BUILT_IN = 'badge-built-in Extension', } -export interface PlayKubernetesOptions { - runtime?: PlayYamlRuntime; - kubernetesNamespace?: string; - kubernetesContext: string; +export enum ExtensionStatus { + RUNNING = 'running', + STOPPED = 'stopped', + UNKNOWN = 'unknown', } -export enum KubernetesResources { - Nodes = 'Nodes', - Deployments = 'Deployments', - Services = 'Services', - IngeressesRoutes = 'Ingresses & Routes', - PVCs = 'Persistent Volume Claims', - ConfigMapsSecrets = 'ConfigMaps & Secrets', - PortForwarding = 'Port Forwarding', - Pods = 'Pods', - Cronjobs = 'CronJobs', - Jobs = 'Jobs', +export const proxyConfigurations = [ + { option: 'System', editable: false }, + { option: 'Manual', editable: true }, + { option: 'Disabled', editable: false }, +] as const; + +export type ProxyConfigurationOption = (typeof proxyConfigurations)[number]['option']; + +export enum PreferenceOption { + APPEARANCE = 'Appearance', + EDITOR = 'Editor', + EXIT_ON_CLOSE = 'Exit On Close', + EXPERIMENTAL_STATUS_BAR = 'Experimental (Status Bar Providers)', + EXTENSIONS = 'Extensions', + KUBERNETES = 'Kubernetes', + TASKS = 'Tasks', + TELEMETRY = 'Telemetry', + TERMINAL = 'Terminal', + USER_CONFIRMATION = 'User Confirmation', + WINDOW = 'Window', } -export const KubernetesResourceAttributes: Record = { - [KubernetesResources.Nodes]: ['Status', 'Name', 'Roles', 'Version', 'OS', 'Kernel', 'Age'], - [KubernetesResources.Deployments]: ['Selected', 'Status', 'Name', 'Conditions', 'Pods', 'Age', 'Actions'], - [KubernetesResources.Services]: ['Selected', 'Status', 'Name', 'Type', 'Cluster IP', 'Ports', 'Age', 'Actions'], - [KubernetesResources.IngeressesRoutes]: ['Selected', 'Status', 'Name', 'Host/Path', 'Backend', 'Age', 'Actions'], - [KubernetesResources.PVCs]: ['Selected', 'Status', 'Name', 'Environment', 'Age', 'Size', 'Actions'], - [KubernetesResources.ConfigMapsSecrets]: ['Selected', 'Status', 'Name', 'Type', 'Keys', 'Age', 'Actions'], - [KubernetesResources.PortForwarding]: ['Status', 'Name', 'Type', 'Local Port', 'Remote Port', 'Actions'], - [KubernetesResources.Pods]: ['Selected', 'Status', 'Name', 'Containers', 'Age', 'Actions'], - [KubernetesResources.Cronjobs]: [ - 'Selected', - 'Status', - 'Name', - 'Schedule', - 'Last scheduled', - 'Suspended', - 'Active', - 'Age', - 'Actions', - ], - [KubernetesResources.Jobs]: ['Selected', 'Status', 'Name', 'Conditions', 'Completions', 'Age', 'Actions'], -}; +export const preferenceOptions = (): PreferenceOption[] => Object.values(PreferenceOption); + +export const resources = { + openshiftai: { displayName: 'OpenShift AI', hasCreateButton: true }, + openai: { displayName: 'OpenAI', hasCreateButton: true }, + goose: { displayName: 'goose', hasCreateButton: false }, + gemini: { displayName: 'Gemini', hasCreateButton: true }, +} as const; + +export type SettingsResourceId = keyof typeof resources; + +export const featuredResources = Object.keys(resources) as (keyof typeof resources)[]; +export const resourcesWithCreateButton = Object.values(resources) + .filter(r => r.hasCreateButton) + .map(r => r.displayName); + +export interface MCPServerConfig { + readonly envVarName: string; + readonly serverName: string; +} + +export const MCP_SERVERS = { + github: { + envVarName: 'GITHUB_TOKEN', + serverName: 'com.github.mcp', + }, +} as const satisfies Record; + +export type MCPServerId = keyof typeof MCP_SERVERS; + +export interface ResourceConfig { + readonly envVarName: string; + readonly resourceId: SettingsResourceId; + readonly baseURL?: string; +} + +export const PROVIDERS = { + gemini: { + envVarName: 'GEMINI_API_KEY', + resourceId: 'gemini', + }, + openai: { + envVarName: 'OPENAI_API_KEY', + resourceId: 'openai', + baseURL: 'https://api.openai.com/v1', + }, + 'openshift-ai': { + envVarName: 'OPENSHIFT_AI_TOKEN', + resourceId: 'openshiftai', + }, +} as const satisfies Record; + +export type ResourceId = keyof typeof PROVIDERS; + +export interface DialogOptions { + dialogName?: string; + buttonName?: string; + timeout?: number; + throwErrorOnFailOrMissing?: boolean; + waitForDialogToDisappear?: boolean; +} + +export const SELECTORS = { + MAIN_ANY: 'main', + MAIN_INITIALIZING: 'main.flex.flex-row.w-screen.h-screen.justify-center', + MAIN_APP_CONTAINER: 'main.flex.flex-col.w-screen.h-screen.overflow-hidden', + TITLE_BAR: 'header#navbar', + WELCOME_PAGE: 'div:has-text("Get started with Kortex")', + NAVIGATION: { role: 'navigation' as const, name: 'AppNavigation' }, +} as const; + +export const TIMEOUTS = { + PAGE_LOAD: 90_000, + NON_DEVTOOLS_WINDOW: 60_000, + RETRY_DELAY: 1_000, + MAX_RETRIES: 3, + DEFAULT: 120_000, + INITIALIZING_SCREEN: 180_000, + STANDARD: 30_000, + SHORT: 10_000, +} as const; diff --git a/tests/playwright/src/model/navigation/navigation.ts b/tests/playwright/src/model/navigation/navigation.ts new file mode 100644 index 0000000000..2a1c884adf --- /dev/null +++ b/tests/playwright/src/model/navigation/navigation.ts @@ -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 { expect, type Locator, type Page } from '@playwright/test'; + +import type { BasePage } from '../pages/base-page'; +import { ChatPage } from '../pages/chat-page'; +import { ExtensionsPage } from '../pages/extensions-page'; +import { FlowsPage } from '../pages/flows-page'; +import { McpPage } from '../pages/mcp-page'; +import { SettingsPage } from '../pages/settings-page'; + +export class NavigationBar { + readonly page: Page; + readonly navigationLocator: Locator; + readonly chatLink: Locator; + readonly mcpLink: Locator; + readonly flowsLink: Locator; + readonly extensionsLink: Locator; + readonly settingsLink: Locator; + private readonly links: Locator[]; + + constructor(page: Page) { + this.page = page; + this.navigationLocator = this.page.getByRole('navigation', { name: 'AppNavigation' }); + this.chatLink = this.navigationLocator.getByRole('link', { name: 'Chat' }); + this.mcpLink = this.navigationLocator.getByRole('link', { name: 'MCP' }); + this.flowsLink = this.navigationLocator.getByRole('link', { name: 'Flows', exact: true }); + this.extensionsLink = this.navigationLocator.getByRole('link', { name: 'Extensions', exact: true }); + this.settingsLink = this.navigationLocator.getByRole('link', { name: 'Settings', exact: true }); + this.links = [this.chatLink, this.mcpLink, this.flowsLink, this.extensionsLink, this.settingsLink]; + } + + getAllLinks(): Locator[] { + return this.links; + } + + private async navigateTo(link: Locator, PageClass: new (page: Page) => T): Promise { + await expect(link).toBeVisible(); + await link.click(); + + const pageInstance = new PageClass(this.page); + await pageInstance.waitForLoad(); + return pageInstance; + } + + async navigateToChatPage(): Promise { + return this.navigateTo(this.chatLink, ChatPage); + } + + async navigateToMCPPage(): Promise { + return this.navigateTo(this.mcpLink, McpPage); + } + + async navigateToFlowsPage(): Promise { + return this.navigateTo(this.flowsLink, FlowsPage); + } + + async navigateToExtensionsPage(): Promise { + return this.navigateTo(this.extensionsLink, ExtensionsPage); + } + + async navigateToSettingsPage(): Promise { + return this.navigateTo(this.settingsLink, SettingsPage); + } +} diff --git a/tests/playwright/src/model/pages/authentication-page.ts b/tests/playwright/src/model/pages/authentication-page.ts deleted file mode 100644 index fffdec3185..0000000000 --- a/tests/playwright/src/model/pages/authentication-page.ts +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; - -import { SettingsPage } from './settings-page'; - -export class AuthenticationPage extends SettingsPage { - readonly main: Locator; - readonly header: Locator; - readonly heading: Locator; - readonly content: Locator; - readonly providersList: Locator; - - constructor(page: Page) { - super(page, 'Authentication'); - this.main = page.getByRole('region', { name: 'Authentication' }); - this.header = this.main.getByRole('region', { name: 'Header' }); - this.heading = this.header.getByRole('heading', { name: 'Title', exact: true }); - this.content = this.main.getByRole('region', { name: 'Content' }); - this.providersList = this.content.getByRole('list'); - } - - public getProvider(providerName: string): Locator { - return this.providersList.getByRole('listitem', { name: providerName }); - } -} diff --git a/tests/playwright/src/model/pages/base-page.ts b/tests/playwright/src/model/pages/base-page.ts index 7021814fcb..cadab6f596 100644 --- a/tests/playwright/src/model/pages/base-page.ts +++ b/tests/playwright/src/model/pages/base-page.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (C) 2023 Red Hat, Inc. + * 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. @@ -16,17 +16,27 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import type { Page } from '@playwright/test'; +import { expect, type Locator, type Page } from '@playwright/test'; export abstract class BasePage { readonly page: Page; - // TODO: extract header and content locators into base class, add that roles into all pages constructor(page: Page) { this.page = page; } - public getPage(): Page { - return this.page; + protected async openTab( + button: Locator, + PageClass: new (page: Page) => T, + timeout = 10_000, + ): Promise { + await expect(button).toBeEnabled({ timeout }); + await button.click({ timeout }); + + const pageInstance = new PageClass(this.page); + await pageInstance.waitForLoad(); + return pageInstance; } + + abstract waitForLoad(): Promise; } diff --git a/tests/playwright/src/model/pages/base-table-page.ts b/tests/playwright/src/model/pages/base-table-page.ts new file mode 100644 index 0000000000..9af0d1c039 --- /dev/null +++ b/tests/playwright/src/model/pages/base-table-page.ts @@ -0,0 +1,66 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BasePage } from './base-page'; + +export abstract class BaseTablePage extends BasePage { + readonly content: Locator; + readonly table: Locator; + + constructor(page: Page, tableName: string) { + super(page); + this.content = this.page.getByRole('region', { name: 'content' }); + this.table = this.content.getByRole('table', { name: tableName }); + } + + async getTableRowByName(name: string, exact = true): Promise { + const locator = this.table.getByRole('row').and(this.page.getByLabel(name, { exact: exact })); + return (await locator.count()) > 0 ? locator : undefined; + } + + async countRowsFromTable(): Promise { + const rowCount = await this.table.getByRole('row').count(); + return Math.max(0, rowCount - 1); + } + + async ensureRowExists(name: string, timeout = 30_000, exact = true): Promise { + await expect.poll(async () => await this.getTableRowByName(name, exact), { timeout: timeout }).toBeTruthy(); + } + + async ensureRowDoesNotExist(name: string, timeout = 30_000, exact = true): Promise { + await expect.poll(async () => await this.getTableRowByName(name, exact), { timeout: timeout }).toBeFalsy(); + } + + async getRowLocatorByName(name: string, exact = true): Promise { + const row = await this.getTableRowByName(name, exact); + if (row === undefined) { + throw new Error(`Row with name '${name}' does not exist`); + } + return row; + } + + async getRowLocatorByIndex(index: number): Promise { + const rows = await this.table.getByRole('row').all(); + if (index < 0 || index >= rows.length) { + throw new Error(`Row index ${index} is out of bounds. Table has ${rows.length} row(s)`); + } + return rows[index]; + } +} diff --git a/tests/playwright/src/model/pages/build-image-page.ts b/tests/playwright/src/model/pages/build-image-page.ts deleted file mode 100644 index f05f6b2769..0000000000 --- a/tests/playwright/src/model/pages/build-image-page.ts +++ /dev/null @@ -1,173 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { archType } from '../../utility/platform'; -import { ArchitectureType } from '../core/platforms'; -import { BasePage } from './base-page'; -import { ImagesPage } from './images-page'; - -export class BuildImagePage extends BasePage { - readonly heading: Locator; - readonly containerFilePathInput: Locator; - readonly buildContextDirectoryInput: Locator; - readonly imageNameInput: Locator; - readonly buildButton: Locator; - readonly doneButton: Locator; - readonly containerFilePathButton: Locator; - readonly platformRegion: Locator; - readonly arm64Button: Locator; - readonly amd64Button: Locator; - readonly arm64checkbox: Locator; - readonly amd64checkbox: Locator; - readonly archMoreOptionsButton: Locator; - readonly archLessOptionsButton: Locator; - - constructor(page: Page) { - super(page); - this.heading = page.getByRole('heading', { - name: 'Build Image from Containerfile', - }); - this.containerFilePathInput = page.getByPlaceholder('Containerfile to build'); - this.buildContextDirectoryInput = page.getByPlaceholder('Directory to build in'); - this.imageNameInput = page.getByPlaceholder('my-custom-image'); - this.buildButton = page.getByRole('button', { name: 'Build', exact: true }); - this.doneButton = page.getByRole('button', { name: 'Done' }); - this.containerFilePathButton = page.getByRole('button', { name: 'Browse...' }).first(); - this.platformRegion = page.getByRole('region', { - name: 'Build Platform Options', - }); - this.arm64Button = this.platformRegion.getByLabel('linux/arm64'); - this.amd64Button = this.platformRegion.getByLabel('linux/amd64'); - this.arm64checkbox = this.platformRegion.getByLabel('ARM® aarch64 systems'); - this.amd64checkbox = this.platformRegion.getByLabel('Intel and AMD x86_64 systems'); - this.archMoreOptionsButton = this.platformRegion.getByRole('button', { name: 'Show more options' }); - this.archLessOptionsButton = this.platformRegion.getByRole('button', { name: 'Show less options' }); - } - - async buildImage( - imageName: string, - containerFilePath: string, - contextDirectory: string, - archType: string[] = [ArchitectureType.Default], - timeout = 120000, - ): Promise { - return test.step(`Building image ${imageName} from ${containerFilePath} in ${contextDirectory} with ${archType} architecture`, async () => { - if (!containerFilePath) { - throw Error(`Path to containerfile is incorrect or not provided!`); - } - - await this.containerFilePathInput.fill(containerFilePath); - - if (contextDirectory) await this.buildContextDirectoryInput.fill(contextDirectory); - if (imageName) { - await this.imageNameInput.clear(); - await this.imageNameInput.pressSequentially(imageName, { delay: 50 }); - } - - if (!archType.includes(ArchitectureType.Default)) { - await this.uncheckDefaultCheckbox(); - await this.showAllArchOptions(); - - for (const architecture of archType) { - await this.checkArchCheckbox(architecture); - } - } - - await playExpect(this.buildButton).toBeEnabled(); - await this.buildButton.scrollIntoViewIfNeeded(); - await this.buildButton.click(); - - await playExpect(this.doneButton).toBeEnabled({ timeout: timeout }); - await this.validateBuildLogs(); - await this.doneButton.scrollIntoViewIfNeeded(); - await this.doneButton.click(); - console.log(`Image ${imageName} has been built successfully!`); - - return new ImagesPage(this.page); - }); - } - - async uncheckedAllCheckboxes(): Promise { - return test.step('Uncheck all checkboxes', async () => { - await this.showAllArchOptions(); - for (const button of await this.platformRegion.getByRole('button').all()) { - const buttonText = (await button.textContent()) ?? ''; - if (buttonText.trim() === 'New platform' || buttonText.trim() === 'Less Options...') { - continue; - } - const checkbox = button.getByRole('checkbox'); - try { - await playExpect(checkbox).toBeVisible(); - await playExpect(checkbox).toBeChecked(); - await playExpect(button).toBeEnabled(); - await button.click(); - await playExpect(checkbox).not.toBeChecked(); - } catch { - console.log(`Checkbox for button "${buttonText.trim()}" is already unchecked.`); - } - } - }); - } - - private async checkArchCheckbox(archType: string): Promise { - return test.step(`Check ${archType} checkbox`, async () => { - const archTypeButton = this.platformRegion.getByLabel('linux/' + archType); - await playExpect(archTypeButton).toBeEnabled(); - await archTypeButton.click(); - const checkbox = archTypeButton.getByRole('checkbox'); - await playExpect(checkbox).toBeChecked(); - }); - } - - async uncheckDefaultCheckbox(): Promise { - return test.step('Uncheck default checkbox', async () => { - if (archType === 'arm64') { - await playExpect(this.arm64checkbox).toBeChecked(); - await this.arm64Button.click(); - await playExpect(this.arm64checkbox).not.toBeChecked(); - } else { - await playExpect(this.amd64checkbox).toBeChecked(); - await this.amd64Button.click(); - await playExpect(this.amd64checkbox).not.toBeChecked(); - } - }); - } - - private async showAllArchOptions(): Promise { - if ((await this.archMoreOptionsButton.count()) > 0) { - await playExpect(this.archMoreOptionsButton).toBeEnabled(); - await this.archMoreOptionsButton.click(); - } else { - await playExpect(this.archLessOptionsButton).toBeEnabled(); - } - } - - async validateBuildLogs(): Promise { - const logs = this.page.locator('.xterm-rows'); - await playExpect(logs).toBeVisible(); - const logRows = await logs.locator('div:has(span)').all(); - - await Promise.all( - logRows.map(async logRow => { - await playExpect.poll(async () => logRow.textContent()).not.toContain('Error'); - }), - ); - } -} diff --git a/tests/playwright/src/model/pages/chat-page.ts b/tests/playwright/src/model/pages/chat-page.ts new file mode 100644 index 0000000000..00d7ef1d3c --- /dev/null +++ b/tests/playwright/src/model/pages/chat-page.ts @@ -0,0 +1,212 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import { clearAllToasts, handleDialogIfPresent } from 'src/utils/app-ready'; + +import { TIMEOUTS } from '../core/types'; +import { BasePage } from './base-page'; +import { FlowsCreatePage } from './flows-create-page'; + +export class ChatPage extends BasePage { + readonly toggleSidebarButton: Locator; + readonly mcpDropdown: Locator; + readonly newChatButton: Locator; + readonly sidebarNewChatButton: Locator; + readonly deleteAllChatsButton: Locator; + readonly modelDropdownSelector: Locator; + readonly messageField: Locator; + readonly sendButton: Locator; + readonly suggestedMessagesGrid: Locator; + readonly chatHistoryItems: Locator; + readonly conversationMessages: Locator; + readonly modelConversationMessages: Locator; + readonly chatHistoryItem: Locator; + readonly chatHistoryItemMenuAction: Locator; + readonly chatHistoryItemDeleteButton: Locator; + readonly chatHistoryEmptyMessage: Locator; + readonly toasts: Locator; + readonly modelMenuItems: Locator; + readonly activeModelMenuItem: Locator; + readonly exportAsFlowButton: Locator; + + constructor(page: Page) { + super(page); + this.toggleSidebarButton = page.getByRole('button', { name: 'Toggle sidebar' }); + this.mcpDropdown = page.getByRole('button', { name: 'Select MCP servers' }); + this.newChatButton = page.getByRole('button', { name: 'New Chat' }); + this.sidebarNewChatButton = page.locator('[data-sidebar="header"] button[data-tooltip-trigger]').first(); + this.deleteAllChatsButton = page.getByRole('button', { name: 'Delete all chats' }); + this.modelDropdownSelector = page.getByRole('button', { name: 'Select model' }); + this.messageField = page.getByPlaceholder('Send a message...'); + this.sendButton = page.getByRole('button', { name: 'Send message' }); + this.suggestedMessagesGrid = page.getByRole('region', { name: 'Suggested prompts' }); + this.chatHistoryItems = page.locator('li[data-sidebar="menu-item"]'); + this.conversationMessages = page.locator('div[data-role]'); + this.modelConversationMessages = page.locator('div[data-role="assistant"]'); + this.chatHistoryItem = page.locator('button[data-sidebar="menu-button"]'); + this.chatHistoryItemMenuAction = page.locator('button[data-sidebar="menu-action"]'); + this.chatHistoryItemDeleteButton = page.getByRole('menuitem', { name: 'Delete' }); + this.chatHistoryEmptyMessage = page.getByText('Your conversations will appear here once you start chatting!'); + this.toasts = page.locator('[data-sonner-toast]'); + this.modelMenuItems = page.getByRole('menuitem'); + this.activeModelMenuItem = page.locator('[role="menuitem"][data-active="true"]'); + this.exportAsFlowButton = page.getByRole('button', { name: 'Export as Flow' }); + } + + async waitForLoad(): Promise { + await expect(this.messageField).toBeVisible({ timeout: 10_000 }); + await expect(this.toggleSidebarButton).toBeVisible({ timeout: 10_000 }); + } + + getSuggestedMessages(): Locator { + return this.suggestedMessagesGrid.getByRole('button'); + } + + async verifyHeaderElementsVisible(): Promise { + await expect(this.toggleSidebarButton).toBeVisible(); + await expect(this.mcpDropdown).toBeVisible(); + await expect(this.newChatButton).toBeVisible(); + await expect(this.modelDropdownSelector).toBeVisible(); + } + + async verifyInputAreaVisible(): Promise { + await expect(this.messageField).toBeVisible(); + await expect(this.sendButton).toBeVisible(); + } + + async verifySuggestedMessagesVisible(minCount = 4): Promise { + const suggestedMessages = this.getSuggestedMessages(); + const count = await suggestedMessages.count(); + expect(count).toBeGreaterThanOrEqual(minCount); + for (let i = 0; i < minCount; i++) { + await expect(suggestedMessages.nth(i)).toBeVisible(); + } + } + + async ensureSidebarVisible(): Promise { + const isSidebarOpen = await this.sidebarNewChatButton.isVisible(); + if (!isSidebarOpen) { + await this.toggleSidebarButton.click(); + await expect(this.sidebarNewChatButton).toBeVisible(); + } + } + + async ensureSidebarHidden(): Promise { + const isSidebarOpen = await this.sidebarNewChatButton.isVisible(); + if (isSidebarOpen) { + await this.toggleSidebarButton.click(); + await expect(this.sidebarNewChatButton).not.toBeVisible(); + } + } + + async sendMessage(message: string, timeout = 5_000): Promise { + await this.messageField.fill(message); + await expect(this.messageField).toHaveValue(message); + await expect(this.sendButton).toBeEnabled(); + await this.sendButton.click(); + await this.page.waitForTimeout(timeout); + } + + async clickNewChat(): Promise { + const isSidebarOpen = await this.sidebarNewChatButton.isVisible(); + if (isSidebarOpen) { + await this.sidebarNewChatButton.click(); + } else { + await this.newChatButton.click(); + } + await expect(this.suggestedMessagesGrid).toBeVisible(); + } + + async getChatHistoryCount(): Promise { + return await this.chatHistoryItems.count(); + } + + async clickChatHistoryItemByIndex(index: number): Promise { + await this.chatHistoryItems.nth(index).locator(this.chatHistoryItem).click(); + } + + async deleteChatHistoryItemByIndex(index: number): Promise { + const item = this.chatHistoryItems.nth(index); + await item.locator(this.chatHistoryItemMenuAction).click(); + await this.chatHistoryItemDeleteButton.click(); + await handleDialogIfPresent(this.page); + } + + async deleteAllChatHistoryItems(): Promise { + await this.deleteAllChatsButton.click(); + await handleDialogIfPresent(this.page); + } + + async waitForChatHistoryCount(expectedCount: number, timeout = 10_000): Promise { + await expect(this.chatHistoryItems).toHaveCount(expectedCount, { timeout }); + } + + async verifyChatHistoryEmpty(): Promise { + await expect(this.chatHistoryEmptyMessage).toBeVisible(); + } + + async verifyConversationMessage(message: string): Promise { + await expect(this.conversationMessages.getByRole('paragraph').getByText(message, { exact: true })).toBeVisible(); + } + + async verifyModelConversationMessage(textOrRegex: string | RegExp): Promise { + const messagesLocators = await this.modelConversationMessages.all(); + + for (const messageLocator of messagesLocators) { + const message = await messageLocator.textContent(); + if (message?.match(textOrRegex)) { + return true; + } + } + return false; + } + + async ensureNotificationsAreNotVisible(): Promise { + await clearAllToasts(this.page, this.toasts); + } + + async getAvailableModelsCount(): Promise { + await this.modelDropdownSelector.click(); + const count = await this.modelMenuItems.count(); + await this.page.keyboard.press('Escape'); + return count; + } + + async selectModelByIndex(index: number): Promise { + await this.modelDropdownSelector.click(); + const modelItem = this.modelMenuItems.nth(index); + await expect(modelItem).toBeVisible(); + await modelItem.click(); + } + + async getSelectedModelName(): Promise { + await this.modelDropdownSelector.click(); + const text = await this.activeModelMenuItem.textContent(); + await this.page.keyboard.press('Escape'); + return text?.trim() ?? ''; + } + + async exportAsFlow(): Promise { + await expect(this.exportAsFlowButton).toBeEnabled({ timeout: TIMEOUTS.STANDARD }); + await this.exportAsFlowButton.click(); + await expect(this.exportAsFlowButton).not.toBeVisible({ timeout: TIMEOUTS.STANDARD }); + + return new FlowsCreatePage(this.page); + } +} diff --git a/tests/playwright/src/model/pages/cli-tools-page.ts b/tests/playwright/src/model/pages/cli-tools-page.ts deleted file mode 100644 index ee6bacb67a..0000000000 --- a/tests/playwright/src/model/pages/cli-tools-page.ts +++ /dev/null @@ -1,217 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 test, { expect as playExpect } from '@playwright/test'; -import type { Locator, Page } from 'playwright'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { SettingsPage } from './settings-page'; - -export class CLIToolsPage extends SettingsPage { - readonly main: Locator; - readonly header: Locator; - readonly content: Locator; - readonly heading: Locator; - readonly toolsTable: Locator; - readonly dropDownDialog: Locator; - readonly versionInputField: Locator; - - constructor(page: Page) { - super(page, 'CLI Tools'); - this.main = page.getByRole('region', { name: 'CLI Tools' }); //check name - this.header = this.main.getByRole('region', { name: 'Header' }); - this.heading = this.header.getByRole('heading', { name: 'CLI Tools', exact: true }); - this.content = this.main.getByRole('region', { name: 'Content' }); - this.toolsTable = this.content.getByRole('table', { name: 'cli-tools' }); - this.dropDownDialog = page.getByRole('dialog', { name: 'drop-down-dialog' }); - this.versionInputField = this.dropDownDialog.getByRole('textbox'); - } - - public getToolRow(toolName: string): Locator { - return this.toolsTable.getByRole('row', { name: toolName, exact: true }); - } - - public getInstallButton(toolName: string): Locator { - return this.getToolRow(toolName).getByLabel('Install', { exact: true }); - } - - public getUninstallButton(toolName: string): Locator { - return this.getToolRow(toolName).getByLabel('Uninstall', { exact: true }); - } - - public getUpdateButton(toolName: string): Locator { - return this.getToolRow(toolName) - .getByRole('button') - .and(this.getToolRow(toolName).getByText('Update available', { exact: true })); - } - - public getDowngradeButton(toolName: string): Locator { - return this.getToolRow(toolName) - .getByRole('button') - .and(this.getToolRow(toolName).getByText('Upgrade/Downgrade', { exact: true })); - } - - public getVersionSelectionButton(version: string): Locator { - return this.dropDownDialog.getByRole('button', { name: version }); - } - - public async getCurrentToolVersion(toolName: string): Promise { - return test.step(`Get current version of ${toolName}`, async () => { - if ((await this.getToolRow(toolName).getByLabel('no-cli-version', { exact: true }).count()) > 0) { - return ''; - } - - if ((await this.getToolRow(toolName).getByLabel('cli-version', { exact: true }).count()) === 0) { - return ''; - } - - try { - return await this.getToolRow(toolName).getByLabel('cli-version', { exact: true }).innerText(); - } catch (e) { - console.log(`Could not get version for ${toolName}: ${e}`); - return ''; - } - }); - } - - public async installTool(toolName: string, version: string = '', timeout = 60_000): Promise { - return test.step(`Install ${toolName}`, async () => { - await playExpect(this.getInstallButton(toolName)).toBeEnabled(); - await this.getInstallButton(toolName).click(); - await playExpect(this.dropDownDialog).toBeVisible(); - if (!version) { - version = await this.getLatestVersionNumber(); - } - - await playExpect(this.getVersionSelectionButton(version)).toBeEnabled(); - await this.getVersionSelectionButton(version).click(); - - const confirmationDialog = this.page.getByRole('dialog', { name: toolName }); - try { - await playExpect(confirmationDialog).toBeVisible(); - await handleConfirmationDialog(this.page, toolName); - } catch { - console.log(`Dialog for tool ${toolName} was not visible. Proceeding.`); - } - - await playExpect - .poll(async () => await this.getCurrentToolVersion(toolName), { timeout: timeout }) - .toContain(version); - return this; - }); - } - - public async installToolWithSecondLatestVersion(toolName: string, timeout = 60_000): Promise { - return test.step(`Install ${toolName} with second latest version`, async () => { - await playExpect(this.getInstallButton(toolName)).toBeEnabled(); - await this.getInstallButton(toolName).click(); - await playExpect(this.dropDownDialog).toBeVisible(); - - const version = await this.getSecondLatestVersionNumber(); - await playExpect(this.getVersionSelectionButton(version)).toBeEnabled(); - await this.getVersionSelectionButton(version).click(); - - const confirmationDialog = this.page.getByRole('dialog', { name: toolName }); - try { - await playExpect(confirmationDialog).toBeVisible(); - await handleConfirmationDialog(this.page, toolName); - } catch { - console.log(`Dialog for tool ${toolName} was not visible. Proceeding.`); - } - - await playExpect - .poll(async () => await this.getCurrentToolVersion(toolName), { timeout: timeout }) - .toContain(version); - return this; - }); - } - - public async uninstallTool(toolName: string): Promise { - return test.step(`Uninstall ${toolName}`, async () => { - if ((await this.getUninstallButton(toolName).count()) === 0) { - console.log(`Tool ${toolName} is not installed`); - return this; - } - - await playExpect(this.getUninstallButton(toolName)).toBeEnabled(); - await this.getUninstallButton(toolName).click(); - await handleConfirmationDialog(this.page, 'Uninstall'); - return this; - }); - } - - public async downgradeTool(toolName: string, version: string = '', timeout = 60_000): Promise { - return test.step(`Downgrade ${toolName}`, async () => { - const currentVersion = await this.getCurrentToolVersion(toolName); - if (!currentVersion) { - throw new Error(`Tool ${toolName} is not installed`); - } - - if ((await this.getDowngradeButton(toolName).count()) === 0) { - console.log(`Tool ${toolName} is already in a downgraded version`); - return this; - } - - await playExpect(this.getDowngradeButton(toolName)).toBeEnabled(); - await this.getDowngradeButton(toolName).click(); - await playExpect(this.dropDownDialog).toBeVisible(); - - if (!version) { - version = await this.getSecondLatestVersionNumber(); - } - - await playExpect(this.getVersionSelectionButton(version)).toBeEnabled(); - await this.getVersionSelectionButton(version).click(); - - await playExpect - .poll(async () => await this.getCurrentToolVersion(toolName), { timeout: timeout }) - .toContain(version); - return this; - }); - } - - public async updateTool(toolName: string, timeout = 60_000): Promise { - return test.step(`Update ${toolName}`, async () => { - const currentVersion = await this.getCurrentToolVersion(toolName); - if (!currentVersion) { - throw new Error(`Tool ${toolName} is not installed`); - } - - if ((await this.getUpdateButton(toolName).count()) === 0) { - console.log(`Tool ${toolName} is already on latest`); - return this; - } - - await playExpect(this.getUpdateButton(toolName)).toBeEnabled(); - await this.getUpdateButton(toolName).click(); - await playExpect - .poll(async () => await this.getCurrentToolVersion(toolName), { timeout: timeout }) - .not.toContain(currentVersion); - - return this; - }); - } - - private async getLatestVersionNumber(): Promise { - return await this.dropDownDialog.getByRole('button').first().innerText(); - } - - private async getSecondLatestVersionNumber(): Promise { - return await this.dropDownDialog.getByRole('button').nth(1).innerText(); - } -} diff --git a/tests/playwright/src/model/pages/cluster-creation-base-page.ts b/tests/playwright/src/model/pages/cluster-creation-base-page.ts deleted file mode 100644 index 67084a1fd6..0000000000 --- a/tests/playwright/src/model/pages/cluster-creation-base-page.ts +++ /dev/null @@ -1,70 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export abstract class CreateClusterBasePage extends BasePage { - readonly header: Locator; - readonly content: Locator; - readonly clusterPropertiesInformation: Locator; - readonly clusterCreationButton: Locator; - readonly goBackButton: Locator; - readonly logsButton: Locator; - readonly errorMessage: Locator; - - constructor(page: Page) { - super(page); - this.header = this.page.getByRole('region', { name: 'Header' }); - this.content = this.page.getByRole('region', { name: 'Tab Content' }); - this.clusterPropertiesInformation = this.content.getByRole('form', { - name: 'Properties Information', - }); - this.clusterCreationButton = this.clusterPropertiesInformation.getByRole('button', { name: 'Create', exact: true }); - this.logsButton = this.content.getByRole('button', { name: 'Show Logs' }); - this.goBackButton = this.page.getByRole('button', { - name: 'Go back to resources', - }); - this.errorMessage = this.content.getByRole('alert', { - name: 'Error Message Content', - }); - } - - async createCluster(timeout: number = 300_000): Promise { - return test.step('Create cluster', async () => { - try { - await playExpect(this.clusterCreationButton).toBeEnabled(); - await this.clusterCreationButton.click(); - await this.logsButton.scrollIntoViewIfNeeded(); - await this.logsButton.click(); - await playExpect(this.goBackButton).toBeVisible({ timeout: timeout }); - await this.goBackButton.click(); - } catch (error) { - let errorText = ''; - if (await this.errorMessage.count()) errorText = (await this.errorMessage.textContent()) ?? ''; - - if (error instanceof Error) { - throw new Error(`Failed to create cluster: ${error.message} with dialog error: ${errorText}`); - } - - throw new Error(`Failed to create cluster: ${error} with dialog error: ${errorText}`); - } - }); - } -} diff --git a/tests/playwright/src/model/pages/command-palette.ts b/tests/playwright/src/model/pages/command-palette.ts deleted file mode 100644 index c5c412b6c5..0000000000 --- a/tests/playwright/src/model/pages/command-palette.ts +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************** - * 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 Locator, type Page } from '@playwright/test'; -import { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export class CommandPalette extends BasePage { - readonly commandPaletteInputField: Locator; - constructor(page: Page) { - super(page); - this.commandPaletteInputField = this.page.getByLabel('Command palette command input', { exact: true }); - } - - async executeCommand(command: string): Promise { - if (!command) { - throw new Error('Command is required'); - } - - if (!(await this.commandPaletteInputField.isVisible())) { - await this.page.keyboard.press('F1'); - } - - await playExpect(this.commandPaletteInputField).toBeVisible(); - await this.commandPaletteInputField.pressSequentially(command, { delay: 25 }); - await this.commandPaletteInputField.press('Enter'); - } -} diff --git a/tests/playwright/src/model/pages/compose-onboarding/compose-failed-page.ts b/tests/playwright/src/model/pages/compose-onboarding/compose-failed-page.ts deleted file mode 100644 index 1961e15055..0000000000 --- a/tests/playwright/src/model/pages/compose-onboarding/compose-failed-page.ts +++ /dev/null @@ -1,30 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from 'playwright'; - -import { ComposeOnboardingPage } from './compose-onboarding-page'; - -export class ComposeFailedPage extends ComposeOnboardingPage { - readonly tryAgainButton: Locator; - - constructor(page: Page) { - super(page); - this.tryAgainButton = this.mainPage.getByRole('button', { name: 'Try again' }); - } -} diff --git a/tests/playwright/src/model/pages/compose-onboarding/compose-local-install-page.ts b/tests/playwright/src/model/pages/compose-onboarding/compose-local-install-page.ts deleted file mode 100644 index 91d67b5a6d..0000000000 --- a/tests/playwright/src/model/pages/compose-onboarding/compose-local-install-page.ts +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from 'playwright'; - -import { ComposeOnboardingPage } from './compose-onboarding-page'; - -export class ComposeLocalInstallPage extends ComposeOnboardingPage { - readonly wideDownloadAvailableMessage: Locator; - - constructor(page: Page) { - super(page); - this.wideDownloadAvailableMessage = this.mainPage.getByText( - 'The next step will install Compose system-wide. You will be prompted for system', - ); - } -} diff --git a/tests/playwright/src/model/pages/compose-onboarding/compose-onboarding-page.ts b/tests/playwright/src/model/pages/compose-onboarding/compose-onboarding-page.ts deleted file mode 100644 index 45c2a21412..0000000000 --- a/tests/playwright/src/model/pages/compose-onboarding/compose-onboarding-page.ts +++ /dev/null @@ -1,30 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from 'playwright'; - -import { OnboardingPage } from '../onboarding-page'; - -export class ComposeOnboardingPage extends OnboardingPage { - readonly heading: Locator; - - constructor(page: Page) { - super(page); - this.heading = this.page.getByRole('heading', { name: 'Compose Setup Header' }); - } -} diff --git a/tests/playwright/src/model/pages/compose-onboarding/compose-version-page.ts b/tests/playwright/src/model/pages/compose-onboarding/compose-version-page.ts deleted file mode 100644 index 695898bbe2..0000000000 --- a/tests/playwright/src/model/pages/compose-onboarding/compose-version-page.ts +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from 'playwright'; - -import { ComposeOnboardingPage } from './compose-onboarding-page'; - -export class ComposeVersionPage extends ComposeOnboardingPage { - readonly versionStatusMessage: Locator; - - constructor(page: Page) { - super(page); - this.versionStatusMessage = this.mainPage.getByText( - /Compose will be downloaded in the next step \(Version v[0-9.]+\). Want to download/, - { exact: true }, - ); - } - - async getVersion(): Promise { - const versionInfoText = await this.versionStatusMessage.textContent(); - let composeVersion = ''; - - const matches = versionInfoText?.match(/v\d+(\.\d+)+/); - if (matches) { - composeVersion = matches[0]; - } - return composeVersion; - } -} diff --git a/tests/playwright/src/model/pages/compose-onboarding/compose-wide-install-page.ts b/tests/playwright/src/model/pages/compose-onboarding/compose-wide-install-page.ts deleted file mode 100644 index 0dbb44a580..0000000000 --- a/tests/playwright/src/model/pages/compose-onboarding/compose-wide-install-page.ts +++ /dev/null @@ -1,30 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from 'playwright'; - -import { ComposeOnboardingPage } from './compose-onboarding-page'; - -export class ComposeWideInstallPage extends ComposeOnboardingPage { - readonly composeCommandMessage: Locator; - - constructor(page: Page) { - super(page); - this.composeCommandMessage = this.mainPage.getByText('$ podman compose up', { exact: true }); - } -} diff --git a/tests/playwright/src/model/pages/container-details-page.ts b/tests/playwright/src/model/pages/container-details-page.ts deleted file mode 100644 index 6b2bb7dedb..0000000000 --- a/tests/playwright/src/model/pages/container-details-page.ts +++ /dev/null @@ -1,113 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { ContainerState } from '../core/states'; -import { ContainersPage } from './containers-page'; -import { DeployToKubernetesPage } from './deploy-to-kubernetes-page'; -import { DetailsPage } from './details-page'; - -export class ContainerDetailsPage extends DetailsPage { - readonly stopButton: Locator; - readonly deleteButton: Locator; - readonly imageLink: Locator; - readonly deployButton: Locator; - readonly startButton: Locator; - readonly terminalInput: Locator; - readonly terminalContent: Locator; - - static readonly SUMMARY_TAB = 'Summary'; - static readonly LOGS_TAB = 'Logs'; - static readonly KUBE_TAB = 'Kube'; - static readonly TERMINAL_TAB = 'Terminal'; - static readonly INSPECT_TAB = 'Inspect'; - - constructor(page: Page, name: string) { - super(page, name); - this.stopButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Stop Container')); - this.deleteButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Delete Container')); - this.imageLink = this.header.getByRole('link', { name: 'Image Details' }); - this.deployButton = this.controlActions.getByRole('button', { - name: 'Deploy to Kubernetes', - }); - this.startButton = this.controlActions.getByRole('button', { - name: 'Start Container', - exact: true, - }); - - this.terminalInput = this.tabContent.getByLabel('Terminal input'); - this.terminalContent = this.tabContent.locator('.xterm-rows'); - } - - async getState(): Promise { - return test.step('Get container state', async () => { - const currentState = await this.header.getByRole('status').getAttribute('title'); - for (const state of Object.values(ContainerState)) { - if (currentState === state) return state; - } - - return ContainerState.Unknown; - }); - } - - async stopContainer(): Promise { - return test.step('Stop container', async () => { - await playExpect(this.stopButton).toBeEnabled(); - await this.stopButton.click(); - }); - } - - async deleteContainer(): Promise { - return test.step('Delete container', async () => { - await playExpect(this.deleteButton).toBeEnabled(); - await this.deleteButton.click(); - await handleConfirmationDialog(this.page); - return new ContainersPage(this.page); - }); - } - - async getContainerPort(): Promise { - return test.step('Get container port', async () => { - await this.activateTab(ContainerDetailsPage.SUMMARY_TAB); - const summaryTable = this.tabContent.getByRole('table'); - const portsRow = summaryTable.locator('tr:has-text("Ports")'); - const portsCell = portsRow.getByRole('cell').nth(1); - await playExpect(portsCell).toBeVisible(); - return await portsCell.innerText(); - }); - } - - async openDeployToKubernetesPage(): Promise { - return test.step('Open Deploy to Kubernetes page', async () => { - await playExpect(this.deployButton).toBeVisible(); - await this.deployButton.click(); - return new DeployToKubernetesPage(this.page); - }); - } - - async executeCommandInTerminal(command: string): Promise { - await this.activateTab(ContainerDetailsPage.TERMINAL_TAB); - - await playExpect(this.terminalInput).toBeVisible(); - await this.terminalInput.pressSequentially(command); - await this.terminalInput.press('Enter'); - } -} diff --git a/tests/playwright/src/model/pages/containers-page.ts b/tests/playwright/src/model/pages/containers-page.ts deleted file mode 100644 index e0753109ee..0000000000 --- a/tests/playwright/src/model/pages/containers-page.ts +++ /dev/null @@ -1,222 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { ContainerState } from '../core/states'; -import { ContainerDetailsPage } from './container-details-page'; -import { CreatePodsPage } from './create-pod-page'; -import { MainPage } from './main-page'; - -export class ContainersPage extends MainPage { - readonly pruneContainersButton: Locator; - readonly createContainerButton: Locator; - readonly playKubernetesYAMLButton: Locator; - readonly pruneConfirmationButton: Locator; - readonly runAllContainersButton: Locator; - - constructor(page: Page) { - super(page, 'containers'); - this.pruneContainersButton = this.additionalActions.getByRole('button', { - name: 'Prune', - }); - this.createContainerButton = this.additionalActions.getByRole('button', { - name: 'Create', - }); - this.playKubernetesYAMLButton = this.additionalActions.getByRole('button', { - name: 'Play Kubernetes YAML', - }); - this.pruneConfirmationButton = this.page.getByRole('button', { - name: 'Yes', - }); - this.runAllContainersButton = this.page.getByLabel('Run selected containers and pods'); - } - - async openContainersDetails(name: string): Promise { - return test.step(`Open Container: ${name} details`, async () => { - const containerRow = await this.getContainerRowByName(name); - if (containerRow === undefined) { - throw Error(`Container: '${name}' does not exist`); - } - const containerRowName = containerRow.getByRole('cell').nth(3); - await containerRowName.click(); - return new ContainerDetailsPage(this.page, name); - }); - } - - async startContainer(containerName: string): Promise { - return test.step(`Start Container: ${containerName}`, async () => { - const containerRow = await this.getContainerRowByName(containerName); - if (containerRow === undefined) { - throw Error(`Container: '${containerName}' does not exist`); - } - const containerRowStartButton = containerRow.getByRole('button', { - name: 'Start Container', - }); - await playExpect(containerRowStartButton).toBeVisible(); - await containerRowStartButton.click(); - return this; - }); - } - - async stopContainer(containerName: string): Promise { - return test.step(`Stop Container: ${containerName}`, async () => { - const containerRow = await this.getContainerRowByName(containerName); - if (containerRow === undefined) { - throw Error(`Container: '${containerName}' does not exist`); - } - const containerRowStopButton = containerRow.getByRole('button', { - name: 'Stop Container', - }); - await playExpect(containerRowStopButton).toBeVisible(); - await containerRowStopButton.click(); - return this; - }); - } - - async deleteContainer(containerName: string): Promise { - return test.step(`Delete Container: ${containerName}`, async () => { - const containerRow = await this.getContainerRowByName(containerName); - if (containerRow === undefined) { - throw Error(`Container: '${containerName}' does not exist`); - } - const containerRowDeleteButton = containerRow.getByRole('button', { - name: 'Delete Container', - }); - await playExpect(containerRowDeleteButton).toBeVisible(); - await playExpect(containerRowDeleteButton).toBeEnabled(); - await containerRowDeleteButton.click(); - await handleConfirmationDialog(this.page); - return new ContainersPage(this.page); - }); - } - - async stopContainerFromDetails(container: string): Promise { - return test.step(`Stop Container ${container} from details page`, async () => { - const containerDetailsPage = await this.openContainersDetails(container); - await playExpect(containerDetailsPage.heading).toBeVisible(); - await playExpect(containerDetailsPage.heading).toContainText(container); - playExpect(await containerDetailsPage.getState()).toContain(ContainerState.Running); - await containerDetailsPage.stopContainer(); - return containerDetailsPage; - }); - } - - async getContainerRowByName(name: string): Promise { - return this.getRowByName(name); - } - - async uncheckAllContainers(): Promise { - return test.step('Uncheck all containers', async () => { - let containersTable; - try { - containersTable = await this.getTable(); - await playExpect(containersTable).toBeVisible(); - const controlRow = containersTable.getByRole('row').first(); - await playExpect(controlRow).toBeAttached(); - const checkboxColumnHeader = controlRow.getByRole('columnheader').nth(1); - await playExpect(checkboxColumnHeader).toBeAttached(); - const containersToggle = checkboxColumnHeader.getByTitle('Toggle all'); - await playExpect(containersToggle).toBeAttached(); - // cannot be resolved using getByRole('img') ; const containersToggleSvg = containersToggle.getByRole('img'); - - if ((await containersToggle.innerHTML()).includes('pd-input-checkbox-indeterminate')) { - await containersToggle.click(); - } - - if ((await containersToggle.innerHTML()).includes('pd-input-checkbox-checked')) { - await containersToggle.click(); - } - - playExpect(await containersToggle.innerHTML()).toContain('pd-input-checkbox-unchecked'); - } catch (err) { - console.log(`Exception caught on containers page when checking cells for unchecking with message: ${err}`); - } - }); - } - - async checkAllContainers(): Promise { - return test.step('Checks all containers', async () => { - try { - const containersTable = await this.getTable(); - await playExpect(containersTable).toBeVisible(); - const controlRow = containersTable.getByRole('row').first(); - await playExpect(controlRow).toBeAttached(); - const checkboxColumnHeader = controlRow.getByRole('columnheader').nth(1); - await playExpect(checkboxColumnHeader).toBeAttached(); - const containersToggle = checkboxColumnHeader.getByTitle('Toggle all'); - await playExpect(containersToggle).toBeAttached(); - - if ((await containersToggle.innerHTML()).includes('pd-input-checkbox-unchecked')) { - await containersToggle.click(); - } - - await playExpect - .poll(async () => containersToggle.innerHTML(), { timeout: 15_000 }) - .toContain('pd-input-checkbox-checked'); - } catch (err) { - console.log(`Exception caught on containers page when checking cells with message: ${err}`); - } - }); - } - - async containerExists(name: string): Promise { - return (await this.getContainerRowByName(name)) !== undefined; - } - - async openCreatePodPage(names: string[]): Promise { - return test.step(`Open Create Pod page for containers: ${names}`, async () => { - for (const containerName of names) { - const row = await this.getContainerRowByName(containerName); - if (row === undefined) { - throw Error('Container cannot be podified'); - } - await row.getByRole('cell').nth(1).click(); - } - await this.page.getByRole('button', { name: `Create Pod` }).click(); - return new CreatePodsPage(this.page); - }); - } - - async pruneContainers(): Promise { - return test.step('Prune Containers', async () => { - await this.pruneContainersButton.click(); - await handleConfirmationDialog(this.page, 'Prune'); - return this; - }); - } - - async getContainerImage(name: string): Promise { - const container = await this.getContainerRowByName(name); - const image = await container?.getByRole('cell').nth(5).textContent(); - if (image) { - return image; - } - return ''; - } - - async startAllContainers(): Promise { - return test.step('Start all containers', async () => { - await this.checkAllContainers(); - await this.runAllContainersButton.click(); - return this; - }); - } -} diff --git a/tests/playwright/src/model/pages/create-kind-cluster-page.ts b/tests/playwright/src/model/pages/create-kind-cluster-page.ts deleted file mode 100644 index 8b6b7e66c0..0000000000 --- a/tests/playwright/src/model/pages/create-kind-cluster-page.ts +++ /dev/null @@ -1,126 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { fillTextbox } from '/@/utility/operations'; - -import type { KindClusterOptions } from '../core/types'; -import { CreateClusterBasePage } from './cluster-creation-base-page'; - -export class CreateKindClusterPage extends CreateClusterBasePage { - readonly clusterNameField: Locator; - readonly controllerCheckbox: Locator; - readonly providerType: Locator; - readonly httpPort: Locator; - readonly httpsPort: Locator; - readonly containerImage: Locator; - readonly configFileInput: Locator; - readonly warning: Locator; - - constructor(page: Page) { - super(page); - this.clusterNameField = this.clusterPropertiesInformation.getByRole('textbox', { name: 'Name', exact: true }); - // Locator for the parent element of the ingress controller checkbox, used to change its value - this.controllerCheckbox = this.clusterPropertiesInformation - .getByRole('checkbox', { - name: 'Setup an ingress controller', - }) - .locator('..'); - this.providerType = this.clusterPropertiesInformation.getByLabel('Provider Type'); - this.httpPort = this.clusterPropertiesInformation.getByLabel('HTTP Port'); - this.httpsPort = this.clusterPropertiesInformation.getByLabel('HTTPS Port'); - this.containerImage = this.clusterPropertiesInformation.getByPlaceholder('Leave empty for using latest.'); - this.configFileInput = this.clusterPropertiesInformation.getByRole('textbox', { - name: 'Custom path to Kind config file (Default is blank)', - exact: true, - }); - this.warning = this.page.getByRole('alert', { name: 'warning' }); - } - - private async validateKindClusterDefaultSettings(): Promise { - return test.step('Validate kind cluster default settings', async () => { - await playExpect(this.configFileInput).toBeEmpty(); - await playExpect(this.clusterNameField).toHaveValue('kind-cluster'); - await playExpect(this.providerType).toHaveText('podman'); - await playExpect(this.httpPort).toHaveValue('9090'); - await playExpect(this.httpsPort).toHaveValue('9443'); - await playExpect(this.controllerCheckbox).toBeChecked(); - await playExpect(this.containerImage).toBeEmpty(); - }); - } - - public async createKindCluster( - clusterName: string = 'kind-cluster', - { configFilePath, providerType, httpPort, httpsPort, useIngressController, containerImage }: KindClusterOptions = { - useIngressController: true, - }, - timeout?: number, - ): Promise { - return test.step('Create kind cluster', async () => { - await this.validateKindClusterDefaultSettings(); - - // Use the default cluster name if a custom config file is used; the default cluster name should be ignored in this case. - if (configFilePath) { - await this.configFileInput.evaluate(node => node.removeAttribute('readonly')); - await this.configFileInput.fill(configFilePath); - await playExpect(this.warning).toBeVisible(); - await playExpect(this.warning).toContainText( - 'By specifying a config file, all other options will be ignored except for ingress controller deployment.', - ); - } else { - await fillTextbox(this.clusterNameField, clusterName); - } - - if (providerType && providerType !== 'podman') { - await this.providerType.click(); - const providerTypeButton = this.clusterPropertiesInformation.getByRole('button', { - name: providerType, - exact: true, - }); - await playExpect(providerTypeButton).toBeEnabled(); - await providerTypeButton.click(); - await playExpect(this.providerType).toHaveText(providerType); - } - - if (httpPort) { - await fillTextbox(this.httpPort, httpPort); - await playExpect(this.httpPort).toHaveText(httpPort); - } - if (httpsPort) { - await fillTextbox(this.httpsPort, httpsPort); - await playExpect(this.httpsPort).toHaveText(httpsPort); - } - - await playExpect(this.controllerCheckbox).toBeEnabled(); - if (!useIngressController) { - await this.controllerCheckbox.uncheck(); - await playExpect(this.controllerCheckbox).not.toBeChecked(); - } else { - await this.controllerCheckbox.check(); - await playExpect(this.controllerCheckbox).toBeChecked(); - } - - if (containerImage) { - await fillTextbox(this.containerImage, containerImage); - await playExpect(this.containerImage).toHaveText(containerImage); - } - await this.createCluster(timeout); - }); - } -} diff --git a/tests/playwright/src/model/pages/create-machine-page.ts b/tests/playwright/src/model/pages/create-machine-page.ts deleted file mode 100644 index 0cb47a7fbb..0000000000 --- a/tests/playwright/src/model/pages/create-machine-page.ts +++ /dev/null @@ -1,94 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { MachineCreationForm } from './forms/machine-creation-form'; -import { ResourcesPage } from './resources-page'; - -export class CreateMachinePage extends BasePage { - readonly heading: Locator; - readonly machineCreationForm: MachineCreationForm; - readonly closeButton: Locator; - - constructor(page: Page) { - super(page); - this.heading = this.page.getByRole('heading', { - name: 'Create Podman Machine', - }); - this.machineCreationForm = new MachineCreationForm(this.page); - this.closeButton = this.page.getByRole('button', { name: 'Close' }); - } - - async createMachine( - machineName: string, - { isRootful = true, enableUserNet = false, startNow = true, setAsDefault = true }, - ): Promise { - return test.step(`Create Podman Machine: ${machineName}`, async () => { - await this.machineCreationForm.setupAndCreateMachine(machineName, { - isRootful, - enableUserNet, - startNow, - }); - - const successfulCreationMessage = this.page.getByText('Successful operation'); - const goBackToResourcesButton = this.page.getByRole('button', { - name: 'Go back to resources', - }); - - await playExpect(successfulCreationMessage).toBeVisible({ - timeout: 120_000, - }); - await playExpect(goBackToResourcesButton).toBeVisible(); - - try { - await this.handleConnectionDialog(machineName, setAsDefault); - } catch (error) { - console.log('No handling dialog displayed', error); - } - - await playExpect(goBackToResourcesButton).toBeEnabled(); - await goBackToResourcesButton.click(); - return new ResourcesPage(this.page); - }); - } - - async handleConnectionDialog(machineName: string, setAsDefault: boolean): Promise { - return test.step('Handle Podman Machine connection dialog', async () => { - const connectionDialog = this.page.getByRole('dialog', { - name: 'Podman', - }); - await playExpect(connectionDialog).toBeVisible({ timeout: 10_000 }); - - const dialogMessage = connectionDialog.getByLabel('Dialog Message'); - await playExpect(dialogMessage).toHaveText( - new RegExp( - `Podman Machine '${machineName}' is running but not the default machine .+ Do you want to set it as default?`, - ), - ); - - const handleButtonName = setAsDefault ? 'Yes' : 'Ignore'; - const handleButton = connectionDialog.getByRole('button', { - name: handleButtonName, - }); - await handleButton.click(); - }); - } -} diff --git a/tests/playwright/src/model/pages/create-pod-page.ts b/tests/playwright/src/model/pages/create-pod-page.ts deleted file mode 100644 index a3d7cded53..0000000000 --- a/tests/playwright/src/model/pages/create-pod-page.ts +++ /dev/null @@ -1,51 +0,0 @@ -/********************************************************************** - * 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 { type Locator, type Page, test } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { PodsPage } from './pods-page'; - -export class CreatePodsPage extends BasePage { - readonly heading: Locator; - readonly closeLink: Locator; - readonly podNameBox: Locator; - readonly closeButton: Locator; - readonly createPodButton: Locator; - - constructor(page: Page) { - super(page); - this.heading = this.page.getByRole('heading', { - name: 'Copy containers to a pod', - }); - this.closeLink = this.page.getByRole('link', { name: 'Close' }); - this.podNameBox = this.page.getByRole('textbox', { name: 'Pod name' }); - this.closeButton = this.page.getByRole('button', { name: 'Close' }); - this.createPodButton = this.page.getByRole('button', { - name: 'Create Pod', - }); - } - - async createPod(podName: string): Promise { - return test.step(`Create pod ${podName}`, async () => { - await this.podNameBox.fill(podName); - await this.createPodButton.click(); - return new PodsPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/create-volume-page.ts b/tests/playwright/src/model/pages/create-volume-page.ts deleted file mode 100644 index a9f74732aa..0000000000 --- a/tests/playwright/src/model/pages/create-volume-page.ts +++ /dev/null @@ -1,55 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { VolumesPage } from './volumes-page'; - -export class CreateVolumePage extends BasePage { - readonly heading: Locator; - readonly closeLink: Locator; - readonly volumeNameBox: Locator; - readonly doneButton: Locator; - readonly closeButton: Locator; - readonly createVolumeButton: Locator; - - constructor(page: Page) { - super(page); - this.heading = this.page.getByRole('heading', { name: 'Create a volume' }); - this.closeLink = this.page.getByRole('link', { name: 'Close' }); - this.volumeNameBox = this.page.getByRole('textbox', { - name: 'Volume name', - }); - this.doneButton = this.page.getByRole('button', { name: 'Done' }); - this.closeButton = this.page.getByRole('button', { name: 'Close' }); - this.createVolumeButton = this.page.getByRole('button', { name: 'Create' }); - } - - async createVolume(name: string): Promise { - return test.step(`Create volume ${name}`, async () => { - await this.volumeNameBox.fill(name); - await playExpect(this.createVolumeButton).toBeEnabled(); - await this.createVolumeButton.click(); - await playExpect(this.doneButton).toBeEnabled({ timeout: 30000 }); - await this.doneButton.click(); - return new VolumesPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/dashboard-page.ts b/tests/playwright/src/model/pages/dashboard-page.ts deleted file mode 100644 index 9172ae9038..0000000000 --- a/tests/playwright/src/model/pages/dashboard-page.ts +++ /dev/null @@ -1,79 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export class DashboardPage extends BasePage { - readonly mainPage: Locator; - readonly header: Locator; - readonly content: Locator; - readonly heading: Locator; - readonly notificationsBox: Locator; - readonly featuredExtensions: Locator; - // dev sandbox - readonly devSandboxProvider: Locator; - readonly devSandboxBox: Locator; - readonly devSandboxStatusLabel: Locator; - // openshift local - readonly openshiftLocalProvider: Locator; - readonly openshiftLocalBox: Locator; - readonly openshiftLocalStatusLabel: Locator; - readonly transitioningState: Locator; - - // podman/machine - readonly podmanProvider: Locator; - readonly podmanStatusLabel: Locator; - readonly podmanInitilizeAndStartButton: Locator; - - // contants - readonly ACTUAL_STATE = 'Actual State'; - readonly CONNECTION_STATUS_LABEL = 'Connection Status Label'; - - constructor(page: Page) { - super(page); - this.mainPage = page.getByRole('region', { name: 'Dashboard' }); - this.header = this.mainPage.getByRole('region', { name: 'header' }); - this.content = this.mainPage.getByRole('region', { name: 'content' }); - this.heading = page.getByRole('heading', { name: 'Dashboard' }); - - this.notificationsBox = this.content.getByRole('region', { name: 'Notifications Box' }); - this.featuredExtensions = page.getByRole('region', { name: 'FeaturedExtensions' }); - - // Dev Sandbox locators - this.devSandboxProvider = page.getByRole('region', { name: 'Developer Sandbox Provider' }); - this.devSandboxBox = this.featuredExtensions.getByLabel('Developer Sandbox'); - this.devSandboxStatusLabel = this.devSandboxProvider.getByLabel(this.CONNECTION_STATUS_LABEL); - - // OpenShift Local locators - this.openshiftLocalProvider = page.getByRole('region', { name: 'OpenShift Local Provider' }); - this.openshiftLocalBox = this.featuredExtensions.getByLabel('OpenShift Local'); - this.openshiftLocalStatusLabel = this.openshiftLocalProvider.getByLabel(this.CONNECTION_STATUS_LABEL); - - // Podman/Machine Provider locators - this.podmanProvider = page.getByRole('region', { name: 'Podman Provider' }); - this.podmanInitilizeAndStartButton = this.podmanProvider.getByRole('button', { name: 'Initialize and start ' }); - this.transitioningState = this.podmanProvider.getByLabel('Transitioning State'); - this.podmanStatusLabel = this.podmanProvider.getByLabel(this.CONNECTION_STATUS_LABEL); - } - - public getPodmanStatusLocator(): Locator { - return this.content.getByRole('region', { name: 'Podman Provider' }); - } -} diff --git a/tests/playwright/src/model/pages/deploy-to-kubernetes-page.ts b/tests/playwright/src/model/pages/deploy-to-kubernetes-page.ts deleted file mode 100644 index 2cea26afb6..0000000000 --- a/tests/playwright/src/model/pages/deploy-to-kubernetes-page.ts +++ /dev/null @@ -1,163 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import type { DeployPodOptions } from '../core/types'; -import { BasePage } from './base-page'; - -export class DeployToKubernetesPage extends BasePage { - readonly content: Locator; - readonly podName: Locator; - readonly kubernetesContext: Locator; - readonly deployButton: Locator; - readonly doneButton: Locator; - readonly namespaceCombobox: Locator; - readonly servicesCheckbox: Locator; - readonly restrictedContextCheckbox: Locator; - readonly createIngressCheckbox: Locator; - readonly createOpenShiftRoutesCheckbox: Locator; - readonly selectPortCombobox: Locator; - readonly deploymentStatus: Locator; - - constructor(page: Page) { - super(page); - this.content = this.page.getByRole('region', { name: 'Tab Content' }); - this.podName = this.content.getByRole('textbox', { name: 'Pod Name' }); - this.kubernetesContext = this.content.getByRole('textbox', { - name: 'Kubernetes Context', - }); - this.namespaceCombobox = this.content.getByRole('combobox', { - name: 'Select a Kubernetes Namespace', - }); - this.deployButton = this.content.getByRole('button', { name: 'Deploy' }); - this.doneButton = this.content.getByRole('button', { name: 'Done' }); - this.servicesCheckbox = this.content.getByRole('checkbox', { - name: 'Use Services', - }); - this.restrictedContextCheckbox = this.content.getByRole('checkbox', { - name: 'Use restricted security context', - }); - this.createIngressCheckbox = this.content.getByRole('checkbox', { - name: 'Create Ingress', - }); - this.createOpenShiftRoutesCheckbox = this.content.getByRole('checkbox', { - name: 'Use Routes', - }); - this.selectPortCombobox = this.content.getByRole('combobox', { - name: 'Select a Port', - }); - this.deploymentStatus = this.content.getByRole('region', { - name: 'Pod Deployment Status Info', - }); - } - - public async deployPod( - name: string, - { - useKubernetesServices, - useRestrictedSecurityContext, - useKubernetesIngress, - containerExposedPort, - isOpenShiftCluster, - useOpenShiftRoutes, - }: DeployPodOptions = {}, - context: string, - namespace: string = 'default', - timeout: number = 100_000, - ): Promise { - return test.step(`Deploy pod ${name}`, async () => { - await playExpect(this.podName).toBeVisible(); - await this.podName.clear(); - await this.podName.fill(name); - - await playExpect(this.servicesCheckbox).toBeEnabled(); - if (useKubernetesServices) { - await this.servicesCheckbox.check(); - await playExpect(this.servicesCheckbox).toBeChecked(); - } else { - await this.servicesCheckbox.uncheck(); - await playExpect(this.servicesCheckbox).not.toBeChecked(); - } - - await playExpect(this.restrictedContextCheckbox).toBeEnabled(); - if (useRestrictedSecurityContext) { - await this.restrictedContextCheckbox.check(); - await playExpect(this.restrictedContextCheckbox).toBeChecked(); - } else { - await this.restrictedContextCheckbox.uncheck(); - await playExpect(this.restrictedContextCheckbox).not.toBeChecked(); - } - - if (isOpenShiftCluster) { - await playExpect(this.createOpenShiftRoutesCheckbox).toBeEnabled(); - if (useOpenShiftRoutes) { - await this.createOpenShiftRoutesCheckbox.check(); - await playExpect(this.createOpenShiftRoutesCheckbox).toBeChecked(); - } else { - await this.createOpenShiftRoutesCheckbox.uncheck(); - await playExpect(this.createOpenShiftRoutesCheckbox).not.toBeChecked(); - } - } else { - await playExpect(this.createIngressCheckbox).toBeEnabled(); - if (useKubernetesIngress) { - await this.createIngressCheckbox.check(); - await playExpect(this.createIngressCheckbox).toBeChecked(); - if (containerExposedPort) { - await this.selectExposedPort(containerExposedPort); - } - } else { - await this.createIngressCheckbox.uncheck(); - await playExpect(this.createIngressCheckbox).not.toBeChecked(); - } - } - - await this.kubernetesContext.scrollIntoViewIfNeeded(); - await playExpect(this.kubernetesContext).toHaveValue(context); - await playExpect(this.namespaceCombobox).toBeVisible(); - const currentNamespace = await this.namespaceCombobox.inputValue(); - if (currentNamespace !== namespace) { - const namespaceOptions = await this.namespaceCombobox.locator('option').allInnerTexts(); - if (!namespaceOptions.includes(namespace)) { - throw new Error(`${namespace} doesn't exist`); - } - await this.namespaceCombobox.selectOption({ value: namespace }); - await playExpect(this.namespaceCombobox).toHaveValue(namespace); - } - - await playExpect(this.deployButton).toBeEnabled(); - await this.deployButton.click(); - await playExpect(this.deploymentStatus).toBeVisible({ timeout: 30_000 }); - await this.deploymentStatus.scrollIntoViewIfNeeded(); - await playExpect(this.doneButton).toBeVisible({ timeout: timeout }); - }); - } - - private async selectExposedPort(containerExposedPort: string): Promise { - return test.step(`Select exposed port: ${containerExposedPort}`, async () => { - await playExpect(this.selectPortCombobox).toBeVisible(); - const exposedPorts = await this.selectPortCombobox.locator('option').allInnerTexts(); - if (!exposedPorts.includes(containerExposedPort)) { - throw new Error(`Port: ${containerExposedPort} doesn't exist`); - } - await this.selectPortCombobox.selectOption({ - value: containerExposedPort, - }); - await playExpect(this.selectPortCombobox).toHaveValue(containerExposedPort); - }); - } -} diff --git a/tests/playwright/src/model/pages/details-page.ts b/tests/playwright/src/model/pages/details-page.ts deleted file mode 100644 index 886df8dbd8..0000000000 --- a/tests/playwright/src/model/pages/details-page.ts +++ /dev/null @@ -1,61 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 test, { type Locator, type Page } from '@playwright/test'; - -import { waitUntil } from '../../utility/wait'; -import { BasePage } from './base-page'; - -export abstract class DetailsPage extends BasePage { - readonly header: Locator; - readonly tabs: Locator; - readonly tabContent: Locator; - readonly controlActions: Locator; - readonly closeButton: Locator; - readonly backLink: Locator; - readonly pageName: Locator; - readonly resourceName: string; - readonly heading: Locator; - readonly breadcrumb: Locator; - - constructor(page: Page, resourceName: string) { - super(page); - this.resourceName = resourceName; - - this.tabContent = page.getByRole('region', { name: 'Tab Content' }); - this.header = page.getByRole('region', { name: 'Header' }); - this.tabs = page.getByRole('region', { name: 'Tabs' }); - this.breadcrumb = this.header.getByRole('navigation', { name: 'Breadcrumb' }); - this.controlActions = this.header.getByRole('group', { name: 'Control Actions' }); - this.heading = this.header.getByRole('heading', { name: this.resourceName }); - this.closeButton = this.breadcrumb.getByRole('button', { name: 'Close' }); - this.backLink = this.breadcrumb.getByRole('link', { name: 'Back' }); - this.pageName = this.breadcrumb.getByRole('region', { name: 'Page Name' }); - } - - async activateTab(tabName: string): Promise { - return test.step(`Activate tab: ${tabName}`, async () => { - const tabItem = this.tabs.getByRole('link', { name: tabName, exact: true }); - await waitUntil(async () => await tabItem.isVisible(), { - message: `Tab ${tabName} does not exist currently, maybe entry was deleted!`, - }); - await tabItem.click(); - return this; - }); - } -} diff --git a/tests/playwright/src/model/pages/docker-compatibility-page.ts b/tests/playwright/src/model/pages/docker-compatibility-page.ts deleted file mode 100644 index dfa0a0625f..0000000000 --- a/tests/playwright/src/model/pages/docker-compatibility-page.ts +++ /dev/null @@ -1,54 +0,0 @@ -/********************************************************************** - * 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 { Locator, Page } from 'playwright'; - -import { expect as playExpect } from '../../utility/fixtures'; -import { SettingsPage } from './settings-page'; - -export class DockerCompatibilityPage extends SettingsPage { - readonly heading: Locator; - readonly serverInformationBox: Locator; - readonly podmanComposeCLICard: Locator; - readonly dockerCLICard: Locator; - readonly dockerContextDropdownMenu: Locator; - readonly podmanListeningLabel: Locator; - - constructor(page: Page) { - super(page, 'Docker Compatibility'); - this.heading = this.header - .getByRole('heading', { name: 'Title' }) - .and(this.header.getByText('Docker Compatibility')); - this.serverInformationBox = this.content.getByRole('status', { name: 'Server information' }); - this.podmanComposeCLICard = this.content.getByRole('list', { name: 'podman-desktop.podman' }); - this.dockerCLICard = this.content.getByRole('list', { name: 'podman-desktop.docker' }); - this.dockerContextDropdownMenu = this.dockerCLICard.getByRole('button', { - name: 'select-property-docker.cli.context', - }); - this.podmanListeningLabel = this.content.getByText('podman is listening'); - } - - public async socketIsReachable(): Promise { - try { - await playExpect(this.podmanListeningLabel).toBeVisible(); - return await this.podmanListeningLabel.isVisible(); - } catch (error) { - return false; - } - } -} diff --git a/tests/playwright/src/model/pages/experimental-page.ts b/tests/playwright/src/model/pages/experimental-page.ts deleted file mode 100644 index f92aaef1d5..0000000000 --- a/tests/playwright/src/model/pages/experimental-page.ts +++ /dev/null @@ -1,65 +0,0 @@ -/********************************************************************** - * 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 { expect as playExpect } from '@playwright/test'; -import type { Locator, Page } from 'playwright'; - -import { SettingsPage } from './settings-page'; - -export class ExperimentalPage extends SettingsPage { - readonly heading: Locator; - readonly enableAllExperimentalFeaturesCheckbox: Locator; - readonly enableAllExperimentalFeaturesButton: Locator; - readonly statusBarProvidersCheckbox: Locator; - - constructor(page: Page) { - super(page, 'Experimental'); - this.heading = this.header.getByRole('heading', { name: 'Title' }).and(this.header.getByText('Experimental')); - this.enableAllExperimentalFeaturesCheckbox = this.content - .getByRole('checkbox') - .and(this.content.locator('#input-experimental-enable-all')); - this.enableAllExperimentalFeaturesButton = this.enableAllExperimentalFeaturesCheckbox.locator('..'); - this.statusBarProvidersCheckbox = this.content.getByRole('checkbox', { name: 'Show providers in the status bar' }); - } - - public async enableAllExperimentalFeatures(): Promise { - await playExpect(this.enableAllExperimentalFeaturesCheckbox).toBeVisible(); - if (await this.enableAllExperimentalFeaturesCheckbox.isChecked()) return; - - await this.enableAllExperimentalFeaturesButton.check(); - await playExpect(this.enableAllExperimentalFeaturesCheckbox).toBeChecked(); - } - - public async disableAllExperimentalFeatures(): Promise { - await playExpect(this.enableAllExperimentalFeaturesCheckbox).toBeVisible(); - if (!(await this.enableAllExperimentalFeaturesCheckbox.isChecked())) return; - - await this.enableAllExperimentalFeaturesButton.uncheck(); - await playExpect(this.enableAllExperimentalFeaturesCheckbox).not.toBeChecked(); - } - - public async setExperimentalCheckbox(checkbox: Locator, enable: boolean): Promise { - await playExpect(checkbox).toBeVisible(); - const isEnabled = await checkbox.isChecked(); - if (isEnabled !== enable) { - await checkbox.locator('..').setChecked(enable); - const isEnabled = await checkbox.isChecked(); - playExpect(isEnabled).toEqual(enable); - } - } -} diff --git a/tests/playwright/src/model/pages/extension-card-page.ts b/tests/playwright/src/model/pages/extension-card-page.ts deleted file mode 100644 index ca1793ea9c..0000000000 --- a/tests/playwright/src/model/pages/extension-card-page.ts +++ /dev/null @@ -1,114 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { ExtensionDetailsPage } from './extension-details-page'; -import { ExtensionsPage } from './extensions-page'; - -export class ExtensionCardPage extends BasePage { - readonly parent: Locator; - readonly card: Locator; - readonly leftActions: Locator; - readonly rightActions: Locator; - readonly detailsLink: Locator; - readonly extensionName: string; - readonly extensionLabel: string; - readonly extensionActions: Locator; - readonly onboardingButton: Locator; - readonly editButton: Locator; - readonly status: Locator; - readonly enableButton: Locator; - readonly disableButton: Locator; - readonly removeButton: Locator; - - constructor(page: Page, extensionName: string, extensionLabel: string) { - super(page); - this.parent = this.page.getByRole('region', { name: 'content' }); - this.extensionName = extensionName; - this.extensionLabel = extensionLabel; - this.card = this.parent.getByRole('region', { - name: this.extensionLabel, - exact: true, - }); - this.leftActions = this.card.getByRole('region', { name: 'left actions' }); - this.rightActions = this.card.getByRole('region', { - name: 'right actions', - }); - this.detailsLink = this.rightActions.getByRole('button', { - name: `${this.extensionName} extension details`, - exact: true, - }); - this.extensionActions = this.leftActions.getByRole('group', { - name: 'Extension Actions', - }); - this.onboardingButton = this.leftActions.getByRole('button', { - name: 'Onboarding bootc', - }); - this.editButton = this.leftActions.getByRole('button', { - name: `Edit properties of ${extensionName} extension`, - }); - this.status = this.leftActions.getByLabel('Extension Status Label'); - this.enableButton = this.extensionActions.getByRole('button', { - name: 'Start', - }); - this.disableButton = this.extensionActions.getByRole('button', { - name: 'Stop', - }); - this.removeButton = this.extensionActions.getByRole('button', { - name: 'Delete', - }); - } - - public async openExtensionDetails(heading: string): Promise { - await playExpect(this.card).toBeVisible(); - await this.card.scrollIntoViewIfNeeded(); - await playExpect(this.detailsLink).toBeVisible(); - await this.detailsLink.click(); - return new ExtensionDetailsPage(this.page, heading); - } - - async disableExtension(): Promise { - return test.step(`Disable extension: ${this.extensionName}`, async () => { - if ((await this.status.innerText()) === 'DISABLED') return this; - - await this.disableButton.click(); - await playExpect(this.status).toHaveText('DISABLED'); - return this; - }); - } - - async enableExtension(): Promise { - return test.step(`Enable extension: ${this.extensionName}`, async () => { - if ((await this.status.innerText()) === 'ACTIVE') return this; - - await this.enableButton.click(); - await playExpect(this.status).toHaveText('ACTIVE'); - return this; - }); - } - - async removeExtension(): Promise { - return test.step(`Remove extension: ${this.extensionName}`, async () => { - await this.disableExtension(); - await this.removeButton.click(); - return new ExtensionsPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/extension-catalog-card-page.ts b/tests/playwright/src/model/pages/extension-catalog-card-page.ts deleted file mode 100644 index ec4edfdd89..0000000000 --- a/tests/playwright/src/model/pages/extension-catalog-card-page.ts +++ /dev/null @@ -1,77 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { ExtensionDetailsPage } from './extension-details-page'; - -export class ExtensionCatalogCardPage extends BasePage { - readonly parent: Locator; - readonly extensionName: string; - readonly detailsButton: Locator; - readonly downloadButton: Locator; - readonly alreadyInstalledText: Locator; - - constructor(page: Page, extensionName: string) { - super(page); - this.extensionName = extensionName; - this.parent = this.page.getByRole('group', { name: this.extensionName }); - this.detailsButton = this.parent.getByRole('button', { - name: 'More details', - }); - this.downloadButton = this.parent.getByRole('button', { name: 'Install' }); - this.alreadyInstalledText = this.parent.getByText('Already installed', { - exact: true, - }); - } - - public async openDetails(): Promise { - return test.step(`Open details for extension: ${this.extensionName}`, async () => { - await this.parent.scrollIntoViewIfNeeded(); - await playExpect(this.detailsButton).toBeVisible(); - await this.detailsButton.click(); - return new ExtensionDetailsPage(this.page, this.extensionName); - }); - } - - public async isInstalled(): Promise { - return test.step(`Check if extension ${this.extensionName} is installed`, async () => { - await this.parent.scrollIntoViewIfNeeded(); - const downloadButton = this.parent.getByRole('button', { - name: 'Install', - }); - return (await this.alreadyInstalledText.count()) > 0 && (await downloadButton.count()) === 0; - }); - } - - public async install(timeout: number): Promise { - return test.step(`Install extension ${this.extensionName}`, async () => { - if (await this.isInstalled()) { - console.log(`Extension ${this.extensionName} is already installed`); - return; - } - await playExpect(this.downloadButton).toBeEnabled(); - await this.downloadButton.click(); - await playExpect(this.alreadyInstalledText).toBeVisible({ - timeout: timeout, - }); - }); - } -} diff --git a/tests/playwright/src/model/pages/extension-details-page.ts b/tests/playwright/src/model/pages/extension-details-page.ts deleted file mode 100644 index a0ee98b392..0000000000 --- a/tests/playwright/src/model/pages/extension-details-page.ts +++ /dev/null @@ -1,101 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { ExtensionsPage } from './extensions-page'; - -export class ExtensionDetailsPage extends BasePage { - readonly header: Locator; - readonly tabs: Locator; - readonly tabContent: Locator; - readonly enableButton: Locator; - readonly disableButton: Locator; - readonly removeExtensionButton: Locator; - readonly status: Locator; - readonly heading: Locator; - readonly errorStackTrace: Locator; - - constructor( - page: Page, - public readonly extensionName: string, - ) { - super(page); - this.header = page.getByRole('region', { name: 'Header' }); - this.tabs = page.getByRole('region', { name: 'Tabs' }); - this.tabContent = page.getByRole('region', { name: 'Tab Content' }); - this.heading = this.header.getByRole('heading', { name: extensionName }); - this.enableButton = this.header.getByRole('button', { name: 'Start' }); - this.disableButton = this.header.getByRole('button', { name: 'Stop' }); - this.removeExtensionButton = this.header.getByRole('button', { - name: 'Delete', - }); - this.status = this.header.getByLabel('Extension Status Label'); - this.errorStackTrace = this.tabContent.getByRole('group', { - name: 'Stack Trace', - exact: true, - }); - } - - async disableExtension(): Promise { - return test.step(`Disable extension: ${this.extensionName}`, async () => { - if ((await this.status.innerText()) === 'DISABLED') return this; - - await this.disableButton.click(); - await playExpect(this.status).toHaveText('DISABLED', { timeout: 30000 }); - return this; - }); - } - - async enableExtension(): Promise { - return test.step(`Enable extension: ${this.extensionName}`, async () => { - if ((await this.status.innerText()) === 'ACTIVE') return this; - - await this.enableButton.click(); - await playExpect(this.status).toHaveText('ACTIVE', { timeout: 30000 }); - return this; - }); - } - - async removeExtension(disableBeforeRemove = true): Promise { - return test.step(`Remove extension: ${this.extensionName}`, async () => { - if (disableBeforeRemove) { - await this.disableExtension(); - } - - await playExpect(this.removeExtensionButton).toBeVisible(); - await this.removeExtensionButton.click(); - await playExpect(this.removeExtensionButton).not.toBeVisible({ timeout: 30_000 }); - return new ExtensionsPage(this.page); - }); - } - - async activateTab(tabName: string): Promise { - return test.step(`Activate tab: ${tabName}`, async () => { - const tabItem = this.tabs.getByRole('button', { - name: tabName, - exact: true, - }); - await playExpect(tabItem).toBeVisible(); - await tabItem.click(); - return this; - }); - } -} diff --git a/tests/playwright/src/model/pages/extensions-installed-tab-page.ts b/tests/playwright/src/model/pages/extensions-installed-tab-page.ts new file mode 100644 index 0000000000..1c4ced7391 --- /dev/null +++ b/tests/playwright/src/model/pages/extensions-installed-tab-page.ts @@ -0,0 +1,112 @@ +/********************************************************************** + * 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 { Locator, Page } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { ExtensionLocator } from 'src/model/core/types'; +import { BadgeType, builtInExtensions, Button, ExtensionStatus, State } from 'src/model/core/types'; + +import { BasePage } from './base-page'; + +const stateMap: { [key: string]: ExtensionStatus } = { + [State.ACTIVE]: ExtensionStatus.RUNNING, + [State.DISABLED]: ExtensionStatus.STOPPED, +}; + +export class ExtensionsInstalledPage extends BasePage { + constructor(page: Page) { + super(page); + } + + async waitForLoad(): Promise { + await expect(this.getExtension(builtInExtensions[0].locator)).toBeVisible(); + } + + public getExtension(locator: ExtensionLocator): Locator { + return this.page.getByLabel(locator); + } + + public getExtensionBadge(locator: ExtensionLocator): Locator { + return this.getExtension(locator).getByLabel(BadgeType.BUILT_IN); + } + + public async getExtensionState(locator: ExtensionLocator): Promise { + try { + const statusLabel = this.extensionStateLocator(locator); + const status = (await statusLabel.textContent()) ?? ''; + + return stateMap[status] ?? ExtensionStatus.UNKNOWN; + } catch { + return ExtensionStatus.UNKNOWN; + } + } + + public async stopExtensionAndVerify(locator: ExtensionLocator, timeout = 10_000): Promise { + await this.clickExtensionButton(locator, Button.STOP); + await expect(this.extensionStateLocator(locator)).toHaveText(State.DISABLED, { + timeout, + }); + } + + public async startExtensionAndVerify(locator: ExtensionLocator, timeout = 10_000): Promise { + await this.clickExtensionButton(locator, Button.START); + await expect(this.extensionStateLocator(locator)).toHaveText(State.ACTIVE, { + timeout, + }); + } + + public async toggleExtensionState(locator: ExtensionLocator): Promise { + const currentState = await this.getExtensionState(locator); + switch (currentState) { + case ExtensionStatus.RUNNING: + await this.stopExtensionAndVerify(locator); + break; + case ExtensionStatus.STOPPED: + await this.startExtensionAndVerify(locator); + break; + default: + throw new Error(`Cannot toggle extension with unknown state: ${locator}`); + } + } + + public getDeleteButtonForExtension(locator: ExtensionLocator): Locator { + return this.getExtensionButton(locator, Button.DELETE); + } + + public getStartButtonForExtension(locator: ExtensionLocator): Locator { + return this.getExtensionButton(locator, Button.START); + } + + public getStopButtonForExtension(locator: ExtensionLocator): Locator { + return this.getExtensionButton(locator, Button.STOP); + } + + private extensionStateLocator(locator: ExtensionLocator): Locator { + return this.getExtension(locator).getByLabel('Extension Status Label'); + } + + private async clickExtensionButton(locator: ExtensionLocator, buttonType: Button): Promise { + const button = this.getExtensionButton(locator, buttonType); + await expect(button).toBeEnabled(); + await button.click(); + } + + private getExtensionButton(locator: ExtensionLocator, buttonType: Button): Locator { + return this.getExtension(locator).getByLabel(buttonType); + } +} diff --git a/tests/playwright/src/model/pages/extensions-page.ts b/tests/playwright/src/model/pages/extensions-page.ts index fbc5e7ea45..7ce2965237 100644 --- a/tests/playwright/src/model/pages/extensions-page.ts +++ b/tests/playwright/src/model/pages/extensions-page.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (C) 2023-2025 Red Hat, Inc. + * 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. @@ -17,111 +17,63 @@ ***********************************************************************/ import type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; +import { expect } from '@playwright/test'; +import type { ExtensionLocator } from 'src/model/core/types'; +import { builtInExtensions } from 'src/model/core/types'; -import { ExtensionCardPage } from './extension-card-page'; -import type { ExtensionDetailsPage } from './extension-details-page'; +import { BasePage } from './base-page'; +import { ExtensionsInstalledPage } from './extensions-installed-tab-page'; -export class ExtensionsPage { - readonly page: Page; - readonly heading: Locator; - readonly header: Locator; - readonly content: Locator; - readonly additionalActions: Locator; +export class ExtensionsPage extends BasePage { + readonly searchField: Locator; readonly installedTab: Locator; readonly catalogTab: Locator; - readonly installExtensionFromOCIImageButton: Locator; + readonly localExtensionsTab: Locator; + private readonly tabs: Locator[]; constructor(page: Page) { - this.page = page; - this.header = page.getByRole('region', { name: 'header' }); - this.content = page.getByRole('region', { name: 'content' }); - this.heading = this.header.getByRole('heading', { name: 'extensions' }); - this.additionalActions = this.header.getByRole('group', { - name: 'additionalActions', - }); - this.installedTab = this.page.getByRole('button', { name: 'Installed' }); - this.catalogTab = this.page.getByRole('button', { name: 'Catalog', exact: true }); - this.installExtensionFromOCIImageButton = this.additionalActions.getByLabel('Install custom'); + super(page); + this.searchField = page.getByLabel('search extensions'); + this.installedTab = page.getByRole('button', { name: 'Installed', exact: true }); + this.catalogTab = page.getByRole('button', { name: 'Catalog', exact: true }); + this.localExtensionsTab = page.getByRole('button', { name: 'Local Extensions', exact: true }); + this.tabs = [this.installedTab, this.catalogTab, this.localExtensionsTab]; } - public async installExtensionFromOCIImage(extension: string, timeout = 100_000): Promise { - return test.step(`Install extension from OCI image: ${extension}`, async () => { - // open button to install extension from OCI image - await playExpect(this.installExtensionFromOCIImageButton).toBeEnabled(); - await this.installExtensionFromOCIImageButton.click(); - - const dialog = this.page.getByRole('dialog', { - name: 'Install Custom Extension', - exact: true, - }); - await playExpect(dialog).toBeVisible(); - const imageInput = dialog.getByRole('textbox', { - name: 'Image name to install custom extension', - }); - // check visibility of the input - await playExpect(imageInput).toBeVisible(); - - await imageInput.fill(extension); - - const installButton = dialog.getByRole('button', { - name: 'Install', - exact: true, - }); - await playExpect(installButton).toBeEnabled(); - - await installButton.click(); - - const doneButton = dialog.getByRole('button', { - name: 'Done', - exact: true, - }); - await playExpect(doneButton).toBeEnabled({ timeout: timeout }); - await doneButton.click(); - - return this; - }); - } - - public async openInstalledTab(): Promise { - await playExpect(this.installedTab).toBeVisible({ timeout: 10_000 }); - await this.installedTab.click({ force: true }); + async waitForLoad(): Promise { + await expect(this.searchField).toBeVisible(); + await expect(this.installedTab).toBeVisible(); + await expect(this.catalogTab).toBeVisible(); + await expect(this.localExtensionsTab).toBeVisible(); } - public async openCatalogTab(): Promise { - await this.catalogTab.click(); + async openInstalledTab(): Promise { + return this.openTab(this.installedTab, ExtensionsInstalledPage); } - public async openExtensionDetails(name: string, label: string, heading: string): Promise { - const extensionCard = await this.getInstalledExtension(name, label); - return await extensionCard.openExtensionDetails(heading); + getAllTabs(): Locator[] { + return this.tabs; } - public async getInstalledExtension(name: string, label: string): Promise { - await this.openInstalledTab(); - const extensionCard = new ExtensionCardPage(this.page, name, label); - await playExpect(extensionCard.card).toBeVisible(); - return extensionCard; + async searchExtension(searchTerm: string): Promise { + await expect(this.searchField).toBeVisible(); + await this.searchField.fill(searchTerm); + await expect(this.searchField).toHaveValue(searchTerm); } - public async extensionIsInstalled(label: string): Promise { - await this.openInstalledTab(); - const extension = this.content.getByRole('region', { name: label, exact: true }); - return (await extension.count()) > 0; + async clearSearch(): Promise { + await expect(this.searchField).toBeVisible(); + await this.searchField.clear(); + await expect(this.searchField).toHaveValue(''); } - public async getInstalledExtensionVersion(name: string, label: string): Promise { - const extensionCard = await this.getInstalledExtension(name, label); - const version = extensionCard.rightActions.getByLabel('Version'); - if ((await version.count()) === 0) { - return undefined; - } - - try { - return await version.innerText(); - } catch (error) { - console.log(`Could not get ${label} extension version:`, error); - return undefined; + async verifySearchResults(searchedExtensionLocator: ExtensionLocator): Promise { + const installedPage = new ExtensionsInstalledPage(this.page); + await expect(installedPage.getExtension(searchedExtensionLocator)).toBeVisible(); + for (const otherExtension of builtInExtensions.filter( + extension => extension.locator !== searchedExtensionLocator, + )) { + await expect(installedPage.getExtension(otherExtension.locator)).not.toBeVisible(); } } } diff --git a/tests/playwright/src/model/pages/flows-create-page.ts b/tests/playwright/src/model/pages/flows-create-page.ts new file mode 100644 index 0000000000..e0c7408ba7 --- /dev/null +++ b/tests/playwright/src/model/pages/flows-create-page.ts @@ -0,0 +1,128 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import { type FlowParameters, TIMEOUTS } from 'src/model/core/types'; + +import { BasePage } from './base-page'; +import { FlowDetailsPage } from './flows-details-page'; + +export class FlowsCreatePage extends BasePage { + readonly header: Locator; + readonly pageContentRegion: Locator; + readonly heading: Locator; + readonly nameInputField: Locator; + readonly selectModelDropdown: Locator; + readonly descriptionInputField: Locator; + readonly mcpServerDropdown: Locator; + readonly promptInputField: Locator; + readonly instructionInputField: Locator; + readonly generateButton: Locator; + + constructor(page: Page) { + super(page); + this.header = this.page.getByRole('region', { name: 'header' }); + this.heading = this.header.getByRole('heading', { name: 'Flow Create' }); + + this.pageContentRegion = this.page.getByRole('region', { name: 'Tab Content' }); + this.nameInputField = this.pageContentRegion.getByPlaceholder('name'); + this.selectModelDropdown = this.pageContentRegion.getByRole('button', { name: 'Select model' }); + this.descriptionInputField = this.pageContentRegion.getByPlaceholder('Description...'); + this.mcpServerDropdown = this.pageContentRegion.getByRole('button', { name: 'Select MCP servers' }); + this.promptInputField = this.pageContentRegion.getByPlaceholder('Prompt'); + this.instructionInputField = this.pageContentRegion.getByPlaceholder('Instruction'); + this.generateButton = this.pageContentRegion.getByRole('button', { name: 'Generate' }); + } + + async waitForLoad(): Promise { + await expect(this.heading).toBeVisible({ timeout: TIMEOUTS.STANDARD }); + } + + async createNewFlow( + name: string, + { description, model, mcpServer, prompt, instruction }: FlowParameters = {}, + ): Promise { + await this.waitForLoad(); + await this.fillFlowForm(name, { description, model, mcpServer, prompt, instruction }); + + await this.generateButton.scrollIntoViewIfNeeded(); + await expect(this.generateButton).toBeEnabled(); + await this.generateButton.click(); + + return new FlowDetailsPage(this.page, name); + } + + private async fillFlowForm(name: string, flowParameters?: FlowParameters): Promise { + await this.waitForFormReady(); + + await this.nameInputField.clear(); + await expect(this.nameInputField).toHaveValue(''); + await this.nameInputField.fill(name); + await expect(this.nameInputField).toHaveValue(name); + + if (!flowParameters) { + console.log('No flow parameters provided, skipping form fill...'); + return; + } + + if (flowParameters.description) { + await this.descriptionInputField.clear(); + await expect(this.descriptionInputField).toHaveValue(''); + await this.descriptionInputField.fill(flowParameters.description); + await expect(this.descriptionInputField).toHaveValue(flowParameters.description); + } + + if (flowParameters.model) { + await expect(this.selectModelDropdown).toBeVisible(); + await this.selectModelDropdown.click(); + await this.page.getByRole('menuitem', { name: flowParameters.model }).click(); + await expect(this.selectModelDropdown).toContainText(flowParameters.model); + } + + if (flowParameters.mcpServer) { + await expect(this.mcpServerDropdown).toContainText('0 selected'); + await this.mcpServerDropdown.click(); + await this.page.getByRole('menuitem', { name: flowParameters.mcpServer }).click(); + await expect(this.mcpServerDropdown).toContainText('1 selected'); + } + + if (flowParameters.prompt) { + await this.promptInputField.clear(); + await expect(this.promptInputField).toHaveValue(''); + await this.promptInputField.fill(flowParameters.prompt); + await expect(this.promptInputField).toHaveValue(flowParameters.prompt); + } + + if (flowParameters.instruction) { + await this.instructionInputField.clear(); + await expect(this.instructionInputField).toHaveValue(''); + await this.instructionInputField.fill(flowParameters.instruction); + await expect(this.instructionInputField).toHaveValue(flowParameters.instruction); + } + } + + private async waitForFormReady(): Promise { + await expect(this.nameInputField).toBeVisible({ timeout: 15_000 }); + await expect(this.selectModelDropdown).toBeVisible(); + await expect(this.descriptionInputField).toBeVisible(); + await expect(this.mcpServerDropdown).toBeVisible(); + await expect(this.promptInputField).toBeVisible(); + await expect(this.instructionInputField).toBeVisible(); + await expect(this.generateButton).toBeVisible(); + } +} diff --git a/tests/playwright/src/model/pages/flows-details-page.ts b/tests/playwright/src/model/pages/flows-details-page.ts new file mode 100644 index 0000000000..3fb38ec6ef --- /dev/null +++ b/tests/playwright/src/model/pages/flows-details-page.ts @@ -0,0 +1,216 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import { handleDialogIfPresent } from 'src/utils/app-ready'; + +import { TIMEOUTS } from '../core/types'; +import { BasePage } from './base-page'; +import { FlowsPage } from './flows-page'; + +export class FlowDetailsPage extends BasePage { + readonly flowName: string; + readonly header: Locator; + readonly heading: Locator; + readonly pageTabsRegion: Locator; + readonly tabContentRegion: Locator; + readonly controlActionsButtonGroup: Locator; + readonly runFlowButton: Locator; + readonly deleteFlowButton: Locator; + readonly closeDetailsPageButton: Locator; + readonly summaryTabLink: Locator; + readonly sourceTabLink: Locator; + readonly kubernetesTabLink: Locator; + readonly runTabLink: Locator; + readonly terminalLocator: Locator; + readonly terminalInput: Locator; + readonly terminalContent: Locator; + readonly runsDropdownButton: Locator; + readonly hiddenInputField: Locator; + + constructor(page: Page, name: string) { + super(page); + this.flowName = name; + this.header = this.page.getByRole('region', { name: 'header' }); + this.heading = this.header.getByRole('heading', { name: this.flowName }); + this.pageTabsRegion = this.page.getByRole('region', { name: 'Tabs' }); + this.tabContentRegion = this.page.getByRole('region', { name: 'Tab Content' }); + this.controlActionsButtonGroup = this.header.getByRole('group', { name: 'Control Actions' }); + this.runFlowButton = this.controlActionsButtonGroup.getByRole('button', { name: 'Run this recipe' }); + this.deleteFlowButton = this.controlActionsButtonGroup.getByRole('button', { name: 'Delete' }); + this.summaryTabLink = this.pageTabsRegion.getByRole('link', { name: 'Summary' }); + this.sourceTabLink = this.pageTabsRegion.getByRole('link', { name: 'Source' }); + this.kubernetesTabLink = this.pageTabsRegion.getByRole('link', { name: 'Kubernetes' }); + this.runTabLink = this.pageTabsRegion.getByRole('link', { name: 'Run' }); + this.closeDetailsPageButton = this.header.getByRole('button', { name: 'Close' }); + this.runsDropdownButton = this.page.getByRole('button', { name: 'task-' }).first(); + this.hiddenInputField = this.page.getByLabel('hidden input'); + + this.terminalLocator = this.tabContentRegion.getByRole('term'); + this.terminalInput = this.terminalLocator.getByLabel('Terminal input'); + this.terminalContent = this.terminalLocator.locator('.xterm-rows'); + } + + async waitForLoad(): Promise { + await expect(this.header).toBeVisible(); + await expect(this.heading).toContainText(this.flowName); + await expect(this.pageTabsRegion).toBeVisible(); + } + + async runFlow(): Promise { + await expect(this.runFlowButton).toBeEnabled(); + await this.runFlowButton.click(); + } + + async deleteFlow(): Promise { + await expect(this.deleteFlowButton).toBeEnabled(); + await this.deleteFlowButton.click(); + await handleDialogIfPresent(this.page); + + return new FlowsPage(this.page); + } + + async closeDetailsPage(): Promise { + await expect(this.closeDetailsPageButton).toBeEnabled(); + await this.closeDetailsPageButton.click(); + + return new FlowsPage(this.page); + } + + async switchToSummaryTab(): Promise { + await this.switchTab(this.summaryTabLink); + } + + async switchToSourceTab(): Promise { + await this.switchTab(this.sourceTabLink); + } + + async switchToKubernetesTab(): Promise { + await this.switchTab(this.kubernetesTabLink); + } + + async switchToRunTab(): Promise { + await this.switchTab(this.runTabLink); + } + + private async switchTab(tabLink: Locator): Promise { + await expect(tabLink).toBeVisible(); + await tabLink.click(); + + await expect(tabLink).toHaveClass(/text-\[var\(--pd-tab-text-highlight\)\]/); + } + + async waitForTerminalReady(timeout = 10_000): Promise { + await expect(this.terminalLocator).toBeVisible({ timeout }); + await expect(this.terminalInput).toBeVisible({ timeout }); + } + + async waitForTerminalContent(textOrRegex: string | RegExp, timeout = 30_000): Promise { + await expect(this.terminalContent).toContainText(textOrRegex, { timeout }); + } + + async waitForTerminalContentNotToContainText(textOrRegex: string | RegExp, timeout = 30_000): Promise { + await expect(this.terminalContent).not.toContainText(textOrRegex, { timeout }); + } + + async sendTerminalInput(input: string, pressEnter = true): Promise { + await this.waitForTerminalReady(); + await expect(this.terminalInput).toBeEnabled(); + await this.terminalInput.fill(input); + await expect(this.terminalInput).toHaveValue(input); + if (pressEnter) { + await this.terminalInput.press('Enter'); + } + } + + async getCurrentNumberOfRuns(): Promise { + const fullText = await this.runTabLink.textContent(); + if (!fullText) { + return 0; + } + + const match = RegExp(/\((\d+)\)/).exec(fullText); + if (!match) { + return 0; + } + + return Number.parseInt(match[1]); + } + + async getCurrentSelectedTask(): Promise { + const value = await this.hiddenInputField.getAttribute('value'); + if (!value) { + return ''; + } + + return value; + } + + async getCurrentSelectedTaskIndex(): Promise { + const currentSelectedRun = await this.getCurrentSelectedTask(); + if (!currentSelectedRun) { + return 0; + } + + const index = currentSelectedRun.split('-').pop(); + if (!index) { + return 0; + } + + return Number.parseInt(index); + } + + async selectTaskByIndex(index: number, checkTerminalContentForText = ''): Promise { + const optionValue = `task-${index}`; + await this.selectTask(optionValue, checkTerminalContentForText); + } + + async selectTaskById(id: string, checkTerminalContentForText = ''): Promise { + await this.selectTask(id, checkTerminalContentForText); + } + + async selectLastTask(checkTerminalContentForText = ''): Promise { + await this.selectTaskById('latest', checkTerminalContentForText); + } + + private async selectTask(optionValue: string, checkTerminalContentForText = ''): Promise { + let taskLocator: Locator; + const currentValue = await this.getCurrentSelectedTask(); + if (currentValue === optionValue) { + return; + } + + if (optionValue === 'latest') { + taskLocator = this.page.getByText('task-').last(); + } else { + taskLocator = this.page.getByText(optionValue, { exact: true }); + } + + await this.runsDropdownButton.click(); + await expect(taskLocator).toBeVisible({ timeout: TIMEOUTS.STANDARD }); + await taskLocator.click(); + + if (checkTerminalContentForText) { + await expect(this.terminalContent).toContainText(checkTerminalContentForText, { timeout: TIMEOUTS.STANDARD }); + } else { + await expect(this.terminalContent).toHaveText('', { timeout: TIMEOUTS.STANDARD }); // Empty terminal content ensure terminal is ready for new input + } + + await expect(this.hiddenInputField).not.toHaveValue(currentValue, { timeout: TIMEOUTS.STANDARD }); + } +} diff --git a/tests/playwright/src/model/pages/flows-page.ts b/tests/playwright/src/model/pages/flows-page.ts new file mode 100644 index 0000000000..ae1859d083 --- /dev/null +++ b/tests/playwright/src/model/pages/flows-page.ts @@ -0,0 +1,176 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import type { FlowParameters } from 'src/model/core/types'; +import { handleDialogIfPresent } from 'src/utils/app-ready'; + +import { BaseTablePage } from './base-table-page'; +import { FlowsCreatePage } from './flows-create-page'; +import { FlowDetailsPage } from './flows-details-page'; + +export class FlowsPage extends BaseTablePage { + readonly header: Locator; + readonly heading: Locator; + readonly additionalActionsButtonGroup: Locator; + readonly createFlowButton: Locator; + readonly refreshButton: Locator; + readonly createFlowButtonFromContentRegion: Locator; + readonly noCurrentFlowExistsMessage: Locator; + + constructor(page: Page) { + super(page, 'flows'); + this.header = this.page.getByRole('region', { name: 'header' }); + this.heading = this.header.getByRole('heading', { name: 'Flows' }); + this.additionalActionsButtonGroup = this.header.getByRole('group', { name: 'additionalActions' }); + this.createFlowButton = this.additionalActionsButtonGroup.getByRole('button', { name: 'Create' }); + this.refreshButton = this.additionalActionsButtonGroup.getByRole('button', { name: 'Refresh' }); + + this.createFlowButtonFromContentRegion = this.content.getByRole('button', { name: 'Create' }); + this.noCurrentFlowExistsMessage = this.content.getByText('No flow'); + } + + async waitForLoad(): Promise { + await expect(this.heading).toBeVisible({ timeout: 15_000 }); + } + + async runFlowByName(name: string, exact = false): Promise { + await this.waitForLoad(); + const row = await this.getRowLocatorByName(name, exact); + + const runButton = this.getRunButtonForRow(row); + await expect(runButton).toBeEnabled(); + await runButton.click(); + + return new FlowDetailsPage(this.page, name); + } + + async deleteFlowByName(name: string, exact = false): Promise { + await this.waitForLoad(); + const row = await this.getRowLocatorByName(name, exact); + + const deleteButton = this.getDeleteButtonForRow(row); + await expect(deleteButton).toBeEnabled(); + await deleteButton.click(); + await handleDialogIfPresent(this.page); + } + + async deleteFirstFlow(): Promise { + await this.waitForLoad(); + const rowCount = await this.countRowsFromTable(); + if (rowCount === 0) { + return; + } + + // Index 0 is the header row, so index 1 is the first data row + const row = await this.getRowLocatorByIndex(1); + + const deleteButton = this.getDeleteButtonForRow(row); + await expect(deleteButton).toBeEnabled(); + await deleteButton.click(); + await handleDialogIfPresent(this.page); + } + + async createFlow( + name: string, + { description, model, mcpServer, prompt, instruction }: FlowParameters = {}, + ): Promise { + return this.createFlowWithButton(this.createFlowButton, name, { + description, + model, + mcpServer, + prompt, + instruction, + }); + } + + async createFlowFromContentRegion( + name: string, + { description, model, mcpServer, prompt, instruction }: FlowParameters = {}, + ): Promise { + return this.createFlowWithButton(this.createFlowButtonFromContentRegion, name, { + description, + model, + mcpServer, + prompt, + instruction, + }); + } + + async openFlowDetailsPageByName(name: string, exact = false): Promise { + await this.waitForLoad(); + const button = await this.createFlowsDetailsPageButton(name, exact); + + await expect(button).toBeVisible(); + await button.click(); + + return new FlowDetailsPage(this.page, name); + } + + async checkIfFlowsPageIsEmpty(): Promise { + await this.waitForLoad(); + return (await this.noCurrentFlowExistsMessage.count()) > 0; + } + + async deleteAllFlows(timeout = 30_000): Promise { + const isEmpty = await this.checkIfFlowsPageIsEmpty(); + + if (!isEmpty) { + let rowCount = await this.countRowsFromTable(); + while (rowCount > 0) { + await this.deleteFirstFlow(); + + await expect.poll(async () => await this.countRowsFromTable(), { timeout: timeout }).toBeLessThan(rowCount); + rowCount = await this.countRowsFromTable(); + } + } + + await expect.poll(async () => await this.checkIfFlowsPageIsEmpty(), { timeout: timeout }).toBeTruthy(); + } + + private async createFlowWithButton( + button: Locator, + name: string, + { description, model, mcpServer, prompt, instruction }: FlowParameters = {}, + ): Promise { + await this.waitForLoad(); + + await expect(button).toBeEnabled(); + await button.click(); + + const flowCreatePage = new FlowsCreatePage(this.page); + return flowCreatePage.createNewFlow(name, { description, model, mcpServer, prompt, instruction }); + } + + private async createFlowsDetailsPageButton(name: string, exact = false): Promise { + const row = await this.getRowLocatorByName(name, exact); + return row.getByRole('button').first(); + } + + private getRowButtonByLabel(row: Locator, label: string): Locator { + return row.getByLabel(label, { exact: true }).first(); + } + + private getDeleteButtonForRow(row: Locator): Locator { + return this.getRowButtonByLabel(row, 'Delete'); + } + + private getRunButtonForRow(row: Locator): Locator { + return this.getRowButtonByLabel(row, 'Run this recipe'); + } +} diff --git a/tests/playwright/src/model/pages/forms/machine-creation-form.ts b/tests/playwright/src/model/pages/forms/machine-creation-form.ts deleted file mode 100644 index c5d17535e4..0000000000 --- a/tests/playwright/src/model/pages/forms/machine-creation-form.ts +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { isWindows } from '/@/utility/platform'; - -import { BasePage } from '../base-page'; - -export class MachineCreationForm extends BasePage { - readonly podmanMachineConfiguration: Locator; - readonly podmanMachineName: Locator; - readonly imagePathBox: Locator; - readonly browseImagesButton: Locator; - readonly podmanMachineCPUs: Locator; - readonly podmanMachineMemory: Locator; - readonly podmanMachineDiskSize: Locator; - readonly rootPriviledgesCheckbox: Locator; - readonly userModeNetworkingCheckbox: Locator; - readonly startNowCheckbox: Locator; - readonly createMachineButton: Locator; - - constructor(page: Page) { - super(page); - this.podmanMachineConfiguration = this.page.getByRole('form', { - name: 'Properties Information', - }); - this.podmanMachineName = this.podmanMachineConfiguration.getByRole('textbox', { name: 'Name' }); - this.imagePathBox = this.podmanMachineConfiguration.getByRole('textbox', { - name: 'Image Path (Optional) ', - }); - this.browseImagesButton = this.podmanMachineConfiguration.getByRole('button', { - name: 'button-Image Path (Optional)', - }); - this.podmanMachineCPUs = this.podmanMachineConfiguration.getByRole('slider', { name: 'CPU(s)' }); - this.podmanMachineMemory = this.podmanMachineConfiguration.getByRole('slider', { name: 'Memory' }); - this.podmanMachineDiskSize = this.podmanMachineConfiguration.getByRole('slider', { name: 'Disk size' }); - this.rootPriviledgesCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { - name: 'Machine with root privileges', - }); - this.userModeNetworkingCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { - name: 'User mode networking', - }); - this.startNowCheckbox = this.podmanMachineConfiguration.getByRole('checkbox', { name: 'Start the machine now' }); - this.createMachineButton = this.podmanMachineConfiguration.getByRole('button', { name: 'Create' }); - } - - async setupAndCreateMachine( - machineName: string, - { - isRootful = true, - enableUserNet = false, - startNow = true, - }: { - isRootful?: boolean; - enableUserNet?: boolean; - startNow?: boolean; - } = {}, - ): Promise { - return test.step(`Create Podman Machine: ${machineName} with settings ${isRootful}, ${enableUserNet} and ${startNow}`, async () => { - await playExpect(this.podmanMachineConfiguration).toBeVisible({ - timeout: 10_000, - }); - await this.podmanMachineName.clear(); - await this.podmanMachineName.fill(machineName); - - await this.ensureCheckboxState(isRootful, this.rootPriviledgesCheckbox); - if (isWindows) { - await this.ensureCheckboxState(enableUserNet, this.userModeNetworkingCheckbox); - } - await this.ensureCheckboxState(startNow, this.startNowCheckbox); - - await playExpect(this.createMachineButton).toBeEnabled(); - await this.createMachineButton.click(); - }); - } - - async ensureCheckboxState(desiredState: boolean, checkbox: Locator): Promise { - return test.step(`Ensure checkbox is ${desiredState ? 'checked' : 'unchecked'}`, async () => { - if (desiredState !== (await checkbox.isChecked())) { - await checkbox.locator('..').click(); - playExpect(await checkbox.isChecked()).toBe(desiredState); - } - }); - } -} diff --git a/tests/playwright/src/model/pages/image-details-page.ts b/tests/playwright/src/model/pages/image-details-page.ts deleted file mode 100644 index 15be74b523..0000000000 --- a/tests/playwright/src/model/pages/image-details-page.ts +++ /dev/null @@ -1,140 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { DetailsPage } from './details-page'; -import { ImageEditPage } from './image-edit-page'; -import { ImagesPage } from './images-page'; -import { RunImagePage } from './run-image-page'; - -export class ImageDetailsPage extends DetailsPage { - readonly runImageButton: Locator; - readonly deleteButton: Locator; - readonly editButton: Locator; - readonly summaryTab: Locator; - readonly historyTab: Locator; - readonly inspectTab: Locator; - readonly actionsButton: Locator; - readonly buildDiskImageButton: Locator; - readonly saveImagebutton: Locator; - readonly saveImageInput: Locator; - readonly confirmSaveImages: Locator; - readonly browseButton: Locator; - readonly pushButton: Locator; - - constructor(page: Page, name: string) { - super(page, name); - this.runImageButton = this.controlActions.getByRole('button', { - name: 'Run Image', - }); - this.deleteButton = this.controlActions.getByRole('button', { - name: 'Delete Image', - }); - this.editButton = this.controlActions.getByRole('button', { - name: 'Edit Image', - }); - this.pushButton = this.controlActions.getByRole('button', { - name: 'Push Image', - exact: true, - }); - this.summaryTab = this.tabs.getByText('Summary'); - this.historyTab = this.tabs.getByText('History'); - this.inspectTab = this.tabs.getByText('Inspect'); - this.actionsButton = page.getByRole('button', { name: 'kebab menu' }); - this.buildDiskImageButton = page.getByTitle('Build Disk Image'); - this.saveImagebutton = this.controlActions.getByRole('button', { - name: 'Save Image', - exact: true, - }); - this.saveImageInput = page.locator('#input-output-directory'); - this.confirmSaveImages = page.getByLabel('Save images', { exact: true }); - this.browseButton = page.getByLabel('Select output folder'); - } - - async openRunImage(): Promise { - return test.step('Open run image page', async () => { - await playExpect(this.runImageButton).toBeEnabled({ timeout: 30_000 }); - await this.runImageButton.click(); - return new RunImagePage(this.page, this.resourceName); - }); - } - - async openEditImage(): Promise { - return test.step('Open edit image page', async () => { - await playExpect(this.editButton).toBeEnabled({ timeout: 30_000 }); - await this.editButton.click(); - return new ImageEditPage(this.page, this.resourceName); - }); - } - - async deleteImage(): Promise { - return test.step('Delete image', async () => { - await playExpect(this.deleteButton).toBeEnabled({ timeout: 30_000 }); - await this.deleteButton.click(); - await handleConfirmationDialog(this.page); - return new ImagesPage(this.page); - }); - } - - async pushImage(): Promise { - return test.step('Push image', async () => { - await playExpect(this.pushButton).toBeEnabled(); - await this.pushButton.click(); - await handleConfirmationDialog(this.page, 'Push image', true, 'Push image', '', 60_000, true); - }); - } - - async saveImage(outputPath: string): Promise { - if (!outputPath) { - throw Error(`Path is incorrect or not provided!`); - } - // TODO: Will probably require refactoring when https://github.com/containers/podman-desktop/issues/7620 is done - await playExpect(this.saveImagebutton).toBeEnabled(); - await this.saveImagebutton.click(); - await playExpect(this.saveImageInput).toBeVisible(); - await playExpect(this.confirmSaveImages).toBeVisible(); - - await this.saveImageInput.evaluate(node => node.removeAttribute('readonly')); - await this.confirmSaveImages.evaluate(node => node.removeAttribute('disabled')); - - await this.saveImageInput.pressSequentially(outputPath, { delay: 10 }); - await this.confirmSaveImages.click(); - - return new ImagesPage(this.page); - } - - async pushImageToKindCluster(): Promise { - const kebabMenuButton = this.controlActions.getByRole('button', { name: 'kebab menu', exact: true }); - let pushToKindButton; - const pushToKindName = 'Push image to Kind Cluster'; - - if ((await kebabMenuButton.count()) > 0) { - await kebabMenuButton.click(); - pushToKindButton = this.controlActions.getByTitle('Drop Down Menu Items').getByTitle(pushToKindName); - } else { - pushToKindButton = this.controlActions.getByRole('button', { name: pushToKindName }); - } - await playExpect(pushToKindButton).toBeVisible(); - await pushToKindButton.click(); - - await handleConfirmationDialog(this.page, 'Kind', true, 'OK', '', 80_000); - } -} diff --git a/tests/playwright/src/model/pages/image-edit-page.ts b/tests/playwright/src/model/pages/image-edit-page.ts deleted file mode 100644 index 48fafd9e9e..0000000000 --- a/tests/playwright/src/model/pages/image-edit-page.ts +++ /dev/null @@ -1,73 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { ImagesPage } from './images-page'; - -export class ImageEditPage extends BasePage { - readonly name: string; - readonly cancelButton: Locator; - readonly saveButton: Locator; - readonly imageName: Locator; - readonly imageTag: Locator; - readonly editDialog: Locator; - readonly alertDialog: Locator; - - constructor(page: Page, name: string) { - super(page); - this.editDialog = page.getByRole('dialog', { name: 'Edit Image' }); - this.imageName = this.editDialog.getByLabel('imageName'); - this.cancelButton = this.editDialog.getByRole('button', { - name: 'Cancel', - exact: true, - }); - this.saveButton = this.editDialog.getByRole('button', { - name: 'Save', - exact: true, - }); - this.name = name; - this.imageTag = this.editDialog.getByLabel('imageTag'); - this.alertDialog = page.getByLabel('Error Message Content'); - } - - async renameImage(name: string, tag: string = ''): Promise { - return test.step('Rename image', async () => { - if (!name) { - throw Error(`Provide name is invalid!`); - } - - await playExpect(this.saveButton).toBeVisible(); - await this.imageName.clear(); - await this.imageName.fill(name); - - if (tag) { - await this.imageTag.clear(); - await this.imageTag.fill(tag); - } - - await playExpect.poll(async () => this.alertDialog.count()).toBe(0); - - await playExpect(this.saveButton).toBeEnabled(); - await this.saveButton.click(); - return new ImagesPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/images-page.ts b/tests/playwright/src/model/pages/images-page.ts deleted file mode 100644 index 6be5699ce0..0000000000 --- a/tests/playwright/src/model/pages/images-page.ts +++ /dev/null @@ -1,270 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { waitUntil, waitWhile } from '../../utility/wait'; -import type { ContainerInteractiveParams } from '../core/types'; -import { BuildImagePage } from './build-image-page'; -import type { ContainersPage } from './containers-page'; -import { ImageDetailsPage } from './image-details-page'; -import { MainPage } from './main-page'; -import { PullImagePage } from './pull-image-page'; - -export class ImagesPage extends MainPage { - readonly pullImageButton: Locator; - readonly pruneImagesButton: Locator; - readonly buildImageButton: Locator; - readonly pruneConfirmationButton: Locator; - readonly loadImagesFromTarButton: Locator; - readonly addArchiveButton: Locator; - readonly confirmLoadImagesButton: Locator; - readonly deleteAllUnusedImagesCheckbox: Locator; - readonly deleteAllSelectedButton: Locator; - - constructor(page: Page) { - super(page, 'images'); - this.pullImageButton = this.additionalActions.getByRole('button', { name: 'Pull', exact: true }); - this.pruneImagesButton = this.additionalActions.getByRole('button', { name: 'Prune', exact: true }); - this.buildImageButton = this.additionalActions.getByRole('button', { name: 'Build', exact: true }); - this.pruneConfirmationButton = this.page.getByRole('button', { name: 'All unused images', exact: true }); - this.loadImagesFromTarButton = this.additionalActions.getByLabel('Load Images', { exact: true }); - this.addArchiveButton = this.page.getByRole('button', { name: 'Add archive', exact: true }); - this.confirmLoadImagesButton = this.page.getByRole('button', { name: 'Load Images', exact: true }); - this.deleteAllUnusedImagesCheckbox = this.page.getByRole('checkbox', { name: 'Toggle all', exact: true }); - this.deleteAllSelectedButton = this.bottomAdditionalActions.getByRole('button', { name: 'Delete' }); - } - - async openPullImage(): Promise { - return test.step('Open pull image page', async () => { - await waitWhile(() => this.noContainerEngine(), { - timeout: 50_000, - message: 'No Container Engine is available, cannot pull an image', - }); - await this.pullImageButton.click(); - return new PullImagePage(this.page); - }); - } - - async pullImage(image: string): Promise { - return test.step(`Pull image: ${image}`, async () => { - const pullImagePage = await this.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - return await pullImagePage.pullImage(image); - }); - } - - async renameImage(oldname: string, newname: string, newtag: string = ''): Promise { - return test.step(`Rename ${oldname} to ${newname}`, async () => { - const imageDetailsPage = await this.openImageDetails(oldname); - await playExpect(imageDetailsPage.heading).toContainText(oldname); - const editImagePage = await imageDetailsPage.openEditImage(); - return await editImagePage.renameImage(newname, newtag); - }); - } - - async startContainerWithImage( - image: string, - containerName: string, - containersParams?: ContainerInteractiveParams, - ): Promise { - return test.step(`Start container with image: ${image}`, async () => { - const imageDetails = await this.openImageDetails(image); - const runImage = await imageDetails.openRunImage(); - return await runImage.startContainer(containerName, containersParams); - }); - } - - async openImageDetails(name: string): Promise { - return test.step(`Open image details page for image: ${name}`, async () => { - const imageRow = await this.getImageRowByName(name); - if (imageRow === undefined) { - throw Error(`Image: '${name}' does not exist`); - } - const imageRowName = imageRow.getByRole('cell').nth(3); - await imageRowName.click(); - return new ImageDetailsPage(this.page, name); - }); - } - - async pruneImages(): Promise { - return test.step('Prune all images', async () => { - await this.pruneImagesButton.click(); - await handleConfirmationDialog(this.page, 'Prune', true, 'All unused images'); - return this; - }); - } - - async pruneUntaggedImages(): Promise { - return test.step('Prune untagged images', async () => { - await this.pruneImagesButton.click(); - await handleConfirmationDialog(this.page, 'Prune', true, 'All untagged images'); - return this; - }); - } - - async openBuildImage(): Promise { - return test.step(`Open build image page`, async () => { - await this.buildImageButton.click(); - return new BuildImagePage(this.page); - }); - } - - async getImageRowByName(name: string): Promise { - return this.getRowByName(name); - } - - private async imageExists(name: string): Promise { - return test.step(`Check if image: ${name} exists`, async () => { - const result = await this.getImageRowByName(name); - return result !== undefined; - }); - } - - async waitForImageExists(name: string, timeout = 5_000): Promise { - return test.step(`Wait for image: ${name} to exist`, async () => { - await waitUntil(async () => await this.imageExists(name), { timeout: timeout }); - return true; - }); - } - - async waitForImageDelete(name: string, timeout = 5_000): Promise { - return test.step(`Wait for image: ${name} to be deleted`, async () => { - await waitWhile(async () => await this.imageExists(name), { timeout: timeout }); - return true; - }); - } - - async getCurrentStatusOfImage(name: string): Promise { - return test.step(`Get current status of image: ${name}`, async () => { - let status = ''; - const row = await this.getImageRowByName(name); - - if (row === undefined) throw new Error(`Image: '${name}' does not exist`); - status = status + (await row.getByRole('status').getAttribute('title')); - return status; - }); - } - - async loadImages(archivePath: string): Promise { - // TODO: Will probably require refactoring when https://github.com/containers/podman-desktop/issues/7620 is done - - await playExpect(this.loadImagesFromTarButton).toBeEnabled(); - await this.loadImagesFromTarButton.click(); - await playExpect(this.addArchiveButton).toBeEnabled(); - await this.addArchiveButton.setInputFiles(archivePath); - await playExpect(this.confirmLoadImagesButton).toBeEnabled(); - await this.confirmLoadImagesButton.click(); - return this; - } - - async markAllUnusedImages(): Promise { - return test.step('Mark all unused images', async () => { - if (!(await this.deleteAllUnusedImagesCheckbox.isVisible())) { - console.log('No images available on the page'); - return false; - } - - if (!(await this.deleteAllUnusedImagesCheckbox.isEnabled())) { - console.log('No unused images available on the page'); - return false; - } - - await playExpect(this.deleteAllUnusedImagesCheckbox).not.toBeChecked(); - await this.deleteAllUnusedImagesCheckbox.locator('..').click(); - await playExpect(this.deleteAllUnusedImagesCheckbox).toBeChecked(); - return true; - }); - } - - async deleteAllUnusedImages(): Promise { - return test.step('Delete all unused images', async () => { - if (!(await this.markAllUnusedImages())) { - console.log('No images available to delete'); - return; - } - - await playExpect(this.deleteAllSelectedButton).toBeEnabled(); - await this.deleteAllSelectedButton.click(); - await handleConfirmationDialog(this.page); - }); - } - - async getCountOfImagesByStatus(status: string): Promise { - return test.step(`Get count from ${this.title} for images with status: ${status}`, async () => { - const currentRows = await this.getAllTableRows(); - let count = 0; - if (currentRows.length < 2) return 0; - - for (let rowNum = 1; rowNum < currentRows.length; rowNum++) { - //skip header - const statusCount = await currentRows[rowNum].getByRole('status').getByTitle(status, { exact: true }).count(); - if (statusCount > 0) ++count; - } - return count; - }); - } - - async toggleImageManifest(manifestName: string): Promise { - const manifest = await this.getImageRowByName(manifestName); - if (!manifest) { - throw new Error(`Manifest with name "${manifestName}" not found`); - } - - const extendManifestButton = manifest.getByRole('cell').nth(0).getByRole('button'); - await playExpect(extendManifestButton).toBeEnabled(); - await extendManifestButton.click(); - } - - async deleteImageManifest(manifestName: string): Promise { - const manifest = await this.getImageRowByName(manifestName); - if (!manifest) { - throw new Error(`Manifest with name "${manifestName}" not found`); - } - - const deleteManifestButton = manifest.getByRole('button', { name: 'Delete Manifest' }); - await playExpect(deleteManifestButton).toBeEnabled(); - await deleteManifestButton.click(); - await handleConfirmationDialog(this.page); - } - - async getAllImageBadges(name: string): Promise { - return test.step(`Get all badges for image: ${name}`, async () => { - const locators = await this.getAllBadgeLocators(name); - return await Promise.all(locators.map(async locator => await locator.innerText())); - }); - } - - async checkImageBadge(name: string, badge: string): Promise { - return test.step(`Check if badge: ${badge} exists for image: ${name}`, async () => { - return (await this.getAllImageBadges(name)).includes(badge); - }); - } - - async getAllBadgeLocators(name: string): Promise { - return test.step(`Get all badge locators for image: ${name}`, async () => { - const imageRow = await this.getImageRowByName(name); - if (imageRow === undefined) { - throw Error(`Image: '${name}' does not exist`); - } - return imageRow.getByLabel('badge-').all(); - }); - } -} diff --git a/tests/playwright/src/model/pages/kubernetes-bar.ts b/tests/playwright/src/model/pages/kubernetes-bar.ts deleted file mode 100644 index 7c709c7d86..0000000000 --- a/tests/playwright/src/model/pages/kubernetes-bar.ts +++ /dev/null @@ -1,54 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { Locator, Page } from '@playwright/test'; - -import type { KubernetesResources } from '../core/types'; -import { KubernetesResourcePage } from './kubernetes-resource-page'; - -export class KubernetesBar { - readonly page: Page; - readonly kubernetesNavBar: Locator; - readonly title: Locator; - - constructor(page: Page) { - this.page = page; - this.kubernetesNavBar = page.getByRole('navigation', { name: 'Kubernetes Navigation Bar' }); - this.title = this.kubernetesNavBar.getByText('Kubernetes'); - } - - public async openTabPage(kubernetesResource: KubernetesResources): Promise { - const resource = this.kubernetesNavBar.getByRole('link', { name: kubernetesResource, exact: true }); - await resource.click(); - - switch (kubernetesResource) { - case 'Persistent Volume Claims': - return new KubernetesResourcePage(this.page, 'PVCs'); - case 'ConfigMaps & Secrets': - return new KubernetesResourcePage(this.page, 'Configmaps and Secrets'); - case 'Ingresses & Routes': - return new KubernetesResourcePage(this.page, 'ingresses and routes'); - default: - return new KubernetesResourcePage(this.page, kubernetesResource); - } - } - - public getSettingsNavBarTabLocator(name: string): Locator { - return this.kubernetesNavBar.getByLabel(name); - } -} diff --git a/tests/playwright/src/model/pages/kubernetes-context-page.ts b/tests/playwright/src/model/pages/kubernetes-context-page.ts deleted file mode 100644 index f12da585a1..0000000000 --- a/tests/playwright/src/model/pages/kubernetes-context-page.ts +++ /dev/null @@ -1,117 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { SettingsPage } from './settings-page'; - -export class KubeContextPage extends SettingsPage { - readonly heading: Locator; - readonly content: Locator; - readonly contextTable: Locator; - - constructor(page: Page) { - super(page, 'Kubernetes'); - this.heading = this.page.getByLabel('Title', { exact: true }); - this.content = this.page.getByLabel('Content'); - this.contextTable = this.content.getByLabel('Contexts'); - } - - async pageIsEmpty(): Promise { - const emptyHeading = this.page.getByRole('heading', { name: 'No Kubernetes contexts found', exact: true }); - return (await emptyHeading.count()) > 0; - } - - async getContextRowByName(name: string): Promise { - return this.contextTable.getByLabel(name, { exact: true }); - } - - async isContextDefault(name: string): Promise { - const row = await this.getContextRowByName(name); - if (row === undefined) { - throw Error(`Context: '${name}' does not exist`); - } - - const bannerText = await row.getByLabel('Current Context').textContent(); - return bannerText === 'Current Context'; - } - - async isContextReachable(name: string): Promise { - const row = await this.getContextRowByName(name); - if (row === undefined) { - throw Error(`Context: '${name}' does not exist`); - } - const contextReachable = row.getByLabel('Context Reachable'); - - return (await contextReachable.count()) > 0; - } - - async setDefaultContext(name: string): Promise { - const contextRow = await this.getContextRowByName(name); - if (contextRow === undefined) { - throw Error(`Context: '${name}' does not exist`); - } - const switchButton = contextRow.getByLabel('Set as Current Context'); - await playExpect(switchButton).toBeEnabled(); - await switchButton.click(); - } - - async deleteContext(name: string, handleConfirmation: boolean = true): Promise { - const contextRow = await this.getContextRowByName(name); - if (contextRow === undefined) { - throw Error(`Context: '${name}' does not exist`); - } - const deleteButton = contextRow.getByLabel('Delete Context'); - await playExpect(deleteButton).toBeEnabled(); - await deleteButton.click(); - if (handleConfirmation) { - await handleConfirmationDialog(this.page, 'Delete Context'); - } - } - async getContextName(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Context Name', { exact: true }).innerText(); - } - - async getContextCluster(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Context Cluster').innerText(); - } - - async getContextServer(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Context Server').innerText(); - } - - async getContextUser(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Context User').innerText(); - } - - async getContextNamespace(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Context Namespace').innerText(); - } - - async getSetCurrentContextButton(context: string): Promise { - const row = await this.getContextRowByName(context); - return row.getByLabel('Set as Current Context'); - } -} diff --git a/tests/playwright/src/model/pages/kubernetes-resource-details-page.ts b/tests/playwright/src/model/pages/kubernetes-resource-details-page.ts deleted file mode 100644 index 9f147bf66a..0000000000 --- a/tests/playwright/src/model/pages/kubernetes-resource-details-page.ts +++ /dev/null @@ -1,85 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { isMac } from '../../utility/platform'; -import { KubernetesResourceState } from '../core/states'; -import { DetailsPage } from './details-page'; - -export class KubernetesResourceDetailsPage extends DetailsPage { - readonly applyChangesButton: Locator; - readonly revertChagesButton: Locator; - readonly deleteButton: Locator; - readonly editorWidget: Locator; - readonly toggleButton: Locator; - readonly findTextArea: Locator; - readonly replaceTextArea: Locator; - readonly replaceButton: Locator; - - static readonly SUMMARY_TAB = 'Summary'; - static readonly INSPECT_TAB = 'Inspect'; - static readonly KUBE_TAB = 'Kube'; - - constructor(page: Page, title: string) { - super(page, title); - this.applyChangesButton = this.tabContent.getByRole('button', { name: 'Apply changes to cluster' }); - this.revertChagesButton = this.tabContent.getByRole('button', { name: 'Revert Changes' }); - this.deleteButton = this.controlActions.getByRole('button', { name: 'Delete', exact: false }); - this.editorWidget = this.page.getByRole('dialog', { name: 'Find / Replace' }); - this.toggleButton = this.editorWidget.getByRole('button', { name: 'Toggle Replace' }); - this.findTextArea = this.editorWidget.getByPlaceholder('Find'); - this.replaceTextArea = this.editorWidget.getByPlaceholder('Replace'); - this.replaceButton = this.editorWidget.getByRole('button', { name: 'Replace All' }); - } - - async getState(): Promise { - return test.step('Get resource state', async () => { - const currentState = await this.header.getByRole('status').getAttribute('title'); - return currentState ?? KubernetesResourceState.Unknown; - }); - } - - public async editKubernetsYamlFile(textToBeChanged: string, newText: string): Promise { - return test.step('Edit Kubernetes YAML file', async () => { - await this.activateTab(KubernetesResourceDetailsPage.KUBE_TAB); - const presentation = this.tabContent.getByRole('presentation'); - await playExpect(presentation).toBeVisible(); - await presentation.click(); - if (isMac) { - await this.page.keyboard.press('Meta+F'); - } else { - await this.page.keyboard.press('Control+F'); - } - await playExpect(this.editorWidget).toBeVisible(); - await playExpect(this.findTextArea).toBeVisible(); - await this.findTextArea.fill(textToBeChanged); - await playExpect(this.toggleButton).toBeVisible(); - await this.toggleButton.click(); - await playExpect(this.replaceTextArea).toBeVisible(); - await this.replaceTextArea.fill(newText); - await playExpect(this.replaceButton).toBeVisible(); - await this.replaceButton.click(); - await playExpect(this.revertChagesButton).toBeEnabled(); - await playExpect(this.applyChangesButton).toBeEnabled(); - await this.applyChangesButton.click(); - await handleConfirmationDialog(this.page, 'Kubernetes', true, 'OK'); - }); - } -} diff --git a/tests/playwright/src/model/pages/kubernetes-resource-page.ts b/tests/playwright/src/model/pages/kubernetes-resource-page.ts deleted file mode 100644 index e34009cbf2..0000000000 --- a/tests/playwright/src/model/pages/kubernetes-resource-page.ts +++ /dev/null @@ -1,84 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { KubernetesResourceAttributes, KubernetesResources } from '../core/types'; -import { KubernetesResourceDetailsPage } from './kubernetes-resource-details-page'; -import { MainPage } from './main-page'; - -export class KubernetesResourcePage extends MainPage { - readonly applyYamlButton: Locator; - - constructor(page: Page, name: string) { - super(page, name); - this.applyYamlButton = this.additionalActions.getByRole('button', { - name: 'Apply YAML', - }); - } - - async fetchKubernetesResource(resourceName: string, timeout: number = 15_000): Promise { - try { - await playExpect.poll(async () => this.getRowByName(resourceName, false), { timeout: timeout }).toBeTruthy(); - } catch { - throw Error(`Resource: ${resourceName} does not exist`); - } - - return (await this.getRowByName(resourceName, false)) as Locator; - } - - async geAttributeByRow(row: Locator, attributeName: string, resourceType: KubernetesResources): Promise { - const attributes = KubernetesResourceAttributes[resourceType]; - const attrIndex = attributes.indexOf(attributeName) + 1; - return row.getByRole('cell').nth(attrIndex); - } - - async openResourceDetails( - resourceName: string, - resourceType: KubernetesResources, - timeout?: number, - ): Promise { - return test.step(`Open ${resourceType}: ${resourceName} details`, async () => { - const resourceRow = await this.fetchKubernetesResource(resourceName, timeout); - - let resourceRowName; - if (resourceType === KubernetesResources.Nodes) { - resourceRowName = resourceRow.getByRole('cell').nth(2); - } else { - resourceRowName = resourceRow.getByRole('cell').nth(3); - } - - await playExpect(resourceRowName).toBeEnabled(); - await resourceRowName.click(); - - return new KubernetesResourceDetailsPage(this.page, resourceName); - }); - } - - async deleteKubernetesResource(resourceName: string): Promise { - return test.step(`Delete ${resourceName}`, async () => { - const resourceRow = await this.fetchKubernetesResource(resourceName); - const deleteButton = resourceRow.getByRole('button', { - name: 'Delete', - exact: false, - }); - await playExpect(deleteButton).toBeVisible(); - await deleteButton.click(); - }); - } -} diff --git a/tests/playwright/src/model/pages/main-page.ts b/tests/playwright/src/model/pages/main-page.ts deleted file mode 100644 index 448b11c8aa..0000000000 --- a/tests/playwright/src/model/pages/main-page.ts +++ /dev/null @@ -1,146 +0,0 @@ -/********************************************************************** - * 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 { type Locator, type Page, test } from '@playwright/test'; - -import { waitUntil } from '../../utility/wait'; -import { BasePage } from './base-page'; - -/** - * Abstract representation of a visual page objects of the main content pages of Podman Desktop app: Images, - * Containers, Volumes and Pods. - * Is not intended to be directly used, but rather by particular page's implementation. - */ -export abstract class MainPage extends BasePage { - readonly title: string; - readonly mainPage: Locator; - readonly header: Locator; - readonly search: Locator; - readonly content: Locator; - readonly additionalActions: Locator; - readonly bottomAdditionalActions: Locator; - readonly heading: Locator; - readonly noContainerEngineHeading: Locator; - readonly noImagesHeading: Locator; - - constructor(page: Page, title: string) { - super(page); - this.title = title; - this.mainPage = page.getByRole('region', { name: this.title }); - this.header = this.mainPage.getByRole('region', { name: 'header' }); - this.search = this.mainPage.getByRole('region', { name: 'search' }); - this.content = this.mainPage.getByRole('region', { name: 'content' }); - this.additionalActions = this.header.getByRole('group', { - name: 'additionalActions', - }); - this.bottomAdditionalActions = this.search.getByRole('group', { - name: 'bottomAdditionalActions', - }); - this.heading = this.header.getByRole('heading', { name: this.title }); - this.noContainerEngineHeading = this.content.getByRole('heading', { - name: 'No Container Engine', - exact: true, - }); - this.noImagesHeading = this.content.getByRole('heading', { - name: `No ${this.title}`, - exact: true, - }); - } - - /** - * Check the presence of items in main page's content. - * @returns true, if there are any items present in the content's table, false otherwise - */ - async pageIsEmpty(): Promise { - return test.step('Check if the page is empty', async () => { - if (await this.noContainerEngine()) return true; - return (await this.noImagesHeading.count()) > 0; - }); - } - - async noContainerEngine(): Promise { - return test.step('Check if there is no container engine', async () => { - return (await this.noContainerEngineHeading.count()) > 0; - }); - } - - async getTable(): Promise { - return this.content.getByRole('table'); - } - - async rowsAreVisible(): Promise { - return await this.page.getByRole('row').first().isVisible(); - } - - async getAllTableRows(): Promise { - return await (await this.getTable()).getByRole('row').all(); - } - - async getRowsFromTableByStatus(status: string): Promise { - return test.step(`Get rows from ${this.title} page table by status: ${status}`, async () => { - await waitUntil(async () => await this.rowsAreVisible(), { - sendError: false, - }); - - const rows = await this.getAllTableRows(); - const filteredRows = []; - for (let rowNum = 1; rowNum < rows.length; rowNum++) { - //skip header - const statusCount = await rows[rowNum].getByRole('cell').nth(2).getByTitle(status, { exact: true }).count(); - if (statusCount > 0) filteredRows.push(rows[rowNum]); - } - return filteredRows; - }); - } - - async countRowsFromTable(): Promise { - return test.step(`Count rows from ${this.title} page table`, async () => { - await waitUntil(async () => await this.rowsAreVisible(), { - sendError: false, - }); - const table = this.content.getByRole('table'); - const rows = await table.getByRole('row').all(); - return rows.length > 1 ? rows.length - 1 : 0; - }); - } - - async getRowByName(name: string, exact = true): Promise { - return test.step(`Get row from ${this.title} page table by name: ${name}`, async () => { - const locator = this.page - .getByRole('row') - .and(this.page.getByLabel(name, { exact: exact })) - .first(); - - return (await locator.count()) > 0 ? locator : undefined; - }); - } - - async waitForRowToExists(name: string, timeout = 5_000): Promise { - return test.step(`Wait for row with name: ${name} to exist`, async () => { - await waitUntil(async () => (await this.getRowByName(name)) !== undefined, { timeout: timeout }); - return true; - }); - } - - async waitForRowToBeDelete(name: string, timeout = 5_000): Promise { - return test.step(`Wait for row with name: ${name} to be deleted`, async () => { - await waitUntil(async () => (await this.getRowByName(name)) === undefined, { timeout: timeout }); - return true; - }); - } -} diff --git a/tests/playwright/src/model/pages/mcp-edit-registries-tab-page.ts b/tests/playwright/src/model/pages/mcp-edit-registries-tab-page.ts new file mode 100644 index 0000000000..14bfd263e3 --- /dev/null +++ b/tests/playwright/src/model/pages/mcp-edit-registries-tab-page.ts @@ -0,0 +1,68 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BaseTablePage } from './base-table-page'; + +export class McpEditRegistriesTabPage extends BaseTablePage { + readonly addMcpRegistryButton: Locator; + readonly addMcpRegistryDialog: Locator; + + constructor(page: Page) { + super(page, 'Registries'); + this.addMcpRegistryButton = page.getByRole('button', { name: 'Add MCP registry' }); + this.addMcpRegistryDialog = page.getByRole('dialog', { name: 'Add MCP Registry' }); + } + + async waitForLoad(): Promise { + await expect(this.table).toBeVisible(); + } + + async removeRegistry(name: string): Promise { + const locator = await this.getTableRowByName(name); + + if (!locator) { + console.log(`MCP Registry '${name}' does not exist, skipping...`); + return; + } + + const removeButton = locator.getByRole('button', { name: 'Remove' }); + await expect(removeButton).toBeEnabled(); + await removeButton.click(); + } + + async addNewRegistry(registryUrl: string): Promise { + await expect(this.addMcpRegistryButton).toBeEnabled(); + await this.addMcpRegistryButton.click(); + await this.handleAddMcpRegistryDialog(registryUrl); + } + + private async handleAddMcpRegistryDialog(registryUrl: string): Promise { + await expect(this.addMcpRegistryDialog).toBeVisible(); + const addButton = this.addMcpRegistryDialog.getByRole('button', { name: 'Add' }); + const registryUrlInput = this.addMcpRegistryDialog.getByPlaceholder('Enter the URL of a registry'); + + await expect(addButton).not.toBeEnabled(); + await expect(registryUrlInput).toBeEmpty(); + await registryUrlInput.fill(registryUrl); + await expect(registryUrlInput).toHaveValue(registryUrl); + await expect(addButton).toBeEnabled(); + await addButton.click(); + } +} diff --git a/tests/playwright/src/model/pages/mcp-install-tab-page.ts b/tests/playwright/src/model/pages/mcp-install-tab-page.ts new file mode 100644 index 0000000000..75cdf8542e --- /dev/null +++ b/tests/playwright/src/model/pages/mcp-install-tab-page.ts @@ -0,0 +1,72 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BaseTablePage } from './base-table-page'; + +export class McpInstallTabPage extends BaseTablePage { + readonly noMcpServersAvailableHeading: Locator; + readonly passwordInput: Locator; + readonly connectButton: Locator; + + constructor(page: Page) { + super(page, 'mcpServer'); + this.noMcpServersAvailableHeading = this.table.getByRole('heading', { name: 'No MCP servers available' }); + this.passwordInput = this.page.getByLabel('password'); + this.connectButton = this.page.getByRole('button', { name: 'Connect' }); + } + + async waitForLoad(): Promise { + await expect(this.table).toBeVisible(); + } + + async verifyServerCountIncreased(initialServerCount: number, timeout = 10_000): Promise { + await expect + .poll(async () => await this.countRowsFromTable(), { timeout: timeout }) + .toBeGreaterThan(initialServerCount); + } + + async verifyServerCountIsRestored(initialServerCount: number, timeout = 10_000): Promise { + await expect.poll(async () => await this.countRowsFromTable(), { timeout: timeout }).toBe(initialServerCount); + } + + async verifyInstallTabIsNotEmpty(timeout = 10_000): Promise { + await expect(this.noMcpServersAvailableHeading).not.toBeVisible({ timeout: timeout }); + } + + findServer(serverName: string): Locator { + return this.table.getByRole('row').filter({ hasText: serverName }); + } + + async installRemoteServer(serverName: string, token: string): Promise { + const serverRow = this.findServer(serverName); + await expect(serverRow).toBeVisible(); + + const installButton = serverRow.getByRole('button', { name: 'Install Remote server' }); + await expect(installButton).toBeEnabled(); + await installButton.click(); + + await expect(this.passwordInput).toBeVisible(); + await this.passwordInput.fill(token); + await expect(this.passwordInput).toHaveValue(token); + + await expect(this.connectButton).toBeEnabled(); + await this.connectButton.click(); + } +} diff --git a/tests/playwright/src/model/pages/mcp-page.ts b/tests/playwright/src/model/pages/mcp-page.ts new file mode 100644 index 0000000000..49d5e70598 --- /dev/null +++ b/tests/playwright/src/model/pages/mcp-page.ts @@ -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 { expect, type Locator, type Page } from '@playwright/test'; + +import { TIMEOUTS } from '../core/types'; +import { BasePage } from './base-page'; +import { McpEditRegistriesTabPage } from './mcp-edit-registries-tab-page'; +import { McpInstallTabPage } from './mcp-install-tab-page'; +import { McpReadyTabPage } from './mcp-ready-tab-page'; + +export class McpPage extends BasePage { + readonly searchMcpServersField: Locator; + readonly editRegistriesTabButton: Locator; + readonly installTabButton: Locator; + readonly readyTabButton: Locator; + + constructor(page: Page) { + super(page); + this.searchMcpServersField = page.getByLabel('search MCP servers'); + this.editRegistriesTabButton = page.getByRole('button', { name: 'Edit registries' }); + this.installTabButton = page.getByRole('button', { name: 'Install' }); + this.readyTabButton = page.getByRole('button', { name: 'Ready' }); + } + + async waitForLoad(): Promise { + await expect(this.searchMcpServersField).toBeVisible(); + } + + async openEditRegistriesTab(): Promise { + return this.openTab(this.editRegistriesTabButton, McpEditRegistriesTabPage); + } + + async openInstallTab(): Promise { + return this.openTab(this.installTabButton, McpInstallTabPage); + } + + async openReadyTab(): Promise { + return this.openTab(this.readyTabButton, McpReadyTabPage); + } + + async createServer(serverName: string, token: string): Promise { + const readyTab = await this.openReadyTab(); + + if (await readyTab.isServerConnected(serverName)) { + console.log(`MCP server ${serverName} is already connected, skipping...`); + return; + } + + const installTab = await this.openInstallTab(); + await installTab.installRemoteServer(serverName, token); + + const readyTabAfterInstall = await this.openReadyTab(); + await expect + .poll(async () => await readyTabAfterInstall.isServerConnected(serverName), { timeout: TIMEOUTS.SHORT }) + .toBeTruthy(); + } + + async deleteServer(serverName: string): Promise { + const readyTab = await this.openReadyTab(); + await readyTab.deleteServer(serverName); + await expect + .poll(async () => await readyTab.isServerConnected(serverName), { timeout: TIMEOUTS.SHORT }) + .toBeFalsy(); + } +} diff --git a/tests/playwright/src/model/pages/mcp-ready-tab-page.ts b/tests/playwright/src/model/pages/mcp-ready-tab-page.ts new file mode 100644 index 0000000000..e2a60d3bc5 --- /dev/null +++ b/tests/playwright/src/model/pages/mcp-ready-tab-page.ts @@ -0,0 +1,59 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BaseTablePage } from './base-table-page'; + +export class McpReadyTabPage extends BaseTablePage { + readonly noMcpServersReadyHeading: Locator; + + constructor(page: Page) { + super(page, 'mcp'); + this.noMcpServersReadyHeading = this.content.getByRole('heading', { name: 'No MCP servers available' }); + } + + async waitForLoad(): Promise { + await expect(this.table.or(this.noMcpServersReadyHeading)).toBeVisible({ timeout: 10_000 }); + } + + findServer(serverName: string): Locator { + return this.table.getByRole('row').filter({ hasText: serverName }); + } + + async isServerConnected(serverName: string): Promise { + return (await this.findServer(serverName).count()) > 0; + } + + async deleteServer(serverName: string): Promise { + const server = this.findServer(serverName).first(); + if ((await server.count()) > 0) { + const deleteButton = server.getByRole('button', { name: 'Remove instance of MCP' }); + await deleteButton.focus(); + await deleteButton.click(); + } + } + + async verifyEmpty(): Promise { + await expect + .poll(async () => (await this.noMcpServersReadyHeading.count()) > 0 || (await this.countRowsFromTable()) === 0, { + timeout: 10_000, + }) + .toBeTruthy(); + } +} diff --git a/tests/playwright/src/model/pages/onboarding-page.ts b/tests/playwright/src/model/pages/onboarding-page.ts deleted file mode 100644 index 16d44bb85e..0000000000 --- a/tests/playwright/src/model/pages/onboarding-page.ts +++ /dev/null @@ -1,44 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export class OnboardingPage extends BasePage { - readonly mainPage: Locator; - readonly header: Locator; - readonly skipSetupButton: Locator; - readonly onboardingComponent: Locator; - readonly onboardingStatusMessage: Locator; - readonly nextInfoMessage: Locator; - readonly nextStepButton: Locator; - readonly cancelSetupButtion: Locator; - - constructor(page: Page) { - super(page); - this.mainPage = page.getByRole('region', { name: 'Onboarding Body' }); - this.header = this.mainPage.getByRole('heading', { name: 'Header' }); - this.skipSetupButton = this.header.getByRole('button', { name: 'Skip this entire setup' }); - this.onboardingComponent = this.mainPage.getByLabel('Onboarding Component'); - this.onboardingStatusMessage = this.mainPage.getByLabel('Onboarding Status Message'); - this.nextInfoMessage = this.mainPage.getByLabel('Next Info Message'); - this.nextStepButton = this.mainPage.getByRole('button', { name: 'Next Step' }); - this.cancelSetupButtion = this.mainPage.getByRole('button', { name: 'Cancel Setup' }); - } -} diff --git a/tests/playwright/src/model/pages/play-kube-yaml-page.ts b/tests/playwright/src/model/pages/play-kube-yaml-page.ts deleted file mode 100644 index e37be2d1ba..0000000000 --- a/tests/playwright/src/model/pages/play-kube-yaml-page.ts +++ /dev/null @@ -1,124 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { PlayYamlRuntime } from '../core/operations'; -import type { PlayKubernetesOptions } from '../core/types'; -import { BasePage } from './base-page'; -import { PodsPage } from './pods-page'; - -export class PlayKubeYamlPage extends BasePage { - readonly heading: Locator; - readonly yamlPathInput: Locator; - readonly playButton: Locator; - readonly doneButton: Locator; - readonly podmanRuntimeButton: Locator; - readonly kubernetesRuntimeButton: Locator; - readonly kubernetesContext: Locator; - readonly kubernetesNamespaces: Locator; - readonly alertMessage: Locator; - readonly buildCheckbox: Locator; - - constructor(page: Page) { - super(page); - this.heading = page.getByRole('heading', { - name: 'Create pods from a Kubernetes YAML file', - }); - this.yamlPathInput = page.getByPlaceholder('Select a .yaml file to play'); - this.podmanRuntimeButton = page.getByRole('button', { - name: 'Podman Container Engine Runtime', - }); - this.kubernetesRuntimeButton = page.getByRole('button', { - name: 'Kubernetes Cluster Runtime', - exact: true, - }); - this.kubernetesContext = this.kubernetesRuntimeButton.getByLabel('Default Kubernetes Context'); - this.kubernetesNamespaces = this.kubernetesRuntimeButton.getByLabel('Kubernetes Namespace', { exact: true }); - this.playButton = page.getByRole('button', { name: 'Play' }); - this.doneButton = page.getByRole('button', { name: 'Done' }); - this.alertMessage = this.page.getByLabel('Error Message Content'); - this.buildCheckbox = page.getByRole('checkbox', { name: 'Enable build' }).locator('..'); - } - - async playYaml( - pathToYaml: string, - buildImage: boolean = false, - timeout: number = 120_000, - { runtime, kubernetesContext, kubernetesNamespace }: PlayKubernetesOptions = { - kubernetesContext: 'kind-kind-cluster', - }, - ): Promise { - return test.step('Play Kubernetes YAML', async () => { - if (!pathToYaml) { - throw Error(`Path to Yaml file is incorrect or not provided!`); - } - - // TODO: evaluate() is required due to noninteractivity of fields currently, once https://github.com/containers/podman-desktop/issues/5479 is done they will no longer be needed - await this.yamlPathInput.evaluate(node => node.removeAttribute('readonly')); - await this.playButton.evaluate(node => node.removeAttribute('disabled')); - - switch (runtime) { - case PlayYamlRuntime.Kubernetes: - await playExpect(this.kubernetesRuntimeButton).toBeEnabled(); - await this.kubernetesRuntimeButton.locator('..').click(); - await playExpect(this.kubernetesRuntimeButton).toHaveAttribute('aria-pressed', 'true'); - - await playExpect(this.kubernetesContext).toBeVisible(); - await playExpect(this.kubernetesContext).toHaveValue(kubernetesContext); - - if (kubernetesNamespace) { - await playExpect(this.kubernetesNamespaces).toBeVisible({ timeout: 15_000 }); - await this.kubernetesNamespaces.click(); - - const namespaceSelection = this.kubernetesNamespaces - .getByRole('button', { name: kubernetesNamespace }) - .first(); - - await playExpect(namespaceSelection).toBeEnabled(); - await namespaceSelection.click(); - await playExpect(this.kubernetesNamespaces).toContainText(kubernetesNamespace); - } - break; - default: - await playExpect(this.podmanRuntimeButton).toBeVisible(); - await playExpect(this.podmanRuntimeButton).toHaveAttribute('aria-pressed', 'true'); - break; - } - - await this.yamlPathInput.fill(pathToYaml); - await playExpect(this.buildCheckbox).not.toBeChecked(); - await playExpect(this.buildCheckbox).toBeEnabled(); - if (buildImage) { - await this.buildCheckbox.check(); - await playExpect(this.buildCheckbox).toBeChecked(); - } - await this.playButton.click(); - await playExpect(this.doneButton.or(this.alertMessage).first()).toBeVisible({ timeout: timeout }); - - if (await this.alertMessage.isVisible()) { - const errorMessage = await this.alertMessage.textContent(); - throw Error(`Error while playing Kubernetes YAML: ${errorMessage}`); - } - - await playExpect(this.doneButton).toBeEnabled(); - await this.doneButton.click(); - return new PodsPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/podman-machine-details-page.ts b/tests/playwright/src/model/pages/podman-machine-details-page.ts deleted file mode 100644 index 362b7db3e4..0000000000 --- a/tests/playwright/src/model/pages/podman-machine-details-page.ts +++ /dev/null @@ -1,61 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; - -import { ResourcesPage } from './resources-page'; - -export class PodmanMachineDetails extends ResourcesPage { - readonly podmanMachineName: Locator; - readonly podmanMachineStatus: Locator; - readonly podmanMachineConnectionActions: Locator; - readonly podmanMachineStartButton: Locator; - readonly podmanMachineRestartButton: Locator; - readonly podmanMachineStopButton: Locator; - readonly podmanMachineDeleteButton: Locator; - - readonly tabs: Locator; - readonly summaryTab: Locator; - readonly logsTab: Locator; - readonly terminalTab: Locator; - readonly tabContent: Locator; - readonly terminalInput: Locator; - readonly terminalContent: Locator; - - constructor(page: Page, podmanMachineName: string) { - super(page); - this.podmanMachineName = page.getByRole('heading', { name: podmanMachineName }); - this.podmanMachineStatus = page.getByLabel('Connection Status Label'); - this.podmanMachineConnectionActions = page.getByRole('group', { name: 'Connection Actions' }); - this.podmanMachineStartButton = this.podmanMachineConnectionActions.getByRole('button', { - name: 'Start', - exact: true, - }); - this.podmanMachineRestartButton = this.podmanMachineConnectionActions.getByRole('button', { name: 'Restart' }); - this.podmanMachineStopButton = this.podmanMachineConnectionActions.getByRole('button', { name: 'Stop' }); - this.podmanMachineDeleteButton = this.podmanMachineConnectionActions.getByRole('button', { name: 'Delete' }); - - this.tabs = page.getByRole('region', { name: 'Tabs' }); - this.summaryTab = this.tabs.getByText('Summary'); - this.logsTab = this.tabs.getByText('Logs'); - this.terminalTab = this.tabs.getByText('Terminal', { exact: true }); - this.tabContent = page.getByRole('region', { name: 'Tab Content' }); - this.terminalInput = this.tabContent.getByLabel('Terminal input'); - this.terminalContent = this.tabContent.locator('.xterm-rows'); - } -} diff --git a/tests/playwright/src/model/pages/podman-onboarding-page.ts b/tests/playwright/src/model/pages/podman-onboarding-page.ts deleted file mode 100644 index ea86b4053e..0000000000 --- a/tests/playwright/src/model/pages/podman-onboarding-page.ts +++ /dev/null @@ -1,41 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; - -import { MachineCreationForm } from './forms/machine-creation-form'; -import { OnboardingPage } from './onboarding-page'; - -export class PodmanOnboardingPage extends OnboardingPage { - readonly podmanAutostartToggle: Locator; - readonly createMachinePageTitle: Locator; - readonly machineCreationForm: MachineCreationForm; - readonly podmanMachineShowLogsButton: Locator; - readonly goBackButton: Locator; - - constructor(page: Page) { - super(page); - this.podmanAutostartToggle = this.mainPage.getByRole('checkbox', { - name: 'Autostart Podman engine when launching Podman Desktop', - }); - this.createMachinePageTitle = this.onboardingComponent.getByLabel('title'); - this.machineCreationForm = new MachineCreationForm(this.page); - this.podmanMachineShowLogsButton = this.mainPage.getByRole('button', { name: 'Show Logs' }); - this.goBackButton = this.page.getByRole('button', { name: 'Go back to resources' }); - } -} diff --git a/tests/playwright/src/model/pages/pods-details-page.ts b/tests/playwright/src/model/pages/pods-details-page.ts deleted file mode 100644 index de2cf064b3..0000000000 --- a/tests/playwright/src/model/pages/pods-details-page.ts +++ /dev/null @@ -1,90 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { PodState } from '../core/states'; -import { DetailsPage } from './details-page'; -import { PodsPage } from './pods-page'; - -export class PodDetailsPage extends DetailsPage { - readonly startButton: Locator; - readonly stopButton: Locator; - readonly restartButton: Locator; - readonly deleteButton: Locator; - - static readonly SUMMARY_TAB = 'Summary'; - static readonly LOGS_TAB = 'Logs'; - static readonly INSPECT_TAB = 'Inspect'; - static readonly KUBE_TAB = 'Kube'; - - constructor(page: Page, name: string) { - super(page, name); - this.startButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Start Pod', { exact: true })); - this.stopButton = this.controlActions.getByRole('button').and(this.page.getByLabel('Stop Pod', { exact: true })); - this.restartButton = this.controlActions - .getByRole('button') - .and(this.page.getByLabel('Restart Pod', { exact: true })); - this.deleteButton = this.controlActions - .getByRole('button') - .and(this.page.getByLabel('Delete Pod', { exact: true })); - } - - async getState(): Promise { - return test.step(`Get Pod State`, async () => { - const currentState = await this.header.getByRole('status').getAttribute('title'); - for (const state of Object.values(PodState)) { - if (currentState === state) return state; - } - - return PodState.Unknown; - }); - } - - async startPod(): Promise { - return test.step(`Start Pod`, async () => { - await playExpect(this.startButton).toBeEnabled({ timeout: 10_000 }); - await this.startButton.click(); - }); - } - - async stopPod(): Promise { - return test.step(`Stop Pod`, async () => { - await playExpect(this.stopButton).toBeEnabled({ timeout: 10_000 }); - await this.stopButton.click(); - }); - } - - async restartPod(): Promise { - return test.step(`Restart Pod`, async () => { - await playExpect(this.restartButton).toBeEnabled({ timeout: 20_000 }); - await this.restartButton.click(); - }); - } - - async deletePod(): Promise { - return test.step(`Delete Pod`, async () => { - await playExpect(this.deleteButton).toBeEnabled({ timeout: 10_000 }); - await this.deleteButton.click(); - await handleConfirmationDialog(this.page); - return new PodsPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/pods-page.ts b/tests/playwright/src/model/pages/pods-page.ts deleted file mode 100644 index 5b20f66c9e..0000000000 --- a/tests/playwright/src/model/pages/pods-page.ts +++ /dev/null @@ -1,110 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { MainPage } from './main-page'; -import { PlayKubeYamlPage } from './play-kube-yaml-page'; -import { PodDetailsPage } from './pods-details-page'; - -export class PodsPage extends MainPage { - readonly playKubernetesYAMLButton: Locator; - readonly prunePodsButton: Locator; - readonly pruneConfirmationButton: Locator; - - constructor(page: Page) { - super(page, 'pods'); - this.playKubernetesYAMLButton = this.page.getByRole('button', { - name: 'Play Kubernetes YAML', - }); - this.prunePodsButton = this.page.getByRole('button', { name: 'Prune' }); - this.pruneConfirmationButton = this.page.getByRole('button', { - name: 'Yes', - }); - } - - async openPodDetails(name: string): Promise { - return test.step(`Open Pod: ${name} details`, async () => { - const podRow = await this.getPodRowByName(name); - if (podRow === undefined) { - throw Error(`Pod: ${name} does not exist`); - } - const openPodDetailsButton = podRow.getByRole('button').getByText(name, { exact: true }); - await playExpect(openPodDetailsButton).toBeEnabled(); - await openPodDetailsButton.click(); - return new PodDetailsPage(this.page, name); - }); - } - - async getPodRowByName(name: string): Promise { - return this.getRowByName(name); - } - - async podExists(name: string): Promise { - return (await this.getPodRowByName(name)) !== undefined; - } - - async openPlayKubeYaml(): Promise { - return test.step('Open Play Kubernetes YAML', async () => { - await playExpect(this.playKubernetesYAMLButton).toBeEnabled(); - await this.playKubernetesYAMLButton.click(); - return new PlayKubeYamlPage(this.page); - }); - } - - async prunePods(): Promise { - return test.step('Prune Pods', async () => { - await this.prunePodsButton.click(); - await handleConfirmationDialog(this.page, 'Prune'); - return this; - }); - } - - async selectPod(names: string[]): Promise { - return test.step(`Select Pod: ${names}`, async () => { - for (const containerName of names) { - const row = await this.getPodRowByName(containerName); - if (row === undefined) { - throw Error('Pod cannot be selected'); - } - await row.getByRole('cell').nth(1).click(); - } - }); - } - - async getPodActionsMenu(name: string): Promise { - const row = await this.getPodRowByName(name); - if (row === undefined) { - throw Error('Cannot select actions menu, pod does not exist'); - } - return row.getByRole('button', { name: 'kebab menu', exact: true }); - } - - public async deployedPodExists(podName: string, environment: string = 'Podman'): Promise { - return test.step(`Check if deployed pod exists: ${podName}`, async () => { - const deployedContainerRow = await this.getPodRowByName(podName); - if (deployedContainerRow) { - const env = await deployedContainerRow.getByRole('cell').nth(4).textContent(); - return env?.trim() === environment; - } - return false; - }); - } -} diff --git a/tests/playwright/src/model/pages/preferences-page.ts b/tests/playwright/src/model/pages/preferences-page.ts deleted file mode 100644 index 7523892be8..0000000000 --- a/tests/playwright/src/model/pages/preferences-page.ts +++ /dev/null @@ -1,46 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { SettingsPage } from './settings-page'; - -export class PreferencesPage extends SettingsPage { - readonly heading: Locator; - readonly kubePathInput: Locator; - - constructor(page: Page) { - super(page, 'Preferences'); - this.heading = this.header.getByLabel('Title', { exact: true }); - this.kubePathInput = this.content.getByLabel( - 'Path to the Kubeconfig file for accessing clusters. (Default is usually ~/.kube/config)', - ); - } - - async selectKubeFile(pathToKube: string): Promise { - return test.step('Select Kube File', async () => { - if (!pathToKube) { - throw Error(`Path to Kube config file is incorrect or not provided!`); - } - playExpect(this.kubePathInput).toBeDefined(); - await this.kubePathInput.clear(); - await this.kubePathInput.fill(pathToKube); - }); - } -} diff --git a/tests/playwright/src/model/pages/pull-image-page.ts b/tests/playwright/src/model/pages/pull-image-page.ts deleted file mode 100644 index 51b39a0739..0000000000 --- a/tests/playwright/src/model/pages/pull-image-page.ts +++ /dev/null @@ -1,221 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; -import { ImagesPage } from './images-page'; - -export class PullImagePage extends BasePage { - readonly heading: Locator; - readonly pullImageButton: Locator; - readonly closeLink: Locator; - readonly backToImagesLink: Locator; - readonly manageRegistriesButton: Locator; - readonly imageNameInput: Locator; - readonly tabContent: Locator; - readonly searchResultsTable: Locator; - readonly doneButton: Locator; - - constructor(page: Page) { - super(page); - this.heading = page.getByRole('heading', { - name: 'Pull Image From a Registry', - }); - this.pullImageButton = page.getByRole('button', { name: 'Pull' }); - this.closeLink = page.getByRole('link', { name: 'Close' }); - this.backToImagesLink = page.getByRole('link', { - name: 'Go back to Images', - }); - this.manageRegistriesButton = page.getByRole('button', { - name: 'Manage registries', - }); - this.imageNameInput = page.getByLabel('Image to Pull'); - this.tabContent = page.getByRole('region', { - name: 'Tab Content', - exact: true, - }); - this.searchResultsTable = this.tabContent.getByRole('row'); - this.doneButton = page.getByRole('button', { name: 'Done', exact: true }); - } - - async pullImage(imageName: string, tag = '', timeout = 60_000): Promise { - return test.step(`Pulling image ${imageName}:${tag}`, async () => { - const fullImageName = `${imageName}${tag.length === 0 ? '' : ':' + tag}`; - await this.imageNameInput.fill(fullImageName); - await playExpect(this.pullImageButton).toBeEnabled(); - await this.pullImageButton.click(); - - await playExpect(this.doneButton).toBeEnabled({ timeout: timeout }); - await this.doneButton.click(); - return new ImagesPage(this.page); - }); - } - - async getAllSearchResultsFor( - imageName: string, - searchForVersion: boolean, - imageTag = '', - resultsExpected = true, - ): Promise { - return test.step(`Get all search results for ${imageName}:${imageTag}`, async () => { - const searchString = await this.handleFormAndResultSearchString( - imageName, - searchForVersion, - imageTag, - resultsExpected, - ); - return await this.getAllSearchResultsInstantly(searchString); - }); - } - - async getFirstSearchResultFor( - imageName: string, - searchForVersion: boolean, - imageTag = '', - resultsExpected = true, - ): Promise { - return test.step(`Get first search result for ${imageName}:${imageTag}`, async () => { - await this.handleFormAndResultSearchString(imageName, searchForVersion, imageTag, resultsExpected); - return await this.getFirstSearchResultInstantly(); - }); - } - - async refineSearchResults(stringToAppend: string, resultsExpected = true): Promise { - return test.step(`Refine search results by appending: ${stringToAppend}`, async () => { - await this.imageNameInput.pressSequentially(stringToAppend, { - delay: 10, - }); - const searchString = await this.imageNameInput.inputValue(); - - if (resultsExpected) { - await playExpect(this.searchResultsTable).toBeVisible({ - timeout: 15_000, - }); - await playExpect - .poll(async () => await this.getFirstSearchResultInstantly(), { - timeout: 10_000, - }) - .toContain(searchString); - } else { - await playExpect(this.searchResultsTable).not.toBeVisible({ - timeout: 15_000, - }); - } - - return await this.getAllSearchResultsInstantly(searchString); - }); - } - - async getAllSearchResultsInstantly(searchString: string): Promise { - return test.step(`Get search results instantly for ${searchString}`, async () => { - const resultList: string[] = []; - const resultRows = await this.getAllResultButtonLocators(searchString); - for (const row of resultRows) { - const result = await row.innerText(); - resultList.push(result); - } - console.log(`Found ${resultList.length} results for ${searchString}`); - return resultList; - }); - } - - async getFirstSearchResultInstantly(): Promise { - return test.step(`Get first search result from the results table`, async () => { - const resultRow = this.getFirstResultButtonLocator(); - return await resultRow.innerText(); - }); - } - - async pullImageFromSearchResults(pattern: string, timeout = 60_000): Promise { - return test.step(`Pull image from search results: ${pattern}`, async () => { - await this.selectValueFromSearchResults(pattern); - await playExpect(this.pullImageButton).toBeEnabled(); - await this.pullImageButton.click(); - - await playExpect(this.doneButton).toBeEnabled({ timeout: timeout }); - await this.doneButton.click(); - return new ImagesPage(this.page); - }); - } - - async selectValueFromSearchResults(pattern: string): Promise { - return test.step(`Select value from search results: ${pattern}`, async () => { - const getExactButtonLocator = this.searchResultsTable.getByRole('button', { name: pattern, exact: true }).first(); - - await getExactButtonLocator.scrollIntoViewIfNeeded(); - await getExactButtonLocator.focus(); - - await playExpect(getExactButtonLocator).toBeEnabled(); - await getExactButtonLocator.click(); - - await playExpect(this.imageNameInput).toHaveValue(pattern); - }); - } - - async clearImageSearch(): Promise { - return test.step('Clear image search', async () => { - await this.imageNameInput.clear(); - await playExpect(this.imageNameInput).toHaveValue(''); - await playExpect(this.searchResultsTable).not.toBeVisible(); - }); - } - - private getAllResultButtonLocators(pattern: string): Promise { - return this.searchResultsTable.getByRole('button', { name: pattern }).all(); - } - - private getFirstResultButtonLocator(): Locator { - return this.searchResultsTable.getByRole('button').first(); - } - - private async handleFormAndResultSearchString( - imageName: string, - searchForVersion: boolean, - imageTag = '', - resultsExpected = true, - ): Promise { - return test.step(`Handle form and result search string for ${imageName}:${imageTag}`, async () => { - if (!imageName || imageName.length === 0) { - throw new Error('Image name is invalid'); - } - - let searchString; - - if (searchForVersion) { - searchString = `${imageName}:${imageTag}`; - } else { - searchString = imageName; - } - - await this.clearImageSearch(); - await this.imageNameInput.fill(searchString); - - if (resultsExpected) { - await playExpect(this.searchResultsTable).toBeVisible({ - timeout: 15_000, - }); - } else { - await playExpect(this.searchResultsTable).not.toBeVisible({ - timeout: 15_000, - }); - } - return searchString; - }); - } -} diff --git a/tests/playwright/src/model/pages/registries-page.ts b/tests/playwright/src/model/pages/registries-page.ts deleted file mode 100644 index 95a2cbba26..0000000000 --- a/tests/playwright/src/model/pages/registries-page.ts +++ /dev/null @@ -1,136 +0,0 @@ -/********************************************************************** - * 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 test, { expect as playExpect } from '@playwright/test'; -import type { Locator, Page } from 'playwright'; - -import { waitUntil } from '../../utility/wait'; -import { SettingsPage } from './settings-page'; - -export class RegistriesPage extends SettingsPage { - readonly heading: Locator; - readonly addRegistryButton: Locator; - readonly registriesTable: Locator; - readonly addRegistryDialog: Locator; - readonly cancelDialogButton: Locator; - readonly confirmDialogButton: Locator; - readonly registryUrlField: Locator; - readonly registryUsernameField: Locator; - readonly registryPswdField: Locator; - - constructor(page: Page) { - super(page, 'Registries'); - this.heading = page.getByRole('heading').and(page.getByText('Registries', { exact: true })); - this.addRegistryButton = page.getByRole('button', { name: 'Add registry' }); - this.registriesTable = page.getByRole('table', { name: 'Registries' }); - this.addRegistryDialog = page.getByRole('dialog', { name: 'Add Registry' }); - this.cancelDialogButton = this.addRegistryDialog.getByRole('button', { - name: 'Cancel', - }); - this.confirmDialogButton = this.addRegistryDialog.getByRole('button', { - name: 'Add', - }); - this.registryUrlField = this.addRegistryDialog.getByPlaceholder('https://registry.io'); - this.registryUsernameField = this.addRegistryDialog.getByPlaceholder('username'); - this.registryPswdField = this.addRegistryDialog.getByPlaceholder('password'); - } - - async createRegistry(url: string, username: string, pswd: string): Promise { - return test.step('Create a new registry', async () => { - await this.page.waitForTimeout(4_000); - await playExpect(this.addRegistryButton).toBeEnabled(); - await this.addRegistryButton.click(); - await playExpect(this.addRegistryDialog).toBeVisible(); - await playExpect(this.cancelDialogButton).toBeEnabled(); - - await this.registryUrlField.fill(url); - await this.registryUsernameField.fill(username); - await this.registryPswdField.fill(pswd); - - await playExpect(this.confirmDialogButton).toBeEnabled(); - await this.confirmDialogButton.click(); - }); - } - - async editRegistry(title: string, newUsername: string, newPswd: string): Promise { - return test.step('Edit registry', async () => { - const registryBox = await this.getRegistryRowByName(title); - - const dropdownMenu = registryBox.getByRole('button', { - name: 'kebab menu', - }); - await dropdownMenu.click(); - - const editButton = registryBox.getByTitle('Edit password'); - await editButton.click(); - - const registryUsername = registryBox.getByLabel('Username'); - const registryPswd = registryBox.getByRole('textbox', { - name: 'Password', - }); - await registryUsername.pressSequentially(newUsername, { delay: 100 }); - await registryPswd.pressSequentially(newPswd, { delay: 100 }); - - const loginButton = registryBox.getByRole('button', { name: 'Login' }); - await this.loginButtonHandling(loginButton); - }); - } - - /* - * There are two types of registries, if it is custom, then it can be actually deleted - * If it is default registry, it will delete only the credentials and the record will be kept there. - */ - async removeRegistry(title: string): Promise { - return test.step('Remove registry', async () => { - const registryBox = await this.getRegistryRowByName(title); - - const dropdownMenu = registryBox.getByRole('button', { - name: 'kebab menu', - }); - try { - await dropdownMenu.waitFor({ state: 'visible', timeout: 3_000 }); - } catch (err) { - throw Error(`Dropdown menu on ${title} registry not available.`); - } - await dropdownMenu.click(); - - const editButton = registryBox.getByTitle('Remove'); - await editButton.click(); - }); - } - - async getRegistryRowByName(name: string): Promise { - return this.registriesTable.getByRole('row', { name: name }); - } - - private async loginButtonHandling(loginButton: Locator): Promise { - return test.step('Handle login button', async () => { - try { - await waitUntil( - async function loginIsEnabled() { - return await loginButton.isEnabled(); - }, - { message: 'Login Button not enabled in time' }, - ); - await loginButton.click({ timeout: 3000 }); - } catch (err) { - throw Error(`An error occured when trying to log into registry: ${(err as Error).message}`); - } - }); - } -} diff --git a/tests/playwright/src/model/pages/resource-card-page.ts b/tests/playwright/src/model/pages/resource-card-page.ts deleted file mode 100644 index b5d937e4c4..0000000000 --- a/tests/playwright/src/model/pages/resource-card-page.ts +++ /dev/null @@ -1,40 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 Locator, type Page } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export abstract class ResourceCardPage extends BasePage { - readonly parent: Locator; - readonly card: Locator; - readonly providerSetup: Locator; - readonly providerConnections: Locator; - readonly markdownContent: Locator; - readonly setupButton: Locator; - - constructor(page: Page, resourceName: string) { - super(page); - this.parent = this.page.getByRole('region', { name: 'content' }); - this.card = this.parent.getByRole('region', { name: resourceName, exact: true }); - this.providerSetup = this.card.getByRole('region', { name: 'Provider Setup', exact: true }); - this.providerConnections = this.card.getByRole('region', { name: 'Provider Connections', exact: true }); - this.markdownContent = this.providerConnections.getByLabel('markdown-content'); - this.setupButton = this.providerSetup.getByRole('button', { name: 'Setup' }); - } -} diff --git a/tests/playwright/src/model/pages/resource-cli-card-page.ts b/tests/playwright/src/model/pages/resource-cli-card-page.ts deleted file mode 100644 index b347f77a4e..0000000000 --- a/tests/playwright/src/model/pages/resource-cli-card-page.ts +++ /dev/null @@ -1,27 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 Page } from '@playwright/test'; - -import { ResourceCardPage } from './resource-card-page'; - -export class ResourceCliCardPage extends ResourceCardPage { - constructor(page: Page, resourceName: string) { - super(page, resourceName); - } -} diff --git a/tests/playwright/src/model/pages/resource-connection-card-page.ts b/tests/playwright/src/model/pages/resource-connection-card-page.ts deleted file mode 100644 index d55b6aa7ed..0000000000 --- a/tests/playwright/src/model/pages/resource-connection-card-page.ts +++ /dev/null @@ -1,63 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import type { ResourceElementActions } from '../core/operations'; -import { ResourceCardPage } from './resource-card-page'; - -export class ResourceConnectionCardPage extends ResourceCardPage { - readonly resourceElement: Locator; - readonly resourceElementDetailsButton: Locator; - readonly resourceElementConnectionStatus: Locator; - readonly resourceElementConnectionActions: Locator; - readonly createButton: Locator; - - constructor(page: Page, resourceName: string, resourceElementVisibleName?: string) { - super(page, resourceName); - this.resourceElement = this.providerConnections.getByRole('region', { - name: resourceElementVisibleName, - exact: true, - }); - this.resourceElementDetailsButton = this.resourceElement.getByRole('button', { name: 'details' }); - this.resourceElementConnectionStatus = this.resourceElement.getByLabel('Connection Status Label'); - this.resourceElementConnectionActions = this.resourceElement.getByRole('group', { name: 'Connection Actions' }); - this.createButton = this.providerSetup.getByRole('button', { - name: 'Create', - }); - } - - public async doesResourceElementExist(): Promise { - return (await this.resourceElement.count()) > 0; - } - - public async performConnectionAction(operation: ResourceElementActions, timeout: number = 25000): Promise { - return test.step(`Perform connection action '${operation}' on resource element '${this.resourceElement}'`, async () => { - const button = this.resourceElementConnectionActions.getByRole('button', { - name: operation, - exact: true, - }); - await playExpect(button).toBeEnabled({ timeout: timeout }); - await button.click(); - }); - } - - public async getConnectionInfoByLabel(label: string): Promise { - return (await this.card.getByLabel(label, { exact: true }).textContent()) ?? ''; - } -} diff --git a/tests/playwright/src/model/pages/resource-details-page.ts b/tests/playwright/src/model/pages/resource-details-page.ts deleted file mode 100644 index 8cf86f33a7..0000000000 --- a/tests/playwright/src/model/pages/resource-details-page.ts +++ /dev/null @@ -1,45 +0,0 @@ -/********************************************************************** - * 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import type { ResourceElementActions } from '../core/operations'; -import { DetailsPage } from './details-page'; - -export class ResourceDetailsPage extends DetailsPage { - readonly resourceStatus: Locator; - - constructor(page: Page, title: string) { - super(page, title); - this.resourceStatus = this.header.getByLabel('Connection Status Label'); - } - - public async performConnectionActionDetails( - operation: ResourceElementActions, - timeout: number = 25_000, - ): Promise { - return test.step(`Perform connection action '${operation}' on resource element '${this.resourceName}' from details page`, async () => { - const button = this.controlActions.getByRole('button', { - name: operation, - exact: true, - }); - await playExpect(button).toBeEnabled({ timeout: timeout }); - await button.click(); - }); - } -} diff --git a/tests/playwright/src/model/pages/resources-page.ts b/tests/playwright/src/model/pages/resources-page.ts deleted file mode 100644 index 7c059ac8b6..0000000000 --- a/tests/playwright/src/model/pages/resources-page.ts +++ /dev/null @@ -1,50 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from 'playwright'; - -import { SettingsPage } from './settings-page'; - -export class ResourcesPage extends SettingsPage { - readonly heading: Locator; - readonly featuredProviderResources: Locator; - - constructor(page: Page) { - super(page, 'Resources'); - this.heading = this.header.getByRole('heading', { name: 'Title' }).and(this.header.getByText('Resources')); - this.featuredProviderResources = this.content.getByRole('region', { name: 'Featured Provider Resources' }); - } - - public async resourceCardIsVisible(resourceLabel: string): Promise { - return (await this.resourceCardLocatorGenerator(resourceLabel).count()) > 0; - } - - public async goToCreateNewResourcePage(resourceLabel: string): Promise { - if (!(await this.resourceCardIsVisible(resourceLabel))) { - throw new Error(`Resource card with label ${resourceLabel} is not available`); - } - - await this.resourceCardLocatorGenerator(resourceLabel) - .getByRole('button', { name: `Create new ${resourceLabel}` }) - .click(); - } - - private resourceCardLocatorGenerator(resourceLabel: string): Locator { - return this.content.getByRole('region', { name: resourceLabel, exact: true }); - } -} diff --git a/tests/playwright/src/model/pages/run-image-page.ts b/tests/playwright/src/model/pages/run-image-page.ts deleted file mode 100644 index 03421d90ec..0000000000 --- a/tests/playwright/src/model/pages/run-image-page.ts +++ /dev/null @@ -1,186 +0,0 @@ -/********************************************************************** - * 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { waitWhile } from '../../utility/wait'; -import type { ContainerInteractiveParams } from '../core/types'; -import { BasePage } from './base-page'; -import { ContainerDetailsPage } from './container-details-page'; -import { ContainersPage } from './containers-page'; - -export class RunImagePage extends BasePage { - readonly name: Locator; - readonly heading: Locator; - readonly closeLink: Locator; - readonly backToImageDetailsLink: Locator; - readonly imageName: string; - readonly startContainerButton: Locator; - readonly errorAlert: Locator; - readonly containerNameInput: Locator; - readonly containerEntryPointInput: Locator; - readonly containerComamndInput: Locator; - readonly containerAddCustomPortMappingButton: Locator; - readonly volumeNameInput: Locator; - readonly volumeContainerPath: Locator; - - constructor(page: Page, name: string) { - super(page); - this.imageName = name; - this.name = page.getByLabel('name').and(page.getByText('Run Image')); - this.heading = page.getByRole('heading', { name: this.imageName }); - this.closeLink = page.getByRole('link', { name: 'Close' }); - this.errorAlert = page.getByRole('alert', { - name: 'Error Message Content', - }); - this.backToImageDetailsLink = page.getByRole('link', { - name: 'Go back to Image Details', - }); - this.startContainerButton = page.getByLabel('Start Container', { - exact: true, - }); - this.containerNameInput = page.getByLabel('Container Name'); - this.containerEntryPointInput = page.getByLabel('Entrypoint'); - this.containerComamndInput = page.getByLabel('Command'); - this.containerAddCustomPortMappingButton = page.getByLabel('Add custom port mapping', { exact: true }); - this.volumeNameInput = page.getByPlaceholder('Path on the host'); - this.volumeContainerPath = page.getByPlaceholder('Path inside the container'); - } - - async activateTab(name: string): Promise { - return test.step(`Activate tab: ${name}`, async () => { - const tabactive = this.page.getByRole('link', { name: name, exact: true }).and(this.page.getByText(name)); - await tabactive.click(); - }); - } - - // If the container has a defined exposed port, the mapping offers only one part of the input box, host port - // Example of the placeholder: 'Enter value for port 80/tcp' : settable value - async setHostPortToExposedContainerPort(exposedPort: string, port: string): Promise { - return test.step(`Set host port to exposed container port`, async () => { - await this.activateTab('Basic'); - const portMapping = this.page - .getByRole('textbox') - .and(this.page.getByPlaceholder(`Enter value for port ${exposedPort}/tcp`)); - await portMapping.waitFor({ state: 'visible' }); - await portMapping.fill(port); - }); - } - - async startInteractiveContainer(customName = ''): Promise { - return test.step(`Start interactive container from image: ${this.imageName}`, async () => { - await this.startContainer(customName, { - attachTerminal: true, - interactive: true, - } as ContainerInteractiveParams); - const detailsPageLocator = this.page.getByLabel('name').and(this.page.getByText('Container Details')); - await playExpect(detailsPageLocator).toBeVisible(); // we are sure to get into details page - const heading = this.page.getByRole('heading'); - const containerName = customName ? customName : await heading.innerText(); - console.log(`Heading and container name: ${await heading.innerText()}`); - return new ContainerDetailsPage(this.page, containerName); - }); - } - - async startContainer(customName = '', optionalParams?: ContainerInteractiveParams): Promise { - return test.step(`Start container from image: ${this.imageName}`, async () => { - if (customName !== '') { - await this.activateTab('Basic'); - await playExpect(this.containerNameInput).toBeVisible(); - await this.containerNameInput.fill(customName); - } - - if (optionalParams?.attachVolumeName !== undefined && optionalParams?.attachVolumePath !== undefined) { - // - await this.activateTab('Basic'); - await playExpect(this.volumeNameInput).toBeVisible(); - await playExpect(this.volumeContainerPath).toBeVisible(); - await this.volumeNameInput.pressSequentially(optionalParams.attachVolumeName, { delay: 10 }); - await this.volumeContainerPath.pressSequentially(optionalParams.attachVolumePath, { delay: 10 }); - } - - if (optionalParams?.attachTerminal !== undefined) { - // disable the checkbox in advanced tab - await this.activateTab('Advanced'); - const checkbox = this.page.getByRole('checkbox', { - name: 'Attach a pseudo terminal', - }); - if (optionalParams.attachTerminal) { - await checkbox.check(); - } else { - await checkbox.uncheck(); - } - await playExpect(checkbox).toBeChecked({ - checked: optionalParams.attachTerminal, - }); - } - - if (optionalParams?.interactive !== undefined) { - // disable the checkbox in advanced tab - await this.activateTab('Advanced'); - const checkbox = this.page.getByRole('checkbox', { - name: 'Use interactive', - }); - if (optionalParams.interactive) { - await checkbox.check(); - } else { - await checkbox.uncheck(); - } - await playExpect(checkbox).toBeChecked({ - checked: optionalParams.interactive, - }); - } - - await this.activateTab('Basic'); - await playExpect(this.startContainerButton).toBeEnabled(); - await this.startContainerButton.click(); - // After clicking on the button there seems to be four possible outcomes - // 1. Opening particular container's details page with tty tab opened - // 2. Opening Containers page with new container on it - // 3. staying on the run image page with an error - // 4. Starting a container without entrypoint or command creates a container, but it stays on Run Image Page without error - await waitWhile( - async () => { - return await this.name.isVisible(); - }, - { sendError: false }, - ); - - const errorCount = await this.errorAlert.count(); - if (errorCount > 0) { - const runImagePageActive = await this.name.isVisible(); - const message = runImagePageActive ? 'threw an ' : 'redirected to another page with an '; - throw Error(`Starting the container ${message} error: ${await this.errorAlert.innerText()}`); - } - return new ContainersPage(this.page); - }); - } - - async setCustomPortMapping(customPortMapping: string): Promise { - return test.step(`Set custom port mapping: ${customPortMapping}`, async () => { - // add port mapping - await this.activateTab('Basic'); - await playExpect(this.containerAddCustomPortMappingButton).toBeVisible(); - await this.containerAddCustomPortMappingButton.click(); - const hostPort = this.page.getByLabel('host port'); - const containerPort = this.page.getByLabel('container port'); - await hostPort.fill(customPortMapping.split(':')[0]); - await containerPort.fill(customPortMapping.split(':')[1]); - }); - } -} diff --git a/tests/playwright/src/model/pages/settings-bar.ts b/tests/playwright/src/model/pages/settings-bar.ts deleted file mode 100644 index 1c13411ffd..0000000000 --- a/tests/playwright/src/model/pages/settings-bar.ts +++ /dev/null @@ -1,73 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 Locator, type Page, test } from '@playwright/test'; -import { expect as playExpect } from '@playwright/test'; - -import type { SettingsPage } from './settings-page'; - -export class SettingsBar { - readonly page: Page; - readonly settingsNavBar: Locator; - readonly resourcesTab: Locator; - readonly proxyTab: Locator; - readonly registriesTab: Locator; - readonly authenticationTab: Locator; - readonly preferencesTab: Locator; - readonly cliToolsTab: Locator; - readonly kubernetesTab: Locator; - - constructor(page: Page) { - this.page = page; - this.settingsNavBar = page.getByRole('navigation', { name: 'PreferencesNavigation' }); - this.resourcesTab = this.settingsNavBar.getByRole('link', { name: 'Resources' }); - this.proxyTab = this.settingsNavBar.getByRole('link', { name: 'Proxy' }); - this.registriesTab = this.settingsNavBar.getByRole('link', { name: 'Registries' }); - this.authenticationTab = this.settingsNavBar.getByRole('link', { name: 'Authentication' }); - this.cliToolsTab = this.settingsNavBar.getByRole('link', { name: 'CLI Tools' }); - this.kubernetesTab = this.settingsNavBar.getByRole('link', { name: 'Kubernetes' }); - this.preferencesTab = this.settingsNavBar.getByLabel('preferences'); - } - - public async openTabPage(type: new (page: Page) => T): Promise { - return test.step(`Open ${type.name} tab from Settings`, async () => { - const desiredPage = new type(this.page); - const tab = await desiredPage.getTab(); - await playExpect(tab).toBeVisible(); - await tab.click({ force: true }); - return desiredPage; - }); - } - - public getSettingsNavBarTabLocator(name: string): Locator { - return this.settingsNavBar.getByLabel(name); - } - - public getPreferencesLinkLocator(label: string): Locator { - return this.settingsNavBar.getByRole('link', { name: label, exact: true }); - } - - public getLinkLocatorByHref(href: string): Locator { - return this.settingsNavBar.locator(`[href*="${href}"]`); - } - - public async expandPreferencesTab(): Promise { - await playExpect(this.preferencesTab).toBeVisible({ timeout: 10_000 }); - await this.preferencesTab.click(); - } -} diff --git a/tests/playwright/src/model/pages/settings-cli-tab-page.ts b/tests/playwright/src/model/pages/settings-cli-tab-page.ts new file mode 100644 index 0000000000..a2f9bc0ec8 --- /dev/null +++ b/tests/playwright/src/model/pages/settings-cli-tab-page.ts @@ -0,0 +1,34 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BasePage } from './base-page'; + +export class SettingsCliPage extends BasePage { + readonly toolName: Locator; + + constructor(page: Page) { + super(page); + this.toolName = page.getByLabel('cli-name'); + } + + async waitForLoad(): Promise { + await expect(this.toolName).toBeVisible(); + } +} diff --git a/tests/playwright/src/model/pages/settings-create-gemini-page.ts b/tests/playwright/src/model/pages/settings-create-gemini-page.ts new file mode 100644 index 0000000000..e16aab8302 --- /dev/null +++ b/tests/playwright/src/model/pages/settings-create-gemini-page.ts @@ -0,0 +1,56 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BasePage } from './base-page'; + +export class SettingsCreateGeminiPage extends BasePage { + readonly apiKeyInput: Locator; + readonly createButton: Locator; + readonly goBackToResourcesButton: Locator; + + constructor(page: Page) { + super(page); + this.apiKeyInput = page.getByLabel('Enter your Gemini API key (GEMINI_API_KEY)'); + this.createButton = page.getByRole('button', { name: 'Create' }); + this.goBackToResourcesButton = page.getByRole('button', { name: 'Go back to resources' }); + } + + async waitForLoad(): Promise { + await expect(this.apiKeyInput).toBeVisible(); + await expect(this.createButton).toBeVisible(); + } + + async create(apiKey: string): Promise { + await this.apiKeyInput.fill(apiKey); + await expect(this.apiKeyInput).toHaveValue(apiKey); + await expect(this.createButton).toBeEnabled(); + await this.createButton.click(); + } + + async goBackToResources(): Promise { + await expect(this.goBackToResourcesButton).toBeVisible(); + await this.goBackToResourcesButton.click(); + } + + async createAndGoBack(apiKey: string): Promise { + await this.create(apiKey); + await this.goBackToResources(); + } +} diff --git a/tests/playwright/src/model/pages/settings-create-openai-page.ts b/tests/playwright/src/model/pages/settings-create-openai-page.ts new file mode 100644 index 0000000000..22049b819d --- /dev/null +++ b/tests/playwright/src/model/pages/settings-create-openai-page.ts @@ -0,0 +1,61 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; + +import { BasePage } from './base-page'; + +export class SettingsCreateOpenAIPage extends BasePage { + readonly baseURLInput: Locator; + readonly apiKeyInput: Locator; + readonly createButton: Locator; + readonly goBackToResourcesButton: Locator; + + constructor(page: Page) { + super(page); + this.baseURLInput = page.getByLabel('baseURL'); + this.apiKeyInput = page.getByLabel('apiKey'); + this.createButton = page.getByRole('button', { name: 'Create' }); + this.goBackToResourcesButton = page.getByRole('button', { name: 'Go back to resources' }); + } + + async waitForLoad(): Promise { + await expect(this.baseURLInput).toBeVisible(); + await expect(this.apiKeyInput).toBeVisible(); + await expect(this.createButton).toBeVisible(); + } + + async create(baseURL: string, apiKey: string): Promise { + await this.baseURLInput.fill(baseURL); + await expect(this.baseURLInput).toHaveValue(baseURL); + await this.apiKeyInput.fill(apiKey); + await expect(this.apiKeyInput).toHaveValue(apiKey); + await expect(this.createButton).toBeEnabled(); + await this.createButton.click(); + } + + async goBackToResources(): Promise { + await expect(this.goBackToResourcesButton).toBeVisible(); + await this.goBackToResourcesButton.click(); + } + + async createAndGoBack(baseURL: string, apiKey: string): Promise { + await this.create(baseURL, apiKey); + await this.goBackToResources(); + } +} diff --git a/tests/playwright/src/model/pages/settings-page.ts b/tests/playwright/src/model/pages/settings-page.ts index 0ac0e04f89..250231be74 100644 --- a/tests/playwright/src/model/pages/settings-page.ts +++ b/tests/playwright/src/model/pages/settings-page.ts @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (C) 2023 Red Hat, Inc. + * 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. @@ -16,32 +16,98 @@ * SPDX-License-Identifier: Apache-2.0 ***********************************************************************/ -import type { Locator, Page } from '@playwright/test'; +import { expect, type Locator, type Page } from '@playwright/test'; +import type { ResourceId } from '../core/types'; +import { PROVIDERS } from '../core/types'; import { BasePage } from './base-page'; +import { SettingsCliPage } from './settings-cli-tab-page'; +import { SettingsPreferencesPage } from './settings-preferences-tab-page'; +import { SettingsProxyPage } from './settings-proxy-tab-page'; +import { SettingsResourcesPage } from './settings-resources-tab-page'; -export abstract class SettingsPage extends BasePage { - readonly tabName: string; - readonly parent: Locator; - readonly header: Locator; - readonly content: Locator; +export class SettingsPage extends BasePage { + readonly resourcesTab: Locator; + readonly cliTab: Locator; + readonly proxyTab: Locator; + readonly preferencesTab: Locator; + private readonly tabs: Locator[]; - constructor(page: Page, tabName: string) { + constructor(page: Page) { super(page); - this.tabName = tabName; - this.parent = this.page.getByRole('region', { name: tabName }); - this.header = this.parent.getByRole('region', { name: 'Header' }); - this.content = this.parent.getByRole('region', { name: 'Content' }); + this.resourcesTab = page.getByRole('link', { name: 'Resources' }); + this.cliTab = page.getByRole('link', { name: 'CLI' }); + this.proxyTab = page.getByRole('link', { name: 'Proxy' }); + this.preferencesTab = page.getByRole('link', { name: 'Preferences' }); + this.tabs = [this.resourcesTab, this.cliTab, this.proxyTab, this.preferencesTab]; } - async getTab(): Promise { - let tabName = this.tabName; - if (this.tabName === 'Preferences') { - // special case for lower case first letter in 'preferences' tab - tabName = this.tabName.toLowerCase(); + async waitForLoad(): Promise { + await expect(this.resourcesTab).toBeVisible(); + await expect(this.cliTab).toBeVisible(); + await expect(this.proxyTab).toBeVisible(); + await expect(this.preferencesTab).toBeVisible(); + } + + async openResources(): Promise { + return this.openTab(this.resourcesTab, SettingsResourcesPage); + } + + async openCli(): Promise { + return this.openTab(this.cliTab, SettingsCliPage); + } + + async openProxy(): Promise { + return this.openTab(this.proxyTab, SettingsProxyPage); + } + + async openPreferences(): Promise { + return this.openTab(this.preferencesTab, SettingsPreferencesPage); + } + + getAllTabs(): Locator[] { + return this.tabs; + } + + async createResource(providerId: ResourceId, credentials: string): Promise { + const provider = PROVIDERS[providerId]; + const resourcesPage = await this.openResources(); + + if ((await resourcesPage.getCreatedResourceFor(provider.resourceId).count()) > 0) { + console.log(`Resource ${providerId} already exists, skipping...`); + return; + } + + switch (providerId) { + case 'gemini': { + const createGeminiPage = await resourcesPage.openCreateGeminiPage(); + await createGeminiPage.createAndGoBack(credentials); + break; + } + case 'openai': { + const createOpenAIPage = await resourcesPage.openCreateOpenAIPage(); + await createOpenAIPage.createAndGoBack(PROVIDERS.openai.baseURL, credentials); + break; + } + case 'openshift-ai': + throw new Error('OpenShift AI resource creation not yet implemented'); + default: + throw new Error(`Unknown provider: ${providerId}`); } - return this.page - .getByRole('navigation', { name: 'PreferencesNavigation' }) - .getByRole('link', { name: tabName, exact: true }); + + await resourcesPage.waitForLoad(); + const resource = resourcesPage.getCreatedResourceFor(provider.resourceId); + await expect(resource).toBeVisible(); + } + + async deleteResource(providerId: ResourceId): Promise { + const provider = PROVIDERS[providerId]; + const resourcesPage = await this.openResources(); + + await resourcesPage.waitForLoad(); + await resourcesPage.deleteCreatedResourceFor(provider.resourceId); + + const resource = resourcesPage.getCreatedResourceFor(provider.resourceId); + await expect(resource).not.toBeVisible(); } } diff --git a/tests/playwright/src/model/pages/settings-preferences-tab-page.ts b/tests/playwright/src/model/pages/settings-preferences-tab-page.ts new file mode 100644 index 0000000000..ca4ad2f03d --- /dev/null +++ b/tests/playwright/src/model/pages/settings-preferences-tab-page.ts @@ -0,0 +1,63 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import type { PreferenceOption } from 'src/model/core/types'; + +import { BasePage } from './base-page'; + +export class SettingsPreferencesPage extends BasePage { + readonly searchField: Locator; + + constructor(page: Page) { + super(page); + this.searchField = page.getByLabel('search preferences'); + } + + async waitForLoad(): Promise { + await expect(this.searchField).toBeVisible(); + } + + async searchPreferences(searchTerm: string): Promise { + await expect(this.searchField).toBeVisible(); + await this.searchField.fill(searchTerm); + await expect(this.searchField).toHaveValue(searchTerm); + } + + async clearSearch(): Promise { + await expect(this.searchField).toBeVisible(); + await this.searchField.clear(); + await expect(this.searchField).toHaveValue(''); + } + + getPreferenceLink(option: PreferenceOption): Locator { + const preferencesNav = this.page.getByRole('navigation', { name: 'PreferencesNavigation' }); + return preferencesNav.getByRole('link', { name: option }); + } + + getPreferenceContent(option: PreferenceOption): Locator { + return this.page.getByText(option, { exact: true }).first(); + } + + async selectPreference(option: PreferenceOption): Promise { + const preferenceLink = this.getPreferenceLink(option); + await expect(preferenceLink).toBeVisible(); + await preferenceLink.click(); + await expect(this.getPreferenceContent(option)).toBeVisible(); + } +} diff --git a/tests/playwright/src/model/pages/settings-proxy-tab-page.ts b/tests/playwright/src/model/pages/settings-proxy-tab-page.ts new file mode 100644 index 0000000000..68c17d2fd9 --- /dev/null +++ b/tests/playwright/src/model/pages/settings-proxy-tab-page.ts @@ -0,0 +1,78 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import type { ProxyConfigurationOption } from 'src/model/core/types'; +import { proxyConfigurations } from 'src/model/core/types'; + +import { BasePage } from './base-page'; + +export class SettingsProxyPage extends BasePage { + readonly proxyConfigDropdown: Locator; + readonly httpProxyField: Locator; + readonly httpsProxyField: Locator; + readonly noProxyField: Locator; + readonly proxyUpdateButton: Locator; + + constructor(page: Page) { + super(page); + this.proxyConfigDropdown = page.locator('#toggle-proxy'); + this.httpProxyField = page.locator('#httpProxy'); + this.httpsProxyField = page.locator('#httpsProxy'); + this.noProxyField = page.locator('#noProxy'); + this.proxyUpdateButton = page.getByRole('button', { name: 'Update' }); + } + + async waitForLoad(): Promise { + await expect(this.proxyConfigDropdown).toBeVisible(); + } + + getProxyFields(): Locator[] { + return [this.httpProxyField, this.httpsProxyField, this.noProxyField]; + } + + getProxyConfigOption(option: ProxyConfigurationOption): Locator { + return this.page.getByRole('button', { name: option, exact: true }); + } + + async verifyProxyConfigurationOptions(): Promise { + await expect(this.proxyConfigDropdown).toBeVisible(); + await this.proxyConfigDropdown.click(); + for (const config of proxyConfigurations) { + await expect(this.getProxyConfigOption(config.option)).toBeVisible(); + } + await this.proxyConfigDropdown.click(); + } + + async selectProxyConfigurationAndVerifyFields( + option: ProxyConfigurationOption, + shouldBeEditable: boolean, + ): Promise { + await this.proxyConfigDropdown.click(); + await this.getProxyConfigOption(option).click(); + for (const field of this.getProxyFields()) { + if (shouldBeEditable) { + await expect(field).toBeEditable(); + } else { + await expect(field).toBeDisabled(); + } + } + await expect(this.proxyUpdateButton).toBeVisible(); + await expect(this.proxyUpdateButton).toBeEnabled(); + } +} diff --git a/tests/playwright/src/model/pages/settings-resources-tab-page.ts b/tests/playwright/src/model/pages/settings-resources-tab-page.ts new file mode 100644 index 0000000000..686ac2b765 --- /dev/null +++ b/tests/playwright/src/model/pages/settings-resources-tab-page.ts @@ -0,0 +1,69 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import { featuredResources, resources } from 'src/model/core/types'; + +import { BasePage } from './base-page'; +import { SettingsCreateGeminiPage } from './settings-create-gemini-page'; +import { SettingsCreateOpenAIPage } from './settings-create-openai-page'; + +export class SettingsResourcesPage extends BasePage { + constructor(page: Page) { + super(page); + } + + async waitForLoad(): Promise { + await expect(this.getResourceRegion(featuredResources[0])).toBeVisible(); + } + + getResourceRegion(resourceId: string): Locator { + return this.page.getByRole('region', { name: resourceId }); + } + + getResourceCreateButton(displayName: string): Locator { + return this.page.getByRole('button', { name: `Create new ${displayName}` }); + } + + async openCreateGeminiPage(): Promise { + return this.openTab(this.getResourceCreateButton(resources.gemini.displayName), SettingsCreateGeminiPage); + } + + async openCreateOpenAIPage(): Promise { + return this.openTab(this.getResourceCreateButton(resources.openai.displayName), SettingsCreateOpenAIPage); + } + + getCreatedResourcesFor(resourceId: keyof typeof resources): Locator { + const resourceRegion = this.getResourceRegion(resourceId); + return resourceRegion.getByRole('region').filter({ hasText: '(Inference)' }); + } + + getCreatedResourceFor(resourceId: keyof typeof resources): Locator { + return this.getCreatedResourcesFor(resourceId).first(); + } + + getDeleteButtonForCreatedResource(resource: Locator): Locator { + return resource.getByRole('button', { name: 'Delete' }); + } + + async deleteCreatedResourceFor(resourceId: keyof typeof resources): Promise { + const resource = this.getCreatedResourceFor(resourceId); + const deleteButton = this.getDeleteButtonForCreatedResource(resource); + await deleteButton.click(); + } +} diff --git a/tests/playwright/src/model/pages/tasks-page.ts b/tests/playwright/src/model/pages/tasks-page.ts deleted file mode 100644 index 911b46e7a3..0000000000 --- a/tests/playwright/src/model/pages/tasks-page.ts +++ /dev/null @@ -1,48 +0,0 @@ -/********************************************************************** - * 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 Locator, type Page } from '@playwright/test'; -import { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { BasePage } from './base-page'; - -export class TasksPage extends BasePage { - readonly heading: Locator; - readonly taskList: Locator; - - constructor(page: Page) { - super(page); - this.heading = page.getByRole('heading', { - name: 'Tasks', - }); - this.taskList = page.getByRole('rowgroup').nth(1); - } - - async cancelLatestTask(): Promise { - const cancelButton = this.taskList.getByRole('button').and(this.taskList.getByLabel('Cancel task')).first(); - await playExpect(cancelButton).toBeEnabled(); - await cancelButton.click(); - await handleConfirmationDialog(this.page); - await handleConfirmationDialog(this.page, 'Long task example', true, 'OK'); - } - - async getStatusForLatestTask(): Promise { - return (await this.taskList.getByRole('status').first().textContent()) ?? ''; - } -} diff --git a/tests/playwright/src/model/pages/troubleshooting-page.ts b/tests/playwright/src/model/pages/troubleshooting-page.ts deleted file mode 100644 index f2a47745e1..0000000000 --- a/tests/playwright/src/model/pages/troubleshooting-page.ts +++ /dev/null @@ -1,111 +0,0 @@ -/********************************************************************** - * 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 { Locator, Page } from '@playwright/test'; -import { expect as playExpect } from '@playwright/test'; - -import { BasePage } from './base-page'; - -export class TroubleshootingPage extends BasePage { - readonly heading: Locator; - readonly header: Locator; - readonly tabs: Locator; - readonly tabContent: Locator; - readonly containerConnectionsStatus: Locator; - readonly storesStatus: Locator; - readonly logsText: Locator; - readonly logsList: Locator; - readonly storesList: Locator; - readonly gatherLogsText: Locator; - readonly reconnectProvidersButton: Locator; - readonly reconnectProvidersStatus: Locator; - - constructor(page: Page) { - super(page); - this.header = this.page.getByRole('region', { name: 'Header' }); - this.heading = this.header.getByRole('heading', { name: 'Troubleshooting' }); - this.tabs = this.page.getByRole('region', { name: 'Tabs' }); - this.tabContent = this.page.getByRole('region', { name: 'Tab Content' }); - // only available when specific tab is opened - this.containerConnectionsStatus = this.tabContent.getByRole('status', { name: 'Container Connections' }); - this.storesStatus = this.tabContent.getByRole('status', { name: 'stores' }); - this.logsText = this.tabContent.getByText('Logs', { exact: true }); - this.gatherLogsText = this.tabContent.getByText('Gather Log Files'); - this.logsList = this.tabContent.getByRole('list', { name: 'logs', exact: true }); - this.storesList = this.tabContent.getByRole('list', { name: 'stores' }); - this.reconnectProvidersButton = this.tabContent.getByRole('button', { name: 'Reconnect providers' }); - this.reconnectProvidersStatus = this.tabContent.getByRole('status', { name: 'Reconnect Providers' }); - } - - public async openRepairConnections(): Promise { - await this.openTab('Repair & Connections'); - await playExpect(this.containerConnectionsStatus).toBeVisible(); - } - - public async openStores(): Promise { - await this.openTab('Stores'); - await playExpect(this.storesStatus).toBeVisible(); - } - - public async openLogs(): Promise { - await this.openTab('Logs'); - await playExpect(this.logsText).toBeVisible(); - } - - public async openGatherLogs(): Promise { - await this.openTab('Gather Logs'); - await playExpect(this.gatherLogsText).toBeVisible(); - } - - private async openTab(tabName: string): Promise { - const link = this.tabs.getByRole('link', { name: tabName, exact: true }); - await playExpect(link, `Tab Link ${tabName} is not visible`).toBeVisible(); - await link.click(); - } - - // return locator for better processing in playwright assertions - public async getLogs(): Promise { - await this.openLogs(); - await playExpect(this.logsList).toBeVisible(); - return this.logsList; - } - - public async refreshStore(storeName: string): Promise { - await this.openStores(); - const store = this.storesList.getByRole('listitem', { name: storeName }); - await playExpect(store).toBeVisible(); - await store.scrollIntoViewIfNeeded(); - const refreshButton = store.getByRole('button', { name: 'Refresh' }); - await playExpect(refreshButton).toBeEnabled(); - await refreshButton.click(); - } - - public async getContainerConnectionsStatus(): Promise { - await this.openRepairConnections(); - await playExpect(this.containerConnectionsStatus).toBeVisible(); - return await this.containerConnectionsStatus.innerText(); - } - - public async reconnectProviders(): Promise { - await this.openRepairConnections(); - await playExpect(this.reconnectProvidersButton).toBeEnabled(); - await this.reconnectProvidersButton.click(); - await playExpect(this.reconnectProvidersStatus).toBeVisible(); - return await this.reconnectProvidersStatus.innerText(); - } -} diff --git a/tests/playwright/src/model/pages/volume-details-page.ts b/tests/playwright/src/model/pages/volume-details-page.ts deleted file mode 100644 index a1ed6601ad..0000000000 --- a/tests/playwright/src/model/pages/volume-details-page.ts +++ /dev/null @@ -1,51 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { VolumeState } from '../core/states'; -import { DetailsPage } from './details-page'; -import { VolumesPage } from './volumes-page'; - -export class VolumeDetailsPage extends DetailsPage { - readonly deleteButton: Locator; - - static readonly SUMMARY_TAB = 'Summary'; - - constructor(page: Page, name: string) { - super(page, name); - this.deleteButton = this.controlActions.getByRole('button', { - name: 'Delete Volume', - }); - } - - async isUsed(): Promise { - return (await this.header.getByTitle(VolumeState.Used).count()) > 0; - } - - async deleteVolume(): Promise { - return test.step('Delete Volume', async () => { - await playExpect(this.deleteButton).toBeEnabled(); - await this.deleteButton.click(); - await handleConfirmationDialog(this.page); - return new VolumesPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/pages/volumes-page.ts b/tests/playwright/src/model/pages/volumes-page.ts deleted file mode 100644 index 8a0a8946ea..0000000000 --- a/tests/playwright/src/model/pages/volumes-page.ts +++ /dev/null @@ -1,129 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { waitUntil, waitWhile } from '../../utility/wait'; -import { VolumeState } from '../core/states'; -import { CreateVolumePage } from './create-volume-page'; -import { MainPage } from './main-page'; -import { VolumeDetailsPage } from './volume-details-page'; - -export class VolumesPage extends MainPage { - readonly createVolumeButton: Locator; - readonly pruneVolumesButton: Locator; - readonly collectUsageDataButton: Locator; - - constructor(page: Page) { - super(page, 'volumes'); - this.createVolumeButton = this.additionalActions.getByRole('button', { name: 'Create' }); - this.pruneVolumesButton = this.additionalActions.getByRole('button', { name: 'Prune' }); - this.collectUsageDataButton = this.additionalActions.getByRole('button', { name: 'Gather volume sizes' }); - } - - async openCreateVolumePage(volumeName: string): Promise { - return test.step('Open Create Volume Page', async () => { - const row = await this.getVolumeRowByName(volumeName); - if (row !== undefined) { - throw Error('Volume is already created'); - } - - await playExpect(this.createVolumeButton).toBeEnabled(); - await this.createVolumeButton.click(); - return new CreateVolumePage(this.page); - }); - } - - async openVolumeDetails(volumeName: string): Promise { - return test.step('Open Volume Details Page', async () => { - const volumeRow = await this.getVolumeRowByName(volumeName); - if (volumeRow === undefined) { - throw Error(`Volume: ${volumeName} does not exist`); - } - const containerRowName = volumeRow.getByRole('cell').nth(3); - await containerRowName.click(); - - return new VolumeDetailsPage(this.page, volumeName); - }); - } - - async deleteVolume(volumeName: string): Promise { - return test.step('Delete Volume', async () => { - const volumeRow = await this.getVolumeRowByName(volumeName); - if (volumeRow === undefined) { - throw Error(`Volume: ${volumeName} does not exist`); - } - const containerRowDeleteButton = volumeRow.getByRole('button', { name: 'Delete Volume' }); - await playExpect(containerRowDeleteButton).toBeEnabled(); - await containerRowDeleteButton.click(); - await handleConfirmationDialog(this.page); - - return this; - }); - } - - async getVolumeRowByName(name: string): Promise { - return this.getRowByName(name, false); - } - - protected async volumeExists(name: string): Promise { - return test.step(`Check if volume ${name} exists`, async () => { - const result = await this.getVolumeRowByName(name); - return result !== undefined; - }); - } - - async countVolumesFromTable(): Promise { - return this.countRowsFromTable(); - } - - async countUsedVolumesFromTable(): Promise { - return (await this.getRowsFromTableByStatus(VolumeState.Used)).length; - } - - async waitForVolumeExists(name: string, timeout = 30_000): Promise { - return test.step(`Wait for volume ${name} to exist`, async () => { - if (!name) { - throw Error('Volume name is not provided'); - } - await waitUntil(async () => await this.volumeExists(name), { timeout }); - return true; - }); - } - - async waitForVolumeDelete(name: string, timeout = 30_000): Promise { - return test.step(`Wait for volume ${name} to be deleted`, async () => { - if (!name) { - throw Error('Volume name is not provided'); - } - await waitWhile(async () => await this.volumeExists(name), { timeout }); - return true; - }); - } - - async pruneVolumes(): Promise { - return test.step('Prune Volumes', async () => { - await playExpect(this.pruneVolumesButton).toBeEnabled(); - await this.pruneVolumesButton.click(); - await handleConfirmationDialog(this.page, 'Prune'); - return this; - }); - } -} diff --git a/tests/playwright/src/model/pages/welcome-page.ts b/tests/playwright/src/model/pages/welcome-page.ts deleted file mode 100644 index 2cd9139491..0000000000 --- a/tests/playwright/src/model/pages/welcome-page.ts +++ /dev/null @@ -1,103 +0,0 @@ -/********************************************************************** - * 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 type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { waitWhile } from '/@/utility/wait'; - -import { BasePage } from './base-page'; -import { DashboardPage } from './dashboard-page'; - -export class WelcomePage extends BasePage { - readonly welcomeMessage: Locator; - readonly telemetryConsent: Locator; - readonly skipOnBoarding: Locator; - readonly checkLoader: Locator; - readonly startOnboarding: Locator; - - constructor(page: Page) { - super(page); - this.welcomeMessage = page.getByText('Welcome to Podman Desktop'); - this.telemetryConsent = page.getByLabel('Enable telemetry'); - this.skipOnBoarding = page.getByRole('button', { - name: 'Skip', - exact: true, - }); - this.checkLoader = this.page.getByRole('heading', { - name: 'Initializing...', - }); - this.startOnboarding = page.getByRole('button', { - name: 'Start onboarding', - exact: true, - }); - } - - async turnOffTelemetry(): Promise { - return test.step('Turn off Telemetry', async () => { - const isUpdateTest = test.info().tags.includes('@update-install'); - - if (!isUpdateTest) { - await playExpect(this.startOnboarding).toBeEnabled({ timeout: 20_000 }); - } - - if (await this.telemetryConsent.isChecked()) { - await playExpect(this.telemetryConsent).toBeChecked(); - await this.telemetryConsent.uncheck({ force: true }); - } - - await playExpect(this.telemetryConsent).not.toBeChecked(); - }); - } - - async closeWelcomePage(): Promise { - return test.step('Close Welcome Page', async () => { - await playExpect(this.skipOnBoarding).toBeEnabled(); - await this.skipOnBoarding.click({ force: true }); - try { - await waitWhile(async () => await this.skipOnBoarding.isVisible(), { timeout: 5_000, diff: 250 }); - } catch (err) { - console.log('Skip Onboarding button is still visible, retrying to press the button'); - await this.skipOnBoarding.click({ force: true }); - } - return new DashboardPage(this.page); - }); - } - - /** - * Waits for application to initialize, turn off telemetry and closes welcome page - */ - async handleWelcomePage(skipIfNotPresent: boolean): Promise { - return test.step('Handle Welcome Page', async () => { - if (skipIfNotPresent) { - try { - await this.skipOnBoarding.waitFor({ state: 'visible' }); - } catch (err) { - if ((err as Error).name !== 'TimeoutError') { - throw err; - } - return; - } - } - - await this.turnOffTelemetry(); - await this.closeWelcomePage(); - await playExpect(this.welcomeMessage).toHaveCount(0); - }); - } -} diff --git a/tests/playwright/src/model/workbench/navigation.ts b/tests/playwright/src/model/workbench/navigation.ts deleted file mode 100644 index c9766c8320..0000000000 --- a/tests/playwright/src/model/workbench/navigation.ts +++ /dev/null @@ -1,131 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 test, { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { ContainersPage } from '../pages/containers-page'; -import { DashboardPage } from '../pages/dashboard-page'; -import { ExtensionsPage } from '../pages/extensions-page'; -import { ImagesPage } from '../pages/images-page'; -import { KubernetesBar } from '../pages/kubernetes-bar'; -import { PodsPage } from '../pages/pods-page'; -import { SettingsBar } from '../pages/settings-bar'; -import { VolumesPage } from '../pages/volumes-page'; - -export class NavigationBar { - readonly page: Page; - readonly navigationLocator: Locator; - readonly imagesLink: Locator; - readonly containersLink: Locator; - readonly volumesLink: Locator; - readonly podsLink: Locator; - readonly dashboardLink: Locator; - readonly settingsLink: Locator; - readonly extensionsLink: Locator; - readonly kubernetesLink: Locator; - - constructor(page: Page) { - this.page = page; - this.navigationLocator = this.page.getByRole('navigation', { - name: 'AppNavigation', - }); - this.imagesLink = this.page.getByRole('link', { name: 'Images' }); - this.containersLink = this.page.getByRole('link', { name: 'Containers' }).nth(0); - this.podsLink = this.page.getByRole('link', { name: 'Pods', exact: true }).nth(0); - this.volumesLink = this.page.getByRole('link', { name: 'Volumes' }); - this.dashboardLink = this.page.getByRole('link', { name: 'Dashboard' }); - this.settingsLink = this.page.getByRole('link', { name: 'Settings', exact: true }); - this.extensionsLink = this.navigationLocator.getByRole('link', { - name: 'Extensions', - exact: true, - }); - this.kubernetesLink = this.navigationLocator.getByRole('link', { - name: 'Kubernetes', - }); - } - - async openDashboard(): Promise { - return test.step('Open Dashboard page', async () => { - await playExpect(this.dashboardLink).toBeVisible({ timeout: 10_000 }); - await this.dashboardLink.click({ force: true }); - return new DashboardPage(this.page); - }); - } - - async openImages(): Promise { - return test.step('Open Images page', async () => { - await playExpect(this.imagesLink).toBeVisible({ timeout: 10_000 }); - await this.imagesLink.click({ force: true }); - return new ImagesPage(this.page); - }); - } - - async openContainers(): Promise { - return test.step('Open Containers page', async () => { - await playExpect(this.containersLink).toBeVisible({ timeout: 10_000 }); - await this.containersLink.click({ force: true }); - return new ContainersPage(this.page); - }); - } - - async openPods(): Promise { - return test.step('Open Pods page', async () => { - await playExpect(this.podsLink).toBeVisible({ timeout: 10_000 }); - await this.podsLink.click({ force: true }); - return new PodsPage(this.page); - }); - } - - async openSettings(): Promise { - return test.step('Open Settings Page ', async () => { - const settingsBar = new SettingsBar(this.page); - if (!(await settingsBar.settingsNavBar.isVisible())) { - await playExpect(this.settingsLink).toBeVisible({ timeout: 10_000 }); - await this.settingsLink.click({ force: true }); - } - return settingsBar; - }); - } - - async openVolumes(): Promise { - return test.step('Open Volumes page', async () => { - await playExpect(this.volumesLink).toBeVisible({ timeout: 10_000 }); - await this.volumesLink.click({ force: true }); - return new VolumesPage(this.page); - }); - } - - async openKubernetes(): Promise { - return test.step('Open Kubernetes Page ', async () => { - const kubernetesBar = new KubernetesBar(this.page); - if (!(await kubernetesBar.kubernetesNavBar.isVisible())) { - await playExpect(this.kubernetesLink).toBeVisible({ timeout: 10_000 }); - await this.kubernetesLink.click({ force: true }); - } - return kubernetesBar; - }); - } - - async openExtensions(): Promise { - return test.step('Open Extensions page', async () => { - await playExpect(this.extensionsLink).toBeVisible({ timeout: 10_000 }); - await this.extensionsLink.click({ force: true }); - return new ExtensionsPage(this.page); - }); - } -} diff --git a/tests/playwright/src/model/workbench/status-bar.ts b/tests/playwright/src/model/workbench/status-bar.ts deleted file mode 100644 index 3e90ff737f..0000000000 --- a/tests/playwright/src/model/workbench/status-bar.ts +++ /dev/null @@ -1,115 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { expect as playExpect, type Locator, type Page } from '@playwright/test'; - -import { handleConfirmationDialog } from '../../utility/operations'; -import { BasePage } from '../pages/base-page'; -import { TasksPage } from '../pages/tasks-page'; - -export class StatusBar extends BasePage { - readonly content: Locator; - readonly kindInstallationButton: Locator; - readonly kubernetesContext: Locator; - readonly versionButton: Locator; - readonly updateButtonTitle: Locator; - readonly shareYourFeedbackButton: Locator; - readonly troubleshootingButton: Locator; - readonly tasksButton: Locator; - readonly helpButton: Locator; - readonly pinProvidersButton: Locator; - readonly pinMenu: Locator; - - constructor(page: Page) { - super(page); - this.content = page.getByRole('contentinfo', { name: 'Status Bar' }); - this.kindInstallationButton = this.content.getByTitle( - 'Kind not found on your system, click to download and install it', - ); - this.kubernetesContext = this.content.getByTitle('Current Kubernetes Context'); - this.versionButton = this.content.getByRole('button', { name: /^v\d+\.\d+\.\d+(-\w+)?$/ }); - this.updateButtonTitle = this.content.getByRole('button').and(this.content.getByTitle('Update available')); - this.shareYourFeedbackButton = this.content.getByRole('button').and(this.content.getByTitle('Share your feedback')); - this.troubleshootingButton = this.content.getByRole('button').and(this.content.getByTitle('Troubleshooting')); - this.tasksButton = this.content.getByRole('button').and(this.content.getByTitle('Tasks')); - this.helpButton = this.content.getByRole('button').and(this.content.getByTitle('Help')); - this.pinProvidersButton = this.content.getByRole('button', { name: 'Pin' }); - this.pinMenu = this.page.getByTitle('Pin Menu'); - } - - public async installKindCLI(): Promise { - await playExpect(this.kindInstallationButton).toBeVisible(); - await this.kindInstallationButton.click(); - await handleConfirmationDialog(this.page, 'Kind'); - await handleConfirmationDialog(this.page, 'Kind'); - await handleConfirmationDialog(this.page, 'Kind', true, 'OK'); - } - - public async validateKubernetesContext(context: string): Promise { - await playExpect(this.kubernetesContext).toBeVisible(); - await playExpect(this.kubernetesContext).toHaveText(context); - } - - public async kindInstallationButtonIsVisible(): Promise { - return (await this.kindInstallationButton.count()) > 0; - } - - public async openTasksPage(): Promise { - await playExpect(this.tasksButton).toBeVisible(); - await this.tasksButton.click(); - return new TasksPage(this.page); - } - - public async getProviderButton(providerName: string): Promise { - await playExpect(this.pinProvidersButton, 'status bar providers must be turned on').toBeVisible(); - return this.content.getByRole('button', { name: providerName, exact: true }); - } - - public async pinProvider(providerName: string, pin: boolean): Promise { - const barProviderButton = await this.getProviderButton(providerName); - if ((await barProviderButton.isVisible()) === pin) { - return; - } - - const pinMenuProviderButton = this.pinMenu.getByRole('button', { name: providerName }); - await playExpect(this.pinMenu).not.toBeVisible({ timeout: 5000 }); - - await playExpect(this.pinProvidersButton).toBeVisible(); - await this.pinProvidersButton.click(); - await playExpect(this.pinMenu).toBeVisible({ timeout: 5000 }); - await playExpect(pinMenuProviderButton).toBeVisible(); - - await pinMenuProviderButton.click(); - await playExpect.poll(async () => await barProviderButton.isVisible()).toEqual(pin); - - //close the menu - await this.pinProvidersButton.click(); - await playExpect(this.pinMenu).not.toBeVisible({ timeout: 5000 }); - } - - public async isProviderResourceRunning(providerName: string, resourceName: string): Promise { - const barProviderButton = await this.getProviderButton(providerName); - await playExpect(barProviderButton).toBeVisible(); - const providerArea = barProviderButton.locator('..').locator('..'); - const providerTooltip = providerArea.getByLabel('tooltip'); - - await barProviderButton.hover(); - await playExpect(providerTooltip).toBeVisible(); - return (await providerTooltip.innerText()).includes('Running\n: ' + resourceName); - } -} diff --git a/tests/playwright/src/runner/podman-desktop-runner.ts b/tests/playwright/src/runner/podman-desktop-runner.ts deleted file mode 100644 index 33ebe7cc3e..0000000000 --- a/tests/playwright/src/runner/podman-desktop-runner.ts +++ /dev/null @@ -1,368 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; - -import type { ElectronApplication, JSHandle, Page } from '@playwright/test'; -import { _electron as electron, test } from '@playwright/test'; -import type { BrowserWindow } from 'electron'; - -import { waitWhile } from '../utility/wait'; -import { RunnerOptions } from './runner-options'; - -type WindowState = { - isVisible: boolean; - isDevToolsOpened: boolean; - isCrashed: boolean; -}; - -export class Runner { - private _options: object; - private _running: boolean; - private _app: ElectronApplication | undefined; - private _page: Page | undefined; - private readonly _profile: string; - private readonly _customFolder; - private readonly _testOutput: string; - private _videoAndTraceName: string | undefined; - private _runnerOptions: RunnerOptions; - private _saveTracesOnPass: boolean; - private _saveVideosOnPass: boolean; - - private static _instance: Runner | undefined; - - static async getInstance({ - runnerOptions = new RunnerOptions(), - }: { - runnerOptions?: RunnerOptions; - } = {}): Promise { - if (!Runner._instance) { - Runner._instance = new Runner({ runnerOptions }); - await Runner._instance.start(); - } - return Runner._instance; - } - - private constructor({ - runnerOptions = new RunnerOptions(), - }: { - runnerOptions?: RunnerOptions; - } = {}) { - this._running = false; - this._runnerOptions = runnerOptions; - this._profile = this._runnerOptions._profile; - this._saveTracesOnPass = this._runnerOptions._saveTracesOnPass; - this._saveVideosOnPass = this._runnerOptions._saveVideosOnPass; - this._testOutput = join(this._runnerOptions._customOutputFolder, this._profile); - this._customFolder = join(this._testOutput, this._runnerOptions._customFolder); - this._videoAndTraceName = undefined; - - // Options setting always needs to be last action in constructor in order to apply settings correctly - this._options = this.defaultOptions(); - } - - public async start(): Promise { - if (this.isRunning()) { - console.log('Podman Desktop is already running'); - return this.getPage(); - } - - try { - // start the app with given properties - this._running = true; - console.log('Starting Podman Desktop'); - console.log('Electron app launch options: '); - Object.keys(this._options).forEach(key => { - console.log(`${key}: ${(this._options as { [k: string]: string })[key]}`); - }); - this._app = await electron.launch({ - ...this._options, - }); - // setup state - this._page = await this.getElectronApp().firstWindow(); - const exe = this.getElectronApp().evaluate(async ({ app }) => { - return app.getPath('exe'); - }); - const filePath = await exe; - console.log(`The Executable Electron app. file: ${filePath}`); - - // Evaluate that the main window is visible - // at the same time, the function also makes sure that event 'ready-to-show' was triggered - // keeping this call meeses up communication between playwright and electron app on linux - // did not have time to investigate why is this occasionally happening - // const windowState = await this.getBrowserWindowState(); - } catch (err) { - console.log(`Caught exception in startup: ${err}`); - throw Error(`Podman Desktop could not be started correctly with error: ${err}`); - } - - // Direct Electron console to Node terminal. - this.getPage().on('console', console.log); - - // Start playwright tracing - await this.startTracing(); - - // also get stderr from the node process - this._app.process().stderr?.on('data', data => { - console.log(`STDERR: ${data}`); - }); - - return this._page; - } - - public getPage(): Page { - if (this._page) { - return this._page; - } else { - throw Error('Application was not started yet'); - } - } - - public getElectronApp(): ElectronApplication { - if (this._app) { - return this._app; - } else { - throw Error('Application was not started yet'); - } - } - - public async getBrowserWindow(): Promise> { - return await this.getElectronApp().browserWindow(this.getPage()); - } - - public async screenshot(filename: string): Promise { - await this.getPage().screenshot({ path: join(this._testOutput, 'screenshots', filename) }); - } - - public async startTracing(): Promise { - await this.getPage().context().tracing.start({ screenshots: true, snapshots: true, sources: true }); - } - - public async stopTracing(): Promise { - let name = ''; - if (this._videoAndTraceName) name = this._videoAndTraceName; - - name = name + '_trace.zip'; - await this.getPage() - .context() - .tracing.stop({ path: join(this._testOutput, 'traces', name) }); - } - - public async getBrowserWindowState(): Promise { - return await (await this.getBrowserWindow()).evaluate((mainWindow): Promise => { - const getState = (): { isVisible: boolean; isDevToolsOpened: boolean; isCrashed: boolean } => { - return { - isVisible: mainWindow.isVisible(), - isDevToolsOpened: mainWindow.webContents.isDevToolsOpened(), - isCrashed: mainWindow.webContents.isCrashed(), - }; - }; - - return new Promise(resolve => { - /** - * The main window is created hidden, and is shown only when it is ready. - * See {@link ../packages/main/src/mainWindow.ts} function - */ - if (mainWindow.isVisible()) { - resolve(getState()); - } else - mainWindow.once('ready-to-show', () => { - resolve(getState()); - }); - }); - }); - } - - async saveVideoAs(path: string): Promise { - const video = this.getPage().video(); - if (video) { - await video.saveAs(path); - await video.delete(); - } else { - console.log(`Video file associated was not found`); - } - } - - public async close(timeout: number = 30_000): Promise { - // Stop playwright tracing - await this.stopTracing(); - - if (!this.isRunning()) { - throw Error('Podman Desktop is not running'); - } - - if (this.getElectronApp()) { - const pid = this.getElectronApp()?.process()?.pid; - console.log(`Closing Podman Desktop with a timeout of ${timeout} ms`); - try { - await Promise.race([ - waitWhile(async () => this.isRunning(), { timeout: timeout, diff: 100 }), - this.getElectronApp().close(), - ]); - } catch (err: unknown) { - console.log(`Caught exception in closing: ${err}`); - console.log(`Trying to kill the electron app process`); - if (pid) { - console.log(`Killing the electron app process with PID: ${pid}`); - try { - process.kill(pid as number); - } catch (error: unknown) { - console.log(`Exception thrown when killing the process: ${error}`); - } - } - } - } - this._running = false; - Runner._instance = undefined; - - if (this._videoAndTraceName) { - const videoPath = join(this._testOutput, 'videos', `${this._videoAndTraceName}.webm`); - const elapsed = await this.trackTime(async () => await this.saveVideoAs(videoPath)); - console.log(`Saving a video file took: ${elapsed} ms`); - console.log(`Video file saved as: ${videoPath}`); - } - await this.removeTracesOnFinished(); - } - - async removeTracesOnFinished(): Promise { - const rawTracesPath = join(this._testOutput, 'traces', 'raw'); - - if (existsSync(rawTracesPath)) { - console.log(`Removing raw traces folder: ${rawTracesPath}`); - rmSync(rawTracesPath, { recursive: true, force: true, maxRetries: 5 }); - } - - try { - const testStatus = test.info().status; - console.log(`Test finished with status:${testStatus}`); - if (testStatus !== 'passed' && testStatus !== 'skipped') return; - } catch (err) { - console.log(`Caught exception in removing traces: ${err}`); - return; - } - - if (!process.env.KEEP_TRACES_ON_PASS && !this._saveTracesOnPass) { - const tracesPath = join(this._testOutput, 'traces', `${this._videoAndTraceName}_trace.zip`); - if (existsSync(tracesPath)) { - console.log(`Removing traces folder: ${tracesPath}`); - rmSync(tracesPath, { recursive: true, force: true, maxRetries: 5 }); - } - } - - if (!process.env.KEEP_VIDEOS_ON_PASS && !this._saveVideosOnPass) { - const videoPath = join(this._testOutput, 'videos', `${this._videoAndTraceName}.webm`); - if (existsSync(videoPath)) { - console.log(`Removing video folder: ${videoPath}`); - rmSync(videoPath, { recursive: true, force: true, maxRetries: 5 }); - } - } - } - - protected async trackTime(fn: () => Promise): Promise { - const start = performance.now(); - return await fn - .call(() => { - /* no actual logic */ - }) - .then(() => { - return performance.now() - start; - }); - } - - private defaultOptions(): object { - const pdArgs = process.env.PODMAN_DESKTOP_ARGS; - const pdBinary = process.env.PODMAN_DESKTOP_BINARY; - if (pdArgs && pdBinary) { - throw new Error( - 'PODMAN_DESKTOP_ARGS and PODMAN_DESKTOP_BINARY are both set, cannot run tests in development and production mode at the same time...', - ); - } - const directory = join(this._testOutput, 'videos'); - const tracesDir = join(this._testOutput, 'traces', 'raw'); - console.log(`video will be written to: ${directory}`); - const env = this.setupPodmanDesktopCustomFolder(); - const recordVideo = { - dir: directory, - size: { - width: 1050, - height: 700, - }, - }; - const args = pdArgs ? [pdArgs] : ['.']; - // executablePath defaults to this package's installation location: node_modules/.bin/electron - const executablePath = pdArgs ? join(pdArgs, 'node_modules', '.bin', 'electron') : (pdBinary ?? undefined); - const timeout = 45000; - return { - args, - executablePath, - env, - recordVideo, - timeout, - tracesDir, - }; - } - - private setupPodmanDesktopCustomFolder(): object { - const env: { [key: string]: string } = process.env as { [key: string]: string }; - const dir = join(this._customFolder); - console.log(`podman desktop custom config will be written to: ${dir}`); - env.PODMAN_DESKTOP_HOME_DIR = dir; - - // add a custom config file by disabling OpenDevTools - const settingsFile = resolve(dir, 'configuration', 'settings.json'); - - // create parent folder if missing - const parentDir = dirname(settingsFile); - if (!existsSync(parentDir)) { - mkdirSync(parentDir, { recursive: true }); - } - - const settingsContent = this._runnerOptions.createSettingsJson(); - - // write the file - console.log(`disabling OpenDevTools in configuration file ${settingsFile}`); - writeFileSync(settingsFile, settingsContent); - - return env; - } - - public isRunning(): boolean { - return this._running; - } - - public setOptions(value: object): void { - this._options = value; - } - - public setVideoAndTraceName(name: string): void { - this._videoAndTraceName = name; - - if (test.info().retry > 0) { - this._videoAndTraceName += `_retry-${test.info().retry}`; - } - } - - public getTestOutput(): string { - return this._testOutput; - } - - public get options(): object { - return this._options; - } -} diff --git a/tests/playwright/src/runner/runner-options.ts b/tests/playwright/src/runner/runner-options.ts deleted file mode 100644 index 743f37327b..0000000000 --- a/tests/playwright/src/runner/runner-options.ts +++ /dev/null @@ -1,98 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 - ***********************************************************************/ - -export class RunnerOptions { - public readonly _profile: string; - public readonly _customFolder: string; - public readonly _customOutputFolder: string; - public readonly _openDevTools: string; - public readonly _autoUpdate: boolean; - public readonly _autoCheckUpdates: boolean; - public readonly _extensionsDisabled: string[]; - public readonly _aiLabModelUploadDisabled: boolean; - public readonly _binaryPath: string | undefined; - public readonly _saveTracesOnPass: boolean; - public readonly _saveVideosOnPass: boolean; - public readonly _customSettings: { [key: string]: unknown } = {}; - - constructor({ - profile = '', - customFolder = 'podman-desktop', - customOutputFolder = 'tests/playwright/output/', - openDevTools = 'none', - autoUpdate = true, - autoCheckUpdates = true, - extensionsDisabled = [], - aiLabModelUploadDisabled = false, - binaryPath = undefined, - saveTracesOnPass = false, - saveVideosOnPass = false, - customSettings = {}, - }: { - profile?: string; - customFolder?: string; - customOutputFolder?: string; - openDevTools?: string; - autoUpdate?: boolean; - autoCheckUpdates?: boolean; - extensionsDisabled?: string[]; - aiLabModelUploadDisabled?: boolean; - binaryPath?: string; - saveTracesOnPass?: boolean; - saveVideosOnPass?: boolean; - customSettings?: { [key: string]: unknown }; - } = {}) { - this._profile = profile; - this._customFolder = customFolder; - this._customOutputFolder = customOutputFolder; - this._openDevTools = openDevTools; - this._autoUpdate = autoUpdate; - this._autoCheckUpdates = autoCheckUpdates; - this._extensionsDisabled = extensionsDisabled; - this._aiLabModelUploadDisabled = aiLabModelUploadDisabled; - this._binaryPath = binaryPath; - this._saveTracesOnPass = saveTracesOnPass; - this._saveVideosOnPass = saveVideosOnPass; - this._customSettings = customSettings; - } - - public createSettingsJson(): string { - console.log(`Binary path: ${this._binaryPath}`); - - if (this._binaryPath) { - return JSON.stringify({ - 'preferences.OpenDevTools': this._openDevTools, - 'extensions.autoUpdate': this._autoUpdate, - 'extensions.autoCheckUpdates': this._autoCheckUpdates, - 'extensions.disabled': this._extensionsDisabled, - 'ai-lab.modelUploadDisabled': this._aiLabModelUploadDisabled, - 'podman.binary.path': this._binaryPath, - ...this._customSettings, - }); - } - - return JSON.stringify({ - 'preferences.OpenDevTools': this._openDevTools, - 'extensions.autoUpdate': this._autoUpdate, - 'extensions.autoCheckUpdates': this._autoCheckUpdates, - 'extensions.disabled': this._extensionsDisabled, - 'ai-lab.modelUploadDisabled': this._aiLabModelUploadDisabled, - ...this._customSettings, - }); - } -} diff --git a/tests/playwright/src/setupFiles/setup-kind.ts b/tests/playwright/src/setupFiles/setup-kind.ts deleted file mode 100644 index a9075fad79..0000000000 --- a/tests/playwright/src/setupFiles/setup-kind.ts +++ /dev/null @@ -1,34 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { isLinux, isMac, isWindows } from '../utility/platform'; - -export function setupKind(): boolean[] { - const rootfulMode = process.env.ROOTFUL_MODE === 'true'; // undefined or "false" => false - const runKindTests = process.env.RUN_KIND_TESTS === 'true'; - return [rootfulMode, runKindTests]; -} - -export function canRunKindTests(): boolean { - const [rootfulMode, runKindTests] = setupKind(); - // to run the kind tests there are 3 options: - // 1-the runKindTest envvar is defined and true - // 2-the OS is linux or mac - // 3-the OS is windows + the podman machine is rootful - return runKindTests || isLinux || isMac || (isWindows && rootfulMode); -} diff --git a/tests/playwright/src/setupFiles/setup-registry.ts b/tests/playwright/src/setupFiles/setup-registry.ts deleted file mode 100644 index 49eb4d765a..0000000000 --- a/tests/playwright/src/setupFiles/setup-registry.ts +++ /dev/null @@ -1,33 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 - ***********************************************************************/ - -export function setupRegistry(): string[] { - const urlDefault = 'ghcr.io'; - const registryUrl = process.env.REGISTRY_URL ?? urlDefault; - const ciDefault = process.env.CI ? 'podmandesktop-ci' : ''; - const registryUsername = process.env.REGISTRY_USERNAME ?? ciDefault; - const tokenDef = process.env.PODMANDESKTOP_CI_BOT_TOKEN ?? ''; - const pwsdDefault = process.env.CI ? tokenDef : ''; - const registryPswdSecret = process.env.REGISTRY_PASSWD ?? pwsdDefault; - return [registryUrl, registryUsername, registryPswdSecret]; -} - -export function canTestRegistry(): boolean { - const [registry, username, passwd] = setupRegistry(); - return !!registry && !!username && !!passwd; -} diff --git a/tests/playwright/src/special-specs/installation/update-install.spec.ts b/tests/playwright/src/special-specs/installation/update-install.spec.ts deleted file mode 100644 index 937d399b00..0000000000 --- a/tests/playwright/src/special-specs/installation/update-install.spec.ts +++ /dev/null @@ -1,183 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 fs from 'node:fs'; -import { homedir } from 'node:os'; -import path from 'node:path'; - -import type { Locator } from '@playwright/test'; - -import { extensionsExternalList, podmanExtension } from '/@/model/core/extensions'; - -import { ExtensionCardPage, ExtensionCatalogCardPage } from '../..'; -import type { StatusBar } from '../../model/workbench/status-bar'; -import { expect as playExpect, test } from '../../utility/fixtures'; -import { handleConfirmationDialog } from '../../utility/operations'; -import { isLinux, isMac, isWindows } from '../../utility/platform'; - -const installExtensions = process.env.INSTALLATION_TYPE === 'custom-extensions' ? true : false; -const activeExtensionStatus = 'ACTIVE'; -let sBar: StatusBar; -let updateAvailableDialog: Locator; -let updateDialog: Locator; -let updateDownloadedDialog: Locator; - -test.skip(isLinux, 'Update is not supported on Linux'); - -test.beforeAll(async ({ runner, page, statusBar }) => { - runner.setVideoAndTraceName('update-e2e'); - - sBar = statusBar; - updateAvailableDialog = page.getByRole('dialog', { name: 'Update Available now' }); - updateDialog = page.getByRole('dialog', { name: 'Update', exact: true }); - updateDownloadedDialog = page.getByRole('dialog', { name: 'Update Downloaded', exact: true }); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Podman Desktop Update installation', { tag: '@update-install' }, () => { - test('Update is offered automatically on startup', async ({ welcomePage }) => { - await playExpect(updateAvailableDialog).toBeVisible(); - const updateNowButton = updateAvailableDialog.getByRole('button', { name: 'Update Now' }); - await playExpect(updateNowButton).toBeVisible(); - const doNotshowButton = updateAvailableDialog.getByRole('button', { name: `Don't show again` }); - await playExpect(doNotshowButton).toBeVisible(); - const cancelButton = updateAvailableDialog.getByRole('button', { name: 'Cancel' }); - await playExpect(cancelButton).toBeVisible(); - await cancelButton.click(); - await playExpect(updateAvailableDialog).not.toBeVisible(); - // handle welcome page now - await welcomePage.handleWelcomePage(true); - }); - - test.describe - .serial('External Extensions installation', () => { - test.skip(!installExtensions || isMac, 'Skipping extension installation, testing fresh/vanilla update'); - - test('Podman Extension is activated', async ({ navigationBar, page }) => { - const extensions = await navigationBar.openExtensions(); - await playExpect(async () => { - await extensions.openInstalledTab(); - const podmanExtensionCard = new ExtensionCardPage( - page, - podmanExtension.extensionName, - podmanExtension.extensionFullLabel, - ); - await podmanExtensionCard.card.scrollIntoViewIfNeeded(); - await playExpect(podmanExtensionCard.status).toHaveText(activeExtensionStatus); - }).toPass({ timeout: 30_000 }); - }); - - extensionsExternalList.forEach(extension => { - test(`Installation of ${extension.extensionFullName}`, async ({ navigationBar, page }) => { - test.setTimeout(200_000); - const extensionsPage = await navigationBar.openExtensions(); - await extensionsPage.openCatalogTab(); - const extensionCatalogCard = new ExtensionCatalogCardPage(page, extension.extensionName); - await playExpect(extensionCatalogCard.parent).toBeVisible(); - await extensionCatalogCard.install(180_000); - await test.step('Extension is installed', async () => { - await extensionsPage.openInstalledTab(); - await playExpect - .poll(async () => await extensionsPage.extensionIsInstalled(extension.extensionFullLabel)) - .toBeTruthy(); - const extensionDetailsPage = await extensionsPage.openExtensionDetails( - extension.extensionLabel, - extension.extensionFullLabel, - extension.extensionFullName, - ); - await playExpect(extensionDetailsPage.heading).toBeVisible(); - await test.step.skip('Extension is active - unstable on windows now', async () => { - await playExpect.soft(extensionDetailsPage.status).toHaveText(activeExtensionStatus, { timeout: 20_000 }); - }); - }); - }); - }); - }); - - test('Version button is visible', async () => { - await playExpect(sBar.content).toBeVisible(); - await playExpect(sBar.versionButton).toBeVisible(); - }); - - test('User initiated update option is available', async ({ page }) => { - await playExpect(sBar.updateButtonTitle).toHaveText(await sBar.versionButton.innerText()); - await sBar.updateButtonTitle.click(); - await handleConfirmationDialog(page, 'Update Available now', false, '', 'Cancel'); - }); - - test('Update can be initiated', async () => { - await sBar.updateButtonTitle.click(); - await playExpect(updateAvailableDialog).toBeVisible(); - const updateNowButton = updateAvailableDialog.getByRole('button', { name: 'Update now' }); - await playExpect(updateNowButton).toBeVisible(); - await updateNowButton.click(); - await playExpect(updateAvailableDialog).not.toBeVisible(); - }); - - test('Update is progressing until restart is offered', async ({ page }) => { - test.setTimeout(150000); - await sBar.updateButtonTitle.click(); - await playExpect(updateDialog).toBeVisible(); - // the dialog might change in meantime from Update (in progress) to Update downloaded. - // now it takes some time to perform, in case of failure, PD gets closed - await playExpect(updateDownloadedDialog).toBeVisible({ timeout: 120000 }); - // some buttons - await handleConfirmationDialog(page, 'Update Downloaded', false, 'Restart', 'Cancel'); - await playExpect(updateDownloadedDialog).not.toBeVisible(); - }); - - // Running the test to make sure we download correct architecture of the installer file - // setup.exe should contain both installers (for x64 and arm64) so that option is always valid - // or depending on what process.arch returns - test(`Correct installer file is downloaded for ${process.arch} architecture during update`, async () => { - test.skip(isLinux, 'This test runs only on Windows and Mac platform'); - const fileFormatRegexp = isWindows ? 'exe' : 'zip|dmg'; - const generalInstallerPattern = isWindows ? 'setup' : 'universal'; - // get installer path - windows should be on %LocalAppData%/podman-desktop-updater/pending/ - const cacheDir = isWindows - ? (process.env['LOCALAPPDATA'] ?? path.join(homedir(), 'AppData', 'Local')) - : path.join(homedir(), 'Library', 'Caches'); - const installerPath = path.join(cacheDir, 'podman-desktop-updater', 'pending'); - playExpect( - fs.existsSync(installerPath), - `Directory with installer files (${installerPath}) does not exist`, - ).toBeTruthy(); - const files = await fs.promises.readdir(installerPath); - const findFiles = files.filter(file => new RegExp(`^podman-desktop.*\\.(${fileFormatRegexp})$`).exec(file)); - playExpect( - findFiles.length, - `No files with ${fileFormatRegexp} were found during update on ${installerPath}`, - ).toBeGreaterThanOrEqual(1); - playExpect(findFiles.length, `More than one installer files were found: ${findFiles.join()}`).toEqual(1); - // on windows it is valid to get general setup.exe or setup-$arch.exe file - if (process.arch === 'x64') { - playExpect(findFiles[0]).toMatch( - new RegExp(`podman-desktop.*-(x64|${generalInstallerPattern}).${fileFormatRegexp}`), - ); - } else if (process.arch === 'arm64') { - playExpect(findFiles[0]).toMatch( - new RegExp(`podman-desktop.*-(arm64|${generalInstallerPattern}).${fileFormatRegexp}`), - ); - } else { - throw new Error(`Unsupported architecture: ${process.arch}`); - } - }); -}); diff --git a/tests/playwright/src/special-specs/podman-remote/podman-remote.spec.ts b/tests/playwright/src/special-specs/podman-remote/podman-remote.spec.ts deleted file mode 100644 index 89369dd1fc..0000000000 --- a/tests/playwright/src/special-specs/podman-remote/podman-remote.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Locator } from '@playwright/test'; - -import { ResourceConnectionCardPage } from '/@/model/pages/resource-connection-card-page'; -import { ResourcesPage } from '/@/model/pages/resources-page'; - -import { PreferencesPage } from '../../model/pages/preferences-page'; -import { expect as playExpect, test } from '../../utility/fixtures'; - -// Image is pulled onto the podman machine before the test execution -const IMAGE = 'ghcr.io/podmandesktop-ci/alpine-remote'; -const REMOTE_MACHINE = 'remote-machine'; -const PODMAN = 'podman'; - -test.beforeAll(async ({ runner, welcomePage }) => { - runner.setVideoAndTraceName('podman-remote-e2e'); - - await welcomePage.handleWelcomePage(true); -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120_000); - await runner.close(); -}); - -test.describe.serial('Verification of Podman Remote', { tag: ['@podman-remote'] }, () => { - test('Check Remote Podman Connection is not available', async ({ page, navigationBar }) => { - // open preferences page - const settingsBar = await navigationBar.openSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect(resourcesPage.heading).toBeVisible(); - const podmanResource = new ResourceConnectionCardPage(page, 'podman'); - await playExpect(podmanResource.card).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible('podman')).toBeTruthy(); - const resourcesPodmanConnections = new ResourceConnectionCardPage(page, PODMAN, REMOTE_MACHINE); - await playExpect.poll(async () => await resourcesPodmanConnections.doesResourceElementExist()).toBeFalsy(); - }); - test('Image on the remote machine is not visible in Podman Desktop', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(async () => await imagesPage.waitForImageExists(IMAGE)).rejects.toThrow(); - }); - - test('Podman Remote can be enabled in Preferences', async ({ page, navigationBar }) => { - // open preferences page - const settingsBar = await navigationBar.openSettings(); - await settingsBar.expandPreferencesTab(); - const podmanExtensionLink = settingsBar.getPreferencesLinkLocator('Extension: Podman'); - await playExpect(podmanExtensionLink).toBeVisible(); - await podmanExtensionLink.click(); - const preferencesPage = new PreferencesPage(page); - await playExpect(preferencesPage.heading).toBeVisible(); - await playExpect(preferencesPage.content).toHaveText(/Extension.*Podman/, { ignoreCase: true, useInnerText: true }); - const podmanRemoteCheckbox = preferencesPage.content.getByRole('checkbox', { - name: 'Load remote system connections (ssh)', - }); - await playExpect(podmanRemoteCheckbox).toBeEnabled(); - await toggleCheckbox(podmanRemoteCheckbox, true); - }); - - test('Check Remote Podman Connection is available', async ({ page, navigationBar }) => { - // open preferences page - const settingsBar = await navigationBar.openSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect(resourcesPage.heading).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(PODMAN)).toBeTruthy(); - const resourcesPodmanConnections = new ResourceConnectionCardPage(page, PODMAN, REMOTE_MACHINE); - await playExpect - .poll(async () => await resourcesPodmanConnections.doesResourceElementExist(), { timeout: 15_000 }) - .toBeTruthy(); - await playExpect(resourcesPodmanConnections.resourceElementConnectionStatus).toHaveText('RUNNING'); - }); - test('Image on the remote machine is now visible', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect.poll(async () => imagesPage.waitForImageExists(IMAGE, 8_000), { timeout: 10_000 }).toBeTruthy(); - }); -}); - -export async function toggleCheckbox(checkbox: Locator, checked: boolean): Promise { - return test.step(`Ensure checkbox is ${checked ? 'checked' : 'unchecked'}`, async () => { - if (checked !== (await checkbox.isChecked())) { - await checkbox.locator('..').click(); - playExpect(await checkbox.isChecked()).toBe(checked); - } - }); -} diff --git a/tests/playwright/src/special-specs/ui-stress/ui-stress.spec.ts b/tests/playwright/src/special-specs/ui-stress/ui-stress.spec.ts deleted file mode 100644 index e6af5a5e4f..0000000000 --- a/tests/playwright/src/special-specs/ui-stress/ui-stress.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -/********************************************************************** - * 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 { expect as playExpect, test } from '../../utility/fixtures'; -import { isLinux } from '../../utility/platform'; -import { waitForPodmanMachineStartup } from '../../utility/wait'; - -const numberOfObjects = Number(process.env.OBJECT_NUM) || 100; -console.log(`numberOfObjects => ${numberOfObjects}`); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('ui-stress-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120_000); - await runner.close(); -}); - -test.describe.serial('Verification of UI handling lots of objects', { tag: ['@ui-stress'] }, () => { - test(`Verification of ${numberOfObjects + 2} images`, async ({ navigationBar }) => { - test.setTimeout(300_000); - - const images = await navigationBar.openImages(); - await playExpect(images.heading).toBeVisible({ timeout: 10_000 }); - //count images => 1 original image + (1 tagged * numberOfObjects) + 1 localhost/podman-pause from pods (only ubuntu!) = numberOfObjects + 2 - const expectedImages = isLinux ? numberOfObjects + 2 : numberOfObjects + 1; - await playExpect.poll(async () => await images.countRowsFromTable(), { timeout: 10_000 }).toBe(expectedImages); - for (let imgNum = 1; imgNum <= numberOfObjects; imgNum++) { - await playExpect - .poll(async () => await images.waitForRowToExists(`localhost/my-image-${imgNum}`), { timeout: 0 }) - .toBeTruthy(); - const imgRowLocator = await images.getRowByName(`localhost/my-image-${imgNum}`); - await imgRowLocator?.scrollIntoViewIfNeeded(); - } - }); - - test(`Verification of ${3 * numberOfObjects} containers`, async ({ navigationBar }) => { - test.setTimeout(300_000); - - const containers = await navigationBar.openContainers(); - await playExpect(containers.heading).toBeVisible({ timeout: 10_000 }); - //count containers => (1 manually created + 2 from creating pods) * numberOfObjects = 3 * numberOfObjects - await playExpect - .poll(async () => await containers.countRowsFromTable(), { timeout: 10_000 }) - .toBe(3 * numberOfObjects); - for (let containerNum = 1; containerNum <= numberOfObjects; containerNum++) { - await playExpect - .poll(async () => await containers.waitForRowToExists(`my-container-${containerNum}`), { timeout: 0 }) - .toBeTruthy(); - const containerRowLocator = await containers.getRowByName(`my-container-${containerNum}`); - await containerRowLocator?.scrollIntoViewIfNeeded(); - } - }); - - test(`Verification of ${numberOfObjects} pods`, async ({ navigationBar }) => { - test.setTimeout(300_000); - - const pods = await navigationBar.openPods(); - await playExpect(pods.heading).toBeVisible({ timeout: 10_000 }); - //count pods => 1 manually created * numberOfObjects = numberOfObjects - await playExpect.poll(async () => await pods.countRowsFromTable(), { timeout: 10_000 }).toBe(numberOfObjects); - for (let podNum = 1; podNum <= numberOfObjects; podNum++) { - await playExpect.poll(async () => await pods.waitForRowToExists(`my-pod-${podNum}`), { timeout: 0 }).toBeTruthy(); - const podRowLocator = await pods.getRowByName(`my-pod-${podNum}`); - await podRowLocator?.scrollIntoViewIfNeeded(); - } - }); -}); diff --git a/tests/playwright/src/specs/cancelable-task-smoke.spec.ts b/tests/playwright/src/specs/cancelable-task-smoke.spec.ts deleted file mode 100644 index 60ede48024..0000000000 --- a/tests/playwright/src/specs/cancelable-task-smoke.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -/********************************************************************** - * 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 { CommandPalette } from '../model/pages/command-palette'; -import { ExperimentalPage } from '../model/pages/experimental-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { StatusBar } from '../model/workbench/status-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const longRunningTaskName = 'quay.io/fbenoit/long-task-example:v1.0'; -const taskName = 'Dummy Long Task'; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('cancelable-task-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - await page.screenshot(); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Cancelable task verification', { tag: '@smoke' }, () => { - test('Enable all experimental features', async ({ page, navigationBar }) => { - await navigationBar.openSettings(); - await page.screenshot(); - const settingsBar = new SettingsBar(page); - const experimentalPage = await settingsBar.openTabPage(ExperimentalPage); - await experimentalPage.enableAllExperimentalFeatures(); - }); - - test('Install long running task extension', async ({ navigationBar }) => { - const extensionsPage = await navigationBar.openExtensions(); - await extensionsPage.installExtensionFromOCIImage(longRunningTaskName); - }); - - test('Execute long running task', async ({ page }) => { - const commandPalettePage = new CommandPalette(page); - await commandPalettePage.executeCommand(taskName); - }); - - test('Cancel long running task', async ({ page }) => { - const statusBar = new StatusBar(page); - const tasksPage = await statusBar.openTasksPage(); - await playExpect(tasksPage.heading).toBeVisible(); - await tasksPage.cancelLatestTask(); - await playExpect.poll(async () => await tasksPage.getStatusForLatestTask()).toContain('canceled'); - }); -}); diff --git a/tests/playwright/src/specs/cli-tools-smoke.spec.ts b/tests/playwright/src/specs/cli-tools-smoke.spec.ts deleted file mode 100644 index d404cb6757..0000000000 --- a/tests/playwright/src/specs/cli-tools-smoke.spec.ts +++ /dev/null @@ -1,81 +0,0 @@ -/********************************************************************** - * 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 { CLIToolsPage } from '../model/pages/cli-tools-page'; -import type { SettingsBar } from '../model/pages/settings-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { isLinux, isMac } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -let settingsBar: SettingsBar; -let cliToolsPage: CLIToolsPage; -const tools = process.env.CLI_TOOLS; -const allTools = ['Kind', 'Compose', 'kubectl']; -const toolsToTest = tools === 'all' ? allTools : tools ? tools.split(',') : ['Kind']; - -test.skip(!!isLinux || !!isMac, 'Tests suite should not run on Linux or Mac platform'); - -test.beforeAll(async ({ runner, page, welcomePage }) => { - runner.setVideoAndTraceName('cli-tools-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -toolsToTest.forEach(tool => { - test.describe - .serial('CLI tools tests', () => { - test.beforeAll(async ({ navigationBar, page }) => { - settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - - cliToolsPage = new CLIToolsPage(page); - await playExpect(cliToolsPage.toolsTable).toBeVisible({ timeout: 10_000 }); - await playExpect.poll(async () => await cliToolsPage.toolsTable.count()).toBeGreaterThan(0); - await cliToolsPage.uninstallTool(tool); - await playExpect - .poll(async () => await cliToolsPage.getCurrentToolVersion(tool), { timeout: 60_000 }) - .toBeFalsy(); - }); - - test(`Install ${tool} -> downgrade -> uninstall`, async () => { - test.setTimeout(120_000); - - await cliToolsPage.installTool(tool); - await cliToolsPage.downgradeTool(tool); - await cliToolsPage.uninstallTool(tool); - await playExpect - .poll(async () => await cliToolsPage.getCurrentToolVersion(tool), { timeout: 90_000 }) - .toBeFalsy(); - }); - - test(`Install old version of ${tool} -> upgrade -> uninstall`, async () => { - test.setTimeout(120_000); - - await cliToolsPage.installToolWithSecondLatestVersion(tool); - await cliToolsPage.updateTool(tool); - await cliToolsPage.uninstallTool(tool); - await playExpect - .poll(async () => await cliToolsPage.getCurrentToolVersion(tool), { timeout: 90_000 }) - .toBeFalsy(); - }); - }); -}); diff --git a/tests/playwright/src/specs/compose-onboarding-smoke.spec.ts b/tests/playwright/src/specs/compose-onboarding-smoke.spec.ts deleted file mode 100644 index 2a5a6e2014..0000000000 --- a/tests/playwright/src/specs/compose-onboarding-smoke.spec.ts +++ /dev/null @@ -1,162 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { Page } from '@playwright/test'; - -import { CLIToolsPage } from '../model/pages/cli-tools-page'; -import { ComposeLocalInstallPage } from '../model/pages/compose-onboarding/compose-local-install-page'; -import { ComposeOnboardingPage } from '../model/pages/compose-onboarding/compose-onboarding-page'; -import { ComposeVersionPage } from '../model/pages/compose-onboarding/compose-version-page'; -import { ComposeWideInstallPage } from '../model/pages/compose-onboarding/compose-wide-install-page'; -import { ResourceCliCardPage } from '../model/pages/resource-cli-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import type { NavigationBar } from '../model/workbench/navigation'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { isCI, isLinux } from '../utility/platform'; - -const RESOURCE_NAME: string = 'Compose'; - -let composeVersion: string; -// property that will make sure that on linux we can run only partial tests, by default this is turned off -const composePartialInstallation = process.env.COMPOSE_PARTIAL_INSTALL ?? false; - -test.skip(!!isCI && isLinux, 'Tests suite should not run on Linux platform'); - -test.beforeAll(async ({ runner, welcomePage }) => { - runner.setVideoAndTraceName('compose-onboarding-e2e'); - await welcomePage.handleWelcomePage(true); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Compose onboarding workflow verification', { tag: '@smoke' }, () => { - test.afterEach(async ({ navigationBar }) => { - await navigationBar.openDashboard(); - }); - - test('Compose onboarding button Setup is available', async ({ page, navigationBar }) => { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - - await playExpect.poll(async () => resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const composeResourceCard = new ResourceCliCardPage(page, RESOURCE_NAME); - await composeResourceCard.card.scrollIntoViewIfNeeded(); - const setupButton = composeResourceCard.setupButton; - await playExpect( - setupButton, - 'Compose Setup button is not present, perhaps compose is already installed', - ).toBeVisible({ timeout: 10000 }); - }); - - test('Can enter Compose onboarding', async ({ page, navigationBar }) => { - const onboardingPage = await openComposeOnboarding(page, navigationBar); - - await playExpect(onboardingPage.heading).toBeVisible(); - - const onboardingVersionPage = new ComposeVersionPage(page); - await playExpect(onboardingVersionPage.onboardingStatusMessage).toHaveText('Compose download'); - await playExpect(onboardingVersionPage.versionStatusMessage).toBeVisible(); - - composeVersion = await onboardingVersionPage.getVersion(); - }); - - test('Can install Compose locally', async ({ page, navigationBar }) => { - const onboardingPage = await openComposeOnboarding(page, navigationBar); - await onboardingPage.nextStepButton.click(); - - const onboardigLocalPage = new ComposeLocalInstallPage(page); - await playExpect(onboardigLocalPage.onboardingStatusMessage).toHaveText('Compose successfully Downloaded', { - timeout: 50000, - }); - - await onboardingPage.cancelSetupButtion.click(); - - const skipDialog = page.getByRole('dialog', { name: 'Skip Setup Popup', exact: true }); - const skipOkButton = skipDialog.getByRole('button', { name: 'Ok' }); - await skipOkButton.click(); - }); - - test('Can resume Compose onboarding and it can be canceled', async ({ page, navigationBar }) => { - await openComposeOnboarding(page, navigationBar); - const onboardingLocalPage = new ComposeLocalInstallPage(page); - - await playExpect(onboardingLocalPage.onboardingStatusMessage).toHaveText('Compose successfully Downloaded'); - await playExpect(onboardingLocalPage.wideDownloadAvailableMessage).toBeVisible(); - await playExpect(onboardingLocalPage.nextStepButton).toBeVisible(); - await onboardingLocalPage.cancelSetupButtion.click(); - - const skipDialog = page.getByRole('dialog', { name: 'Skip Setup Popup', exact: true }); - const skipOkButton = skipDialog.getByRole('button', { name: 'Ok' }); - await skipOkButton.click(); - }); - - test('Can install Compose system-wide', async ({ page, navigationBar }) => { - test.skip(!!composePartialInstallation, 'Partial installation of Compose is enabled'); - - const onboardingPage = await openComposeOnboarding(page, navigationBar); - await onboardingPage.nextStepButton.click(); - - const onboardingWidePage = new ComposeWideInstallPage(page); - await playExpect(onboardingWidePage.onboardingStatusMessage).toHaveText('Compose installed', { timeout: 50000 }); - await playExpect(onboardingWidePage.mainPage.getByRole('heading', { name: 'How To Use Compose' })).toBeVisible(); - await playExpect(onboardingWidePage.composeCommandMessage).toBeVisible(); - await playExpect(onboardingWidePage.nextStepButton).toBeEnabled(); - await onboardingWidePage.nextStepButton.click(); - // expects redirection to the Resources page - const resourcesPage = new ResourcesPage(page); - await playExpect(resourcesPage.heading).toBeVisible(); - }); - - test('Verify Compose was installed', async ({ page, navigationBar }) => { - test.skip(!!composePartialInstallation, 'Partial installation of Compose is enabled'); - - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const composeBox = new ResourceCliCardPage(page, RESOURCE_NAME); - const setupButton = composeBox.setupButton; - await playExpect(setupButton).toBeHidden(); - - const cliToolsPage = await settingsBar.openTabPage(CLIToolsPage); - const composeRow = cliToolsPage.toolsTable.getByLabel(RESOURCE_NAME); - const composeVersionInfo = composeRow.getByLabel('cli-version'); - await playExpect(composeVersionInfo).toContainText('docker-compose ' + composeVersion); - }); -}); - -async function openComposeOnboarding(page: Page, navigationBar: NavigationBar): Promise { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect(resourcesPage.heading).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const composeResourceCard = new ResourceCliCardPage(page, RESOURCE_NAME); - await composeResourceCard.card.scrollIntoViewIfNeeded(); - const setupButton = composeResourceCard.setupButton; - await playExpect( - setupButton, - 'Compose Setup button is not present, perhaps compose is already installed', - ).toBeVisible({ timeout: 10000 }); - await setupButton.click(); - return new ComposeOnboardingPage(page); -} diff --git a/tests/playwright/src/specs/container-smoke.spec.ts b/tests/playwright/src/specs/container-smoke.spec.ts deleted file mode 100644 index 5cb26055c6..0000000000 --- a/tests/playwright/src/specs/container-smoke.spec.ts +++ /dev/null @@ -1,342 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { ContainerState } from '../model/core/states'; -import type { ContainerInteractiveParams } from '../model/core/types'; -import { ContainersPage } from '../model/pages/containers-page'; -import { ImageDetailsPage } from '../model/pages/image-details-page'; -import type { ImagesPage } from '../model/pages/images-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteContainer, deleteImage } from '../utility/operations'; -import { waitForPodmanMachineStartup, waitWhile } from '../utility/wait'; - -const imageToPull = 'ghcr.io/linuxcontainers/alpine'; -const imageTag = 'latest'; -const containerToRun = 'alpine-container'; -const containerList = ['first', 'second', 'third']; -const containerStartParams: ContainerInteractiveParams = { attachTerminal: false }; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('containers-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - // wait giving a time to podman desktop to load up - let images: ImagesPage; - try { - images = await new NavigationBar(page).openImages(); - } catch (error) { - await runner.screenshot('error-on-open-images.png'); - throw error; - } - await waitWhile(async () => await images.pageIsEmpty(), { - sendError: false, - message: 'Images page is empty, there are no images present', - }); - try { - await deleteContainer(page, containerToRun); - } catch (error) { - await runner.screenshot('error-on-open-containers.png'); - throw error; - } -}); - -test.afterAll(async ({ runner, page }) => { - test.setTimeout(90000); - - try { - await deleteContainer(page, containerToRun); - for (const container of containerList) { - await deleteContainer(page, container); - } - - await deleteImage(page, imageToPull); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Verification of container creation workflow', { tag: '@smoke' }, () => { - test.describe.configure({ retries: 2 }); - - test(`Pulling of '${imageToPull}:${imageTag}' image`, async ({ navigationBar }) => { - test.setTimeout(90_000); - - let images = await navigationBar.openImages(); - const pullImagePage = await images.openPullImage(); - images = await pullImagePage.pullImage(imageToPull, imageTag); - - await playExpect.poll(async () => await images.waitForImageExists(imageToPull), { timeout: 10_000 }).toBeTruthy(); - }); - - test(`Start a container '${containerToRun}' from image`, async ({ navigationBar }) => { - let images = await navigationBar.openImages(); - const imageDetails = await images.openImageDetails(imageToPull); - const runImage = await imageDetails.openRunImage(); - const containers = await runImage.startContainer(containerToRun, containerStartParams); - await playExpect(containers.header).toBeVisible(); - await playExpect - .poll(async () => await containers.containerExists(containerToRun), { timeout: 15_000 }) - .toBeTruthy(); - const containerDetails = await containers.openContainersDetails(containerToRun); - await playExpect - .poll(async () => await containerDetails.getState(), { timeout: 15_000 }) - .toContain(ContainerState.Running); - - images = await navigationBar.openImages(); - playExpect(await images.getCurrentStatusOfImage(imageToPull)).toBe('USED'); - }); - - test('Test navigation between pages', async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await containersDetails.backLink.click(); - await playExpect(containers.heading).toBeVisible(); - - await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await containersDetails.closeButton.click(); - await playExpect(containers.heading).toBeVisible(); - }); - test('Open a container details', async ({ navigationBar, page }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await playExpect(containersDetails.heading).toContainText(containerToRun); - // test state of container in summary tab - const containerState = await containersDetails.getState(); - playExpect(containerState).toContain(ContainerState.Running); - // check Logs output - await containersDetails.activateTab('Logs'); - const helloWorldMessage = containersDetails.getPage().getByText('No Log'); - await playExpect(helloWorldMessage).toBeVisible(); - // Switch between various other tabs, no checking of the content - await containersDetails.activateTab('Inspect'); - await containersDetails.activateTab('Kube'); - await containersDetails.activateTab('Terminal'); - - await playExpect(containersDetails.terminalContent).toBeVisible(); - await playExpect(containersDetails.terminalContent).toContainText('#'); - await page.waitForTimeout(1_000); - await containersDetails.terminalInput.pressSequentially('ps', { delay: 15 }); - await containersDetails.terminalInput.press('Enter'); - await playExpect(containersDetails.terminalContent).toContainText('root'); - await playExpect(containersDetails.terminalContent).toContainText('/bin/sh'); - }); - test('Redirecting to image details from a container details', async ({ page, navigationBar }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await playExpect(containersDetails.heading).toContainText(containerToRun); - await playExpect(containersDetails.imageLink).toBeVisible(); - await containersDetails.imageLink.click(); - const imageDetails = new ImageDetailsPage(page, imageToPull); - await playExpect(imageDetails.heading).toBeVisible(); - await playExpect(imageDetails.heading).toContainText(imageToPull); - }); - test('Stopping a container from Container details', async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await playExpect(containersDetails.heading).toContainText(containerToRun); - // test state of container in summary tab - playExpect(await containersDetails.getState()).toContain(ContainerState.Running); - await containersDetails.stopContainer(); - - await playExpect - .poll(async () => await containersDetails.getState(), { timeout: 30_000 }) - .toContain(ContainerState.Exited); - await playExpect(containersDetails.startButton).toBeVisible(); - }); - - test(`Start a container from the Containers page`, async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await playExpect(containersDetails.heading).toContainText(containerToRun); - // test state of container in summary tab - await navigationBar.openContainers(); - await playExpect.poll(async () => await containers.containerExists(containerToRun)).toBeTruthy(); - - await containers.startContainer(containerToRun); - - await containers.openContainersDetails(containerToRun); - await playExpect - .poll(async () => containersDetails.getState(), { timeout: 30_000 }) - .toContain(ContainerState.Running); - }); - - test(`Stop a container from the Containers page`, async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toBeVisible(); - await playExpect(containersDetails.heading).toContainText(containerToRun); - // test state of container in summary tab - await navigationBar.openContainers(); - await playExpect.poll(async () => await containers.containerExists(containerToRun)).toBeTruthy(); - - await containers.stopContainer(containerToRun); - - await containers.openContainersDetails(containerToRun); - await playExpect - .poll(async () => containersDetails.getState(), { timeout: 30_000 }) - .toContain(ContainerState.Exited); - }); - - test('Deleting a container from Container details', async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containersDetails.heading).toContainText(containerToRun); - const containersPage = await containersDetails.deleteContainer(); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(containerToRun), { timeout: 10_000 }) - .toBeFalsy(); - }); - - test(`Deleting a container from the Containers page`, async ({ navigationBar }) => { - //re-start the container from an image - let images = await navigationBar.openImages(); - const imageDetails = await images.openImageDetails(imageToPull); - const runImage = await imageDetails.openRunImage(); - const containers = await runImage.startContainer(containerToRun, containerStartParams); - await playExpect(containers.header).toBeVisible(); - await playExpect - .poll(async () => await containers.containerExists(containerToRun), { timeout: 10_000 }) - .toBeTruthy(); - const containerDetails = await containers.openContainersDetails(containerToRun); - await playExpect - .poll(async () => await containerDetails.getState(), { timeout: 15_000 }) - .toContain(ContainerState.Running); - - images = await navigationBar.openImages(); - playExpect(await images.getCurrentStatusOfImage(imageToPull)).toBe('USED'); - - //delete it from containers page - await navigationBar.openContainers(); - const containersPage = await containers.deleteContainer(containerToRun); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(containerToRun), { timeout: 30_000 }) - .toBeFalsy(); - }); - - test('Prune containers', async ({ page, navigationBar }) => { - test.setTimeout(210_000); - - const stopStatusArray = [ContainerState.Stopped, ContainerState.Exited]; - const stopStatusRegex = new RegExp(`${stopStatusArray.join('|')}`); - - //Start 3 containers - for (const container of containerList) { - const images = await navigationBar.openImages(); - const containersPage = await images.startContainerWithImage(imageToPull, container, containerStartParams); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeTruthy(); - } - //Stop a container, prune, and repeat - for (const container of containerList) { - let containersPage = new ContainersPage(page); - const containersDetails = await containersPage.stopContainerFromDetails(container); - await playExpect - .poll(async () => await containersDetails.getState(), { timeout: 60_000 }) - .toMatch(stopStatusRegex); - containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await containersPage.pruneContainers(); - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeFalsy(); - } - - //Start and stop 3 containers - for (const container of containerList) { - const images = await navigationBar.openImages(); - const containersPage = await images.startContainerWithImage(imageToPull, container, containerStartParams); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeTruthy(); - const containersDetails = await containersPage.stopContainerFromDetails(container); - await playExpect - .poll(async () => await containersDetails.getState(), { timeout: 60_000 }) - .toMatch(stopStatusRegex); - } - //Prune the 3 stopped containers at the same time - const containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await containersPage.pruneContainers(); - for (const container of containerList) { - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeFalsy(); - } - }); - - test('Bulk action start all containers', async ({ navigationBar }) => { - test.setTimeout(210_000); - - const stopStatusArray = [ContainerState.Stopped, ContainerState.Exited]; - const stopStatusRegex = new RegExp(`${stopStatusArray.join('|')}`); - - //Start 3 containers and stop them - for (const container of containerList) { - const images = await navigationBar.openImages(); - await playExpect(images.heading).toBeVisible({ timeout: 10_000 }); - - const containersPage = await images.startContainerWithImage(imageToPull, container, containerStartParams); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeTruthy(); - - await containersPage.stopContainer(container); - const containersDetailsPage = await containersPage.openContainersDetails(container); - await playExpect.poll(async () => containersDetailsPage.getState(), { timeout: 30_000 }).toMatch(stopStatusRegex); - } - - //Start all containers - let containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await containersPage.startAllContainers(); - - for (const container of containerList) { - containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - const containersDetailsPage = await containersPage.openContainersDetails(container); - await playExpect(containersDetailsPage.heading).toBeVisible(); - await playExpect - .poll(async () => containersDetailsPage.getState(), { timeout: 30_000 }) - .toContain(ContainerState.Running); - } - - //Delete all containers - for (const container of containerList) { - const containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await containersPage.deleteContainer(container); - await playExpect - .poll(async () => await containersPage.containerExists(container), { timeout: 30_000 }) - .toBeFalsy(); - } - }); -}); diff --git a/tests/playwright/src/specs/dashboard.spec.ts b/tests/playwright/src/specs/dashboard.spec.ts new file mode 100644 index 0000000000..79d01f6cf9 --- /dev/null +++ b/tests/playwright/src/specs/dashboard.spec.ts @@ -0,0 +1,39 @@ +/********************************************************************** + * 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 { TIMEOUTS } from 'src/model/core/types'; +import { waitForNavigationReady } from 'src/utils/app-ready'; + +import { expect, test } from '../fixtures/electron-app'; + +test.describe.serial('App start', { tag: '@smoke' }, () => { + test.beforeEach(async ({ page }) => { + await waitForNavigationReady(page); + }); + + test('[APP-01] Navigation bar is visible and contains all expected navigation links', async ({ navigationBar }) => { + await expect(navigationBar.navigationLocator).toBeVisible({ timeout: TIMEOUTS.DEFAULT }); + const expectedLinksCount = 5; // Chat, MCP, Flows, Extensions, Settings + + const allLinks = navigationBar.getAllLinks(); + expect(allLinks).toHaveLength(expectedLinksCount); + + for (const link of allLinks) { + await expect(link).toBeVisible({ timeout: TIMEOUTS.DEFAULT }); + } + }); +}); diff --git a/tests/playwright/src/specs/docker-compatibility-smoke.spec.ts b/tests/playwright/src/specs/docker-compatibility-smoke.spec.ts deleted file mode 100644 index 5d7c8be75a..0000000000 --- a/tests/playwright/src/specs/docker-compatibility-smoke.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -/********************************************************************** - * 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 { DockerCompatibilityPage } from '../model/pages/docker-compatibility-page'; -import { PodmanMachineDetails } from '../model/pages/podman-machine-details-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { createPodmanMachineFromCLI, setDockerCompatibilityFeature } from '../utility/operations'; -import { isWindows } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const defaultMachine = 'Podman Machine'; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('docker-compatibility-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner, page }) => { - if (test.info().status === 'failed') { - await setDockerCompatibilityFeature(page, false); - await createPodmanMachineFromCLI(); - } - await runner.close(); -}); - -//Feature unstable on mac and linux atm -test.skip(!isWindows, 'Testing only on Windows'); - -test.describe.serial('Verify docker compatibility feature', { tag: '@smoke' }, () => { - test('Enable the docker compatibility experimental feature', async ({ navigationBar, page }) => { - //Verify Settings - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - - const DCLink = settingsBar.getLinkLocatorByHref('/preferences/docker-compatibility'); - await playExpect(DCLink).not.toBeVisible(); - - //Enable the feature - await setDockerCompatibilityFeature(page, true); - }); - test('Verify Docker Compatibility page', async ({ page }) => { - const settingsBar = new SettingsBar(page); - const dockerCompatibilityPage = await settingsBar.openTabPage(DockerCompatibilityPage); - await playExpect(dockerCompatibilityPage.heading).toBeVisible(); - await playExpect.poll(async () => await dockerCompatibilityPage.socketIsReachable()).toBeTruthy(); - await playExpect(dockerCompatibilityPage.serverInformationBox).toBeVisible(); - }); - test('Verify socket reachability is responding to podman machine status', async ({ page }) => { - const settingsBar = new SettingsBar(page); - await settingsBar.openTabPage(ResourcesPage); - const podmanMachineDetails = new PodmanMachineDetails(page, defaultMachine); - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStopButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 50_000 }); - - const dockerCompatibilityPage = await settingsBar.openTabPage(DockerCompatibilityPage); - await playExpect - .poll(async () => await dockerCompatibilityPage.socketIsReachable(), { timeout: 50_000 }) - .toBeFalsy(); - await playExpect(dockerCompatibilityPage.serverInformationBox).not.toBeVisible(); - - await settingsBar.openTabPage(ResourcesPage); - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStartButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 50_000 }); - - await settingsBar.openTabPage(DockerCompatibilityPage); - await playExpect - .poll(async () => await dockerCompatibilityPage.socketIsReachable(), { timeout: 50_000 }) - .toBeTruthy(); - await playExpect(dockerCompatibilityPage.serverInformationBox).toBeVisible(); - }); - test('Disable docker compatibility', async ({ page }) => { - await setDockerCompatibilityFeature(page, false); - }); -}); diff --git a/tests/playwright/src/specs/extension-installation-smoke.spec.ts b/tests/playwright/src/specs/extension-installation-smoke.spec.ts deleted file mode 100644 index 91947ec508..0000000000 --- a/tests/playwright/src/specs/extension-installation-smoke.spec.ts +++ /dev/null @@ -1,307 +0,0 @@ -/********************************************************************** - * 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 { Locator, Page } from '@playwright/test'; -import { expect as playExpect, test } from '@playwright/test'; - -import { - developerSandboxExtension, - extensionsInstallationSmokeList, - openshiftCheckerExtension, - openshiftDockerExtension, - openshiftLocalExtension, -} from '../model/core/extensions'; -import { ExtensionState } from '../model/core/states'; -import { DashboardPage } from '../model/pages/dashboard-page'; -import { ExtensionCatalogCardPage } from '../model/pages/extension-catalog-card-page'; -import { ExtensionsPage } from '../model/pages/extensions-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { WelcomePage } from '../model/pages/welcome-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { Runner } from '../runner/podman-desktop-runner'; - -let pdRunner: Runner; -let page: Page; - -let extensionDashboardStatus: Locator | undefined; -let extensionDashboardProvider: Locator | undefined; -let resourceLabel: string | undefined; -let ociImageUrl: string; - -let navigationBar: NavigationBar; - -async function _startup(extensionLabel: string): Promise { - pdRunner = await Runner.getInstance(); - page = pdRunner.getPage(); - pdRunner.setVideoAndTraceName(`${extensionLabel}-installation-e2e`); - - const welcomePage = new WelcomePage(page); - await welcomePage.handleWelcomePage(true); - - navigationBar = new NavigationBar(page); -} - -for (const { - extensionLabel, - extensionFullLabel, - extensionName, - extensionFullName, -} of extensionsInstallationSmokeList) { - test.describe.serial(`Extension installation for ${extensionName}`, { tag: '@smoke' }, () => { - test.beforeAll(async () => { - await _startup(extensionLabel); - }); - test.afterAll(async () => { - await pdRunner.close(); - }); - - test('Initialize extension type', async () => { - initializeLocators(extensionName); - await navigationBar.openExtensions(); - }); - - test('Install extension through Extensions Catalog', async () => { - test.skip( - extensionName === openshiftCheckerExtension.extensionName || - extensionName === openshiftDockerExtension.extensionName, - ); - test.setTimeout(200_000); - - const extensionsPage = new ExtensionsPage(page); - await playExpect(extensionsPage.heading).toBeVisible(); - - await extensionsPage.openCatalogTab(); - const extensionCatalog = new ExtensionCatalogCardPage(page, extensionName); - await playExpect(extensionCatalog.parent).toBeVisible(); - - await playExpect.poll(async () => await extensionCatalog.isInstalled()).toBeFalsy(); - await extensionCatalog.install(180_000); - - await extensionsPage.openInstalledTab(); - await playExpect.poll(async () => await extensionsPage.extensionIsInstalled(extensionFullLabel)).toBeTruthy(); - }); - - test('Install extension from OCI Image', async () => { - test.skip( - extensionName !== openshiftCheckerExtension.extensionName && - extensionName !== openshiftDockerExtension.extensionName, - ); - test.setTimeout(200_000); - - const extensionsPage = new ExtensionsPage(page); - - await extensionsPage.installExtensionFromOCIImage(ociImageUrl, 180_000); - if (extensionName !== openshiftDockerExtension.extensionName) { - await extensionsPage.openCatalogTab(); - const extensionCatalog = new ExtensionCatalogCardPage(page, extensionName); - await playExpect(extensionCatalog.parent).toBeVisible(); - await playExpect.poll(async () => await extensionCatalog.isInstalled()).toBeTruthy(); - } - - await extensionsPage.openInstalledTab(); - await playExpect - .poll(async () => await extensionsPage.extensionIsInstalled(extensionFullLabel), { timeout: 15_000 }) - .toBeTruthy(); - }); - - test.describe - .serial('Extension verification after installation', () => { - test('Extension details can be opened', async () => { - const extensionsPage = await navigationBar.openExtensions(); - - const extensionDetailsPage = await extensionsPage.openExtensionDetails( - extensionLabel, - extensionFullLabel, - extensionFullName, - ); - await playExpect(extensionDetailsPage.status).toBeVisible({ timeout: 15000 }); - }); - - test('Extension is active and there are not errors', async () => { - const extensionsPage = await navigationBar.openExtensions(); - const extensionPage = await extensionsPage.openExtensionDetails( - extensionLabel, - extensionFullLabel, - extensionFullName, - ); - await playExpect(extensionPage.heading).toBeVisible(); - await playExpect(extensionPage.status).toHaveText(ExtensionState.Active); - // tabs are empty in case there is no error. If there is error, there are two tabs' buttons present - const errorTab = extensionPage.tabs.getByRole('button', { name: 'Error' }); - // we would like to propagate the error's stack trace into test failure message - let stackTrace = ''; - if ((await errorTab.count()) > 0) { - stackTrace = await errorTab.innerText(); - } - await playExpect(errorTab, `Error Tab was present with stackTrace: ${stackTrace}`).not.toBeVisible(); - }); - - test.describe - .serial('Extension can be disabled and reenabled', () => { - test.skip( - extensionName === openshiftDockerExtension.extensionName, - 'OpenShift Docker extension cannot be disabled', - ); - - test('Disable extension and verify Dashboard and Resources components if present', async () => { - const extensionsPage = await navigationBar.openExtensions(); - const extensionPage = await extensionsPage.openExtensionDetails( - extensionLabel, - extensionFullLabel, - extensionFullName, - ); - - await extensionPage.disableExtension(); - await playExpect(extensionPage.status).toHaveText(ExtensionState.Disabled); - - // check that dashboard card provider is hidden/shown - if (extensionDashboardProvider && extensionDashboardStatus) { - await goToDashboard(); - await playExpect(extensionDashboardProvider).toBeHidden(); - } - - // check that the provider card is on Resources Page - if (resourceLabel) { - const settingsBar = await goToSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - const extensionResourceBox = resourcesPage.featuredProviderResources.getByRole('region', { - name: resourceLabel, - }); - await playExpect(extensionResourceBox).toBeHidden(); - } - }); - - test('Enable extension and verify Dashboard and Resources components', async () => { - const extensionsPage = await navigationBar.openExtensions(); - const extensionPage = await extensionsPage.openExtensionDetails( - extensionLabel, - extensionFullLabel, - extensionFullName, - ); - - await extensionPage.enableExtension(); - await playExpect(extensionPage.status).toHaveText(ExtensionState.Active, { timeout: 10000 }); - - // check that dashboard card provider is hidden/shown - if (extensionDashboardProvider && extensionDashboardStatus) { - await goToDashboard(); - await playExpect(extensionDashboardProvider).toBeVisible(); - await playExpect(extensionDashboardStatus).toBeVisible(); - if (extensionName === developerSandboxExtension.extensionName) { - await playExpect(extensionDashboardStatus).toHaveText(ExtensionState.Running); - } else { - await playExpect(extensionDashboardStatus).toHaveText(ExtensionState.NotInstalled); - } - } - - // check that the provider card is on Resources Page - if (resourceLabel) { - const settingsBar = await goToSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - const extensionResourceBox = resourcesPage.featuredProviderResources.getByRole('region', { - name: resourceLabel, - }); - await playExpect(extensionResourceBox).toBeVisible(); - } - }); - }); - }); - - test.describe - .serial('Remove extension and verify UI', () => { - test('Remove extension and verify components', async () => { - let extensionsPage = await navigationBar.openExtensions(); - - const extensionDetails = await extensionsPage.openExtensionDetails( - extensionLabel, - extensionFullLabel, - extensionFullName, - ); - - if (extensionName !== openshiftDockerExtension.extensionName) { - await extensionDetails.disableExtension(); - } - await extensionDetails.removeExtension(false); - - if (extensionName !== openshiftDockerExtension.extensionName) { - // now if deleted from extension details, the page details are still there, just different - await playExpect(extensionDetails.status).toHaveText(ExtensionState.Downloadable); - await playExpect( - extensionDetails.page.getByRole('button', { name: `Install ${extensionFullLabel} Extension` }), - ).toBeVisible(); - } - - await goToDashboard(); - extensionsPage = await navigationBar.openExtensions(); - await playExpect - .poll(async () => extensionsPage.extensionIsInstalled(extensionFullLabel), { timeout: 15_000 }) - .toBeFalsy(); - }); - }); - }); -} - -function initializeLocators(extensionName: string): void { - const dashboardPage = new DashboardPage(page); - switch (extensionName) { - case developerSandboxExtension.extensionName: { - extensionDashboardStatus = dashboardPage.devSandboxStatusLabel; - extensionDashboardProvider = dashboardPage.devSandboxProvider; - resourceLabel = 'redhat.sandbox'; - ociImageUrl = ''; - break; - } - case openshiftLocalExtension.extensionName: { - extensionDashboardStatus = dashboardPage.openshiftLocalStatusLabel; - extensionDashboardProvider = dashboardPage.openshiftLocalProvider; - resourceLabel = 'crc'; - ociImageUrl = ''; - break; - } - case openshiftCheckerExtension.extensionName: { - extensionDashboardStatus = undefined; - extensionDashboardProvider = undefined; - resourceLabel = undefined; - ociImageUrl = 'ghcr.io/redhat-developer/podman-desktop-image-checker-openshift-ext:0.1.5'; - break; - } - case openshiftDockerExtension.extensionName: { - extensionDashboardStatus = undefined; - extensionDashboardProvider = undefined; - resourceLabel = undefined; - ociImageUrl = 'redhatdeveloper/openshift-dd-ext:0.0.1-100'; - break; - } - } -} - -async function goToDashboard(): Promise { - const navigationBar = page.getByRole('navigation', { name: 'AppNavigation' }); - const dashboardLink = navigationBar.getByRole('link', { name: 'Dashboard' }); - await playExpect(dashboardLink).toBeVisible(); - await dashboardLink.click(); -} - -async function goToSettings(): Promise { - const navigationBar = page.getByRole('navigation', { name: 'AppNavigation' }); - const settingsLink = navigationBar.getByRole('link', { name: 'Settings' }); - await playExpect(settingsLink).toBeVisible(); - await settingsLink.click(); - return new SettingsBar(page); -} diff --git a/tests/playwright/src/specs/extensions-smoke.spec.ts b/tests/playwright/src/specs/extensions-smoke.spec.ts new file mode 100644 index 0000000000..4906043b45 --- /dev/null +++ b/tests/playwright/src/specs/extensions-smoke.spec.ts @@ -0,0 +1,82 @@ +/********************************************************************** + * 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 { BADGE_TEXT, builtInExtensions } from 'src/model/core/types'; + +import { expect, test } from '../fixtures/electron-app'; +import { waitForNavigationReady } from '../utils/app-ready'; + +test.describe('Extensions page navigation', { tag: '@smoke' }, () => { + test.beforeEach(async ({ page, navigationBar }) => { + await waitForNavigationReady(page); + await navigationBar.navigateToExtensionsPage(); + }); + + test('[EXT-01] Extension navigation tabs are accessible', async ({ extensionsPage }) => { + const tabs = extensionsPage.getAllTabs(); + const expectedTabCount = 3; // Installed, Catalog, Local Extensions + + expect(tabs).toHaveLength(expectedTabCount); + + for (const tab of tabs) { + await expect(tab).toBeVisible(); + await expect(tab).toBeEnabled(); + } + }); + + test('[EXT-02] Search functionality filters extensions correctly', async ({ extensionsPage }) => { + for (const extension of builtInExtensions) { + await extensionsPage.searchExtension(extension.name); + await extensionsPage.verifySearchResults(extension.locator); + await extensionsPage.clearSearch(); + } + }); + + test('[EXT-03] Built-in extensions are visible with correct badges', async ({ extensionsPage }) => { + const installedPage = await extensionsPage.openInstalledTab(); + + for (const extension of builtInExtensions) { + const extensionLocator = installedPage.getExtension(extension.locator); + await expect(extensionLocator).toBeVisible(); + + const badge = installedPage.getExtensionBadge(extension.locator); + await expect(badge).toBeVisible(); + await expect(badge).toHaveText(BADGE_TEXT); + } + }); + + test('[EXT-04] Built-in extensions delete button is always disabled and unaffected by toggling', async ({ + extensionsPage, + }) => { + const installedPage = await extensionsPage.openInstalledTab(); + + for (const extension of builtInExtensions) { + const deleteButton = installedPage.getDeleteButtonForExtension(extension.locator); + await expect(deleteButton).toBeVisible(); + await expect(deleteButton).toBeDisabled(); + } + + const testExtension = builtInExtensions[0]; + const deleteButton = installedPage.getDeleteButtonForExtension(testExtension.locator); + + await installedPage.toggleExtensionState(testExtension.locator); + await expect(deleteButton).toBeDisabled(); + + await installedPage.toggleExtensionState(testExtension.locator); + await expect(deleteButton).toBeDisabled(); + }); +}); diff --git a/tests/playwright/src/specs/image-manifest-smoke.spec.ts b/tests/playwright/src/specs/image-manifest-smoke.spec.ts deleted file mode 100644 index ef847978dd..0000000000 --- a/tests/playwright/src/specs/image-manifest-smoke.spec.ts +++ /dev/null @@ -1,178 +0,0 @@ -/********************************************************************** - * 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import type { Page } from '@playwright/test'; - -import { ArchitectureType } from '../model/core/platforms'; -import type { ImagesPage } from '../model/pages/images-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { isWindows } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const architectures: string[] = [ArchitectureType.AMD64, ArchitectureType.ARM64]; -const imageNameSimple: string = 'manifest-test-simple'; -const imageNameComplex: string = 'manifest-test-complex'; -const manifestLabelSimple: string = `localhost/${imageNameSimple}`; -const manifestLabelComplex: string = `localhost/${imageNameComplex}`; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -let imagesPage: ImagesPage; -let skipTests: boolean = false; - -let provider: string | undefined; - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - runner.setVideoAndTraceName('image-manifest-smoke-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - const settingsBar = await navigationBar.openSettings(); - await settingsBar.openTabPage(ResourcesPage); - - const podmanResourceCard = new ResourceConnectionCardPage(page, 'podman'); - provider = await podmanResourceCard.getConnectionInfoByLabel('Connection Type'); - - imagesPage = await navigationBar.openImages(); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Image Manifest E2E Validation', { tag: '@smoke' }, () => { - test.describe - .serial('Image Manifest Validation - Simple Containerfile', () => { - test('Build the image using cross-arch build (simple )', async () => { - test.setTimeout(120_000); - - await playExpect(imagesPage.heading).toBeVisible(); - - const buildImagePage = await imagesPage.openBuildImage(); - await playExpect(buildImagePage.heading).toBeVisible(); - const dockerfilePath = path.resolve(__dirname, '..', '..', 'resources', 'test-containerfile'); - const contextDirectory = path.resolve(__dirname, '..', '..', 'resources'); - - const alreadyPresentImagesCount = await imagesPage.countRowsFromTable(); - - imagesPage = await buildImagePage.buildImage(imageNameSimple, dockerfilePath, contextDirectory, architectures); - await playExpect - .poll(async () => imagesPage.waitForImageExists(manifestLabelSimple, 60_000), { timeout: 0 }) - .toBeTruthy(); - await playExpect.poll(async () => await imagesPage.countRowsFromTable()).toBe(alreadyPresentImagesCount + 4); - await imagesPage.toggleImageManifest(manifestLabelSimple); - await playExpect.poll(async () => await imagesPage.countRowsFromTable()).toBe(alreadyPresentImagesCount + 2); - }); - - test('Check Manifest details', async () => { - const imageDetailsPage = await imagesPage.openImageDetails(manifestLabelSimple); - - await Promise.all( - architectures.map(async architecture => { - await playExpect(imageDetailsPage.tabContent).toContainText(architecture); - }), - ); - await playExpect(imageDetailsPage.backLink).toBeVisible(); - await imageDetailsPage.backLink.click(); - }); - test('Delete Manifest', async ({ page }) => { - await deleteImageManifest(page, manifestLabelSimple); - }); - }); - test.describe - .serial('Image Manifest Validation - Complex Containerfile', () => { - test('Build the image using cross-arch build (complex)', async ({ page }) => { - test.setTimeout(120_000); - - await playExpect(imagesPage.heading).toBeVisible(); - - const buildImagePage = await imagesPage.openBuildImage(); - await playExpect(buildImagePage.heading).toBeVisible(); - const dockerfilePath = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'alphine-hello', - 'alphine-hello.containerfile', - ); - const contextDirectory = path.resolve(__dirname, '..', '..', 'resources', 'alphine-hello'); - const alreadyPresentImagesCount = await imagesPage.countRowsFromTable(); - - try { - imagesPage = await buildImagePage.buildImage( - imageNameComplex, - dockerfilePath, - contextDirectory, - architectures, - ); - } catch (error) { - skipTests = true; - await deleteImageManifest(page, manifestLabelComplex); - if (isWindows && provider === 'Wsl') { - console.log('Building cross-architecture images with the WSL hypervisor is not working yet'); - test.fail(); - } - throw error; - } - - await playExpect - .poll(async () => await imagesPage.waitForImageExists(manifestLabelComplex, 60_000), { timeout: 0 }) - .toBeTruthy(); - await playExpect.poll(async () => await imagesPage.countRowsFromTable()).toBe(alreadyPresentImagesCount + 4); - await imagesPage.toggleImageManifest(manifestLabelComplex); - await playExpect.poll(async () => await imagesPage.countRowsFromTable()).toBe(alreadyPresentImagesCount + 2); - }); - - test('Check Manifest details', async () => { - test.skip(skipTests, 'Build manifest failed, manifest should be already deleted, skipping the test'); - - const imageDetailsPage = await imagesPage.openImageDetails(manifestLabelComplex); - await Promise.all( - architectures.map(async architecture => { - await playExpect(imageDetailsPage.tabContent).toContainText(architecture); - }), - ); - await playExpect(imageDetailsPage.backLink).toBeVisible(); - await imageDetailsPage.backLink.click(); - }); - - test('Delete Manifest', async ({ page }) => { - test.skip(skipTests, 'Build manifest failed, manifest should be already deleted, skipping the test'); - await deleteImageManifest(page, manifestLabelComplex); - }); - }); -}); - -async function deleteImageManifest(page: Page, manifestName: string): Promise { - const navigationBar = new NavigationBar(page); - await navigationBar.openImages(); - - await imagesPage.deleteImageManifest(manifestName); - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(manifestName, 30_000), { timeout: 0 }) - .toBeTruthy(); - await imagesPage.deleteAllUnusedImages(); - await playExpect.poll(async () => await imagesPage.countRowsFromTable(), { timeout: 30_000 }).toBe(0); -} diff --git a/tests/playwright/src/specs/image-push-to-registry-smoke.spec.ts b/tests/playwright/src/specs/image-push-to-registry-smoke.spec.ts deleted file mode 100644 index 85a974e23a..0000000000 --- a/tests/playwright/src/specs/image-push-to-registry-smoke.spec.ts +++ /dev/null @@ -1,153 +0,0 @@ -/********************************************************************** - * 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 { ImagesPage } from '../model/pages/images-page'; -import { RegistriesPage } from '../model/pages/registries-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { canTestRegistry, setupRegistry } from '../setupFiles/setup-registry'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteImage, deleteRegistry } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const helloContainer = 'quay.io/podman/hello'; -let registryUrl: string; -let registryUsername: string; -let registryPswdSecret: string; -let fullName: string; -const registryName: string = 'GitHub'; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('push-image-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - - [registryUrl, registryUsername, registryPswdSecret] = setupRegistry(); -}); - -test.afterAll(async ({ runner, page }) => { - try { - await deleteImage(page, helloContainer); - await deleteImage(page, fullName); - await deleteRegistry(page, 'GitHub'); - } finally { - await runner.close(); - } -}); - -test.skip(!canTestRegistry(), 'Registry tests are disabled'); - -test.describe.serial('Push image to container registry', { tag: '@smoke' }, () => { - test('Add registry', async ({ navigationBar, page }) => { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - await playExpect(registryPage.heading).toBeVisible(); - - await registryPage.createRegistry(registryUrl, registryUsername, registryPswdSecret); - - const registryBox = registryPage.registriesTable.getByLabel('GitHub'); - const username = registryBox.getByText(registryUsername); - await playExpect(username).toBeVisible(); - }); - - test('Pull image', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - imagesPage = await pullImagePage.pullImage(helloContainer); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect - .poll(async () => imagesPage.waitForRowToExists(helloContainer, 30_000), { timeout: 0 }) - .toBeTruthy(); - }); - - test('Rename image', async ({ page }) => { - let imagesPage = new ImagesPage(page); - await playExpect(imagesPage.heading).toBeVisible(); - - fullName = `${registryUrl}/${registryUsername}/test-image`; - - imagesPage = await imagesPage.renameImage(helloContainer, fullName, 'latest'); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect - .poll(async () => imagesPage.waitForRowToExists(fullName, 30_000), { - timeout: 0, - }) - .toBeTruthy(); - }); - - test('Push image to registry', async ({ page }) => { - const imagesPage = new ImagesPage(page); - await playExpect(imagesPage.heading).toBeVisible(); - - const imageDetailsPage = await imagesPage.openImageDetails(fullName); - await playExpect(imageDetailsPage.heading).toBeVisible(); - - await imageDetailsPage.pushImage(); - }); - - test('Delete image', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect - .poll(async () => imagesPage.waitForRowToExists(fullName, 30_000), { - timeout: 0, - }) - .toBeTruthy(); - - const imageDetailPage = await imagesPage.openImageDetails(fullName); - await playExpect(imageDetailPage.heading).toBeVisible(); - - imagesPage = await imageDetailPage.deleteImage(); - await playExpect(imagesPage.heading).toBeVisible({ timeout: 30_000 }); - - await playExpect - .poll(async () => await imagesPage.waitForRowToBeDelete(fullName, 60_000), { timeout: 0 }) - .toBeTruthy(); - }); - - test('Registry removal verification', async ({ page, navigationBar }) => { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - await playExpect(registryPage.heading).toBeVisible(); - - await registryPage.removeRegistry(registryName); - const registryBox = registryPage.registriesTable.getByLabel(registryName); - const username = registryBox.getByText(registryUsername); - await playExpect(username).toBeHidden(); - }); - - test('Pull image from github repo under new name', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - imagesPage = await pullImagePage.pullImage(fullName, 'latest'); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect - .poll(async () => await imagesPage.waitForRowToExists(fullName, 15_000), { timeout: 0 }) - .toBeTruthy(); - }); -}); diff --git a/tests/playwright/src/specs/image-search.spec.ts b/tests/playwright/src/specs/image-search.spec.ts deleted file mode 100644 index 135f00991b..0000000000 --- a/tests/playwright/src/specs/image-search.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { expect as playExpect, test } from '../utility/fixtures'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const imageToSearch = 'ghcr.io/linuxcontainers/alpine'; -const httpdImage = 'docker.io/httpd'; -const httpdTag = '2-alpine'; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('image-search'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe('Image search verification', { tag: '@smoke' }, () => { - test('Search for image and then clear field', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - - const searchResults = await pullImagePage.getAllSearchResultsFor(imageToSearch, true); - playExpect(searchResults.length).toBeGreaterThan(0); - - await pullImagePage.clearImageSearch(); - }); - - test('Search for image and then search with tag also', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - - let searchResults = await pullImagePage.getAllSearchResultsFor(imageToSearch, true); - playExpect(searchResults.length).toBeGreaterThan(0); - - searchResults = await pullImagePage.refineSearchResults('3.13'); - playExpect(searchResults.length).toBe(4); - }); - - test('First search result needs to be the most relevant', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - - const searchResults = await pullImagePage.getFirstSearchResultFor('quay.io/podman', false); - playExpect(searchResults).toContain('quay.io/podman'); - }); - - test(`Search for ${httpdImage} after using intermediate steps`, async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - - let searchResults = await pullImagePage.getAllSearchResultsFor('htt', false); - playExpect(searchResults.length).toBeGreaterThan(0); - - searchResults = await pullImagePage.refineSearchResults('pd'); - playExpect(searchResults.length).toBeGreaterThan(0); - - await pullImagePage.selectValueFromSearchResults(httpdImage); - await pullImagePage.getAllSearchResultsFor(httpdImage, true, httpdTag); - await playExpect - .poll(async () => (await pullImagePage.getAllSearchResultsInstantly(`${httpdImage}:${httpdTag}`)).length) - .toBeGreaterThan(0); - }); -}); diff --git a/tests/playwright/src/specs/image-smoke.spec.ts b/tests/playwright/src/specs/image-smoke.spec.ts deleted file mode 100644 index 17b699d8b2..0000000000 --- a/tests/playwright/src/specs/image-smoke.spec.ts +++ /dev/null @@ -1,228 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { ImageDetailsPage } from '../model/pages/image-details-page'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { untagImagesFromPodman } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const helloContainer = 'quay.io/podman/hello'; -const imageList = ['quay.io/podman/image1', 'quay.io/podman/image2']; -const imageToSearch = 'ghcr.io/linuxcontainers/alpine'; -const imageTagToSearch = 'latest'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('pull-image-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Image workflow verification', { tag: '@smoke' }, () => { - test('Pull image', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - const updatedImages = await pullImagePage.pullImage(helloContainer); - await playExpect(updatedImages.heading).toBeVisible({ timeout: 10_000 }); - - await playExpect - .poll(async () => updatedImages.waitForImageExists(helloContainer, 30_000), { timeout: 0 }) - .toBeTruthy(); - - playExpect(await updatedImages.getCurrentStatusOfImage(helloContainer)).toBe('UNUSED'); - }); - - test('Pull image from search results', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - - const searchResults = await pullImagePage.getAllSearchResultsFor(imageToSearch, true); - playExpect(searchResults.length).toBeGreaterThan(0); - - imagesPage = await pullImagePage.pullImageFromSearchResults(imageToSearch + ':' + imageTagToSearch); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect.poll(async () => await imagesPage.waitForImageExists(imageToSearch)).toBeTruthy(); - - const imageDetailPage = await imagesPage.openImageDetails(imageToSearch); - await playExpect(imageDetailPage.heading).toBeVisible(); - - imagesPage = await imageDetailPage.deleteImage(); - await playExpect(imagesPage.heading).toBeVisible({ timeout: 30_000 }); - - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(imageToSearch, 60_000), { timeout: 0 }) - .toBeTruthy(); - }); - - test('Test navigation between pages', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - const imageDetailPage = await imagesPage.openImageDetails(helloContainer); - await playExpect(imageDetailPage.heading).toBeVisible(); - await imageDetailPage.backLink.click(); - await playExpect(imagesPage.heading).toBeVisible(); - - await imagesPage.openImageDetails(helloContainer); - await playExpect(imageDetailPage.heading).toBeVisible(); - await imageDetailPage.closeButton.click(); - await playExpect(imagesPage.heading).toBeVisible(); - }); - - test('Check image details', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - const imageDetailPage = await imagesPage.openImageDetails(helloContainer); - - await playExpect(imageDetailPage.summaryTab).toBeVisible(); - await playExpect(imageDetailPage.historyTab).toBeVisible(); - await playExpect(imageDetailPage.inspectTab).toBeVisible(); - }); - - test('Rename image', async ({ page }) => { - const imageDetailsPage = new ImageDetailsPage(page, helloContainer); - const editPage = await imageDetailsPage.openEditImage(); - const imagesPage = await editPage.renameImage('quay.io/podman/hi'); - playExpect(await imagesPage.waitForImageExists('quay.io/podman/hi')).toBe(true); - }); - - test('Delete image', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - await imagesPage.pullImage(helloContainer); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect.poll(async () => await imagesPage.waitForImageExists(helloContainer)).toBeTruthy(); - - const imageDetailPage = await imagesPage.openImageDetails(helloContainer); - imagesPage = await imageDetailPage.deleteImage(); - - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(helloContainer, 60_000), { timeout: 0 }) - .toBeTruthy(); - playExpect(await imagesPage.waitForImageExists('quay.io/podman/hi')).toBe(true); - }); - - test('Build image', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - const buildImagePage = await imagesPage.openBuildImage(); - await playExpect(buildImagePage.heading).toBeVisible(); - const dockerfilePath = path.resolve(__dirname, '..', '..', 'resources', 'test-containerfile'); - const contextDirectory = path.resolve(__dirname, '..', '..', 'resources'); - - imagesPage = await buildImagePage.buildImage('build-image-test', dockerfilePath, contextDirectory); - playExpect(await imagesPage.waitForImageExists('docker.io/library/build-image-test')).toBeTruthy(); - - const imageDetailsPage = await imagesPage.openImageDetails('docker.io/library/build-image-test'); - await playExpect(imageDetailsPage.heading).toBeVisible(); - imagesPage = await imageDetailsPage.deleteImage(); - playExpect(await imagesPage.waitForImageDelete('docker.io/library/build-image-test')).toBeTruthy(); - }); - - test('Prune all images', async ({ navigationBar }) => { - test.setTimeout(240_000); - - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - for (const image of imageList) { - await imagesPage.pullImage(helloContainer); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(helloContainer, 15_000), { timeout: 0 }) - .toBeTruthy(); - - await imagesPage.renameImage(helloContainer, image); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(image, 10_000), { timeout: 0 }) - .toBeTruthy(); - } - - await imagesPage.pruneImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - for (const image of imageList) { - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(image, 180_000), { timeout: 0 }) - .toBeTruthy(); - } - }); - - test('Prune untagged images', async ({ navigationBar }) => { - test.setTimeout(240_000); - - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - for (const image of imageList) { - await imagesPage.pullImage(helloContainer); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(helloContainer, 15_000), { timeout: 0 }) - .toBeTruthy(); - - await imagesPage.renameImage(helloContainer, image); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(image, 10_000), { timeout: 0 }) - .toBeTruthy(); - } - - await imagesPage.pullImage(imageToSearch); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(imageToSearch, 120_000), { timeout: 0 }) - .toBeTruthy(); - - await untagImagesFromPodman(imageList[0]); - await playExpect - .poll(async () => await imagesPage.waitForImageExists('', 60_000), { timeout: 0 }) - .toBeTruthy(); - - await imagesPage.pruneUntaggedImages(); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.waitForImageDelete('', 60_000), { timeout: 0 }) - .toBeTruthy(); - await playExpect - .poll(async () => await imagesPage.waitForImageExists(imageToSearch, 60_000), { timeout: 0 }) - .toBeTruthy(); - - await imagesPage.deleteAllUnusedImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(imageToSearch, 60_000), { timeout: 0 }) - .toBeTruthy(); - }); -}); diff --git a/tests/playwright/src/specs/kind.spec.ts b/tests/playwright/src/specs/kind.spec.ts deleted file mode 100644 index eba3e9896e..0000000000 --- a/tests/playwright/src/specs/kind.spec.ts +++ /dev/null @@ -1,245 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { ResourceElementActions } from '../model/core/operations'; -import { ContainerState, ResourceElementState } from '../model/core/states'; -import type { ContainerInteractiveParams } from '../model/core/types'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { canRunKindTests } from '../setupFiles/setup-kind'; -import { - checkClusterResources, - createKindCluster, - deleteCluster, - deleteClusterFromDetails, - resourceConnectionAction, - resourceConnectionActionDetails, -} from '../utility/cluster-operations'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deployContainerToCluster } from '../utility/kubernetes'; -import { deleteContainer, deleteImage, ensureCliInstalled } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const RESOURCE_NAME: string = 'kind'; -const EXTENSION_LABEL: string = 'podman-desktop.kind'; -const CLUSTER_NAME: string = 'kind-cluster'; -const KIND_CONTAINER: string = `${CLUSTER_NAME}-control-plane`; -const CUSTOM_CONFIG_CLUSTER_NAME: string = 'test-cluster'; -const CUSTOM_CONFIG_KIND_CONTAINER: string = `${CUSTOM_CONFIG_CLUSTER_NAME}-control-plane`; -const CLUSTER_CREATION_TIMEOUT: number = 300_000; -const KUBERNETES_CONTEXT: string = `kind-${CLUSTER_NAME}`; - -const IMAGE_TO_PULL: string = 'ghcr.io/linuxcontainers/alpine'; -const IMAGE_TAG: string = 'latest'; -const CONTAINER_NAME: string = 'alpine-container'; -const DEPLOYED_POD_NAME: string = CONTAINER_NAME; -const CONTAINER_START_PARAMS: ContainerInteractiveParams = { - attachTerminal: false, -}; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const CUSTOM_CONFIG_FILE_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - 'test-kind-config-file.yaml', -); - -let resourcesPage: ResourcesPage; -let kindResourceCard: ResourceConnectionCardPage; - -const skipKindInstallation = process.env.SKIP_KIND_INSTALL === 'true'; -const providerTypeGHA = process.env.KIND_PROVIDER_GHA ?? ''; - -test.skip(!canRunKindTests(), `This test can't run on a windows rootless machine`); - -test.beforeAll(async ({ runner, page, welcomePage }) => { - runner.setVideoAndTraceName('kind-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - resourcesPage = new ResourcesPage(page); - kindResourceCard = new ResourceConnectionCardPage(page, RESOURCE_NAME); -}); - -test.afterAll(async ({ runner, page }) => { - try { - await deleteContainer(page, CONTAINER_NAME); - await deleteImage(page, IMAGE_TO_PULL); - await deleteCluster(page, RESOURCE_NAME, KIND_CONTAINER, CLUSTER_NAME); - } finally { - await runner.close(); - } -}); - -test.describe('Kind End-to-End Tests', { tag: '@k8s_e2e' }, () => { - test.describe - .serial('Kind installation', () => { - test('Install Kind CLI', async ({ page, navigationBar }) => { - test.skip(!!skipKindInstallation, 'Skipping Kind cluster installation'); - const settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - - await ensureCliInstalled(page, 'Kind'); - }); - - test('Kind extension lifecycle', async ({ navigationBar }) => { - const extensionsPage = await navigationBar.openExtensions(); - const kindExtension = await extensionsPage.getInstalledExtension('Kind extension', EXTENSION_LABEL); - await playExpect - .poll(async () => await extensionsPage.extensionIsInstalled(EXTENSION_LABEL), { timeout: 10000 }) - .toBeTruthy(); - await playExpect(kindExtension.status).toHaveText('ACTIVE'); - await kindExtension.disableExtension(); - await navigationBar.openSettings(); - await playExpect.poll(async () => resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeFalsy(); - await navigationBar.openExtensions(); - await kindExtension.enableExtension(); - await navigationBar.openSettings(); - await playExpect.poll(async () => resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - }); - }); - test.describe - .serial('Kind cluster validation tests', () => { - test('Create a Kind cluster - With Ingress controller', async ({ page }) => { - test.setTimeout(CLUSTER_CREATION_TIMEOUT); - - await createKindCluster(page, CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - providerType: providerTypeGHA, - useIngressController: true, - }); - }); - - test('Check resources added with the Kind cluster', async ({ page }) => { - await checkClusterResources(page, KIND_CONTAINER); - }); - - test('Deploy a container to the Kind cluster', async ({ page, navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - const pullImagePage = await imagesPage.openPullImage(); - await pullImagePage.pullImage(IMAGE_TO_PULL, IMAGE_TAG); - await playExpect.poll(async () => imagesPage.waitForImageExists(IMAGE_TO_PULL, 10_000)).toBeTruthy(); - const containersPage = await imagesPage.startContainerWithImage( - IMAGE_TO_PULL, - CONTAINER_NAME, - CONTAINER_START_PARAMS, - ); - await playExpect - .poll(async () => containersPage.containerExists(CONTAINER_NAME), { - timeout: 15_000, - }) - .toBeTruthy(); - const containerDetails = await containersPage.openContainersDetails(CONTAINER_NAME); - await playExpect(containerDetails.heading).toBeVisible(); - await playExpect.poll(async () => containerDetails.getState()).toBe(ContainerState.Running); - await deployContainerToCluster(page, CONTAINER_NAME, KUBERNETES_CONTEXT, DEPLOYED_POD_NAME); - }); - - test('Kind cluster operations - STOP', async ({ page }) => { - await resourceConnectionAction(page, kindResourceCard, ResourceElementActions.Stop, ResourceElementState.Off); - }); - - test('Kind cluster operations - START', async ({ page }) => { - await resourceConnectionAction( - page, - kindResourceCard, - ResourceElementActions.Start, - ResourceElementState.Running, - ); - }); - - test('Kind cluster operations - RESTART', async ({ page }) => { - await resourceConnectionAction( - page, - kindResourceCard, - ResourceElementActions.Restart, - ResourceElementState.Running, - ); - }); - - test('Kind cluster operations - DELETE', async ({ page }) => { - await deleteCluster(page, RESOURCE_NAME, KIND_CONTAINER, CLUSTER_NAME); - }); - }); - test.describe - .serial('Kind cluster operations - Details', () => { - test('Create a Kind cluster - Without Ingress controller', async ({ page }) => { - test.setTimeout(CLUSTER_CREATION_TIMEOUT); - - await createKindCluster(page, CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - providerType: providerTypeGHA, - useIngressController: false, - }); - }); - - test('Kind cluster operations details - STOP', async ({ page }) => { - await resourceConnectionActionDetails( - page, - kindResourceCard, - CLUSTER_NAME, - ResourceElementActions.Stop, - ResourceElementState.Off, - ); - }); - - test('Kind cluster operations details - START', async ({ page }) => { - await resourceConnectionActionDetails( - page, - kindResourceCard, - CLUSTER_NAME, - ResourceElementActions.Start, - ResourceElementState.Running, - ); - }); - - test('Kind cluster operations details - RESTART', async ({ page }) => { - await resourceConnectionActionDetails( - page, - kindResourceCard, - CLUSTER_NAME, - ResourceElementActions.Restart, - ResourceElementState.Running, - ); - }); - - test('Kind cluster operations details - DELETE', async ({ page }) => { - await deleteClusterFromDetails(page, RESOURCE_NAME, KIND_CONTAINER, CLUSTER_NAME); - }); - }); - test.describe - .serial('Kind cluster creation with custom config file', () => { - test('Create a Kind cluster using the custom config file', async ({ page }) => { - test.setTimeout(CLUSTER_CREATION_TIMEOUT); - - await createKindCluster(page, CUSTOM_CONFIG_CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - configFilePath: CUSTOM_CONFIG_FILE_PATH, - providerType: providerTypeGHA, - useIngressController: false, - }); - await checkClusterResources(page, CUSTOM_CONFIG_KIND_CONTAINER); - }); - test('Delete the Kind cluster', async ({ page }) => { - await deleteClusterFromDetails(page, RESOURCE_NAME, CUSTOM_CONFIG_KIND_CONTAINER, CUSTOM_CONFIG_CLUSTER_NAME); - }); - }); -}); diff --git a/tests/playwright/src/specs/kubernetes-context-smoke.spec.ts b/tests/playwright/src/specs/kubernetes-context-smoke.spec.ts deleted file mode 100644 index fbf0ee29bb..0000000000 --- a/tests/playwright/src/specs/kubernetes-context-smoke.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { KubeContextPage } from '../model/pages/kubernetes-context-page'; -import { PreferencesPage } from '../model/pages/preferences-page'; -import { expect as playExpect, test } from '../utility/fixtures'; - -const testContexts = ['context-1', 'context-2', 'context-3']; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -test.beforeAll(async ({ runner, welcomePage }) => { - runner.setVideoAndTraceName('kube-context-e2e'); - - // copy testing kubeconfig file to the expected location - const kubeConfigPathSrc = path.resolve(__dirname, '..', '..', 'resources', 'test-kube-config'); - const kubeConfigPathDst = path.resolve(__dirname, '..', '..', 'resources', 'kube-config'); - fs.copyFileSync(kubeConfigPathSrc, kubeConfigPathDst); - - await welcomePage.handleWelcomePage(true); -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120_000); - await runner.close(); -}); - -test.describe.serial('Verification of kube context management', { tag: '@smoke' }, () => { - test('Load custom kubeconfig in Preferences', async ({ navigationBar }) => { - // open preferences page - const settingsBar = await navigationBar.openSettings(); - await settingsBar.expandPreferencesTab(); - const preferencesPage = await settingsBar.openTabPage(PreferencesPage); - await playExpect(preferencesPage.heading).toBeVisible(); - - const kubeConfigPathDst = path.resolve(__dirname, '..', '..', 'resources', 'kube-config'); - await preferencesPage.selectKubeFile(kubeConfigPathDst); - }); - - test('Can load kube contexts in Kubernetes page', async ({ navigationBar }) => { - const settingsBar = await navigationBar.openSettings(); - const kubePage = await settingsBar.openTabPage(KubeContextPage); - await playExpect(kubePage.heading).toBeVisible(); - - await playExpect.poll(async () => await kubePage.pageIsEmpty(), { timeout: 30_000 }).toBeFalsy(); - for (const context of testContexts) { - const row = await kubePage.getContextRowByName(context); - await playExpect(row).toBeVisible(); - playExpect(await kubePage.getContextName(context)).toBe(context); - playExpect(await kubePage.getContextCluster(context)).toBe(context + '-cluster'); - playExpect(await kubePage.getContextServer(context)).toBe(context + '-server'); - playExpect(await kubePage.getContextUser(context)).toBe(context + '-user'); - - if (context === 'context-1') { - // check if the context is default - await playExpect - .poll(async () => await kubePage.isContextDefault(context), { - timeout: 10000, - }) - .toBeTruthy(); - await playExpect(await kubePage.getSetCurrentContextButton(context)).not.toBeVisible(); - playExpect(await kubePage.getContextNamespace(context)).toBe(context + '-namespace'); - } else { - await playExpect(await kubePage.getSetCurrentContextButton(context)).toBeVisible(); - } - } - }); - - test('Can switch default context', async ({ navigationBar }) => { - const settingsBar = await navigationBar.openSettings(); - const kubePage = await settingsBar.openTabPage(KubeContextPage); - await playExpect(kubePage.heading).toBeVisible(); - - await kubePage.setDefaultContext('context-2'); - // check that switch worked - current context banner should be visible - await playExpect.poll(async () => await kubePage.isContextDefault('context-1'), { timeout: 10_000 }).toBeFalsy(); - await playExpect - .poll(async () => await kubePage.isContextDefault('context-2'), { - timeout: 10_000, - }) - .toBeTruthy(); - playExpect(await kubePage.isContextDefault('context-3')).toBeFalsy(); - }); - - test('Can delete all contexts from Kubernetes Contexts page', async ({ navigationBar }) => { - const settingsBar = await navigationBar.openSettings(); - const kubePage = await settingsBar.openTabPage(KubeContextPage); - await playExpect(kubePage.heading).toBeVisible(); - - for (const context of testContexts) { - // confirmation only pops up if the context is default - if (await kubePage.isContextDefault(context)) { - await kubePage.deleteContext(context); - } else { - await kubePage.deleteContext(context, false); - } - - await playExpect(await kubePage.getContextRowByName(context)).not.toBeVisible({ timeout: 15_000 }); - } - - // check that the page is empty - await playExpect.poll(async () => await kubePage.pageIsEmpty(), { timeout: 10_000 }).toBeTruthy(); - }); -}); diff --git a/tests/playwright/src/specs/kubernetes-image-push.spec.ts b/tests/playwright/src/specs/kubernetes-image-push.spec.ts deleted file mode 100644 index d7547b5e9c..0000000000 --- a/tests/playwright/src/specs/kubernetes-image-push.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { PlayYamlRuntime } from '../model/core/operations'; -import { KubernetesResourceState } from '../model/core/states'; -import { KubernetesResources } from '../model/core/types'; -import { canRunKindTests } from '../setupFiles/setup-kind'; -import { createKindCluster, deleteCluster } from '../utility/cluster-operations'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { - checkKubernetesResourceState, - createKubernetesResource, - deleteKubernetesResource, -} from '../utility/kubernetes'; -import { ensureCliInstalled } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const CLUSTER_NAME: string = 'kind-cluster'; -const CLUSTER_CREATION_TIMEOUT: number = 300_000; -const KIND_NODE: string = `${CLUSTER_NAME}-control-plane`; -const RESOURCE_NAME: string = 'kind'; -const KUBERNETES_CONTEXT = `kind-${CLUSTER_NAME}`; -const KUBERNETES_NAMESPACE = 'default'; -const DEPLOYMENT_NAME = 'test-image-push'; -const KUBERNETES_RUNTIME = { - runtime: PlayYamlRuntime.Kubernetes, - kubernetesContext: KUBERNETES_CONTEXT, - kubernetesNamespace: KUBERNETES_NAMESPACE, -}; -const IMAGE_NAME = 'ghcr.io/linuxcontainers/alpine'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const DEPLOYMENT_YAML_PATH = path.resolve(__dirname, '..', '..', 'resources', 'kubernetes', `${DEPLOYMENT_NAME}.yaml`); - -const skipKindInstallation = process.env.SKIP_KIND_INSTALL === 'true'; -const providerTypeGHA = process.env.KIND_PROVIDER_GHA ?? ''; - -test.skip(!canRunKindTests(), `This test can't run on a windows rootless machine`); - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - test.setTimeout(350_000); - runner.setVideoAndTraceName('kubernetes-image-push'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - if (!skipKindInstallation) { - const settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - - await ensureCliInstalled(page, 'Kind'); - } - - await createKindCluster(page, CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - providerType: providerTypeGHA, - useIngressController: false, - }); -}); - -test.afterAll(async ({ runner, page }) => { - test.setTimeout(90_000); - try { - await deleteCluster(page, RESOURCE_NAME, KIND_NODE, CLUSTER_NAME); - } finally { - await runner.close(); - } -}); - -test.describe.serial( - 'Kubernetes pushing an image to the cluster and reusing it with a pod E2E Test', - { tag: '@k8s_e2e' }, - () => { - test('Pull image and push it to the cluster', async ({ navigationBar }) => { - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible(); - imagesPage = await pullImagePage.pullImage(IMAGE_NAME); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect.poll(async () => imagesPage.waitForImageExists(IMAGE_NAME, 30_000), { timeout: 0 }).toBeTruthy(); - - const imageDetails = await imagesPage.openImageDetails(IMAGE_NAME); - await playExpect(imageDetails.heading).toBeVisible(); - await imageDetails.pushImageToKindCluster(); - }); - test('Kubernetes Pods page should be empty', async ({ navigationBar }) => { - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesPodsPage = await kubernetesBar.openTabPage(KubernetesResources.Pods); - await playExpect(kubernetesPodsPage.heading).toBeVisible(); - - await playExpect.poll(async () => kubernetesPodsPage.content.textContent()).toContain('No pods'); - }); - test('Create a Kubernetes deployment resource', async ({ page }) => { - test.setTimeout(80_000); - await createKubernetesResource( - page, - KubernetesResources.Pods, - DEPLOYMENT_NAME + '-pod', - DEPLOYMENT_YAML_PATH, - KUBERNETES_RUNTIME, - ); - - await checkKubernetesResourceState( - page, - KubernetesResources.Pods, - DEPLOYMENT_NAME + '-pod', - KubernetesResourceState.Running, - 80_000, - ); - }); - - test('Delete the Kubernetes pod', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.Pods, DEPLOYMENT_NAME); - }); - }, -); diff --git a/tests/playwright/src/specs/kubernetes-networking.spec.ts b/tests/playwright/src/specs/kubernetes-networking.spec.ts deleted file mode 100644 index 78f21f07ff..0000000000 --- a/tests/playwright/src/specs/kubernetes-networking.spec.ts +++ /dev/null @@ -1,255 +0,0 @@ -/********************************************************************** - * 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { PlayYamlRuntime } from '../model/core/operations'; -import { KubernetesResourceState } from '../model/core/states'; -import { KubernetesResources } from '../model/core/types'; -import { createKindCluster, deleteCluster } from '../utility/cluster-operations'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { - checkKubernetesResourceState, - configurePortForwarding, - createKubernetesResource, - deleteKubernetesResource, - deployContainerToCluster, - monitorPodStatusInClusterContainer, - verifyLocalPortResponse, - verifyPortForwardingConfiguration, -} from '../utility/kubernetes'; -import { deleteContainer, deleteImage, ensureCliInstalled } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const CLUSTER_NAME: string = 'kind-cluster'; -const CLUSTER_CREATION_TIMEOUT: number = 300_000; -const KIND_NODE: string = `${CLUSTER_NAME}-control-plane`; -const RESOURCE_NAME: string = 'kind'; -const KUBERNETES_CONTEXT: string = `kind-${CLUSTER_NAME}`; -const KUBERNETES_NAMESPACE: string = 'default'; - -const DEPLOYMENT_NAME: string = 'test-deployment-resource'; -const SERVICE_NAME: string = 'test-service-resource'; -const INGRESS_NAME: string = 'test-ingress-resource'; -const KUBERNETES_RUNTIME = { - runtime: PlayYamlRuntime.Kubernetes, - kubernetesContext: KUBERNETES_CONTEXT, - kubernetesNamespace: KUBERNETES_NAMESPACE, -}; -const IMAGE_NAME: string = 'ghcr.io/podmandesktop-ci/nginx'; -const PULL_IMAGE_NAME: string = `${IMAGE_NAME}:latest`; -const CONTAINER_NAME: string = 'nginx-container'; -const POD_NAME: string = CONTAINER_NAME; - -const INGRESS_CONTROLLER_COMMAND: string = 'kubectl get pods -n projectcontour'; -const REMOTE_PORT: number = 80; -const LOCAL_PORT: number = 50000; -const PORT_FORWARDING_ADDRESS: string = `http://localhost:${LOCAL_PORT}/`; -const SERVICE_ADDRESS: string = `http://localhost:9090/`; -const RESPONSE_MESSAGE: string = 'Welcome to nginx!'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const DEPLOYMENT_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${DEPLOYMENT_NAME}.yaml`, -); -const SERVICE_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${SERVICE_NAME}.yaml`, -); -const INGRESS_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${INGRESS_NAME}.yaml`, -); - -const skipKindInstallation = process.env.SKIP_KIND_INSTALL === 'true'; -const providerTypeGHA = process.env.KIND_PROVIDER_GHA ?? ''; - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - test.setTimeout(350_000); - runner.setVideoAndTraceName('kubernetes-networking'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - if (!skipKindInstallation) { - const settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - - await ensureCliInstalled(page, 'Kind'); - } - - await createKindCluster(page, CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - providerType: providerTypeGHA, - useIngressController: true, - }); -}); - -test.afterAll(async ({ runner, page }) => { - test.setTimeout(90_000); - try { - await deleteContainer(page, CONTAINER_NAME); - await deleteImage(page, IMAGE_NAME); - await deleteCluster(page, RESOURCE_NAME, KIND_NODE, CLUSTER_NAME); - } finally { - await runner.close(); - } -}); - -test.describe('Kubernetes networking E2E test', { tag: '@k8s_e2e' }, () => { - test.describe.serial('Port forwarding workflow verification', { tag: '@k8s_e2e' }, () => { - test('Prepare deployment on the cluster', async ({ page, navigationBar }) => { - test.setTimeout(120_000); - //Pull image - let imagesPage = await navigationBar.openImages(); - const pullImagePage = await imagesPage.openPullImage(); - imagesPage = await pullImagePage.pullImage(PULL_IMAGE_NAME); - await playExpect.poll(async () => imagesPage.waitForImageExists(IMAGE_NAME, 10_000)).toBeTruthy(); - - //Push image to the cluster - const imageDetailPage = await imagesPage.openImageDetails(IMAGE_NAME); - await imageDetailPage.pushImageToKindCluster(); - - //Create container - imagesPage = await navigationBar.openImages(); - await imagesPage.startContainerWithImage(IMAGE_NAME, CONTAINER_NAME); - const containersPage = await navigationBar.openContainers(); - await playExpect - .poll(async () => containersPage.containerExists(CONTAINER_NAME), { timeout: 15_000 }) - .toBeTruthy(); - await containersPage.openContainersDetails(CONTAINER_NAME); - - //Deploy pod to the cluster - await deployContainerToCluster(page, CONTAINER_NAME, KUBERNETES_CONTEXT, POD_NAME); - }); - - test('Create port forwarding configuration', async ({ page }) => { - //Open pod details and create port forwarding configuration - await configurePortForwarding(page, KubernetesResources.Pods, POD_NAME); - }); - - test('Verify new local port response', async () => { - await verifyLocalPortResponse(PORT_FORWARDING_ADDRESS, RESPONSE_MESSAGE); - }); - - test('Verify Kubernetes port forwarding page', async ({ page }) => { - await verifyPortForwardingConfiguration(page, CONTAINER_NAME, LOCAL_PORT, REMOTE_PORT); - }); - - test('Delete configuration', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.PortForwarding, CONTAINER_NAME); - }); - - test('Verify UI components after removal', async ({ page, navigationBar }) => { - //Verify Kubernetes port forwarding page - const noForwardingsMessage = page.getByText('No port forwarding configured'); - await playExpect(noForwardingsMessage).toBeVisible(); - - //Verify Pod details page - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesPodsPage = await kubernetesBar.openTabPage(KubernetesResources.Pods); - await playExpect.poll(async () => kubernetesPodsPage.getRowByName(POD_NAME), { timeout: 15_000 }).toBeTruthy(); - const podDetailPage = await kubernetesPodsPage.openResourceDetails(POD_NAME, KubernetesResources.Pods); - await podDetailPage.activateTab('Summary'); - const forwardButton = page.getByRole('button', { name: `Forward...` }); - await playExpect(forwardButton).toBeVisible(); - }); - - test('Verify link response after removal', async () => { - await verifyLocalPortResponse(PORT_FORWARDING_ADDRESS, RESPONSE_MESSAGE); //expect to contain to pass until #11210 is resolved - }); - }); - test.describe - .serial('Ingress routing workflow verification', () => { - test('Check Ingress controller pods status', async ({ page }) => { - test.setTimeout(160_000); - await monitorPodStatusInClusterContainer(page, KIND_NODE, INGRESS_CONTROLLER_COMMAND); - }); - - test('Create and verify a running Kubernetes deployment', async ({ page }) => { - test.setTimeout(80_000); - await createKubernetesResource( - page, - KubernetesResources.Deployments, - DEPLOYMENT_NAME, - DEPLOYMENT_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.Deployments, - DEPLOYMENT_NAME, - KubernetesResourceState.Running, - 80_000, - ); - }); - test('Create and verify a running Kubernetes service', async ({ page }) => { - await createKubernetesResource( - page, - KubernetesResources.Services, - SERVICE_NAME, - SERVICE_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.Services, - SERVICE_NAME, - KubernetesResourceState.Running, - 10_000, - ); - }); - test('Create and verify a running Kubernetes ingress', async ({ page }) => { - await createKubernetesResource( - page, - KubernetesResources.IngeressesRoutes, - INGRESS_NAME, - INGRESS_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.IngeressesRoutes, - INGRESS_NAME, - KubernetesResourceState.Running, - 10_000, - ); - }); - test(`Verify the availability of the ${SERVICE_NAME} service.`, async () => { - await verifyLocalPortResponse(SERVICE_ADDRESS, RESPONSE_MESSAGE); - }); - test('Delete Kubernetes resources', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.IngeressesRoutes, INGRESS_NAME); - await deleteKubernetesResource(page, KubernetesResources.Services, SERVICE_NAME); - await deleteKubernetesResource(page, KubernetesResources.Deployments, DEPLOYMENT_NAME); - }); - }); -}); diff --git a/tests/playwright/src/specs/kubernetes-status-bar-providers.spec.ts b/tests/playwright/src/specs/kubernetes-status-bar-providers.spec.ts deleted file mode 100644 index 46ca35d4cf..0000000000 --- a/tests/playwright/src/specs/kubernetes-status-bar-providers.spec.ts +++ /dev/null @@ -1,149 +0,0 @@ -/********************************************************************** - * 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 { ResourceElementState } from '../model/core/states'; -import { CreateMachinePage } from '../model/pages/create-machine-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { canRunKindTests } from '../setupFiles/setup-kind'; -import { createKindCluster, deleteCluster } from '../utility/cluster-operations'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { - deletePodmanMachine, - ensureCliInstalled, - handleConfirmationDialog, - setStatusBarProvidersFeature, -} from '../utility/operations'; -import { isLinux } from '../utility/platform'; -import { waitForPodmanMachineStartup, waitUntil } from '../utility/wait'; - -const podmanProviderName = 'Podman'; -const defaultMachine = 'podman-machine-default'; -const newMachineName = 'podman-machine-new'; -const kindProviderName = 'Kind'; -const kindClusterName: string = 'kind-cluster'; -const kindNode: string = `${kindClusterName}-control-plane`; -const skipKindInstall = process.env.SKIP_KIND_INSTALL === 'true'; -const providerTypeGHA = process.env.KIND_PROVIDER_GHA ?? ''; - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - runner.setVideoAndTraceName('status-bar-providers-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - - if (!skipKindInstall) { - const settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - await ensureCliInstalled(page, 'Kind'); - } -}); - -test.afterAll(async ({ runner, page, navigationBar }) => { - if (test.info().status === 'failed') { - await setStatusBarProvidersFeature(page, navigationBar, false); - await deletePodmanMachine(page, newMachineName); - } - await deleteCluster(page, 'kind', kindNode, kindClusterName); - await runner.close(); -}); - -test.describe.serial('Status bar providers feature verification', { tag: '@k8s_e2e' }, () => { - test('Enable status bar providers experimental feature', async ({ navigationBar, page }) => { - await setStatusBarProvidersFeature(page, navigationBar, true); - }); - test('Verify default Podman provider in status bar', async ({ statusBar }) => { - const podmanStatusBarProviderButton = await statusBar.getProviderButton(podmanProviderName); - await playExpect(podmanStatusBarProviderButton).toBeVisible(); - }); - test('Verify clicking on provider', async ({ statusBar, page }) => { - const podmanStatusBarProviderButton = await statusBar.getProviderButton(podmanProviderName); - await podmanStatusBarProviderButton.click(); - const resourcesPage = new ResourcesPage(page); - await playExpect(resourcesPage.heading).toBeVisible(); - }); - test('Verify provider pinning', async ({ statusBar }) => { - await statusBar.pinProvider(podmanProviderName, false); - await statusBar.pinProvider(podmanProviderName, true); - }); - test('Verify hovering podman provider shows machine status', async ({ statusBar }) => { - if (isLinux) { - await playExpect - .poll(async () => await statusBar.isProviderResourceRunning(podmanProviderName, podmanProviderName)) - .toBeTruthy(); - } else { - await playExpect - .poll(async () => await statusBar.isProviderResourceRunning(podmanProviderName, defaultMachine)) - .toBeTruthy(); - } - }); - test('Create and delete new resource and verify providers updated', async ({ page, statusBar }) => { - test.skip(isLinux, 'Not creating new machine on Linux'); - test.setTimeout(350_000); - - //Create a new machine - const settingsBar = new SettingsBar(page); - await settingsBar.resourcesTab.click(); - const podmanResources = new ResourceConnectionCardPage(page, 'podman'); - await podmanResources.createButton.click(); - const createMachinePage = new CreateMachinePage(page); - await createMachinePage.createMachine(newMachineName, { startNow: true, setAsDefault: false }); - - const machineCard = new ResourceConnectionCardPage(page, 'podman', newMachineName); - playExpect(await machineCard.doesResourceElementExist()).toBeTruthy(); - await waitUntil( - async () => - (await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Running), - { timeout: 30_000, sendError: true }, - ); - - playExpect(await statusBar.isProviderResourceRunning(podmanProviderName, newMachineName)).toBeTruthy(); - - await deletePodmanMachine(page, newMachineName); - try { - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - } catch (error) { - console.log('No handling dialog displayed', error); - } - - playExpect(await statusBar.isProviderResourceRunning(podmanProviderName, newMachineName)).toBeFalsy(); - }); - test('Create new provider (Kind) and verify providers updated', async ({ page, statusBar }) => { - test.skip(!canRunKindTests(), `This test can't run on a windows rootless machine`); - test.setTimeout(600_000); - await createKindCluster(page, kindClusterName, 550_000, { - providerType: providerTypeGHA, - useIngressController: false, - }); - - //Verify its not pinned by default - const kindStatusBarProviderButton = await statusBar.getProviderButton(kindProviderName); - await playExpect(kindStatusBarProviderButton).not.toBeVisible(); - - await statusBar.pinProvider(kindProviderName, true); - await playExpect - .poll(async () => await statusBar.isProviderResourceRunning(kindProviderName, kindClusterName)) - .toBeTruthy(); - }); - test('Disable status bar providers feature', async ({ page, navigationBar, statusBar }) => { - await setStatusBarProvidersFeature(page, navigationBar, false); - await playExpect(statusBar.pinProvidersButton).not.toBeVisible(); - }); -}); diff --git a/tests/playwright/src/specs/kubernetes.spec.ts b/tests/playwright/src/specs/kubernetes.spec.ts deleted file mode 100644 index 690da50726..0000000000 --- a/tests/playwright/src/specs/kubernetes.spec.ts +++ /dev/null @@ -1,284 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { PlayYamlRuntime } from '../model/core/operations'; -import { KubernetesResourceState } from '../model/core/states'; -import { KubernetesResources } from '../model/core/types'; -import { canRunKindTests } from '../setupFiles/setup-kind'; -import { createKindCluster, deleteCluster } from '../utility/cluster-operations'; -import { test } from '../utility/fixtures'; -import { - applyYamlFileToCluster, - checkDeploymentReplicasInfo, - checkKubernetesResourceState, - createKubernetesResource, - deleteKubernetesResource, - editDeploymentYamlFile, -} from '../utility/kubernetes'; -import { deletePod, ensureCliInstalled } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const CLUSTER_NAME: string = 'kind-cluster'; -const CLUSTER_CREATION_TIMEOUT: number = 300_000; -const KIND_NODE: string = `${CLUSTER_NAME}-control-plane`; -const RESOURCE_NAME: string = 'kind'; -const KUBERNETES_CONTEXT: string = `kind-${CLUSTER_NAME}`; -const KUBERNETES_NAMESPACE: string = 'default'; -const PVC_NAME: string = 'test-pvc-resource'; -const PVC_POD_NAME: string = 'test-pod-pvcs'; -const CONFIG_MAP_NAME: string = 'test-configmap-resource'; -const SECRET_NAME: string = 'test-secret-resource'; -const SECRET_POD_NAME: string = 'test-pod-configmaps-secrets'; -const DEPLOYMENT_NAME: string = 'test-deployment-resource'; -const CRON_JOB_NAME: string = 'test-cronjob-resource'; - -const KUBERNETES_RUNTIME = { - runtime: PlayYamlRuntime.Kubernetes, - kubernetesContext: KUBERNETES_CONTEXT, - kubernetesNamespace: KUBERNETES_NAMESPACE, -}; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const PVC_YAML_PATH: string = path.resolve(__dirname, '..', '..', 'resources', 'kubernetes', `${PVC_NAME}.yaml`); -const PVC_POD_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${PVC_POD_NAME}.yaml`, -); -const CONFIG_MAP_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${CONFIG_MAP_NAME}.yaml`, -); -const SECRET_YAML_PATH: string = path.resolve(__dirname, '..', '..', 'resources', 'kubernetes', `${SECRET_NAME}.yaml`); -const SECRET_POD_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${SECRET_POD_NAME}.yaml`, -); -const DEPLOYMENT_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${DEPLOYMENT_NAME}.yaml`, -); -const CRON_JOB_YAML_PATH: string = path.resolve( - __dirname, - '..', - '..', - 'resources', - 'kubernetes', - `${CRON_JOB_NAME}.yaml`, -); - -const skipKindInstallation = process.env.SKIP_KIND_INSTALL === 'true'; -const providerTypeGHA = process.env.KIND_PROVIDER_GHA ?? ''; - -test.skip(!canRunKindTests(), `This test can't run on a windows rootless machine`); - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - test.setTimeout(350_000); - runner.setVideoAndTraceName('kubernetes-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - if (!skipKindInstallation) { - const settingsBar = await navigationBar.openSettings(); - await settingsBar.cliToolsTab.click(); - - await ensureCliInstalled(page, 'Kind'); - } - - await createKindCluster(page, CLUSTER_NAME, CLUSTER_CREATION_TIMEOUT, { - providerType: providerTypeGHA, - useIngressController: false, - }); -}); - -test.afterAll(async ({ runner, page }) => { - test.setTimeout(90000); - try { - await deleteCluster(page, RESOURCE_NAME, KIND_NODE, CLUSTER_NAME); - } finally { - await runner.close(); - } -}); - -test.describe('Kubernetes resources End-to-End test', { tag: '@k8s_e2e' }, () => { - test('Kubernetes Nodes test', async ({ page }) => { - await checkKubernetesResourceState(page, KubernetesResources.Nodes, KIND_NODE, KubernetesResourceState.Running); - }); - test.describe - .serial('PVC lifecycle test', () => { - test('Create a new PVC resource', async ({ page }) => { - await createKubernetesResource(page, KubernetesResources.PVCs, PVC_NAME, PVC_YAML_PATH, KUBERNETES_RUNTIME); - await checkKubernetesResourceState(page, KubernetesResources.PVCs, PVC_NAME, KubernetesResourceState.Stopped); - }); - test('Bind the PVC to a pod', async ({ page }) => { - await applyYamlFileToCluster(page, PVC_POD_YAML_PATH, KUBERNETES_RUNTIME); - await checkKubernetesResourceState( - page, - KubernetesResources.Pods, - PVC_POD_NAME, - KubernetesResourceState.Running, - ); - }); - test('Delete the PVC resource', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.Pods, PVC_POD_NAME); - await deleteKubernetesResource(page, KubernetesResources.PVCs, PVC_NAME); - }); - }); - test.describe - .serial('ConfigMaps and Secrets lifecycle test', () => { - test('Create ConfigMap resource', async ({ page }) => { - await createKubernetesResource( - page, - KubernetesResources.ConfigMapsSecrets, - CONFIG_MAP_NAME, - CONFIG_MAP_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.ConfigMapsSecrets, - CONFIG_MAP_NAME, - KubernetesResourceState.Running, - ); - }); - test('Create Secret resource', async ({ page }) => { - await createKubernetesResource( - page, - KubernetesResources.ConfigMapsSecrets, - SECRET_NAME, - SECRET_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.ConfigMapsSecrets, - SECRET_NAME, - KubernetesResourceState.Running, - ); - }); - test('Can load config and secrets via env. var in pod', async ({ page }) => { - test.setTimeout(120_000); - - await applyYamlFileToCluster(page, SECRET_POD_YAML_PATH, KUBERNETES_RUNTIME); - await checkKubernetesResourceState( - page, - KubernetesResources.Pods, - SECRET_POD_NAME, - KubernetesResourceState.Running, - ); - }); - test('Delete the ConfigMap and Secret resources', async ({ page }) => { - await deletePod(page, SECRET_POD_NAME); - await deleteKubernetesResource(page, KubernetesResources.ConfigMapsSecrets, SECRET_NAME); - await deleteKubernetesResource(page, KubernetesResources.ConfigMapsSecrets, CONFIG_MAP_NAME); - }); - }); - - test.describe - .serial('Deployment lifecycle test', () => { - test('Create a Kubernetes deployment resource', async ({ page }) => { - test.setTimeout(90_000); - await createKubernetesResource( - page, - KubernetesResources.Deployments, - DEPLOYMENT_NAME, - DEPLOYMENT_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.Deployments, - DEPLOYMENT_NAME, - KubernetesResourceState.Running, - ); - await checkDeploymentReplicasInfo(page, KubernetesResources.Deployments, DEPLOYMENT_NAME, 3); - }); - test('Edit the Kubernetes deployment YAML file', async ({ page }) => { - test.setTimeout(90_000); - await editDeploymentYamlFile(page, KubernetesResources.Deployments, DEPLOYMENT_NAME); - await checkKubernetesResourceState( - page, - KubernetesResources.Deployments, - DEPLOYMENT_NAME, - KubernetesResourceState.Running, - ); - await checkDeploymentReplicasInfo(page, KubernetesResources.Deployments, DEPLOYMENT_NAME, 5); - }); - test('Delete the Kubernetes deployment resource', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.Deployments, DEPLOYMENT_NAME); - }); - }); - - test.describe - .serial('Cronjobs lifecycle test', () => { - test('Create and verify a running Kubernetes cronjob', async ({ page }) => { - await createKubernetesResource( - page, - KubernetesResources.Cronjobs, - CRON_JOB_NAME, - CRON_JOB_YAML_PATH, - KUBERNETES_RUNTIME, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.Cronjobs, - CRON_JOB_NAME, - KubernetesResourceState.Running, - ); - }); - test('Validate Job and Pod execution from CronJob', async ({ page }) => { - test.setTimeout(80_000); - await checkKubernetesResourceState( - page, - KubernetesResources.Jobs, - CRON_JOB_NAME, - KubernetesResourceState.Running, - 70_000, - ); - await checkKubernetesResourceState( - page, - KubernetesResources.Pods, - CRON_JOB_NAME, - KubernetesResourceState.Succeeded, - 70_000, - ); - }); - test('Delete CronJob resource', async ({ page }) => { - await deleteKubernetesResource(page, KubernetesResources.Cronjobs, CRON_JOB_NAME); - }); - }); -}); diff --git a/tests/playwright/src/specs/play-yaml-build-smoke.spec.ts b/tests/playwright/src/specs/play-yaml-build-smoke.spec.ts deleted file mode 100644 index 69dee0a04b..0000000000 --- a/tests/playwright/src/specs/play-yaml-build-smoke.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -/********************************************************************** - * 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { ImageState, PodState } from '../model/core/states'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteImage, deletePod } from '../utility/operations'; -import { isCI, isLinux } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const POD_NAME: string = 'play-yaml-build-test'; -const LOCAL_IMAGE_NAME: string = 'localhost/foobar'; -const NGINX_IMAGE_NAME: string = 'docker.io/library/nginx'; -const CONTAINER_IMAGE: string = `${LOCAL_IMAGE_NAME}:latest`; -const CONTAINER_NAME: string = `${POD_NAME}-container`; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const POD_YAML_PATH: string = path.resolve(__dirname, '..', '..', 'resources', 'kube', 'play-yaml-build-test.yaml'); - -test.skip(!!isCI && isLinux, 'Skipping E2E test on GitHub Actions due to an outdated Podman version'); - -test.beforeAll(async ({ runner, page, welcomePage }) => { - runner.setVideoAndTraceName('play-yaml-build-smoke'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ page, runner }) => { - try { - await deletePod(page, POD_NAME); - await deleteImage(page, LOCAL_IMAGE_NAME); - await deleteImage(page, NGINX_IMAGE_NAME); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Deploy pod via Play YAML using locally built image', { tag: '@smoke' }, () => { - test('Deploy pod from YAML using build option and verify it is running', async ({ navigationBar }) => { - const podsPage = await navigationBar.openPods(); - await playExpect(podsPage.heading).toBeVisible(); - const playYamlPage = await podsPage.openPlayKubeYaml(); - await playExpect(playYamlPage.heading).toBeVisible(); - - await playYamlPage.playYaml(POD_YAML_PATH, true); - await playExpect(podsPage.heading).toBeVisible(); - const podDetails = await podsPage.openPodDetails(POD_NAME); - await playExpect(podDetails.heading).toBeVisible(); - await playExpect.poll(async () => await podDetails.getState(), { timeout: 15_000 }).toBe(PodState.Running); - }); - test('Verify that the deployed pod container uses the localhost image', async ({ page, navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect.poll(async () => await imagesPage.getCurrentStatusOfImage(LOCAL_IMAGE_NAME)).toBe('USED'); - - const containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect.poll(async () => containersPage.getContainerImage(CONTAINER_NAME)).toBe(CONTAINER_IMAGE); - - // delete applied pod, check the images now have unused state - await deletePod(page, POD_NAME); - await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - await playExpect - .poll(async () => await imagesPage.getCurrentStatusOfImage(LOCAL_IMAGE_NAME)) - .toEqual(ImageState.Unused); - await playExpect - .poll(async () => await imagesPage.getCurrentStatusOfImage(NGINX_IMAGE_NAME)) - .toEqual(ImageState.Unused); - }); -}); diff --git a/tests/playwright/src/specs/pod-smoke.spec.ts b/tests/playwright/src/specs/pod-smoke.spec.ts deleted file mode 100644 index 5e4e8e901f..0000000000 --- a/tests/playwright/src/specs/pod-smoke.spec.ts +++ /dev/null @@ -1,377 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 os from 'node:os'; - -import { ContainerState, PodState } from '../model/core/states'; -import type { ContainerInteractiveParams } from '../model/core/types'; -import { PodsPage } from '../model/pages/pods-page'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteContainer, deleteImage, deletePod } from '../utility/operations'; -import { waitForPodmanMachineStartup, waitUntil, waitWhile } from '../utility/wait'; - -let backendPort: string; -let frontendPort: string; - -const backendImage = 'quay.io/podman-desktop-demo/podify-demo-backend'; -const frontendImage = 'quay.io/podman-desktop-demo/podify-demo-frontend'; -const imagesTag = 'v1'; -const backendContainer = 'backend'; -const frontendContainer = 'frontend'; -const podToRun = 'frontend-app-pod'; -const isMac = os.platform() === 'darwin'; -const containerNames = ['container1', 'container2', 'container3']; -const podNames = ['pod1', 'pod2', 'pod3']; -const containerStartParams: ContainerInteractiveParams = { attachTerminal: false }; -let resetTestData = true; - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - runner.setVideoAndTraceName('pods-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - // wait giving a time to podman desktop to load up - const images = await navigationBar.openImages(); - await waitWhile(async () => await images.pageIsEmpty(), { - sendError: false, - message: 'Images page is empty, there are no images present', - }); - - if (test.info().retry > 0) return; - - await deletePod(page, podToRun); - await deleteContainer(page, backendContainer); - await deleteContainer(page, frontendContainer); -}); - -test.afterEach(async () => { - // This should always be compared to the final test in the suite, if any test is added after this one, this also should be updated - if (test.info().title.includes('Pruning pods')) { - resetTestData = true; - return; - } - - if (test.info().status !== test.info().expectedStatus) { - resetTestData = false; - return; - } - - resetTestData = true; -}); - -test.afterAll(async ({ page, runner }) => { - test.setTimeout(120_000); - - try { - if (!resetTestData) return; - - for (const pod of podNames) { - await deletePod(page, pod); - } - await deletePod(page, podToRun); - - for (const container of containerNames) { - await deleteContainer(page, container); - } - await deleteContainer(page, backendContainer); - await deleteContainer(page, frontendContainer); - await deleteImage(page, backendImage); - await deleteImage(page, frontendImage); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Verification of pod creation workflow', { tag: '@smoke' }, () => { - test('Pulling images', async ({ navigationBar }) => { - test.setTimeout(180_000); - - let images = await navigationBar.openImages(); - let pullImagePage = await images.openPullImage(); - images = await pullImagePage.pullImage(backendImage, imagesTag, 90_000); - const backendExists = await images.waitForImageExists(backendImage); - playExpect(backendExists, `${backendImage} image is not present in the list of images`).toBeTruthy(); - - await navigationBar.openImages(); - pullImagePage = await images.openPullImage(); - images = await pullImagePage.pullImage(frontendImage, imagesTag, 90_000); - const frontendExists = await images.waitForImageExists(frontendImage); - playExpect(frontendExists, `${frontendImage} image is not present in the list of images`).toBeTruthy(); - }); - - test('Starting containers', async ({ navigationBar }) => { - let images = await navigationBar.openImages(); - let imageDetails = await images.openImageDetails(backendImage); - let runImage = await imageDetails.openRunImage(); - await runImage.setCustomPortMapping('6379:6379'); - let containers = await runImage.startContainer(backendContainer, containerStartParams); - await playExpect(containers.header).toBeVisible(); - await playExpect - .poll(async () => await containers.containerExists(backendContainer), { timeout: 15_000 }) - .toBeTruthy(); - let containerDetails = await containers.openContainersDetails(backendContainer); - await playExpect - .poll(async () => await containerDetails.getState(), { timeout: 15_000 }) - .toBe(ContainerState.Running); - await waitUntil(async () => { - backendPort = await containerDetails.getContainerPort(); - return backendPort.includes('6379'); - }); - images = await navigationBar.openImages(); - imageDetails = await images.openImageDetails(frontendImage); - runImage = await imageDetails.openRunImage(); - if (isMac) { - await runImage.setHostPortToExposedContainerPort('5000', '5101'); - } - containers = await runImage.startContainer(frontendContainer, containerStartParams); - await playExpect(containers.header).toBeVisible(); - await playExpect - .poll(async () => await containers.containerExists(frontendContainer), { timeout: 15_000 }) - .toBeTruthy(); - containerDetails = await containers.openContainersDetails(frontendContainer); - frontendPort = await containerDetails.getContainerPort(); - const expectedPort = isMac ? '5101' : '5000'; - playExpect(frontendPort).toContain(expectedPort); - await playExpect - .poll(async () => await containerDetails.getState(), { timeout: 15_000 }) - .toBe(ContainerState.Running); - }); - - test('Podify containers', async ({ navigationBar }) => { - test.setTimeout(90000); - - const containers = await navigationBar.openContainers(); - const createPodPage = await containers.openCreatePodPage(Array.of(backendContainer, frontendContainer)); - const pods = await createPodPage.createPod(podToRun); - await playExpect(pods.heading).toBeVisible({ timeout: 60_000 }); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 15_000 }).toBeTruthy(); - const podDetails = await pods.openPodDetails(podToRun); - await playExpect.poll(async () => await podDetails.getState(), { timeout: 15_000 }).toBe(PodState.Running); - }); - - test('Test navigation between pages', async ({ navigationBar }) => { - const pods = await navigationBar.openPods(); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 10_000 }).toBeTruthy(); - - const podDetails = await pods.openPodDetails(podToRun); - await playExpect(podDetails.heading).toBeVisible(); - await podDetails.backLink.click(); - await playExpect(pods.heading).toBeVisible(); - - await pods.openPodDetails(podToRun); - await playExpect(podDetails.heading).toBeVisible(); - await podDetails.closeButton.click(); - await playExpect(pods.heading).toBeVisible(); - }); - - test('Checking pod details', async ({ navigationBar }) => { - const pods = await navigationBar.openPods(); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 10_000 }).toBeTruthy(); - const podDetails = await pods.openPodDetails(podToRun); - await playExpect(podDetails.heading).toBeVisible(); - await playExpect(podDetails.heading).toContainText(podToRun); - await podDetails.activateTab('Logs'); - await podDetails.activateTab('Summary'); - const row = podDetails.getPage().getByRole('table').getByRole('row'); - const nameText = await row.getByRole('cell').allInnerTexts(); - playExpect(nameText).toContain(podToRun); - await podDetails.activateTab('Inspect'); - await podDetails.activateTab('Kube'); - }); - - test('Checking original containers stopped', async ({ navigationBar }) => { - const containers = await navigationBar.openContainers(); - const backendContainerDetails = await containers.openContainersDetails(backendContainer); - await playExpect - .poll(async () => await backendContainerDetails.getState(), { timeout: 15_000 }) - .toBe(ContainerState.Exited); - await navigationBar.openContainers(); - const frontendContainerDetails = await containers.openContainersDetails(frontendContainer); - await playExpect - .poll(async () => await frontendContainerDetails.getState(), { timeout: 15_000 }) - .toBe(ContainerState.Exited); - }); - - test('Checking pods page options buttons', async ({ navigationBar }) => { - const pods = await navigationBar.openPods(); - await pods.selectPod([podToRun]); - const deleteButton = pods.getPage().getByRole('button', { name: 'Delete 1 selected items', exact: true }); - await playExpect(deleteButton).toBeVisible(); - - const actionsMenuButton = await pods.getPodActionsMenu(podToRun); - await playExpect(actionsMenuButton).toBeVisible(); - await actionsMenuButton.click(); - const kubeButton = pods.getPage().getByTitle('Generate Kube'); - const kubernetesButton = pods.getPage().getByTitle('Deploy to Kubernetes'); - const restartButton = pods.getPage().getByTitle('Restart Pod'); - await playExpect(kubeButton).toBeVisible(); - await playExpect(kubernetesButton).toBeVisible(); - await playExpect(restartButton).toBeVisible(); - }); - - test(`Checking pods under containers`, async ({ navigationBar, page }) => { - const containers = await navigationBar.openContainers(); - await playExpect.poll(async () => containers.containerExists(podToRun), { timeout: 10_000 }).toBeTruthy(); - await playExpect - .poll(async () => containers.containerExists(`${backendContainer}-podified`), { timeout: 10_000 }) - .toBeTruthy(); - await playExpect - .poll(async () => containers.containerExists(`${frontendContainer}-podified`), { timeout: 10_000 }) - .toBeTruthy(); - - const containerDetailsPage = await containers.openContainersDetails(`${frontendContainer}-podified`); - await playExpect(containerDetailsPage.heading).toContainText(`${frontendContainer}-podified`); - await containerDetailsPage.activateTab('Terminal'); - - await playExpect(containerDetailsPage.terminalContent).toBeVisible(); - await playExpect(containerDetailsPage.terminalContent).toContainText('@'); - await page.waitForTimeout(1_000); - await containerDetailsPage.terminalInput.pressSequentially('pwd', { delay: 15 }); - await containerDetailsPage.terminalInput.press('Enter'); - await playExpect(containerDetailsPage.terminalContent).toContainText('app'); - }); - - test('Checking deployed application', async () => { - test.setTimeout(75000); - - // fetch the application page - // this might not work on macos - const address = 'http://localhost:' + frontendPort; - await playExpect.poll(async () => await appRunningOnPort(address), { timeout: 60_000 }).toBeTruthy(); - - for (let i = 2; i < 5; i++) { - const response: Response = await fetch(address); - const blob: Blob = await response.blob(); - const text: string = await blob.text(); - playExpect(text).toContain('Hello World!'); - // regex for div with number of visits - const regex = /]*>(\d+)<\/div>/i; - const matches = RegExp(regex).exec(text); - playExpect(matches![1]).toEqual(i.toString()); - playExpect(matches).toBeDefined(); - playExpect(text).toContain('time(s)'); - } - }); - - test.describe(() => { - test.describe.configure({ retries: 1 }); - test('Restarting pod', async ({ navigationBar }) => { - test.setTimeout(180_000); - const pods = await navigationBar.openPods(); - await playExpect(pods.heading).toBeVisible({ timeout: 15_000 }); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 15_000 }).toBeTruthy(); - - const podDetails = await pods.openPodDetails(podToRun); - await playExpect(podDetails.heading).toBeVisible(); - await playExpect(podDetails.heading).toContainText(podToRun); - - await playExpect(async () => { - await podDetails.restartPod(); - await playExpect.poll(async () => await podDetails.getState(), { timeout: 15_000 }).toBe(PodState.Restarting); - await playExpect.poll(async () => await podDetails.getState(), { timeout: 30_000 }).toBe(PodState.Running); - }).toPass({ timeout: 120_000 }); - - await playExpect(podDetails.stopButton).toBeVisible(); - }); - - test('Stopping and starting pod', async ({ navigationBar }) => { - const pods = await navigationBar.openPods(); - await playExpect(pods.heading).toBeVisible({ timeout: 15_000 }); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 15_000 }).toBeTruthy(); - - const podDetailsPage = await pods.openPodDetails(podToRun); - await playExpect(podDetailsPage.heading).toBeVisible(); - await playExpect(podDetailsPage.heading).toContainText(podToRun); - await podDetailsPage.stopPod(); - await playExpect.poll(async () => await podDetailsPage.getState(), { timeout: 30_000 }).toBe(PodState.Exited); - - await podDetailsPage.startPod(); - await playExpect.poll(async () => await podDetailsPage.getState(), { timeout: 30_000 }).toBe(PodState.Running); - await playExpect(podDetailsPage.stopButton).toBeVisible(); - }); - - test('Stopping and deleting pod', async ({ navigationBar }) => { - test.setTimeout(90_000); - - const pods = await navigationBar.openPods(); - await playExpect(pods.heading).toBeVisible({ timeout: 15_000 }); - await playExpect.poll(async () => await pods.podExists(podToRun), { timeout: 15_000 }).toBeTruthy(); - - const podDetailsPage = await pods.openPodDetails(podToRun); - await playExpect(podDetailsPage.heading).toBeVisible(); - await playExpect(podDetailsPage.heading).toContainText(podToRun); - - await podDetailsPage.stopPod(); - await playExpect.poll(async () => await podDetailsPage.getState(), { timeout: 30_000 }).toBe(PodState.Exited); - - await playExpect(podDetailsPage.heading).toContainText(podToRun); - const podsPage = await podDetailsPage.deletePod(); - await playExpect(podsPage.heading).toBeVisible({ timeout: 30_000 }); - await playExpect.poll(async () => await podsPage.podExists(podToRun), { timeout: 30_000 }).toBeFalsy(); - }); - - test('Pruning pods', async ({ page, navigationBar }) => { - test.setTimeout(90_000); - - const portsList = [5001, 5002, 5003]; - - for (let i = 0; i < 3; i++) { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - const imageDetailsPage = await imagesPage.openImageDetails(backendImage); - await playExpect(imageDetailsPage.heading).toContainText(backendImage); - const runImagePage = await imageDetailsPage.openRunImage(); - await playExpect(runImagePage.heading).toContainText(backendImage); - await runImagePage.setCustomPortMapping(`${portsList[i]}:${portsList[i]}`); - const containersPage = await runImagePage.startContainer(containerNames[i], containerStartParams); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => containersPage.containerExists(containerNames[i]), { timeout: 15_000 }) - .toBeTruthy(); - await containersPage.uncheckAllContainers(); - const createPodPage = await containersPage.openCreatePodPage(Array.of(containerNames[i])); - const podsPage = await createPodPage.createPod(podNames[i]); - await playExpect(podsPage.heading).toBeVisible({ timeout: 60_000 }); - await playExpect.poll(async () => await podsPage.podExists(podNames[i]), { timeout: 15_000 }).toBeTruthy(); - } - - for (const pod of podNames) { - const podDetailsPage = await new PodsPage(page).openPodDetails(pod); - await playExpect(podDetailsPage.heading).toBeVisible(); - await playExpect(podDetailsPage.heading).toContainText(pod); - - await podDetailsPage.stopPod(); - await playExpect.poll(async () => await podDetailsPage.getState(), { timeout: 30_000 }).toBe(PodState.Exited); - - const podsPage = await navigationBar.openPods(); - await playExpect(podsPage.heading).toBeVisible(); - await podsPage.prunePods(); - await playExpect.poll(async () => await podsPage.podExists(pod), { timeout: 15_000 }).toBeFalsy(); - } - }); - }); - - async function appRunningOnPort(address: string): Promise { - return await fetch(address) - .then(response => { - return response.ok; - }) - .catch(() => { - return false; - }); - } -}); diff --git a/tests/playwright/src/specs/podman-compose-smoke.spec.ts b/tests/playwright/src/specs/podman-compose-smoke.spec.ts deleted file mode 100644 index c69d33b0f4..0000000000 --- a/tests/playwright/src/specs/podman-compose-smoke.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************** - * 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { CLIToolsPage } from '../model/pages/cli-tools-page'; -import { ResourceCliCardPage } from '../model/pages/resource-cli-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteContainer, deleteImage, runComposeUpFromCLI } from '../utility/operations'; -import { isCI, isLinux, isMac } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const RESOURCE_NAME: string = 'Compose'; -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const backendContainerName = 'backend-1'; -const frontendContainerName = 'frontend-1'; -const composeContainer = 'resources'; -const backendImageName = 'quay.io/podman-desktop-demo/podify-demo-backend'; -const frontendImageName = 'quay.io/podman-desktop-demo/podify-demo-frontend'; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('podman-compose-e2e'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ page, runner }) => { - test.setTimeout(180_000); - try { - await deleteContainer(page, backendContainerName); - await deleteContainer(page, frontendContainerName); - await deleteImage(page, backendImageName); - await deleteImage(page, frontendImageName); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Compose compose workflow verification', { tag: '@smoke' }, () => { - test('Verify Compose was installed', async ({ page, navigationBar }) => { - test.skip(!!isCI && isLinux, 'This test should not run on Ubuntu platform in Github Actions'); - test.skip(!!isMac, 'Currently there is an issue with running this test on macOS platform'); - - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const composeBox = new ResourceCliCardPage(page, RESOURCE_NAME); - const setupButton = composeBox.setupButton; - await playExpect(setupButton).toBeHidden(); - - const cliToolsPage = await settingsBar.openTabPage(CLIToolsPage); - const composeRow = cliToolsPage.toolsTable.getByLabel(RESOURCE_NAME); - const composeVersionInfo = composeRow.getByLabel('cli-version'); - await playExpect(composeVersionInfo).toContainText('docker-compose'); - }); - - test('Check Podman Desktop autorefresh when using podman compose up', async ({ navigationBar }) => { - test.setTimeout(300_000); - - const composeFilePath = path.resolve(__dirname, '..', '..', 'resources', `compose.yaml`); - await runComposeUpFromCLI(composeFilePath); - - const containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - - await playExpect - .poll(async () => await containersPage.containerExists(composeContainer), { timeout: 120_000 }) - .toBeTruthy(); - await playExpect - .poll(async () => await containersPage.containerExists(backendContainerName), { timeout: 120_000 }) - .toBeTruthy(); - await playExpect - .poll(async () => await containersPage.containerExists(frontendContainerName), { timeout: 120_000 }) - .toBeTruthy(); - - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - await playExpect.poll(async () => await imagesPage.waitForImageExists(backendImageName)).toBeTruthy(); - await playExpect.poll(async () => await imagesPage.waitForImageExists(frontendImageName)).toBeTruthy(); - }); -}); diff --git a/tests/playwright/src/specs/podman-extension-smoke.spec.ts b/tests/playwright/src/specs/podman-extension-smoke.spec.ts deleted file mode 100644 index e172c37e4b..0000000000 --- a/tests/playwright/src/specs/podman-extension-smoke.spec.ts +++ /dev/null @@ -1,135 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { DashboardPage } from '../model/pages/dashboard-page'; -import type { ExtensionDetailsPage } from '../model/pages/extension-details-page'; -import type { SettingsBar } from '../model/pages/settings-bar'; -import { NavigationBar } from '../model/workbench/navigation'; -import { expect as playExpect, test } from '../utility/fixtures'; - -const extensionLabel = 'podman-desktop.podman'; -const extensionLabelName = 'podman'; -const extensionHeading = 'podman'; -const PODMAN_EXTENSION_STATUS_ACTIVE: string = 'ACTIVE'; -const PODMAN_EXTENSION_STATUS_DISABLED: string = 'DISABLED'; -const SETTINGS_NAVBAR_PREFERENCES_PODMAN_EXTENSION: string = 'Extension: Podman'; - -let dashboardPage: DashboardPage; -let settingsBar: SettingsBar; -let navigationBar: NavigationBar; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('podman-extensions-e2e'); - await welcomePage.handleWelcomePage(true); - navigationBar = new NavigationBar(page); -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120000); - await runner.close(); -}); - -test.describe.serial('Verification of Podman extension', { tag: '@smoke' }, () => { - test('Podman is enabled and present', async () => { - await verifyPodmanExtensionStatus(true); - }); - test('Podman extension can be disabled from Podman Extension Page', async () => { - const podmanExtensionPage = await openExtensionsPodmanPage(); - await podmanExtensionPage.disableExtension(); - await verifyPodmanExtensionStatus(false); - }); - test('Podman extension can be re-enabled from Extension Page', async () => { - const podmanExtensionPage = await openExtensionsPodmanPage(); // enable the extension - await podmanExtensionPage.enableExtension(); - await verifyPodmanExtensionStatus(true); - }); -}); - -async function verifyPodmanExtensionStatus(enabled: boolean): Promise { - dashboardPage = await navigationBar.openDashboard(); - - if (enabled) { - await playExpect(dashboardPage.getPodmanStatusLocator()).toBeVisible({ - timeout: 15_000, - }); - } else { - await playExpect(dashboardPage.getPodmanStatusLocator()).not.toBeVisible({ - timeout: 15_000, - }); - } - // always present and visible - // go to the details of the extension - const extensionsPage = await navigationBar.openExtensions(); - const extensionDetailsPage = await extensionsPage.openExtensionDetails( - extensionLabelName, - extensionLabel, - extensionHeading, - ); - - const extensionStatusLabel = extensionDetailsPage.status; - - await playExpect(extensionStatusLabel).toBeVisible(); - await extensionStatusLabel.scrollIntoViewIfNeeded(); - // -------------------------- - - if (enabled) { - await playExpect(extensionStatusLabel).toContainText(PODMAN_EXTENSION_STATUS_ACTIVE, { timeout: 20_000 }); - } else { - await playExpect(extensionStatusLabel).toContainText(PODMAN_EXTENSION_STATUS_DISABLED, { timeout: 20_000 }); - } - // always present and visible - const extensionsPageAfter = await navigationBar.openExtensions(); - const podmanExtensionPage = await extensionsPageAfter.openExtensionDetails( - extensionLabelName, - extensionLabel, - extensionHeading, - ); - - // -------------------------- - if (enabled) { - await playExpect(podmanExtensionPage.enableButton).not.toBeVisible({ - timeout: 10_000, - }); - await playExpect(podmanExtensionPage.disableButton).toBeVisible({ - timeout: 10_000, - }); - await playExpect(podmanExtensionPage.status.getByText(PODMAN_EXTENSION_STATUS_ACTIVE)).toBeVisible(); - } else { - await playExpect(podmanExtensionPage.enableButton).toBeVisible({ - timeout: 10_000, - }); - await playExpect(podmanExtensionPage.disableButton).not.toBeVisible({ - timeout: 10_000, - }); - await playExpect(podmanExtensionPage.status.getByText(PODMAN_EXTENSION_STATUS_DISABLED)).toBeVisible(); - } - - // expand Settings -> Preferences menu - settingsBar = await navigationBar.openSettings(); - await settingsBar.preferencesTab.click(); - - await playExpect(settingsBar.getSettingsNavBarTabLocator(SETTINGS_NAVBAR_PREFERENCES_PODMAN_EXTENSION)).toBeVisible(); - - // collapse Settings -> Preferences menu - await settingsBar.preferencesTab.click(); -} - -async function openExtensionsPodmanPage(): Promise { - const extensionsPage = await navigationBar.openExtensions(); - return extensionsPage.openExtensionDetails(extensionLabelName, extensionLabel, extensionHeading); -} diff --git a/tests/playwright/src/specs/provider-specs/chat-smoke.spec.ts b/tests/playwright/src/specs/provider-specs/chat-smoke.spec.ts new file mode 100644 index 0000000000..e34cc1548c --- /dev/null +++ b/tests/playwright/src/specs/provider-specs/chat-smoke.spec.ts @@ -0,0 +1,173 @@ +/********************************************************************** + * 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 { TIMEOUTS } from 'src/model/core/types'; + +import { expect, test } from '../../fixtures/provider-fixtures'; +import { waitForNavigationReady } from '../../utils/app-ready'; + +test.skip(!!process.env.CI, 'Skipping chat tests on CI'); + +test.describe.serial('Chat page navigation', { tag: '@smoke' }, () => { + test.beforeEach(async ({ page, navigationBar }) => { + await waitForNavigationReady(page); + await navigationBar.navigateToChatPage(); + }); + + test('[CHAT-01] All chat UI elements are visible', async ({ chatPage }) => { + await chatPage.verifyHeaderElementsVisible(); + await chatPage.verifyInputAreaVisible(); + await chatPage.verifySuggestedMessagesVisible(); + }); + + test('[CHAT-02] Create and check new chat history item', async ({ chatPage }) => { + await chatPage.ensureSidebarVisible(); + const initialCount = await chatPage.getChatHistoryCount(); + await chatPage.getSuggestedMessages().last().click(); + await chatPage.waitForChatHistoryCount(initialCount + 1, 15_000); + }); + + test('[CHAT-03] Create and switch between multiple chat sessions without data loss', async ({ chatPage }) => { + await chatPage.ensureSidebarVisible(); + let messageCount = await chatPage.getChatHistoryCount(); + + const chatSessions = [ + { message: 'What is Kubernetes?', expectedIndex: 1 }, + { message: 'Explain Docker containers', expectedIndex: 0 }, + ]; + + for (const session of chatSessions) { + await chatPage.clickNewChat(); + await chatPage.sendMessage(session.message); + + messageCount++; + await chatPage.waitForChatHistoryCount(messageCount); + } + + for (const session of chatSessions) { + await chatPage.clickChatHistoryItemByIndex(session.expectedIndex); + await chatPage.verifyConversationMessage(session.message); + } + }); + + test('[CHAT-04] Delete single chat item and then delete all remaining items', async ({ chatPage }) => { + await chatPage.ensureSidebarVisible(); + const initialCount = await chatPage.getChatHistoryCount(); + + await chatPage.deleteChatHistoryItemByIndex(0); + const expectedCountAfterSingleDelete = initialCount - 1; + await chatPage.waitForChatHistoryCount(expectedCountAfterSingleDelete); + + await chatPage.deleteAllChatHistoryItems(); + await chatPage.verifyChatHistoryEmpty(); + + await chatPage.ensureNotificationsAreNotVisible(); + }); + + test('[CHAT-05] Switch between all available models and verify each selection', async ({ chatPage }) => { + const modelCount = await chatPage.getAvailableModelsCount(); + + if (modelCount < 2) { + test.skip(true, 'Skipping test: Less than 2 models available'); + return; + } + + const maxModelsToTest = Math.min(modelCount, 3); + + for (let modelIndex = maxModelsToTest - 1; modelIndex >= 0; modelIndex--) { + await chatPage.selectModelByIndex(modelIndex); + const selectedModelName = await chatPage.getSelectedModelName(); + expect(selectedModelName).toBeTruthy(); + + await chatPage.clickNewChat(); + + const testMessage = `Test message for model: ${selectedModelName}`; + await chatPage.sendMessage(testMessage); + await chatPage.verifyConversationMessage(testMessage); + } + }); + + test('[CHAT-06] Change models mid-conversation, verify conversation history is preserved', async ({ chatPage }) => { + const modelCount = await chatPage.getAvailableModelsCount(); + + if (modelCount < 2) { + test.skip(true, 'Skipping test: Less than 2 models available'); + return; + } + + await chatPage.ensureSidebarVisible(); + await chatPage.clickNewChat(); + const initialCount = await chatPage.getChatHistoryCount(); + + const modelSwitches = [ + { modelIndex: 0, message: 'Hello, how are you?' }, + { modelIndex: 1, message: 'Tell me about AI models' }, + ]; + + const sentMessages: string[] = []; + const expectedCountAfterFirstMessage = initialCount + 1; + + for (const modelSwitch of modelSwitches) { + await chatPage.selectModelByIndex(modelSwitch.modelIndex); + const modelName = await chatPage.getSelectedModelName(); + expect(modelName).toBeTruthy(); + + await chatPage.sendMessage(modelSwitch.message); + sentMessages.push(modelSwitch.message); + + await chatPage.waitForChatHistoryCount(expectedCountAfterFirstMessage); + + for (const message of sentMessages) { + await chatPage.verifyConversationMessage(message); + } + } + }); + + test('[CHAT-07] Export chat as Flow', async ({ chatPage, navigationBar, flowsPage }) => { + await chatPage.ensureSidebarVisible(); + await chatPage.clickNewChat(); + + const promptForExport = + 'write a typescript recursive method that calculates the fibonacci number for a given index without using memoization'; + // Regex pattern to verify the model response contains recursive Fibonacci code + const expectedModelResponsePattern = /(\w+)\(\s*(\w+)\s*-\s*1\s*\)\s*\+\s*\1\(\s*\2\s*-\s*2\s*\)/; + const flowName = 'export-chat-as-flow'; + + await chatPage.sendMessage(promptForExport); + await chatPage.verifyConversationMessage(promptForExport); + await expect + .poll(async () => await chatPage.verifyModelConversationMessage(expectedModelResponsePattern), { + timeout: TIMEOUTS.STANDARD, + message: 'Model should respond with recursive Fibonacci code pattern', + }) + .toBeTruthy(); + + // Capture the current model name before exporting to verify it's preserved in the flow + const currentModelName = await chatPage.getSelectedModelName(); + expect(currentModelName).toBeTruthy(); + + const flowCreatePage = await chatPage.exportAsFlow(); + await flowCreatePage.waitForLoad(); + await expect(flowCreatePage.selectModelDropdown).toContainText(currentModelName); + + await flowCreatePage.createNewFlow(flowName); + await navigationBar.navigateToFlowsPage(); + await flowsPage.ensureRowExists(flowName, TIMEOUTS.STANDARD, false); + + await flowsPage.deleteAllFlows(); + }); +}); diff --git a/tests/playwright/src/specs/provider-specs/flows-smoke.spec.ts b/tests/playwright/src/specs/provider-specs/flows-smoke.spec.ts new file mode 100644 index 0000000000..00ee8dd5a1 --- /dev/null +++ b/tests/playwright/src/specs/provider-specs/flows-smoke.spec.ts @@ -0,0 +1,125 @@ +/********************************************************************** + * 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 { TIMEOUTS } from 'src/model/core/types'; + +import { expect, test } from '../../fixtures/provider-fixtures'; +import { waitForNavigationReady } from '../../utils/app-ready'; + +const flowName = 'custom-flow-smoke-test'; +const flowNameFromContentRegion = 'custom-flow-content-region-test'; +const prompt = + 'write a typescript recursive method that calculates the fibonacci number for a given index without using memoization'; +const expectedTerminalContent = /(\w+)\(\s*(\w+)\s*-\s*1\s*\)\s*\+\s*\1\(\s*\2\s*-\s*2\s*\)/; + +test.skip(!!process.env.CI, 'Skipping flow tests on CI'); + +test.beforeAll(async ({ page, navigationBar, flowsPage }) => { + await waitForNavigationReady(page); + await navigationBar.navigateToFlowsPage(); + await flowsPage.deleteAllFlows(TIMEOUTS.STANDARD); +}); + +test.afterAll(async ({ navigationBar, flowsPage }) => { + await navigationBar.navigateToFlowsPage(); + await flowsPage.deleteAllFlows(TIMEOUTS.STANDARD); +}); + +test.describe.serial('Flow page e2e test suite', { tag: '@smoke' }, () => { + test('[FLOW-01] Check that Flows page is displayed and empty', async ({ flowsPage }) => { + await expect.poll(async () => flowsPage.checkIfFlowsPageIsEmpty()).toBeTruthy(); + }); + + test('[FLOW-02] Check that user can create a new flow', async ({ navigationBar, flowsPage }) => { + await flowsPage.createFlow(flowName, { prompt }); + await navigationBar.navigateToFlowsPage(); + await flowsPage.ensureRowExists(flowName, TIMEOUTS.STANDARD, false); + }); + + test('[FLOW-03] Check that user can run the created flow from details page and validate the results', async ({ + flowsPage, + }) => { + const flowDetailsPage = await flowsPage.openFlowDetailsPageByName(flowName); + await flowDetailsPage.waitForLoad(); + + await flowDetailsPage.runFlow(); + await flowDetailsPage.switchToRunTab(); + + await flowDetailsPage.waitForTerminalContent(expectedTerminalContent, TIMEOUTS.DEFAULT); + }); + + test('[FLOW-04] Check that user can delete the created flow from details page', async ({ + navigationBar, + flowsPage, + }) => { + await navigationBar.navigateToFlowsPage(); + + const flowDetailsPage = await flowsPage.openFlowDetailsPageByName(flowName); + await flowDetailsPage.waitForLoad(); + await flowDetailsPage.deleteFlow(); + + await flowsPage.waitForLoad(); + await flowsPage.ensureRowDoesNotExist(flowName, TIMEOUTS.STANDARD, false); + }); + + test('[FLOW-05] Check that user can create a new flow from content region', async ({ navigationBar, flowsPage }) => { + await expect.poll(async () => await flowsPage.checkIfFlowsPageIsEmpty()).toBeTruthy(); + await flowsPage.createFlowFromContentRegion(flowNameFromContentRegion, { prompt }); + await navigationBar.navigateToFlowsPage(); + await flowsPage.ensureRowExists(flowNameFromContentRegion, TIMEOUTS.STANDARD, false); + }); + + test('[FLOW-06] Check that user can run the created flow from Flows page using the run button', async ({ + flowsPage, + }) => { + const flowDetailsPage = await flowsPage.runFlowByName(flowNameFromContentRegion); + await flowDetailsPage.waitForLoad(); + + await flowDetailsPage.switchToRunTab(); + await expect + .poll(async () => await flowDetailsPage.getCurrentNumberOfRuns(), { timeout: TIMEOUTS.STANDARD }) + .toBe(1); + await flowDetailsPage.waitForTerminalContent(expectedTerminalContent, TIMEOUTS.DEFAULT); + }); + + test('[FLOW-07] Run a flow a second time and check that task can be selected and changed', async ({ + navigationBar, + flowsPage, + }) => { + await navigationBar.navigateToFlowsPage(); + const flowDetailsPage = await flowsPage.runFlowByName(flowNameFromContentRegion); + await flowDetailsPage.waitForLoad(); + + await flowDetailsPage.switchToRunTab(); + await expect + .poll(async () => await flowDetailsPage.getCurrentNumberOfRuns(), { timeout: TIMEOUTS.STANDARD }) + .toBe(2); + + await flowDetailsPage.selectLastTask(); + await flowDetailsPage.waitForTerminalContent(expectedTerminalContent, TIMEOUTS.DEFAULT); + }); + + test('[FLOW-08] Check that user can delete the created flow from Flows page using the delete button', async ({ + navigationBar, + flowsPage, + }) => { + await navigationBar.navigateToFlowsPage(); + await flowsPage.deleteFlowByName(flowNameFromContentRegion); + await flowsPage.ensureRowDoesNotExist(flowNameFromContentRegion, TIMEOUTS.STANDARD, false); + }); +}); diff --git a/tests/playwright/src/specs/provider-specs/mcp-smoke.spec.ts b/tests/playwright/src/specs/provider-specs/mcp-smoke.spec.ts new file mode 100644 index 0000000000..376ff70d14 --- /dev/null +++ b/tests/playwright/src/specs/provider-specs/mcp-smoke.spec.ts @@ -0,0 +1,74 @@ +/********************************************************************** + * 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 { expect, test } from '../../fixtures/provider-fixtures'; +import { MCP_SERVERS } from '../../model/core/types'; +import { waitForNavigationReady } from '../../utils/app-ready'; + +const MCP_REGISTRY_EXAMPLE = 'MCP Registry example'; +const MCP_REGISTRY_URL = 'https://registry.modelcontextprotocol.io'; +const SERVER_LIST_UPDATE_TIMEOUT = 60_000; +const SERVER_CONNECTION_TIMEOUT = 10_000; + +test.describe('MCP Registry Management', { tag: '@smoke' }, () => { + test.beforeEach(async ({ page, navigationBar }) => { + await waitForNavigationReady(page); + await navigationBar.navigateToMCPPage(); + }); + + test('[MCP-01] Add and remove MCP registry: verify server list updates accordingly', async ({ mcpPage }) => { + const editRegistriesTab = await mcpPage.openEditRegistriesTab(); + await editRegistriesTab.ensureRowExists(MCP_REGISTRY_EXAMPLE); + + const installTab = await mcpPage.openInstallTab(); + await installTab.verifyInstallTabIsNotEmpty(); + const initialServerCount = await installTab.countRowsFromTable(); + + await mcpPage.openEditRegistriesTab(); + await editRegistriesTab.addNewRegistry(MCP_REGISTRY_URL); + await editRegistriesTab.ensureRowExists(MCP_REGISTRY_URL); + + await mcpPage.openInstallTab(); + await installTab.verifyServerCountIncreased(initialServerCount, SERVER_LIST_UPDATE_TIMEOUT); + + await mcpPage.openEditRegistriesTab(); + await editRegistriesTab.removeRegistry(MCP_REGISTRY_URL); + await editRegistriesTab.ensureRowDoesNotExist(MCP_REGISTRY_URL); + + await mcpPage.openInstallTab(); + await installTab.verifyServerCountIsRestored(initialServerCount); + }); + + // Test expected to fail until issue https://github.com/kortex-hub/kortex/issues/651 is fixed + test.fail( + '[MCP-02] Add and remove MCP server: verify server list updates accordingly', + async ({ mcpSetup: _mcpSetup, mcpPage }) => { + test.skip( + !process.env[MCP_SERVERS.github.envVarName], + `${MCP_SERVERS.github.envVarName} environment variable is not set`, + ); + + const serverName = MCP_SERVERS.github.serverName; + const mcpReadyTab = await mcpPage.openReadyTab(); + + await expect + .poll(async () => await mcpReadyTab.isServerConnected(serverName), { timeout: SERVER_CONNECTION_TIMEOUT }) + .toBeTruthy(); + }, + ); +}); diff --git a/tests/playwright/src/specs/registry-image.spec.ts b/tests/playwright/src/specs/registry-image.spec.ts deleted file mode 100644 index 1af9e3ed66..0000000000 --- a/tests/playwright/src/specs/registry-image.spec.ts +++ /dev/null @@ -1,102 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { RegistriesPage } from '../model/pages/registries-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { canTestRegistry, setupRegistry } from '../setupFiles/setup-registry'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteImage, deleteRegistry } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -let registryUrl: string; -let registryUsername: string; -let registryPswdSecret: string; -let imageName: string; -let imageTag: string; -let imageUrl: string; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('registry-image-e2e'); - - [registryUrl, registryUsername, registryPswdSecret] = setupRegistry(); - imageName = process.env.REGISTRY_IMAGE_NAME ?? 'alpine-hello'; - imageTag = process.env.REGISTRY_IMAGE_TAG ?? 'latest'; - imageUrl = registryUrl + '/' + registryUsername + '/' + imageName; - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner, page }) => { - try { - await deleteImage(page, imageUrl); - await deleteRegistry(page, 'GitHub'); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Pulling image from authenticated registry workflow verification', { tag: '@smoke' }, () => { - test('Cannot pull image from unauthenticated registry', async ({ page, navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible({ timeout: 10_000 }); - - const fullImageTitle = imageUrl.concat(':' + imageTag); - const errorAlert = page.getByLabel('Error Message Content'); - - const pullImagePage = await imagesPage.openPullImage(); - await playExpect(pullImagePage.heading).toBeVisible({ timeout: 10_000 }); - - await pullImagePage.imageNameInput.pressSequentially(fullImageTitle, { delay: 25 }); - await playExpect(pullImagePage.imageNameInput).toHaveValue(fullImageTitle); - await playExpect(pullImagePage.pullImageButton).toBeEnabled(); - await pullImagePage.pullImageButton.click(); - - await playExpect(errorAlert).toBeVisible({ timeout: 10_000 }); - await playExpect(errorAlert).toContainText('Error while pulling image from'); - await playExpect(errorAlert).toContainText(fullImageTitle); - await playExpect(errorAlert).toContainText('Can also be that the registry requires authentication'); - }); - - test.describe.serial(() => { - test.skip(!canTestRegistry(), 'Registry tests are disabled'); - - test('Add registry', async ({ page, navigationBar }) => { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - await playExpect(registryPage.heading).toBeVisible({ timeout: 10_000 }); - await registryPage.createRegistry(registryUrl, registryUsername, registryPswdSecret); - - const registryBox = registryPage.registriesTable.getByLabel('GitHub'); - const username = registryBox.getByText(registryUsername); - await playExpect(username).toBeVisible({ timeout: 15_000 }); - }); - - test('Image pulling from authenticated registry verification', async ({ navigationBar }) => { - const imagesPage = await navigationBar.openImages(); - - const fullImageTitle = imageUrl.concat(':' + imageTag); - const pullImagePage = await imagesPage.openPullImage(); - const updatedImages = await pullImagePage.pullImage(fullImageTitle); - - const exists = await updatedImages.waitForImageExists(imageUrl); - playExpect(exists, fullImageTitle + ' image not present in the list of images').toBeTruthy(); - }); - }); -}); diff --git a/tests/playwright/src/specs/registry.spec.ts b/tests/playwright/src/specs/registry.spec.ts deleted file mode 100644 index 104f735e50..0000000000 --- a/tests/playwright/src/specs/registry.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { RegistriesPage } from '../model/pages/registries-page'; -import { canTestRegistry, setupRegistry } from '../setupFiles/setup-registry'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -let registryUrl: string; -let registryUsername: string; -let registryPswdSecret: string; -let registryName: string; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('registry-e2e'); - - [registryUrl, registryUsername, registryPswdSecret] = setupRegistry(); - registryName = 'GitHub'; - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Registries handling verification', { tag: '@smoke' }, () => { - test('Check Registries page components and presence of default registries', async ({ navigationBar }) => { - const settingsBar = await navigationBar.openSettings(); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - - await playExpect(registryPage.heading).toBeVisible({ timeout: 10_000 }); - await playExpect(registryPage.addRegistryButton).toBeEnabled(); - await playExpect(registryPage.registriesTable).toBeVisible({ - timeout: 10_000, - }); - - const defaultRegistries = ['Docker Hub', 'Red Hat Quay', 'GitHub', 'Google Container Registry']; - for (const registryName of defaultRegistries) { - const registryBox = registryPage.registriesTable.getByLabel(registryName); - await playExpect(registryBox).toBeVisible({ timeout: 30_000 }); - } - }); - - test('Cannot add invalid registry', async ({ page, navigationBar }) => { - await navigationBar.openDashboard(); - const settingsBar = await navigationBar.openSettings(); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - - await registryPage.createRegistry('invalidUrl', 'invalidName', 'invalidPswd'); - const urlErrorMsg = page.getByText( - /Unable to find auth info for https:\/\/invalidUrl\/v2\/\. Error: RequestError: getaddrinfo [A-Z_]+ invalidurl$/, - ); - await playExpect(urlErrorMsg).toBeVisible({ timeout: 50000 }); - await playExpect(registryPage.cancelDialogButton).toBeEnabled(); - await registryPage.cancelDialogButton.click(); - - await registryPage.createRegistry(registryUrl, 'invalidName', 'invalidPswd'); - const credsErrorMsg = page.getByText('Wrong Username or Password.'); - await playExpect(credsErrorMsg).toBeVisible(); - await playExpect(registryPage.cancelDialogButton).toBeEnabled(); - await registryPage.cancelDialogButton.click(); - }); - - test.describe - .serial('Registry addition workflow verification', () => { - test.skip(!canTestRegistry(), 'Registry tests are disabled'); - - test('Valid registry addition verification', async ({ page }) => { - const registryPage = new RegistriesPage(page); - - await registryPage.createRegistry(registryUrl, registryUsername, registryPswdSecret); - - const registryBox = registryPage.registriesTable.getByLabel(registryName); - const username = registryBox.getByText(registryUsername); - await playExpect(username).toBeVisible({ timeout: 50000 }); - }); - - test('Registry editing availability and invalid credentials verification', async ({ page }) => { - const registryPage = new RegistriesPage(page); - - await registryPage.editRegistry(registryName, 'invalidName', 'invalidPswd'); - const errorMsg = page.getByText('Wrong Username or Password.'); - await playExpect(errorMsg).toBeVisible({ timeout: 30_000 }); - - const cancelButton = page.getByRole('button', { name: 'Cancel' }); - await cancelButton.click(); - }); - - test('Registry removal verification', async ({ page }) => { - const registryPage = new RegistriesPage(page); - - await registryPage.removeRegistry(registryName); - const registryBox = registryPage.registriesTable.getByLabel(registryName); - const username = registryBox.getByText(registryUsername); - await playExpect(username).toBeHidden(); - }); - }); -}); diff --git a/tests/playwright/src/specs/settings-smoke.spec.ts b/tests/playwright/src/specs/settings-smoke.spec.ts new file mode 100644 index 0000000000..fd6e7ba095 --- /dev/null +++ b/tests/playwright/src/specs/settings-smoke.spec.ts @@ -0,0 +1,103 @@ +/********************************************************************** + * 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 { + featuredResources, + preferenceOptions, + proxyConfigurations, + resourcesWithCreateButton, +} from 'src/model/core/types'; + +import { expect, test } from '../fixtures/electron-app'; +import { waitForNavigationReady } from '../utils/app-ready'; + +test.describe('Settings page navigation', { tag: '@smoke' }, () => { + test.beforeEach(async ({ page, navigationBar }) => { + await waitForNavigationReady(page); + await navigationBar.navigateToSettingsPage(); + }); + + test('[SET-01] All settings tabs are visible', async ({ settingsPage }) => { + const tabs = settingsPage.getAllTabs(); + const expectedTabCount = 4; // Resources, CLI, Proxy, Preferences + + expect(tabs).toHaveLength(expectedTabCount); + + for (const tab of tabs) { + await expect(tab).toBeVisible(); + } + }); + + test('[SET-02] Resources tab shows all providers with create buttons', async ({ settingsPage }) => { + const resourcesPage = await settingsPage.openResources(); + + for (const resourceId of featuredResources) { + await expect(resourcesPage.getResourceRegion(resourceId)).toBeVisible(); + } + + for (const displayName of resourcesWithCreateButton) { + const createButton = resourcesPage.getResourceCreateButton(displayName); + await expect(createButton).toBeVisible(); + await expect(createButton).toBeEnabled(); + } + }); + + test('[SET-03] CLI tab shows goose CLI tool', async ({ settingsPage }) => { + const cliPage = await settingsPage.openCli(); + + await expect(cliPage.toolName).toBeVisible(); + await expect(cliPage.toolName).toHaveText('goose'); + }); + + test('[SET-04] Proxy tab configurations and fields', async ({ settingsPage }) => { + const proxyPage = await settingsPage.openProxy(); + await proxyPage.verifyProxyConfigurationOptions(); + const proxyFields = proxyPage.getProxyFields(); + expect(proxyFields.length).toBeGreaterThan(0); + + for (const field of proxyFields) { + await expect(field).toBeVisible(); + } + + for (const config of proxyConfigurations) { + await proxyPage.selectProxyConfigurationAndVerifyFields(config.option, config.editable); + } + }); + + test('[SET-05] Preferences submenu items are visible and can be interacted with', async ({ settingsPage }) => { + const preferencesPage = await settingsPage.openPreferences(); + const options = preferenceOptions(); + expect(options.length).toBeGreaterThan(0); + + for (const option of options) { + await preferencesPage.selectPreference(option); + await expect(preferencesPage.getPreferenceContent(option)).toBeVisible(); + } + }); + + test('[SET-06] Preferences search filters options correctly', async ({ settingsPage }) => { + const preferencesPage = await settingsPage.openPreferences(); + const options = preferenceOptions(); + expect(options.length).toBeGreaterThan(0); + + for (const option of options) { + await preferencesPage.searchPreferences(option); + await expect(preferencesPage.getPreferenceContent(option)).toBeVisible(); + await preferencesPage.clearSearch(); + } + }); +}); diff --git a/tests/playwright/src/specs/troubleshooting-smoke.spec.ts b/tests/playwright/src/specs/troubleshooting-smoke.spec.ts deleted file mode 100644 index 355878556b..0000000000 --- a/tests/playwright/src/specs/troubleshooting-smoke.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -/********************************************************************** - * 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 { TroubleshootingPage } from '../model/pages/troubleshooting-page'; -import { StatusBar } from '../model/workbench/status-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; - -test.beforeAll(async ({ runner, welcomePage }) => { - runner.setVideoAndTraceName('troubleshooting-e2e'); - - await welcomePage.handleWelcomePage(true); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe.serial('Troubleshooting page verification', { tag: '@smoke' }, () => { - let troubleshootingPage: TroubleshootingPage; - - test('Troubleshooting page is available', async ({ page }) => { - const statusBar = new StatusBar(page); - await playExpect(statusBar.troubleshootingButton).toBeEnabled(); - await statusBar.troubleshootingButton.click(); - troubleshootingPage = new TroubleshootingPage(page); - await playExpect(troubleshootingPage.heading).toBeVisible(); - }); - - test('Can reconnect providers', async () => { - await troubleshootingPage.openRepairConnections(); - await playExpect - .poll(async () => troubleshootingPage.getContainerConnectionsStatus(), { timeout: 15_000 }) - .toMatch(/[1-9]\d* running/); - const status = await troubleshootingPage.reconnectProviders(); - playExpect(status).toContain('Done'); - }); - - test('Content of the application Log', async () => { - await troubleshootingPage.openLogs(); - const logs = await troubleshootingPage.getLogs(); - for (const logEntry in [ - /System ready. Loading extensions/, - /PluginSystem: received dom-ready event from the UI/, - /Delayed startup, flushing/, - /PluginSystem: initialization done/, - ]) { - await playExpect(logs).toContainText(logEntry, { timeout: 10_000 }); - } - }); - - test('Refresh auth providers store', async () => { - await troubleshootingPage.openStores(); - await troubleshootingPage.refreshStore('auth providers'); - }); -}); diff --git a/tests/playwright/src/specs/volume-smoke.spec.ts b/tests/playwright/src/specs/volume-smoke.spec.ts deleted file mode 100644 index 8788661af1..0000000000 --- a/tests/playwright/src/specs/volume-smoke.spec.ts +++ /dev/null @@ -1,286 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { ContainerState, VolumeState } from '../model/core/states'; -import type { ContainerInteractiveParams } from '../model/core/types'; -import { ContainerDetailsPage } from '../model/pages/container-details-page'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteContainer, deleteImage, readFileInVolumeFromCLI } from '../utility/operations'; -import { isCI, isWindows } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const imageToPull = 'quay.io/centos-bootc/bootc-image-builder'; -const imageTag = 'latest'; -const noVolumeImageToPull = 'ghcr.io/linuxcontainers/alpine'; -const containerName = 'alpine'; -const containerToRun = 'bootc-image-builder'; -const volumeName = 'e2eVolume'; -const containerVolumePath = '/tmp/mount'; -const fileName = 'test.txt'; -const textContent = 'This is a test file created in the volume.'; -const containerStartParams: ContainerInteractiveParams = { - attachTerminal: false, -}; - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('volume-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner, page }) => { - try { - await deleteContainer(page, containerName); - - await deleteImage(page, noVolumeImageToPull); - } finally { - await runner.close(); - } -}); - -test.describe.serial('Volume workflow verification', { tag: '@smoke' }, () => { - test('Create new Volume', async ({ navigationBar }) => { - let volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const createVolumePage = await volumesPage.openCreateVolumePage(volumeName); - volumesPage = await createVolumePage.createVolume(volumeName); - await playExpect - .poll(async () => await volumesPage.waitForVolumeExists(volumeName), { - timeout: 25_000, - }) - .toBeTruthy(); - }); - - test('Test navigation between pages', async ({ navigationBar }) => { - const volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const volumeRow = await volumesPage.getVolumeRowByName(volumeName); - playExpect(volumeRow).not.toBeUndefined(); - - const volumeDetails = await volumesPage.openVolumeDetails(volumeName); - await playExpect(volumeDetails.heading).toBeVisible(); - await volumeDetails.backLink.click(); - await playExpect(volumesPage.heading).toBeVisible(); - - await volumesPage.openVolumeDetails(volumeName); - await playExpect(volumeDetails.heading).toBeVisible(); - await volumeDetails.closeButton.click(); - await playExpect(volumesPage.heading).toBeVisible(); - }); - - test('Delete volume from Volumes page', async ({ navigationBar }) => { - let volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const volumeRow = await volumesPage.getVolumeRowByName(volumeName); - playExpect(volumeRow).not.toBeUndefined(); - volumesPage = await volumesPage.deleteVolume(volumeName); - await playExpect - .poll(async () => await volumesPage.waitForVolumeDelete(volumeName), { - timeout: 35_000, - }) - .toBeTruthy(); - }); - - test('Delete volume through details page', async ({ navigationBar }) => { - //re-create a new volume - let volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - - const createVolumePage = await volumesPage.openCreateVolumePage(volumeName); - volumesPage = await createVolumePage.createVolume(volumeName); - - await playExpect - .poll(async () => await volumesPage.waitForVolumeExists(volumeName), { - timeout: 35_000, - }) - .toBeTruthy(); - - //delete it from the details page - volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const volumeRow = await volumesPage.getVolumeRowByName(volumeName); - playExpect(volumeRow).not.toBeUndefined(); - - const volumeDetails = await volumesPage.openVolumeDetails(volumeName); - volumesPage = await volumeDetails.deleteVolume(); - - await playExpect - .poll(async () => await volumesPage.waitForVolumeDelete(volumeName), { - timeout: 35_000, - }) - .toBeTruthy(); - }); - - test('Create volumes from bootc-image-builder', async ({ navigationBar }) => { - test.setTimeout(210_000); - - //count the number of existing volumes - let volumesPage = await navigationBar.openVolumes(); - let previousVolumes = await volumesPage.countVolumesFromTable(); - - //if there are volumes, check how many are used - if (previousVolumes > 0) { - const usedVolumes = await volumesPage.countUsedVolumesFromTable(); - //if there are unused volumes, prune them - if (previousVolumes - usedVolumes > 0) { - volumesPage = await volumesPage.pruneVolumes(); - await playExpect - .poll(async () => (await volumesPage.getRowsFromTableByStatus(VolumeState.Unused)).length, { - timeout: 10_000, - }) - .toBe(0); - previousVolumes = await volumesPage.countVolumesFromTable(); - } - } - - //pull image from quay.io/centos-bootc/bootc-image-builder - let images = await navigationBar.openImages(); - const pullImagePage = await images.openPullImage(); - images = await pullImagePage.pullImage(imageToPull, imageTag, 120_000); - await playExpect - .poll(async () => await images.waitForImageExists(imageToPull, 30_000), { timeout: 0 }) - .toBeTruthy(); - - //start a container from the image (generates 4 new volumes) - const imageDetails = await images.openImageDetails(imageToPull); - const runImage = await imageDetails.openRunImage(); - let containers = await runImage.startContainer(containerToRun, containerStartParams); - await playExpect(containers.header).toBeVisible({ timeout: 60_000 }); - await playExpect - .poll(async () => await containers.containerExists(containerToRun), { - timeout: 60_000, - }) - .toBeTruthy(); - await containers.startContainer(containerToRun); - - //check that four volumes are created (in addition to the existing ones) - volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const newVolumes = await volumesPage.countVolumesFromTable(); - playExpect(newVolumes - previousVolumes).toBe(4); - - //check the container is stopped and delete it - containers = await navigationBar.openContainers(); - await playExpect(containers.heading).toBeVisible(); - - const containerDetails = await containers.openContainersDetails(containerToRun); - await playExpect(containerDetails.heading).toBeVisible({ timeout: 10_000 }); - await playExpect - .poll(async () => containerDetails.getState(), { timeout: 30_000 }) - .toContain(ContainerState.Exited); - - containers = await navigationBar.openContainers(); - await playExpect(containers.heading).toBeVisible(); - - const containersPage = await containers.deleteContainer(containerToRun); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPage.containerExists(containerToRun), { - timeout: 60_000, - intervals: [1_000], - }) - .toBeFalsy(); - - //prune unused volumes - volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible({ timeout: 10_000 }); - await playExpect - .poll(async () => (await volumesPage.getRowsFromTableByStatus(VolumeState.Unused)).length, { timeout: 30_000 }) - .toBeGreaterThanOrEqual(1); - - volumesPage = await volumesPage.pruneVolumes(); - await playExpect - .poll(async () => volumesPage.getRowsFromTableByStatus(VolumeState.Unused), { timeout: 30_000 }) - .toHaveLength(0); - const finalVolumes = await volumesPage.countVolumesFromTable(); - playExpect(finalVolumes - previousVolumes).toBe(0); - }); - - test('Create volume on the system mapped into container', async ({ navigationBar, page }) => { - test.skip(!!isCI && isWindows, 'Skipped on Windows due to file system issues'); - //create a new volume - let volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const createVolumePage = await volumesPage.openCreateVolumePage(volumeName); - volumesPage = await createVolumePage.createVolume(volumeName); - await playExpect - .poll(async () => await volumesPage.waitForVolumeExists(volumeName), { - timeout: 25_000, - }) - .toBeTruthy(); - - //pull image from quay.io/podman-desktop-demo/podify-demo-backend - let images = await navigationBar.openImages(); - const pullImagePage = await images.openPullImage(); - images = await pullImagePage.pullImage(noVolumeImageToPull, imageTag, 120_000); - await playExpect - .poll(async () => await images.waitForImageExists(noVolumeImageToPull, 30_000), { timeout: 0 }) - .toBeTruthy(); - - //start a container from the image and map the volume into it - const imageDetails = await images.openImageDetails(noVolumeImageToPull); - const runImage = await imageDetails.openRunImage(); - const containerStartParams: ContainerInteractiveParams = { - attachTerminal: true, - attachVolumeName: volumeName, - attachVolumePath: containerVolumePath, - }; - await runImage.startContainer(containerName, containerStartParams); - const containerDetailsPage = new ContainerDetailsPage(page, containerName); - await playExpect(containerDetailsPage.heading).toBeVisible({ timeout: 60_000 }); - - //access the container's terminal and create a file inside the volume's path to confirm that it is mounted and has write permissions - const containers = await navigationBar.openContainers(); - const containersDetails = await containers.openContainersDetails(containerName); - await playExpect(containersDetails.heading).toContainText(containerName); - await playExpect.poll(async () => containersDetails.getState()).toContain(ContainerState.Running); - await containersDetails.activateTab('Terminal'); - await playExpect(containersDetails.terminalInput).toBeVisible(); - await containersDetails.executeCommandInTerminal(`cd ${containerVolumePath}`); - await containersDetails.executeCommandInTerminal('pwd'); - await playExpect(containersDetails.terminalContent).toContainText(containerVolumePath); - await containersDetails.executeCommandInTerminal(`echo ${textContent} > ${fileName}`); - await containersDetails.executeCommandInTerminal('ls'); - await playExpect(containersDetails.terminalContent).toContainText(fileName); - - //read the file from the volume using CLI - const fileContent = await readFileInVolumeFromCLI(volumeName, fileName); - console.log(`Successfully read file. Content: "${fileContent}"`); - playExpect(fileContent).toContain(textContent); // Check if the file is not empty - console.log(`File "${fileName}" exists in volume "${volumeName}"`); - - //delete the volume - await containersDetails.deleteContainer(); - const containersPageAfterDelete = await navigationBar.openContainers(); - await playExpect(containersPageAfterDelete.heading).toBeVisible(); - await playExpect - .poll(async () => await containersPageAfterDelete.containerExists(containerName), { timeout: 60_000 }) - .toBeFalsy(); - volumesPage = await navigationBar.openVolumes(); - await playExpect(volumesPage.heading).toBeVisible(); - const volumeRow = await volumesPage.getVolumeRowByName(volumeName); - playExpect(volumeRow).not.toBeUndefined(); - volumesPage = await volumesPage.deleteVolume(volumeName); - await playExpect - .poll(async () => await volumesPage.waitForVolumeDelete(volumeName), { - timeout: 35_000, - }) - .toBeTruthy(); - }); -}); diff --git a/tests/playwright/src/specs/welcome-page-smoke.spec.ts b/tests/playwright/src/specs/welcome-page-smoke.spec.ts deleted file mode 100644 index a357197396..0000000000 --- a/tests/playwright/src/specs/welcome-page-smoke.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { RunnerOptions } from '../runner/runner-options'; -import { expect as playExpect, test } from '../utility/fixtures'; - -test.use({ runnerOptions: new RunnerOptions({ customFolder: 'welcome-podman-desktop' }) }); -test.beforeAll(async ({ runner }) => { - runner.setVideoAndTraceName('welcome-page-e2e'); -}); - -test.afterAll(async ({ runner }) => { - await runner.close(); -}); - -test.describe('Basic e2e verification of podman desktop start', { tag: '@smoke' }, () => { - test.describe('Welcome page handling', () => { - test('Check the Welcome page is displayed', async ({ welcomePage }) => { - await playExpect(welcomePage.welcomeMessage).toBeVisible(); - }); - - test('Telemetry checkbox is present, set to true, consent can be changed', async ({ welcomePage }) => { - await playExpect(welcomePage.telemetryConsent).toBeVisible(); - await playExpect(welcomePage.telemetryConsent).toBeChecked(); - await welcomePage.turnOffTelemetry(); - }); - - test('Redirection from Welcome page to Dashboard works', async ({ welcomePage }) => { - const dashboardPage = await welcomePage.closeWelcomePage(); - await playExpect(dashboardPage.heading).toBeVisible(); - }); - }); - - test.describe('Navigation Bar test', () => { - test('Verify navigation items are visible', async ({ navigationBar }) => { - await playExpect(navigationBar.navigationLocator).toBeVisible(); - await playExpect(navigationBar.dashboardLink).toBeVisible(); - await playExpect(navigationBar.imagesLink).toBeVisible(); - await playExpect(navigationBar.podsLink).toBeVisible(); - await playExpect(navigationBar.containersLink).toBeVisible(); - await playExpect(navigationBar.volumesLink).toBeVisible(); - await playExpect(navigationBar.settingsLink).toBeVisible(); - }); - }); -}); diff --git a/tests/playwright/src/specs/yaml-smoke.spec.ts b/tests/playwright/src/specs/yaml-smoke.spec.ts deleted file mode 100644 index 447edcd6f7..0000000000 --- a/tests/playwright/src/specs/yaml-smoke.spec.ts +++ /dev/null @@ -1,113 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -import { expect as playExpect, test } from '../utility/fixtures'; -import { deleteImage, deletePod } from '../utility/operations'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const podAppName = 'primary-podify-demo'; -const podName = 'podify-demo-pod'; -const frontendImage = 'quay.io/podman-desktop-demo/podify-demo-frontend'; -const backendImage = 'quay.io/podman-desktop-demo/podify-demo-backend'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('play-yaml-e2e'); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); -}); - -test.afterAll(async ({ runner, page }) => { - try { - await deletePod(page, podName); - await deleteImage(page, backendImage); - await deleteImage(page, frontendImage); - } finally { - await runner.close(); - } -}); - -test.describe.serial(`Play yaml file to pull images and create pod for app ${podAppName}`, { tag: '@smoke' }, () => { - test.describe.configure({ timeout: 150_000 }); - - test('Playing yaml', async ({ navigationBar }) => { - let podsPage = await navigationBar.openPods(); - await playExpect(podsPage.heading).toBeVisible(); - - const playYamlPage = await podsPage.openPlayKubeYaml(); - await playExpect(playYamlPage.heading).toBeVisible(); - - const yamlFilePath = path.resolve(__dirname, '..', '..', 'resources', `${podAppName}.yaml`); - podsPage = await playYamlPage.playYaml(yamlFilePath); - await playExpect(podsPage.heading).toBeVisible(); - }); - - test('Checking that created pod from yaml is correct', async ({ page, navigationBar }) => { - test.setTimeout(120_000); - const podsPage = await navigationBar.openPods(); - await playExpect(podsPage.heading).toBeVisible(); - - await playExpect.poll(async () => await podsPage.podExists(podName), { timeout: 60_000 }).toBeTruthy(); - await deletePod(page, podName); - await playExpect.poll(async () => await podsPage.podExists(podName), { timeout: 60_000 }).toBeFalsy(); - }); - - test('Checking that pulled images from yaml are correct', async ({ navigationBar }) => { - test.setTimeout(120_000); - - let imagesPage = await navigationBar.openImages(); - await playExpect(imagesPage.heading).toBeVisible(); - - await test.step('Checking that images are pulled', async () => { - await playExpect.poll(async () => await imagesPage.waitForImageExists(backendImage)).toBeTruthy(); - await playExpect.poll(async () => await imagesPage.waitForImageExists(frontendImage)).toBeTruthy(); - await playExpect - .poll(async () => await imagesPage.getCurrentStatusOfImage(backendImage), { timeout: 15_000 }) - .toBe('UNUSED'); - await playExpect - .poll(async () => await imagesPage.getCurrentStatusOfImage(frontendImage), { timeout: 15_000 }) - .toBe('UNUSED'); - }); - - await test.step(`Deleting image ${backendImage}`, async () => { - const imageDetailsPage = await imagesPage.openImageDetails(backendImage); - await playExpect(imageDetailsPage.heading).toContainText(backendImage); - imagesPage = await imageDetailsPage.deleteImage(); - await playExpect(imagesPage.heading).toBeVisible({ timeout: 30_000 }); - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(backendImage), { timeout: 10_000 }) - .toBeTruthy(); - }); - - await test.step(`Deleting image ${frontendImage}`, async () => { - const imageDetailsPage = await imagesPage.openImageDetails(frontendImage); - await playExpect(imageDetailsPage.heading).toContainText(frontendImage); - imagesPage = await imageDetailsPage.deleteImage(); - await playExpect(imagesPage.heading).toBeVisible({ timeout: 30_000 }); - await playExpect - .poll(async () => await imagesPage.waitForImageDelete(frontendImage), { timeout: 10_000 }) - .toBeTruthy(); - }); - }); -}); diff --git a/tests/playwright/src/specs/z-podman-machine-dashboard-onboarding.spec.ts b/tests/playwright/src/specs/z-podman-machine-dashboard-onboarding.spec.ts deleted file mode 100644 index e74bca8a52..0000000000 --- a/tests/playwright/src/specs/z-podman-machine-dashboard-onboarding.spec.ts +++ /dev/null @@ -1,71 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { expect as playExpect, test } from '../utility/fixtures'; -import { createPodmanMachineFromCLI, deletePodmanMachine } from '../utility/operations'; -import { isLinux } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const PODMAN_MACHINE_NAME: string = 'podman-machine-default'; - -test.skip( - isLinux || process.env.TEST_PODMAN_MACHINE !== 'true', - 'Tests suite should not run on Linux platform or if TEST_PODMAN_MACHINE is not true', -); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - test.setTimeout(120_000); - runner.setVideoAndTraceName('podman-machine-dashboard'); - await welcomePage.handleWelcomePage(true); - - if ( - (process.env.TEST_PODMAN_MACHINE !== undefined && process.env.TEST_PODMAN_MACHINE === 'true') || - (process.env.MACHINE_CLEANUP !== undefined && process.env.MACHINE_CLEANUP === 'true') - ) { - await waitForPodmanMachineStartup(page); - await deletePodmanMachine(page, PODMAN_MACHINE_NAME); - } -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120_000); - - if (test.info().status === 'failed') { - await createPodmanMachineFromCLI(); - } - - await runner.close(); -}); - -test.describe - .serial(`Podman machine onboarding from Dashboard`, () => { - test('Create Podman machine from Dashboard', async ({ navigationBar }) => { - test.setTimeout(320000); - - console.log('Starting PD dashboard test'); - const dashboardPage = await navigationBar.openDashboard(); - await playExpect(dashboardPage.podmanInitilizeAndStartButton).toBeEnabled({ timeout: 60000 }); - await dashboardPage.podmanInitilizeAndStartButton.click(); - await playExpect(dashboardPage.podmanStatusLabel).toHaveText('RUNNING', { timeout: 300000 }); - }); - - test('Clean Up Podman Machine', async ({ page }) => { - test.skip(process.env.MACHINE_CLEANUP !== 'true', 'Machine cleanup is disabled'); - await deletePodmanMachine(page, PODMAN_MACHINE_NAME); - }); - }); diff --git a/tests/playwright/src/specs/z-podman-machine-onboarding.spec.ts b/tests/playwright/src/specs/z-podman-machine-onboarding.spec.ts deleted file mode 100644 index 23e3a6d00d..0000000000 --- a/tests/playwright/src/specs/z-podman-machine-onboarding.spec.ts +++ /dev/null @@ -1,235 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 os from 'node:os'; - -import type { Locator, Page } from '@playwright/test'; - -import type { DashboardPage } from '../model/pages/dashboard-page'; -import { PodmanMachineDetails } from '../model/pages/podman-machine-details-page'; -import { PodmanOnboardingPage } from '../model/pages/podman-onboarding-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import type { SettingsBar } from '../model/pages/settings-bar'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { createPodmanMachineFromCLI, deletePodmanMachine } from '../utility/operations'; -import { isLinux } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const PODMAN_MACHINE_STARTUP_TIMEOUT: number = 360_000; -const PODMAN_FULL_STARTUP_TIMEOUT = PODMAN_MACHINE_STARTUP_TIMEOUT + 30000; -const PODMAN_MACHINE_NAME: string = 'podman-machine-default'; -const RESOURCE_NAME: string = 'podman'; - -let dashboardPage: DashboardPage; -let resourcesPage: ResourcesPage; -let settingsBar: SettingsBar; -let podmanOnboardingPage: PodmanOnboardingPage; - -let notificationPodmanSetup: Locator; - -test.skip( - isLinux || process.env.TEST_PODMAN_MACHINE !== 'true', - 'Tests suite should not run on Linux platform or if TEST_PODMAN_MACHINE is not true', -); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - test.setTimeout(120_000); - runner.setVideoAndTraceName('podman-machine-e2e'); - - await welcomePage.handleWelcomePage(true); - - // Delete machine if it already exists - if ( - (process.env.TEST_PODMAN_MACHINE !== undefined && process.env.TEST_PODMAN_MACHINE === 'true') || - (process.env.MACHINE_CLEANUP !== undefined && process.env.MACHINE_CLEANUP === 'true') - ) { - await waitForPodmanMachineStartup(page); - await deletePodmanMachine(page, PODMAN_MACHINE_NAME); - } -}); - -test.afterAll(async ({ runner }) => { - test.setTimeout(120_000); - - if (test.info().status === 'failed') { - await createPodmanMachineFromCLI(); - } - - await runner.close(); -}); - -test.describe - .serial('Podman Machine verification', () => { - test.describe - .serial('Podman Machine onboarding workflow', () => { - test('Setup Podman push notification is present', async ({ navigationBar }) => { - dashboardPage = await navigationBar.openDashboard(); - await playExpect(dashboardPage.mainPage).toBeVisible(); - await playExpect(dashboardPage.notificationsBox).toBeVisible(); - notificationPodmanSetup = dashboardPage.notificationsBox - .getByRole('region', { name: 'id:' }) - .filter({ hasText: 'Podman needs to be set up' }); - await playExpect(notificationPodmanSetup).toBeVisible(); - }); - - test.describe - .serial('Onboarding navigation', () => { - test('Open Podman Machine Onboarding through Setup Notification', async ({ page }) => { - await notificationPodmanSetup.getByTitle('Set up Podman').click(); - podmanOnboardingPage = await checkPodmanMachineOnboardingPage(page); - }); - - test('Return to Dashboard', async ({ navigationBar }) => { - dashboardPage = await navigationBar.openDashboard(); - await playExpect(dashboardPage.mainPage).toBeVisible(); - }); - - test('Re-Open Podman Machine Onboarding through Settings Resources page', async ({ - page, - navigationBar, - }) => { - settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - resourcesPage = new ResourcesPage(page); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const podmanResourceCard = new ResourceConnectionCardPage(page, RESOURCE_NAME); - await podmanResourceCard.setupButton.click(); - podmanOnboardingPage = await checkPodmanMachineOnboardingPage(page); - }); - }); - test('Verify Podman Autostart is enabled and proceed to next page', async () => { - await playExpect(podmanOnboardingPage.podmanAutostartToggle).toBeChecked({ timeout: 30_000 }); - await podmanOnboardingPage.nextStepButton.click(); - }); - - test('Expect no machine created message and proceed to next page', async () => { - await playExpect(podmanOnboardingPage.onboardingStatusMessage).toHaveText( - `We could not find any Podman machine. Let's create one!`, - { timeout: 30_000 }, - ); - await podmanOnboardingPage.nextStepButton.click(); - }); - - test('Verify default podman machine settings', async () => { - await playExpect(podmanOnboardingPage.createMachinePageTitle).toHaveText(`Create a Podman machine`, { - timeout: 30_000, - }); - await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineConfiguration).toBeVisible(); - await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineName).toHaveValue( - 'podman-machine-default', - ); - await playExpect(podmanOnboardingPage.machineCreationForm.imagePathBox).toHaveValue(''); - await playExpect(podmanOnboardingPage.machineCreationForm.rootPriviledgesCheckbox).toBeChecked(); - await playExpect(podmanOnboardingPage.machineCreationForm.startNowCheckbox).toBeChecked(); - - if (os.platform() === 'win32') { - await playExpect(podmanOnboardingPage.machineCreationForm.userModeNetworkingCheckbox).not.toBeChecked({ - timeout: 30_000, - }); - } else { - await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineCPUs).toBeVisible({ - timeout: 30_000, - }); - await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineMemory).toBeVisible(); - await playExpect(podmanOnboardingPage.machineCreationForm.podmanMachineDiskSize).toBeVisible(); - } - }); - }); - test.describe - .serial('Podman Machine creation and operations', () => { - test.skip(process.env.TEST_PODMAN_MACHINE !== 'true'); - - test('Create a default Podman machine', async () => { - test.setTimeout(PODMAN_FULL_STARTUP_TIMEOUT); - await podmanOnboardingPage.machineCreationForm.createMachineButton.click(); - await playExpect(podmanOnboardingPage.podmanMachineShowLogsButton).toBeVisible(); - await podmanOnboardingPage.podmanMachineShowLogsButton.click(); - await playExpect(podmanOnboardingPage.onboardingStatusMessage).toBeVisible({ - timeout: PODMAN_MACHINE_STARTUP_TIMEOUT, - }); - await playExpect(podmanOnboardingPage.onboardingStatusMessage).toHaveText('Podman installed'); - await podmanOnboardingPage.nextStepButton.click(); - }); - - test.describe - .serial('Podman machine operations', () => { - test.describe.configure({ timeout: 120000 }); - - test('Open podman machine details', async ({ page, navigationBar }) => { - dashboardPage = await navigationBar.openDashboard(); - await playExpect(dashboardPage.mainPage).toBeVisible(); - settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - resourcesPage = new ResourcesPage(page); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const resourcesPodmanConnections = new ResourceConnectionCardPage( - page, - RESOURCE_NAME, - PODMAN_MACHINE_NAME, - ); - await playExpect(resourcesPodmanConnections.providerConnections).toBeVisible({ timeout: 10_000 }); - await playExpect(resourcesPodmanConnections.resourceElement).toBeVisible({ timeout: 20_000 }); - await playExpect(resourcesPodmanConnections.resourceElementDetailsButton).toBeVisible(); - await resourcesPodmanConnections.resourceElementDetailsButton.click(); - const podmanMachineDetails = new PodmanMachineDetails(page, PODMAN_MACHINE_NAME); - await playExpect(podmanMachineDetails.podmanMachineStatus).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineConnectionActions).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineRestartButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineDeleteButton).toBeVisible(); - }); - - test('Podman machine operations - STOP', async ({ page }) => { - const podmanMachineDetails = new PodmanMachineDetails(page, PODMAN_MACHINE_NAME); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 60_000 }); - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStopButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 60_000 }); - }); - - test('Podman machine operations - START', async ({ page }) => { - const podmanMachineDetails = new PodmanMachineDetails(page, PODMAN_MACHINE_NAME); - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStartButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - }); - - test('Podman machine operations - RESTART', async ({ page }) => { - const podmanMachineDetails = new PodmanMachineDetails(page, PODMAN_MACHINE_NAME); - await playExpect(podmanMachineDetails.podmanMachineRestartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineRestartButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 60_000 }); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - }); - }); - }); - - test('Clean Up Podman Machine', async ({ page }) => { - test.skip(process.env.MACHINE_CLEANUP !== 'true', 'Machine cleanup is disabled'); - await deletePodmanMachine(page, 'Podman Machine'); - }); - }); - -async function checkPodmanMachineOnboardingPage(page: Page): Promise { - const onboardingPage = new PodmanOnboardingPage(page); - await playExpect(onboardingPage.header).toBeVisible(); - await playExpect(onboardingPage.mainPage).toBeVisible(); - return onboardingPage; -} diff --git a/tests/playwright/src/specs/z-podman-machine-resources.spec.ts b/tests/playwright/src/specs/z-podman-machine-resources.spec.ts deleted file mode 100644 index c54b1550b0..0000000000 --- a/tests/playwright/src/specs/z-podman-machine-resources.spec.ts +++ /dev/null @@ -1,212 +0,0 @@ -/********************************************************************** - * 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 { Locator } from '@playwright/test'; - -import { ResourceElementActions } from '../model/core/operations'; -import { ResourceElementState } from '../model/core/states'; -import { CreateMachinePage } from '../model/pages/create-machine-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { - createPodmanMachineFromCLI, - deletePodmanMachine, - deletePodmanMachineFromCLI, - handleConfirmationDialog, - resetPodmanMachinesFromCLI, -} from '../utility/operations'; -import { isLinux, isWindows } from '../utility/platform'; -import { waitForPodmanMachineStartup, waitUntil } from '../utility/wait'; - -const DEFAULT_PODMAN_MACHINE_NAME = 'podman-machine-default'; -const RESOURCE_NAME = 'podman'; -let dialog: Locator; - -const machineTypes = [ - { - PODMAN_MACHINE_NAME: 'podman-machine-rootless', - MACHINE_VISIBLE_NAME: 'Podman Machine rootless', - isRoot: false, - userNet: false, - }, - { - PODMAN_MACHINE_NAME: 'podman-machine-rootful', - MACHINE_VISIBLE_NAME: 'Podman Machine rootful', - isRoot: true, - userNet: false, - }, - { - PODMAN_MACHINE_NAME: 'podman-machine-user-networking', - MACHINE_VISIBLE_NAME: 'Podman Machine user-networking', - isRoot: true, - userNet: true, - }, -]; - -test.beforeAll(async ({ runner, welcomePage, page, navigationBar }) => { - runner.setVideoAndTraceName(`podman-machine-resources-e2e`); - - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - dialog = page.getByRole('dialog', { name: 'Podman', exact: true }); - - // Check for default machine and stop - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - - const resourcesPage = new ResourcesPage(page); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const defaultMachineCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, DEFAULT_PODMAN_MACHINE_NAME); - - playExpect(await defaultMachineCard.resourceElementConnectionStatus.innerText()).toContain( - ResourceElementState.Running, - ); - await defaultMachineCard.performConnectionAction(ResourceElementActions.Stop); - await waitUntil( - async () => - (await defaultMachineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Off), - { timeout: 30_000, sendError: true }, - ); -}); - -test.afterAll(async ({ runner, page, navigationBar }) => { - test.setTimeout(120_000); - - try { - if (test.info().status === 'failed') { - await resetPodmanMachinesFromCLI(); - await createPodmanMachineFromCLI(); - } - - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - - const resourcesPage = new ResourcesPage(page); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const defaultMachineCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, DEFAULT_PODMAN_MACHINE_NAME); - - try { - playExpect(await defaultMachineCard.resourceElementConnectionStatus.innerText()).toContain( - ResourceElementState.Off, - ); - await defaultMachineCard.performConnectionAction(ResourceElementActions.Start); - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - } catch (error) { - console.log('No handling dialog displayed', error); - } - - await waitUntil( - async () => - (await defaultMachineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Running), - { timeout: 60_000, sendError: true }, - ); - } finally { - await runner.close(); - } -}); - -for (const { PODMAN_MACHINE_NAME, MACHINE_VISIBLE_NAME, isRoot, userNet } of machineTypes) { - test.afterAll(async () => { - test.setTimeout(60_000); - if (test.info().status === 'failed') { - await deletePodmanMachineFromCLI(PODMAN_MACHINE_NAME); - } - }); - - test.skip( - isLinux || process.env.TEST_PODMAN_MACHINE !== 'true', - 'Tests suite should not run on Linux platform or if TEST_PODMAN_MACHINE is not true', - ); - - test.skip( - PODMAN_MACHINE_NAME === 'podman-machine-user-networking' && !isWindows, - 'Testing user networking machine only on Windows', - ); - - test.describe - .serial(`${MACHINE_VISIBLE_NAME} Resources workflow Verification`, () => { - test('Create machine through Resources page', async ({ page, navigationBar }) => { - test.setTimeout(200_000); - - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - - const podmanResources = new ResourceConnectionCardPage(page, RESOURCE_NAME); - await podmanResources.createButton.click(); - - const createMachinePage = new CreateMachinePage(page); - const resourcePage = await createMachinePage.createMachine(PODMAN_MACHINE_NAME, { - isRootful: isRoot, - enableUserNet: userNet, - setAsDefault: false, - startNow: false, - }); - - await playExpect(resourcePage.heading).toBeVisible(); - const machineCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, PODMAN_MACHINE_NAME); - playExpect(await machineCard.doesResourceElementExist()).toBeTruthy(); - playExpect(await machineCard.resourceElementConnectionStatus.innerText()).toContain(ResourceElementState.Off); - }); - - test('Start the machine', async ({ page }) => { - const machineCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, PODMAN_MACHINE_NAME); - await machineCard.performConnectionAction(ResourceElementActions.Start); - - await playExpect(dialog).toBeVisible({ timeout: 60_000 }); - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - - await waitUntil( - async () => - (await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Running), - { timeout: 30_000, sendError: true }, - ); - }); - - test('Restart the machine', async ({ page }) => { - const machineCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, PODMAN_MACHINE_NAME); - await machineCard.performConnectionAction(ResourceElementActions.Restart); - - await waitUntil( - async () => - (await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Off), - { timeout: 30_000, sendError: true }, - ); - - await waitUntil( - async () => - (await machineCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Running), - { timeout: 30_000, sendError: true }, - ); - }); - - test('Stop and delete the machine', async ({ page }) => { - test.setTimeout(150_000); - await deletePodmanMachine(page, PODMAN_MACHINE_NAME); - - try { - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - } catch (error) { - console.log('No handling dialog displayed', error); - } - }); - }); -} diff --git a/tests/playwright/src/specs/z-podman-machine-tests.spec.ts b/tests/playwright/src/specs/z-podman-machine-tests.spec.ts deleted file mode 100644 index 593eab4d1d..0000000000 --- a/tests/playwright/src/specs/z-podman-machine-tests.spec.ts +++ /dev/null @@ -1,283 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024-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 { Locator } from '@playwright/test'; - -import { PodmanMachineDetails } from '../model/pages/podman-machine-details-page'; -import { PodmanOnboardingPage } from '../model/pages/podman-onboarding-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { expect as playExpect, test } from '../utility/fixtures'; -import { - createPodmanMachineFromCLI, - deletePodmanMachine, - handleConfirmationDialog, - resetPodmanMachinesFromCLI, -} from '../utility/operations'; -import { isLinux } from '../utility/platform'; -import { waitForPodmanMachineStartup } from '../utility/wait'; - -const DEFAULT_PODMAN_MACHINE = 'Podman Machine'; -const DEFAULT_PODMAN_MACHINE_VISIBLE = 'podman-machine-default'; -const ROOTLESS_PODMAN_MACHINE_VISIBLE = 'podman-machine-rootless'; -const ROOTLESS_PODMAN_MACHINE = 'Podman Machine rootless'; -const RESOURCE_NAME = 'podman'; -let dialog: Locator; - -test.skip( - isLinux || process.env.TEST_PODMAN_MACHINE !== 'true', - 'Tests suite should not run on Linux platform or if TEST_PODMAN_MACHINE is not true', -); - -test.beforeAll(async ({ runner, welcomePage, page }) => { - runner.setVideoAndTraceName('podman-machine-tests'); - await welcomePage.handleWelcomePage(true); - await waitForPodmanMachineStartup(page); - dialog = page.getByRole('dialog', { name: 'Podman', exact: true }); -}); - -test.afterAll(async ({ runner, page }) => { - test.setTimeout(120_000); - - try { - if (test.info().status === 'failed') { - await resetPodmanMachinesFromCLI(); - await createPodmanMachineFromCLI(); - } - - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - } catch (error) { - console.log('No handling dialog displayed', error); - } - - await runner.close(); -}); - -test.describe - .serial(`Podman machine switching validation `, () => { - test.describe.configure({ timeout: 120_000 }); - - test('Check data for available Podman Machine and stop machine', async ({ page, navigationBar }) => { - await test.step('Open resources page', async () => { - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - }); - - await test.step('Check default podman machine', async () => { - const resourcesPage = new ResourcesPage(page); - await playExpect(resourcesPage.heading).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const resourcesPodmanConnections = new ResourceConnectionCardPage( - page, - RESOURCE_NAME, - DEFAULT_PODMAN_MACHINE_VISIBLE, - ); - await playExpect(resourcesPodmanConnections.providerConnections).toBeVisible({ timeout: 10_000 }); - await playExpect(resourcesPodmanConnections.resourceElement).toBeVisible({ timeout: 20_000 }); - await playExpect(resourcesPodmanConnections.resourceElementDetailsButton).toBeVisible(); - await resourcesPodmanConnections.resourceElementDetailsButton.click(); - }); - - await test.step('Check default podman machine details', async () => { - const podmanMachineDetails = new PodmanMachineDetails(page, DEFAULT_PODMAN_MACHINE); - await test.step('Ensure default podman machine is RUNNING', async () => { - await playExpect(podmanMachineDetails.podmanMachineStatus).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineConnectionActions).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineRestartButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineDeleteButton).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 60_000 }); - }); - - await test.step('Check terminal tab for podman machine', async () => { - await playExpect(podmanMachineDetails.terminalTab).toBeVisible(); - await podmanMachineDetails.terminalTab.click(); - await playExpect(podmanMachineDetails.terminalContent).toBeVisible(); - await playExpect(podmanMachineDetails.terminalContent).toContainText('@'); - await podmanMachineDetails.terminalInput.pressSequentially('pwd', { delay: 15 }); - await podmanMachineDetails.terminalInput.press('Enter'); - await playExpect(podmanMachineDetails.terminalContent).toContainText('/home/'); - }); - - await test.step('Stop default podman machine', async () => { - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStopButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 60_000 }); - await playExpect(podmanMachineDetails.logsTab).toBeEnabled(); - await podmanMachineDetails.logsTab.click(); - await playExpect( - podmanMachineDetails.tabContent.getByText('Machine "podman-machine-default" stopped successfully'), - ).toBeVisible({ timeout: 10_000 }); - }); - }); - }); - - test('Create rootless podman machine', async ({ page, navigationBar }) => { - test.setTimeout(200_000); - - await test.step('Open resources page', async () => { - const dashboard = await navigationBar.openDashboard(); - await playExpect(dashboard.heading).toBeVisible(); - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - }); - - const resourcesPage = new ResourcesPage(page); - await test.step('Go to create new podman machine page', async () => { - await playExpect(resourcesPage.heading).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - await resourcesPage.goToCreateNewResourcePage(RESOURCE_NAME); - }); - - const podmanMachineCreatePage = new PodmanOnboardingPage(page); - - await test.step('Create podman machine', async () => { - await podmanMachineCreatePage.machineCreationForm.setupAndCreateMachine(ROOTLESS_PODMAN_MACHINE_VISIBLE, { - isRootful: false, - enableUserNet: true, - startNow: false, - }); - await playExpect(podmanMachineCreatePage.goBackButton).toBeEnabled({ timeout: 180_000 }); - await podmanMachineCreatePage.goBackButton.click(); - }); - - await playExpect(resourcesPage.heading).toBeVisible(); - }); - - test('Switch to rootless podman machine', async ({ page }) => { - await test.step('Go to rootless podman machine details page', async () => { - const resourcesPodmanConnections = new ResourceConnectionCardPage( - page, - RESOURCE_NAME, - ROOTLESS_PODMAN_MACHINE_VISIBLE, - ); - - await playExpect(resourcesPodmanConnections.resourceElementDetailsButton).toBeVisible({ timeout: 30_000 }); - await resourcesPodmanConnections.resourceElementDetailsButton.click(); - }); - - await test.step('Check rootless podman machine details', async () => { - const podmanMachineDetails = new PodmanMachineDetails(page, ROOTLESS_PODMAN_MACHINE); - await test.step('Ensure rootless podman machine is OFF', async () => { - await playExpect(podmanMachineDetails.podmanMachineName).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF'); - }); - - await test.step('Start rootless podman machine', async () => { - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStartButton.click(); - - await playExpect(dialog).toBeVisible({ timeout: 60_000 }); - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - await playExpect(podmanMachineDetails.logsTab).toBeEnabled(); - await podmanMachineDetails.logsTab.click(); - await playExpect( - podmanMachineDetails.tabContent.getByText('Machine "podman-machine-rootless" started successfully'), - ).toBeVisible({ timeout: 10_000 }); - }); - - await test.step('Restart rootless podman machine', async () => { - await playExpect(podmanMachineDetails.podmanMachineRestartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineRestartButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 90_000 }); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - await playExpect(podmanMachineDetails.logsTab).toBeEnabled(); - await podmanMachineDetails.logsTab.click(); - await playExpect( - podmanMachineDetails.tabContent.getByText('Machine "podman-machine-rootless" stopped successfully'), - ).toBeVisible({ timeout: 10_000 }); - }); - }); - }); - - test('Stop rootless podman machine', async ({ page }) => { - const podmanMachineDetails = new PodmanMachineDetails(page, ROOTLESS_PODMAN_MACHINE); - await test.step('Ensure rootless podman machine is RUNNING', async () => { - await playExpect(podmanMachineDetails.podmanMachineName).toBeVisible(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING'); - }); - - await test.step('Stop rootless podman machine', async () => { - await playExpect(podmanMachineDetails.podmanMachineStopButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStopButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 60_000 }); - }); - }); - - test('Restart default podman machine', async ({ page, navigationBar }) => { - await test.step('Open resources page', async () => { - const dashboard = await navigationBar.openDashboard(); - await playExpect(dashboard.heading).toBeVisible(); - const settingsBar = await navigationBar.openSettings(); - await settingsBar.resourcesTab.click(); - }); - - await test.step('Go to default podman machine details page', async () => { - const resourcesPage = new ResourcesPage(page); - await playExpect(resourcesPage.heading).toBeVisible(); - await playExpect.poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME)).toBeTruthy(); - const resourcesPodmanConnections = new ResourceConnectionCardPage( - page, - RESOURCE_NAME, - DEFAULT_PODMAN_MACHINE_VISIBLE, - ); - await playExpect(resourcesPodmanConnections.resourceElementDetailsButton).toBeVisible(); - await resourcesPodmanConnections.resourceElementDetailsButton.click(); - }); - - await test.step('Turn default podman machine on', async () => { - const podmanMachineDetails = new PodmanMachineDetails(page, DEFAULT_PODMAN_MACHINE); - - await playExpect(podmanMachineDetails.podmanMachineStartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineStartButton.click(); - - await playExpect(dialog).toBeVisible({ timeout: 60_000 }); - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - - await playExpect(podmanMachineDetails.logsTab).toBeEnabled(); - await podmanMachineDetails.logsTab.click(); - await playExpect( - podmanMachineDetails.tabContent.getByText('Machine "podman-machine-default" started successfully').first(), - ).toBeVisible({ timeout: 30_000 }); - - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - - await playExpect(podmanMachineDetails.podmanMachineRestartButton).toBeEnabled(); - await podmanMachineDetails.podmanMachineRestartButton.click(); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('OFF', { timeout: 90_000 }); - await playExpect(podmanMachineDetails.podmanMachineStatus).toHaveText('RUNNING', { timeout: 90_000 }); - }); - }); - - test('Clean up rootless podman machine', async ({ page }) => { - await deletePodmanMachine(page, ROOTLESS_PODMAN_MACHINE_VISIBLE); - - try { - await handleConfirmationDialog(page, 'Podman', true, 'Yes'); - await handleConfirmationDialog(page, 'Podman', true, 'OK'); - } catch (error) { - console.log('No handling dialog displayed', error); - } - }); - }); diff --git a/tests/playwright/src/utility/auth-utils.ts b/tests/playwright/src/utility/auth-utils.ts deleted file mode 100644 index 3f565f8865..0000000000 --- a/tests/playwright/src/utility/auth-utils.ts +++ /dev/null @@ -1,172 +0,0 @@ -/********************************************************************** - * 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 { join } from 'node:path'; - -import type { Browser, Locator, Page } from '@playwright/test'; -import { chromium, expect as playExpect } from '@playwright/test'; - -import { waitUntil } from './wait'; - -export type ConfirmInputValue = { - inputLocator: Locator; - inputValue: string; - confirmLocator: Locator; -}; - -export async function findPageWithTitleInBrowser(browser: Browser, expectedTitle: string): Promise { - await waitUntil(async () => browser.contexts().length > 0, { - timeout: 10_000, - message: 'Waiting for browser contexts to be available', - sendError: false, - }); - const pages = browser.contexts().flatMap(context => context.pages()); - const pagesTitles = await Promise.all(pages.map(async page => ({ page, title: await page.title() }))); - - const chromePage = pagesTitles.find(p => p.title.includes(expectedTitle))?.page; - if (!chromePage) { - console.error(`No page found with title: ${expectedTitle}`); - } - return chromePage; -} - -export async function performBrowserLogin( - page: Page, - title: string | RegExp, - usernameAction: ConfirmInputValue, - passwordAction: ConfirmInputValue, - postLoginAction: (myPage: Page) => Promise, - options: { - screenshotsPath: string | undefined; - } = { screenshotsPath: '' }, -): Promise { - console.log(`Performing browser login...`); - const path = options.screenshotsPath; - if (path) { - await page.screenshot({ path: join(path, 'screenshots', 'initial_page.png'), type: 'png', fullPage: true }); - } - // title - await playExpect(page).toHaveTitle(title); - // username - await playExpect(usernameAction.inputLocator).toBeVisible(); - await usernameAction.inputLocator.fill(usernameAction.inputValue); - await playExpect(usernameAction.confirmLocator).toBeEnabled(); - await usernameAction.confirmLocator.click(); - if (path) { - await page.screenshot({ path: join(path, 'screenshots', 'after_username_page.png'), type: 'png', fullPage: true }); - } - // password - await playExpect(passwordAction.inputLocator).toBeVisible(); - await passwordAction.inputLocator.fill(passwordAction.inputValue); - await playExpect(passwordAction.confirmLocator).toBeEnabled(); - await passwordAction.confirmLocator.click(); - if (path) { - await page.screenshot({ path: join(path, 'screenshots', 'after_password_page.png'), type: 'png', fullPage: true }); - } - // custom doing... - await postLoginAction(page); -} - -export async function startChromium(port: string, tracesPath: string, args: string[] = []): Promise { - console.log('Starting a web server on port 9222'); - const browserLaunch = await chromium.launch({ - headless: false, - args: [`--remote-debugging-port=${port}`, ...args], - tracesDir: tracesPath, - slowMo: 200, - }); - - // hard wait - await waitUntil(async () => browserLaunch?.isConnected(), { - timeout: 10_000, - message: 'Waiting for browser to be connected', - sendError: false, - }); - // Connect to the same Chrome instance via CDP - // possible option is to use chromium.connectOverCDP(`http://localhost:${port}`); - if (!browserLaunch) { - throw new Error('Browser object was not initialized properly'); - } - console.log(`Browser is launched. Executable path: ${browserLaunch.browserType().executablePath()}`); - return browserLaunch; -} - -// to be deprecated, use getEntryFromConsoleLogs instead -export async function getEntryFromLogs( - page: Page, - filter: RegExp, - regex: RegExp, - lineContains = '', -): Promise { - return await getEntryFromConsoleLogs(page, filter, regex, lineContains, 10_000); -} - -// get a page's console log entry filtered by a regexp filter argument and matched by a regex -export async function getEntryFromConsoleLogs( - page: Page, - filter: RegExp, - regex: RegExp, - checkString: string, - timeout = 10_000, -): Promise { - const consoleLogPromise = page.waitForEvent('console', { - predicate: msg => { - return msg.type() === 'log' && filter.test(msg.text()); - }, - timeout: timeout, - }); - const consoleMsg = await consoleLogPromise; - const logLine = consoleMsg.text(); - if (checkString) { - playExpect(logLine).toContain(checkString); - } - const parsedString = regex.exec(logLine); - const urlMatch = parsedString ? parsedString[1] : undefined; - console.log(`Matched string: ${urlMatch}`); - return urlMatch; -} - -// Accept/Refuse the cooking in the iframe element -export async function handleCookies( - page: Page, - iframTitle: string, - buttonName: string, - timeout: number, -): Promise { - const iframe = page.frameLocator(`iframe[title="${iframTitle}"]`); - const button = iframe.getByRole('button', { name: buttonName }); - const buttonVisible = await checkLocatorExistence(button, timeout); - if (buttonVisible) { - await playExpect(button).toBeVisible(); - await button.click(); - console.log(`Clicked on the button: ${buttonName}`); - } else { - console.log(`${buttonName} button is not visible, skipping confirmation...`); - } -} - -// function is dedicated to verify if some locator exists, depending on external circumstances -export async function checkLocatorExistence(locator: Locator, timeout = 5000): Promise { - try { - await playExpect(locator).toBeVisible({ timeout: timeout }); - } catch (error: unknown) { - console.log(`Locator not found: ${error}`); - return false; - } - return true; -} diff --git a/tests/playwright/src/utility/cleanup.ts b/tests/playwright/src/utility/cleanup.ts deleted file mode 100644 index e62f8372ea..0000000000 --- a/tests/playwright/src/utility/cleanup.ts +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { existsSync, rmSync } from 'node:fs'; - -/** - * Force remove recursively folder, if exists - * @param path path to a folder to be force removed recursively - */ -export async function removeFolderIfExists(path: string): Promise { - console.log(`Cleaning up folder: ${path}`); - - if (existsSync(path)) { - console.log(`Folder found, removing...`); - rmSync(path, { recursive: true, force: true, maxRetries: 5 }); - } -} diff --git a/tests/playwright/src/utility/cluster-operations.ts b/tests/playwright/src/utility/cluster-operations.ts deleted file mode 100644 index 2eaeec8543..0000000000 --- a/tests/playwright/src/utility/cluster-operations.ts +++ /dev/null @@ -1,255 +0,0 @@ -/********************************************************************** - * 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 type { Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { ResourceElementActions } from '../model/core/operations'; -import { ContainerState, ResourceElementState } from '../model/core/states'; -import type { KindClusterOptions } from '../model/core/types'; -import { CreateKindClusterPage } from '../model/pages/create-kind-cluster-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourceDetailsPage } from '../model/pages/resource-details-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { VolumesPage } from '../model/pages/volumes-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { StatusBar } from '../model/workbench/status-bar'; -import { getVolumeNameForContainer } from './operations'; - -export async function createKindCluster( - page: Page, - clusterName: string, - timeout: number = 300_000, - { configFilePath, providerType, httpPort, httpsPort, useIngressController, containerImage }: KindClusterOptions = {}, -): Promise { - return test.step(`Create Kind cluster with settings: configFilePath=${configFilePath}, - providerType=${providerType}, httpPort=${httpPort}, - httpsPort=${httpsPort}, ingressController=${useIngressController}`, async () => { - const navigationBar = new NavigationBar(page); - const statusBar = new StatusBar(page); - const kindResourceCard = new ResourceConnectionCardPage(page, 'kind', clusterName); - const createKindClusterPage = new CreateKindClusterPage(page); - - const settingsPage = await navigationBar.openSettings(); - const resourcesPage = await settingsPage.openTabPage(ResourcesPage); - await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 }); - await playExpect.poll(async () => resourcesPage.resourceCardIsVisible('kind')).toBeTruthy(); - await playExpect(kindResourceCard.createButton).toBeVisible(); - - if (await kindResourceCard.doesResourceElementExist()) { - if ((await kindResourceCard.resourceElementConnectionStatus.textContent()) !== ResourceElementState.Running) { - console.log(`Kind cluster [${clusterName}] already present, but not running. Delete the cluster...`); - await deleteCluster(page); - } else { - console.log(`Kind cluster [${clusterName}] already present, skipping creation.`); - return; - } - } - - await kindResourceCard.createButton.click(); - await createKindClusterPage.createKindCluster( - clusterName, - { - configFilePath: configFilePath, - providerType: providerType, - httpPort: httpPort, - httpsPort: httpsPort, - useIngressController: useIngressController, - containerImage: containerImage, - }, - timeout, - ); - await playExpect(kindResourceCard.resourceElement).toBeVisible(); - await playExpect(kindResourceCard.resourceElementConnectionStatus).toHaveText(ResourceElementState.Running, { - timeout: 15_000, - }); - await statusBar.validateKubernetesContext(`kind-${clusterName}`); - }); -} - -export async function deleteCluster( - page: Page, - resourceName: string = 'kind', - containerName: string = 'kind-cluster-control-plane', - clusterName: string = 'kind-cluster', - timeout: number = 50_000, -): Promise { - return test.step(`Delete ${resourceName} cluster`, async () => { - const volumeName = await getVolumeNameForContainer(page, containerName); - const navigationBar = new NavigationBar(page); - const resourceCard = new ResourceConnectionCardPage(page, resourceName, clusterName); - - await navigationBar.openSettings(); - const resourcesPage = new ResourcesPage(page); - await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 }); - if (!(await resourceCard.doesResourceElementExist())) { - console.log(`Kind cluster [${clusterName}] not present, skipping deletion.`); - return; - } - - await resourceCard.performConnectionAction(ResourceElementActions.Stop); - await playExpect(resourceCard.resourceElementConnectionStatus).toHaveText(ResourceElementState.Off, { - timeout: timeout, - }); - await resourceCard.performConnectionAction(ResourceElementActions.Delete); - await playExpect(resourceCard.markdownContent).toBeVisible({ - timeout: timeout, - }); - await validateClusterResourcesDeletion(page, clusterName, containerName, volumeName); - }); -} - -export async function checkClusterResources(page: Page, containerName: string): Promise { - return test.step(`Check container '${containerName}' and volume cluster resources.`, async () => { - const navigationBar = new NavigationBar(page); - const containersPage = await navigationBar.openContainers(); - await playExpect.poll(async () => containersPage.containerExists(containerName)).toBeTruthy(); - const containerDetailsPage = await containersPage.openContainersDetails(containerName); - await playExpect.poll(async () => await containerDetailsPage.getState()).toEqual(ContainerState.Running); - - const volumesPage = new VolumesPage(page); - const volumeName = await getVolumeNameForContainer(page, containerName); - if (!volumeName) { - throw new Error(`Volume name for container "${containerName}" is not defined.`); - } - const volumeDetailsPage = await volumesPage.openVolumeDetails(volumeName); - await playExpect.poll(async () => await volumeDetailsPage.isUsed()).toBeTruthy(); - }); -} - -export async function resourceConnectionAction( - page: Page, - resourceCard: ResourceConnectionCardPage, - resourceConnectionAction: ResourceElementActions, - expectedResourceState: ResourceElementState, - timeout: number = 30_000, -): Promise { - return test.step(`Performs "${resourceConnectionAction}" action, expects "${expectedResourceState}" state.`, async () => { - const navigationBar = new NavigationBar(page); - await navigationBar.openSettings(); - await resourceCard.performConnectionAction(resourceConnectionAction); - if (resourceConnectionAction === ResourceElementActions.Restart) { - const stopButton = resourceCard.resourceElementConnectionActions.getByRole('button', { - name: ResourceElementActions.Stop, - exact: true, - }); - await playExpect(stopButton).toBeEnabled({ timeout: timeout }); - } - await playExpect(resourceCard.resourceElementConnectionStatus).toHaveText(expectedResourceState, { - timeout: timeout, - }); - }); -} - -export async function resourceConnectionActionDetails( - page: Page, - resourceCard: ResourceConnectionCardPage, - resourceName: string, - resourceConnectionAction: ResourceElementActions, - expectedResourceState: ResourceElementState, - timeout: number = 30_000, -): Promise { - return test.step(`Performs a connection action '${resourceConnectionAction}' on the resource from the details page, verifies the expected resource state '${expectedResourceState}'`, async () => { - const navigationBar = new NavigationBar(page); - const resourceDetailsPage = new ResourceDetailsPage(page, resourceName); - - try { - await playExpect(resourceDetailsPage.heading).toBeVisible(); - } catch { - const settingsBar = await navigationBar.openSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 }); - await playExpect(resourceCard.resourceElementDetailsButton).toBeEnabled(); - await resourceCard.resourceElementDetailsButton.click(); - } - - await resourceDetailsPage.performConnectionActionDetails(resourceConnectionAction); - if (resourceConnectionAction === ResourceElementActions.Restart) { - const stopButton = resourceDetailsPage.controlActions.getByRole('button', { - name: ResourceElementActions.Stop, - exact: true, - }); - await playExpect(stopButton).toBeEnabled({ timeout: timeout }); - } - await playExpect(resourceDetailsPage.resourceStatus).toHaveText(expectedResourceState, { - timeout: timeout, - }); - }); -} - -export async function deleteClusterFromDetails( - page: Page, - resourceName: string = 'kind', - containerName: string = 'kind-cluster-control-plane', - clusterName: string = 'kind-cluster', - timeout: number = 30_000, -): Promise { - return test.step(`Deletes the '${clusterName}' cluster from the details page`, async () => { - const navigationBar = new NavigationBar(page); - const volumeName = await getVolumeNameForContainer(page, containerName); - - const settingsBar = await navigationBar.openSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - const resourceCard = new ResourceConnectionCardPage(page, resourceName, clusterName); - await playExpect(resourcesPage.heading).toBeVisible({ timeout: 10_000 }); - if (!(await resourceCard.doesResourceElementExist())) { - console.log(`Cluster [${clusterName}] not present, skipping deletion.`); - return; - } - await playExpect(resourceCard.resourceElementDetailsButton).toBeEnabled(); - await resourceCard.resourceElementDetailsButton.click(); - - const resourceDetails = new ResourceDetailsPage(page, clusterName); - await playExpect(resourceDetails.heading).toBeVisible(); - await resourceDetails.performConnectionActionDetails(ResourceElementActions.Stop); - await playExpect(resourceDetails.resourceStatus).toHaveText(ResourceElementState.Off, { - timeout: timeout, - }); - await resourceDetails.performConnectionActionDetails(ResourceElementActions.Delete); - - await validateClusterResourcesDeletion(page, clusterName, containerName, volumeName); - }); -} - -export async function validateClusterResourcesDeletion( - page: Page, - clusterName: string, - containerName: string, - volumeName: string, - timeout: number = 20_000, -): Promise { - return test.step(`Validates that resources associated with the deleted '${clusterName}' cluster are removed`, async () => { - const navigationBar = new NavigationBar(page); - const containersPage = await navigationBar.openContainers(); - - await playExpect(containersPage.heading).toBeVisible(); - await playExpect - .poll(async () => containersPage.containerExists(containerName), { - timeout: timeout, - }) - .toBeFalsy(); - - const volumePage = await navigationBar.openVolumes(); - await playExpect(volumePage.heading).toBeVisible(); - await playExpect - .poll(async () => await volumePage.waitForVolumeDelete(volumeName), { - timeout: timeout, - }) - .toBeTruthy(); - }); -} diff --git a/tests/playwright/src/utility/fixtures.ts b/tests/playwright/src/utility/fixtures.ts deleted file mode 100644 index ad5c20a83a..0000000000 --- a/tests/playwright/src/utility/fixtures.ts +++ /dev/null @@ -1,62 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { Page } from '@playwright/test'; -import { test as base } from '@playwright/test'; - -import { WelcomePage } from '../model/pages/welcome-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { StatusBar } from '../model/workbench/status-bar'; -import { Runner } from '../runner/podman-desktop-runner'; -import { RunnerOptions } from '../runner/runner-options'; - -export type TestFixtures = { - runner: Runner; - navigationBar: NavigationBar; - welcomePage: WelcomePage; - page: Page; - statusBar: StatusBar; -}; - -export type FixtureOptions = { - runnerOptions: RunnerOptions; -}; - -export const test = base.extend({ - runnerOptions: [new RunnerOptions(), { option: true }], - runner: async ({ runnerOptions }, use) => { - const runner = await Runner.getInstance({ runnerOptions }); - await use(runner); - }, - page: async ({ runner }, use) => { - await use(runner.getPage()); - }, - navigationBar: async ({ page }, use) => { - const navigationBar = new NavigationBar(page); - await use(navigationBar); - }, - welcomePage: async ({ page }, use) => { - const welcomePage = new WelcomePage(page); - await use(welcomePage); - }, - statusBar: async ({ page }, use) => { - const statusBar = new StatusBar(page); - await use(statusBar); - }, -}); -export { expect } from '@playwright/test'; diff --git a/tests/playwright/src/utility/kubernetes.ts b/tests/playwright/src/utility/kubernetes.ts deleted file mode 100644 index 1738420dce..0000000000 --- a/tests/playwright/src/utility/kubernetes.ts +++ /dev/null @@ -1,299 +0,0 @@ -/********************************************************************** - * 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 { Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import type { KubernetesResourceState } from '../model/core/states'; -import type { PlayKubernetesOptions } from '../model/core/types'; -import { KubernetesResources } from '../model/core/types'; -import { ContainerDetailsPage } from '../model/pages/container-details-page'; -import type { PodsPage } from '../model/pages/pods-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { handleConfirmationDialog } from './operations'; - -export async function deployContainerToCluster( - page: Page, - containerName: string, - kubernetesContext: string, - deployedPodName: string, -): Promise { - return test.step(`Deploy '${containerName}' and verify pod '${deployedPodName}' appears in the Kubernetes environment`, async () => { - const containerDetailsPage = new ContainerDetailsPage(page, containerName); - const navigationBar = new NavigationBar(page); - - await playExpect(containerDetailsPage.heading).toBeVisible(); - const deployToKubernetesPage = await containerDetailsPage.openDeployToKubernetesPage(); - await deployToKubernetesPage.deployPod(containerName, { useKubernetesServices: true }, kubernetesContext); - - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesPodsPage = await kubernetesBar.openTabPage(KubernetesResources.Pods); - await playExpect - .poll(async () => kubernetesPodsPage.getRowByName(deployedPodName), { timeout: 15_000 }) - .toBeTruthy(); - }); -} - -export async function createKubernetesResource( - page: Page, - resourceType: KubernetesResources, - resourceName: string, - resourceYamlPath: string, - kubernetesRuntime: PlayKubernetesOptions, -): Promise { - return test.step(`Create ${resourceType} kubernetes resource: ${resourceName}`, async () => { - const navigationBar = new NavigationBar(page); - - await applyYamlFileToCluster(page, resourceYamlPath, kubernetesRuntime); - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesResourcePage = await kubernetesBar.openTabPage(resourceType); - await playExpect(kubernetesResourcePage.heading).toBeVisible(); - await playExpect.poll(async () => kubernetesResourcePage.getRowByName(resourceName)).toBeTruthy(); - }); -} - -export async function deleteKubernetesResource( - page: Page, - resourceType: KubernetesResources, - resourceName: string, - timeout: number = 30_000, -): Promise { - return test.step(`Delete ${resourceType} kubernetes resource: ${resourceName}`, async () => { - const navigationBar = new NavigationBar(page); - - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesResourcePage = await kubernetesBar.openTabPage(resourceType); - await kubernetesResourcePage.deleteKubernetesResource(resourceName); - await handleConfirmationDialog(page); - await playExpect - .poll(async () => await kubernetesResourcePage.getRowByName(resourceName), { timeout: timeout }) - .not.toBeTruthy(); - }); -} - -export async function checkDeploymentReplicasInfo( - page: Page, - resourceType: KubernetesResources, - resourceName: string, - expectedReplicaCount: number, -): Promise { - const navigationBar = new NavigationBar(page); - const kubernetesBar = await navigationBar.openKubernetes(); - - const kubernetesResourcePage = await kubernetesBar.openTabPage(resourceType); - const kubernetesResourceDetails = await kubernetesResourcePage.openResourceDetails(resourceName, resourceType); - await playExpect(kubernetesResourceDetails.heading).toBeVisible(); - const summaryTab = await kubernetesResourceDetails.activateTab('Summary'); - await playExpect(summaryTab.tabContent).toContainText( - `Desired: ${expectedReplicaCount}, Updated: ${expectedReplicaCount}, Total: ${expectedReplicaCount}, Available: ${expectedReplicaCount}, Unavailable: N/A`, - ); -} - -export async function checkKubernetesResourceState( - page: Page, - resourceType: KubernetesResources, - resourceName: string, - expectedResourceState: KubernetesResourceState, - timeout: number = 90_000, -): Promise { - return test.step(`Check ${resourceType} kubernetes resource state, should be ${expectedResourceState}`, async () => { - const navigationBar = new NavigationBar(page); - const kubernetesBar = await navigationBar.openKubernetes(); - - const kubernetesResourcePage = await kubernetesBar.openTabPage(resourceType); - const kubernetesResourceDetails = await kubernetesResourcePage.openResourceDetails( - resourceName, - resourceType, - timeout, - ); - await playExpect(kubernetesResourceDetails.heading).toBeVisible(); - await playExpect - .poll(async () => kubernetesResourceDetails.getState(), { timeout: timeout }) - .toEqual(expectedResourceState); - }); -} - -export async function applyYamlFileToCluster( - page: Page, - resourceYamlPath: string, - kubernetesRuntime: PlayKubernetesOptions, -): Promise { - return test.step(`Apply YAML file to Kubernetes cluster`, async () => { - const navigationBar = new NavigationBar(page); - const podsPage = await navigationBar.openPods(); - - await playExpect(podsPage.heading).toBeVisible(); - const playYamlPage = await podsPage.openPlayKubeYaml(); - await playExpect(playYamlPage.heading).toBeVisible(); - return await playYamlPage.playYaml(resourceYamlPath, false, 180_000, kubernetesRuntime); - }); -} - -export async function editDeploymentYamlFile( - page: Page, - resourceType: KubernetesResources, - deploymentName: string, - currentReplicaCount: number = 3, - updatedReplicaCount: number = 5, -): Promise { - return test.step(`Change deployment kubernetes cluster resource`, async () => { - const navigationBar = new NavigationBar(page); - const kubernetesBar = await navigationBar.openKubernetes(); - await playExpect - .poll(async () => await countKubernetesPodReplicas(page, deploymentName), { - timeout: 60_000, - }) - .toBe(currentReplicaCount); - - const deploymentsPage = await kubernetesBar.openTabPage(resourceType); - await playExpect(deploymentsPage.heading).toBeVisible(); - const deploymentDetails = await deploymentsPage.openResourceDetails(deploymentName, resourceType); - await playExpect(deploymentDetails.heading).toBeVisible(); - await deploymentDetails.editKubernetsYamlFile( - `replicas: ${currentReplicaCount}`, - `replicas: ${updatedReplicaCount}`, - ); - - await playExpect - .poll(async () => await countKubernetesPodReplicas(page, deploymentName), { - timeout: 60_000, - }) - .toBe(updatedReplicaCount); - }); -} - -export async function countKubernetesPodReplicas(page: Page, expectedPodName: string): Promise { - return test.step(`Count pod replicas: ${expectedPodName}`, async () => { - const navigationBar = new NavigationBar(page); - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesPodsPage = await kubernetesBar.openTabPage(KubernetesResources.Pods); - - let counter: number = 0; - const rows = await kubernetesPodsPage.getAllTableRows(); - for (let i = rows.length - 1; i > 0; i--) { - const podName = await rows[i].getByRole('cell').nth(3).getByRole('button').textContent(); - if (podName?.includes(expectedPodName)) { - counter += 1; - } - } - return counter; - }); -} - -export async function configurePortForwarding( - page: Page, - resourceType: KubernetesResources, - resourceName: string, -): Promise { - return test.step(`Configure port forwarding for ${resourceName} ${resourceType} k8s resource`, async () => { - const navigationBar = new NavigationBar(page); - - const kubernetesBar = await navigationBar.openKubernetes(); - const kubernetesResourcePage = await kubernetesBar.openTabPage(resourceType); - const kubernetesResourceDetailsPage = await kubernetesResourcePage.openResourceDetails(resourceName, resourceType); - await kubernetesResourceDetailsPage.activateTab('Summary'); - const forwardButton = page.getByRole('button', { name: `Forward...` }); - await playExpect(forwardButton).toBeVisible(); - await forwardButton.click(); - - const openInBrowserButton = page.getByRole('button', { name: 'Open', exact: true }); - const removeConfigurationButton = page.getByRole('button', { name: 'Remove' }); - await playExpect(openInBrowserButton).toBeVisible({ timeout: 10_000 }); - await playExpect(removeConfigurationButton).toBeVisible(); - }); -} - -export async function verifyPortForwardingConfiguration( - page: Page, - configurationName: string, - localPort: number, - remotePort: number, -): Promise { - return test.step(`Verify port forwarding for ${configurationName} configuration: local port ${localPort}, remote port ${remotePort}`, async () => { - const navigationBar = new NavigationBar(page); - const kubernetesBar = await navigationBar.openKubernetes(); - const portForwardingPage = await kubernetesBar.openTabPage(KubernetesResources.PortForwarding); - await playExpect(portForwardingPage.heading).toBeVisible(); - const configurationRow = await portForwardingPage.fetchKubernetesResource(configurationName); - - const localPortCell = await portForwardingPage.geAttributeByRow( - configurationRow, - 'Local Port', - KubernetesResources.PortForwarding, - ); - const remotePortCell = await portForwardingPage.geAttributeByRow( - configurationRow, - 'Remote Port', - KubernetesResources.PortForwarding, - ); - playExpect(Number(await localPortCell.textContent())).toBe(localPort); - playExpect(Number(await remotePortCell.textContent())).toBe(remotePort); - }); -} - -export async function verifyLocalPortResponse(forwardAddress: string, responseMessage: string): Promise { - return test.step('Verify local port response', async () => { - const response: Response = await fetch(forwardAddress, { cache: 'no-store' }); - const blob: Blob = await response.blob(); - const text: string = await blob.text(); - playExpect(text).toContain(responseMessage); - }); -} - -export async function monitorPodStatusInClusterContainer( - page: Page, - containerName: string, - command: string, - timeout: number = 160_000, -): Promise { - const navigationBar = new NavigationBar(page); - const containersPage = await navigationBar.openContainers(); - await playExpect(containersPage.heading).toBeVisible(); - await playExpect.poll(async () => containersPage.getContainerRowByName(containerName)).toBeTruthy(); - const containerDetailsPage = await containersPage.openContainersDetails(containerName); - - await playExpect - .poll( - async () => { - await containerDetailsPage.executeCommandInTerminal(command); - const result = await checkContourPodsInTerminal(page, containerName); - await containerDetailsPage.executeCommandInTerminal('clear'); - return result; - }, - { timeout: timeout }, - ) - .toBeTruthy(); -} - -async function checkContourPodsInTerminal(page: Page, containerName: string): Promise { - const containerDetailsPage = new ContainerDetailsPage(page, containerName); - await containerDetailsPage.activateTab('Terminal'); - await playExpect(containerDetailsPage.terminalContent).toBeVisible(); - await page.waitForTimeout(2_000); - - try { - await playExpect(containerDetailsPage.terminalContent).toContainText(/contour-\S+\s+1\/1\s+Running\s+\d+\s+\S+/); - await playExpect(containerDetailsPage.terminalContent).toContainText( - /contour-certgen-\S+\s+0\/1\s+Completed\s+\d+\s+\S+/, - ); - await playExpect(containerDetailsPage.terminalContent).toContainText(/envoy-\S+\s+2\/2\s+Running\s+\d+\s+\S+/); - return true; - } catch { - return false; - } -} diff --git a/tests/playwright/src/utility/operations.ts b/tests/playwright/src/utility/operations.ts deleted file mode 100644 index c585093fee..0000000000 --- a/tests/playwright/src/utility/operations.ts +++ /dev/null @@ -1,510 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { execSync } from 'node:child_process'; -import * as os from 'node:os'; - -import type { Locator, Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { ResourceElementActions } from '../model/core/operations'; -import { ResourceElementState } from '../model/core/states'; -import { CLIToolsPage } from '../model/pages/cli-tools-page'; -import { ExperimentalPage } from '../model/pages/experimental-page'; -import { PreferencesPage } from '../model/pages/preferences-page'; -import { RegistriesPage } from '../model/pages/registries-page'; -import { ResourceConnectionCardPage } from '../model/pages/resource-connection-card-page'; -import { ResourcesPage } from '../model/pages/resources-page'; -import { SettingsBar } from '../model/pages/settings-bar'; -import { VolumeDetailsPage } from '../model/pages/volume-details-page'; -import { NavigationBar } from '../model/workbench/navigation'; -import { isLinux, isMac, isWindows } from './platform'; -import { waitUntil, waitWhile } from './wait'; - -/** - * Stop and delete container defined by its name - * @param page playwright's page object - * @param name name of container to be removed - */ -export async function deleteContainer(page: Page, name: string): Promise { - return test.step(`Delete container with name ${name}`, async () => { - const navigationBar = new NavigationBar(page); - const containers = await navigationBar.openContainers(); - await playExpect(containers.heading).toBeVisible({ timeout: 10_000 }); - const container = await containers.getContainerRowByName(name); - // check for container existence - if (container === undefined) { - console.log(`container '${name}' does not exist, skipping...`); - } else { - // stop container first, might not be running - const stopButton = container.getByRole('button').and(container.getByLabel('Stop Container')); - if ((await stopButton.count()) > 0) await stopButton.click(); - - // delete the container - const deleteButton = container.getByRole('button').and(container.getByLabel('Delete Container')); - await deleteButton.click(); - await handleConfirmationDialog(page); - // wait for container to disappear - try { - console.log('Waiting for container to get deleted ...'); - await playExpect - .poll(async () => await containers.getContainerRowByName(name), { timeout: 30_000 }) - .toBeFalsy(); - } catch (error) { - if (!(error as Error).message.includes('Page is empty')) { - throw Error(`Error waiting for container '${name}' to get removed, ${error}`); - } - } - } - }); -} - -/** - * Delete image defined by its name - * @param page playwright's page object - * @param name name of image to be removed - */ -export async function deleteImage(page: Page, name: string): Promise { - return test.step(`Delete image ${name}`, async () => { - const navigationBar = new NavigationBar(page); - const images = await navigationBar.openImages(); - await playExpect(images.heading).toBeVisible({ timeout: 10_000 }); - const row = await images.getImageRowByName(name); - if (row === undefined) { - console.log(`image '${name}' does not exist, skipping...`); - } else { - const deleteButton = row.getByRole('button', { name: 'Delete Image' }); - if (await deleteButton.isEnabled()) { - await deleteButton.click(); - await handleConfirmationDialog(page); - } else { - throw Error(`Cannot delete image ${name}, because it is in use`); - } - // wait for image to disappear - try { - console.log('image deleting, waiting...'); - await waitWhile( - async () => { - const images = await new NavigationBar(page).openImages(); - const result = await images.getImageRowByName(name); - return !!result; - }, - { timeout: 10_000, sendError: false }, - ); - } catch (error) { - if (!(error as Error).message.includes('Page is empty')) { - throw Error(`Error waiting for image '${name}' to get removed, ${error}`); - } - } - } - }); -} - -export async function deleteRegistry(page: Page, name: string, failIfNotExist = false): Promise { - return test.step(`Delete registry ${name}`, async () => { - const navigationBar = new NavigationBar(page); - const settingsBar = await navigationBar.openSettings(); - const registryPage = await settingsBar.openTabPage(RegistriesPage); - const registryRecord = await registryPage.getRegistryRowByName(name); - await waitUntil(() => registryRecord.isVisible(), { sendError: failIfNotExist }); - if (await registryRecord.isVisible()) { - // it might be that the record exist but there are no credentials -> it is default registry and it is empty - // or if there is a kebab memu available - const dropdownMenu = registryRecord.getByRole('button', { name: 'kebab menu' }); - if (await dropdownMenu.isVisible()) { - await registryPage.removeRegistry(name); - } - } - }); -} - -export async function deletePod(page: Page, name: string, timeout: number = 50_000): Promise { - return test.step(`Delete pod ${name}`, async () => { - const navigationBar = new NavigationBar(page); - const pods = await navigationBar.openPods(); - await playExpect(pods.heading).toBeVisible({ timeout: 10_000 }); - const pod = await pods.getPodRowByName(name); - // check if pod exists - if (pod === undefined) { - console.log(`pod '${name}' does not exist, skipping...`); - } else { - // delete the pod - const deleteButton = pod.getByRole('button').and(pod.getByLabel('Delete Pod')); - await deleteButton.click(); - // config delete dialog - await handleConfirmationDialog(page); - // wait for pod to disappear - try { - console.log('Waiting for pod to get deleted ...'); - await waitWhile( - async () => { - return !!(await pods.getPodRowByName(name)); - }, - { timeout: timeout }, - ); - } catch (error) { - if (!(error as Error).message.includes('Page is empty')) { - throw Error(`Error waiting for pod '${name}' to get removed, ${error}`); - } - } - } - }); -} - -// Handles dialog that has accessible name `dialogTitle` and either confirms or rejects it. -export async function handleConfirmationDialog( - page: Page, - dialogTitle = 'Confirmation', - confirm = true, - confirmationButton = 'Yes', - cancelButton = 'Cancel', - timeout = 10_000, - moreThanOneConsecutiveDialogs = false, -): Promise { - return test.step('Handle confirmation dialog', async () => { - // wait for dialog to appear using waitFor - const dialog = page.getByRole('dialog', { name: dialogTitle, exact: true }); - await waitUntil(async () => await dialog.isVisible(), { timeout: timeout }); - const button = confirm - ? dialog.getByRole('button', { name: confirmationButton }) - : dialog.getByRole('button', { name: cancelButton }); - await playExpect(button).toBeEnabled(); - await button.click(); - - if (moreThanOneConsecutiveDialogs) { - const button = dialog.getByRole('button', { name: 'Done' }); - await playExpect(button).toBeEnabled({ timeout: timeout }); - await button.click(); - } - - await waitUntil(async () => !(await dialog.isVisible()), { timeout: timeout }); - }); -} - -/** - * Async function that stops and deletes Podman Machine through Settings -> Resources page - * @param page playwright's page object - * @param machineVisibleName Name of the Podman Machine to delete - */ -export async function deletePodmanMachine(page: Page, machineVisibleName: string): Promise { - return test.step('Delete Podman machine', async () => { - const RESOURCE_NAME: string = 'podman'; - const navigationBar = new NavigationBar(page); - const dashboardPage = await navigationBar.openDashboard(); - await playExpect(dashboardPage.heading).toBeVisible(); - const settingsBar = await navigationBar.openSettings(); - const resourcesPage = await settingsBar.openTabPage(ResourcesPage); - await playExpect - .poll(async () => await resourcesPage.resourceCardIsVisible(RESOURCE_NAME), { timeout: 10_000 }) - .toBeTruthy(); - const podmanResourceCard = new ResourceConnectionCardPage(page, RESOURCE_NAME, machineVisibleName); - await playExpect(podmanResourceCard.providerConnections).toBeVisible({ timeout: 10_000 }); - await waitUntil( - async () => { - return await podmanResourceCard.resourceElement.isVisible(); - }, - { timeout: 15_000 }, - ); - if (await podmanResourceCard.resourceElement.isVisible()) { - await playExpect(podmanResourceCard.resourceElementConnectionActions).toBeVisible(); - await playExpect(podmanResourceCard.resourceElementConnectionStatus).toBeVisible(); - if ((await podmanResourceCard.resourceElementConnectionStatus.innerText()) === ResourceElementState.Starting) { - console.log('Podman machine is in starting currently, will send stop command via CLI'); - // eslint-disable-next-line sonarjs/os-command - execSync(`podman machine stop ${machineVisibleName}`); - await playExpect(podmanResourceCard.resourceElementConnectionStatus).toHaveText(ResourceElementState.Off, { - timeout: 30_000, - }); - console.log('Podman machine stopped via CLI'); - } - if ((await podmanResourceCard.resourceElementConnectionStatus.innerText()) === ResourceElementState.Running) { - try { - await podmanResourceCard.performConnectionAction(ResourceElementActions.Stop); - await waitUntil( - async () => - (await podmanResourceCard.resourceElementConnectionStatus.innerText()).includes(ResourceElementState.Off), - { timeout: 30_000, sendError: true }, - ); - } catch (error) { - console.log('Podman machine stop failed, will try to stop it via CLI'); - // eslint-disable-next-line sonarjs/os-command - execSync(`podman machine stop ${machineVisibleName}`); - } - await playExpect(podmanResourceCard.resourceElementConnectionStatus).toHaveText(ResourceElementState.Off, { - timeout: 30_000, - }); - } - await podmanResourceCard.performConnectionAction(ResourceElementActions.Delete); - await playExpect(podmanResourceCard.resourceElement).toBeHidden({ timeout: 60_000 }); - } else { - console.log(`Podman machine [${machineVisibleName}] not present, skipping deletion.`); - } - }); -} - -export async function getVolumeNameForContainer(page: Page, containerName: string): Promise { - return test.step('Get volume name for container', async () => { - let volumeName; - let volumeSummaryContent; - try { - const navigationBar = new NavigationBar(page); - const volumePage = await navigationBar.openVolumes(); - await playExpect(volumePage.heading).toBeVisible({ timeout: 10_000 }); - const rows = await volumePage.getAllTableRows(); - - for (let i = rows.length - 1; i > 0; i--) { - volumeName = await rows[i].getByRole('cell').nth(3).getByRole('button').textContent(); - if (volumeName) { - const volumeDetails = await volumePage.openVolumeDetails(volumeName); - await volumeDetails.activateTab(VolumeDetailsPage.SUMMARY_TAB); - volumeSummaryContent = await volumeDetails.tabContent.allTextContents(); - for (const content of volumeSummaryContent) { - if (content.includes(containerName)) { - await volumeDetails.backLink.click(); - return volumeName; - } - } - await volumeDetails.backLink.click(); - } - } - return ''; - } catch (error) { - if ( - error instanceof Error && - (error.message === 'Page is empty, there is no content' || error.message.includes('does not exist')) - ) { - return ''; - } else { - throw error; - } - } - }); -} - -export async function ensureCliInstalled( - page: Page, - resourceName: string, - timeout = 60_000, - version = '', -): Promise { - return test.step(`Ensure ${resourceName} CLI is installed`, async () => { - const cliToolsPage = new CLIToolsPage(page); - await playExpect(cliToolsPage.toolsTable).toBeVisible({ timeout: 10_000 }); - await playExpect.poll(async () => await cliToolsPage.toolsTable.count()).toBeGreaterThan(0); - await playExpect(cliToolsPage.getToolRow(resourceName)).toBeVisible({ timeout: 10_000 }); - - if (!(await cliToolsPage.getCurrentToolVersion(resourceName))) { - await cliToolsPage.installTool(resourceName, version, timeout); - } - - await playExpect - .poll(async () => await cliToolsPage.getCurrentToolVersion(resourceName), { timeout: timeout }) - .toBeTruthy(); - }); -} - -export async function createPodmanMachineFromCLI(): Promise { - return test.step('Create Podman machine from CLI', async () => { - if (isLinux) return; - - const podmanMachineMode = process.env.PODMAN_ROOTFUL === '0' ? '' : '--rootful'; - const userModeNetworking = process.env.PODMAN_NETWORKING === '1' ? '--user-networking' : ''; - - try { - // eslint-disable-next-line sonarjs/no-os-command-from-path, sonarjs/os-command - execSync(`podman machine init ${podmanMachineMode} ${userModeNetworking}`); - } catch (error) { - if (error instanceof Error && error.message.includes('VM already exists')) { - console.log(`Podman machine already exists, skipping creation.`); - } - } - - try { - // eslint-disable-next-line sonarjs/no-os-command-from-path - execSync('podman machine start'); - console.log('Default podman machine started'); - } catch (error) { - if (error instanceof Error && error.message.includes('already running')) { - console.log('Default podman machine already started, skipping start.'); - } - } - }); -} - -export async function deletePodmanMachineFromCLI(podmanMachineName: string): Promise { - return test.step('Delete Podman machine from CLI', () => { - try { - // eslint-disable-next-line sonarjs/os-command - execSync(`podman machine rm ${podmanMachineName} -f`); - } catch (error) { - if (error instanceof Error && error.message.includes('VM does not exist')) { - console.log(`Podman machine [${podmanMachineName}] does not exist, skipping deletion.`); - } - } - }); -} - -export async function resetPodmanMachinesFromCLI(): Promise { - return test.step('Reset Podman machine from CLI', () => { - execSync(`podman machine reset -f`); - }); -} - -export async function fillTextbox(textbox: Locator, text: string): Promise { - return test.step(`Fill textbox with ${text}`, async () => { - await playExpect(textbox).toBeVisible({ timeout: 15_000 }); - await textbox.fill(text); - await playExpect(textbox).toHaveValue(text); - }); -} - -export async function runComposeUpFromCLI(composeFilePath: string): Promise { - return test.step('Run Compose up from CLI', async () => { - try { - // eslint-disable-next-line sonarjs/os-command - execSync(`podman compose -f ${composeFilePath} up -d`); - } catch (error) { - throw new Error(`Error running podman compose up from CLI: ${error}`); - } - }); -} - -export async function untagImagesFromPodman(name: string, tag: string = ''): Promise { - return test.step('Untag images from Podman', async () => { - try { - if (tag) { - // eslint-disable-next-line sonarjs/os-command - execSync(`podman untag ${name}:${tag}`); - } else { - // eslint-disable-next-line sonarjs/os-command - execSync(`podman untag ${name}`); - } - } catch (error) { - throw new Error(`Error untagging images from Podman: ${error}`); - } - }); -} - -export async function setDockerCompatibilityFeature(page: Page, enable: boolean): Promise { - //Open the preferences bar and verify DC preferences page - const settingsBar = new SettingsBar(page); - - if (await settingsBar.preferencesTab.isHidden()) { - //Open settings if not opened already - const navigationBar = new NavigationBar(page); - await navigationBar.openSettings(); - } - - await settingsBar.expandPreferencesTab(); - - const DCPreferencesLink = settingsBar.getLinkLocatorByHref('/preferences/default/preferences.dockerCompatibility'); - await playExpect(DCPreferencesLink).toBeVisible(); - await DCPreferencesLink.click(); - const DCPreferencesPage = new PreferencesPage(page); - - await playExpect(DCPreferencesPage.heading).toBeVisible(); - const experimentalTitle = DCPreferencesPage.content.getByText('Docker Compatibility', { exact: true }); - await playExpect(experimentalTitle).toBeVisible(); - - //Set the feature - const dockerCompatibilityCheckbox = DCPreferencesPage.content.getByRole('checkbox', { - name: 'Enable the section for Docker compatibility.', - }); - await playExpect(dockerCompatibilityCheckbox).toBeVisible(); - const isEnabled = await dockerCompatibilityCheckbox.isChecked(); - if (isEnabled !== enable) { - await dockerCompatibilityCheckbox.locator('..').setChecked(enable); - const isEnabled = await dockerCompatibilityCheckbox.isChecked(); - playExpect(isEnabled).toEqual(enable); - } - - //Verify the main docker compatibility page (dis)appeared - const DCSettingsLink = settingsBar.getLinkLocatorByHref('/preferences/docker-compatibility'); - if (enable) { - await playExpect(DCSettingsLink).toBeVisible(); - } else { - await playExpect(DCSettingsLink).not.toBeVisible(); - } - - //Close the preferences bar - await settingsBar.expandPreferencesTab(); -} - -export async function setStatusBarProvidersFeature( - page: Page, - navigationBar: NavigationBar, - enable: boolean, -): Promise { - await navigationBar.openSettings(); - const settingsBar = new SettingsBar(page); - const experimentalPage = await settingsBar.openTabPage(ExperimentalPage); - await experimentalPage.setExperimentalCheckbox(experimentalPage.statusBarProvidersCheckbox, enable); -} - -function isRootlessPodman(): boolean { - try { - let output: string; - - if (isMac || isWindows) { - output = execSync(`podman machine ssh podman info --format json`).toString(); - } else if (isLinux) { - output = execSync(`podman info --format json`).toString(); - } else { - throw new Error('Unsupported platform'); - } - const info = JSON.parse(output); - return info?.host?.security?.rootless === true; - } catch (err) { - throw new Error(`Failed to determine Podman rootless mode: ${err}`); - } -} - -function getPodmanVolumePath(volumeName: string, fileName: string): string { - const relativePath = `${volumeName}/_data/${fileName}`; - const isRootless = isRootlessPodman(); - - if (isMac || isWindows) { - const base = isRootless ? `.local/share/containers/storage/volumes` : '/var/lib/containers/storage/volumes'; - return `${base}/${relativePath}`; - } - - if (isLinux) { - const base = isRootless - ? `${os.homedir()}/.local/share/containers/storage/volumes` - : '/var/lib/containers/storage/volumes'; - return `${base}/${relativePath}`; - } - - throw new Error('Unsupported platform'); -} - -export async function readFileInVolumeFromCLI(volumeName: string, fileName: string): Promise { - return test.step('Read file in volume from CLI', async () => { - try { - const fullPath = getPodmanVolumePath(volumeName, fileName); - - const command = isMac || isWindows ? `podman machine ssh sudo cat ${fullPath}` : `cat ${fullPath}`; - - // eslint-disable-next-line sonarjs/os-command - const output = execSync(command); - return output.toString(); - } catch (error) { - throw new Error(`Error reading file: ${fileName} in volume: ${volumeName} from CLI: ${error}`); - } - }); -} diff --git a/tests/playwright/src/utility/platform.ts b/tests/playwright/src/utility/platform.ts deleted file mode 100644 index a28c328edf..0000000000 --- a/tests/playwright/src/utility/platform.ts +++ /dev/null @@ -1,26 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 os from 'node:os'; - -export const isLinux = os.platform() === 'linux'; -export const isMac = os.platform() === 'darwin'; -export const isWindows = os.platform() === 'win32'; -export const archType = os.arch(); - -export const isCI = process.env.CI ? process.env.CI === 'true' : false; diff --git a/tests/playwright/src/utility/wait.ts b/tests/playwright/src/utility/wait.ts deleted file mode 100644 index cf38a8d772..0000000000 --- a/tests/playwright/src/utility/wait.ts +++ /dev/null @@ -1,132 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 { Page } from '@playwright/test'; -import test, { expect as playExpect } from '@playwright/test'; - -import { NavigationBar } from '../model/workbench/navigation'; -import { createPodmanMachineFromCLI } from './operations'; - -export async function wait( - waitFunction: () => Promise, - until: boolean, - timeout: number, - diff: number, - sendError: boolean, - errorMessage: string, -): Promise { - return test.step(`Wait for condition ${waitFunction.name} to become '${String(until)}'`, async () => { - let time = timeout; - while (time > 0) { - const waitFuncResult = await waitFunction(); - if (waitFuncResult === until) { - return; - } - time = time - diff; - await delay(diff); - } - const message = - errorMessage.length === 0 - ? `Timeout (${timeout} ms) was reach while waiting for condition (${waitFunction.name}) to become '${String( - until, - )}'` - : errorMessage; - if (sendError) { - throw Error(message); - } - }); -} - -/** - * Waiting function that tests the condition (callback) to become true until timeout is reached, - * error is thrown if not fulfilled - * @param waitFunction a callback returning Promise - * @param timeout timeout in ms - * @param diff a diff (number) in ms defining the delay between each cycle - * - * Example: await waitUntil(() => dialogIsOpen(), 1000, 250, 'Dialog window was not openend as expected in 1 s'); - */ -export async function waitUntil( - waitFunction: () => Promise, - { - timeout = 5_000, - diff = 500, - sendError = true, - message = '', - }: { - timeout?: number; - diff?: number; - sendError?: boolean; - message?: string; - } = {}, -): Promise { - await wait(waitFunction, true, timeout, diff, sendError, message); -} - -/** - * Waiting function that tests the condition (callback) to become false until timeout is reached, - * error is thrown if not fulfilled - * @param waitFunction a callback returning Promise - * @param timeout timeout in ms - * @param diff a diff (number) in ms defining the delay between each cycle - * - * Example: await waitWhile(() => dialogIsOpen(), 1000, 250, 'Dialog window was not closed as expected in 1 s'); - */ -export async function waitWhile( - waitFunction: () => Promise, - { - timeout = 5_000, - diff = 500, - sendError = true, - message = '', - }: { - timeout?: number; - diff?: number; - sendError?: boolean; - message?: string; - } = {}, -): Promise { - await wait(waitFunction, false, timeout, diff, sendError, message); -} - -/** - * Standard delay function - * @param ms number of ms to sleep for - * @returns promise void - */ -export async function delay(ms: number): Promise { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -} - -export async function waitForPodmanMachineStartup(page: Page, timeoutOut = 30_000): Promise { - return test.step('Wait for Podman machine to be running', async () => { - await createPodmanMachineFromCLI(); - - const dashboardPage = await new NavigationBar(page).openDashboard(); - await playExpect(dashboardPage.heading).toBeVisible(); - await waitUntil(async () => await dashboardPage.podmanStatusLabel.isVisible(), { - timeout: timeoutOut, - sendError: false, - }); - await playExpect(dashboardPage.podmanStatusLabel).toHaveText('RUNNING', { - timeout: timeoutOut, - }); - }); -} diff --git a/tests/playwright/src/utils/app-ready.ts b/tests/playwright/src/utils/app-ready.ts new file mode 100644 index 0000000000..7712222e5d --- /dev/null +++ b/tests/playwright/src/utils/app-ready.ts @@ -0,0 +1,111 @@ +/********************************************************************** + * 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 { expect, type Locator, type Page } from '@playwright/test'; +import { type DialogOptions, SELECTORS, TIMEOUTS } from 'src/model/core/types'; + +export async function waitForAppReady(page: Page, timeout = TIMEOUTS.DEFAULT): Promise { + try { + await expect(page.locator(SELECTORS.MAIN_ANY).first()).toBeVisible({ timeout }); + } catch (error) { + const url = page.url(); + const title = await page.title().catch(() => 'Unable to get title'); + const html = await page.content().catch(() => 'Unable to get content'); + console.error('Failed to find main element. Page state:', { url, title, htmlLength: html.length }); + throw error; + } + await waitForInitializingScreenToDisappear(page); + await expect(page.locator(SELECTORS.MAIN_APP_CONTAINER)).toBeVisible({ timeout }); + await expect(page.locator(SELECTORS.TITLE_BAR)).toBeVisible({ timeout }); + await handleWelcomePageIfPresent(page); +} + +export async function waitForNavigationReady(page: Page, timeout = TIMEOUTS.DEFAULT): Promise { + await waitForAppReady(page, timeout); + await expect(page.getByRole(SELECTORS.NAVIGATION.role, { name: SELECTORS.NAVIGATION.name })).toBeVisible({ + timeout, + }); +} + +async function waitForInitializingScreenToDisappear(page: Page): Promise { + const initializingScreen = page.locator(SELECTORS.MAIN_INITIALIZING); + await expect(initializingScreen).toBeHidden({ timeout: TIMEOUTS.INITIALIZING_SCREEN }); +} + +async function handleWelcomePageIfPresent(page: Page, timeout = 5_000): Promise { + const welcomePage = page.locator(SELECTORS.WELCOME_PAGE).first(); + + try { + await expect(welcomePage).not.toBeVisible({ timeout: timeout }); + } catch { + const skipButton = page.getByRole('button', { name: 'Skip' }); + await skipButton.click(); + await expect(welcomePage).toBeHidden({ timeout: TIMEOUTS.STANDARD }); + } +} + +export async function handleDialogIfPresent( + page: Page, + { + dialogName = 'Confirmation', + buttonName = 'Yes', + timeout = 5_000, + throwErrorOnFailOrMissing = false, + waitForDialogToDisappear = true, + }: DialogOptions = {}, +): Promise { + const dialog = page.getByRole('dialog', { name: dialogName, exact: true }); + + try { + await expect(dialog).toBeVisible({ timeout }); + } catch (error) { + if (throwErrorOnFailOrMissing) { + throw new Error( + `Dialog "${dialogName}" not found within ${timeout}ms: ${error instanceof Error ? error.message : String(error)}`, + ); + } + return false; + } + + try { + const button = dialog.getByRole('button', { name: buttonName, exact: true }); + await expect(button).toBeEnabled({ timeout }); + await button.click(); + + if (waitForDialogToDisappear) { + await expect(dialog).toBeHidden({ timeout }); + } + + return true; + } catch (error) { + const errorMessage = `Failed to interact with dialog "${dialogName}" button "${buttonName}": ${ + error instanceof Error ? error.message : String(error) + }`; + console.error(errorMessage); + + if (throwErrorOnFailOrMissing) { + throw new Error(errorMessage); + } + return false; + } +} + +export async function clearAllToasts(page: Page, toastLocator: Locator, timeout = 10_000): Promise { + await page.keyboard.press('Escape'); + await expect(toastLocator).toHaveCount(0, { timeout }); +} diff --git a/tests/playwright/tsconfig.json b/tests/playwright/tsconfig.json index d31024e082..a6cffe2293 100644 --- a/tests/playwright/tsconfig.json +++ b/tests/playwright/tsconfig.json @@ -1,25 +1,14 @@ { "compilerOptions": { - "module": "esnext", "target": "esnext", - "sourceMap": true, - "moduleResolution": "Node", - "declaration": true, - "outDir": "dist", - "rootDirs": ["src", "../../playwright.config.ts"], - "skipLibCheck": true, - "allowSyntheticDefaultImports": true, - "resolveJsonModule": true, + "module": "esnext", + "moduleResolution": "node", "strict": true, - "isolatedModules": true, - - "types": ["node"], - + "preserveValueImports": false, + "skipLibCheck": true, "baseUrl": ".", - "paths": { - "/@/*": ["src/*"] - } + "resolveJsonModule": true }, - "include": ["src/**/*.ts", "../types/**/*.d.ts", "../../playwright.config.ts"], - "exclude": ["node_modules"] + "include": ["src/**/*.ts", "playwright.config.ts"], + "exclude": ["node_modules/**"] } diff --git a/tests/playwright/vite.config.js b/tests/playwright/vite.config.js deleted file mode 100644 index f474156c1b..0000000000 --- a/tests/playwright/vite.config.js +++ /dev/null @@ -1,61 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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'; -import typescript from '@rollup/plugin-typescript'; - -const PACKAGE_ROOT = __dirname; -const PACKAGE_NAME = '@podman-desktop/tests-playwright'; - -/** - * @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') + '/', - }, - }, - plugins: [typescript()], - build: { - sourcemap: true, - target: 'esnext', - outDir: 'dist', - assetsDir: '.', - lib: { - entry: 'src/index.ts', - formats: ['es'], - name: PACKAGE_NAME, - }, - // emptyOutDir: true, - reportCompressedSize: false, - rollupOptions: { - external: ['electron', '@playwright/test', ...builtinModules.flatMap(p => [p, `node:${p}`])], - output: { - entryFileNames: '[name].js', - }, - }, - }, -}; - -export default config; diff --git a/vitest.config.js b/vitest.config.js index 5427afb23f..567ef1a217 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -29,10 +29,7 @@ const PODMAN_DESKTOP_EXCLUDED = [ */ export default defineConfig({ test: { - workspace: [ - '{extensions,packages,tools,storybook,website,scripts}/**/{vitest,vite}.config.{js,ts}', - '!**/builtin/**', - ], + workspace: ['{extensions,packages,tools,scripts}/**/{vitest,vite}.config.{js,ts}', '!**/builtin/**'], // use GitHub action reporters when running in CI reporters: process.env.CI ? [['junit', { includeConsoleOutput: false }], 'default'] : ['default'], outputFile: process.env.CI ? { junit: 'coverage/junit-results.xml' } : {}, diff --git a/website-argos/package.json b/website-argos/package.json deleted file mode 100644 index 1c246314d7..0000000000 --- a/website-argos/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "website-argos", - "version": "0.0.0", - "private": true, - "scripts": { - "screenshot": "playwright test", - "upload": "npx @argos-ci/cli upload ./screenshots" - }, - "devDependencies": { - "@argos-ci/cli": "^3.0.0", - "@argos-ci/playwright": "^6.0.0", - "@playwright/test": "1.54.2", - "cheerio": "^1.1.2" - } -} diff --git a/website-argos/playwright.config.ts b/website-argos/playwright.config.ts deleted file mode 100644 index 8b2ac68947..0000000000 --- a/website-argos/playwright.config.ts +++ /dev/null @@ -1,38 +0,0 @@ -/********************************************************************** - * 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 type { PlaywrightTestConfig } from '@playwright/test'; -import { devices } from '@playwright/test'; - -const config: PlaywrightTestConfig = { - webServer: { - cwd: '../website', - port: 3000, - command: 'pnpm serve', - }, - projects: [ - { - name: 'chromium', - use: { - ...devices['Desktop Chrome'], - }, - }, - ], -}; - -export default config; diff --git a/website-argos/screenshot.css b/website-argos/screenshot.css deleted file mode 100644 index f8470e883e..0000000000 --- a/website-argos/screenshot.css +++ /dev/null @@ -1,31 +0,0 @@ -/* Iframes can load lazily */ -iframe, -/* Avatars can be flaky due to using external sources: GitHub/Unavatar */ - .avatar__photo, -/* Gifs load lazily and are animated */ - img[src$='.gif'], -/* Algolia keyboard shortcuts appear with a little delay */ -.DocSearch-Button-Keys > kbd, -/* The live playground preview can often display dates/counters */ - [class*='playgroundPreview'] { - visibility: hidden; -} - -/* Different docs last-update dates can alter layout */ -.theme-last-updated, -/* Mermaid diagrams are rendered client-side and produce layout shifts */ - .docusaurus-mermaid-container { - display: none; -} - -/* Hide the video element that usually contains 'loading' spinner */ -video { - visibility: hidden; - height: 0px !important; - width: 0px !important; -} - -/* Hide the GitHub stars badge, since this dynamically changes, it would create false errors in Argos */ -#github-stars-button { - visibility: hidden; -} diff --git a/website-argos/screenshot.spec.ts b/website-argos/screenshot.spec.ts deleted file mode 100644 index ba049ba028..0000000000 --- a/website-argos/screenshot.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -/********************************************************************** - * 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 * as fs from 'node:fs'; - -import { argosScreenshot } from '@argos-ci/playwright'; -import { test } from '@playwright/test'; - -import { extractSitemapPathnames, pathnameToArgosName } from './utils'; - -// Constants -const siteUrl = 'http://localhost:3000'; -const sitemapPath = '../website/build/sitemap.xml'; -const stylesheetPath = './screenshot.css'; -const stylesheet = fs.readFileSync(stylesheetPath).toString(); - -// Wait for hydration, requires Docusaurus v2.4.3+ -// Docusaurus adds a once hydrated -// See https://github.com/facebook/docusaurus/pull/9256 -function waitForDocusaurusHydration(): boolean { - return document.documentElement.dataset.hasHydrated === 'true'; -} - -function screenshotPathname(pathname: string): void { - test(`pathname ${pathname}`, async ({ page }) => { - test.slow(); - const url = siteUrl + pathname; - await page.goto(url); - await page.waitForFunction(waitForDocusaurusHydration); - - // for downloads page, wait for the version being fetched - if (pathname.includes('/downloads')) { - // wait for the version being fetched during 5seconds using async setTimeout - await new Promise(resolve => setTimeout(resolve, 5000)); - } - - await page.addStyleTag({ content: stylesheet }); - await argosScreenshot(page, pathnameToArgosName(pathname)); - }); -} - -test.describe('Docusaurus site screenshots', () => { - const pathnames = extractSitemapPathnames(sitemapPath); - console.log('Pathnames to screenshot:'); - pathnames.forEach(pathname => console.log(`- ${pathname}`)); - pathnames.forEach(screenshotPathname); -}); diff --git a/website-argos/tsconfig.json b/website-argos/tsconfig.json deleted file mode 100644 index c584a07d4c..0000000000 --- a/website-argos/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - "target": "esnext", - "module": "esnext", - "moduleResolution": "Node", - "sourceMap": true, - "outDir": "./dist", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true - } -} diff --git a/website-argos/utils.spec.ts b/website-argos/utils.spec.ts deleted file mode 100644 index e93833963c..0000000000 --- a/website-argos/utils.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { expect, test } from '@playwright/test'; - -import { extractSitemapPathnames } from './utils'; - -const sitemapPath = '../website/build/sitemap.xml'; - -test('check /blog is not included', () => { - const allURLs = extractSitemapPathnames(sitemapPath); - - // Check that /blog is not present - expect(allURLs).not.toContain('/blog'); -}); diff --git a/website-argos/utils.ts b/website-argos/utils.ts deleted file mode 100644 index d017d6f219..0000000000 --- a/website-argos/utils.ts +++ /dev/null @@ -1,58 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 fs from 'node:fs'; - -import * as cheerio from 'cheerio'; - -const allowed: string[] = [ - '/api/namespaces/commands', - '/api/enumerations/ProgressLocation', - '/api/classes/Disposable', - '/api/interfaces/Command', - '/api/type-aliases/StatusBarAlignment', - '/api/variables/StatusBarItemDefaultPriority', -]; - -// Extract a list of pathnames, given a fs path to a sitemap.xml file -// Docusaurus generates a build/sitemap.xml file for you! -export function extractSitemapPathnames(sitemapPath: string): string[] { - const sitemap = fs.readFileSync(sitemapPath).toString(); - const $ = cheerio.load(sitemap, { xmlMode: true }); - let urls: string[] = []; - $('loc').each(function handleLoc() { - urls.push($(this).text()); - }); - - // filter out all /tags/ pages and /blog page - urls = urls.filter(url => !url.includes('/tags/') && !url.endsWith('/tags')).filter(url => !url.endsWith('/blog')); - - let pathnames = urls.map(url => new URL(url).pathname); - - // filter out all /api pages since they are auto generated - // keeping an example of each type (namespace, enum, class, interface, type, variable) to avoid regression. - pathnames = pathnames.filter(url => !url.startsWith('/api') || allowed.includes(url)); - - return pathnames; -} - -// Converts a pathname to a decent screenshot name -export function pathnameToArgosName(pathname: string): string { - // eslint-disable-next-line sonarjs/anchor-precedence - return pathname.replace(/^\/|\/$/g, '') || 'index'; -} diff --git a/website/.gitignore b/website/.gitignore deleted file mode 100644 index b7e1fe32d1..0000000000 --- a/website/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Dependencies -/node_modules - -# Production -/build - -# Generated -/api - -# Generated files -.docusaurus -.cache-loader -static/release-notes - -# Cache -.eslintcache - -# Misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* -/src/pages/storybook/sidebar.cjs diff --git a/website/.prettierignore b/website/.prettierignore deleted file mode 100644 index a77982df4c..0000000000 --- a/website/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -.docusaurus -/api/ -/build/ -/node_modules/ diff --git a/website/babel.config.js b/website/babel.config.js deleted file mode 100644 index e00595dae7..0000000000 --- a/website/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], -}; diff --git a/website/blog/2022-11-17-develop-podman-using-codespaces.md b/website/blog/2022-11-17-develop-podman-using-codespaces.md deleted file mode 100644 index ca996bae36..0000000000 --- a/website/blog/2022-11-17-develop-podman-using-codespaces.md +++ /dev/null @@ -1,310 +0,0 @@ ---- -title: Build & run Podman Desktop in a DevContainer -description: Develop Podman Desktop using a DevContainer locally or using GitHub Codespaces. -slug: develop-using-devcontainer -authors: [benoitf] -tags: [podman-desktop, devcontainer, codespaces] -hide_table_of_contents: false ---- - -GitHub [announced last week](https://github.blog/changelog/2022-11-09-codespaces-for-free-and-pro-accounts/) that Codespaces is available for everyone and it includes free minutes. - -Let see how we can use a [Development Container](https://containers.dev/) having all the tools to build and run Podman Desktop. The Development Container works locally using Visual Studio Code but in this blog post we will see how it works directly with a simple click from GitHub. - -The challenges are to run a desktop tool (Podman Desktop) and running a container engine (Podman) inside this Development Container without using too many memory ! - - - -## Defining image of the container - -The first thing is in the choice of the image for the container. It is possible to pick-up a default image and add some features but there is no existing feature for Podman at [https://github.com/devcontainers/features/tree/main/src](https://github.com/devcontainers/features/tree/main/src) and most of the features are expecting to run on top of Debian/Ubuntu - -If you are not interested in how to setup the image, jump to the [next section](#configure-the-devcontainer-using-devcontainerjson). - -Podman binaries are available quickly after the releases for Fedora. I decided then to use `Fedora 37` as the base image. - -Let start the Containerfile using: - -```docker -FROM quay.io/fedora/fedora:37 -``` - -Then I install Node.js 16 from official nodejs.org repository. It's easier to switch to the version that we need. - - - -```docker -# install Node.js + yarn -ENV NODE_VERSION 16.18.1 -RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" && \ - tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 && \ - rm "node-v$NODE_VERSION-linux-x64.tar.gz" && \ - npm install -g yarn -``` - -Now, all system dependencies used to run an Electron application needs to be installed. - -Podman is also installed so we can run some containers inside this container. - -And of course, we need to install VNC (I choose [tigervnc](https://tigervnc.org/)) with a light Window Manager ([fluxbox](http://fluxbox.org/)). - -To connect to the display of the container, we need to expose VNC over HTML/websocket using [noVNC](https://novnc.com/) - -xterm is installed to start a terminal from the VNC side. - -```docker -RUN dnf -y update && \ - yum -y reinstall shadow-utils && \ - yum install -y git \ - # dependencies for Podman Desktop - nss \ - atk \ - at-spi2-atk \ - cups-libs \ - gtk3 \ - # for remote Display - fluxbox \ - tigervnc-server \ - xorg-x11-fonts-Type1 \ - novnc \ - supervisor \ - xdpyinfo \ - # for podman - podman \ - fuse-overlayfs --exclude container-selinux \ - xterm && \ - rm -rf /var/cache /var/log/dnf* /var/log/yum.* -``` - -Supervisord setup the launch of the VNC server and the Window manager - -```docker -COPY supervisord.conf /etc/supervisord.conf -``` - -A custom theme for fluxbox: - -```docker -COPY fluxbox /usr/share/fluxbox/init -``` - -Then we need a special configuration to allow to have Podman working inside the container - -We add the `podman-desktop` user with correct range on subuid and subgid when running containers. I used the [tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md#etcsubuid-and-etcsubgid-configuration). - -```docker -RUN useradd -u 1000 podman-desktop && echo podman-desktop:10000:5000 > /etc/subuid && echo podman-desktop:10000:5000 > /etc/subgid -``` - -Then use some default configuration files - -```docker -# initialize conf files -ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf -ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman-desktop/.config/containers/containers.conf -``` - -and make sure that all permissions are correct following the guide [https://www.redhat.com/sysadmin/podman-inside-container](https://www.redhat.com/sysadmin/podman-inside-container) - -```docker -# set permissions -RUN chown podman-desktop:podman-desktop -R /home/podman-desktop && chmod 644 /etc/containers/containers.conf && \ - mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers /var/lib/shared/vfs-images /var/lib/shared/vfs-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock; touch /var/lib/shared/vfs-images/images.lock; touch /var/lib/shared/vfs-layers/layers.lock && \ - mkdir -p /run/user/1000 && chown podman-desktop:podman-desktop /run/user/1000 -``` - -plus define an empty user namespace. - -```docker -ENV _CONTAINERS_USERNS_CONFIGURED="" -``` - -Make sure Podman will create the socket in an expected directory: - -```docker -# socket path for podman -ENV XDG_RUNTIME_DIR=/run/user/1000 -``` - -OK ! we have a custom Containerfile providing all the tools to build and run Podman Desktop (using VNC for the display), run Podman and run Electron. - -The current file is available at https://github.com/containers/podman-desktop/blob/main/.devcontainer/.parent/Containerfile - -Let's configure the DevContainer. - -## Configure the DevContainer using devcontainer.json - -DevContainer definition is stored at `.devcontainer/devcontainer.json` file. - -We need to reuse the image of the previous step. For that let's use the build section of the `devcontainer.json` file. - -```json -"build": { - "dockerfile": "Containerfile" -}, -``` - -In order to avoid to redo all the build steps each time we open a workspace using this dev container, we published the image to quay.io at [quay.io/podman-desktop/devcontainer-parent:next](https://quay.io/repository/podman-desktop/devcontainer-parent?tab=tags&tag=next). This parent image is not changing much so it's better to use is as a parent one. - -Inside `.devcontainer` directory there is a `.parent` directory with everything related to the parent image. - -And in the `.devcontainer/Containerfile` file we reference this image - -```docker -FROM quay.io/podman-desktop/devcontainer-parent:next -``` - -By default, we will be `root` in the container and this is probably not what we expect. Let's change that. - -```json -"containerUser": "podman-desktop" -``` - -Some Visual Studio Code extensions are nice to use and we can add them - -```json - "extensions": ["svelte.svelte-vscode", "bradlc.vscode-tailwindcss"] -``` - -Then here is the tricky part, how to run our container allowing to run again inside the container some containers with podman. - -We specify the arguments to make it possible. It's possible to use `--privileged` flag but I prefer to list the subset of permissions. -Using `--privileged` we don't really know what are the privilege that are required while specifying all of them, people are aware of what is granted/denied. - -```json -"runArgs": [ - "--cap-add=sys_admin", - "--security-opt", - "seccomp=unconfined", - "--device", - "/dev/fuse", - "--security-opt", - "label=disable", - "--security-opt", - "apparmor=unconfined" - ], -``` - -Source code of Podman Desktop needs to be editable within the DevContainer so it needs to be mounted. - -```json -"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", -"workspaceFolder": "/workspace", -``` - -Then we need a command to build Podman Desktop. - -For that, we use `onCreateCommand` hook with a custom command - -```json -"onCreateCommand": "${containerWorkspaceFolder}/.devcontainer/onCreateCommand.sh", -``` - -and in the `.devcontainer` folder the `onCreateCommand.sh` script is the following - -```shell -#!/bin/sh -yarn - -MODE=production yarn run build && yarn run electron-builder build --linux --dir --config .electron-builder.config.cjs -``` - -Two instructions: - -1. Fetch all Node.js dependencies. -2. build Podman Desktop in the `dist` folder using `Linux` as target Operating System. - -After the start of the container, how to launch Podman Desktop, the website and VNC, etc ? - -Just use `postStartCommand` hook. - -```json -"postStartCommand": "${containerWorkspaceFolder}/.devcontainer/postStartCommand.sh", -``` - -and in the `.devcontainer` folder the `postStartCommand.sh` script is the following: - -```shell -#!/bin/sh - -# Start all services -/usr/bin/supervisord -c /etc/supervisord.conf & - -# wait X server to be ready or after 2mn exit -echo "Waiting for X server to be ready" -timeout 120 bash -c 'until xdpyinfo -display :0 &> /dev/null; do printf "."; sleep 1; done' - -# launch podman desktop -echo "Launching Podman Desktop" -cd dist/linux-unpacked/&& ./podman-desktop & - -# Launch the 9000 redirect after 20 seconds -sleep 20 -websockify --web=/usr/share/novnc localhost:9000 localhost:5900 & - -# launch the website rendering -echo "Launching Website" -cd website && yarn start -``` - -It starts VNC and noVNC, start precompiled Podman Desktop and start the documentation rendering. - -It is not launching the Watch mode/development mode of Podman Desktop as it requires a container having more than 8GB of memory. - -Picking up a larger instance with for example 16GB, it's possible to use development mode. - -Of course, to make VNC happy, we need to specify the `DISPLAY` environment variable. - -```json -"remoteEnv": { - "DISPLAY": ":0" -} -``` - -When the DevContainer is fully available, we want to have a way to quickly open the `Website rendering URL` and `noVNC` - -Let's tweak the `devcontainer.json` file by adding the `portsAttributes` section - -```json -"portsAttributes": { - "9000": { - "label": "vnc", - "onAutoForward": "openPreview" - }, - "3000": { - "label": "website" - } -} -``` - -After all post-creation steps, the `Preview` browser inside the VS Code editor will open a window to VNC. And another port (`3000`) is flagged for the website. - -## Using the DevContainer.json on Github Codespace - -As a user, opening a workspace with all what we configured is done using a single click. - -Go to https://github.com/containers/podman-desktop then click on the `< > Code` dropdown and click on `Create codespace on main` button. - -![Open Codespace](img/develop-podman-using-codespaces/codespaces-click-repository.png) - -Once you click on the button, the codespace is setting up: - -![Preparing Codespace](img/develop-podman-using-codespaces/codespaces-preparing-codespace.png) - -After few minutes, as there is not yet [prebuilt codespaces](https://docs.github.com/en/codespaces/prebuilding-your-codespaces/about-github-codespaces-prebuilds), the codespace is opening. - -The simple Browser displays the noVNC window: -![Opening Codespace](img/develop-podman-using-codespaces/codespaces-open-novnc.png) - -Click on the connect button. Then on the terminal you can enter `podman run quay.io/podman/hello` and the container is detected in Podman Desktop. -![Testing Codespace](img/develop-podman-using-codespaces/codespaces-testing-podman-desktop.png) - -It's also possible using the port widget to get on `3000` port by clicking on the world icon a preview of the website in another tab. Changing source code of the website will refresh the content of the window. - -Depending on the usecase, it's also possible to open documentation in the preview browser. - -![Edit website Codespace](img/develop-podman-using-codespaces/codespaces-edit-website.png) - -## Conclusion - -The DevContainer image for Podman Desktop is recent so it'll probably evolve over time by adding new capabilities but it allows you to easily build/run/experiment and **contribute** to the tool or the website. diff --git a/website/blog/2022-12-01-release-0.10-blog.md b/website/blog/2022-12-01-release-0.10-blog.md deleted file mode 100644 index c85db7d161..0000000000 --- a/website/blog/2022-12-01-release-0.10-blog.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.10 -description: Podman Desktop 0.10 has been released! New Create Container Wizard, improved Kubernetes workflows, revamped registries configuration, and more! -slug: podman-desktop-release-0.10 -authors: [deekay2310] -tags: [podman-desktop, release, kubernetes] -hide_table_of_contents: false ---- - -This release note covers Podman Desktop 0.10 release changes. - -- **Containers Configuration**: Container creation wizzard allowing to define environment variables, networking and more configuration options. -- **Kubernetes Improvements**: Play Kubernetes YAML, custom Kubeconfig path support, reload of kube context. -- **Registries Configuration**: Revamped registries configuration UI. -- **Podman Version**: Podman 4.3.1 now included in Windows and Mac installers. -- **UX/UI Improvements**: Improved lists, better contrast, and more. - -Podman Desktop 0.10 is now available. [Click here to download it](/downloads)! - - - ---- - -## Release Details - -### New configuration wizzard to create containers ([#773](https://github.com/containers/podman-desktop/pull/773)) - -Until now, we could only specify port binding when building images to start containers. The create container wizzard now includes several options enabling configuring volumes, environment variables, restart policy and settings on networking and security. It is also possible to reuse an existing (and already configured) a network when creating containers. - -![new container creation wizard](img/podman-desktop-release-0.10/new_container_creation_wizard.gif) - -### Kubernetes Capabilities Improvements - -**Custom Kubeconfig file path ([#780](https://github.com/containers/podman-desktop/pull/780))** - -Kubeconfig path location is now configurable from ** Settings > Preferences > Kubernetes: Kubeconfig** and can be set to a custom path. By default, Podman Desktop use the path `$HOME/.kube/config` for the Kubeconfig file. - -![custom kubeconfig file path](img/podman-desktop-release-0.10/custom-kubeconfig.png) - -**Play Kubernetes YAML from the Pods list ([#739](https://github.com/containers/podman-desktop/pull/739))** - -Podman Desktop enables to play existing Kubernetes YAML files. This is now available from `Play Kubernetes YAML` button added to the Pods list, in addition to the Containers list. - -![play k8s yaml](img/podman-desktop-release-0.10/play_k8s_yaml.gif) - -**Kube context automatically reloaded when updated ([#813](https://github.com/containers/podman-desktop/pull/813))** - -Any change to the kube context will now be detected by Podman Desktop in its running state. The system tray, allowing to select which Kubernetes environment to work with, will now reload the kube context without restarting Podman Desktop. - -### Revamped Container Registries UI ([#446](https://github.com/containers/podman-desktop/issues/446)) - -The registries configuration UI has been revamped. Instead of using tiles for displaying the registries, now it uses a list where each registry can be added or edited directly in the list. The kebab menu provides options to edit or remove a registry from the list. - -![registries](img/podman-desktop-release-0.10/registries.png) - -### Update to Podman 4.3.1 ([#913](https://github.com/containers/podman-desktop/issues/913)) - -Podman Desktop 0.10 is now embedding [Podman 4.3.1](https://github.com/containers/podman/releases/tag/v4.3.1) in Windows and macOS installers. - -### UI/UX Improvements - -**Improved lists UX ([#877](https://github.com/containers/podman-desktop/pull/877))** - -To reduce the width used in the lists when displaying all the icons, main actions icons are now always displayed (before it was on hover) and the secondary actions are displayed in a kebab menu. For example Start/Stop and delete are primary actions while open the browser, inspect, generate kube yaml, etc. are secondary options. - -![kebab menu](img/podman-desktop-release-0.10/kebab-menu.png) - -**Delay appearance of text in navbar ([#767](https://github.com/containers/podman-desktop/pull/767))** - -When minimizing or expanding the navbar, the text was overlapping for a few second the page. Now it's displayed with a fading animation. - -**Selected state: better text color contrast ([#802](https://github.com/containers/podman-desktop/pull/802))** - -In the `Images` page, on hover for both the image and the name column, the text contrast has been increased for better visibility: violet indicates that you have selected it. - -![text color when hover](img/podman-desktop-release-0.10/text-color.gif) - -**On Linux and Windows, the menu bar is now hidden by default ([#668](https://github.com/containers/podman-desktop/pull/668))** - -With this change, the menu bar looks more integrated to the Operating System. The menu can appear if you hit the `Alt` key. Thanks to [Dylan M. Taylor](https://github.com/dylanmtaylor) for contributing to fix this. - -**Exit when clicking on the close icon of the dashboard on Linux ([#671](https://github.com/containers/podman-desktop/pull/671))** - -For most users on Linux, there is no tray icon support. In this situation, when closed, Podman Desktop continues to run in background. [Dylan M. Taylor](https://github.com/dylanmtaylor) added a property to exit the app by default when clicking on the close icon. For people having tray icon support or with the desire of a different behavior, it is possible to disable it from the Settings. - ---- - -## Other Notable Enhancements - -- Rename `Started` into `Age` in Containers list.([#878](https://github.com/containers/podman-desktop/pull/878)) -- Added domain validation when adding a registry. ([#838](https://github.com/containers/podman-desktop/pull/838)) -- Provided a consistent behavior with Podman CLI, `podman-machine-default` is now the default name when creating a Podman machine from Podman Desktop. ([#775](https://github.com/containers/podman-desktop/pull/775)) - ---- - -## Notable bug fixes - -- Fixed error handling when pulling images of Desktop Extensions. ([#782](https://github.com/containers/podman-desktop/pull/782)) -- Fixed use of the full height of the screen when displaying log in the details of a container. ([#946](https://github.com/containers/podman-desktop/discussions/946)) -- Fixed First start of Podman Desktop on flatpak was not seeing the podman engine ([#860](https://github.com/containers/podman-desktop/pull/860)) - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop 0.10 even better: - -- [Dylan M. Taylor](https://github.com/dylanmtaylor) in [#671](https://github.com/containers/podman-desktop/pull/671) -- [Fionn Kelleher](https://github.com/osslate) in [#713](https://github.com/containers/podman-desktop/pull/713) -- [Rostislav Svoboda](https://github.com/rsvoboda) in [#737](https://github.com/containers/podman-desktop/pull/737) -- [Denis Shemanaev](https://github.com/shemanaev) in [#783](https://github.com/containers/podman-desktop/pull/783) -- [Fabrice Flore-Thébault](https://github.com/themr0c) in [#856](https://github.com/containers/podman-desktop/pull/856) -- [Kevin](https://github.com/KevinAtSesam) in [#864](https://github.com/containers/podman-desktop/pull/864) -- [@sfrunza13](https://github.com/sfrunza13) in [#872](https://github.com/containers/podman-desktop/pull/872) -- [Anjan Nath](https://github.com/anjannath) in [#918](https://github.com/containers/podman-desktop/pull/918) - -## Final Notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.10.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-01-18-release-0.11.md b/website/blog/2023-01-18-release-0.11.md deleted file mode 100644 index a006a40187..0000000000 --- a/website/blog/2023-01-18-release-0.11.md +++ /dev/null @@ -1,242 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.11 -description: Podman Desktop 0.11 has been released! -slug: podman-desktop-release-0.11 -authors: [slemeur] -tags: [podman-desktop, release, kubernetes] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' - -This release note covers Podman Desktop 0.11 release changes. - -- **Air-Gapped Installation**: New all-in-one binaries for air-gapped installation. -- **Feedback**: Submit feedback directly from Podman Desktop. -- **Docker Compatibility Mode**: Information about the Docker compatibility mode. -- **Proxy Setting**: Toggle on/off the proxy setting. -- **Deploy to Kubernetes**: Select the namespace to deploy to Kubernetes. -- **Registry Configuration**: Simplified registry configuration for well known registries. -- **UX/UI Improvements**: View pod's container logs, better visual feedback, configurable editor font size, and more. - -Podman Desktop 0.11 is now available. [Click here to download it](/downloads)! - - - ---- - -## Release Details - -### Air-Gapped installation [#1104](https://github.com/containers/podman-desktop/pull/1104) [#1120](https://github.com/containers/podman-desktop/pull/1120) - -For users who are not able to connect to the internet, a new all-in-one (airgap) binary that includes the Podman Desktop application and the Podman binaries is now available for both Mac and Windows. When initializing a new Podman machine using all-in-one binaries, it'll use the embedded files and not grab them from internet: - -- on Mac, it embeds the qemu FCOS binaries. -- on Windows, it embeds the WSL binary. - -To differentiate the air-gapped binaries from the regular ones, you can check the artifactname, they're containing 'airgap' and are bigger. -Please note, that those binaries are available only on releases and not the pre-releases. - -There is also an optional way to provide a custom Podman machine image in the create machine form. By providing the path to the image you want, Podman Desktop will create a machine with that image. Leaving the field empty will use the default image (the one included in the binary). - - - -### Feedback within Podman Desktop [#1078](https://github.com/containers/podman-desktop/pull/1078) - -Submitting feedback on Podman Desktop is getting easier as it is possible directly within the tool. This will help to get more information about the issues you are facing and will help us to improve the tool. - - - -Please feel free to submit any feedback you have, we are looking forward to hearing from you! - -### Docker compatibility mode [#1047](https://github.com/containers/podman-desktop/pull/1047) - -The Docker compatibility mode is a feature that allows to use Podman as a drop-in replacement for Docker. It relies on the Docker socket helper provided with Podman and automatically handle the local setup of Podman path to the Docker socket. Tools like [Testcontainers](https://www.testcontainers.com/) or others are relying on this to communicate to the container engine. - -In this new version of Podman Desktop, we are now providing information to the user about the status of this compatibility mode directly on the dashboard page. The way it works is the following: - -- When Docker is running: the socket is pointing to Docker. -- When Podman is starting and Docker is not running: the docker API socket will use Podman. - -In case the compatibility mode is not activated, new documentation on how to activate it and how to easily switch between Docker and Podman is available here: [https://podman-desktop.io/docs/migrating-from-docker](https://podman-desktop.io/docs/migrating-from-docker). - -This is a warning displayed for Windows users: -![windows-docker-compatibility](https://user-images.githubusercontent.com/436777/209305744-9056addd-f122-4f75-9e03-ad27a0109375.png) - -This is the warning for Mac users: -![mac-docker-compatibility](https://user-images.githubusercontent.com/6422176/207964861-a9c1f72c-89d6-4816-beab-397af4125620.png) - -### Allow to toggle on/off the proxy setting [#983](https://github.com/containers/podman-desktop/pull/983) - -In some context, users need the ability to disable and re-enable the proxy configuration very quickly, without having to entirely reconfigure it. This is now possible from the Podman Desktop settings page, where a toggle to enable/disable the proxy configuration has been added. - - - -Note: extensions can read this information and then update the proxy configuration. - -### Namespace selection for deploy to Kubernetes [#1008](https://github.com/containers/podman-desktop/pull/1008) - -A new dropdown menu has been added to the deploy to Kubernetes screen to allow selecting the namespace to deploy to. Defaults to defaultnamespace, if unable to get any from kube_client module. - -If the user have set `kubectl config set-context --current --namespace=NAMESPACE` it honors that as the chosen value. - -![deploy-kubernetes-namespace](https://user-images.githubusercontent.com/7339809/206688886-095e4f15-42ae-4a0a-b1c6-ae4b547fcdfb.gif) - -### Configure Podman binary path [#941](https://github.com/containers/podman-desktop/pull/941) - -Users might use a custom path to the podman binary. An option within settings (Settings -> Preferences) to allow providing an additional path to Podman binary has been added. -This is useful for users who have installed Podman using a package manager and want to use the binary provided by the package manager. -See: [Unable to set custom binary path for Podman on macOS](/docs/troubleshooting/troubleshooting-podman-on-macos#unable-to-set-custom-binary-path-for-podman-on-macos) -![custom-path](https://user-images.githubusercontent.com/6422176/204832063-5858065a-2fc3-49de-8d23-3b99b7d10dbf.png) - -### Provide pre-defined registries [#1201](https://github.com/containers/podman-desktop/pull/1201) - -The experience of configuring a registry is getting simplified for the most popular ones. The user will be able to select a registry from a pre-defined list and will only have to provide the username and password. The following registries will be pre-defined: - -- Dockerhub -- Red Hat Quay -- GitHub -- IBM Container Registry -- Google Container Registry - - - -### UI/UX Improvements - -#### View Pods logs [#1122](https://github.com/containers/podman-desktop/pull/1122) - -The pods details view provides the ability to view the logs of each containers that might be running in a pod. Moreover, as it can also be hard to scan and identify which container is producing which output, we appended the container name at the beginning of each output and differentiate each container with a different color. - -![pods-logs](https://user-images.githubusercontent.com/6422176/211024673-eee9bad8-1b0c-4446-b8d1-97a226282c4d.png) - -#### Improved feedback when clicking on container list action icons [#1150](https://github.com/containers/podman-desktop/pull/1150) [#1161](https://github.com/containers/podman-desktop/pull/1161) - -When starting/stopping or deleting a container, a spinner is now displayed. In case of error, a message indicating that the action failed will also be better indicated. - - - -For containers that exit immediately or short-lived containers, the feedback is also improved and include report of error now provide a better feedback to the user [#1161](https://github.com/containers/podman-desktop/pull/1161). - - - -#### Allows to change the default font size for the editor [#1160](https://github.com/containers/podman-desktop/pull/1160) - -An editor is used in several screens of Podman Desktop, from the inspect screen to container's outputs and Kubernetes YAML. The default font size is 10 pixels. It's now possible to adjust the font size to the one the one you prefer. This setting is persisted and will be used for all the editors of Podman Desktop and available from the preferences page (Settings -> Preferences). - - - -#### Keep expanded state of pods when refreshing containers [#1042](https://github.com/containers/podman-desktop/pull/1042) - -When switching from different screens of the application or simply refreshing the list of containers, the expanded state of each item in the list is now persisted and will be properly restored. - - - -#### Click on the Pod name redirects to the Pod details page [#1159](https://github.com/containers/podman-desktop/pull/1159) - -The list of containers also displays pods, now clicking on the pod name directly redirects to the Pod details page. - - - -#### Improved styles of buttons for actions [#984](https://github.com/containers/podman-desktop/pull/984) - -The style of the buttons for actions on item in the list of in details pages have been improved. The background has been removed, but to make the hover state more visible, the "hover" circle is visible and the icon's color is also changing. - -On lists: -![list-actions](https://user-images.githubusercontent.com/6422176/205979121-b49a0ddf-03bb-4a4d-8d12-bc8d0bd52387.png) - -On details pages: -![details-actions](https://user-images.githubusercontent.com/6422176/205979123-ea420b17-e834-4029-82eb-22949889eee9.png) - -#### Improved alignments in pages with lists [#1182](https://github.com/containers/podman-desktop/pull/1182) - -The alignment of the items in the list of containers and pods have been improved. The header of each columns are now aligned with the text. and the actions icons are now aligned with the top of the text. - -![list-actions](https://user-images.githubusercontent.com/19958075/212712580-096fd090-0beb-40a4-8dc6-b3fdc5e81e35.png) - ---- - -## Other Notable Enhancements - -- Statistics area in container details are always shown. ([#1131](https://github.com/containers/podman-desktop/pull/1131)) -- Add more descriptive wording and more information to extensions. ([#985](https://github.com/containers/podman-desktop/pull/985)) -- Allow to install on-the fly Podman Desktop extensions using an OCI image. ([#1187](https://github.com/containers/podman-desktop/pull/1187)) -- Display correctly non-multiplexed stream in logs (when using non interactive mode). ([#1197](https://github.com/containers/podman-desktop/pull/1197)) -- Font changed from white to gray for non-name sections (creation date, size). ([#1206](https://github.com/containers/podman-desktop/pull/1206)) -- Add Help/about menu for macOS/Windows/Linux. ([#1207](https://github.com/containers/podman-desktop/pull/1207)) - ---- - -## Documentation - -Coming with this new version of Podman Desktop, the documentation has been getting attention (with number of editorial reviews) and new content have been added. - -Content for helping users to migrate from Docker to Podman has been added: - -- [Using the `podman-mac-helper` tool to migrate from Docker to Podman on macOS](https://github.com/containers/podman-desktop/pull/1088) -- [better identify podman-mac-helper verification steps](https://github.com/containers/podman-desktop/pull/1129) -- [Emulating Docker CLI using Podman to migrate from Docker to Podman](https://github.com/containers/podman-desktop/pull/1143) -- [Migrating from Docker section](https://github.com/containers/podman-desktop/pull/1146) -- [Verifying that your tools are using Podman](https://github.com/containers/podman-desktop/pull/1152) -- [Importing saved containers](https://github.com/containers/podman-desktop/pull/1144) - -Several of other improvements have been made to the documentation and the website: - -- [Troubleshooting for Apple Silicon and brew x86_64 installs](https://github.com/containers/podman-desktop/pull/962) -- [Change text of custom binary location, add docs](https://github.com/containers/podman-desktop/pull/972) -- [Silent install option for windows](https://github.com/containers/podman-desktop/pull/974) -- [Upgrade to vite v4 and rollup v3](https://github.com/containers/podman-desktop/pull/1079) -- [Refreshing documentation on installing on Linux](https://github.com/containers/podman-desktop/pull/1118) -- [Identify the shell sessions in Installing on Linux](https://github.com/containers/podman-desktop/pull/1130) -- [Website: the linux binary is not a source, it should be promoted as a binary](https://github.com/containers/podman-desktop/pull/1133) -- [Introducing tabs for operatings systems and container engines](https://github.com/containers/podman-desktop/pull/1162) -- [website: In the landing page, update plugins and container engines lists](https://github.com/containers/podman-desktop/pull/1165) -- [Document where code is located within Podman Desktop](https://github.com/containers/podman-desktop/pull/1163) -- [(extension-api): Documentation on how to write an extension and use the api](https://github.com/containers/podman-desktop/pull/1172) - ---- - -## Notable bug fixes - -- Update to v0.3.2 Docker Desktop API for extensions - [#1070](https://github.com/containers/podman-desktop/pull/1070) -- Updated link to the Matrix room - [#1076](https://github.com/containers/podman-desktop/pull/1076) -- Report error when container name already exists - [#1142](https://github.com/containers/podman-desktop/pull/1142) -- Fixed connectivity to the Podman machine when the generated socket length is greater than 104 characters in MacOS - [#1145](https://github.com/containers/podman-desktop/pull/1145) -- Do not use long calls for external clients - [#1151](https://github.com/containers/podman-desktop/pull/1151) -- Invalid name/tag for images with registries using a different port - [#1127](https://github.com/containers/podman-desktop/pull/1127) -- Invalid badge count for images when two images have the same tag - [#1124](https://github.com/containers/podman-desktop/pull/1124) -- Fixed detection of WSL2 - [#981](https://github.com/containers/podman-desktop/pull/981) -- Handle invalid kubeconfig files instead of crashing - [#953](https://github.com/containers/podman-desktop/pull/953) -- Removed redundant actions from details pages - [#1200](https://github.com/containers/podman-desktop/pull/1200) -- Fixed registry URL not aligned with header - [#1205](https://github.com/containers/podman-desktop/pull/1205) -- Moved SVGs to reusable components - [#1211](https://github.com/containers/podman-desktop/pull/1211) - -**Extensions API has also been improved in this new release:** - -- (extension-api): Allows to register a factory for kubernetes connection objects (like for container connections) - [#1040](https://github.com/containers/podman-desktop/pull/1040) -- (extension-api): List or inspect containers, be notified on events - [#1041](https://github.com/containers/podman-desktop/pull/1041) -- (extension-api): Allows to get provider notification - [#1045](https://github.com/containers/podman-desktop/pull/1045) -- (dd-extension-api): Implement toast API - [#1154](https://github.com/containers/podman-desktop/pull/1154) -- (extension-api): Moved extension properties to preferences - [#948](https://github.com/containers/podman-desktop/pull/948) -- (dd-extension-api): Added open dev console on dd webview in development mode - [#1071](https://github.com/containers/podman-desktop/pull/1071) -- (extension-api): Added notes on extension and frameworks/tooling of Podman Desktop - [#1166](https://github.com/containers/podman-desktop/pull/1166) -- (extension-api): Documentation on how to write an extension and use the api - [#1172](https://github.com/containers/podman-desktop/pull/1172) -- (dd-extension-api): DD extensions should see only minimal information on containers - [#1191](https://github.com/containers/podman-desktop/pull/1191) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop 0.11 even better: - -- [Emmanuel Bernard](https://github.com/emmanuelbernard) in [#962 - Troubleshooting for Apple Silicon](https://github.com/containers/podman-desktop/pull/962) -- [Michael Prankl](https://github.com/eidottermihi) in [#974 - Silent install option for windows](https://github.com/containers/podman-desktop/pull/974) -- [Endre Lervik](https://github.com/elervik) in [#1008 - Namespace selection for deploy to kubernetes](https://github.com/containers/podman-desktop/pull/1008) -- [Evan FP](https://github.com/evanfpearson) in [#1145 - fixed socket path length error](https://github.com/containers/podman-desktop/pull/1145) -- [Lokesh Mandvekar](https://github.com/lsm5) in [#956 - add repository key to package.json](https://github.com/containers/podman-desktop/pull/956) -- [Jean François Maury](https://github.com/jeffmaury) in [#1194 - Cannot create Kind cluster on Windows](https://github.com/containers/podman-desktop/pull/1194) - -## Final Notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.11.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-02-15-release-0.12.md b/website/blog/2023-02-15-release-0.12.md deleted file mode 100644 index 178ef9248d..0000000000 --- a/website/blog/2023-02-15-release-0.12.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.12 -description: Podman Desktop 0.12 has been released! -slug: podman-desktop-release-0.12 -authors: [slemeur] -tags: [podman-desktop, release, kubernetes] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' - -This release note covers Podman Desktop 0.12 release changes. - - - -- **Podman Version**: Podman 4.4.1 now included in Windows and Mac installers. -- **Containers Configuration**: Configure port mappings for an image without exported ports. -- **Windows Home Support**: Podman Desktop now supports Windows Home Edition. -- **Start minimized**: Option to start Podman Desktop minimized to system tray. -- **UX and UI Improvements**: Consistent actions, placeholder for logs, unified icons and others. - -Podman Desktop 0.12 is now available. [Click here to download it](/downloads)! - -![podman-desktop-0-12-hero](img/podman-desktop-release-0.12/podman-desktop-release-0.12.png) - - - ---- - -## Release Details - -### Update to Podman v4.4.1 [#1456](https://github.com/containers/podman-desktop/pull/1456) - -Podman Desktop 0.12 embeds [Podman 4.4.1](https://github.com/containers/podman/releases/tag/v4.4.1) in Windows and macOS installers. Make sure to upgrade to benefit from the latest Podman features and bug fixes. - -### Configuring port mappings when an image has no exported port [#1265](https://github.com/containers/podman-desktop/pull/1265) - -With the latest update, users can now add multiple local-to-remote port mappings when starting a container from an image that has no exported ports. This feature provides users with the flexibility to specify which ports on their local machine should map to which ports on the container, even if the container does not have any exported ports by default. - -![port-mapping](https://user-images.githubusercontent.com/49404737/215112797-86dcf3f0-121a-487e-a71f-ad41e91f93da.gif) - -### Installing Podman Desktop on Windows Home Edition [#1268](https://github.com/containers/podman-desktop/pull/1268) - -Podman Desktop 0.12 offers the ability to be installed on Windows Home Edition. The mechanism uses Virtual Machine Platform detection, instead of hyper-v. WSL2 is still requires but can be installed along with the installation process. - -![install-windows-home-edition-2](https://user-images.githubusercontent.com/436777/215121429-7c757aaa-a838-43db-98a2-78ad368f407e.png) - -### Option to automatically minimize Podman Desktop on login [#1374](https://github.com/containers/podman-desktop/pull/1374) - -Podman Desktop now provides a "start minimized" option when users log in onto the laptop. This option, available from the application's settings, can be useful if users want to automatically launch Podman Desktop at log in, but prefer not to have the window visible on the screen. With this feature, you can ensure that Podman Desktop is up and running in the background without any interruption to your workflows. - -- Go to ** Settings > Preferences > Login: Minimize** to activate the option. - -![minimize-on-login](https://user-images.githubusercontent.com/6422176/216651424-bcf756fd-7554-4b24-a838-e3e2f798fe6e.png) - -### UI and UX Improvements - -#### Improved actions consistency [#1225](https://github.com/containers/podman-desktop/pull/1225) - -Actions displayed in the list pages for Images and Containers have been reviewed and made consistent between other pages. - -![action-consistency-1](https://user-images.githubusercontent.com/19958075/214104678-2d4148d7-484e-41f9-9da9-aecee328ae2b.png) - -Actions displayed in the header of the container's details page have been improved so that the actions are always displayed in the same order. - -![action-consistency-2](https://user-images.githubusercontent.com/19958075/214104663-64fa0601-bb79-49bf-8226-6a78a88d3622.png) - -#### Consistent status icons for Pods, Containers, Images and Volumes [#1326](https://github.com/containers/podman-desktop/pull/1326), [#1377](https://github.com/containers/podman-desktop/pull/1377), [#1459](https://github.com/containers/podman-desktop/pull/1459) and [#1245](https://github.com/containers/podman-desktop/pull/1245) - -The status icons for Pods, Containers, Images, and Volumes have been unified and use consistent states. - -![pods-status-icon](https://user-images.githubusercontent.com/19958075/216671859-bdd8dca4-56b7-40a8-961a-dcb6e01be61e.png) - -Icons in empty screens have been updated to use the same consistent SVG icon as the status icons. - -![empty-screen-2](https://user-images.githubusercontent.com/436777/214577726-f4cfde7b-017a-499d-a2a9-e50d455ffaf0.png) - -#### Placeholder when logs for containers are being fetched [#1353](https://github.com/containers/podman-desktop/pull/1353) - -A placeholder is now displayed when logs are being fetched. -![placeholder-loading-logs](https://user-images.githubusercontent.com/49404737/216952505-899308ae-183e-487a-b6e5-28832a0b6452.gif) - -#### Fixed alignment in badges from the navigation sidebar [#1357](https://github.com/containers/podman-desktop/pull/1357) - -Badges in the sidebar are now aligned with the title of the section. - -![alignement-badges](https://user-images.githubusercontent.com/49404737/216336502-2a34dea3-fd41-4184-8cfe-9226d70da070.png) - -#### Enable/disable open browser action based on container state [#1395](https://github.com/containers/podman-desktop/pull/1395) and [#1397](https://github.com/containers/podman-desktop/pull/1397) - -The action to open the browser if a port is opened in a container, is now disabled when the container is stopped. It is also hidden in the kebab menu. - -![enable-disable-container-actions](https://user-images.githubusercontent.com/49404737/217284414-1bdc820b-30a8-485e-b0f9-485229026696.gif) - ---- - -## Other Notable Enhancements - -- Display extension icons [#1058](https://github.com/containers/podman-desktop/pull/1058) -- Solid Icons [#1267](https://github.com/containers/podman-desktop/pull/1276) -- Differentiate icons compared to status icons [#1268](https://github.com/containers/podman-desktop/pull/1298) -- Refactors window initializing [#1309](https://github.com/containers/podman-desktop/pull/1309) -- Change window creation background color to dark [#1310](https://github.com/containers/podman-desktop/pull/1310) -- Use Podman `machine inspect` to read the address to connect to the Podman machine [#1364](https://github.com/containers/podman-desktop/pull/1364) -- Update the editor content when the pod name changes, and disable the deploy button when empty [#1403](https://github.com/containers/podman-desktop/pull/1403) -- Ability to manually set color of tray icon for Windows and Linux [#1243](https://github.com/containers/podman-desktop/pull/1243) -- Better UI feedback when starting pods [#1242](https://github.com/containers/podman-desktop/pull/1242) - ---- - -## Documentation - -The documentation had many editorial reviews, and new content. - -- [Link to simple WSL2 installation instructions](/docs/installation/windows-install) -- [Refresh Windows Installation](/docs/installation/windows-install) -- [Revamped installation introduction](/docs/installation) -- [Reorganized Linux installation](/docs/installation/linux-install) -- [Installing Podman Desktop on Windows in a restricted environment](/docs/proxy -- Fixed documentation on `podman-mac-helper` setup - ---- - -## Notable bug fixes - -- Fixed handling path with spaces on Windows when installing Podman [#1270](https://github.com/containers/podman-desktop/pull/1270) -- Removed padding from SVG icons [#1253](https://github.com/containers/podman-desktop/pull/1253) -- Website: fixed download links for Windows and macOS binaries [#1255](https://github.com/containers/podman-desktop/pull/1255) -- Fixed prettier commands on Windows [#1266](https://github.com/containers/podman-desktop/pull/1267) -- Fixed new xterm instance spawn when clicking the logs route [#1344](https://github.com/containers/podman-desktop/pull/1344) -- Fixed need to wait that telemetry has been initialized before proceeding [#1373](https://github.com/containers/podman-desktop/pull/1373) -- Fixed new xterm instance spawn when clicking the logs route in pod details[#1393](https://github.com/containers/podman-desktop/pull/1393) -- Fixed stop spinner if image cannot be retrieved [#1394](https://github.com/containers/podman-desktop/pull/1394) -- Fixed escape command with quotes only for Windows [#1462](https://github.com/containers/podman-desktop/pull/1462) -- Fixed random CRC status change [#1420](https://github.com/containers/podman-desktop/pull/1420) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop 0.12 even better: - -- [Philipp Wagner](https://github.com/imphil) in [#1274 - Link to simple WSL2 installation instructions](https://github.com/containers/podman-desktop/pull/1274) -- [Arthur S](https://github.com/arixmkii) in [#1364 - Get podman machine socketPath from podman machine inspect first](https://github.com/containers/podman-desktop/pull/1364) -- [Mitch West](https://github.com/Mitch9378) in [#1428 - Fix old documentation for 'podman-mac-helper setup'](https://github.com/containers/podman-desktop/pull/1428) - -## Final Notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.12.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-03-24-5-things-to-know-for-a-docker-user.md b/website/blog/2023-03-24-5-things-to-know-for-a-docker-user.md deleted file mode 100644 index 4ed5a53676..0000000000 --- a/website/blog/2023-03-24-5-things-to-know-for-a-docker-user.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: 5 things to know about Podman Desktop for a Docker user -description: Important things to know when switching from Docker Desktop to Podman Desktop -slug: 5-things-to-know-for-a-docker-user -authors: [benoitf] -tags: [podman-desktop, docker, migrating] -hide_table_of_contents: false ---- - -The 5 things to know being a Docker user by using Podman Desktop: - -- **Use a single UI**: Podman Desktop works with several container engines, including Docker. -- The **compatibility mode**: How to ensure tools are working with Podman instead of Docker. -- **Compose** support: How to work with Compose files and Podman. -- **Kubernetes** support: How to use Kubernetes with Podman. -- **Security**: Use `rootless` mode or containers without root privileges. - -![5-things-to-know-for-a-docker-user-hero](img/5-things-to-know-for-a-docker-user/5-things-to-know-for-a-docker-user-hero.png) - - - ---- - -## Use Podman Desktop to interact with containers running in Docker - -Docker Desktop provides a UI to interact with containers and images. But the UI depends on Docker API and it is not possible to use the UI with Docker and Podman at the same time. - -Podman Desktop is a multi-engine UI tool. The UI is compatible with the API of Docker and Podman. It means all containers and images from all the engines at the same time are visible in the UI. - -When migrating from Docker to Podman, you can use Podman Desktop to interact with containers running in Docker. Explore all commands and features of Podman Desktop and see all the resources from Docker. - -![Many container engines at the same time](img/5-things-to-know-for-a-docker-user/multiple-container-engines.png) - -## Docker compatibility mode - -Using Podman with Podman Desktop or with the Podman CLI is straightforward. But some tools expect to find `docker` CLI or `docker.sock` socket. In this case, you have to use the compatibility mode of Podman. - -### Socket file compatibility - -The socket compatibility mode is a feature of Podman that allows to bind the Podman socket under the Docker socket path. - -On Windows the socket compatibility mode is always enabled by default. On macOS, by using the `.pkg installer` it is active by default. But when installing with `brew`, it will not be there because it requires some admin permissions. - -That is not an issue because you can enable it by [invoking a CLI tool](https://podman-desktop.io/docs/migrating-from-docker/using-podman-mac-helper) that will setup the compatibility mode. - -For example if you use [`Testcontainers`](https://www.testcontainers.com/) in your Java project, you can use the compatibility mode to ensure that the tool will use Podman instead of Docker. - -### CLI compatibility - -If you have scripts relying on `docker` CLI, you can use the compatibility mode to ensure that the tool is working with Podman instead of Docker. - -If you have the `docker` CLI installed on your computer, you can use the socket file compatibility of docker to ensure that the tool is working with Podman engine instead of Docker. - -If you do not have the `docker` CLI installed on your computer, you can [Create a script](https://podman-desktop.io/docs/migrating-from-docker/emulating-docker-cli-with-podman) called `docker` that will call the `podman` CLI - -**_NOTE:_** creating a shell prompt alias, for example `alias docker=podman`, will not work inside scripts that you call. - -## Compose - -As a user of Docker, you might use `docker compose` (or `docker-compose`) to run some of your applications. - -For now Podman does not include a `Compose` support directly in the CLI with a command `podman compose`. - -`Compose` can work with the Podman socket. - -Based on the compatibility mode (see [section about Docker compatibility mode](#docker-compatibility-mode)): - -- Enabled: you can use the `compose` binary to run your applications. -- Disabled: you need to [export the environment variable DOCKER_HOST](https://podman-desktop.io/docs/migrating-from-docker/using-the-docker_host-environment-variable) before running compose. - -You can now use the `compose` binary to run your applications and it will use Podman engine. - -Podman Desktop has a `compose` extension that can fetch `compose` binary if not already available on the filesystem. - -Podman Desktop UI displays the containers created by `Compose` are in the same group. - -![Compose support in the UI](img/5-things-to-know-for-a-docker-user/compose-containers-in-ui.png) - -## Kubernetes - -It is possible to start a Kubernetes cluster with Docker. - -Podman supports directly a subset of Kubernetes resources that you can use with `.yaml` files. - -For example if you only want to create a `Pod` resource, you can use the `Play Kubernetes YAML` button from the `Containers` list screen with your `.yaml` file. No need to install or start a Kubernetes cluster. - -![Play Kubernetes YAML](img/5-things-to-know-for-a-docker-user/play-kubernetes-yaml.png) - -It is possible to do the counter-part. Export the definition of a container or pod to a Kubernetes resource. You can use the `Generate kube` button from the kebab menu of a given container or pod. - -![Kubernetes generate](img/5-things-to-know-for-a-docker-user/kube-generate.png) - -Podman handles pods and in the Podman Desktop UI, you can see all the pods inside a Pod section. All containers inside the pod are in the same group. - -![Pods in the UI](img/5-things-to-know-for-a-docker-user/pods-in-ui.png) - -![Containers from pod](img/5-things-to-know-for-a-docker-user/containers-from-pod.png) - -An experimental `kind` extension is bringing the creation of full-blown Kubernetes cluster with Podman. - -## Rootless mode - -One of the difference of Docker and Podman is the way they handle containers. Docker requires root privileges to run containers by default. Podman can run containers without root privileges by default. - -It means that for example, starting a container with a port < 1024 will not work. You need to use a port > 1024. - -If you still need to create containers with a port < 1024, you can change the Podman machine configuration of the Podman Machine if you are on Windows or macOS. - -The command is `podman machine set --rootful` to enable the execution with root privileges or `podman machine set --rootful=false` to switch back to rootless mode. diff --git a/website/blog/2023-03-29-release-0.13.md b/website/blog/2023-03-29-release-0.13.md deleted file mode 100644 index 8bbca5aaa8..0000000000 --- a/website/blog/2023-03-29-release-0.13.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.13 -description: Podman Desktop 0.13 has been released! -slug: podman-desktop-release-0.13 -authors: [deboer] -tags: [podman-desktop, release, kubernetes, compose] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' - -This release note covers Podman Desktop 0.13 release changes. - - - -- **Podman Version**: Podman 4.4.4 now included in Windows and Mac installers. -- **Compose**: Support for installing Docker Compose. -- **Extensions**: Improved extension support for Podman Desktop with additional capabilities. -- **UX and UI Improvements**: Welcome page, task manager, resources, and update alerts. - -Podman Desktop 0.13 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-0-13-hero](img/podman-desktop-release-0.13/podman-desktop-release-0.13.png) - - - ---- - -## Release details - -### Update to Podman v4.4.4 - -Podman Desktop 0.13 embeds [Podman 4.4.4](https://github.com/containers/podman/releases/tag/v4.4.4) in -Windows and macOS installers [#1456](https://github.com/containers/podman-desktop/pull/1456). - -### Compose support - -You can install Docker Compose from the Podman Desktop UI [#1578](https://github.com/containers/podman-desktop/pull/1578). This will allow you use Compose workflows using Podman. - -### Extensions - -To make it easier to extend Podman Desktop we have: - -- Published the [Podman Desktop Extension API](https://www.npmjs.com/package/@podman-desktop/api) to npmjs [#1727](https://github.com/containers/podman-desktop/pull/1727) -- Documented [how to create, publish, and install your own extensions](https://podman-desktop.io/docs/extensions) -- Expanded the API for `QuickPickInput` [#1839](https://github.com/containers/podman-desktop/pull/1839) - -### UI and UX improvements - -#### Welcome page [#1726](https://github.com/containers/podman-desktop/pull/1726) - -The first time every user starts Podman Desktop, they will see a welcome page: - -![welcome-page](https://user-images.githubusercontent.com/19958075/225682776-e016ba31-5bb9-41d5-87ed-f93caeb3fb84.png) - -Note that the Welcome page will only appear the first time you run Podman Desktop 0.13.0, and none of your settings or assets in Podman Desktop will be affected by it. -Over time, this page will be expanded to help with initial setup and configuration. - -#### New Task Manager [#1724](https://github.com/containers/podman-desktop/pull/1724) - -A new Task Manager has been added to the status bar to see the progress (or return to) long running tasks. To start with, building images [#1725](https://github.com/containers/podman-desktop/pull/1725) and Podman machine [#1742](https://github.com/containers/podman-desktop/pull/1742) are using the task manager. - -![task-manager](img/podman-desktop-release-0.13/task-manager.png) - -#### Updated Resources Settings [#1582](https://github.com/containers/podman-desktop/pull/1582) - -The ** Settings > Resources** page has been updated with a new design, making it easier to see and control your providers from a single place. - -![resources](https://user-images.githubusercontent.com/49404737/221908815-595715fe-4c95-4087-89e0-45e5544ed5c9.gif) - -The other settings pages have been updated for consistency with this new design. - -#### Update Alerts [#1827](https://github.com/containers/podman-desktop/pull/1827) - -A new alert button will appear in the status bar when future updates are available. - - - -#### Prune buttons [#1481](https://github.com/containers/podman-desktop/pull/1481), [#1482](https://github.com/containers/podman-desktop/pull/1482), [#1484](https://github.com/containers/podman-desktop/pull/1484) - -We've added buttons to prune unused volumes [#1481](https://github.com/containers/podman-desktop/pull/1481), images [#1482](https://github.com/containers/podman-desktop/pull/1482) and pods [#1484](https://github.com/containers/podman-desktop/pull/1484). - -![prune-image](img/podman-desktop-release-0.13/prune-image.png) - ---- - -## Other notable enhancements - -- Kubernetes pods are now shown in the Pods view [#1312](https://github.com/containers/podman-desktop/pull/1312) -- Easy button to fix Docker compatibility mode on macOS [#1697](https://github.com/containers/podman-desktop/pull/1697) -- Display extension icons [#1058](https://github.com/containers/podman-desktop/pull/1058) -- API to cancel long running tasks [#1777](https://github.com/containers/podman-desktop/pull/1777) - ---- - -## Documentation - -The documentation has new content: - -- [How to create, publish, and install your own Podman Desktop extensions](https://podman-desktop.io/docs/extensions) -- [Using Compose](https://podman-desktop.io/docs/compose/podman-compose) -- [Restarting Kind](https://podman-desktop.io/docs/kind) - ---- - -## Notable bug fixes - -- Periodically check and refresh Podman connection on Windows and Mac [#1662](https://github.com/containers/podman-desktop/pull/1662) -- Fix inconsistent Log view behaviour [#1710](https://github.com/containers/podman-desktop/pull/1710) -- Don't create route for regular Kubernetes clusters [#1707](https://github.com/containers/podman-desktop/pull/1707) -- Fix copy button on empty screen [#1804](https://github.com/containers/podman-desktop/pull/1804) -- Register extension tray items correctly [#1778](https://github.com/containers/podman-desktop/pull/1778) and handle updates [#1800](https://github.com/containers/podman-desktop/pull/1800) - ---- - -## Community thank you - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop 0.13 even better: - -- [`Tony Soloveyv`](https://github.com/Tony-Sol) in [#1605 - Fix directories inconsistency in `~/.local/share/`](https://github.com/containers/podman-desktop/pull/1605) -- [`Oleg`](https://github.com/RobotSail) in [#1567 - Check for Flatpak when calling command from exec](https://github.com/containers/podman-desktop/pull/1567) -- [`Christoph Blecker`](https://github.com/cblecker) in [#1556 - Fix: Hide dock icon on macOS if starting minimized](https://github.com/containers/podman-desktop/pull/1556) -- [`Paul Wright`](https://github.com/pwright) in [#1604 - Docs: correct syntax of kind doc](https://github.com/containers/podman-desktop/pull/1604) -- [`Denis Golovin`](https://github.com/dgolovin) in [#1790 - Feat: add EventEmitter class to extension API](https://github.com/containers/podman-desktop/pull/1790) -- [`Christophe Fergeau`](https://github.com/cfergeau) in [#1642 - Fix: Pod list "an pod" typing error](https://github.com/containers/podman-desktop/pull/1642) - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.13.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-04-14-release-0.14.md b/website/blog/2023-04-14-release-0.14.md deleted file mode 100644 index ae66d3cbd0..0000000000 --- a/website/blog/2023-04-14-release-0.14.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.14 -description: Podman Desktop 0.14 has been released! -slug: podman-desktop-release-0.14 -authors: [deboer] -tags: [podman-desktop, release, kubernetes, kind] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 0.14 - Our Kind-est release yet! - -We have been working on a Kind extension for a while now, and decided it is time to promote it -into a release just in time for KubeCon and CloudNativeCon Europe! - -We're especially excited about releasing Kind because it finally shows the full purpose -of Podman Desktop: not just local container engines, but Kubernetes too. More importantly, -providing tools that allow you to manage both environments and seamlessly move between them. - -Some of these features were available in development mode over the last few releases, -but since they are now in the release build, we will do a full roundup and talk about -all the Kind features. - - - -- **Kind Installation**: Install Kind from the status bar -- **Manage Kind Clusters**: Create and manage Kind clusters from ** Settings > Resources** -- **Using Kind**: Deploying YAML and sharing images to a cluster -- **Kind Ingress**: Install a Contour ingress controller -- **UX and UI Improvements**: Updated preferences and telemetry prompt - -Podman Desktop 0.14 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-0-14-hero](img/podman-desktop-release-0.14/podman-desktop-release-0.14.png) - - - ---- - -## Release details - -### Kind Installation - -Get Kind up and running in seconds! The Kind extension is now bundled as part of Podman Desktop -[1421](https://github.com/containers/podman-desktop/issues/1421) -and allows you to easily [install Kind directly from the status bar](/docs/kind/installing) -[1257](https://github.com/containers/podman-desktop/issues/1257). - -The installed `kind` CLI is available from the system shell [1516](https://github.com/containers/podman-desktop/issues/1516), -allowing you to open a terminal window to `kind get clusters` or work with other tools. - -### Manage Kind Clusters - -Once Kind is installed (or if you already had it), you can manage your clusters in ** Settings > Resources**. -From here you can [create Kind clusters](/docs/kind/creating-a-kind-cluster), -start/stop [1953](https://github.com/containers/podman-desktop/issues/1953) -or delete [1977](https://github.com/containers/podman-desktop/issues/1977) them. - -![kind-clusters](img/podman-desktop-release-0.14/kind-clusters.png) - -The Kind control plane runs as a container. You will see this container -in the **Container** list and can also start or stop it from there. - -### Using Kind - -Now that you have Kind installed and running, what can you do with it? -If you like terminals, you can always open one up and use the Kind CLI to -[interact with your cluster](https://kind.sigs.k8s.io/docs/user/quick-start/#interacting-with-your-cluster). - -Within Podman Desktop we have started with two ways to interact with the cluster. -The first is the ability to play local YAML files on your Kind (or any other Kubernetes!) cluster [1261](https://github.com/containers/podman-desktop/issues/1261). This allows you to take existing Kubernetes YAML definitions - -your deployments, services, or other objects - and deploy it to the cluster. - - - -As you deploy pods, they will automatically appear in the list of **Pods** [1263](https://github.com/containers/podman-desktop/issues/1263), allowing you to start, stop, and interact them just like pods running on Podman. - -One of the most common uses is to deploy a container that you have been running on Podman, and this will fail -if the image is not available in Kind. To solve this we have made it easy to push images from -Podman to Kind [1448](https://github.com/containers/podman-desktop/issues/1448). - -![push-image-kind](img/podman-desktop-release-0.14/push-image-kind.png) - -### Kind Ingress - -If you deploy a pod to Kind, you are also going to want to reach it! To do this you will need to install a load balancer or -ingress controller so that the pod is accessible from outside the cluster. We made it easy to install the Contour ingress -controller while creating a Kind cluster [1675](https://github.com/containers/podman-desktop/issues/1675), -so if you created your cluster with Podman Desktop it is already there! - -![kind-ingress](img/podman-desktop-release-0.14/kind-ingress.png) - -We have several other Kind and Kubernetes features planned to expand the supported scenarios, but hopefully this -makes it easy to get started with Kind and shows where we're headed. As always, feedback is appreciated! - -### UI and UX improvements - -#### Updated Preferences - -The ** Settings > Preferences** page has been updated with a new design [1913](https://github.com/containers/podman-desktop/pull/1913), -making it easier to see and change preferences. Changes are live, no more Update button. - -![preferences](https://user-images.githubusercontent.com/49404737/229498507-e754b55c-dcbd-486d-9ee3-a1fe3bed7271.gif) - -#### Telemetry Prompt - -The prompt to enable or disable telemetry has been moved from its own dialog into the **Welcome** screen. -[1927](https://github.com/containers/podman-desktop/pull/1927) -This is more usable, one less prompt, and solves a window-layering issue for some users! - -![telemetry prompt](https://user-images.githubusercontent.com/19958075/229577331-365a9a01-0426-4482-a95d-f5dfe39af90a.png) - ---- - -## Other notable enhancements - -- Extension support for opening an external URL [2028](https://github.com/containers/podman-desktop/pull/2028) and - accessing the clipboard [2023](https://github.com/containers/podman-desktop/pull/2023) - ---- - -## Documentation - -Naturally, we have a section in the documentation just for [Kind](https://podman-desktop.io/docs/kind). - ---- - -## Notable bug fixes - -- Avoid a dialog box if unable to check for updates [2062](https://github.com/containers/podman-desktop/pull/2062) -- Unable to get to the Dashboard if Kind (or Kubernetes) cluster was not running [2052](https://github.com/containers/podman-desktop/issues/2052) - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.14.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-05-02-release-0.15.md b/website/blog/2023-05-02-release-0.15.md deleted file mode 100644 index a3a41d6641..0000000000 --- a/website/blog/2023-05-02-release-0.15.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: Release Notes - Podman Desktop 0.15 -description: Podman Desktop 0.15 has been released! -slug: podman-desktop-release-0.15 -authors: [deboer] -tags: [podman-desktop, release, kubernetes, kind] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-0.15.webp ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 0.15 - Cleanliness is next to Podliness! - -It has only been two weeks since our last release, but we really wanted to complete a few scenarios, -fix a few bugs, and show off several design updates and UI improvements that we have been working on. - - - -- **Podman Version**: Podman 4.5 now included in Windows and Mac installers. -- **Kind Ingress**: Creating an ingress to expose services outside the Kind cluster. -- **Podliness**: Ability to choose external ports when podifying containers. -- **Cleanliness**: New navigation bar, dialog, and palette update. -- **UX and UI Improvements**: Markdown support for extensions. - -Podman Desktop 0.15 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-0-15-hero](img/podman-desktop-release-0.15/podman-desktop-release-0.15.webp) - - - ---- - -## Release Details - -### Update to Podman v4.5.O - -Podman Desktop 0.15 embeds [Podman 4.5.0](https://github.com/containers/podman/releases/tag/v4.5.0) in -Windows and macOS installers [#2115](https://github.com/containers/podman-desktop/issues/2115). - -### Kind Ingress - -Wait a minute, didn't we enable this last release? Well yes, last time we did add support for installing -the Contour ingress controller on Kind [#1675](https://github.com/containers/podman-desktop/issues/1675), -but you still couldn't access your containers without the corresponding ingress. - -This release adds a simple checkbox you can use when deploying to Kind to create an ingress and -make your service accessible [#1322](https://github.com/containers/podman-desktop/issues/1322). - - - -### Podliness: Ability to Choose External Ports when Podifying Containers - -When you create a pod from of a set of containers (Podifying!), you probably don't want to expose every -single port from every container to the world. With an updated panel you can now see which ports each container -exposes, pick which should remain visible outside the pod, and which are for internal use -[#2232](https://github.com/containers/podman-desktop/pull/2232). - -![Podify page](https://user-images.githubusercontent.com/49404737/234527674-ed14f52c-8f66-445f-8038-c8135bb61136.gif) - -### Cleanliness: New Navigation Bar, Dialogs, and Palette - -It was time to catch up on some design ideas and do some UI cleanup! - -#### New Navgation Bar - -The navigation bar is now always fixed on the left size, without labels. This opens up more space -for the content on each page, and is easier to jump in and out of ** Settings**. -[#2167](https://github.com/containers/podman-desktop/issues/2167) - -![Navigation bar](img/podman-desktop-release-0.15/navigation.png) - -#### Updated Dialogs - -We wanted messages and dialog boxes to feel a bit more integrated, so we have a new message box -[#1808](https://github.com/containers/podman-desktop/pull/1808) and use it for all dialogs opened by -extensions, pruning containers/pods/images/volumes [#2138](https://github.com/containers/podman-desktop/pull/2138), -and updating Podman Desktop itself [#2249](https://github.com/containers/podman-desktop/pull/2249). - -![Dialog](img/podman-desktop-release-0.15/dialog.png) - -#### Colors - -We spent some time tweaking colors and closing on our final palette -[#2199](https://github.com/containers/podman-desktop/pull/2199), updating the colors in the terminal & detail page -[#2222](https://github.com/containers/podman-desktop/pull/2222), tweaking the navigation and main page colors -[#2223](https://github.com/containers/podman-desktop/pull/2223), -and improving the look of forms [#2156](https://github.com/containers/podman-desktop/issues/2156). - -We're not done yet, but hopefully you will notice a more polished, consistent, good-looking application! - -### Other UI and UX Improvements - -#### Markdown Support for Extensions - -We added a new component to display markdown [#2219](https://github.com/containers/podman-desktop/pull/2219) and -enabled it in preferences [#2253](https://github.com/containers/podman-desktop/pull/2253), and -provider properties/creation pages [#2152](https://github.com/containers/podman-desktop/issues/2152). -We can now embed links and other formatting in preferences, and extensions can use them in many places, for example: - -![Markdown](img/podman-desktop-release-0.15/markdown.png) - ---- - -## Other Notable Enhancements - -- We know which **Settings** page is used the most often, so now it's the default: ** Settings > Resources** [#2105](https://github.com/containers/podman-desktop/issues/2105). - -- Extensions can now use the Tasks API to let long running tasks continue in the background [#2019](https://github.com/containers/podman-desktop/issues/2019) and the existing withProgress API also uses the task manager now - [#2187](https://github.com/containers/podman-desktop/pull/2187). - -![Task API](https://user-images.githubusercontent.com/695993/233560830-85cfa685-5dcd-4efa-9fae-730a8a9eef3b.gif) - -- Images are now sorted by age [#2311](https://github.com/containers/podman-desktop/pull/2311). - -- When you start/stop a container or pod, the button is now animated instead of having an separate spinner - [#2101](https://github.com/containers/podman-desktop/issues/2101). - -- The ** Settings > Preferences** page now has a search bar [#2128](https://github.com/containers/podman-desktop/pull/2128). - -![Search preferences](img/podman-desktop-release-0.15/prefs.png) - -- The Help page has been updated [#431](https://github.com/containers/podman-desktop/issues/431). - -![Updated Help](img/podman-desktop-release-0.15/help.png) - ---- - -## Notable Bug Fixes - -- There was no way to see log or outcome if you leave the Kind cluster creation page [#2079](https://github.com/containers/podman-desktop/issues/2079). -- Kind image load doesn't show a notification [#2225](https://github.com/containers/podman-desktop/issues/2225). -- Fix odd selection in ** Settings > Extensions** [#2130](https://github.com/containers/podman-desktop/issues/2130). -- Menus are now cleaned up properly when extensions are stopped [#2188](https://github.com/containers/podman-desktop/pull/2188). -- Kind clusters are now cleaned up when Podman machine is stopped [#2306](https://github.com/containers/podman-desktop/pull/2306). - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A0.15.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-05-17-release-1.0.md b/website/blog/2023-05-17-release-1.0.md deleted file mode 100644 index 094b704b43..0000000000 --- a/website/blog/2023-05-17-release-1.0.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -title: Podman Desktop 1.0 Release -description: Podman Desktop 1.0 has been released! -slug: podman-desktop-release-1.0 -authors: [deboer] -tags: [podman-desktop, release, kubernetes, kind, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.0.jpg ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.0 Release! 🎉 - -We still have many things planned, but with a little polish and a few more bug fixes we -felt we've reached a level of maturity and it is now time to declare our 1.0 release. - -Thank you to everyone who has been with us on this journey so far! Please keep the -feedback coming! - - - -- **Highlighting Featured Extensions**: Easily find and install new extensions. -- **Featured Extensions**: Two new extensions supporting OpenShift. -- **Podman Machine as Root**: Ability to run a Podman machine as root. -- **UX and UI Improvements**: Opening external websites, editing numbers, and tooltips. - -Podman Desktop 1.0 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-0-hero](img/podman-desktop-release-1.0/podman-desktop-release-1.0.jpg) - - - ---- - -## Release Details - -### Highlighting Featured Extensions - -A critical part of our vision for Podman Desktop is the ability to install extensions to -support additional container engines, Kubernetes providers, or other tools. However, it -has not been easy to discover new extensions. - -With 1.0 we show a list of featured extensions in the **Welcome** -[#2354](https://github.com/containers/podman-desktop/pull/2354), the **Dashboard** and in -** Settings > Extensions** -[#2355](https://github.com/containers/podman-desktop/pull/2355). Check out the new -extensions for **Red Hat OpenShift Local** and the **Developer Sandbox for Red Hat OpenShift**! - -![Featured extensions](img/podman-desktop-release-1.0/featured-extensions.png) - -### Featured Extensions - -Speaking of extensions, let's take a minute to turn the spotlight on these two! - -Have another idea for extending Podman Desktop? We would love to hear from you or see -what you can create with the [Extension documentation](/docs/extensions), -and feature your extension here. - -#### Red Hat OpenShift Local - -OpenShift Local is a fully-featured single-node OpenShift cluster designed to run on your local computer. -With this extension you can install, start/stop, and deploy pods or YAML to OpenShift Local, -allowing you to test your applications on a full OpenShift environment, ensuring a consistent -experience between development, test, and production. - -The recent addition of the MicroShift preset provides a lighter, optimized option that -starts faster and uses less resources for deployments that still want OpenShift compatibility -without the full set of OpenShift services. - -![OpenShift Local](img/podman-desktop-release-1.0/openshift-local.png) - -#### Developer Sandbox for Red Hat OpenShift - -The OpenShift Developer Sandbox is a free, cloud-based OpenShift environment that allows developers to -create, build, and deploy applications to OpenShift for 30 days. With this extension you can sign up -and easily deploy pods or YAML to a hosted OpenShift environment without a local installation or -leaving Podman Desktop. - -![Developer Sandbox](img/podman-desktop-release-1.0/sandbox.png) - -### Podman Machine as Root - -When creating a Podman machine you can now decide to run as root [#2427](https://github.com/containers/podman-desktop/pull/2427). This is a prerequisite for some scenarios, such as running Kind on Windows. - -![Podman machine as root](img/podman-desktop-release-1.0/podman-root.png) - -### Other UI and UX Improvements - -#### Prompt to Open External Website - -In the previous release links to external websites were blocked due to the security risk. -In 1.0 you can click to see the URL and have the option of opening or copying it -[#2414](https://github.com/containers/podman-desktop/pull/2414). - -![External link dialog](img/podman-desktop-release-1.0/external-link.png) - -#### Editable Numeric Preferences - -Tired of clicking +, +, +? Us too. Numeric preferences are now editable so -you can directly enter the value you want -[#2368](https://github.com/containers/podman-desktop/pull/2368). - -![Editing numbers](img/podman-desktop-release-1.0/edit-number.png) - -#### Navigation tooltips - -We like our new navigation bar, but the tooltips were slow to appear. This made it harder for new -users to discover the pages and for power users to see the container/pod/image counts. The -tooltips are now a little nicer and appear immediately -[#2286](https://github.com/containers/podman-desktop/pull/2286). - -![Navigation tooltips](img/podman-desktop-release-1.0/nav-tooltips.png) - ---- - -## Other Notable Enhancements - -- If you use a non-default Podman machine you are now prompted to make it the default, so - that command line tools will use the same machine [#2205](https://github.com/containers/podman-desktop/pull/2205). - -- Extensions can now be installed without requiring a running local container engine - [#2273](https://github.com/containers/podman-desktop/pull/2273). - -- When extensions fail to load there is a new state (failed!) and an error message - to help you fix or report the problem [#2424](https://github.com/containers/podman-desktop/pull/2424). - -- The showInputBox API has support for Markdown [#2418](https://github.com/containers/podman-desktop/pull/2418). - ---- - -## Notable Bug Fixes - -- Fixed shared status when using multiple Podman 4.5 machines [#2441](https://github.com/containers/podman-desktop/pull/2441). -- Fixed hang on exit when telemetry is unreachable [#2431](https://github.com/containers/podman-desktop/pull/2431). -- Reduced initial Podman REST API calls to improve performance [#2419](https://github.com/containers/podman-desktop/pull/2419). -- **Play Kubernetes YAML** now honors the namespace [#2509](https://github.com/containers/podman-desktop/pull/2509). -- **Container** list was not remembering expand/collapsed status [#2491](https://github.com/containers/podman-desktop/pull/2491). -- Clear previous error when installing Docker extension [#2469](https://github.com/containers/podman-desktop/pull/2469). -- Ensure correct path with running Kind CLI [#2455](https://github.com/containers/podman-desktop/pull/2455). -- Use IPv4 over IPv6 by default [#2398](https://github.com/containers/podman-desktop/pull/2398). -- When changing the Kubernetes pod name, change the app name to match [#2389](https://github.com/containers/podman-desktop/pull/2389). -- Fixed incorrect pod status in **Containers** view [#2387](https://github.com/containers/podman-desktop/pull/2387). -- **Dashboard** wasn't correctly refreshing Podman status [#2359](https://github.com/containers/podman-desktop/pull/2359). - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this final -sprint we received pull requests from the following people: - -- [Taha Attari](https://github.com/TahaAttari) in [#2470 - Update docker command in importing-saved-containers.md](https://github.com/containers/podman-desktop/pull/2470) -- [Jason Greene](https://github.com/n1hility) in [#2431 - Fix hang on exit when telemetry endpoint is unreachable](https://github.com/containers/podman-desktop/pull/2431) -- [Anders Björklund](https://github.com/afbjorklund) in [#2571 - Fix the location of the lima podman socket](https://github.com/containers/podman-desktop/pull/2571) and [#2572 - Fix the state of the lima container provider](https://github.com/containers/podman-desktop/pull/2572) -- [Tucker Chapman](https://github.com/tuckerrc) in [#2567 - Add example image name in placeholder](https://github.com/containers/podman-desktop/pull/2567) - -There are now a total of 54 people (and two bots!) who have contributed PRs to Podman Desktop, and we'd -like to extend an extra thanks to all of those outside of the immediate development team who contributed -to get us here: - -[afbjorklund](https://github.com/afbjorklund), -[alv67](https://github.com/alv67), -[anjannath](https://github.com/anjannath), -[ankanroy-code](https://github.com/ankanroy-code), -[arixmkii](https://github.com/arixmkii), -[cblecker](https://github.com/cblecker), -[cfergeau](https://github.com/cfergeau), -[chevdor](https://github.com/chevdor), -[chrisjsimpson](https://github.com/chrisjsimpson), -[cu8code](https://github.com/cu8code), -[doehyunbaek](https://github.com/doehyunbaek), -[dylanmtaylor](https://github.com/dylanmtaylor), -[eidottermihi](https://github.com/eidottermihi), -[elervik](https://github.com/elervik), -[emmanuelbernard](https://github.com/emmanuelbernard), -[evanfpearson](https://github.com/evanfpearson), -[gorkem](https://github.com/gorkem), -[idjohnson](https://github.com/idjohnson), -[imphil](https://github.com/imphil), -[iongion](https://github.com/iongion), -[jlosito](https://github.com/jlosito), -[KevinAtSesam](https://github.com/KevinAtSesam), -[lsm5](https://github.com/lsm5), -[Mitch9378](https://github.com/Mitch9378), -[n1hility](https://github.com/n1hility), -[osslate](https://github.com/osslate), -[PatentLobster](https://github.com/PatentLobster), -[pwright](https://github.com/pwright), -[rdwz](https://github.com/rdwz), -[redhatrises](https://github.com/redhatrises), -[rhatdan](https://github.com/rhatdan), -[RobotSail](https://github.com/RobotSail), -[rohit-rambade](https://github.com/rohit-rambade), -[rsvoboda](https://github.com/rsvoboda), -[sfrunza13](https://github.com/sfrunza13), -[shemanaev](https://github.com/shemanaev), -[sstosh](https://github.com/sstosh), -[stickster](https://github.com/stickster), -[TahaAttari](https://github.com/TahaAttari), -[ths83](https://github.com/ths83), -[Tony-Sol](https://github.com/Tony-Sol), -[tuckerrc](https://github.com/tuckerrc), -[ucomesdag](https://github.com/ucomesdag), -[xbabalov](https://github.com/xbabalov), -zezo2019. - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.0.0) and [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.0.1). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-06-08-release-1.1.md b/website/blog/2023-06-08-release-1.1.md deleted file mode 100644 index 2c6ca4db87..0000000000 --- a/website/blog/2023-06-08-release-1.1.md +++ /dev/null @@ -1,107 +0,0 @@ ---- -title: Podman Desktop 1.1 Release -description: Podman Desktop 1.1 has been released! -slug: podman-desktop-release-1.1 -authors: [deboer] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.1.webp ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.1 Release! 🎉 - -This is primarily a bug-fix release to fix a few important issues, but we've managed to squeeze in a few enhancements -along the way. - - - -- **Podman 4.5.1**: Podman 4.5.1 now included in Windows and Mac installers. -- **Extensions**: Update extensions from within Podman Desktop. -- **Lima Support**: Choose engine type and override its name from the settings. -- **UX and UI Improvements**: New loading screen. - -Podman Desktop 1.1 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-1-hero](img/podman-desktop-release-1.1/podman-desktop-release-1.1.png) - - - ---- - -## Release Details - -### Podman v4.5.1 - -Podman Desktop 1.1 moves up to [Podman 4.5.1](https://github.com/containers/podman/releases/tag/v4.5.1). - -### Extensions - -Optional extensions will follow their own lifecycle and update independently from Podman Desktop. As of -this release you'll be able to see when there is an update available and install from within -Podman Desktop [#2655](https://github.com/containers/podman-desktop/pull/2655). - -We've also added options in **Settings > Preferences** to -automatically check for and install extension updates. - - - -![Update extensions](img/podman-desktop-release-1.1/update-extensions.png) - -### Lima Support - -Thanks to contributor [Anders Björklund](https://github.com/afbjorklund), we have some improvements to the -Lima extension! In **Settings > Preferences** you can select which -engine type Lima runs on and override the instance name [#2674](https://github.com/containers/podman-desktop/pull/2674). - -![Lima preferences](https://user-images.githubusercontent.com/10364051/241755966-0a6a293b-b18e-4222-9c40-abd6c114d464.png) - -### Other UI and UX Improvements - -#### New Loading Screen - -We have a new loading screen, Podman Desktop style! [#2743](https://github.com/containers/podman-desktop/pull/2743). - - - ---- - -## Other Notable Enhancements - -- Docker-compose can be installed system-wide [#2718](https://github.com/containers/podman-desktop/pull/2718). -- Show warning when creating a pod with two containers that use the same port [#2671](https://github.com/containers/podman-desktop/pull/2671). -- Display Kubernetes context name in pod label [#2634](https://github.com/containers/podman-desktop/pull/2634). -- Add Docker compatibility button using flatpak-spawn [#1925](https://github.com/containers/podman-desktop/pull/1925). -- Improve UI consistency of Pull Image page [#2604](https://github.com/containers/podman-desktop/pull/2604). - ---- - -## Notable Bug Fixes - -- Could not install extensions on Windows 10 [#2762](https://github.com/containers/podman-desktop/pull/2762). -- Could not use locally built images on Kubernetes [#2710](https://github.com/containers/podman-desktop/pull/2710). -- Dashboard still suggests update after installation [#2648](https://github.com/containers/podman-desktop/pull/2648). -- Could not Play Kubernetes YAML to Podman on Windows [#2594](https://github.com/containers/podman-desktop/pull/2594). -- Pod label wasn't always shown in list [#2614](https://github.com/containers/podman-desktop/pull/2614). -- Dashboard button state was resetting [#2584](https://github.com/containers/podman-desktop/pull/2584). -- Change checkbox style so they don't look like stop buttons [#2723](https://github.com/containers/podman-desktop/pull/2723). - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -sprint we received pull requests from the following people: - -- [wangxiaolei](https://github.com/fatelei) in [#2602 - Add meaningful tooltips to build, pull, prune buttons](https://github.com/containers/podman-desktop/pull/2602) -- [AsciiWolf](https://github.com/AsciiWolf) in [#2607 - fix typing error in Flathub name](https://github.com/containers/podman-desktop/pull/2607) and [#2609 - fix Flatpak install instructions](https://github.com/containers/podman-desktop/pull/2609) -- [Anders Björklund](https://github.com/afbjorklund) in [#2674 - Select engine for Lima provider](https://github.com/containers/podman-desktop/pull/2674) - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.1.0) and [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.1.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-07-12-release-1.2.md b/website/blog/2023-07-12-release-1.2.md deleted file mode 100644 index ed51054159..0000000000 --- a/website/blog/2023-07-12-release-1.2.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -title: Podman Desktop 1.2 Release -description: Podman Desktop 1.2 has been released! -slug: podman-desktop-release-1.2 -authors: [cdrage] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.2.webp ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.2 Release! 🎉 - -We're excited to announce the release of Podman Desktop version 1.2.0! This release includes many new features (Kubernetes, Compose and extension support!), bug fixes, and improvements to enhance your container management experience. Here's a summary of the key changes in this release: - -- **Added start/stop/delete/restart buttons for Compose**: You can now make changes to an entire Compose group of containers -- **Kubernetes context on the status bar**: Choose from multiple Kubernetes contexts more easily all from the status bar -- **Rename images**: Rename an image with a click of a button -- **Protocol handler support**: Added support for protocol handlers such as `open podman-desktop:extension/redhat.openshift-local` -- **Troubleshooting page**: A troubleshooting page for helping diagnose Podman Desktop related development issues - -Podman Desktop 1.2 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-2-hero](img/podman-desktop-release-1.2/podman-desktop-release-1.2.png) - ---- - -## Release Details - -### Added start/stop/delete/restart buttons for Compose - -In the last month we've been addind support for more Compose features. Before you were only able to control a group of containers if they were in a Pod. Now we have added the ability to control a group of Compose containers. You can now start, stop, delete and restart a group of containers launched by either `docker-compose` or `podman-compose`. - -Stay tuned as we add even more features to Compose! If you have any feedback or feature requests, feel free to open an issue or start a discussion on GitHub. - - - -### Kubernetes context on the status bar - -With Kubernetes context on the status bar, you can switch from one context to another in just a couple of clicks. Easily switch to a different cluster all together. If there are multiple contexts available, you can now click and pick which one to use. - - - -### Rename images - -Deployed an image but now you need to rename it / add a new tag? Podman Desktop allows you to edit an image now. Thanks to an awesome contributor [@tuckerrc](https://github.com/tuckerrc) who added the new feature. - - - -### Troubleshooting page - -Developing an extension for Podman Desktop? Want to view the logs of Podman Desktop as well as ping your container connection? We now have a troubleshooting page! - -Click on the lightbulb button on the bottom right to access the page. - - - -### Protocol handler support - -Podman Desktop now supports protocol handling when using the terminal! Want to access your favourite extension directly from a script or the terminal? If you type in `open podman-desktop:extension/redhat.openshift-local` in the terminal, Podman Desktop will automatically load up to the correct extension. - - - ---- - -## Other Notable Features - -- Background colors and FormPage ([PR #2977](https://github.com/containers/podman-desktop/pull/2977)) -- Add ability to add insecure registry / skipping cert verify ([PR #2896](https://github.com/containers/podman-desktop/pull/2896)) -- Add support for icon contribution ([PR #2984](https://github.com/containers/podman-desktop/pull/2984)) -- Add warning dialog message that virtual machine has low memory limit ([PR #2822](https://github.com/containers/podman-desktop/pull/2822)) -- Propose indexed name for new pod ([PR #3028](https://github.com/containers/podman-desktop/pull/3028)) -- Add restart button after enabling / disabling mac os compatibility ([PR #2841](https://github.com/containers/podman-desktop/pull/2841)) -- Add environment related helper constants ([PR #3079](https://github.com/containers/podman-desktop/pull/3079)) -- Allow entrypoint and cmd when starting container ([PR #3031](https://github.com/containers/podman-desktop/pull/3031)) -- Add a way to debug stores in troubleshooting page ([PR #3121](https://github.com/containers/podman-desktop/pull/3121)) -- Add custompick component (#2855) ([PR #3012](https://github.com/containers/podman-desktop/pull/3012)) -- Dynamic breadcrumbs ([PR #3119](https://github.com/containers/podman-desktop/pull/3119)) -- Icons on form pages ([PR #3155](https://github.com/containers/podman-desktop/pull/3155)) -- Switch more pages to formpage ([PR #3162](https://github.com/containers/podman-desktop/pull/3162)) -- Add rename image button ([PR #2588](https://github.com/containers/podman-desktop/pull/2588)) -- Fixed headers, improved scrollbars ([PR #2863](https://github.com/containers/podman-desktop/pull/2863)) -- Reports warnings on failed kube deploy, fixes error out ([PR #3050](https://github.com/containers/podman-desktop/pull/3050)) -- Kube context on statusbar ([PR #2755](https://github.com/containers/podman-desktop/pull/2755)) -- Install provider if not installed when clicking on create new button (#2706) ([PR #2817](https://github.com/containers/podman-desktop/pull/2817)) -- Add tag and authenticated push capacity to the extension API ([PR #2876](https://github.com/containers/podman-desktop/pull/2876)) -- Add navigation bar e2e tests ([PR #2950](https://github.com/containers/podman-desktop/pull/2950)) - ---- - -## Documentation Updates - -- Fix documentation for building image of extensions ([PR #2873](https://github.com/containers/podman-desktop/pull/2873)) -- Add Minikube install docs ([PR #2824](https://github.com/containers/podman-desktop/pull/2824)) -- Add Minikube documentation ([PR #2694](https://github.com/containers/podman-desktop/pull/2694)) -- Updated Building an image procedure ([PR #2964](https://github.com/containers/podman-desktop/pull/2964)) -- Starting a container ([PR #2958](https://github.com/containers/podman-desktop/pull/2958)) -- Pulling an image ([PR #2956](https://github.com/containers/podman-desktop/pull/2956)) -- Updated selecting containers to run in a pod ([PR #2970](https://github.com/containers/podman-desktop/pull/2970)) -- Pushing an image to a registry ([PR #2969](https://github.com/containers/podman-desktop/pull/2969)) -- How to add an insecure registry ([PR #2953](https://github.com/containers/podman-desktop/pull/2953)) -- Add documentation for lima ([PR #2995](https://github.com/containers/podman-desktop/pull/2995)) -- Replace broken link to podman.io ([PR #2994](https://github.com/containers/podman-desktop/pull/2994)) -- Authenticating to a pre-configured registry ([PR #2965](https://github.com/containers/podman-desktop/pull/2965)) -- Lima is not a container engine ([PR #3051](https://github.com/containers/podman-desktop/pull/3051)) -- Using the Troubleshooting page ([PR #3083](https://github.com/containers/podman-desktop/pull/3083)) -- View and select your current Kubernetes context in the status bar ([PR #3090](https://github.com/containers/podman-desktop/pull/3090)) - ---- - -## Notable Bug Fixes - -- Deleting a running pod generates an error ([PR #2827](https://github.com/containers/podman-desktop/pull/2827)) -- If kubeconfig is empty, does not try to do things, cancel ([PR #2874](https://github.com/containers/podman-desktop/pull/2874)) -- Async telemetry startup ([PR #2885](https://github.com/containers/podman-desktop/pull/2885)) -- Do not block startup while waiting for kube resource refresh ([PR #2884](https://github.com/containers/podman-desktop/pull/2884)) -- Images list too wide ([PR #2918](https://github.com/containers/podman-desktop/pull/2918)) -- Compose deactivate function never called ([PR #2922](https://github.com/containers/podman-desktop/pull/2922)) -- Auto-scrolling on form pages, layout issues ([PR #2927](https://github.com/containers/podman-desktop/pull/2927)) -- Show current context in quick pick ([PR #2920](https://github.com/containers/podman-desktop/pull/2920)) -- Remove sticky position of loader in dashboard UI (#2535) ([PR #2959](https://github.com/containers/podman-desktop/pull/2959)) -- Undo change that broke the website ([PR #2992](https://github.com/containers/podman-desktop/pull/2992)) -- Detailspage, resizing & consistency ([PR #2987](https://github.com/containers/podman-desktop/pull/2987)) -- Quick pick click to close and over nav bar ([PR #2758](https://github.com/containers/podman-desktop/pull/2758)) -- Only show empty screen when no pods ([PR #2929](https://github.com/containers/podman-desktop/pull/2929)) -- Do not redirect to /pods when deleting pod in containerlist ([PR #2963](https://github.com/containers/podman-desktop/pull/2963)) -- Bulk delete on pods should call the pod deletion ([PR #2979](https://github.com/containers/podman-desktop/pull/2979)) -- Update nodejs version to 18 in .nvmrc to fix yarn install failure ([PR #2989](https://github.com/containers/podman-desktop/pull/2989)) -- Website check targets ([PR #2996](https://github.com/containers/podman-desktop/pull/2996)) -- Don't show exception to user ([PR #3034](https://github.com/containers/podman-desktop/pull/3034)) -- Interpret arguments given to info command for example ([PR #3015](https://github.com/containers/podman-desktop/pull/3015)) -- Change defaults for Podman machine ([PR #3061](https://github.com/containers/podman-desktop/pull/3061)) -- Normalize development/production folders path ([PR #3113](https://github.com/containers/podman-desktop/pull/3113)) -- Calculate machine socket path for linux ([PR #3070](https://github.com/containers/podman-desktop/pull/3070)) -- Replace backslash with slash so to support rendering on Windows (#3120) ([PR #3122](https://github.com/containers/podman-desktop/pull/3122)) -- Keep stdout and stderr in the error object for Docker Desktop extensions ([PR #3014](https://github.com/containers/podman-desktop/pull/3014)) -- Mark task completed if there's a failure ([PR #3016](https://github.com/containers/podman-desktop/pull/3016)) -- Handle invalid kubeconfig file ([PR #3129](https://github.com/containers/podman-desktop/pull/3129)) -- Do not remove registries when podman extension is stopping ([PR #3136](https://github.com/containers/podman-desktop/pull/3136)) -- Warning should be amber ([PR #3153](https://github.com/containers/podman-desktop/pull/3153)) -- Load user extensions from plugins as removable ([PR #3152](https://github.com/containers/podman-desktop/pull/3152)) -- Images with spaces in entrypoints or commands fail to start ([PR #3161](https://github.com/containers/podman-desktop/pull/3161)) -- Scrolling offscreen when clicking checkbox ([PR #3178](https://github.com/containers/podman-desktop/pull/3178)) -- Avoid messagebox expanding offscreen ([PR #2778](https://github.com/containers/podman-desktop/pull/2778)) -- Release-notes-generator run failure ([PR #2752](https://github.com/containers/podman-desktop/pull/2752)) -- Unable to do a new build if the previous one failed ([PR #2721](https://github.com/containers/podman-desktop/pull/2721)) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. - -A big shoutout to [@afbjorklund](https://github.com/afbjorklund), [@tuckerrc](https://github.com/tuckerrc) and [@evanshortiss](https://github.com/evanshortiss) who contributed to this release! - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.2.0) and [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.2.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-08-16-release-1.3.md b/website/blog/2023-08-16-release-1.3.md deleted file mode 100644 index a83da3dd3e..0000000000 --- a/website/blog/2023-08-16-release-1.3.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: Podman Desktop 1.3 Release -description: Podman Desktop 1.3 has been released! -slug: podman-desktop-release-1.3 -authors: [dgolovin] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.3.webp ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.3 Release! 🎉 - -This is essentially a bug-fix release to fix various issues in UI, extension engine and featured extensions, but as usual we also added a few new features. - -- **Podman 4.6.1**: Podman 4.6.1 included in Windows and Mac installers -- **Podman user-mode networking support to Windows/WSL**: A new switch `User mode networking' is available when creating Podman machine on Windows for Podman 4.6.0+ to configure podman to work in certain VPN setups and other specialized networking configurations -- **Compose group new UI elements**: You can now see summary, inspect, deploy to kubernetes, generate kube yaml and view logs -- **Extension packs and extension dependencies**: Install group of extensions in one click -- **Resource details page update**: See summary and log for resource -- **Create Kind cluster form update**: A new `Node's container image` field can be used to specify the Kubernetes version used for the control-planed -- **Support Docker Desktop extensions using a backend**: When loading Docker Desktop extension the containers are created in the backend as described in the vm section of extension's descriptor -- **Podman Initial Onboarding (preview)**: Install and configure podman using included installer - -Podman Desktop 1.3 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-3-bug-swatting](img/podman-desktop-release-1.3/title-bug-swatting.png) - ---- - -## Release Details - -### Compose group Logs tab [#3176](https://github.com/containers/podman-desktop/pull/3176) - -When clicking on the group of containers, you can now view the logs of the entire group of compose containers and don't need to open -log for every component individually. - -![Screenshot 2023-07-11 at 12 48 47 PM](https://github.com/containers/podman-desktop/assets/6422176/743a4ffc-e291-4697-8ac5-8052cc921946) - -### Podman user-mode networking support to Windows/WSL [#3251](https://github.com/containers/podman-desktop/pull/3251) - -Certain VPN setups or other specialized networking configs will block traffic from the virtual WSL networking device, resulting in the podman WSL backend from being unable to contact systems on the VPN, and potentially losing internet access altogether. The new switch allows you to create a podman VM machine configured to work correctly in mentioned above networking environments. - -![user](https://github.com/containers/podman-desktop/assets/620330/2f521576-b6a6-42b5-b24d-08df5b432608) - -### Compose group Summary tab [#3317](https://github.com/containers/podman-desktop/pull/3317) - -Compose group Summary tab shows all containers in the group and let you navigate to Details page for specific container. - - - -### Compose group Inspect tab [#3316](https://github.com/containers/podman-desktop/pull/3316) - -Compose group Inspect tab shows an array of "container inspect" from docker / podman. - - - -### `Deploy to kubernetes` in compose actions [#3299](https://github.com/containers/podman-desktop/pull/3295) - -A button to deploy to kubernetes added to Compose group. - - - -### `Generate Kube` in Compose actions and `Kube` tab in compose details [#3253](https://github.com/containers/podman-desktop/pull/3253) - -`Generate Kube` item added to Compose actions and "Kube" tab is now available in Compose details view. - - - -### Install multiple extensions using extension pack [#3150](https://github.com/containers/podman-desktop/pull/3150) - -An Extension pack introduced in Extension engine is a way to declare set of extensions to install them all at once. - - - -### Customize icons from extension [#3131](https://github.com/containers/podman-desktop/pull/3131) - -Extensions now can customize icons for list elements using `when` clause. - -![image](https://github.com/containers/podman-desktop/assets/49404737/7aae5347-4f07-4854-ba11-1f629b5ccf22) - -### Resource details page update [#1923](https://github.com/containers/podman-desktop/pull/1923) - -If you click on the arrow icon next to a resource name it will open up a details page (similarly to how clicking on a container in the container list opens up a details page). - -![image](https://user-images.githubusercontent.com/49404737/229542404-bae44f89-5cd3-4baf-8b08-e934e4462697.gif) - -### `Node's container image` field added to `Create a Kind cluster` form [#3508](https://github.com/containers/podman-desktop/pull/3508) - -The new `Node's container image` field can be used to specify the Kubernetes version used for the control-planed. - -![image](https://github.com/containers/podman-desktop/assets/42176370/b2a63faf-629f-436d-8496-7c0cd8158679) - -### Support Docker Desktop extensions using a backend [#3435](https://github.com/containers/podman-desktop/pull/3435) - -Podman Desktop now loads the containers in the backend as described in the vm section of extension descriptor the same way as Docker Desktop does. - -### Initial onboarding implementation for podman (experimental) [#3308](https://github.com/containers/podman-desktop/pull/3308) - -This is the initial implementation for the onboarding feature. It only covers a simple onboarding for podman. Check system requirements -> install podman -> yay! Done! - -### Notable Bug Fixes - -- Updating videos to be adaptive for mobile by @mairin [#3229](https://github.com/containers/podman-desktop/pull/3229) -- Consistent max width and padding in settings by @deboer-tim [#3232](https://github.com/containers/podman-desktop/pull/3232) -- Settings navigation bar resizing by @deboer-tim in [#3231](https://github.com/containers/podman-desktop/pull/3231) -- Move new registry button to header by @deboer-tim [#3245](https://github.com/containers/podman-desktop/pull/3245) -- Bigger lima logo by @afbjorklund [#3248](https://github.com/containers/podman-desktop/pull/3248) -- Horizontal docker logo by @afbjorklund [#3236](https://github.com/containers/podman-desktop/pull/3236) -- Respect LIMA_HOME environment variable by @afbjorklund [#3254](https://github.com/containers/podman-desktop/pull/3254) -- Add check before writing to terminal by @lstocchi [#3263](https://github.com/containers/podman-desktop/pull/3263) -- Wait until remote side has fully initialized the extensions by @benoitf [#3257](https://github.com/containers/podman-desktop/pull/3257) -- Fix loader not centered horizontally by @benoitf [#3270](https://github.com/containers/podman-desktop/pull/3270) -- Troubleshooting still waiting after failure by @deboer-tim [#3354](https://github.com/containers/podman-desktop/pull/3354) -- Store error for build by @cdrage [#3365](https://github.com/containers/podman-desktop/pull/3365) -- Missing checkbox tooltips by @deboer-tim [#3380](https://github.com/containers/podman-desktop/pull/3380) -- Load compose logs async not await by @cdrage [#3377](https://github.com/containers/podman-desktop/pull/3377) -- Set rootful connection when starting rootful machine by @lstocchi [#3364](https://github.com/containers/podman-desktop/pull/3364) -- Default last page by @deboer-tim [#3388](https://github.com/containers/podman-desktop/pull/3388) -- Avoid dashboard displaying providers starting while they don't by @benoitf [#3451](https://github.com/containers/podman-desktop/pull/3451) -- Do not use extensionInfo until it is defined by @benoitf [#V](https://github.com/containers/podman-desktop/pull/3450) -- Allow BASIC authentication (all caps) by @cdrage [#3471](https://github.com/containers/podman-desktop/pull/3471) -- Allow single domain registry such as localhost:5000 by @cdrage [#3468](https://github.com/containers/podman-desktop/pull/3468) -- Create /usr/local/bin directory if it does not exist on binary install by @cdrage [#3425](https://github.com/containers/podman-desktop/pull/3425) -- Only delete selected pods by @deboer-tim [#](https://github.com/containers/podman-desktop/pull/3378) -- Add back 'Done' text on the button by @benoitf [#3487](https://github.com/containers/podman-desktop/pull/3487) -- Do not wait for more than 5s when checking for podman ping by @benoitf [#3497](https://github.com/containers/podman-desktop/pull/3497) -- Add proxy support for extension using patching get and request approach by @dgolovin [#2825](https://github.com/containers/podman-desktop/pull/2825) -- Refresh component when field is updated by @benoitf [#3525](https://github.com/containers/podman-desktop/pull/3525) -- Higher-res icons for featured extensions list by @mairin [#3511](https://github.com/containers/podman-desktop/pull/3511) -- Main nav selection by @deboer-tim [#3510](https://github.com/containers/podman-desktop/pull/3510) -- kube event error when switching context by @jeffmaury [#3494](https://github.com/containers/podman-desktop/pull/3494) -- Reset error message each time we pull image by @benoitf [#3550](https://github.com/containers/podman-desktop/pull/3550) - -### Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. - -A warm welcome to [@rostalan](https://github.com/rostalan) and [@axel7083](https://github.com/axel7083) who made their first contribution to the project in this release. - -### Final Notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.3.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-09-18-release-1.4.md b/website/blog/2023-09-18-release-1.4.md deleted file mode 100644 index 4f2b8ba595..0000000000 --- a/website/blog/2023-09-18-release-1.4.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Podman Desktop 1.4 Release -description: Podman Desktop 1.4 has been released! -slug: podman-desktop-release-1.4 -authors: [jeffmaury] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.4/juggling.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.4 Release! 🎉 - -This is essentially a bug-fix release to fix various issues in UI, but as usual we also added a few new features. - -- **Podman 4.6.2**: Podman 4.6.2 included with Podman Desktop 1.4 -- **Windows Arm64**: Native Windows on Arm64 installers and binaries -- **Port range mapping**: Start containers and map a range of ports -- **Terminal UX Improvement**: Persistent terminal sessions when SSH'ing in a container -- **Volume Creation**: Create volume from the `Volumes` page -- **Bash support**: Terminals are now using `bash` if available. - -Podman Desktop 1.4 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-4-juggling](img/podman-desktop-release-1.4/juggling.png) - ---- - -## Release Details - -### Port range mapping [#3654](https://github.com/containers/podman-desktop/pull/3654) - -When starting a new container, you can now map a port range between host and container. If the range is not valid between host and container, an error is raised. - -![Range mapping](https://user-images.githubusercontent.com/49404737/262927546-da66b67a-0884-40b1-85bd-a9c3ea2f3f9e.gif) - -### Terminal lifetime [#3725](https://github.com/containers/podman-desktop/pull/3725) - -When a terminal is opened for a container, it can now be reused after you switched to another part of the Podman Desktop UI. - -![terminal lifetime](img/podman-desktop-release-1.4/terminal.gif) - -### Create volume [#3742](https://github.com/containers/podman-desktop/pull/3742) - -The Volumes tab now has a Create volume button. The Create volume UI has a single field for the volume name. - -![create volume](img/podman-desktop-release-1.4/create-volume.gif) - -### bash support [#3750](https://github.com/containers/podman-desktop/pull/3750) - -When a terminal is opened for a container, if bash is available within the container, it will be used. Otherwise, sh will be used. - -![bash sh](img/podman-desktop-release-1.4/bash-sh.gif) - -### Notable Bug Fixes - -- Reduce API calls when listing containers by @benoitf [#3489](https://github.com/containers/podman-desktop/pull/3489) -- Removing a connection(podman machine) should redirect to previous page by @benoitf [#3576](https://github.com/containers/podman-desktop/pull/3576) -- Enhance error message when image is not there by @benoitf [#3587](https://github.com/containers/podman-desktop/pull/3587) -- Dispose kind install button when extension is deactivated (#3586) by @lstocchi [#3610](https://github.com/containers/podman-desktop/pull/3610) -- Replace backslash/slash on windows when building image (#3465) by @lstocchi [#3618](https://github.com/containers/podman-desktop/pull/3618) -- Handle null value in container command (#3620) by @lstocchi [#3625](https://github.com/containers/podman-desktop/pull/3625) -- Add maximum activation time for extensions by @benoitf [#3446](https://github.com/containers/podman-desktop/pull/3446) -- Handle single non-spread arguments by @benoitf [#3641](https://github.com/containers/podman-desktop/pull/3641) -- Grab usage data of volumes only on-demand by @benoitf [#3635](https://github.com/containers/podman-desktop/pull/3635) -- Add arm64 binaries for Windows by @benoitf [#3643](https://github.com/containers/podman-desktop/pull/3643) -- Include right airgap file for Windows and arm64 by @benoitf [#3651](https://github.com/containers/podman-desktop/pull/3651) -- Redirect to previous page when removing a kubernetes connection by @benoitf [#3650](https://github.com/containers/podman-desktop/pull/3650) -- Support port ranges when starting a container (#3204) by @lstocchi [#3654](https://github.com/containers/podman-desktop/pull/3654) -- Add a strict undefined check to messagebox result (#3692) by @lstocchi [#3699](https://github.com/containers/podman-desktop/pull/3699) -- Only restart if a machine is running by @cdrage [#3491](https://github.com/containers/podman-desktop/pull/3491) -- Session to the terminal is reused by @benoitf [#3725](https://github.com/containers/podman-desktop/pull/3725) -- Disable next button and show try again when onboarding failed (#3616) by @lstocchi [#3711](https://github.com/containers/podman-desktop/pull/3711) -- Add podman in PATH after updating process environment PATH (#3729) by @lstocchi [#3730](https://github.com/containers/podman-desktop/pull/3730) -- Allow to create a volume by @benoitf [#3742](https://github.com/containers/podman-desktop/pull/3742) -- Using bash if available otherwise sh in terminal by @axel7083 [#3750](https://github.com/containers/podman-desktop/pull/3750) -- Allow to embed existing component to onboarding (#3755) by @lstocchi [#3763](https://github.com/containers/podman-desktop/pull/3763) -- Some containers never return logs, do not wait for them by @dgolovin [#3784](https://github.com/containers/podman-desktop/pull/3784) -- Remove cancel button when on final onboarding step (#3771) by @lstocchi [#3802](https://github.com/containers/podman-desktop/pull/3802) -- Onboarding add link micromark for commands by @cdrage [#3747](https://github.com/containers/podman-desktop/pull/3747) -- Mounts can be null when using older podman by @afbjorklund [#3806](https://github.com/containers/podman-desktop/pull/3806) -- Remove redundant step completion check when onboarding gets started by @lstocchi [#3798](https://github.com/containers/podman-desktop/pull/3798) -- Ability to click enter in pull image name input by @deboer-tim [#3850](https://github.com/containers/podman-desktop/pull/3850) -- Set proxy environment variable when launching process by @jeffmaury [#3838](https://github.com/containers/podman-desktop/pull/3838) -- The socket location was moved for new podman by @afbjorklund [#3853](https://github.com/containers/podman-desktop/pull/3853) -- Don't log console errors when activating lima extension by @afbjorklund [#3852](https://github.com/containers/podman-desktop/pull/3852) - -### Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. - -A warm welcome to [@tomgoren](https://github.com/tomgoren), [@Julian](https://github.com/Julian), [@Gelob](https://github.com/Gelob) and [@cedricclyburn](https://github.com/cedricclyburn) who made their first contribution to the project in this release. - -### Final Notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.4.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-11-03-release-1.5.md b/website/blog/2023-11-03-release-1.5.md deleted file mode 100644 index 6f28a06818..0000000000 --- a/website/blog/2023-11-03-release-1.5.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: Podman Desktop 1.5 Release -description: Podman Desktop 1.5 has been released! -slug: podman-desktop-release-1.5 -authors: duffy -tags: [podman-desktop, release, kubernetes, openshift, onboarding, compose, extensions, settings] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.5/onboarding-selkies.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.5 Release! 🎉 - -With this release of Podman Desktop, we're introducing **a new onboarding feature** that we hope will earn your 🦭 seal of approval! But wait... there's so much more! - -- **Onboarding**: Guided setup and configuration of **Podman** and **Compose** -- **Podman 4.7.2**: [Podman 4.7.2](https://github.com/containers/podman/releases) is now included in Windows and Mac installers -- **Command Palette**: Gain easy access to various commands via a new keyboard-driven command palette -- **Expanded "Summary" tab for Kubernetes pods**: Go deep with extended details on Kubernetes pods in the pod "Summary" tab -- **Environment file support**: Chart out environment variables for new containers to access on creation -- **Enhancements to the Settings area**: Get your bearings with improved Docker compatibility mode controls -- **Improved user experience for state changes**: No more dead reckoning on container state with improved visual indication of status -- **Extension API improvements**: A boatload of improvements to the extension API enabling more goodness from 🦭 Podman Desktop's extensions - -Podman Desktop 1.5 is now available. [Click here to download it](/downloads)! - -![Podman-desktop-1-5-hero](img/podman-desktop-release-1.5/onboarding-selkies.png) - ---- - -## Release Details - -### Onboarding - -We are introducing a new feature providing guided flows for the initial setup of specific 🦭 Podman Desktop extensions. Release 1.5 features two new onboarding flows: Podman and Compose. - -To start the Podman onboarding flow, you can start from the dashboard notification by clicking the "Set up" button: -![podman-onboarding-start](https://user-images.githubusercontent.com/799683/280362279-598cc052-5ea4-4c31-849c-da9bbbcc3e42.png) -![podman-onboarding](https://user-images.githubusercontent.com/799683/280363859-f35b85f8-1dd4-4b7f-a995-25fe5d1ccced.png) - -Visit **Settings > Resources** screen and click the Compose "Setup ..." button in order to start Compose onboarding: -![compose-onboarding-start](https://user-images.githubusercontent.com/799683/280276847-ca0558ab-70ad-48cc-8dd5-67e3eb465a62.png) -![compose-onboarding](https://user-images.githubusercontent.com/799683/280277936-77ba0fb2-5cb0-41de-a7cf-1a3d6400fd89.png) - -### Command Palette - -A new, search-driven command palette is now available to enable quick access to various commands available across 🦭 Podman Desktop. You can try this new tool out by hitting the F1 key. [#4081](https://github.com/containers/podman-desktop/pull/4081) && [#3979](https://github.com/containers/podman-desktop/pull/3979) - - - -### Expanded "Summary" tab for Kubernetes pods - -Kubernetes pods now offer a more comprehensive set of information under the "Summary" tab, including networking, volumes, environment variables, and other key metadata. - - - -### Environment file support - -When creating a container from the Images list, there's now an option to provide an environment file to set env variables for the new container. [#4026](https://github.com/containers/podman-desktop/pull/4026) && [#4025](https://github.com/containers/podman-desktop/pull/4025) - -### Enhancements to the Settings area - -The user experience for enabling or disabling Docker compatibility is improved, with a new entry in the **Settings > Preferences** screen that includes contextual guidance. [#4093](https://github.com/containers/podman-desktop/pull/4093) - - - -### Improved user experience for state changes - -The user experience around state changes for containers, pods, and other objects in the UI is improved, with clear status messages and improved animated visual indicator of state changes. [#4056](https://github.com/containers/podman-desktop/pull/4056) - - - - - -### Extension API improvements - -The 🦭 Podman Desktop extension API received many improvements, including: - -- In addition to pushing and listing image from an extension, it's now possible to pull images from a 🦭 Podman Desktop extension [#4155](https://github.com/containers/podman-desktop/pull/4155) - -- The 🦭 Podman Desktop extension API has been enhanced with both the ability to list images & networks and the ability to create containers & networks. [#4172](https://github.com/containers/podman-desktop/pull/4172) - -- 🦭 Podman Desktop extensions now have a consistent way to run administrative tasks. [#4049](https://github.com/containers/podman-desktop/pull/4049) - -- Extensions now have the ability to register a custom Kubernetes config generator. [#3970](https://github.com/containers/podman-desktop/pull/3970) - -- The ability of extensions to add commands to UI menus has been extended; previously for action menus it was only available in the Image list screen. It is now possible for extensions to add commands to the action menus of items listed on the Containers list screen as well. [#3947](https://github.com/containers/podman-desktop/pull/3947) & [#3963](https://github.com/containers/podman-desktop/pull/3963) - -- Extensions have gained the ability to contribute menu items in the UI based on specific conditions. [#3959](https://github.com/containers/podman-desktop/pull/3959) - -- Enhanced logic for displaying or hiding properties listed under the **Settings > Preferences** screens is now available. [#4159](https://github.com/containers/podman-desktop/pull/4159) - - - ---- - -## Other Notable Enhancements - -- The progress of loading an image into Kind is now visible as a task in the task manager. [#4061](https://github.com/containers/podman-desktop/pull/4061) - -![kind-progress-task](https://user-images.githubusercontent.com/42176370/270154775-eb7007b4-fd0e-4287-be9e-40ffc412de35.png) - -- It's now possible to start a new Podman machine right after creation, or you can create it and wait to start it later. It's up to you! [#4046](https://github.com/containers/podman-desktop/pull/4046) - -![podman-start-now-or-later](https://user-images.githubusercontent.com/49404737/269941187-f4768833-ecfc-4d0b-8acf-d4afedb428d9.png) - -- The Podman machine and Kubernetes provider creation forms have an updated look & feel consisted with other forms in the user interface, along with minor bug fixes. [#4317](https://github.com/containers/podman-desktop/pull/4317) - -![Updated provider creation forms](https://user-images.githubusercontent.com/19958075/274694157-fe89f3bc-e5b8-4735-96e9-669fe52c7a41.png) - -- The empty screen message shown when a search filter results in no matches now provides a message specific to filter matching, including the specific filter terms and an explicit button for clearing the filter. Previously, the screen displayed a generic message about how to create new objects of the type displayed on the screen, which led to some confusion about the status of the system. [#3988](https://github.com/containers/podman-desktop/pull/3988) - - - -- New support for adding spin animations to icons is now available. [#4188](https://github.com/containers/podman-desktop/pull/4188) - -- There is a new indeterminate progress bar type available for the task manager; this is meant for providing limited status for actions whose APIs do not provide detailed status information. [#4016](https://github.com/containers/podman-desktop/pull/4016) - -- For authenticating as admin to perform administrative tasks, 🦭 Podman Desktop now provides touchID support for macOS. [#4050](https://github.com/containers/podman-desktop/pull/4050) - -![touchID-support](https://user-images.githubusercontent.com/436777/248588015-f08115bd-d211-43ad-bddd-286d7b3a7056.png) - - - -- Support for connecting to interactive terminals for containers via tty was added. [#3900](https://github.com/containers/podman-desktop/pull/3900) - -- It's now more clear which container/pod providers will autostart when 🦭 Podman Desktop starts. Previously autostart had both a global and a per-provider setting. It has been simplified by removing the global setting. [#3840](https://github.com/containers/podman-desktop/pull/3840) - -- The "Working with containers" section of the documentation has been reworked and improved. [#3951](https://github.com/containers/podman-desktop/pull/3951) - ---- - -## Notable Bug Fixes - -- The disable registry command was not blocking subsequent pulls from disabled registries. This has been corrected. [#4183](https://github.com/containers/podman-desktop/pull/4183) - -- Some users behind network proxies were unable to complete workflow involving the download of online resources. The mechanism for fetching these resources has been fixed to be proxy compatible to address this issue. [#3994](https://github.com/containers/podman-desktop/pull/3994) - -- The status of pods running in Kubernetes that are undergoing the deletion process is now accurately reflected in the 🦭 Podman Desktop UI. [#3877](https://github.com/containers/podman-desktop/pull/3877) - -- The image details page always listed the image as being "not used" even when it was. This has been corrected. [#3985](https://github.com/containers/podman-desktop/pull/3985) - -- Previously, deleting a specific image tag would cause all tags with the same Image ID to be deleted. This has been fixed so only the selected image tag will be deleted. [#3837](https://github.com/containers/podman-desktop/pull/3837) - -- Could not view the pod details for some remote Kubernetes cluster pods due to an encoding error. [#4371](https://github.com/containers/podman-desktop/pull/4371) - -- Error logs were being tracked separately from the actions they applied to. This has been addressed for kind cluster creation failure [#4427](https://github.com/containers/podman-desktop/pull/4427) and Compose installation failure [#4407](https://github.com/containers/podman-desktop/pull/4407). - -- For Linux users, 🦭 Podman Desktop previously did not appear under the "Development" menu when installed via Flatpak; it appeared under "Utilities." 🦭 Podman Desktop now appears under the "Development" menu. [#3911](https://github.com/containers/podman-desktop/pull/3911) - -- Podman Machine names are no longer prefixed with the "Podman Machine" string. [#3878](https://github.com/containers/podman-desktop/pull/3878) - -![touchID-support](https://user-images.githubusercontent.com/19958075/267378447-aafdfbd7-f005-4b94-8626-9e11eec61b95.png) - -- The initial action state of pods and containers was being displayed as "STARTING" regardless of actual state; this has been corrected. [#3889](https://github.com/containers/podman-desktop/pull/3889) - -- Resizing the application window no longer makes the last lines of a container's terminal invisible. [#3993](https://github.com/containers/podman-desktop/pull/3993) - -- An issue with incorrect terminal behavior in response to long lines in the terminal attached to a container has been resolved. [#3955](https://github.com/containers/podman-desktop/pull/3955) - -- A spacing issue on the run image form has been corrected. [#4089](https://github.com/containers/podman-desktop/pull/4089) - -- The "podify" icon & button on the Containers list was unusually large in release 1.4. That regression has been corrected in this release. [#4122](https://github.com/containers/podman-desktop/pull/4122) - -- An error in the instructions for Windows users on migrating from Docker has been corrected. [#4157](https://github.com/containers/podman-desktop/pull/4157) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make 🦭 Podman Desktop even better. In this -release we received pull requests from the following people: - -- [afbjorklund](https://github.com/afbjorklund) in [#4373 - docs: fix broken links and add lima onboarding](https://github.com/containers/podman-desktop/pull/4373), [#4372 - docs: clear up lima column on containers page](https://github.com/containers/podman-desktop/pull/4372), [#4091 - fix: avoid errors with unexpected JSON input](https://github.com/containers/podman-desktop/pull/4091), [#4098 - docs: Lima onboarding details](https://github.com/containers/podman-desktop/pull/4098), and [#3854 - fix: check if machine init rootful flag supported](https://github.com/containers/podman-desktop/pull/3854) - -- [axel7083](https://github.com/axel7083) in [#4061 - feat: adding task progress for kind](https://github.com/containers/podman-desktop/pull/4061), [#3970 - feat: extension can register custom kube generator](https://github.com/containers/podman-desktop/pull/3970), [#3959 - feat: add when property to extensions menus](https://github.com/containers/podman-desktop/pull/3959), [#4016 - feat: indeterminate progress bar](https://github.com/containers/podman-desktop/pull/4016), [#3963 - fix: ContainerList propagating containers in ComposeActions and PodActions](https://github.com/containers/podman-desktop/pull/3963), and [#3947 - feat: extend menus capabilities](https://github.com/containers/podman-desktop/pull/3947) - -- [ayushrakesh](https://github.com/ayushrakesh) in [#4415 - Update README.md](https://github.com/containers/podman-desktop/pull/4415#) - -- [eltociear](https://github.com/eltociear) in [#4194 - Update README.md](https://github.com/containers/podman-desktop/pull/4194) - -- [foxydeveloper](https://github.com/foxydevloper) in [#4157 - docs: Correct windows instructions for migrating from docker](https://github.com/containers/podman-desktop/pull/4157) - -- [harsh-solanki21](https://github.com/harsh-solanki21) in [#4162 - fix: Removed fullstop from summary](https://github.com/containers/podman-desktop/pull/4162) - -- [rahul0x00](https://github.com/rahul0x00) in [#4240 - fix typos in README.md](https://github.com/containers/podman-desktop/pull/4240) - -- [shelar1423](https://github.com/shelar1423) in [#4221 - chore: document property setting in EXTENSIONS.md](https://github.com/containers/podman-desktop/pull/4221) - ---- - -## Final notes - -### Known Issues - -We have a discussion board topic where we have posted known issues with this release. If you run into problems, please check this list before filing a bug - if we already have an issue open for it, it saves you the time and trouble of filing, and there may be a workaround posted in the issue. - -#### [Known Issues: Podman Desktop 1.5.2](https://github.com/containers/podman-desktop/discussions/4635) - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.5.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2023-12-06-sharing-podman-images-with-kubernetes-cluster.md b/website/blog/2023-12-06-sharing-podman-images-with-kubernetes-cluster.md deleted file mode 100644 index b0eee81c76..0000000000 --- a/website/blog/2023-12-06-sharing-podman-images-with-kubernetes-cluster.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -title: Share your local podman images with the Kubernetes cluster -description: Build image and use it directly in your kubernetes cluster -slug: sharing-podman-images-with-kubernetes-cluster -authors: [benoitf] -tags: [podman-desktop, podman, images, kubernetes] -hide_table_of_contents: false -image: /img/blog/sharing-podman-images-with-kubernetes-cluster/selkie-family.png ---- - -As developers we constantly improve and refine our applications. One of the challenges we face is quickly iterating when working with container images and kubernetes deployments/pods. - -For example, when we want to try a new image in a kubernetes pod, this image needs to be available on a private/public registry or on the nodes of the kubernetes cluster. -Sometimes we need to call extra commands such as `kind load docker-image` or `minikube cache add ` or publish the image first to a 3rd party registry. - -You'll agree that trying out a new image in a Kubernetes pod should be as seamless as building the image itself. - -In this blog post, we will explore the best practice for streamlining the image iteration process in Kubernetes with Podman Desktop. - -![hero](img/sharing-podman-images-with-kubernetes-cluster/selkie-family.png) - - - ---- - -## Introduction - -When using kind or minikube or other 3rd party tools to setup a local kubernetes cluster, we have several ways to publish images. - -Minikube published 8 ways of doing that at [https://minikube.sigs.k8s.io/docs/handbook/pushing/](https://minikube.sigs.k8s.io/docs/handbook/pushing/) - -There are pros and cons either way. Using a third party registry implies that you need to publish the image after each build of the image before being able to use it in the kubernetes cluster. While Podman Desktop could automate the synchronization between the local registry (where you are doing `podman build`) and the third party registry, there remains a duplication of layers between the local and third party registry. And if you change the first layer, it can take a lot of time to send again all the data. - -Loading images requires to pack/unpack the files so it's not adequate for large images. - -Minikube offers a Podman environment, but it's important to note that it utilizes version 3.4 inside a container within the Podman machine. This implies the existence of two Podman instances. - -The 3.4 version is considerably outdated and do not provide new enhancements and support towards compose, compliance with Docker REST API and 3rd party tools. - -Could we just build the image and use it in kubernetes? - -## podman and kubernetes/cri-o - -In the kubernetes world, we need a container engine runtime. At the early stage, container runtimes were integrated with ad hoc solutions on top of docker, rkt, or others. - -But to separate concerns and to be extensible, a new interface was added: CRI for "Container Runtime Interface". Using the CRI interface we can plug container engines. And there are several runtimes such as containerd, cri-o and others. -[https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md) - -What is interesting to us is the cri-o project. This project is implementing the CRI interface but also adopting some projects of the [containers](https://github.com/containers) organization where [podman](https://github.com/containers/podman) and [podman-desktop](https://github.com/containers/podman-desktop) live. - -So it means cri-o uses image management from [https://github.com/containers/image](https://github.com/containers/image) project and handle storage with [https://github.com/containers/storage](https://github.com/containers/storage) project. - -And this is what is really interesting as a podman user. As it is using common libraries between cri-o and podman, it means that in the same environment, podman and cri-o read and write the images at a common location in `/var/lib/containers` folder. - -If we move one step ahead, if we mount the `/var/lib/containers` folder of podman into the cri-o container it means that the kubernetes cluster could find the images that the podman machine is building. - -Oh wait, it means that no more registry, additional steps would be required? Yes just build and load it. - -## Minikube to the rescue - -While we have the goal of using both cri-o and podman altogether, we can explore the current projects allowing us to quickly setup kubernetes clusters. - -### kind - -On the `kind` side, there is a default configuration that is using containerd and there is no plan to support an alternative such as cri-o [https://github.com/kubernetes-sigs/kind/issues/1369#issuecomment-867440704](https://github.com/kubernetes-sigs/kind/issues/1369#issuecomment-867440704) - -That said, some people try to maintain a way to do that but not officialy [https://gist.github.com/aojea/bd1fb766302779b77b8f68fa0a81c0f2](https://gist.github.com/aojea/bd1fb766302779b77b8f68fa0a81c0f2) - -By doing that, we would also need to mount `/var/lib/containers` folder from the host (the podman machine) to the container. And there is no easy flag in kind. - -### Minikube - -#### Minikube options - -Minikube supports more options and provides a way to select the container engine runtime. And it includes support for cri-o. - -We can use the `container-runtime` parameter and ask for `cri-o` runtime. Command-line should include `--container-runtime=cri-o`. - -Then, we do have a podman/cri-o environment inside the container and can use `eval $(minikube podman-env)` to use the Windows/macOS podman CLI. - -One issue is that we then have two 'Podman engines': one running inside the Podman machine and another one running inside the container. The Podman included in the container is using version 3.4, as it's on Debian/Ubuntu stable, while the recent version of Podman is 4.7.x. - -Can we mount the podman machine `/var/lib/containers` into the container ? - -Yes! minikube provides some options to do additional mount with `--mount-string` argument. It is not obvious but you also need to add the `--mount` in addition to this parameter. Full argument is `--mount --mount-string ""` - -But `/var` is already a mounted folder. So here the idea is to change the path of where cri-o is storing its data. - -So we can provide a custom mounted path and make cri-o use that custom location. Let's pickup `/host-containers`. - -When starting minikube we need then to add `--mount --mount-string "/var/lib/containers:/host-containers"`. - -Regarding the configuration of cri-o, currently, it's not achievable using Minikube options. However, Minikube does offer the flexibility to modify the base image. - -#### Minikube kicbase image - -Let's do our own base image named kicbase image. - -Minikube includes a default configuration file for cri-o. -[https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/02-crio.conf](https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/02-crio.conf) - -We need to change this default configuration to say that for storing the images, cri-o needs to use another directory. This new directory `/host-containers` will be mounted from the `/var/lib/containers` folder inside the podman machine. This is how cri-o is able to see podman images. - -Let's include the configuration part in this file. - -```toml -[crio] -root = "/host-containers/storage" -runroot = "/host-containers/storage" -``` - -Let's also upgrade the Podman inside the container by adding the necessary instruction to the Dockerfile. - -The Dockerfile is coming from [https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/Dockerfile#L178-L186](https://github.com/kubernetes/minikube/blob/v1.32.0/deploy/kicbase/Dockerfile#L178-L186) - -In the file, replace - -```Dockerfile -RUN clean-install podman && \ -``` - -with - - - -```Dockerfile -RUN sh -c "echo 'deb https://downloadcontent.opensuse.org/repositories/devel:/kubic:/libcontainers:/unstable/xUbuntu_22.04/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list" && \ - curl -LO https://downloadcontent.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_22.04/Release.key && \ - apt-key add - < Release.key && \ - # need to add dbus-user-session else we have - # cannot open sd-bus: No such file or directory: OCI runtime attempted to invoke a command that was not found - clean-install dbus-user-session podman && \ -``` - -Let's rebuild the image and publish it. You can find it at `quay.io/fbenoit/kicbase:multiarch-2023-11-06` . -To build the image, clone [https://github.com/kubernetes/minikube](https://github.com/kubernetes/minikube) repository, and edit the files referenced before. - -The command to build the kicbase image is `make local-kicbase`. - -#### Trying out cri-o using the podman machine storage folder - -At the time of writing this blog post, the version `v1.32.0-beta.0` has been used. For different versions you might need to build your own kicbase image. - -One crucial note: cri-o is running in root mode, this is why we mount to `/var/lib/containers` (and then in rootful mode). - -For simplicity, let's use a rootful podman machine to map the same folder at the two locations. - -Ok now let's try in two steps: - -1. Create a podman machine: - -```shell -podman machine init --cpus 4 --memory 6000 --rootful -``` - - - -2. Start the cluster using our kicbase image - -We specify Podman as the driver (the default is Docker), state our preference to use cri-o as the container runtime instead of containerd, use our custom image that performs the update of Podman's version and the modification of cri-o's configuration to use a different storage folder. Finally, we specify an additional mount. - -```shell -minikube start --driver=podman --container-runtime=cri-o --base-image=quay.io/fbenoit/kicbase:multiarch-2023-11-06 --mount --mount-string "/var/lib/containers:/host-containers" -``` - -#### Verification - -Assuming the cluster was able to start, there is a new kubernetes cluster configured in the `$HOME/.kube/config` file. - -Using `kubectl` we can start a deployment - -```shell -kubectl apply -f https://k8s.io/examples/application/deployment.yaml -``` - -you can check pods are running using - -```shell -kubectl get pods -l app=nginx -``` - -and if you check your podman images - -```shell -podman images -``` - -you'll see nginx being listed so images are shared. - -Now, you can build image using a Containerfile or pull an image, connect to the control plane instance in Podman Desktop (open a shell in minikube container) and run - -```shell -crictl images -``` - -It will list the images of podman - -Note: by default, kubernetes will use the image pull policy `Always` using the `latest` tag on your image. So it might try to fetch/pull/refresh the image you built locally. Use a specific tag or change the `imagePullPolicy` to `IfNotPresent` in your deployments. - -You can now use your own Containerfile/Dockerfile and build an image using `podman build` command. And then check that the image is available as well in the kubernetes cluster by listing images: - -```shell -crictl images -``` - -### Conclusion - -We have explored how developers can significantly reduce turnaround times by integrating Podman and Kubernetes seamlessly. - -Now, let's experiment with it and provide feedback through the Podman Desktop issue tracker at https://github.com/containers/podman-desktop/issues/ . - -Here are the next steps Podman Desktop plans to take to enhance ease of use for users: - -- To make things easier, automate the process by adding this setup to a creation wizard. -- Collaborate with upstream Minikube project to simplify choices and remove the requirement for customized kicbase images. -- Enhancing the overall user-friendliness of the solution for an improved developer experience. diff --git a/website/blog/2023-12-18-release-1.6.md b/website/blog/2023-12-18-release-1.6.md deleted file mode 100644 index 369b761334..0000000000 --- a/website/blog/2023-12-18-release-1.6.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Podman Desktop 1.6 Release -description: Podman Desktop 1.6 has been released! -slug: podman-desktop-release-1.6 -authors: slemeur -tags: [podman-desktop, release, kubernetes, openshift, onboarding, compose, extensions, settings] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.6/santaseal.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.6 Release! 🎉 - -![Podman-desktop-1-6-hero](img/podman-desktop-release-1.6/santaseal.png) - -This release introduces: - -- **Minikube Featured Extension**: Minikube extension to create local Kubernetes clusters in containers. -- **Podman 4.8.2**: [Podman 4.8.2](https://github.com/containers/podman/releases) is now included in Windows and Mac installers. -- **Setting Page for Command-Line Tools**: Manage and update your CLI tools. -- **Kubernetes Contexts Manager**: Browse all your kubernetes contexts, set default and remove unused ones. -- **Editable Podman Machine for MacOS**: Easy resize and reconfiguration of the Podman runtime environment. -- **Filters for Containers and Pods Lists**: Focus on the containers and Pods you are working with. -- **Sorting on Volumes and Images List**: Sort volumes or images with your prefered criterias. -- **Environment Colums on Containers and Pods lists**: Easy catch of the environment on which a container or a pod is running on. -- **Extension API Improvements**: Another set of improvements to the extension API enabling more goodness for 🦭 Podman Desktop's extensions. - -Podman Desktop 1.6 is now available. [Click here to download it](/downloads)! - ---- - -## Release Details - -### Minikube featured extension - -For developers who need to run Kubernetes locally and reproduce an environment close to production for development and experimentation purposes, Podman Desktop allows users to easily set up that environment on a local machine. There are two extensions providing the capability to configure a open source Kubernetes cluster locally, you can either choose between [Kind](https://kind.sigs.k8s.io/) or [Minikube](https://minikube.sigs.k8s.io/docs/). - -The Minikube extension allows you to install Minikube on your workstation and also to setup a Kubernetes cluster locally running in a container! Yes, you read that correctly - in a container similar to how Kind works. The advantage is that it's lighter and faster to start. With Minikube, one of the advantage, is that you can build your images locally with Podman and get them automatically available in your local Kubernetes cluster - which will speed up your turnarounds when you want to test your application. If you want to learn more this, read the [following blog post](https://podman-desktop.io/blog/sharing-podman-images-with-kubernetes-cluster). - -![Minikube-feature-extension](img/podman-desktop-release-1.6/minikube-feature-extension.png) - -### Command-Line Tools Configuration: Compose and Kubectl - -Configuring and managing your setup is getting easier with the addition of a new section in the Settings to manage command-line tools. In Podman Desktop, extensions can list command-line tools that are helpful to their users or required to make use of the installed extensions. - -There are two command-line tools within Podman Desktop that allows you to view whether they are installed or require an update: - -- Compose binary for running 'podman compose' commands. -- kubectl for interacting with Kubernetes clusters. - -![cli-tools](img/podman-desktop-release-1.6/cli-tools.png) - -From the settings you can see the command-line tools that are installed, and you can see the version - and when a new version is available, you'll get a small notification to allow you easily update to that version. - -![Compose-Update](img/podman-desktop-release-1.6/compose-update.png) - -### Kubernetes Contexts Manager - -We are introducing a new screen available from the Settings which allows you to easily manage your Kubernetes contexts. Podman Desktop was already providing the handy context switcher available from the status bar, but when you get to work with multiple Kubernetes environments, it's not uncommon to end with a big and long list of Kubernetes contexts. - -The new Kubernetes Contexts screen allows you to easily see all your registered Kubernetes contexts. You can use the screen to clean up your registered contexts, or set the current (default) context. - -![Kubernetes Contexts List](img/podman-desktop-release-1.6/kubernetes-contexts.png) - -### Editable Podman Machine - -A Podman machine is a virtual environment specifically designed to run Podman containers on Mac and Windows. It allows users to manage and operate containerized applications in an isolated and controlled setting. When creating a Podman machine, you configure its settings: memory, CPU(s) and disk size. - -We've received the feedback regarding the ability to reconfigure your Podman machine on the go. This is now possible for macOS users, and particularly useful when you start with an environment and need to scale it up based on new needs and containers you would like to run in your Podman environment. - -![Editable podman machine](https://github.com/containers/podman-desktop/assets/1636769/91150767-58a9-47b5-abbc-58d2d50f4fca) - -You'll notice we improved the sliders to configure the Podman machine's options - and also introduced a way to enter numeric values directly. - -### Tabs/Filters for Containers and Pods - -Being able to quickly identify the containers and the pods you are working with is critical when you are iterating on the development of your application. For this reason, we added filters at the top of the lists of Containers and Pods that allow you to easily view all the containers/pods, only those that are running, or only those that are stopped. - -![Filters for containers and pods](https://github.com/containers/podman-desktop/assets/1636769/37190c74-7fa5-485e-81a4-bd970f606286) - -### Sorting for Volumes and Images lists - -The lists of Volumes and Images have improved and are now have the ability to be sorted by the criteria of your choice. You can for example filter images by their size - which can be convenient when you want to clean up your environment. - -![Sorting for Volumes and Images](https://github.com/containers/podman-desktop/assets/1636769/0d20b5c2-517c-4ccc-8992-b8df275bcc30) - -### Environment columns on Containers and Pods lists - -Podman Desktop is able to work with multiple providers: it could work with multiple container engines and multiple Kubernetes environments too. In order to make it easier to identify the containers and the pods and differentiate them depending on which environment they are running onto, we are introducing a new environment column in the list of Containers and Pods to display a badge. - -![Environment Column](img/podman-desktop-release-1.6/environment-column.png) - -#### Better visibility to the containers running in Pods - -The list of Pods has been refined to provide easier visibility and access to the containers running within each of them. Each of the containers now have one dot and you can hover each dot to display the info about the container - and if you click on it you'll be able to access the details of the container. - -![Visibility for containers in Pods](https://github.com/containers/podman-desktop/assets/1636769/0e88a88e-9a17-4261-b60f-b4d09ca19127) - -### Extension API improvements - -The 🦭 Podman Desktop extension API received many improvements, including: - -- Documentation explaining how to create an onboarding workflow for an extension [#4837](https://github.com/containers/podman-desktop/pull/4837) -- Documented how extensions hook into UI [#4633](https://github.com/containers/podman-desktop/pull/4633) -- Documented how to implement api client [#4636](https://github.com/containers/podman-desktop/pull/4636) -- Image checker extension API [#4662](https://github.com/containers/podman-desktop/pull/4662) -- Added api to register cli updater [#5064](https://github.com/containers/podman-desktop/pull/5064) - ---- - -## Other Notable Enhancements - -- Show container connection type and endpoint [#5098](https://github.com/containers/podman-desktop/pull/5098) -- Environment column to pods/containers [#4583](https://github.com/containers/podman-desktop/pull/4583) -- Displaying extension icons in the list of extensions [#5101](https://github.com/containers/podman-desktop/pull/5101) -- Introduced UI icon image component [#5117](https://github.com/containers/podman-desktop/pull/5117) -- Added icon to extensionInfo [#5089](https://github.com/containers/podman-desktop/pull/5089) -- Added encoding option on RunOptions [#4942](https://github.com/containers/podman-desktop/pull/4942) -- Introduced property for appearance but for now only dark is supported [#4887](https://github.com/containers/podman-desktop/pull/4887) -- Default table sorting [#4860](https://github.com/containers/podman-desktop/pull/4860) -- Display notification for completed onboarding in task manager [#4811](https://github.com/containers/podman-desktop/pull/4811) -- Added purple dot when new content is available in dashboard [#4782](https://github.com/containers/podman-desktop/pull/4782) -- Argos CI: Introduce Argos CI to track and detect visual regressions on the website -- Added command palette: add enablement property [#4630](https://github.com/containers/podman-desktop/pull/4630) -- Added documentation for telemetry and usage data [#4618](https://github.com/containers/podman-desktop/pull/4619) -- Introduced table component [#4545](https://github.com/containers/podman-desktop/pull/4545) -- Added ability to abort build image [#4538](https://github.com/containers/podman-desktop/pull/4538) -- Added support in command palette for category [#4531](https://github.com/containers/podman-desktop/pull/4531) -- Upgraded flatpak to org.freedesktop.Platform version 23.08 [#3968](https://github.com/containers/podman-desktop/pull/3968) -- Added open exposed url to pod details [#3762](https://github.com/containers/podman-desktop/pull/3762) - ---- - -## Notable Bug Fixes - -- Fix reconnect to `/events` if disconnected [#4809](https://github.com/containers/podman-desktop/pull/4809) -- fix: reset loggerhandlerKey after restarting machine [#5168](https://github.com/containers/podman-desktop/pull/5168) -- fix: fix: podman machine created with wrong flags [#5178](https://github.com/containers/podman-desktop/pull/5178) -- fix: avoid to crash if configuration is invalid [#5182](https://github.com/containers/podman-desktop/pull/5182) -- fix: extension installation checks architecture and os [#5191](https://github.com/containers/podman-desktop/pull/5191) -- fix: use URL for proxy specification and add validation [#4825](https://github.com/containers/podman-desktop/pull/4825) -- fix: do not change color and underline of markdown buttons [#5138](https://github.com/containers/podman-desktop/pull/5138) -- fix: do not reconnect when connection is removed [#5131](https://github.com/containers/podman-desktop/pull/5131) -- fix: table headers shouldn't allow text selection [#5118](https://github.com/containers/podman-desktop/pull/5118) -- fix: add style to link [#5108](https://github.com/containers/podman-desktop/pull/5108) -- fix: launch.json references wrong script [#5094](https://github.com/containers/podman-desktop/pull/5094) -- fix: don't link to k8s cluster server [5087](https://github.com/containers/podman-desktop/pull/5087) -- fix: pass the complete imageInfo to the check function [#5069](https://github.com/containers/podman-desktop/pull/5069) -- fix: container tabs should match pods [#5057](https://github.com/containers/podman-desktop/pull/5057) -- fix: revert styling of disabled buttons [#5056](https://github.com/containers/podman-desktop/pull/5056) -- fix: update current context reactively [#5055](https://github.com/containers/podman-desktop/pull/5055) -- fix: make ProviderResultPage do not change input values [#5030](https://github.com/containers/podman-desktop/pull/5030) -- fix: add rowgroup to tables [#5005](https://github.com/containers/podman-desktop/pull/5005) -- fix: add path prop for route object [#4981](https://github.com/containers/podman-desktop/pull/4981) -- fix: remove errant hash mark [#4971](https://github.com/containers/podman-desktop/pull/4971) -- fix: check extension folder contains package.json [#4964](https://github.com/containers/podman-desktop/pull/4964) -- fix: refactor List UI components [#4953](https://github.com/containers/podman-desktop/pull/4953) -- fix: succeeded/completed state for Compose onboarding [#4947](https://github.com/containers/podman-desktop/pull/4947) -- fix: remove flex class from markdown button rendering [#4934](https://github.com/containers/podman-desktop/pull/4934) -- fix: unable to read wsl version when using chinese as syslang on Windows [#4918](https://github.com/containers/podman-desktop/pull/4918) -- fix: retain autostart setting [#4879](https://github.com/containers/podman-desktop/pull/4879) -- fix: use vi.waitUtnil instead of cycles with awaiting promises [#4861](https://github.com/containers/podman-desktop/pull/4861) -- fix: docker host on windows when executing compose command [#4855](https://github.com/containers/podman-desktop/pull/4855) -- fix: merged compose deploy to kube page in UI [#4827](https://github.com/containers/podman-desktop/pull/4827) -- fix: use URL for proxy specification and add validation [#4825](https://github.com/containers/podman-desktop/pull/4825) -- fix: reconnect to /events if disconnected [#4809](https://github.com/containers/podman-desktop/pull/4809) -- fix: remove fixed height after patternfly removal [#4804](https://github.com/containers/podman-desktop/pull/4804) -- fix background colours after patternfly removal [#4803](https://github.com/containers/podman-desktop/pull/4803) -- fix: report metrics for stopped machines [#4787](https://github.com/containers/podman-desktop/pull/4787) -- chore: update to docusaurus v3.0.0 [#4764](https://github.com/containers/podman-desktop/pull/4764) -- chore: drop patternfly [#4762](https://github.com/containers/podman-desktop/pull/4762) -- fix: avoid to send telemetry usage as this method is called every 5s [#4692](https://github.com/containers/podman-desktop/pull/4692) -- fix: location of roots.exe in devmode [#4654](https://github.com/containers/podman-desktop/pull/4654) -- fix: disable create/start container if any port is busy [#4637](https://github.com/containers/podman-desktop/pull/4637) -- fix: fix setup in build image tests [#4625](https://github.com/containers/podman-desktop/pull/4625) -- fix: find a free port [#4616](https://github.com/containers/podman-desktop/pull/4616) -- fix: reduce size of provider cards on the dashboard [#4615](https://github.com/containers/podman-desktop/pull/4615) -- fix: shorter doc nav section titles [#4613](https://github.com/containers/podman-desktop/pull/4613) -- fix: report error if container engine action fails in details page [#4556](https://github.com/containers/podman-desktop/pull/4556) -- fix: remove prev/next bar [#4548](https://github.com/containers/podman-desktop/pull/4548) -- fix: reduce website footer [#4546](https://github.com/containers/podman-desktop/pull/4546) -- fix: handle compose format json that is no longer a JSON array object [#4540](https://github.com/containers/podman-desktop/pull/4540) -- fix: disable push to kind menu item if pushing is in progress [#4530](https://github.com/containers/podman-desktop/pull/4530) -- fix: check for self signed cert message and use insecure param when editing registry password [#4523](https://github.com/containers/podman-desktop/pull/4523) -- fix: add autoscroll to summary pages [#4504](https://github.com/containers/podman-desktop/pull/4504) -- fix: report errors when analyzing extensions [#4380](https://github.com/containers/podman-desktop/pull/4380) -- fix: allow editing of build containerfile [#4471](https://github.com/containers/podman-desktop/pull/4471) -- refactor: updated compose onboarding installation [#4479](https://github.com/containers/podman-desktop/pull/4479) -- refactor: remove compose from the status bar [#4492](https://github.com/containers/podman-desktop/pull/4492) - ---- - -## Documentation - -Coming with this new version of 🦭 Podman Desktop, the documentation has been getting the following improvements: - -- Reorganize doc navigation by provider [#4558](https://github.com/containers/podman-desktop/pull/4558) -- Added vsc runtime dependency for Windows development [#5091](https://github.com/containers/podman-desktop/pull/5091) -- Show location of lima podman socket [#5090](https://github.com/containers/podman-desktop/pull/5090) -- Fixed typo in URI for releases [#4909](https://github.com/containers/podman-desktop/pull/4909) -- Explain how to create an onboarding workflow for an extension [#4837](https://github.com/containers/podman-desktop/pull/4837) -- Make it possible for lima to provide both [#4789](https://github.com/containers/podman-desktop/pull/4789) -- Blog post about minikube/sharing images [#4735](https://github.com/containers/podman-desktop/pull/4735) -- Remove duplicate text from windows troubleshooting [#4652](https://github.com/containers/podman-desktop/pull/4652) -- Add step to implement api client [#4636](https://github.com/containers/podman-desktop/pull/4636) -- Fixed the main lima command for limactl [#4623](https://github.com/containers/podman-desktop/pull/4623) -- Lima provider cleanup after the improvements in the implementation [#4622](https://github.com/containers/podman-desktop/pull/4622) -- Update documentation regarding auto merge [#4519](https://github.com/containers/podman-desktop/pull/4519) -- Using standard OS tabs for registries docs [#4497](https://github.com/containers/podman-desktop/pull/4497) -- Fixed mahine -> machine [#4495](https://github.com/containers/podman-desktop/pull/4495) -- Added screenshots and fixed formatting to the registries section [#4472](https://github.com/containers/podman-desktop/pull/4472) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make 🦭 Podman Desktop even better. In this -release we received pull requests from the following people: - -- [afbjorklund](https://github.com/afbjorklund) in [fix: add website target for running vale](https://github.com/containers/podman-desktop/pull/4547), [docs: the main lima command is limactl](https://github.com/containers/podman-desktop/pull/4623), [docs: lima provider cleanup after the merge](https://github.com/containers/podman-desktop/pull/4622), [docs: make it possible for lima to provide both](https://github.com/containers/podman-desktop/pull/4789), [fix: don't link to k8s cluster server](https://github.com/containers/podman-desktop/pull/5087), [feat: show the k8s namespace](https://github.com/containers/podman-desktop/pull/5088), [docs: show location of lima podman socket](https://github.com/containers/podman-desktop/pull/5090) - -- [axel7083](https://github.com/axel7083) in [refactoring: item formats from renderer/preferences in separate files](https://github.com/containers/podman-desktop/pull/3728), [feat: adding optional abort controller to dockerode api](https://github.com/containers/podman-desktop/pull/4364) - -- [ReadingShades](https://github.com/ReadingShades) in [docs: Added the environment variable set commands of the common windows terminal emulators](https://github.com/containers/podman-desktop/pull/4245) - -- [jannikbertram](https://github.com/jannikbertram) in [chore: add close button to troubleshooting and help page](https://github.com/containers/podman-desktop/pull/4457) - -- [singodiyashubham87](https://github.com/singodiyashubham87) in [fix: header line height issue on website](https://github.com/containers/podman-desktop/pull/4494) - -- [edvardsanta](https://github.com/edvardsanta) in [feat: remove redundant naming in buttons](https://github.com/containers/podman-desktop/pull/4518) - -- [Mayureshd-18](https://github.com/Mayureshd-18) in [fix typos](https://github.com/containers/podman-desktop/pull/4551) - -- [jgelens](https://github.com/jgelens) in [Fix rootless command](https://github.com/containers/podman-desktop/pull/4609) - -- [itecompro](https://github.com/itecompro) in [docs: remove duplicate text from windows troubleshooting](https://github.com/containers/podman-desktop/pull/4652) - -- [EricSmekens](https://github.com/EricSmekens) in [docs: Fixed typo in URI for releases](https://github.com/containers/podman-desktop/pull/4909) - -- [ecrookshanks-rh](https://github.com/ecrookshanks-rh) in [fix: added text beside icon for create pods](https://github.com/containers/podman-desktop/pull/5095) - ---- - -## Final notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.6.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-01-24-release-1.7.md b/website/blog/2024-01-24-release-1.7.md deleted file mode 100644 index 6ab19b7218..0000000000 --- a/website/blog/2024-01-24-release-1.7.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Podman Desktop 1.7 Release -description: Podman Desktop 1.7 has been released! -slug: podman-desktop-release-1.7 -authors: deboer -tags: [podman-desktop, release, kubernetes, openshift, onboarding, extensions, settings] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.7/renovations.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.7 Release! 🎉 - -![Podman-desktop-1-7-hero](img/podman-desktop-release-1.7/renovations.png) - -We've got a new release with a ton of seal appeal! This release introduces: - -- **Podman 4.9.0**: [Podman 4.9.0](https://github.com/containers/podman/releases) is now included in both Windows and Mac installers. -- **Extension API Improvements**: A big update to the extension API enabling more goodness for 🦭 Podman Desktop's extensions. -- **Experimental Kubernetes UI**: Get a sneak peek at the more advanced UI for working with Kubernetes clusters. -- **Enhanced Builds, Pods List, and Troubleshooting Pages**: Build for different platforms, an upgraded pods view, and more. - -Podman Desktop 1.7 is now available. [Click here to download it](/downloads)! - ---- - -## Release Details - -### Podman 4.9 - -🦭 Podman 4.9 includes key fixes for stability and reliability issues reported by our users. -If you've been floundering we highly recommend updating! - -If you are on a Mac M3, we are aware of a critical issue in Podman and expect to update very -soon to pick up this fix: -[#21353 - Update to new QEMU](https://github.com/containers/podman/issues/21353) (based on -[#1990 - QEMU issue on M3](https://gitlab.com/qemu-project/qemu/-/issues/1990)). If you are -hitting this problem there is a workaround [here](/docs/troubleshooting/troubleshooting-podman-on-macos#on-apple-silicon-the-podman-machine-does-not-start) and [there](https://github.com/containers/podman/issues/21088#issuecomment-1871502921). - -### Extension API Improvements - -We have spent a lot of time this release adding new extension API to give upcoming extensions -more capability and even better integration into 🦭 Podman Desktop. We have added support -for full page webviews, image badges, icons, a navigation API, and API access to more function -from the container engine: - -- Webview in the UI [#5594](https://github.com/containers/podman-desktop/pull/5594) -- Add webview API for extensions [#5592](https://github.com/containers/podman-desktop/pull/5592) -- Allow extensions to list webviews [#5628](https://github.com/containers/podman-desktop/pull/5628) -- Create container without starting it [#5643](https://github.com/containers/podman-desktop/pull/5643) -- Expose create/start Pod and replicatePodmanContainer [#5648](https://github.com/containers/podman-desktop/pull/5648) -- Expose create/list/delete volumes for extensions [#5598](https://github.com/containers/podman-desktop/pull/5598) -- Add getImageInspect to API [#5596](https://github.com/containers/podman-desktop/pull/5596) -- New contribution points for icon of image [#5543](https://github.com/containers/podman-desktop/pull/5543) -- Add BuildOption [#5533](https://github.com/containers/podman-desktop/pull/5533) -- Add platform parameter to image build method [#5501](https://github.com/containers/podman-desktop/pull/5501) -- Expose build image method [#5500](https://github.com/containers/podman-desktop/pull/5500) -- Navigation api [#5558](https://github.com/containers/podman-desktop/pull/5558) -- Register badges by extensions for image list/details [#5557](https://github.com/containers/podman-desktop/pull/5557) -- Install extensions from private registries [#5473](https://github.com/containers/podman-desktop/pull/5473) - -### Experimental Kubernetes UI - -We have been working the last couple months to expand our support for Kubernetes. This support isn't ready -to set sail yet, but if you're working with a Kubernetes cluster we'd love to start getting your feedback -on the direction! - -To 'break the seal' and try it out, go to Settings > Preferences > Kubernetes, and enable -the Experimental option: - -![Kubernetes Preference](img/podman-desktop-release-1.7/kube-preference.png) - -This will add three new items to the main navigation, allowing you to view -Deployments, Services, and Ingress & Routes: - -![Kubernetes Deployments](img/podman-desktop-release-1.7/kube-deployments.png) -![Kubernetes Services](img/podman-desktop-release-1.7/kube-services.png) -![Kubernetes Ingresses & Routes](img/podman-desktop-release-1.7/kube-ingress-routes.png) - -In this release you can click on deployments and services to view additional details (like the -Summary, Inspect, and YAML tabs), but not yet for ingresses or routes. - -We hope things go swimmingly for you, but please open Github issues to let us know what else you'd like to see. - -### Enhanced Builds, Pods List, and Troubleshooting Pages - -When building an image you can now chose which platform(s) to build the image for: - -![Build platform](img/podman-desktop-release-1.7/build-platform.png) - -We've upgraded the Pods view to use the same table component as images and volumes. This -allowing sorting and better column scaling: - -![Pods table](img/podman-desktop-release-1.7/pods-table.png) - -Having trouble and want a fresh start? The Troubleshooting page has switched to tabs and there is an -option to purge your existing install: - -![Troubleshooting Purge](img/podman-desktop-release-1.7/purge.png) - ---- - -## Other Notable Enhancements - -We added over 40 features this release, here are some of the other highlights: - -- Pressing esc exits onboarding [#5612](https://github.com/containers/podman-desktop/pull/5612) -- Quick pick case-insensitive filtering [#5582](https://github.com/containers/podman-desktop/pull/5582) -- Add UI badge component [#5522](https://github.com/containers/podman-desktop/pull/5522) -- Extend connection input type in build image [#5499](https://github.com/containers/podman-desktop/pull/5499) -- Nav sections [#5449](https://github.com/containers/podman-desktop/pull/5449) -- Improve Authentication Providers page [#5424](https://github.com/containers/podman-desktop/pull/5424) -- Adding groupContributions logic [#5415](https://github.com/containers/podman-desktop/pull/5415) -- Add option to select how to open devtools in dev mode [#5274](https://github.com/containers/podman-desktop/pull/5274) -- Form progress [#5253](https://github.com/containers/podman-desktop/pull/5253) -- Improved provider cards [#5013](https://github.com/containers/podman-desktop/pull/5013) - ---- - -## Notable Bug Fixes - -We squashed over 25 bugs this release, including the following: - -- Copy volume mounted when copying container to pod [#5640](https://github.com/containers/podman-desktop/pull/5640) -- Adding missing `Labels` property [#5632](https://github.com/containers/podman-desktop/pull/5632) -- Fix UI not being refreshed if container is only created [#5619](https://github.com/containers/podman-desktop/pull/5619) -- Quick pick filter removes selection [#5613](https://github.com/containers/podman-desktop/pull/5613) -- Add missing types for createContainer API [#5504](https://github.com/containers/podman-desktop/pull/5504) -- Use window.showMessageBox instead of custom modal [#5421](https://github.com/containers/podman-desktop/pull/5421) -- Add cleanupSupport property [#5309](https://github.com/containers/podman-desktop/pull/5309) -- Empty screen reset filter by default [#5307](https://github.com/containers/podman-desktop/pull/5307) -- Do not fetch pre-releases of compose [#5296](https://github.com/containers/podman-desktop/pull/5296) -- providerinfo badge [#5268](https://github.com/containers/podman-desktop/pull/5268) -- Don't refresh image list when age updates [#5267](https://github.com/containers/podman-desktop/pull/5267) -- Rename kubectl extension [#5255](https://github.com/containers/podman-desktop/pull/5255) -- Try to search kubectl on the user path first [#5248](https://github.com/containers/podman-desktop/pull/5248) -- Dispose the wsl2 command when unregistering extension [#5246](https://github.com/containers/podman-desktop/pull/5246) -- Handle event when loading images from archives [#5240](https://github.com/containers/podman-desktop/pull/5240) -- Edit Podman machine support for MacOS only [#5239](https://github.com/containers/podman-desktop/pull/5239) -- Improve default contribution action icon [#5236](https://github.com/containers/podman-desktop/pull/5236) -- Color of primary/secondary buttons should be white [#5232](https://github.com/containers/podman-desktop/pull/5232) -- Disable notification when updating podman (#5228) [#5229](https://github.com/containers/podman-desktop/pull/5229) -- Allow table columns to specify overflow [#5222](https://github.com/containers/podman-desktop/pull/5222) -- ProgressImpl properly middleware tasks to set the proper result status [#4342](https://github.com/containers/podman-desktop/pull/4342) - ---- - -## Documentation - -Along with this new version of 🦭 Podman Desktop the documentation has had the following improvements: - -- Update compose blog post link [#5547](https://github.com/containers/podman-desktop/pull/5547) -- Message when the app terminates because another instance exists [#5348](https://github.com/containers/podman-desktop/pull/5348) -- Document onboarding id rules [#5211](https://github.com/containers/podman-desktop/pull/5211) -- Multi-platform extension [#5205](https://github.com/containers/podman-desktop/pull/5205) -- Blog post on Compose guestbook application [#5033](https://github.com/containers/podman-desktop/pull/5033) -- Refactored _setting up container registries_ [#4965](https://github.com/containers/podman-desktop/pull/4965) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone (yes, that means you, Anders!) who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following people: - -- [Anders Björklund](https://github.com/afbjorklund) in [docs: minikube extension is now featured](https://github.com/containers/podman-desktop/pull/5638), [docs: kubernetes pushing image to minikube](https://github.com/containers/podman-desktop/pull/5637), [fix: small typo of click as clik](https://github.com/containers/podman-desktop/pull/5636), [Add more docs for Lima customization, with YAML and GUI](https://github.com/containers/podman-desktop/pull/5457), [docs: allow image push to lima kubernetes cluster](https://github.com/containers/podman-desktop/pull/4488), and [feat: allow image push to lima kubernetes cluster](https://github.com/containers/podman-desktop/pull/4487). - ---- - -## Final notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.7.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-01-29-run-webassembly-wasm-workloads-windows-and-macos.md b/website/blog/2024-01-29-run-webassembly-wasm-workloads-windows-and-macos.md deleted file mode 100644 index 7c0353c3dd..0000000000 --- a/website/blog/2024-01-29-run-webassembly-wasm-workloads-windows-and-macos.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -title: Unlock WebAssembly on macOS & Windows -description: Spinning a OCI container image containing a WebAssembly/Wasm workload on macOS or Windows should be as simple as running any other OCI image. -slug: wasm-workloads-on-macos-and-windows-with-podman -authors: [benoitf] -tags: [podman-desktop, wasm, wasi, WebAssembly] -hide_table_of_contents: false -image: /img/blog/run-webassembly-wasm-workloads-windows-and-macos/webassembly-podman.webp ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -Seamlessly run WebAssembly/Wasm binaries on macOS and Windows - -You might have heard excitement recently about Wasm and WASI. Imagine a world where you can effortlessly run Wasm binaries and distribute them using Open Container Initiative (OCI) container images – a singular image deployable across multiple architectures. - -Though the concept seemed straightforward, accomplishing this task proved to be quite challenging, particularly on macOS and Windows. The complexity comes from the additional virtual machine running Linux. This machine needs all of the dependencies and prerequisites correctly setup. - -The wait is over. Our blog post unveils the solution, guiding you through the process of enabling Wasm workloads on both macOS and Windows. - -![hero](img/run-webassembly-wasm-workloads-windows-and-macos/webassembly-podman.webp) - - - ---- - -## What is WebAssembly ? - -[WebAssembly](https://webassembly.org/) (abbreviated Wasm) was designed as a portable compilation target for programming languages, improving performance and portability of web applications (including gaming/emulators). Using a low-level binary format instead of JavaScript boosts applications to have near-native performance. - -The binary format serves as a compilation target and it allows to use a wider range of programming languages such as C, C++, and Rust. While it was a browser/client technology, now it is evolving beyond the web, for example being adapted for use as a back-end or edge technology (this is for example what happened to Java that was first designed for the client side before landing to the server side). - -The Wasm binary format was designed to be secure. Wasm modules are isolated from the rest of the system, and they cannot access any system resources without explicit permission. This makes Wasm modules very safe to run, even in untrusted environments. But on another hand, for developing backend applications, this restriction is limiting the usage of Wasm. - -## The extension of WebAssembly - -WebAssembly System Interface (WASI) was born as an essential complement to WebAssembly. - -It is a system interface that extends WebAssembly's capabilities beyond the browser, making it suitable for a wider range of environments, including servers, edge devices, and more. - -While with Wasm you had limited access to the host resources, WASI provides a standard set of system calls, enabling WebAssembly modules to interact with the host operating system in a secure and consistent manner: it includes filesystem access, sockets, and other low-level resources. - -## Running WebAssembly outside the browser - -Wasm has shipped in the major browser engines so the usage of Wasm is possible without any 3rd party addition in the browser land. But when it comes to the edge/system usage, you need to find a virtual machine to run these workloads supporting WASI extension. And there is not only one application to run them, there are several Wasm runtimes such as WasmEdge, Wasmtime, Wasmer, and so on. All runtimes support different CPU architectures. - -Since WASI is still maturing some of the API provided in these runtimes has not reached the standard, so users need to be careful to write portable applications that do not depend on a given runtime. - -Besides running Wasm/WASI workloads on your computer, there is also the question of how you package this binary format, share, and distribute it. A convenient way to distribute and run these workloads is to use OCI images as it provides all the basics: package, storage and distribution of the binaries. Then comes the execution part. - -## Using Podman engine with Wasm - -When using containers with Podman on macOS or Windows, you have a virtual machine called a "Podman machine" that is executing a Linux environment. We need to add support for Wasm inside this Linux environment. Podman is using the crun project as its OCI runtime, so crun needs to be able to run or delegate execution to Wasm runtimes. Lucky for us, crun supports Wasm execution. - -From the user's point of view, support for Wasm is provided as an additional platform. So when executing a Wasm workload, we specify as a platform `--platform=wasi/wasm` instead of for example `--platform=linux/arm64` or `--platform=linux/amd64`. - -​ - -## Running Wasm workload with podman - -### Setup - - - - -On Windows, ensure that your podman machine is a recent one. You can check using the `podman version` command. - -Depending on the output of the command, you might have extra steps to do. - -- Client's version and server's side version >= v4.7.0: Nothing to do, Wasm support is already there using the wasmedge runtime by default. -- Client's version >= 4.6.0 but server's side version < 4.7. You need to create a new podman machine using the command podman machine init --now wasm -- Old client/old server (< 4.7.0) or podman not being installed: follow the getting started at [podman.io](https://podman.io) - - - - -On macOS, ensure that your podman machine is a recent one. You can check using the `podman version` command. It requires v4.8+. - -Depending on the output of the command, you might have extra steps to do. - -- Client's version and server's side version >= v4.8.0: Nothing to do, Wasm support is already there using the wasmedge runtime by default. -- Client's version >= 4.8.0 but server's side version < 4.8. You need to create a new podman machine using the command podman machine init --now wasm -- Old client/old server (< 4.8.0) or podman not being installed: follow the getting started at [podman.io](https://podman.io) - - - - -### Running Wasm images - -Let's try with a simple hello world sample. - -We will use example coming from [https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world](https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world) - -There is already an OCI image on quay.io - -To run the workload, we will use the following command: - -```shell-shession -$ podman run --platform wasi/wasm quay.io/podman-desktop-demo/wasm-rust-hello-world -``` - -When running the command, you will see a Podman Hello World that was compiled using a Rust project using the println function and compiled into Wasm using `--target wasm32-wasi` parameter at compilation time. - -![Hello World example running](img/wasm-workloads-on-macos-and-windows-with-podman/wasm-hello-world.png) - -you can omit the `--platform wasi/wasm` flag but in that case you'll get a warning that the platform of the image is not matching the platform of your computer (`WARNING: image platform (wasi/wasm) does not match the expected platform (linux/arm64)`) - -From this point, you can run other OCI images using Wasm workloads, not only the podman hello world sample. - -**_NOTE:_** if you don't have the prerequisites installed in your podman machine you will see this error: `Error: requested OCI runtime crun-wasm is not available: invalid argument` - -In that case you should check that the prerequisites from the previous section are met. - -## Building Wasm OCI images with podman - -### Building with a specific platform/architecture - -Running Wasm workload is an interesting use case from a consumer point of view. It helps to consume Wasm binaries. But another interesting case is to distribute and build these Wasm images so anyone could run them quickly. - -The goal is to have a minimal image containing only the Wasm binary. For that we will use a multi-stage build. First stage will be the platform to build/compile the `.wasm` binary file and the second/last stage will copy the binary to a scratch image. - -When building images it will use by default the architecture of the host operating system. If you are using a Mac computer with ARM chip, then the Linux images will default to `linux/arm64`. Using a mac/intel it will default to `linux/amd64` images. In the case of Wasm workloads, the expected target platform is `wasi/wasm`. - -With podman we can use the flag `--platform=wasi/wasm` on the `podman build` command to specify the system/architecture. But if we do that, it means that if the Dockerfile or Containerfile contains as base image `FROM docker.io/redhat/ubi9-minimal` for example it will try to fetch a `ubi9-minimal` image using the `wasi/wasm` platform but of course it does not exist. - -So we need to tweak the Containerfile to include a `--platform` directive inside the Containerfile. - -Example of Containerfile: - -```Dockerfile -FROM --platform=$BUILDPLATFORM docker.io/redhat/ubi9-minimal as builder -``` - -Using this method, we will fetch an image matching our host architecture but as there is still the  `--platform=wasi/wasm` on the command line, the resulting image will use the right platform. - -### Source code - -Here is a simple Containerfile to build a rust application using wasm32-wasi binary output and a multi-layer OCI image. One layer for the build (installing rust, dependencies and compiling the application) and one scratch layer where we only add the `.wasm` output and flag it as the entrypoint. - -Source code is available at [https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world](https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world) - -`Containerfile` content: - -```Dockerfile -# Build using the host platform (and not target platform wasi/wasm) -FROM --platform=$BUILDPLATFORM docker.io/redhat/ubi9-minimal as builder - -# install rust and Wasm/WASI target -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y \ -    && source "$HOME/.cargo/env" && rustup target add wasm32-wasi - -# copy source code -COPY Cargo.toml /app/ -COPY src /app/src  - -# change working directory -WORKDIR /app - -# Build -RUN source "$HOME/.cargo/env" && cd /app && cargo build --target wasm32-wasi --release - -# now copy the Wasm binary and flag it as the entrypoint -FROM scratch -ENTRYPOINT [ "/rust-hello-world.wasm" ] -COPY --from=builder /app/target/wasm32-wasi/release/rust-hello.wasm /rust-hello-world.wasm -  -``` - -The `Cargo.toml` content: - -```toml -[package] -name = "rust-hello-world" -version = "0.1.0" -edition = "2021" - -[[bin]] -name = "rust-hello" -path = "src/main.rs" -``` - -And the rust program `src/main.rs`: - -```toml - - fn main() { - -    // use of strings literal for multi-line string -    // https://doc.rust-lang.org/reference/tokens.html#raw-string-literals - -    // ascii art from Máirín Duffy @mairin -    let hello = r#" -!... Hello Podman Wasm World ...! - -         .--"--. -       / -     - \ -      / (O)   (O) \ -   ~~~| -=(,Y,)=- | -    .---. /`  \   |~~ - ~/  o  o \~~~~.----. ~~ -  | =(X)= |~  / (O (O) \ -   ~~~~~~~  ~| =(Y_)=-  | -  ~~~~    ~~~|   U      |~~ - -Project:   https://github.com/containers/podman -Website:   https://podman.io -Documents: https://docs.podman.io -Twitter:   @Podman_io -"#; -    println!("{}", hello); -     -  } - -``` - -All the source code is available at [https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world](https://github.com/redhat-developer/podman-desktop-demo/tree/main/wasm/rust-hello-world) - -### Building Wasm images - -Run the command from the `wasm/rust-hello-world` folder if you cloned the repository or from the directory where all the files are present. - -```shell-session -$ podman build --platform=wasi/wasm -t rust-hello-world-wasm . -``` - -example of output will be : - -```console -[1/2] STEP 1/6: FROM docker.io/redhat/ubi9-minimal AS builder -Trying to pull docker.io/redhat/ubi9-minimal:latest... -Getting image source signatures -Copying blob sha256:472e9d218c02b84dcd7425232d8b1ac2928602de2de0efc01a7360d1d42bf2f6 -Copying config sha256:317fc66dad246d1fac6996189a26f85554dc9fc92ca23bf1e7bf10e16ead7c8c -Writing manifest to image destination -[1/2] STEP 2/6: RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y     && source "$HOME/.cargo/env" && rustup target add wasm32-wasi -info: downloading installer -info: profile set to 'default' -info: default host triple is aarch64-unknown-linux-gnu -info: syncing channel updates for 'stable-aarch64-unknown-linux-gnu' -info: latest update on 2023-10-05, rust version 1.73.0 (cc66ad468 2023-10-03) -info: downloading component 'cargo' -info: downloading component 'clippy' -info: downloading component 'rust-docs' -info: downloading component 'rust-std' -info: downloading component 'rustc' -info: downloading component 'rustfmt' -info: installing component 'cargo' -info: installing component 'clippy' -info: installing component 'rust-docs' -info: installing component 'rust-std' -info: installing component 'rustc' -info: installing component 'rustfmt' -info: default toolchain set to 'stable-aarch64-unknown-linux-gnu' - -  stable-aarch64-unknown-linux-gnu installed - rustc 1.73.0 (cc66ad468 2023-10-03) - - -Rust is installed now. Great! - -To get started you may need to restart your current shell. -This would reload your PATH environment variable to include -Cargo's bin directory ($HOME/.cargo/bin). - -To configure your current shell, run: -source "$HOME/.cargo/env" -info: downloading component 'rust-std' for 'wasm32-wasi' -info: installing component 'rust-std' for 'wasm32-wasi' ---> c93a3433d432 -[1/2] STEP 3/6: COPY Cargo.toml /app/ ---> cf4488993835 -[1/2] STEP 4/6: COPY src /app/src ---> 531b9389857c -[1/2] STEP 5/6: WORKDIR /app ---> 23379392f585 -[1/2] STEP 6/6: RUN source "$HOME/.cargo/env" && cd /app && cargo build --target wasm32-wasi --release -   Compiling rust-hello-world v0.1.0 (/app) -    Finished release [optimized] target(s) in 0.15s ---> e3582e06f45b -[2/2] STEP 1/3: FROM scratch -[2/2] STEP 2/3: ENTRYPOINT [ "/rust-hello-world.wasm" ] ---> 069b1742d906 -[2/2] STEP 3/3: COPY --from=builder /app/target/wasm32-wasi/release/rust-hello.wasm /rust-hello-world.wasm -[2/2] COMMIT rust-hello-world-wasm ---> e0948298c0be -Successfully tagged localhost/rust-hello-world-wasm:latest -e0948298c0be20e11da5d92646a2d6453f05e66671f72f0f792c1e1ff8de75ba -``` - -This is a multi-stage build but at the end we only have a small image containing the Wasm binary. - -Launch it quickly using - -```shell-session -$ podman run rust-hello-world-wasm -``` - -and we'll see the expected output - -```console -WARNING: image platform (wasi/wasm/v8) does not match the expected platform (linux/arm64) - -!... Hello Podman Wasm World ...! - -         .--"--. -       / -     - \ -      / (O)   (O) \ -   ~~~| -=(,Y,)=- | -    .---. /`  \   |~~ - ~/  o  o \~~~~.----. ~~ -  | =(X)= |~  / (O (O) \ -   ~~~~~~~  ~| =(Y_)=-  | -  ~~~~    ~~~|   U      |~~ - -Project:   https://github.com/containers/podman -Website:   https://podman.io -Documents: https://docs.podman.io -Twitter:   @Podman_io - -``` - -​ - -## Conclusion - -After witnessing the seamless execution and creation of WebAssembly (Wasm) workloads on both Windows and macOS through the utilization of podman, the possibilities are at your fingertips. - -Now, the initiative lies with you to embark on your journey of exploring, experimenting, and pushing the boundaries. - -Run and build new examples and do not hesitate to contribute to the podman community by reporting and discussing these issues. diff --git a/website/blog/2024-02-20-podman-desktop-wins-devies-award.md b/website/blog/2024-02-20-podman-desktop-wins-devies-award.md deleted file mode 100644 index d372547863..0000000000 --- a/website/blog/2024-02-20-podman-desktop-wins-devies-award.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Podman Desktop Wins 2024 DEVIES Award -description: Podman Desktop has been awarded the 2024 DEVIES Award for Best Innovation in Containers & Kubernetes. -slug: podman-desktop-wins-devies-award -authors: [cedric] -tags: [podman-desktop, podman, award, containers, kubernetes] -hide_table_of_contents: false -image: /img/podman-desktop-wins-devies-award/devies-celebration.png ---- - -We’re honored to announce that [Podman Desktop](https://podman-desktop.io/) has been recognized with the prestigious [**2024 DEVIES Award**](https://www.developerweek.com/awards/) in the category of **Containers & Kubernetes**. This award is a testimony to the effectiveness of the Podman Desktop team and greater open-source community's efforts to help developers. Podman Desktop increases developer container workflow efficiency as well as provides [an easy transition of applications from containers to Kubernetes](https://developers.redhat.com/articles/2023/11/06/working-kubernetes-podman-desktop), the leading open-source container orchestration platform. - -“While Podman Desktop only went into general availability last year, the community response has been very impressive and incredibly gratifying. We are extremely proud to receive this outstanding recognition which celebrates passion, commitment and innovation for shaping the future of container development backed by the vibrant open-source standards that Red Hat supports. ” said [Stévan Le Meur](https://twitter.com/stevanlm), _Product Manager_ on the Podman Desktop team. - -![hero](/img/blog/podman-desktop-wins-devies-award/devies-celebration.png) - -## What are the DEVIES awards? - -The DEVIES Awards, presented by [DeveloperWeek](https://www.developerweek.com/), recognize the most innovative and impactful tools, platforms, and technologies in the software development community. Podman Desktop's win as the _best innovation in Containers & Kubernetes_ highlights its significant impact on the industry and its role in revolutionizing the way developers build, ship, and run their applications. DEVIES Award winners are selected from hundreds of nominees by the independent, industry-leading DevNetwork Advisory Board. - -## Join us in celebrating! - -We’re excited to be receiving this award on stage at DeveloperWeek 2024, happening on February 21-23, 2024, in Oakland, CA and February 27-29, 2024 (Virtually). In addition, Red Hat developer advocate [Cedric Clyburn](https://github.com/cedricclyburn) will be presenting a session on Podman Desktop, titled “[Going from Containers, to Pods, to Kubernetes – Help for Your Developer Environments!](https://sched.co/1XZ7k)”, with a full presentation on [Podman](https://podman.io/), a demonstration of the Podman Desktop experience, and a multi-tier application going from containers, to pods, to finally Kubernetes! - -Finally, it would be seal-y to not include and acknowledge that this award was earned by the [entire Podman Desktop community of contributors](https://github.com/containers/podman-desktop/graphs/contributors)! We also thank the DevNetwork Advisory Board and DeveloperWeek for this honorable award, and for the opportunity to share Podman Desktop's innovations with the greater developer community. diff --git a/website/blog/2024-03-07-release-1.8.md b/website/blog/2024-03-07-release-1.8.md deleted file mode 100644 index 19c6207c94..0000000000 --- a/website/blog/2024-03-07-release-1.8.md +++ /dev/null @@ -1,232 +0,0 @@ ---- -title: Podman Desktop 1.8 Release -description: Podman Desktop 1.8 has been released! -slug: podman-desktop-release-1.8 -authors: deboer -tags: [podman-desktop, release, kubernetes, openshift, onboarding, extensions, settings] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.8/selkie-family.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.8 Release! 🎉 - -![Podman-desktop-1-8-hero](img/podman-desktop-release-1.8/selkie-family.png) - -We've got a new release with a ton of seal appeal! This release introduces: - -- **Podman 4.9.3**: [Podman 4.9.3](https://github.com/containers/podman/releases/tag/v4.9.3) is now included in both Windows and Mac installers. -- **Kubernetes Explorer**: Advanced UI and new tools for working with Kubernetes clusters. -- **Global Onboarding**: Configure and set up your environment without any hassle, with a set of guided workflows. -- **Learning Center**: Discover new use cases and capabilities for developers. -- **Extension API Improvements**: Another big update to the extension API enabling more goodness for 🦭 Podman Desktop's extensions. -- **Enhanced Builds, Pods List, and Troubleshooting Pages**: Build for different platforms, an upgraded pods view, and more. - -Podman Desktop 1.8 is now available. [Click here to download it](/downloads)! - ---- - -## Release Details - -### Podman 4.9.3 - -🦭 Podman 4.9.3 includes key fixes for stability and reliability issues reported by our users - especially -if you are using Apple silicon architecture. If you've been floundering we highly recommend updating! - -### Kubernetes Explorer - -Progressively introduced in past releases as an experimental feature, we're ready to expand -our capabilities to help developers transition from containers to Kubernetes. In this release we -are introducing a new set of features that enable the developers to work with more Kubernetes -resources, offering more granular and interactive control over your applications. - -Now available in 🦭 Podman Desktop is a new Kubernetes Explorer with the ability to work with -Deployments, Services, Ingresses, and Routes, in addition to the existing support for Pods. -For each of those resources, 🦭 Podman Desktop provides real-time information about the status of -resources on the cluster. From the top right of this overview page you can also click Apply YAML -to create or update resources on the cluster similar to 'kubectl apply -f', and see the current -connection status. - -![Deployments Overview](img/podman-desktop-release-1.8/deployments.png) - -Just like with local containers or images, you can click for more -details on Summary, Inspect, and Kube (YAML) pages. - -![Deployment Summary](img/podman-desktop-release-1.8/deployment-summary.png) - -See a problem? You can edit and apply changes direct from the Kube tab. - -![Deployment Kube YAML](img/podman-desktop-release-1.8/deployment-edit.png) - -🦭 Podman Desktop continues to bridge the gap and discrepancies to empower developers working -with containers with efficient workflows to target Kubernetes from their local workstation. -This is all in addition to some of the great features already available: - -- Native Kubernetes support with Podman -- Podify - transition containers into Pods -- Setting up local Kubernetes environments with Minikube and Kind extensions -- Deploy to Kubernetes and push local image from Podman to a Kubernetes environments -- Managing Kubernetes contexts -- Connecting to remote Kubernetes clusters - -### Global Onboarding - -Configuring and setting up a local environment is now easier with the introduction of a new -wizard-based onboarding flow. In this flow developers can pick the different tools that they -need, and 🦭 Podman Desktop will walk them through the configuration and setup of each of these -tools. - -The global onboarding flow allows developers to configure Podman, Compose, and kubectl -(needed for working with Kind and Minikube or remote Kubernetes environments). This makes -the transition to 🦭 Podman Desktop becomes simpler, as any needed dependencies are -automatically configured. - -![Global Onboarding](img/podman-desktop-release-1.8/global-onboarding.png) - -### Learning Center - -In this release, we've added a Learning Center on the Dashboardm enabling developers to -discover, learn, and expand their knowledge on related topics to containerization. These -guides are handy and easily accessible, and cover topics from learning how to containerize -an existing application to discovering the latest features of 🦭 Podman Desktop and how to -best use them. - -![Learning Center](img/podman-desktop-release-1.8/learning-center.png) - -### Extension API Improvements - -We continued spent a lot of time adding new extension API to give upcoming extensions -more capabilites and even better integration into 🦭 Podman Desktop: - -- Split getMatchingPodmanEngine [#6160](https://github.com/containers/podman-desktop/pull/6160) -- Add HealthCheck parameter when creating container [#5981](https://github.com/containers/podman-desktop/pull/5981) -- Expose listPods to extensions [#5864](https://github.com/containers/podman-desktop/pull/5864) -- Labels for createPod [#5862](https://github.com/containers/podman-desktop/pull/5862) -- Allow to create containers within a pod [#5848](https://github.com/containers/podman-desktop/pull/5848) -- OpenPod should redirect to the pod's view [#5846](https://github.com/containers/podman-desktop/pull/5846) -- Enhance createContainer API with missing parameters [#6011](https://github.com/containers/podman-desktop/pull/6011) -- Allow extensions to use openDialog/saveDialog [#6009](https://github.com/containers/podman-desktop/pull/6009) -- Allow customized icons in contributed actions [#5995](https://github.com/containers/podman-desktop/pull/5995) -- Adding missing types [#6213](https://github.com/containers/podman-desktop/pull/6213) -- Allow to navigate to a specific webview from extensions [#5899](https://github.com/containers/podman-desktop/pull/5899) -- Expose stopPod and removePod to extensions [#5898](https://github.com/containers/podman-desktop/pull/5898) -- Use new API for open/save dialog [#6051](https://github.com/containers/podman-desktop/pull/6051), [#6050](https://github.com/containers/podman-desktop/pull/6050), [#6049](https://github.com/containers/podman-desktop/pull/6049) -- Extend Podman Desktop API Build Image parameters [#5882](https://github.com/containers/podman-desktop/pull/5882) -- Allow extension to stats container [#6211](https://github.com/containers/podman-desktop/issues/6211) - ---- - -## Other Notable Enhancements - -We've added over 40 features this release, here are some other highlights: - -- Improve Podman Desktop update alert [#6068](https://github.com/containers/podman-desktop/pull/6068) -- Add gather & download logs button in troubleshooting [#5119](https://github.com/containers/podman-desktop/pull/5119) -- Enable podman machine for Linux [#5902](https://github.com/containers/podman-desktop/pull/5902) -- Multi-delete animation [#5717](https://github.com/containers/podman-desktop/pull/5717) -- Image deletion animation [#5709](https://github.com/containers/podman-desktop/pull/5709) -- Volume deletion animation [#5707](https://github.com/containers/podman-desktop/pull/5707) -- Open OpenShift routes [#5560](https://github.com/containers/podman-desktop/pull/5560) -- Add open created pod details [#4499](https://github.com/containers/podman-desktop/pull/4499) -- Use https when deploying to kubernetes cluster [#5824](https://github.com/containers/podman-desktop/pull/5824) -- Getting started carousel on dashboard page [#5142](https://github.com/containers/podman-desktop/pull/5142) -- Add confirmation dialog when deleting objects [#5445](https://github.com/containers/podman-desktop/pull/5445) - -We've also made some significant progress on implementing light mode: - -- Use theme colors for invert content [#6029](https://github.com/containers/podman-desktop/pull/6029) -- Use theme colors for secondary nav [#6028](https://github.com/containers/podman-desktop/pull/6028) -- Apply theme colors for global nav [#6027](https://github.com/containers/podman-desktop/pull/6027) -- Apply theme colors for the titlebar [#6025](https://github.com/containers/podman-desktop/pull/6025) -- Consistent close button [#6060](https://github.com/containers/podman-desktop/pull/6060) -- Use components in quickpick [#6057](https://github.com/containers/podman-desktop/pull/6057) -- Provide css colors to webviews [#5963](https://github.com/containers/podman-desktop/pull/5963) -- Publish colors to the style of the app [#5962](https://github.com/containers/podman-desktop/pull/5962) -- Allow extensions to contribute themes with set of colors [#5961](https://github.com/containers/podman-desktop/pull/5961) -- Store for colors [#5960](https://github.com/containers/podman-desktop/pull/5960) -- Include a color registry [#5958](https://github.com/containers/podman-desktop/pull/5958) -- Add utility method to get the value of the theme [#5947](https://github.com/containers/podman-desktop/pull/5947) -- Send event when operating system change the colors [#5946](https://github.com/containers/podman-desktop/pull/5946) -- Cleanup dark: prefix colors [#5944](https://github.com/containers/podman-desktop/pull/5944) -- Extract color palette to its own file [#5931](https://github.com/containers/podman-desktop/pull/5931) -- Input component [#5904](https://github.com/containers/podman-desktop/pull/5904) -- Input errors, use input component in run image [#5988](https://github.com/containers/podman-desktop/pull/5988) -- Use input when building image [#5986](https://github.com/containers/podman-desktop/pull/5986) -- Use input for proxy settings [#5943](https://github.com/containers/podman-desktop/pull/5943) -- Use input for registries [#5939](https://github.com/containers/podman-desktop/pull/5939) -- Use input when creating pod from containers [#5935](https://github.com/containers/podman-desktop/pull/5935) -- Use input component in extension pages [#5934](https://github.com/containers/podman-desktop/pull/5934) -- Use input in create volume [#5933](https://github.com/containers/podman-desktop/pull/5933) -- Use input when renaming image [#5964](https://github.com/containers/podman-desktop/pull/5964) -- Use checkbox component in deploy to kube [#6030](https://github.com/containers/podman-desktop/pull/6030) - ---- - -## Notable Bug Fixes - -We squashed a lot of bugs this release, including the following: - -- Copy volume mounted when copying container to pod [#5640](https://github.com/containers/podman-desktop/pull/5640) -- Change order of "Create" button on Volumes and Containers list [#6092](https://github.com/containers/podman-desktop/pull/6092) -- Refresh onboarding item when context value gets updated (#4597) [#6173](https://github.com/containers/podman-desktop/pull/6173) -- Better log on informer connection error [#6158](https://github.com/containers/podman-desktop/pull/6158) -- Website: replace broken links [#6111](https://github.com/containers/podman-desktop/pull/6111) -- Center empty screens [#6077](https://github.com/containers/podman-desktop/pull/6077) -- Do not ask confirmation to open local folder/files [#5743](https://github.com/containers/podman-desktop/pull/5743) -- Force breadcrumbs to be vertically aligned [#5741](https://github.com/containers/podman-desktop/pull/5741) -- Long usernames in auth page [#5737](https://github.com/containers/podman-desktop/pull/5737) -- Message property used to update task name [#5731](https://github.com/containers/podman-desktop/pull/5731) -- Nav item UI fixes [#5886](https://github.com/containers/podman-desktop/pull/5886) -- Display back the icons of registries [#5843](https://github.com/containers/podman-desktop/pull/5843) -- Check route tls to use either http or https [#5825](https://github.com/containers/podman-desktop/pull/5825) -- White tooltips [#5887](https://github.com/containers/podman-desktop/pull/5887) -- Limit registry username column width [#5718](https://github.com/containers/podman-desktop/pull/5718) -- Remove docker compatibility warning & button on Linux [#5903](https://github.com/containers/podman-desktop/pull/5903) -- Image usage by containers [#5663](https://github.com/containers/podman-desktop/pull/5663) -- Current context should be changed/updated when deleting it [#5819](https://github.com/containers/podman-desktop/pull/5819) -- Do not collapse categories on sidebar [#5727](https://github.com/containers/podman-desktop/pull/5727) -- Make localhost a valid domain for env.openExternal calls [#5716](https://github.com/containers/podman-desktop/pull/5716) - ---- - -## Documentation - -Along with this new version of 🦭 Podman Desktop the documentation has had the following improvements: - -- Container and image related methods of containerEngine api [#5891](https://github.com/containers/podman-desktop/pull/5891) -- Removed installing podman with openshift local [#6070](https://github.com/containers/podman-desktop/pull/6070) -- Document image checker provider API [#5813](https://github.com/containers/podman-desktop/pull/5813) -- Adding withProgress api docs [#5736](https://github.com/containers/podman-desktop/pull/5736) -- Added link to the troubleshooting page [#5734](https://github.com/containers/podman-desktop/pull/5734) -- Troubleshooting installation on macOS M1/M2/M3 [#5708](https://github.com/containers/podman-desktop/pull/5708) -- Volume deletion [#5707](https://github.com/containers/podman-desktop/pull/5707) -- Added _Accessing Podman from another WSL instance_ (config, verify) [#5706](https://github.com/containers/podman-desktop/pull/5706) -- Using typedoc to generate api documentation [#5705](https://github.com/containers/podman-desktop/pull/5705) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following people: - -- [Anders Björklund](https://github.com/afbjorklund) in [docs: linux no longer disabled](https://github.com/containers/podman-desktop/pull/6238), [fix: don't check default machine on Linux](https://github.com/containers/podman-desktop/pull/6161), [feat: show docker version](https://github.com/containers/podman-desktop/pull/6043), [docs: drop the word virtual from lima](https://github.com/containers/podman-desktop/pull/6003), [docs: document lima socket name prefs](https://github.com/containers/podman-desktop/pull/5984), [docs: split the lima custom config](https://github.com/containers/podman-desktop/pull/5983), [feat: show lima instance name in connection name](https://github.com/containers/podman-desktop/pull/5227), [feat: make it possible for lima to provide both](https://github.com/containers/podman-desktop/pull/4539) -- [Kenichi Kamiya](https://github.com/kachick) in [docs: fix missing podman command in WSL example](https://github.com/containers/podman-desktop/pull/6126) -- [Michael Prankl](https://github.com/eidottermihi) in [docs: fix instructions to edit registries.conf](https://github.com/containers/podman-desktop/pull/6122) -- [Cedric Clyburn](https://github.com/cedricclyburn) in [docs: blog post about devies award](https://github.com/containers/podman-desktop/pull/6078) -- [anon](https://github.com/Moortu) in [chore: Add windows instructions for emulating docker cli](https://github.com/containers/podman-desktop/pull/6066) -- [Thiago Mendes](https://github.com/trmendes) in [docs: Shorter sidebar category names](https://github.com/containers/podman-desktop/pull/5959), [fix: remove incorrect usage of component](https://github.com/containers/podman-desktop/pull/5822), [ci: use macos arm64 runners](https://github.com/containers/podman-desktop/pull/5795), [style: do not hide terminal when there are errors](https://github.com/containers/podman-desktop/pull/5710), [refactor: dialog box moving from 'no' to 'cancel'](https://github.com/containers/podman-desktop/pull/5702), [chore: add copy to clipboard button to resources page](https://github.com/containers/podman-desktop/pull/5687) -- [CARBONNEAUX Mathieu](https://github.com/mcarbonneaux) in [Remove http:// prefix when set proxy variable before exec](https://github.com/containers/podman-desktop/pull/5694) -- [Christophe Fergeau](https://github.com/cfergeau) in [doc: Fix 'podman-mac-help' typo](https://github.com/containers/podman-desktop/pull/5692) - ---- - -## Final notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.8.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-04-05-release-1.9.md b/website/blog/2024-04-05-release-1.9.md deleted file mode 100644 index 4545e75f59..0000000000 --- a/website/blog/2024-04-05-release-1.9.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: Podman Desktop 1.9 Release -description: Podman Desktop 1.9 has been released! -slug: podman-desktop-release-1.9 -authors: benoitf -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.9/juggling.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.9 Release! 🎉 - -![Podman-desktop-1-9-hero](img/podman-desktop-release-1.9/juggling.png) - -This release introduces: 🦭 a splash of innovation, a wave of excitement, and an ocean of possibilities! - -- **Podman 5!** [Podman 5.0.1](https://github.com/containers/podman/releases/tag/v5.0.1) for new users (and as an experimental upgrade for 4.x users). -- **Podman 4.9.4**: [Podman 4.9.4](https://github.com/containers/podman/releases/tag/v4.9.4) is now included in both Windows and macOS installers. -- **Backup/Restore Images**: Save images or containers to tar archives and restore them. -- **Kubernetes Pods Terminal**: Connect to a terminal within Kubernetes pods. -- **Extension API Improvements**: Additional updates to the extension API used by 🦭 Podman Desktop's extensions. - -Podman Desktop 1.9 is now available. [Click here to download it](/downloads)! - - - ---- - -## Release Details - -### Podman v5 (5.0.1) - -Podman version 5 is out! [see blog post introducing Podman v5](https://blog.podman.io/2024/03/podman-5-0-has-been-released/) - -On macOS there are performance enhancements as Apple Hypervisor is used instead of QEMU for the Podman machine. Also expect better performance on the file sharing. - -That said, Podman machine version 5 is introducing changes that are not compliant with podman machine version 4. - -This is why we're rolling out Podman version 5 by default only to new users to improve the migration from Podman Desktop side. - -For Podman version 4 users, Podman version 5 is accessible using an [experimental flag](#enabling-experimental-flag). - -Moving to Podman v5 implies to optionally save images and then delete previous machines and create a new one. - -#### Important steps before updating - -Prioritize data backup by using the save feature in the `Image Lists` section. This feature allows you to back up your images and restore them once you have a new Podman machine. - -When prompted to update, confirm to remove all existing data from your machines. - -For more details on the save/load feature, refer to [the save/load images section of the release notes](#saveload-images-or-exportimport-containers). - -![v5 update](img/podman-desktop-release-1.9/podman-update-to-v5.png) - -#### Enabling experimental flag - -Are you using Podman 4.x but eager to migrate? Enable the Podman `v5` experimental flag in `Settings > Preferences > Extension: Podman`. - -This will make the Upgrade option available on the Dashboard. - -![v5 experimental flag](img/podman-desktop-release-1.9/experimental-podman-5.png) - -#### Onboarding notification for Podman version 5 - -If you have previously installed Podman version 5 and Podman Desktop detects some invalid Podman machines, you'll see a notification on the dashboard to clean up old machines. - -![Onboarding Notification](img/podman-desktop-release-1.9/podman-require-onboarding-1.png) - -![Remove previous Podman machines](img/podman-desktop-release-1.9/podman-require-onboarding-2.png) - -### Podman 4.9.4 - -If you want to stay on Podman v4.x, there is a new update for some bugs including a CVE/security issue. - -### Save/Load Images or Export/Import Containers - -Podman 5 is not able to read 4.x machines, so before updating you'll need to backup images that you need to keep. You don't need to backup images that are available on remote registries or transient. - -#### Save/Load Images - -🦭 Use Podman Desktop to save images and load them again. - -![Select images to save](img/podman-desktop-release-1.9/podman-save-1.png) - -![Save images](img/podman-desktop-release-1.9/podman-save-2.png) - -Load images using the Load button from the image list. - -![Select images to load](img/podman-desktop-release-1.9/podman-load-1.png) - -![Load images](img/podman-desktop-release-1.9/podman-load-2.png) - -#### Save/Load Containers - -🦭 Export filesystem of containers and import them. - -![Select containers to export](img/podman-desktop-release-1.9/podman-export-1.png) - -![Export containers](img/podman-desktop-release-1.9/podman-export-2.png) - -Import containers using the Load button from the image list. - -![Select files to import](img/podman-desktop-release-1.9/podman-import-1.png) - -![Import images](img/podman-desktop-release-1.9/podman-import-2.png) - -**_NOTE:_** Exporting the filesystem of containers only exports the content of the filesystem. Importing will result in a container without any commands, so this might not be what you expect. -Please prioritize the usage of image saving/loading over container export/import. - -### Terminal in Kubernetes Pods - -In the previous release we introduced several new features in Kubernetes, but one notable addition was missing. With the 1.9 release, we're excited to announce that you can now connect to the terminal of a pod. - -- Terminal Connectivity: Users can now establish a direct connection to the terminal of a pod, enhancing the management and troubleshooting capabilities within Kubernetes environments. -- Container Toggle: When a pod contains multiple containers, you can easily toggle between them to access the terminal of the desired container. - -How to access to the Terminal: - -Navigate to the pod details in Podman Desktop and select the "Terminal" Tab.. - -If the pod contains multiple containers, utilize the toggle feature to select the container whose terminal you wish to connect to. - -Once connected, you can interact with the terminal to perform various tasks such as debugging, log monitoring, or executing commands within the container environment. - -![Connect Terminal to a Pod](img/podman-desktop-release-1.9/terminal-pod.png) - -### Extension API Improvements - -We continued spent a lot of time adding new extension API to give upcoming extensions -more capabilites and even better integration into 🦭 Podman Desktop: - -- feat: add navigateToAuthentication method to navigation API [6603](https://github.com/containers/podman-desktop/pull/6603) -- feat: add secrets handling to extensionContext in extension api [6423](https://github.com/containers/podman-desktop/pull/6423) -- feat: add sign in button for auth providers w/ the only auth session request [6446](https://github.com/containers/podman-desktop/pull/6446) -- feat: support for webview reveal [6546](https://github.com/containers/podman-desktop/pull/6546) - -Also we published a test framework to test extensions in separate repositories - -- feat: publish ui components and test component as part of the release [6580](https://github.com/containers/podman-desktop/pull/6580) - -More info on [https://github.com/containers/podman-desktop/blob/main/tests/playwright/README.md](https://github.com/containers/podman-desktop/blob/main/tests/playwright/README.md) - ---- - -## Other Notable Enhancements - -We've added over 20 features this release, here are some other highlights: - -- feat: terminate wsl machines before removing conf files when fixing update [#6596](https://github.com/containers/podman-desktop/pull/6596) -- feat: update experimental v5 of podman to v5.0.1 [#6589](https://github.com/containers/podman-desktop/pull/6589) -- feat: detect podman v4 machines not compliant with the new format of v5 [#6570](https://github.com/containers/podman-desktop/pull/6570) -- feat: detect podman v4 qemu machines after update and delete them [#6565](https://github.com/containers/podman-desktop/pull/6565) -- feat: update to podman v4.9.4 [#6564](https://github.com/containers/podman-desktop/pull/6564) -- feat: default to podman v5 for new users [#6548](https://github.com/containers/podman-desktop/pull/6548) -- feat: import containers images [#6492](https://github.com/containers/podman-desktop/pull/6492) -- feat: open terminal in the running container [#5975](https://github.com/containers/podman-desktop/pull/5975) -- feat: add experimental flag to install podman v5 [#6476](https://github.com/containers/podman-desktop/pull/6476) -- feat: add export container [#6468](https://github.com/containers/podman-desktop/pull/6468) -- feat: allow to revive a Uri object when passing it frontend - backend [#6462](https://github.com/containers/podman-desktop/pull/6462) -- feat: add generic action to task [#6453](https://github.com/containers/podman-desktop/pull/6453) -- feat: add export container logic [#6452](https://github.com/containers/podman-desktop/pull/6452) -- feat: add bottomLeft and bottomRight options for Tooltip component [#6445](https://github.com/containers/podman-desktop/pull/6445) -- feat: add a safe storage registry [#6422](https://github.com/containers/podman-desktop/pull/6422) -- feat: allow to load images [#6540](https://github.com/containers/podman-desktop/pull/6540) -- feat: ask to wipe all data when migrating from podman v4 to v5 [#6539](https://github.com/containers/podman-desktop/pull/6539) -- feat: add loadImages logic [#6538](https://github.com/containers/podman-desktop/pull/6538) -- feat: prompt user to stop any running podman machine before updating [#6533](https://github.com/containers/podman-desktop/pull/6533) -- feat: allow to save images [#6530](https://github.com/containers/podman-desktop/pull/6530) -- feat: add saveImages logic [#6520](https://github.com/containers/podman-desktop/pull/6520) - ---- - -## Notable Bug Fixes - -We squashed a lot of bugs this release, including the following: - -- fix: handle external installation of Podman when checking for updates [#6601](https://github.com/containers/podman-desktop/pull/6601) -- fix: invalid if clause for !isLinux [#6597](https://github.com/containers/podman-desktop/pull/6597) -- fix: allow to select files when wanting to import container tar images [#6591](https://github.com/containers/podman-desktop/pull/6591) -- fix: provides the tag names rather than id when saving images [#6588](https://github.com/containers/podman-desktop/pull/6588) -- fix: allow to select files when importing tar files [#6584](https://github.com/containers/podman-desktop/pull/6584) -- fix: inconsistent tab filtering behavior [#6572](https://github.com/containers/podman-desktop/pull/6572) -- fix: check if updates are available after a new podman machine is added/removed [#6558](https://github.com/containers/podman-desktop/pull/6558) -- fix: making CancellationTokenSource a class instead of an interface [#6557](https://github.com/containers/podman-desktop/pull/6557) -- fix: ensure machine list is up-to-date when running onboarding [#6512](https://github.com/containers/podman-desktop/pull/6512) -- fix: onboarding should be available after reloading an extension [#6510](https://github.com/containers/podman-desktop/pull/6510) -- fix: support more fa icons [#6499](https://github.com/containers/podman-desktop/pull/6499) -- fix: update e2e tests to fix CI failures [#6491](https://github.com/containers/podman-desktop/pull/6491) -- fix: update yarn.lock [#6474](https://github.com/containers/podman-desktop/pull/6474) -- fix: use expected component in the tests [#6424](https://github.com/containers/podman-desktop/pull/6424) -- fix: correct layout for openshift routes [#6398](https://github.com/containers/podman-desktop/pull/6398) -- fix: add playsinline to avoid full screen [#6395](https://github.com/containers/podman-desktop/pull/6395) -- fix: try to listen on all ipv4 interfaces AND 0.0.0.0 before to declare a port free [#6354](https://github.com/containers/podman-desktop/pull/6354) - ---- - -## Documentation - -Along with this new version of 🦭 Podman Desktop the documentation has had the following improvements: - -- docs: single page for _In a restricted environment_ [#5756](https://github.com/containers/podman-desktop/pull/5756) -- docs: updated _Installing Podman Desktop and Podman on Windows_ (compact version) [#5751](https://github.com/containers/podman-desktop/pull/5751) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following people: - -- [Hlib Haranin](https://github.com/GLEF1X) in [fix: inconsistent tab filtering behavior](https://github.com/containers/podman-desktop/pull/6572), [chore(palette): make command titles capitalization consistent](https://github.com/containers/podman-desktop/pull/6574), [chore(no-engine-screen): add guidance for engine configuration](https://github.com/containers/podman-desktop/pull/6575), [chore(image-list): improve button tooltips](https://github.com/containers/podman-desktop/pull/6586) - ---- - -## Final notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.9.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-04-30-release-1.10.md b/website/blog/2024-04-30-release-1.10.md deleted file mode 100644 index 940b938f16..0000000000 --- a/website/blog/2024-04-30-release-1.10.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: Podman Desktop 1.10 Release -description: Podman Desktop 1.10 has been released! -slug: podman-desktop-release-1.10 -authors: deboer -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.10/1000000-downloads.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.10 Release! 🎉 - -![Podman-desktop-1-10-hero](img/podman-desktop-release-1.10/1000000-downloads.png) - -This release introduces: - -- **1 Million Downloads!**: Wow, we made it! -- **Extension Catalog**: Redesigned extensions page and catalog to get the most out of Podman Desktop. -- **Podman 5**: [Podman 5.0.2](https://github.com/containers/podman/releases/tag/v5.0.2) now recommended for all users. -- **Multi-platform Builds**: Build for multiple platforms at once. -- **Extension API Improvements**: Additional updates to the extension API used by 🦭 Podman Desktop's extensions. - -Podman Desktop 1.10 is now available. [Click here to download it](/downloads)! - - - ---- - -## Release Details - -### 1 Million Downloads! 🎉 - -We've hit over 1 million downloads of Podman Desktop since we started! A big thank you 🙏 to everyone -who has helped get us to this number - especially those who have opened issues or PRs and contributed -to this success! - -Looking forward to the next million! 🚀 - -### Extension Catalog - -To make it easier to access extensions we added an item to the main navigation and completely revamped -the Extensions page. You can now see and control all your Podman Desktop and Docker Desktop extensions -in a single place. If you want to view more details, click on an extension to see the expanded readme. - -![extensions](img/podman-desktop-release-1.10/extensions.png) - -With the growing number of extensions available we've also introduced a catalog to make it easy to find -and install them. Check the catalog regularly as we hear there are some exciting new extensions sailing in! - -![extension catalog](img/podman-desktop-release-1.10/extension-catalog.png) - -### Podman v5 (5.0.2) - -Now that we've had some experience and time to test our migration tools, we're glad to make Podman v5 a -recommended upgrade for all users. Podman Desktop will help ensure you don't lose any data during the -migration. In particular macOS users will see a performance boost as we've switched to the native -Apple Hypervisor. - -Find out more details in the blog announcement for 🦭 Podman version 5 [here](https://blog.podman.io/2024/03/podman-5-0-has-been-released/). - -### Multi-platform Builds - -Sticking to one platform keeping you down? Building on the work we did in a recent release to allow building -images for other platforms, you can now select multiple platforms at once. When you do this we will -automatically switch to building an image for each selected platform and then creating a multi-platform -manifest to package them all together. - -![multi-platform builds](img/podman-desktop-release-1.10/multi-platform-builds.png) - -We still have work to do to allow seamlessly working with images or manifests, so stay tuned for more. - -### Extension API Improvements - -We've added support for manifests (multi-arch images) to the extension API to give upcoming extensions -more capabilites and even better integration into 🦭 Podman Desktop: - -- feat: add inspectManifest API endpoint [#6812](https://github.com/containers/podman-desktop/pull/6812) -- feat: add createManifest API [#6630](https://github.com/containers/podman-desktop/pull/6630) - ---- - -## Other Notable Enhancements - -We've added lots of features this release, here are some other highlights: - -- feat: add quick install of extensions providing authentication [#6936](https://github.com/containers/podman-desktop/pull/6936) -- feat: add alias for registry entries [#6839](https://github.com/containers/podman-desktop/pull/6839) -- feat: make recommendation have publishDate property [#6912](https://github.com/containers/podman-desktop/pull/6912) -- feat: suggest extensions if building or pulling from some registries report errors [#6891](https://github.com/containers/podman-desktop/pull/6891) -- feat: switch to using libpodapi for listImages [#6736](https://github.com/containers/podman-desktop/pull/6736) -- feat: add navigateTo resources and edit container connection [#6733](https://github.com/containers/podman-desktop/pull/6733) -- feat: adding dashboard extension banner [#6708](https://github.com/containers/podman-desktop/pull/6708) -- feat: adding internal logic to manage recommended extensions based on featured extensions [#6681](https://github.com/containers/podman-desktop/pull/6681) - ---- - -## Notable Bug Fixes - -We squashed a lot of bugs this release, including the following: - -- fix: default to rootful mode if unspecified [#6968](https://github.com/containers/podman-desktop/pull/6968) -- fix: boolean values should be true and not 'on' using forms [#6967](https://github.com/containers/podman-desktop/pull/6967) -- fix: handle deletion of all Docker Desktop extensions [#6964](https://github.com/containers/podman-desktop/pull/6964) -- fix: skip existing installed extensions when installing a pack [#6914](https://github.com/containers/podman-desktop/pull/6914) -- fix: hide cpu, memory, size sliders on WSL [#6878](https://github.com/containers/podman-desktop/pull/6878) -- fix: container details image link [#6805](https://github.com/containers/podman-desktop/pull/6805) -- fix: provider card layout [#6797](https://github.com/containers/podman-desktop/pull/6797) -- fix: kubectl-cli update error [#6759](https://github.com/containers/podman-desktop/pull/6759) -- fix: make flatpak metadata compliant with flathub [#6635](https://github.com/containers/podman-desktop/pull/6635) -- fix: airgap mode and default for podman v5 [#6633](https://github.com/containers/podman-desktop/pull/6633) -- fix: hide restart action for kubernetes pods [#6620](https://github.com/containers/podman-desktop/pull/6620) -- fix: increase default timeout for extension activation [#7053](https://github.com/containers/podman-desktop/pull/7053) -- fix: use podman machine inspect to know if machine is rootful [#7024](https://github.com/containers/podman-desktop/pull/7024) -- fix: edit button should be visible during started and stopped [#7045](https://github.com/containers/podman-desktop/pull/7045) -- fix: fix dashboard UI [#7006](https://github.com/containers/podman-desktop/pull/7006) -- fix: handle ids with spaces for extensions [#6965](https://github.com/containers/podman-desktop/pull/6965) -- fix: remove request for login after getting session programmatically [#6665](https://github.com/containers/podman-desktop/pull/6665) -- fix: LoadingIconButton consider failed state [#6997](https://github.com/containers/podman-desktop/pull/6997) - ---- - -## Documentation - -Along with this new version of 🦭 Podman Desktop the documentation has had the following improvement: - -- docs: add compose troubleshooting doc related to authentication [#6928](https://github.com/containers/podman-desktop/pull/6928) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following people: - -- [Norman Dankert](https://github.com/totkeks) in [feat: open dashboard with left click on tray icon on Windows](https://github.com/containers/podman-desktop/pull/6750) -- [Tim Heuer](https://github.com/timheuer) in [docs: changes to writing an extension](https://github.com/containers/podman-desktop/pull/6715) -- [Hlib Haranin](https://github.com/GLEF1X) in [feat(webview-telemetry): make telemetry for webviews more detailed](https://github.com/containers/podman-desktop/pull/6692) and [feat(container-detail): make image name a link that redirects to image detail page](https://github.com/containers/podman-desktop/pull/6628) -- [tmancill](https://github.com/tmancill) in [docs: Update pushing-an-image-to-kind.md - add info re: crictl](https://github.com/containers/podman-desktop/pull/6666) - ---- - -## Final Notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.10.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-06-24-release-1.11.md b/website/blog/2024-06-24-release-1.11.md deleted file mode 100644 index e99d08fb42..0000000000 --- a/website/blog/2024-06-24-release-1.11.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Podman Desktop 1.11 Release -description: Podman Desktop 1.11 has been released! -slug: podman-desktop-release-1.11 -authors: cdrage -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.11/banner.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.11 Release! 🎉 - -![Podman-desktop-1-11-hero](img/podman-desktop-release-1.11/banner.png) - -This release introduces: - -- **Experimental light mode!**: Our most-requested feature is here! Try out our new experimental light mode in the settings. -- **Rosetta support for Apple Silicon**: Build AMD64 binaries near the same speed as ARM64 binaries. -- **Kubernetes improvements**: Check out our new Node and Volume pages for Kubernetes. -- **Improved UI**: In addition to our light mode, you'll notice updates to our container listing page. -- **Increased manifest support**: Images built as a manifest are now grouped together. - -Podman Desktop 1.11 is now available. [Click here to download it](/downloads)! - - - ---- - -## Release Details - -### Experimental Light Mode 💡 - -We're excited to announce the arrival of our most-requested feature: light mode! You can now enjoy a brighter interface by toggling the light mode option in the **Settings > Preferences** section. Thank you for your patience as we continue to implement light mode for better accessibility! Please note, this feature is currently experimental as we continue to optimize the rest of Podman Desktop for this new mode. - -![Podman-desktop-1-11-hero](img/podman-desktop-release-1.11/light_mode.png) - -### macOS Rosetta Support for Apple Silicon - -Podman Desktop 1.11 proudly introduces support for macOS Rosetta, facilitating seamless integration with Apple Silicon. This update, part of Podman 5.1, allows users to enable or disable Rosetta support directly through the Podman Settings. With this enhancement, running AMD64 image builds or containers achieves speeds that are nearly identical to those experienced on ARM64 architectures. For more details on the implementation, visit the [GitHub podman pull request](https://github.com/containers/podman/pull/21670). - -### Kubernetes Enhancements - -In our latest release, we've enhanced Kubernetes functionality by adding node and volume listings. This update aims to improve your Kubernetes management experience within Podman Desktop. - -![kubernetes enhancements](img/podman-desktop-release-1.11/nodes.png) - -![kubernetes enhancements](img/podman-desktop-release-1.11/pvc.png) - -### User Interface Improvements - -Take a moment to appreciate the upgraded user interface of Podman Desktop. You'll find that the container listing section, among others, has received significant enhancements, elevating your overall user experience. - -### Enhanced Manifest Support - -We've made strides in how we handle manifest images. Multi-arch images are now grouped under the manifest, simplifying the process and enhancing efficiency. - -![manifests](img/podman-desktop-release-1.11/manifest.png) - ---- - -## Other Notable Enhancements - -We've added lots of features this release, here are some other highlights: - -- feat(PreferenceResource): adding light theme support for some preferences page by @axel7083 in [#7107](https://github.com/containers/podman-desktop/pull/7107) -- feat: extensions should stay disabled after restart by @deboer-tim in [#7085](https://github.com/containers/podman-desktop/pull/7085) -- feat: allow to select a folder using the fileComponent by @lstocchi in [#7135](https://github.com/containers/podman-desktop/pull/7135) -- feat: update to podman v5.0.3 by @benoitf in [#7173](https://github.com/containers/podman-desktop/pull/7173) -- feat: migrate MessageBox to modal component by @axel7083 in [#7172](https://github.com/containers/podman-desktop/pull/7172) -- feat: add tip slot to customize tip content by @lstocchi in [#7150](https://github.com/containers/podman-desktop/pull/7150) -- feat(UI): migrate DropdownMenu component by @axel7083 in [#7233](https://github.com/containers/podman-desktop/pull/7233) -- feat: add build arguments to build page by @cdrage in [#7253](https://github.com/containers/podman-desktop/pull/7253) -- feat(SettingsNavItem): adding icon property by @axel7083 in [#7307](https://github.com/containers/podman-desktop/pull/7307) -- feat: create task when updating Podman Desktop by @benoitf in [#7286](https://github.com/containers/podman-desktop/pull/7286) -- feat: add node listing to Kubernetes section by @cdrage in [#7347](https://github.com/containers/podman-desktop/pull/7347) -- feat: container page table component by @deboer-tim in [#7424](https://github.com/containers/podman-desktop/pull/7424) -- feat: handle dir creation during fs watch by @feloy in [#7573](https://github.com/containers/podman-desktop/pull/7573) -- feat: uses TableColumnDuration for container list table by @axel7083 in [#7725](https://github.com/containers/podman-desktop/pull/7725) -- feat: request confirmation to allow sign in requested from extension by @dgolovin in [#7443](https://github.com/containers/podman-desktop/pull/7443) -- feat: add volumes (PVC) to kubernetes by @cdrage in [#7640](https://github.com/containers/podman-desktop/pull/7640) -- feat: add rosetta support by @cdrage in [#7540](https://github.com/containers/podman-desktop/pull/7540) -- feat: adds images under manifest by @cdrage in [#7552](https://github.com/containers/podman-desktop/pull/7552) -- feat: show manifests in image list by @cdrage in [#7227](https://github.com/containers/podman-desktop/pull/7227) -- feat: show node and namespace information for k8s pods by @cdrage in [#7684](https://github.com/containers/podman-desktop/pull/7684) -- feat: allow extensions to expose their own API by @benoitf in [#7384](https://github.com/containers/podman-desktop/pull/7384) -- feat(QuickPickInput): using modal component by @axel7083 in [#7180](https://github.com/containers/podman-desktop/pull/7180) -- feat: update podman 5.0.3 to 5.1.1 by @dgolovin in [#7433](https://github.com/containers/podman-desktop/pull/7433) - ---- - -## Notable Bug Fixes - -- fix(UI): resource page link to extension page by @axel7083 in [#6986](https://github.com/containers/podman-desktop/pull/6986) -- fix: update extension link and text on empty screen by @deboer-tim in [#7005](https://github.com/containers/podman-desktop/pull/7005) -- fix: the Image.Id should contain sha256: prefix when listing image Id by @benoitf in [#7009](https://github.com/containers/podman-desktop/pull/7009) -- fix: fix dashboard UI by @lstocchi in [#7006](https://github.com/containers/podman-desktop/pull/7006) -- fix: add suggestion when WSL seems to require a reboot by @lstocchi in [#7007](https://github.com/containers/podman-desktop/pull/7007) -- fix(LoadingIconButton): consider failed state by @axel7083 in [#6997](https://github.com/containers/podman-desktop/pull/6997) -- fix: use podman machine inspect to know if machine is rootful by @benoitf in [#7024](https://github.com/containers/podman-desktop/pull/7024) -- fix: make markdown TOC links clickable by @benoitf in [#7010](https://github.com/containers/podman-desktop/pull/7010) -- fix: increasing default timeout for extension activation by @axel7083 in [#7053](https://github.com/containers/podman-desktop/pull/7053) -- fix: show description link on preflight checks on dashboard by @lstocchi in [#7056](https://github.com/containers/podman-desktop/pull/7056) -- fix: edit button should be visible during started and stopped by @benoitf in [#7063](https://github.com/containers/podman-desktop/pull/7063) -- fix: add tests for PreflightChecks by @lstocchi in [#7069](https://github.com/containers/podman-desktop/pull/7069) -- fix(Button): remove unused `hidden` property by @axel7083 in [#7092](https://github.com/containers/podman-desktop/pull/7092) -- fix: flaky test port availability by @axel7083 in [#7110](https://github.com/containers/podman-desktop/pull/7110) -- fix: reset initialize and start mode after starting provider by @lstocchi in [#7115](https://github.com/containers/podman-desktop/pull/7115) -- fix: stop informer not more valid and handle similar contexts by @lstocchi in [#6934](https://github.com/containers/podman-desktop/pull/6934) -- fix: custom extension install modal buttons by @axel7083 in [#7067](https://github.com/containers/podman-desktop/pull/7067) -- fix(tests): adding label to avoid duplicates by @axel7083 in [#7160](https://github.com/containers/podman-desktop/pull/7160) -- fix: provider card UI on dashboard by @lstocchi in [#7082](https://github.com/containers/podman-desktop/pull/7082) -- fix(extension-loader): cleanup resource on extension error by @axel7083 in [#7228](https://github.com/containers/podman-desktop/pull/7228) -- fix: remove tinro from Tab and ui by @lstocchi in [#7288](https://github.com/containers/podman-desktop/pull/7288) -- fix: remove tinro dependency from SettingsNavItem component by @axel7083 in [#7280](https://github.com/containers/podman-desktop/pull/7280) -- fix: button should have spinner when inProgress is enable by @axel7083 in [#7259](https://github.com/containers/podman-desktop/pull/7259) -- fix: table child selection by @deboer-tim in [#7555](https://github.com/containers/podman-desktop/pull/7555) -- fix: close watcher when FileSystemWatcher is disposed by @feloy in [#7503](https://github.com/containers/podman-desktop/pull/7503) -- fix: table properly support undefined value result from rendermapping by @axel7083 in [#7723](https://github.com/containers/podman-desktop/pull/7723) -- fix(UI): formpage missing content shadow by @axel7083 in [#7733](https://github.com/containers/podman-desktop/pull/7733) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following new people: - -- [Jitse](https://github.com/jitseklomp) in [chore: ignore /kind in .gitignore](https://github.com/containers/podman-desktop/pull/7631) -- [Adrián Lorenzo](https://github.com/adrianriobo) in [chore: Manage EnableNodeCliInspectArguments Fuse Enablement based on ENV: ELECTRON_ENABLE_INSPECT](https://github.com/containers/podman-desktop/pull/7128) - ---- - -## Final Notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.11.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-08-01-using-rhel-wsl-podman-machine.md b/website/blog/2024-08-01-using-rhel-wsl-podman-machine.md deleted file mode 100644 index 1163177c65..0000000000 --- a/website/blog/2024-08-01-using-rhel-wsl-podman-machine.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -title: Using RHEL as a WSL podman machine -description: Build RHEL image and use it as the operating system for the WSL podman machine -authors: [jeffmaury] -tags: [podman-desktop, podman, rhel, wsl, machine] -hide_table_of_contents: false ---- - -Red Hat provides a tool called Image Builder that allows developers to build their own custom image of RHEL in a variety of formats. Recently, Image Builder added WSL as a target, enabling you to run RHEL on Windows as a WSL distribution. - -This [article](https://developers.redhat.com/articles/2023/11/15/create-customized-rhel-images-wsl-environment) details the steps and actions required to build and run your RHEL WSL image. - -The purpose of this article is to describe the options needed for the RHEL WSL distribution so that it can be used as a Podman machine. - -# Requirements - -To use the RHEL WSL image as a Podman machine, ensure that the following packages are installed: - -- podman -- podman-docker -- procps-ng -- openssh-server -- net-tools -- iproute -- dhcp-client -- sudo -- systemd-networkd - -Luckily, all but the last package are available from the pre-configured RHEL 9 repositories. The last package (systemd-networkd) is available from the EPEL 9 repository and will need to be configured when building the image. - -# Build the image - -Navigate to [image builder](https://console.redhat.com/insights/image-builder) - -![image builder](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine1.png) - -On the upper right menu, enable the **Preview** mode. - -![image builder preview](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine2.png) - -Click **Add blueprint** to open the **Create image** dialog wizard. - -![image wizard](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine3.png) - -On the **Image output** page, select the following: - -- From the **Release** list, select Red Hat Enterprise Linux (RHEL) 9. -- From the **Select target environments** option, select **WSL - Windows Subsystem for Linux (**`.tar.gz`**)**. -- Click **Next**. - -:::warning - -Although the RHEL 10 (Beta) option is available in the **Release**, please note that it is incompatible with -At the time of writing, the WSL kernel does not support nftables. - -::: - -On the **Register** page, select **Automatically register and enable advanced capabilities.** - -- From the dropdown menu, choose an activation key to use for the image. See [Creating an activation key](https://access.redhat.com/documentation/en-us/subscription_central/2023/html/getting_started_with_activation_keys_on_the_hybrid_cloud_console/assembly-creating-managing-activation-keys#proc-creating-act-keys-console_). -- Click **Next**. - -On the **OpenSCAP** page, as it is not supported for WSL images, click **Next**. - -On the **File system configuration** page, select **Recommended: Use automatic partitioning**. - -- Click **Next**. - -On the **Content** page, complete the following steps to add additional packages to your image: - -- On the Repository snapshot step: - - Select Use latest content. - - Click **Next**. - -- On the Custom repositories step: - -![custom repositories](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine4.png) - -Click on the [Create and manage repositories here](https://console.redhat.com/preview/settings/content) link. This will open a new tab - -![custom repositories](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine5.png) - -Click **Add repositories now** - -![add custom repository](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine6.png) - -Click **Add repositories** - -![add custom repository](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine7.png) - -On the **Add custom repositories** page, select the following: - -- In the **Name** list, enter EPEL 9. -- In the **URL** field, enter `https://dl.fedoraproject.org/pub/epel/9/Everything/x86_64/` -- In the **GPG key** field, enter `https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-9` -- Click **Save**. - -Close the tab and switch back to the previous one - -- In the filter input field, type EPEL -- Select the EPEL 9 repository - -![custom repository created](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine8.png) - -Click **Next** - -- On the Additional packages step: - - On the **Available packages** search field, enter podman and click the **→** button. - - Select the podman and podman-docker packages. - - On the **Available packages** search field, enter procps-ng and click the **→** button. - - Select the procps-ng package. - - On the **Available packages** search field, enter openssh-server and click the **→** button. - - Select the openssh-server package. - - On the **Available packages** search field, enter net-tools and click the **→** button. - - Select the net-tools package. - - On the **Available packages** search field, enter iproute and click the **→** button. - - Select the iproute package. - - On the **Available packages** search field, enter dhcp-client and click the **→** button. - - Select the dhcp-client package. - - On the **Available packages** search field, enter sudo and click the **→** button. - - Select the sudo package and click the **>** button to add the selected package shown in the package search results to the **Chosen packages** dual list box. - - On the **Available packages** search field, enter systemd and click the **→** button. - - Select the systemd-networkd package. - - Click **Next** - -On the **First boot script configuration** page: - -- Click **Next**. - -On the **Details** page: - -- In the **Blueprint name**, enter rhel-wsl. -- Click **Next**. - -On the **Review** page: - -- Click **Create blueprint and build image**. - -![images list](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine9.png) - -The image is being built. Once the build is finished, the download link will be available. Click on the **Download (.tar.gz)** link and save the downloaded file to one of your local folders. - -# Create the RHEL WSL podman machine - -Launch Podman Desktop and go to the **Settings -> Resources** page: - -![images list](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine10.png) - -On the Podman provider, click on **Create new ...** - -On the **Create Podman machine** page, click the **Browse** button for the **Image Path** field and select the file downloaded from Image Builder. - -![create podman machine](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine11.png) - -Click on the **Create** button: the machine will be created and started. After a short time, the operation status should be reported. - -![podman machine created](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine12.png) - -# Let's play with the RHEL WSL podman machine - -Go to the **Images** page and pull the **httpd** image - -![pull httpd image](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine13.png) - -Click on **Done** - -![images list](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine14.png) - -Start the image by clicking on the **Run image** icon - -![images list](img/using-rhel-wsl-podman-machine\rhel-wsl-podman-machine15.png) - -Once the container is started, the Apache server can be accessed on [localhost:9000](http://localhost:9000) diff --git a/website/blog/2024-08-08-release-1.12.md b/website/blog/2024-08-08-release-1.12.md deleted file mode 100644 index 0841456aa2..0000000000 --- a/website/blog/2024-08-08-release-1.12.md +++ /dev/null @@ -1,369 +0,0 @@ ---- -title: Podman Desktop 1.12 Release -description: Podman Desktop 1.12 has been released! -slug: podman-desktop-release-1.12 -authors: cdrage -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.12/banner.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.12 Release! 🎉 - -![podman-desktop-hero-1.12](img/podman-desktop-release-1.12/banner.png) - -Podman Desktop 1.12 is now available. [Click here to download it](/downloads)! - -This release includes: - -- **Podman remote**: We now support remote Podman setups! Manage your remote Podman machines all within the UI. -- **macOS GPU support**: Container GPU access on macOS is now available. [`libkrun`](https://github.com/containers/libkrun) is now a selectable provider type to allow GPU passthrough enablement. -- **Windows GPU support**: Want to try out Windows GPU support too? Podman already supports it, but we are now showcasing it in our [AI Lab extension](https://podman-desktop.io/extensions/ai-lab) -- **Podman 5.2.0**: This new version of Podman provides GPU access for macOS, as well as a host of [new features](https://github.com/containers/podman/releases/tag/v5.2.0). -- **Light mode out of experimental**: Our light mode has been well-received, and we have now marked it as non-experimental! Enjoy the new theme. -- **Kubernetes features**: ConfigMaps, Secrets and multi-file Kubernetes YAML applying have now been added to our Kubernetes dashboard. -- **Improved font consistency**: You'll notice a big difference in consistency this release, as we updated all font sizes throughout Podman Desktop. - - - -## Release Details - -### Podman remote - -We now support remote Podman sessions! Use Podman Desktop to interact with remote instances over SSH. This allows both container and image management. - -You can enable this in the **Preferences** section of Podman Desktop: - -![remote](img/podman-desktop-release-1.12/remote.png) - -To set up access to a remote machine, follow the official [Podman remote-client tutorial](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md). - -### macOS GPU support - -GPU support is now available for macOS users! - -This seamless setup can be enabled during Podman Machine creation by selecting the provider type: - -![libkrun](img/podman-desktop-release-1.12/libkrun.png) - -After enablement, you can test that the GPU has been supported by running a custom container with `--device /dev/dri` passed through: - -```sh -$ podman run --rm -it --device /dev/dri --name gpu-info quay.io/slopezpa/fedora-vgpu vulkaninfo | grep "GPU" -``` - -Which will output information regarding your GPU: - -```sh - - GPU id = 0 (Virtio-GPU Venus (Apple M1 Pro)) - GPU id = 1 (llvmpipe (LLVM 17.0.6, 128 bits)) -GPU0: - deviceType = PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU - deviceName = Virtio-GPU Venus (Apple M1 Pro) -GPU1: -``` - -Stay tuned as we continue to improve and write documentation on GPU support. - -The most practical way to try these new features out is by installing [AI Lab (>1.2.3)](https://podman-desktop.io/extensions/ai-lab) and seeing the improved speed. - -This can be done by following the below steps: - -1. **Update to the latest Podman AI Lab extension (v1.2.3)** from the Extensions Catalog on Podman Desktop. -2. Create a new Podman Machine with `libkrun` enabled (see above instructions). -3. Enable GPU support within Podman AI Lab extension: - -![ai_lab](img/podman-desktop-release-1.12/ai_lab.png) - -4. Start an inference server / playground environment and enjoy the benefits of accelerated GPU support! - -### Windows GPU support - -Windows GPU support has been integrated into Podman for a while, however our associated extensions have now been utilizing this great feature! Such as in [AI Lab](https://podman-desktop.io/extensions/ai-lab). - -To showcase this awesome feature, this can be done by following the below steps: - -1. **Update to the latest Podman AI Lab extension** from the Extensions Catalog on Podman Desktop. -2. Create a Podman Machine with WSL enabled. -3. Enable GPU support within the Podman AI Lab Extension: - -![ai_lab](img/podman-desktop-release-1.12/ai_lab.png) - -4. Start an inference server / playground environment and enjoy the benefits of accelerated GPU support! - -### Light mode out of experimental - -We've made excellent progress in light mode and we have now marked it out of non-experimental! If you have not already tried it yet, this can be enabled within the interface here: - -![light mode](img/podman-desktop-release-1.12/light_mode.png) - -### Kubernetes features - -With this release, you can now view ConfigMaps as well as Secrets: - -![configmaps secrets](img/podman-desktop-release-1.12/configmaps_secrets.png) - -You also have the ability to now select multiple files when applying your Kubernetes YAML. - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped make 🦭 Podman Desktop even better. In this release we received pull requests from the following new people: - -- [@myfear](https://github.com/myfear) in [fix(docs): remove all linter errors](https://github.com/containers/podman-desktop/pull/7862) -- [@danivilla9](https://github.com/danivilla9) in [test: added start/stop/delete/prune multiple containers from the container list test cases](https://github.com/containers/podman-desktop/pull/7962) -- [@BinaryWizard904](https://github.com/BinaryWizard904) in [Add arm64 installer to publish-to-winget.yaml](https://github.com/containers/podman-desktop/pull/8173) -- [@s-en-o](https://github.com/s-en-o) in [test: perform stop if not stopped](https://github.com/containers/podman-desktop/pull/8350) - ---- - -## Final Notes - -### Fixed Issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.12.0). - -### Where to Download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. - ---- - -## Detailed Release Changelog - -### authentication 🔑 - -- fix: show sign out request when signing out from active session by @dgolovin in [#8005](https://github.com/containers/podman-desktop/pull/8005) - -### ci 🔁 - -- chore(ci): exclude playwright raw traces from archived artifacts by @odockal in [#7863](https://github.com/containers/podman-desktop/pull/7863) -- chore(ci): include junit file path to be in test artifacts by @odockal in [#7859](https://github.com/containers/podman-desktop/pull/7859) -- chore(test): better handle for podman machine wait by @cbr7 in [#7957](https://github.com/containers/podman-desktop/pull/7957) -- chore(test): fix locator by @cbr7 in [#8022](https://github.com/containers/podman-desktop/pull/8022) -- chore(test): increase robustness for e2e test by @cbr7 in [#8327](https://github.com/containers/podman-desktop/pull/8327) -- chore(test): update locator for dialog by @cbr7 in [#8027](https://github.com/containers/podman-desktop/pull/8027) -- chore(tests): delete traces when not needed by @cbr7 in [#7874](https://github.com/containers/podman-desktop/pull/7874) -- chore(tests): fix welcome page tests by @cbr7 in [#7829](https://github.com/containers/podman-desktop/pull/7829) - -### dashboard 📊 - -- Cancel image layers loading when Files tab is closed by @feloy in [#8301](https://github.com/containers/podman-desktop/pull/8301) -- chore: improved status colors by @deboer-tim in [#8236](https://github.com/containers/podman-desktop/pull/8236) -- chore: update pod columns, provider card by @deboer-tim in [#8089](https://github.com/containers/podman-desktop/pull/8089) -- feat: Add manifest push and delete butons by @cdrage in [#8142](https://github.com/containers/podman-desktop/pull/8142) -- feat: add option to require user confirmation for certain actions by @SoniaSandler in [#7878](https://github.com/containers/podman-desktop/pull/7878) -- feat: search image tags by @feloy in [#8170](https://github.com/containers/podman-desktop/pull/8170) -- fix: add background to lima icon by @afbjorklund in [#8328](https://github.com/containers/podman-desktop/pull/8328) -- fix: click on the dock icon should display dashboard by @benoitf in [#7649](https://github.com/containers/podman-desktop/pull/7649) -- fix: remove afterUpdate in PreferencesRenderingItem by @lstocchi in [#7990](https://github.com/containers/podman-desktop/pull/7990) -- Search images in registry (backend) by @feloy in [#7930](https://github.com/containers/podman-desktop/pull/7930) - -### documentation 📖 - -- docs: add notes about accessing opening devtools of extension by @cdrage in [#8329](https://github.com/containers/podman-desktop/pull/8329) -- fix(docs): homepage screenshot update by @myfear in [#7865](https://github.com/containers/podman-desktop/pull/7865) -- fix(docs): remove all linter errors by @myfear in [#7862](https://github.com/containers/podman-desktop/pull/7862) -- fix(docs): slightly enhancing ai lab documentation by @myfear in [#7902](https://github.com/containers/podman-desktop/pull/7902) -- fix(docs): update introduction by @myfear in [#7861](https://github.com/containers/podman-desktop/pull/7861) - -### extension/kind 🍾 - -- chore: add setup instructions to kind extension readme by @evanshortiss in [#7981](https://github.com/containers/podman-desktop/pull/7981) -- fix: prevent submit event propagation when clicking on number input buttons by @lstocchi in [#7978](https://github.com/containers/podman-desktop/pull/7978) -- feat: making kind extension register cli tool by @axel7083 in [#8038](https://github.com/containers/podman-desktop/pull/8038) - -### extension/kube-context ☸ - -- Use the real path when watching files by @feloy in [#7951](https://github.com/containers/podman-desktop/pull/7951) - -### extension/kubectl-cli - -- fix: kubectl should not upgrade to older version by @gastoner in [#8347](https://github.com/containers/podman-desktop/pull/8347) - -### extension/podman 🦭 - -- feat: add support for podman remote ssh hosts by @benoitf in [#8265](https://github.com/containers/podman-desktop/pull/8265) -- feat: add manifest api for pushing by @cdrage in [#8108](https://github.com/containers/podman-desktop/pull/8108) -- feat: add removeManifest API by @cdrage in [#8127](https://github.com/containers/podman-desktop/pull/8127) -- feat: allow to use libkrun and applehv machines starting from 5.2.0 by @lstocchi in [#8247](https://github.com/containers/podman-desktop/pull/8247) -- chore: use isManifestList for manifest guessing by @cdrage in [#8302](https://github.com/containers/podman-desktop/pull/8302) -- fix: podman machine switch may lose API connection by @axel7083 in [#7975](https://github.com/containers/podman-desktop/pull/7975) -- fix: update minimum version for libkrun support by @lstocchi in [#8298](https://github.com/containers/podman-desktop/pull/8298) - -### extensions 🧩 - -- add API for Image Files providers by @feloy in [#7802](https://github.com/containers/podman-desktop/pull/7802) -- display image files by @feloy in [#7844](https://github.com/containers/podman-desktop/pull/7844) -- feat: add support for zstd images for extensions by @benoitf in [#7929](https://github.com/containers/podman-desktop/pull/7929) -- feat: allow to handle update of extensions only for specific range of Podman Desktop by @benoitf in [#7867](https://github.com/containers/podman-desktop/pull/7867) -- feat: publish @podman-desktop/webview-api to npmjs by @benoitf in [#7961](https://github.com/containers/podman-desktop/pull/7961) -- fix: extensions cannot be installed on symlinked folders by @jeffmaury in [#7926](https://github.com/containers/podman-desktop/pull/7926) -- make saveImage cancellable by @feloy in [#8232](https://github.com/containers/podman-desktop/pull/8232) - -### install 🎁 - -- Add arm64 installer to publish-to-winget.yaml by @BinaryWizard904 in [#8173](https://github.com/containers/podman-desktop/pull/8173) -- feat: track rosetta support before starting machine by @benoitf in [#7881](https://github.com/containers/podman-desktop/pull/7881) -- fix: remove set up podman notification if podman is set by @lstocchi in [#8171](https://github.com/containers/podman-desktop/pull/8171) - -### kubernetes ☸️ - -- feat: apply multiple kube yamls by @deboer-tim in [#8204](https://github.com/containers/podman-desktop/pull/8204) -- feat: move column underneath name for k8s by @cdrage in [#8055](https://github.com/containers/podman-desktop/pull/8055) -- feat: add config maps and secrets to k8s (renderer code) by @cdrage in [#8042](https://github.com/containers/podman-desktop/pull/8042) -- feat: add configmap / secret functions for k8s integration by @cdrage in [#8019](https://github.com/containers/podman-desktop/pull/8019) -- fix: resize terminal so it is entirely visible by @lstocchi in [#7999](https://github.com/containers/podman-desktop/pull/7999) -- fix: restart informers for not current contexts after changes by @dgolovin in [#7906](https://github.com/containers/podman-desktop/pull/7906) -- chore: consistent layout when >10 kube contexts by @deboer-tim in [#7935](https://github.com/containers/podman-desktop/pull/7935) - -### podify - -- fix: podify a container with named volumes fails by @jeffmaury in [#8159](https://github.com/containers/podman-desktop/pull/8159) - -### podman-upstream 🦭 - -- fix: fix how to handle env with multiple = and spaces by @benoitf in [#8342](https://github.com/containers/podman-desktop/pull/8342) - -### release - -- docs: add 1.11 release blog post by @cdrage in [#7807](https://github.com/containers/podman-desktop/pull/7807) -- feat: update podman to 5.2.0 release by @dgolovin in [#8306](https://github.com/containers/podman-desktop/pull/8306) - -### settings ⚙️ - -- chore: move light mode out of experimental by @deboer-tim in [#8290](https://github.com/containers/podman-desktop/pull/8290) -- fix: support multiple scopes on create/edit configurations by @lstocchi in [#7659](https://github.com/containers/podman-desktop/pull/7659) - -### telemetry 📈 - -- chore: reduce unused telemetry events by @deboer-tim in [#8087](https://github.com/containers/podman-desktop/pull/8087) - -### tests 🚦 - -- chore: improve aria/role labelling in tables by @deboer-tim in [#8085](https://github.com/containers/podman-desktop/pull/8085) -- chore(test): add param for timeout setting by @cbr7 in [#8246](https://github.com/containers/podman-desktop/pull/8246) -- chore(test): adding test finished hook call by @cbr7 in [#8126](https://github.com/containers/podman-desktop/pull/8126) -- chore(test): avoid race condition by @cbr7 in [#8271](https://github.com/containers/podman-desktop/pull/8271) -- chore(test): catch exception from race condition by @cbr7 in [#8299](https://github.com/containers/podman-desktop/pull/8299) -- chore(test): create shared component for different POM classes by @cbr7 in [#8018](https://github.com/containers/podman-desktop/pull/8018) -- chore(test): destructuring method params by @cbr7 in [#7945](https://github.com/containers/podman-desktop/pull/7945) -- chore(test): fix locator and update message by @cbr7 in [#7896](https://github.com/containers/podman-desktop/pull/7896) -- chore(test): handle weird linux behaviour by @cbr7 in [#7901](https://github.com/containers/podman-desktop/pull/7901) -- chore(test): increase install timeout by @cbr7 in [#7907](https://github.com/containers/podman-desktop/pull/7907) -- chore(test): perform stop if not stopped by @cbr7 in [#8366](https://github.com/containers/podman-desktop/pull/8366) -- chore(test): try to kill app if close timesout by @cbr7 in [#8261](https://github.com/containers/podman-desktop/pull/8261) -- chore(tests): add openshift checker extension installation test using… by @odockal in [#8030](https://github.com/containers/podman-desktop/pull/8030) -- chore(tests): Added individual Compose onboarding pages by @xbabalov in [#8176](https://github.com/containers/podman-desktop/pull/8176) -- chore(tests): Fix failing Compose tests by @xbabalov in [#8266](https://github.com/containers/podman-desktop/pull/8266) -- Fix podman e2e test by @cbr7 in [#7911](https://github.com/containers/podman-desktop/pull/7911) -- fix: random flaky port test by @jeffmaury in [#8300](https://github.com/containers/podman-desktop/pull/8300) -- fix(tests): add missing argument into extensionPage's openDetailsPage… by @odockal in [#7892](https://github.com/containers/podman-desktop/pull/7892) -- Pods e2e tests by @cbr7 in [#7883](https://github.com/containers/podman-desktop/pull/7883) -- refactor: refactor resource card pages by @amisskii in [#8310](https://github.com/containers/podman-desktop/pull/8310) -- test: added start/stop/delete/prune multiple containers from the container list test cases by @danivilla9 in [#7962](https://github.com/containers/podman-desktop/pull/7962) -- test: delete from volume page and prune volume e2e tests by @danivilla9 in [#8128](https://github.com/containers/podman-desktop/pull/8128) - -### ui - -- chore: improvements to light mode by @deboer-tim in [#8251](https://github.com/containers/podman-desktop/pull/8251) -- chore: light status icon default text color by @deboer-tim in [#7828](https://github.com/containers/podman-desktop/pull/7828) -- chore: move /images/run to /image/run for creating containers by @cdrage in [#8375](https://github.com/containers/podman-desktop/pull/8375) -- chore: provide default text color by @deboer-tim in [#7963](https://github.com/containers/podman-desktop/pull/7963) -- chore: use color variables for providers by @deboer-tim in [#8235](https://github.com/containers/podman-desktop/pull/8235) -- chore: terminal color cleanup by @deboer-tim in [#8264](https://github.com/containers/podman-desktop/pull/8264) -- feat: add confirmation when bulk-delete if required based on preferences by @SoniaSandler in [#8020](https://github.com/containers/podman-desktop/pull/8020) -- chore: reduce navigation border by @deboer-tim in [#8254](https://github.com/containers/podman-desktop/pull/8254) -- chore: remove empty aria-label by @benoitf in [#7848](https://github.com/containers/podman-desktop/pull/7848) -- chore: account icon by @deboer-tim in [#8278](https://github.com/containers/podman-desktop/pull/8278) -- chore: add page rendering for images/imageId/engineId by @SoniaSandler in [#8249](https://github.com/containers/podman-desktop/pull/8249) -- chore: add sort option to more container list table columns by @SoniaSandler in [#8086](https://github.com/containers/podman-desktop/pull/8086) -- chore: adding navigation paths by @axel7083 in [#8292](https://github.com/containers/podman-desktop/pull/8292) -- chore: consistent horizontal padding in extensions by @deboer-tim in [#8075](https://github.com/containers/podman-desktop/pull/8075) -- chore: extract some code to a function by @benoitf in [#8334](https://github.com/containers/podman-desktop/pull/8334) -- chore: improve no contexts empty screen by @gastoner in [#8209](https://github.com/containers/podman-desktop/pull/8209) -- chore: improve toggle colors by @deboer-tim in [#7955](https://github.com/containers/podman-desktop/pull/7955) -- chore: light dark mode for banners by @cdrage in [#8343](https://github.com/containers/podman-desktop/pull/8343) -- chore: light loading screen by @deboer-tim in [#8175](https://github.com/containers/podman-desktop/pull/8175) -- chore: light mode for dashboard notifications by @deboer-tim in [#8054](https://github.com/containers/podman-desktop/pull/8054) -- chore: light mode for dashboard recommendations banner by @cdrage in [#8056](https://github.com/containers/podman-desktop/pull/8056) -- chore: light mode for docker desktop extensions (and more) by @deboer-tim in [#8289](https://github.com/containers/podman-desktop/pull/8289) -- chore: light mode for donut component by @deboer-tim in [#8136](https://github.com/containers/podman-desktop/pull/8136) -- chore: light mode for feedback form by @SoniaSandler in [#7905](https://github.com/containers/podman-desktop/pull/7905) -- chore: light mode for tasks icon, empty screen by @deboer-tim in [#8123](https://github.com/containers/podman-desktop/pull/8123) -- chore: light mode for troubleshooting by @deboer-tim in [#8195](https://github.com/containers/podman-desktop/pull/8195) -- chore: make text input field of kubernetes filepath in preferences pa… by @SoniaSandler in [#8139](https://github.com/containers/podman-desktop/pull/8139) -- chore: move each entry of the left navbar to individual items by @benoitf in [#8215](https://github.com/containers/podman-desktop/pull/8215) -- chore: reduce vertical bump on modal dialogs by @deboer-tim in [#8092](https://github.com/containers/podman-desktop/pull/8092) -- chore: reusable base page by @deboer-tim in [#8093](https://github.com/containers/podman-desktop/pull/8093) -- chore: statusbar notification dot overflow by @deboer-tim in [#8107](https://github.com/containers/podman-desktop/pull/8107) -- chore: support-multiline-messages-in-message-dialogs by @gastoner in [#8147](https://github.com/containers/podman-desktop/pull/8147) -- chore: update provider rendering create new background by @SoniaSandler in [#8137](https://github.com/containers/podman-desktop/pull/8137) -- chore: update ProviderResultPage to light mode by @SoniaSandler in [#8286](https://github.com/containers/podman-desktop/pull/8286) -- chore: update the UI of the compose details summary page by @SoniaSandler in [#8090](https://github.com/containers/podman-desktop/pull/8090) -- chore: update to svelte5 by @benoitf in [#7854](https://github.com/containers/podman-desktop/pull/7854) -- chore: update volumes summary page UI by @SoniaSandler in [#7826](https://github.com/containers/podman-desktop/pull/7826) -- chore: use dialog component for remaining dialogs by @deboer-tim in [#7997](https://github.com/containers/podman-desktop/pull/7997) -- chore(ContainerActions): uses global navigation by @axel7083 in [#7921](https://github.com/containers/podman-desktop/pull/7921) -- chore(navigation): adding type safety handleNavigation by @axel7083 in [#7933](https://github.com/containers/podman-desktop/pull/7933) -- chore(StatusIcon): adding accepting values for status by @axel7083 in [#7912](https://github.com/containers/podman-desktop/pull/7912) -- chore(test): add addional aria labels by @cbr7 in [#7941](https://github.com/containers/podman-desktop/pull/7941) -- chore(test): add additional aria labels by @cbr7 in [#7960](https://github.com/containers/podman-desktop/pull/7960) -- chore(test): enhance Detail Page Locators, ARIA Labels, and Add Navigation Tests by @amisskii in [#8196](https://github.com/containers/podman-desktop/pull/8196) -- Display info when layers of an image are being loaded by @feloy in [#8210](https://github.com/containers/podman-desktop/pull/8210) -- feat: add tooltip with full path when cli tool is detected by @dgolovin in [#8330](https://github.com/containers/podman-desktop/pull/8330) -- feat: allow to manually refresh the catalog by @benoitf in [#8340](https://github.com/containers/podman-desktop/pull/8340) -- feat: allow to open dropdownmenu on right side of a menu button by @gastoner in [#8308](https://github.com/containers/podman-desktop/pull/8308) -- feat: allow to show/hide navigation items by @benoitf in [#8322](https://github.com/containers/podman-desktop/pull/8322) -- feat: dialog component by @deboer-tim in [#7967](https://github.com/containers/podman-desktop/pull/7967) -- feat: enable light mode for buildImageFromContainerfile page by @lstocchi in [#7773](https://github.com/containers/podman-desktop/pull/7773) -- feat: enable light mode on onboarding by @lstocchi in [#7932](https://github.com/containers/podman-desktop/pull/7932) -- feat: enable light mode play k8s page by @lstocchi in [#7774](https://github.com/containers/podman-desktop/pull/7774) -- feat: light and dark mode for terminals by @cdrage in [#8237](https://github.com/containers/podman-desktop/pull/8237) -- feat: restart kubernetes pod by @vzhukovs in [#5174](https://github.com/containers/podman-desktop/pull/5174) -- Fix layer size by @feloy in [#8161](https://github.com/containers/podman-desktop/pull/8161) -- fix: add light mode to TaskManager by @lstocchi in [#8046](https://github.com/containers/podman-desktop/pull/8046) -- fix: center loading circle where LoadingIconButton is used by @SoniaSandler in [#8285](https://github.com/containers/podman-desktop/pull/8285) -- fix: Delete the removal of sha256: prefix in imageHref by @SoniaSandler in [#7677](https://github.com/containers/podman-desktop/pull/7677) -- fix: fix tooltip error message overflow by @SoniaSandler in [#8200](https://github.com/containers/podman-desktop/pull/8200) -- fix: handle ignoreFocusOut on Modal and dialog visibility by @lstocchi in [#8081](https://github.com/containers/podman-desktop/pull/8081) -- fix: light mode for podman machine details page by @lstocchi in [#7889](https://github.com/containers/podman-desktop/pull/7889) -- fix: preferences not shown on selected section by @lstocchi in [#8208](https://github.com/containers/podman-desktop/pull/8208) -- fix: refine colors used in form pages by @lstocchi in [#7910](https://github.com/containers/podman-desktop/pull/7910) -- fix: rename volume usage button by @myfear in [#7879](https://github.com/containers/podman-desktop/pull/7879) -- fix: revalidate ports when one custom port mapping entry is removed by @lstocchi in [#8015](https://github.com/containers/podman-desktop/pull/8015) -- fix: text overlay on dashboard card by @lstocchi in [#8032](https://github.com/containers/podman-desktop/pull/8032) -- fix: update media queries to prevent install button to overflow card by @lstocchi in [#8002](https://github.com/containers/podman-desktop/pull/8002) -- fix: use markdown prop with Markdown component by @lstocchi in [#8072](https://github.com/containers/podman-desktop/pull/8072) -- fix(compose-extension): pre-existing system-wide installation by @axel7083 in [#7942](https://github.com/containers/podman-desktop/pull/7942) -- bug: fix name display issues with compose files by @cdrage in [#8025](https://github.com/containers/podman-desktop/pull/8025) -- indicate tag image when deleting image by @feloy in [#8320](https://github.com/containers/podman-desktop/pull/8320) -- Light mode for FilesystemLayerView by @feloy in [#8145](https://github.com/containers/podman-desktop/pull/8145) -- refactor(Task): removing gotoTask field by @axel7083 in [#7799](https://github.com/containers/podman-desktop/pull/7799) -- test: add installation component for new e2e test of Kind feature by @amisskii in [#7976](https://github.com/containers/podman-desktop/pull/7976) -- nit: fix capitalization in search bar by @deboer-tim in [#8063](https://github.com/containers/podman-desktop/pull/8063) -- refactor(navigation): move navigation pages to api package by @axel7083 in [#7924](https://github.com/containers/podman-desktop/pull/7924) -- Font sizes and UI consistency by @deboer-tim in [#7995](https://github.com/containers/podman-desktop/pull/7995) - -### ui-components - -- chore: add missing initializer of global variables by @benoitf in [#7849](https://github.com/containers/podman-desktop/pull/7849) -- chore: adjust search and tab bar width by @deboer-tim in [#7953](https://github.com/containers/podman-desktop/pull/7953) -- chore: migrate StatusIcon to ui library by @axel7083 in [#7900](https://github.com/containers/podman-desktop/pull/7900) -- chore: use fileinput in build image form by @deboer-tim in [#7939](https://github.com/containers/podman-desktop/pull/7939) -- chore: use fileinput in play kube by @deboer-tim in [#7966](https://github.com/containers/podman-desktop/pull/7966) -- chore: use fileinput in run image by @deboer-tim in [#8076](https://github.com/containers/podman-desktop/pull/8076) -- feat: basic light mode for monaco editors by @deboer-tim in [#8262](https://github.com/containers/podman-desktop/pull/8262) - -### website 🌐 - -- chore: bump typedoc, typedoc-plugin-markdown, docusaurus-plugin-typedoc by @axel7083 in [#7882](https://github.com/containers/podman-desktop/pull/7882) -- fix(website): storybook blank page by @axel7083 in [#7994](https://github.com/containers/podman-desktop/pull/7994) -- fix(website): Use consistent arch names on download page for macOS by @cfergeau in [#7838](https://github.com/containers/podman-desktop/pull/7838) -- fix(website): windows build and redirects by @axel7083 in [#7992](https://github.com/containers/podman-desktop/pull/7992) -- hotfix: website build by @axel7083 in [#8105](https://github.com/containers/podman-desktop/pull/8105) -- chore: adding storybook to the @storybook group by @axel7083 in [#8052](https://github.com/containers/podman-desktop/pull/8052) diff --git a/website/blog/2024-10-05-kubernetes-blog.md b/website/blog/2024-10-05-kubernetes-blog.md deleted file mode 100644 index 79c061e9d6..0000000000 --- a/website/blog/2024-10-05-kubernetes-blog.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: Build your Kubernetes application with Podman Desktop -description: Covers the end-to-end workflow to create a Kubernetes application -authors: [shipsing] -tags: [podman-desktop, podman, Kubernetes-application, deployment] -hide_table_of_contents: false ---- - -# Using Podman Desktop to create a Kubernetes application - -The integration of Podman Desktop with Kubernetes helps you to run your application on a Kubernetes cluster, such as Kind or Minikube. - -This blog covers the following aspects: - -- Build a containerized application from registry images -- Create a pod -- Set up a local Kubernetes cluster -- Deploy the application to Kubernetes -- Verify the running service - -## Building a containerized application - -With this blog, you will build a containerized application that uses: - -- a back-end Redis server container -- a front-end Python application container - -To do so, you can pull the relevant images from the `quay.io` registry. - -1. Go to the **Images** component page. -2. Click **Pull**. - ![pull from registry](img/building-a-kubernetes-application/pulling-from-registry.png) -3. Start the first container: - 1. Enter the image name to pull from a registry. For example, `quay.io/podman-desktop-demo/podify-demo-backend`. - ![enter image name](img/building-a-kubernetes-application/enter-image-name.png) - 2. Click **Pull image**. A download complete notification opens. - 3. Click **Done**. - 4. Click the **Run Image** icon corresponding to the new image added. - ![running an image](img/building-a-kubernetes-application/running-an-image.png) - 5. Enter the container name `redis-server`. - 6. Click **Start Container**. - ![start a back-end container](img/building-a-kubernetes-application/starting-a-backend-container.png) - 7. Click the **Logs** tab to view that the Redis server is running in standalone mode. - ![view Logs tab](img/building-a-kubernetes-application/redis-running-in-logs.png) - -4. Start the second container: - 1. Enter the image name to pull from a registry. For example, `quay.io/podman-desktop-demo/podify-demo-frontend`. - 2. Click **Pull image** and then **Done**. - 3. Click the `Run Image` icon corresponding to the new image added. - 4. Enter the container name `python-app`. - ![enter image name](img/building-a-kubernetes-application/python-app-image.png) - - :::note - - If the default port is already in use, you can specify a different port in the `Port mapping` field. - - ::: - - 5. Select the **Networking** tab and enter hostname as `redis-server` and IP address as `10.88.0.2` to enable communication with the Redis server. - - :::note - - You can find the IP address in the **Inspect** tab of the `redis-server` Container Details page. - - ::: - - 6. Click **Start Container**. - ![start a front-end container](img/building-a-kubernetes-application/starting-a-frontend-container.png) - 7. Click the **Logs** tab to view that the application is running on port `5000`. - ![front-end app running](img/building-a-kubernetes-application/frontend-app-running.png) - 8. Click the **Open browser** icon on the right side of the page. - 9. View the running front-end application. - ![running front-end application](img/building-a-kubernetes-application/running-application-locally.png) - -## Creating a pod - -You can use both the containers to create a pod. This way both the front-end and back end container applications can share resources, such as storage and network. - -**_Pod creation with existing containers_** - -1. Go to the **Containers** page. -2. Select both the front-end and back-end containers. -3. Click the **Create Pod** button. - ![create a pod from containers](img/building-a-kubernetes-application/creating-pod-from-containers.png) -4. Click **Create Pod**. - ![copying containers to a pod](img/building-a-kubernetes-application/copying-containers-to-a-pod.png) -5. View the newly created pod on the Pods page. -6. Click the name of the pod and then click the **Summary** tab to view its summary. - ![viewing pod details](img/building-a-kubernetes-application/viewing-pod-details.png) - -**_Alternative: Pod creation with Kubernetes YAML_** - -You can generate a Kubernetes manifest for any existing pod or container and use it to create a local Kubernetes YAML file. Then, you can customize that file and create a pod from it. - -At the time of pod creation, you can select a runtime to indicate whether you want to run the pod on a Podman engine or a Kubernetes cluster. Based on the selection, you get to see the newly created pod running in that environment on the Pods component page. - -The following procedure creates a pod that runs on a Podman engine. - -1. Go to the **Pods** page. -2. Click the overflow menu icon corresponding to the pod. - ![overflow menu icon](img/building-a-kubernetes-application/overflow-menu-icon.png) -3. Select the **Generate Kube** option from the dropdown list. -4. View the Kubernetes YAML configuration in the **Kube** tab. - ![kube manifest](img/building-a-kubernetes-application/kube-manifest.png) -5. Copy the configuration and paste it into a YAML file on your machine. -6. Edit the YAML configuration and save it. -7. Go to the **Pods** component page. -8. Click **Play Kubernetes YAML**. - ![play kubernetes yaml](img/building-a-kubernetes-application/play-kubernetes-yaml.png) -9. Select the YAML file from your machine. -10. Check that the **Runtime** field is set to `Podman container engine`. -11. Click **Play** and then **Done**. -12. View the newly created pod on the same page. - -After creating the pod, set up a local Kubernetes cluster to deploy the pod. - -## Setting up a local Kubernetes cluster - -You can set up a local Kubernetes cluster. Once the cluster is connected and running, you can deploy your application on it. - -Based on your preference, use the Kind or Minikube extension: - -1. [Install the extension](/docs/extensions/install) from the Extensions catalog. -2. Create a Kubernetes cluster. See [Kind cluster creation](/docs/kind/installing-extension) or [Minikube cluster creation](/docs/minikube/installing-extension). - -Once a kubernetes cluster is created, you can view a running control plane node and a running Kubernetes service on the Kubernetes component page. The page also shows that the cluster is connected. - -## Deploying the application to Kubernetes - -You can deploy the application pod to a Kubernetes cluster that has an active connection and access it through a service. Also, any container that is part of a pod is deployable to a Kubernetes cluster. - -1. Select your [Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). -2. Go to the **Pods** component page. -3. Click the overflow menu icon corresponding to the pod. - ![overflow menu icon](img/building-a-kubernetes-application/overflow-menu-icon.png) -4. Select the **Deploy to Kubernetes** option from the dropdown list. -5. Select the checkbox to expose the service locally using the ingress controller. - ![expose the service locally](img/building-a-kubernetes-application/expose-the-service-locally.png) - - :::note - - When you configure custom port mapping while running an image, you get the option to select an Ingress host port from the dropdown list. - ![ingress-host-port](img/building-a-kubernetes-application/ingress-host-port.png) - Otherwise, you do not see the option. - ::: - -6. Click **Deploy** and then **Done**. - -## Verifying the running service - -1. Go the **Kubernetes** component page. -2. Perform the following steps: - 1. Click the **Services** option to view the newly created service, `my-pod-5000`. - ![service created](img/building-a-kubernetes-application/service-created.png) - 2. Click the **Ingresses and Routes** option to view the newly created ingress, `my-pod`. - ![ingress created](img/building-a-kubernetes-application/ingress-created.png) - - :::note - - Use the **Apply YAML** button to directly apply a Kubernetes YAML file to create a resource. - - ::: diff --git a/website/blog/2024-10-07-release-1.13.md b/website/blog/2024-10-07-release-1.13.md deleted file mode 100644 index 51248f8044..0000000000 --- a/website/blog/2024-10-07-release-1.13.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: Podman Desktop 1.13 Release -description: Podman Desktop 1.13 has been released! -slug: podman-desktop-release-1.13 -authors: SoniaSandler -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.13/banner.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.13 Release! 🎉 - -![podman-desktop-hero-1.13](img/podman-desktop-release-1.13/banner.png) - -Podman Desktop 1.13 is now available. [Click here to download it](/downloads)! - -This release includes: - -- **Hyper-V support**: You can now create and manage Hyper-V Podman machines directly from Podman Desktop. -- **Search image feature**: Search for an image from Podman Desktop. -- **Updated empty state pages**: Start your journey in the containers, images, pods, and Kubernetes pages with a click of a button -- **New Kubernetes navigation**: All Kubernetes related pages have been moved to a separate sub menu for easier and less crowded navigation. -- **Image Layer explorer extension**: With this new extension, you can explore the various layers of images. -- **Experimental Docker compatibility mode page**: You can now try out our experimental Docker compatibility mode page. - - -## Release Details - -### Hyper-V support - -Podman Desktop is now supporting the creation and modification of Hyper-V based Podman machines on Windows directly from the app. -When creating a Podman machine in Windows, if both Hyper-V and WSL are enabled, you can choose either one of them as the provider from the dropdown menu. - -### Search image feature - -As you type, a list of likely images is now shown to help you find the right one and reduce typing. If you would like to search for a specific tag, just add : to the name. - -![search image](img/podman-desktop-release-1.13/search_image.png) - -### Updated empty state pages - -We have updated the empty state pages for images, containers, pods, and Kubernetes. You can now pull your first image, create your first container or pod, all with only one click. - -![images empty page](img/podman-desktop-release-1.13/images_empty_page.png) - -### New Kubernetes navigation - -As we grew our Kubernetes section and added new Kubernetes related pages, the navigation was becoming a bit crowded. For a better user experience, this release includes a new Kubernetes submenu with all the relevant pages in it. - -![Kubernetes menu](img/podman-desktop-release-1.13/kubernetes_menu.png) - -### Image layer explorer extension - -You can now see each layer of any image you have in Podman Desktop using the `Image Layers Explorer` extension from the extensions catalog. After downloading the extension, choose any image from the available image list, go to the `Files` tab in the `Image Details` page, click on `Fetch Layers`, and start exploring. - -![Layer explorer](img/podman-desktop-release-1.13/layer_explorer.png) - -### Docker compatibility mode page - -Try out our new experimental Docker compatibility page where you can view information about the Docker compatibility mode. -Go to Settings -> Preferences, and enable the option in the `Experimental (Docker Compatibility)` section. You'll then see the Docker compatibility page show up in the Settings menu. - -![Docker compatibility page](img/podman-desktop-release-1.13/docker_comp_page.png) - -### Additional changes - -- You can now control the zoom level of Podman Desktop in the Preferences page -- When reopening Podman Desktop, the window will open in the same position it was closed -- You can now see a summary of the release notes on the top of the Dashboard page - ---- - -## Other Notable Enhancements - -- chore: light mode for welcome page by @deboer-tim in [#8401](https://github.com/containers/podman-desktop/pull/8401) -- chore: light icon support by @deboer-tim in [#8359](https://github.com/containers/podman-desktop/pull/8359) -- chore: light mode for deploy pod to kube status by @deboer-tim in [#8395](https://github.com/containers/podman-desktop/pull/8395) -- chore: update the list of guides to include ai lab by @gastoner in [#8422](https://github.com/containers/podman-desktop/pull/8422) -- chore: allow windows titlebar icons change color dynamicaly by @gastoner in [#8677](https://github.com/containers/podman-desktop/pull/8677) -- chore: publish prereleases to another repository by @benoitf in [#8732](https://github.com/containers/podman-desktop/pull/8732) -- chore: generate summary of release notes with JSON format to the website by @SoniaSandler in [#8790](https://github.com/containers/podman-desktop/pull/8790) -- chore: add release notes to dashboard page - renderer by @SoniaSandler in [#9190](https://github.com/containers/podman-desktop/pull/9190) -- feat: allow to upgrade/downgrade cli tool by @lstocchi in [#8513](https://github.com/containers/podman-desktop/pull/8513) -- feat: remember window position and size of Podman Desktop window by @benoitf in [#8511](https://github.com/containers/podman-desktop/pull/8511) -- feat: support cli tool installer by @lstocchi in [#8534](https://github.com/containers/podman-desktop/pull/8534) -- feat: adding open release note to update confirmation dialog by @axel7083 in [#8585](https://github.com/containers/podman-desktop/pull/8585) -- feat(configuration): allow to define the value of step for Input widget by @benoitf in [#8700](https://github.com/containers/podman-desktop/pull/8700) -- feat: allow to set a zoom level by @benoitf in [#8747](https://github.com/containers/podman-desktop/pull/8747) -- feat: add preflights check for HyperV by @jeffmaury in [#8821](https://github.com/containers/podman-desktop/pull/8821) -- feat(renderer): enhance feedback form with github star link by @axel7083 in [#8800](https://github.com/containers/podman-desktop/pull/8800) -- feat: add Preference to ask before fetching image layers by @feloy in [#9146](https://github.com/containers/podman-desktop/pull/9146) -- feat: bring docker compatibility page if flag is enabled by @benoitf in [#9183](https://github.com/containers/podman-desktop/pull/9183) - ---- - -## Notable Bug Fixes - -- fix: support light theme for auth provider in 'Logged In' state by @dgolovin in [#8380](https://github.com/containers/podman-desktop/pull/8380) -- fix: update webview urls on change by @deboer-tim in [#8456](https://github.com/containers/podman-desktop/pull/8456) -- fix: storybook svelte 5 by @axel7083 in [#8495](https://github.com/containers/podman-desktop/pull/8495) -- fix: duplicated_indeterminate_progress_bar by @gastoner in [#8238](https://github.com/containers/podman-desktop/pull/8238) -- fix: select first element of quickpick by @feloy in [#8591](https://github.com/containers/podman-desktop/pull/8591) -- fix(Tasks): task lifecycle managed in main by @axel7083 in [#8144](https://github.com/containers/podman-desktop/pull/8144) -- fix: zoom level resets on window change after navigation by @dgolovin in [#8489](https://github.com/containers/podman-desktop/pull/8489) -- fix: rename untagged image by @axel7083 in [#8614](https://github.com/containers/podman-desktop/pull/8614) -- fix: number widget should accept float value for number type by @benoitf in [#8685](https://github.com/containers/podman-desktop/pull/8685) -- fix: Fixed width of widget for inc/dec numbers by @gastoner in [#8741](https://github.com/containers/podman-desktop/pull/8741) -- fix: input boxes are still using dark theme when PD is in light mode by @gastoner in [#8549](https://github.com/containers/podman-desktop/pull/8549) -- fix: update native theme directly on config change by @deboer-tim in [#8485](https://github.com/containers/podman-desktop/pull/8485) -- fix: floating precision when adding numbers by @benoitf in [#8793](https://github.com/containers/podman-desktop/pull/8793) -- fix: track external changes when displaying configuration values by @benoitf in [#8795](https://github.com/containers/podman-desktop/pull/8795) -- fix: configure certificates for secure proxy by @jeffmaury in [#8704](https://github.com/containers/podman-desktop/pull/8704) -- fix: make terminal work after stopping and starting a container by @SoniaSandler in [#8657](https://github.com/containers/podman-desktop/pull/8657) -- fix: use docker.io registry credentials for index.docker.io images by @feloy in [#8892](https://github.com/containers/podman-desktop/pull/8892) -- fix: apply light theme on preflight checks box by @benoitf in [#8991](https://github.com/containers/podman-desktop/pull/8991) -- fix: reflecting light and dark theme by AuditMessageBox by @gastoner in [#9040](https://github.com/containers/podman-desktop/pull/9040) -- fix: update input colors by @deboer-tim in [#9094](https://github.com/containers/podman-desktop/pull/9094) - ---- - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make 🦭 Podman Desktop even better. In this -release we received pull requests from the following new people: - -- [jingyuwa](https://github.com/jingyuwa) in [#8455 - doc: add section for using podman-desktop with Hyper-V](https://github.com/containers/podman-desktop/pull/8455) -- [AshvG](https://github.com/AshvG) in [#9131 - docs: fix two typos on windows-install documentation](https://github.com/containers/podman-desktop/pull/9131) -- [eddumelendez](https://github.com/eddumelendez) in [#9193 - fix: Testcontainers name](https://github.com/containers/podman-desktop/pull/9193) - ---- - -## Final notes - - - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.13.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. diff --git a/website/blog/2024-10-29-creating-an-extension.md b/website/blog/2024-10-29-creating-an-extension.md deleted file mode 100644 index fe2105b7b6..0000000000 --- a/website/blog/2024-10-29-creating-an-extension.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Introduction to Podman Desktop extensions -description: Learn how to create and customize your own extension for Podman Desktop -slug: extensions-introduction -authors: [cdrage] -tags: [podman-desktop, release, podman, extension, plugin] -hide_table_of_contents: false ---- - -![programming](img/creating-an-extension/programming.png) - -# How to create your first extension - -Extensions are a powerful tool to customize and extend the functionality of Podman Desktop. Whether you want to add new container management features, streamline current workflows, or create custom UI elements specific to your tech stack, building extensions allows you to tailor the Podman Desktop experience to your specific needs. - -In this guide, we'll introduce how you can build your own Podman Desktop extension, with links to detailed documentation that covers each part of the process. - -## Introduction to extensions - -Extensions are abundant in Podman Desktop and can be found in the **Extensions -> Catalog** section. - -![extension catalog](img/podman-desktop-release-1.10/extension-catalog.png) - -Each extension expands on Podman Desktop, such as providing [Kubernetes development clusters with Minikube](https://github.com/containers/podman-desktop-extension-minikube) or even [analyzing your image layers](https://github.com/containers/podman-desktop-extension-layers-explorer). - -Below is an example of the [layers explorer extension](https://github.com/containers/podman-desktop-extension-layers-explorer) and how it integrates into Podman Desktop: - -![layers_explorer](img/creating-an-extension/layers_explorer.png) - -## Getting started with your project - -The first step in creating your extension is setting up the project environment. To learn how to configure the project and add basic components, check out the **[Templates for creating an extension](/docs/extensions/templates)** guide, which walks you through initializing your project from an official template. - -## Adding UI components - -One of the most common tasks when creating an extension is adding a user interface. Whether it’s adding buttons, panels, or icons, UI components help make your extension more interactive and accessible. Adding a UI component is totally optional and an extension can be ran without UI components. Learn more about this in the **[Adding UI components](/docs/extensions/developing/adding-ui-components)** documentation, where you’ll find instructions on creating and integrating components into the application’s UI. - -### Working with icons - -Icons are a great way to make your extension more visually unique. You can learn how to add and style custom icons by following the **[Adding icons](/docs/extensions/developing/adding-icons)** documentation. - -Below is an example of how the [bootc extension](https://github.com/containers/podman-desktop-extension-bootc) added icons to the image list within Podman Desktop: - -![icons](img/creating-an-extension/icons.png) - -### Menus and navigation - -Extensions often integrate with existing menus and navigation to offer users easy access to new commands and features. If you want to add items to the context menu, explore the **[Menu configuration](/docs/extensions/developing/menu)** documentation, which explains how to add commands to menus and control when they are displayed using When Clauses. - -Below is an example of how the [bootc extension](https://github.com/containers/podman-desktop-extension-bootc) added a new menu command to image list: - -![menus](img/creating-an-extension/menus.png) - -## Adding and configuring commands - -Commands are the backbone of most extensions, allowing users to interact with the application and trigger specific actions. - -If you need to define and register custom commands, the **[Commands](/docs/extensions/developing/commands)** guide will show you how to create commands that respond to user actions or input, and tie them into your extension’s workflow. - -You can also configure these commands to appear in different contexts. Check out the **[When clause Contexts](/docs/extensions/developing/when-clause-context)** documentation to learn more about restricting commands to specific scenarios. - -Commands are heavily influenced by [VS Code commands](https://code.visualstudio.com/api/extension-guides/command) and can be configured similarly. See our [commands guide](/docs/extensions/developing/commands) for more information. - -## Setting up onboarding workflows - -Creating a smooth onboarding experience is essential to help users get started with your extension. This includes steps for CLI binary installations or other initial setup values. - -You can provide guidance, tutorials, or initial setup steps using the **[Onboarding workflow](/docs/extensions/developing/onboarding-workflow)** guide. - -Below is an example of how the [built-in compose extension](https://github.com/containers/podman-desktop/tree/main/extensions/compose) adds onboarding for the compose CLI binary installation: - -![compose](img/creating-an-extension/compose.png) - -## Configuration settings - -Once you’ve built your components and commands, you may want to setup configuration settings for advanced usage of your extension. - -The **[Configuration](/docs/extensions/developing/config)** documentation outlines the configuration file structure and how to link everything together to use user-specific values. - -## Publishing your extension - -Publishing enables users to install your extension, you can compile your extension into a container image for users to easily consume. Follow the **[Publishing](/docs/extensions/publish)** guide to learn how to distribute your extension. - -## Conclusion - -Creating an extension opens up endless possibilities to customize Podman Desktop to your specific needs. - -It is also easy to package and publish your extension for others to use. - -Have fun exploring our documentation on how to create an extension and happy coding! diff --git a/website/blog/2024-10-31-release-1.14.md b/website/blog/2024-10-31-release-1.14.md deleted file mode 100644 index c5b3ae523d..0000000000 --- a/website/blog/2024-10-31-release-1.14.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: Podman Desktop 1.14 Release -description: Podman Desktop 1.14 has been released! -slug: podman-desktop-release-1.14 -authors: [gastoner] -tags: [podman-desktop, release, kubernetes] -hide_table_of_contents: false - -image: /img/blog/podman-desktop-release-1.14/banner.png ---- - -import ReactPlayer from 'react-player' - -Podman Desktop 1.14 Release! 🎉 - -![podman-desktop-hero-1.14](img/podman-desktop-release-1.14/banner.png) - -Podman Desktop 1.14 is now available. [Click here to download it](/downloads)! - -This release includes: - -- **Kubernetes improvements with a new dashboard**: A new landing screen for Kubernetes has been added with UI changes that gives an overview of your entire cluster. -- **Port forwarding for pods**: This new feature allows users to configure port forwarding in their Kubernetes environment. - - -## Release details - -### Kubernetes improvements - -### Kubernetes improvements with a new dashboard - -We have updated the Kubernetes dashboard page to provide a quick overview of a user's Kubernetes cluster, alongside with multiple changes to Kubernetes backend. - -![kubernetes dashboard](img/podman-desktop-release-1.14/kubernetes_dashboard.png) - -### Port forwarding for pods - -Podman Desktop now supports port forwarding for pods in Kubernetes environments. Port forwarding can be done from the pod detail page and then visible in the Port forwarding page. - -![port forwarding](img/podman-desktop-release-1.14/port_forwarding.png) - ---- - -## Community thank you - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -release we received pull requests from the following people: - -- [Indekkusu545](https://github.com/Indekkusu545) in [#9626 - fix: incorrect system proxy format on Windows](https://github.com/containers/podman-desktop/pull/9626) - ---- - -## Final notes - -### Fixed issues - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.14.0). - -### Where to download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. - ---- - -## Detailed release changelog - -### ci 🔁 - -- chore: validate also the title of the PR to be semantic by @benoitf in [#9438](https://github.com/containers/podman-desktop/pull/9438) -- chore: allow merge commits in semantic check by @benoitf in [#9581](https://github.com/containers/podman-desktop/pull/9581) -- fix: publish @podman-extension/api package by @dgolovin in [#9061](https://github.com/containers/podman-desktop/pull/9061) - -### dashboard 📊 - -- feat(dashboard): new guides added to learning center by @slemeur in [#9638](https://github.com/containers/podman-desktop/pull/9638) -- chore: redirect to dashboard when clicking on release notes button in statusbar by @SoniaSandler in [#9623](https://github.com/containers/podman-desktop/pull/9623) -- feat: navigation to the dashboard by @deboer-tim in [#9333](https://github.com/containers/podman-desktop/pull/9333) -- fix: remove Red Hat extension pack from the recommendations by @benoitf in [#9551](https://github.com/containers/podman-desktop/pull/9551) -- chore: change the order of elements on the dashboard page by @gastoner in [#8381](https://github.com/containers/podman-desktop/pull/8381) -- fix: next version check by @SoniaSandler in [#9595](https://github.com/containers/podman-desktop/pull/9595) - -### documentation 📖 - -- docs: add kind video by @cdrage in [#9444](https://github.com/containers/podman-desktop/pull/9444) -- docs: add minikube video by @cdrage in [#9391](https://github.com/containers/podman-desktop/pull/9391) -- docs: add 1.13 release notes by @SoniaSandler in [#9261](https://github.com/containers/podman-desktop/pull/9261) -- chore: update extension catalog screenshot by @deboer-tim in [#9347](https://github.com/containers/podman-desktop/pull/9347) -- chore: update DD extension image by @deboer-tim in [#9346](https://github.com/containers/podman-desktop/pull/9346) - -### extension/kind 🍾 - -- fix: add implementation for kind update by @dgolovin in [#9258](https://github.com/containers/podman-desktop/pull/9258) - -### extension/podman 🦭 - -- fix: cannot create machine with WSL provider without administrator ri… by @jeffmaury in [#9644](https://github.com/containers/podman-desktop/pull/9644) -- chore: fix version of the podman extension by @benoitf in [#9567](https://github.com/containers/podman-desktop/pull/9567) -- chore: fix the release workflow to properly update podman ext version by @benoitf in [#9566](https://github.com/containers/podman-desktop/pull/9566) -- feat: allow to ssh to podman virtual machine - backend changes by @gastoner in [#9384](https://github.com/containers/podman-desktop/pull/9384) -- feat: allow to ssh to podman virtual machine - podman changes by @gastoner in [#9383](https://github.com/containers/podman-desktop/pull/9383) -- feat: allow to ssh to podman virtual machine - api.d.ts changes by @gastoner in [#9382](https://github.com/containers/podman-desktop/pull/9382) -- feat: provide ability to easily get a shell in a machine - backend changes - additional changes by @gastoner in [#9550](https://github.com/containers/podman-desktop/pull/9550) - -### extensions 🧩 - -- fix: add implementation for compose update by @dgolovin in [#9402](https://github.com/containers/podman-desktop/pull/9402) -- fix: check if newly installed extensions dependencies are already installed by @SoniaSandler in [#9596](https://github.com/containers/podman-desktop/pull/9596) -- chore: prefer method of the extension over system to get OS by @benoitf in [#9612](https://github.com/containers/podman-desktop/pull/9612) -- fix: update embedded image extension by @benoitf in [#9547](https://github.com/containers/podman-desktop/pull/9547) - -### install 🎁 - -- chore: fix broken pnpm lock file by @benoitf in [#9437](https://github.com/containers/podman-desktop/pull/9437) -- chore: update electron-builder to v25.1.8 by @benoitf in [#9421](https://github.com/containers/podman-desktop/pull/9421) -- fix: avoid to have proxy arguments when calling the status bar entry command by @benoitf in [#9394](https://github.com/containers/podman-desktop/pull/9394) -- fix: call onUpdate callback if configuration is updated programmatically by @benoitf in [#9366](https://github.com/containers/podman-desktop/pull/9366) -- chore: fix ui library component packaging by @benoitf in [#9340](https://github.com/containers/podman-desktop/pull/9340) -- chore: switch from prettier to biome for formatting the files by @benoitf in [#9336](https://github.com/containers/podman-desktop/pull/9336) -- chore: apply missing formatting on some files by @benoitf in [#9335](https://github.com/containers/podman-desktop/pull/9335) -- chore: fix svelte 5 deprecation warning by @deboer-tim in [#9331](https://github.com/containers/podman-desktop/pull/9331) -- chore: replace the toml library by @benoitf in [#9317](https://github.com/containers/podman-desktop/pull/9317) -- fix: cache eslint based on content by @jeffmaury in [#9305](https://github.com/containers/podman-desktop/pull/9305) -- fix: search for universal installer on macOS by @benoitf in [#9294](https://github.com/containers/podman-desktop/pull/9294) -- chore: fix publish job for npmjs by @benoitf in [#9292](https://github.com/containers/podman-desktop/pull/9292) -- fix: reset extra assets array in beforePack call to avoid including podman vm image for x64 and arm64 in arm64.zip by @dgolovin in [#9274](https://github.com/containers/podman-desktop/pull/9274) -- feat: enable podman desktop running in electron by @dgolovin in [#8478](https://github.com/containers/podman-desktop/pull/8478) -- fix: rm packages/main/src/assets from extra resources to get include into app.asar by @dgolovin in [#9514](https://github.com/containers/podman-desktop/pull/9514) -- fix: replace node-fetch by native Node.js fetch by @jeffmaury in [#9489](https://github.com/containers/podman-desktop/pull/9489) - -### Kubernetes ☸️ - -- feat: initial Kubernetes dashboard by @deboer-tim in [#9588](https://github.com/containers/podman-desktop/pull/9588) -- feat(ui): adding KubernetesCurrentContextPortForwards store by @axel7083 in [#9642](https://github.com/containers/podman-desktop/pull/9642) -- fix: add Kubernetes port forward IPCs and events by @jeffmaury in [#9505](https://github.com/containers/podman-desktop/pull/9505) -- feat: make Kubernetes informers cancellable by @feloy in [#9411](https://github.com/containers/podman-desktop/pull/9411) -- fix: patch Kubernetes-client-node to be able to handle cluster restart by @feloy in [#9409](https://github.com/containers/podman-desktop/pull/9409) -- fix: do not add again an existing Kubernetes resource by @feloy in [#9380](https://github.com/containers/podman-desktop/pull/9380) -- feat: blank kubeconfig path will be set to default one by @gastoner in [#9587](https://github.com/containers/podman-desktop/pull/9587) -- fix: work on related context, not current context by @feloy in [#9523](https://github.com/containers/podman-desktop/pull/9523) -- feat: refresh the state of a specific context by restarting its informers by @feloy in [#9487](https://github.com/containers/podman-desktop/pull/9487) -- refactor: simplify context states update by @feloy in [#9443](https://github.com/containers/podman-desktop/pull/9443) -- feat: set slower backoff for current context by @feloy in [#9392](https://github.com/containers/podman-desktop/pull/9392) -- fix: start secondary informers when context is reachable by @feloy in [#9388](https://github.com/containers/podman-desktop/pull/9388) -- feat: port forwarding backend part by @vzhukovs in [#7379](https://github.com/containers/podman-desktop/pull/7379) -- feat(k8s-port-forwaring): increasing management of ports mapping by @axel7083 in [#9592](https://github.com/containers/podman-desktop/pull/9592) -- fix: dispose existing port forwards on delete by @jeffmaury in [#9575](https://github.com/containers/podman-desktop/pull/9575) -- refactor: context checking state as derived store by @feloy in [#9504](https://github.com/containers/podman-desktop/pull/9504) - -### podman-upstream 🦭 - -- feat: update podman to v5.2.5 by @benoitf in [#9563](https://github.com/containers/podman-desktop/pull/9563) - -### settings ⚙️ - -- fix: incorrect system proxy format on Windows by @Indekkusu545 in [#9626](https://github.com/containers/podman-desktop/pull/9626) -- chore: introduce a new property scope for docker compatibility by @benoitf in [#9604](https://github.com/containers/podman-desktop/pull/9604) -- chore: properties can be part of a group by @benoitf in [#9602](https://github.com/containers/podman-desktop/pull/9602) - -### telemetry 📈 - -- chore: remove podmanListImages telemetry by @deboer-tim in [#9466](https://github.com/containers/podman-desktop/pull/9466) - -### tests 🚦 - -- chore(test): initial draft for test.step by @cbr7 in [#9613](https://github.com/containers/podman-desktop/pull/9613) -- chore: refactor test to use a mock of the utility by @benoitf in [#9611](https://github.com/containers/podman-desktop/pull/9611) -- chore(test): minor fixes and robustness improvements by @cbr7 in [#9609](https://github.com/containers/podman-desktop/pull/9609) -- chore(test): some e2e test fixes by @cbr7 in [#9573](https://github.com/containers/podman-desktop/pull/9573) -- chore(test): define the correct provider type locator by @amisskii in [#9520](https://github.com/containers/podman-desktop/pull/9520) -- chore(test): use aria-label for locator by @cbr7 in [#9464](https://github.com/containers/podman-desktop/pull/9464) -- chore(test): fix wrong category used for locator by @cbr7 in [#9404](https://github.com/containers/podman-desktop/pull/9404) -- chore(test): add delete all unused images functionality to POM by @cbr7 in [#9368](https://github.com/containers/podman-desktop/pull/9368) -- chore(test): increase Pod deletion timeout in Kubernetes PVC test by @amisskii in [#9364](https://github.com/containers/podman-desktop/pull/9364) -- fix(tests): not all update e2e tests were run by @odockal in [#9362](https://github.com/containers/podman-desktop/pull/9362) -- chore(test): make output folder customizable by @cbr7 in [#9349](https://github.com/containers/podman-desktop/pull/9349) -- test: add scenario for Kubernetes YAML edit feature by @amisskii in [#9323](https://github.com/containers/podman-desktop/pull/9323) -- test: Test for Kubernetes ConfigMaps and Secrets resource pages by @amisskii in [#9244](https://github.com/containers/podman-desktop/pull/9244) -- refactor: mock contexts constants to be able to set different values on tests by @feloy in [#9529](https://github.com/containers/podman-desktop/pull/9529) -- fix: button name in update-install E2E test by @SoniaSandler in [#9509](https://github.com/containers/podman-desktop/pull/9509) -- fix: suppress `HTMLCanvasElement.prototype.getContext not implemented` error by @dgolovin in [#9287](https://github.com/containers/podman-desktop/pull/9287) - -### ui - -- chore: change no update release notes button in status bar by @SoniaSandler in [#9621](https://github.com/containers/podman-desktop/pull/9621) -- fix: invalid condition for the display of the button by @benoitf in [#9618](https://github.com/containers/podman-desktop/pull/9618) -- feat: display a spinner when connectivity is being checked in Kubernetes pages by @feloy in [#9535](https://github.com/containers/podman-desktop/pull/9535) -- fix(ui): quick-pick handle overflow by @axel7083 in [#9527](https://github.com/containers/podman-desktop/pull/9527) -- fix: proxy settings page stuck by @jeffmaury in [#9524](https://github.com/containers/podman-desktop/pull/9524) -- chore: use dropdown component in proxy settings by @deboer-tim in [#9511](https://github.com/containers/podman-desktop/pull/9511) -- chore: use dropdown component in Kube terminal by @deboer-tim in [#9510](https://github.com/containers/podman-desktop/pull/9510) -- chore: prevent default button action (submitting form) by @deboer-tim in [#9498](https://github.com/containers/podman-desktop/pull/9498) -- fix: update Updater button names to fit in message box by @SoniaSandler in [#9497](https://github.com/containers/podman-desktop/pull/9497) -- fix: remove extra v from release notes link by @SoniaSandler in [#9495](https://github.com/containers/podman-desktop/pull/9495) -- feat: refresh button in Kubernetes empty pages by @feloy in [#9491](https://github.com/containers/podman-desktop/pull/9491) -- chore: default initial selection in dropdown by @deboer-tim in [#9537](https://github.com/containers/podman-desktop/pull/9537) -- feat: add experimental flag to display toasts when we have notifications by @benoitf in [#9488](https://github.com/containers/podman-desktop/pull/9488) -- chore: add experimental property for toasts by @benoitf in [#9481](https://github.com/containers/podman-desktop/pull/9481) -- chore: update enum item and run image page by @deboer-tim in [#9458](https://github.com/containers/podman-desktop/pull/9458) -- chore: update container engine selects to Dropdown component by @deboer-tim in [#9452](https://github.com/containers/podman-desktop/pull/9452) -- chore: remember last child page visited for Kubernetes and preferences pages by @SoniaSandler in [#9451](https://github.com/containers/podman-desktop/pull/9451) -- chore: remove timer to refresh tasks in frontend side by @benoitf in [#9446](https://github.com/containers/podman-desktop/pull/9446) -- chore: replace colors by using registry colors in ingresses-routes, kube, node, onboarding, and pod by @SoniaSandler in [#9426](https://github.com/containers/podman-desktop/pull/9426) -- chore: replace colors by using registry colors in preferences, item-formats, pvc, and recommendation by @SoniaSandler in [#9425](https://github.com/containers/podman-desktop/pull/9425) -- fix(ui): layers explorer margin issue by @axel7083 in [#9412](https://github.com/containers/podman-desktop/pull/9412) -- fix: release notes banner update button by @SoniaSandler in [#9371](https://github.com/containers/podman-desktop/pull/9371) -- fix(ui): aligned task indicator by @axel7083 in [#9363](https://github.com/containers/podman-desktop/pull/9363) -- chore: reduce width and font size subnavigation by @cdrage in [#9325](https://github.com/containers/podman-desktop/pull/9325) -- fix: Done button in DeployPodToKube by @SoniaSandler in [#9306](https://github.com/containers/podman-desktop/pull/9306) -- fix: correct selection borders for Kube Play by @deboer-tim in [#9277](https://github.com/containers/podman-desktop/pull/9277) -- fix: enable 'Update' for kubectl CLI by @dgolovin in [#9205](https://github.com/containers/podman-desktop/pull/9205) -- feat(UI): task indicator by @axel7083 in [#9186](https://github.com/containers/podman-desktop/pull/9186) -- feat: dropdown component by @deboer-tim in [#9157](https://github.com/containers/podman-desktop/pull/9157) -- chore: add warning for users about short image names by @SoniaSandler in [#9116](https://github.com/containers/podman-desktop/pull/9116) -- fix: show correct empty screens when filtering catalog by @deboer-tim in [#9108](https://github.com/containers/podman-desktop/pull/9108) -- chore: adding string value for workload kind by @axel7083 in [#9641](https://github.com/containers/podman-desktop/pull/9641) -- chore: move 2 pixel back to the left the cards to align with the mockup by @benoitf in [#9601](https://github.com/containers/podman-desktop/pull/9601) -- feat: makes containers port(s) list multi line by @axel7083 in [#9554](https://github.com/containers/podman-desktop/pull/9554) -- feat: display deployments and nodes conditions in a table by @feloy in [#9548](https://github.com/containers/podman-desktop/pull/9548) - -### website 🌐 - -- docs(website): added a troubleshooting section by @shipsing in [#9459](https://github.com/containers/podman-desktop/pull/9459) -- docs(website): updated the screenshot by @shipsing in [#9316](https://github.com/containers/podman-desktop/pull/9316) -- docs(website): created a discover Podman Desktop page by @shipsing in [#9315](https://github.com/containers/podman-desktop/pull/9315) -- docs(website): added a tutorial for interacting with a database server by @shipsing in [#9238](https://github.com/containers/podman-desktop/pull/9238) - -### other - -- chore: include 1.13.2 and 1.13.3 in bug template by @benoitf in [#9615](https://github.com/containers/podman-desktop/pull/9615) -- fix: skip output folder during format actions by @jeffmaury in [#9580](https://github.com/containers/podman-desktop/pull/9580) -- chore: move the file to the correct directory by @benoitf in [#9546](https://github.com/containers/podman-desktop/pull/9546) -- revert: define the correct provider type locator (#9520) by @benoitf in [#9530](https://github.com/containers/podman-desktop/pull/9530) -- chore: refactor logs by using an object rather than list of params by @benoitf in [#9494](https://github.com/containers/podman-desktop/pull/9494) -- chore(issue-template): sort versions by @axel7083 in [#9486](https://github.com/containers/podman-desktop/pull/9486) -- chore: comment out some duplicated/unnecessary rules taking time by @benoitf in [#9482](https://github.com/containers/podman-desktop/pull/9482) diff --git a/website/blog/2024-11-08-bootc-microshift.md b/website/blog/2024-11-08-bootc-microshift.md deleted file mode 100644 index 25b994cd86..0000000000 --- a/website/blog/2024-11-08-bootc-microshift.md +++ /dev/null @@ -1,302 +0,0 @@ ---- -title: Creating a MicroShift bootable image with Podman Desktop -description: Learn how to create a bootable image featuring MicroShift, using BootC and Podman Desktop. -authors: [cdrage] -tags: [podman-desktop, podman, rhel, bootc, extensions] -hide_table_of_contents: false ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -![banner](img/bootc-microshift/redhat_selkie.png) - -If you're unfamiliar with [BootC](https://docs.fedoraproject.org/en-US/bootc/), it offers an impressive method for deploying applications directly to bare metal from either a single Containerfile or a pre-existing bootc-supported image. - -A "bootable" image, known as a BootC container image, allows you to use a simple container image to create a full bootable operating system, whether it's a `raw` virtual machine image or an `iso` for USB installation! - -This capability is ideal for a variety of uses, from a simple HTTP server to an OS powering a full-stack application. - -In this tutorial, we'll deploy an OpenShift derivative called [MicroShift](https://www.redhat.com/en/topics/edge-computing/microshift), an edge-optimized version of OpenShift designed for single-node setups on resource-constrained configurations. Think of it as a compact version of OpenShift! - -This entire process is carried out using a single Containerfile (or Dockerfile). - -## Requirements - -Before starting the tutorial, ensure you have: - -- An active [Red Hat account](https://developers.redhat.com/articles/2024/05/07/podman-desktop-red-hat-developer-subscription) in order to access RHEL-based images -- Access to an [OpenShift Hybrid Cloud pull secret](https://console.redhat.com/openshift/install/pull-secret) -- [Podman Desktop installed](https://podman-desktop.io/downloads) -- [Podman Desktop BootC Extension](https://github.com/podman-desktop/podman-desktop-extension-bootc) -- Your preferred VM-running software (e.g., [using libvirt](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_and_managing_virtualization/introducing-virtualization-in-rhel_configuring-and-managing-virtualization#what-is-virtualization_introducing-virtualization-in-rhel)) - -## Building the BootC container image - -First, we'll build the initial BootC container image from which we'll later create a bootable OS. - -### Logging into Red Hat registry - -Before proceeding, download the Red Hat Authentication extension from the catalog to enable access to Red Hat registries: - -![red hat login](img/bootc-microshift/redhat_login.png) - -Then log into your account: - -![red hat login sign in](img/bootc-microshift/redhat_login2.png) - -### Download your OpenShift Hybrid Cloud pull secret - -Download your [pull secret](https://console.redhat.com/openshift/install/pull-secret). - -This is downloaded as `pull-secret.txt`. Put it in a secure location. - -### Creating the Containerfile (or Dockerfile) - -The Containerfile is crucial for creating the bootable image. - -It's important to note that we will be providing _one_ argument during the build and that is the **PASSWORD** in order to access the Virtual Machine that will be logged in via the `redhat` username. - -We will be using the Containerfile from the [MicroShift image mode GitHub documentation](https://github.com/openshift/microshift/blob/main/docs/contributor/image_mode.md#build-image). - -Copy the Containerfile from the above link to a new file which we will be building with Podman Desktop: - -```sh -$ curl https://raw.githubusercontent.com/openshift/microshift/main/docs/config/Containerfile.bootc-rhel9 -o Containerfile -``` - -### Build with Podman Desktop - -Select the Containerfile and build it within Podman Desktop. - -You will need to provide: - -- A password for the `redhat` user that will be created in the Containerfile. - -Pass the argument as `USER_PASSWD` in the build page arguments. - -![build](img/bootc-microshift/build.png) - -## Build the bootable image with BootC Podman Desktop extension - -### Install - -Install the BootC Podman Desktop extension from the extensions catalog: - -![install](img/bootc-microshift/install.png) - -### Build the image - -Now, create the bootable image from our container image! - -Click the new BootC icon on the navigation bar and go to build: - -![build_button](img/bootc-microshift/build_button.png) - -Once the build is complete, you'll see a confirmation on the dashboard. - -Next, select the image we built and choose an appropriate output format for testing the bootable image. **RAW** is a common choice for local testing with QEMU and other VM software like `libvirt`. - -## Testing the image - -Explore various ways to test your image, using local software or cloud platforms. Here are some common steps for using the **RAW** output bootable image. - -### Running the Virtual Machine - -This guide doesn't cover all methods for running a virtual machine, but here are the most common: - - - - -When using Hyper-V, create a `.vhd` image with BootC: - -1. When building, select the `.vhd` option. -2. [Install Hyper-V](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v) -3. [Import the virtual machine](https://learn.microsoft.com/en-us/windows-server/virtualization/hyper-v/deploy/export-and-import-virtual-machines) - - - - -1. When building, select the `.raw` option and ARM64 architecture. - -2. Install QEMU: - -```sh -$ brew install qemu -``` - -3. Navigate to the directory containing your `disk.raw` file: - -```sh -$ cd ~/output -``` - -4. Run the `qemu` command: - -```sh -$ qemu-system-aarch64 \ - -m 8G \ - -M virt \ - -accel hvf \ - -cpu host \ - -smp 4 \ - -serial mon:stdio \ - -nographic \ - -netdev user,id=mynet0,hostfwd=tcp::2222-:22 \ - -device e1000,netdev=mynet0 \ - -drive file=/opt/homebrew/share/qemu/edk2-aarch64-code.fd,format=raw,if=pflash,readonly=on \ - -drive file=disk.raw,if=virtio,cache=writethrough,format=raw -``` - -4. Verify the connection: - -With the above `qemu` command, a port has now been opened locally at :2222 to SSH forward to the bootable image. You can now access your virtual machine by doing the following: - -```sh -$ ssh redhat@localhost -p 2222 -``` - - - - -1. When building, select the `.raw` option and AMD64 architecture. - -2. [Install QEMU](https://www.qemu.org/download/#linux). - -3. Navigate to the directory containing your `disk.raw` file: - -```sh -$ cd ~/output -``` - -4. Run the `qemu` command: - -```sh -$ qemu-system-x86_64 \ - -m 8G \ - -cpu Broadwell-v4 \ - -nographic \ - -netdev user,id=mynet0,hostfwd=tcp::2222-:22 \ - -device e1000,netdev=mynet0 \ - -snapshot disk.raw -``` - -4. Verify the connection: - -With the above `qemu` command, a port has now been opened locally at :2222 to SSH forward to the bootable image. You can now access your virtual machine by doing the following: - -```sh -$ ssh redhat@localhost -p 2222 -``` - - - - -## Configuring and verifying MicroShift - -After you boot your virtual machine, you can now configure MicroShift as well as verify the connection. - -### Copying over the OpenShift pull secret - -Before proceeding with verifying OpenShift, the OpenShift pull secret must be copied over so that MicroShift can download Red Hat registry-authenticated container images. - -Below we will copy the OpenShift secret you had previously downloaded to the virtual machine. - -1. Download your [OpenShift pull secret](https://console.redhat.com/openshift/install/pull-secret) which is downloaded as `pull-secret.txt` - -2. Use `scp` to copy over to the virtual machine: - -```sh -$ scp -P 2222 pull-secret.txt redhat@localhost:~/ -``` - -3. SSH into the VM: - -```sh -$ ssh redhat@localhost -p 2222 -``` - -4. Move the secret to `/etc/crio/openshift-pull-secret`: - -```sh -$ sudo mv pull-secret.txt /etc/crio/openshift-pull-secret -``` - -5. Restart the `microshift` service: - -```sh -$ sudo systemctl restart microshift -``` - -### Listing pods - -Below we will SSH into the virtual machine and confirm that MicroShift is deploying Pods correctly: - -1. SSH into the VM: - -```sh -$ ssh redhat@localhost -p 2222 -``` - -2. Copy the generated `kubeconfig` file to `~/.kube/config`: - -```sh -$ mkdir -p ~/.kube -$ sudo cp /var/lib/microshift/resources/kubeadmin/kubeconfig ~/.kube/config -$ sudo chown redhat ~/.kube/config -``` - -3. Verify Pods are running by using `oc` or `kubectl`: - -```sh -$ kubectl get pods -A -NAMESPACE NAME READY STATUS RESTARTS AGE -kube-system csi-snapshot-controller-856bb8b9bc-9n7lj 1/1 Running 1 3d23h -kube-system csi-snapshot-webhook-7c64d4d4d7-98v6l 1/1 Running 1 3d23h -openshift-dns dns-default-n2td4 2/2 Running 2 3d23h -openshift-dns node-resolver-7cslg 1/1 Running 1 3d23h -openshift-ingress router-default-7cbc67954b-nqqc6 1/1 Running 1 3d23h -openshift-ovn-kubernetes ovnkube-master-zcqw5 4/4 Running 5 3d23h -openshift-ovn-kubernetes ovnkube-node-crnn9 1/1 Running 2 3d23h -openshift-service-ca service-ca-6799f567-k7lsc 1/1 Running 1 3d23h -``` - -### Using Podman Desktop to verify MicroShift - -Alternatively, you can copy the MicroShift configuration file to your local machine and test it remotely on Podman Desktop. - -1. On your local machine, create the `.kube` directory if it does not exist already: - -```sh -$ mkdir ~/.kube -``` - -2. Copy the remote `kubeconfig` file to a local configuration file: - -Within MicroShift, a `kubeconfig` file is automatically created at `/var/lib/microshift/resources/kubeadmin/kubeconfig`. - -Copy the file over to your local system: - -```sh -scp -P 2222 redhat@localhost:/var/lib/microshift/resources/kubeadmin/kubeconfig ~/config -``` - -If you already have a `~/.kube/config`, copy the contents of `config` to the `~/.kube/config` file. - -3. Use Podman Desktop to verify the MicroShift cluster: - -Podman Desktop will automatically detect your `.kube/config` file. - -Note: You may need to modify your `.kube/config` file to reflect the correct domain or IP address of your cluster. - -![cluster](img/bootc-microshift/cluster.png) - -### Storage configuration - -By default, storage configuration [requires an LVM partition](https://github.com/openshift/microshift/blob/main/docs/contributor/storage/default_csi_plugin.md#default-volume-group) and LVMS storage manager will not be deployed. This is due to a limitation when building a RAW image. An alternative non-local storage solution is required to use OpenShift artifacts with storage capabilities. The feature to add LVM support is tracked in this [pull request](https://github.com/osbuild/images/pull/926). - -## Conclusion - -This tutorial provided a step-by-step guide on deploying a bootable MicroShift image using Podman Desktop and the BootC extension. By leveraging tools such as BootC and Podman, we've streamlined the process of creating a lightweight, yet fully functional, OpenShift environment suitable for single-node edge computing scenarios. - -Thank you for following along, and happy deploying! diff --git a/website/blog/2024-11-14-podman-desktop-cncf.md b/website/blog/2024-11-14-podman-desktop-cncf.md deleted file mode 100644 index bcfbea2282..0000000000 --- a/website/blog/2024-11-14-podman-desktop-cncf.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: Podman Desktop + CNCF - Community Driven Move -description: Today, we're thrilled to announce our application for Podman Desktop to join the Cloud Native Computing Foundation (CNCF) as a Sandbox project. -authors: [meisele, slemeur] -tags: [podman-desktop, podman, cncf, container] -hide_table_of_contents: false ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -![banner](img/podman-desktop-cncf/cncf_rocket.png) - -Today, we're thrilled to announce our application for Podman Desktop to join the Cloud Native Computing Foundation (CNCF) as a Sandbox project.This is a huge milestone for our project and our community, and we're incredibly excited for what the future holds. - -### About Podman Desktop - -Podman Desktop builds upon the innovations brought by Podman, offering a powerful yet user-friendly environment for containerized development. Podman provides a daemonless, rootless container engine that enhances security and flexibility, while Podman Desktop delivers an intuitive graphical interface for managing containers and interacting with Kubernetes. Enhanced by a plug-in system that allows developers to customize their inner loop container workflows to their needs and offers flexible extension points for other projects as well as other container engines. - -### Why the CNCF? - -The CNCF is a vital organization for the cloud-native world, supporting collaboration and driving innovation for critical projects like Kubernetes, Prometheus, and Envoy. As a CNCF project, Podman Desktop will benefit from increased visibility, a neutral home for open governance, and access to a wealth of resources and expertise. CNCF projects will take a renewed interest in collaborating with us. This move will help us grow our community, ensure the project's long-term sustainability, and accelerate its development. - -### How Podman Desktop simplifies containers and Kubernetes - -Developing with containers can be complex. Podman Desktop simplifies this by providing an intuitive interface and powerful tools for building, managing, and running containers. This allows developers to focus on writing code, not wrestling with infrastructure. Why is this important? Because in today's complex world, developer productivity is paramount. By removing friction and simplifying workflows, Podman Desktop empowers developers to deliver value faster. -Podman Desktop enables developers to run Kubernetes locally, mirroring their production environment. This eliminates the "works on my machine" problem and allows for early detection of configuration issues. By closing the gap between development and production, Podman Desktop reduces deployment risks and accelerates the feedback loop. This leads to higher quality software and faster release cycles. - -### Open source and the CNCF: A perfect match for developer tools - -We believe in the power of open source. Open source software fosters transparency, encourages collaboration, and drives innovation. By donating Podman Desktop to the CNCF, we're ensuring it remains open and accessible to all, fostering a vibrant community around it. The CNCF is the perfect home for Podman Desktop because it champions open source values and provides a neutral ground for collaborative development. This ensures that Podman Desktop remains vendor-neutral and driven by the needs of its users, keeping options open and avoiding vendor lock-in. - -Furthermore, while the CNCF has fostered incredible innovation and many developers profit from the number of projects available through the CNCF, there's a recognized need for more developer-focused tooling. Podman Desktop fills this gap perfectly by providing a developer-centric, streamlined, and intuitive experience for containerizing, managing, and deploying cloud-native applications. -Podman Desktop has a natural affinity to Kubernetes due to its design and features that seamlessly bridge the gap between local container development and Kubernetes deployments. This close relationship aligns perfectly with the CNCF's mission to drive the adoption of cloud-native technologies. - -### Join the growing Podman Desktop community - -As a CNCF project, we have the opportunity to open our doors to a wider community of contributors and users. We believe that open source thrives on collaboration and diverse perspectives. By broadening our contributor base, we can accelerate innovation, improve the quality of Podman Desktop, and ensure it meets the needs of a diverse range of users. - -### You can contribute in various ways - -- Reporting issues: If you encounter bugs or have suggestions for improvements, you can report them on the [GitHub issue tracker](https://github.com/podman-desktop/podman-desktop/issues). Be sure to provide detailed information and steps to reproduce the issue. - -- Working on issues: You can browse the [issue tracker](https://github.com/podman-desktop/podman-desktop/issues) and contribute by fixing bugs or implementing new features. This involves forking the repository, making changes, and submitting pull requests. - -- Contributing code: Beyond addressing existing issues, you can propose and contribute entirely new features or enhancements to Podman Desktop's functionality, user interface, or integrations with other tools. Learn more about [how to contribute](https://github.com/podman-desktop/podman-desktop/blob/main/CONTRIBUTING.md). - -- Contributing plug-ins: Feel like there is an integration missing? You can contribute your own plug-in functionality for Podman Desktop. [Check out the guide](https://podman-desktop.io/blog/extensions-introduction). - -- Improving documentation: Clear and comprehensive documentation is essential. You can contribute by improving existing documentation, adding new guides, or creating tutorials. - -- Providing website contributions: The [Podman Desktop website](https://podman-desktop.io/) is also open source. You can contribute to its content, design, or translations. - -- Becoming an Adopter: If you are a happy user, we’d love to know and share the word. Consider adding yourself or your organization to [the list of adopters](https://github.com/podman-desktop/podman-desktop/blob/main/ADOPTERS.md) with a pull request. - -Before you start contributing, it's helpful to familiarize yourself with the [project's contribution guidelines](https://github.com/podman-desktop/podman-desktop/blob/main/CONTRIBUTING.md) and code architecture. These resources provide valuable information on coding style, testing procedures, and the overall development process. You can also join #podman-desktop on the Kubernetes Slack to connect with other contributors and get help. - -### KubeCon 2024 and beyond - -We are at KubeCon NA 2024 in Salt Lake, to officially announce this exciting news and share more about Podman Desktop and our roadmap. Stop by the Red Hat booth to say hello, contribute to the project, and help us shape the future of cloud-native development. - -### Get involved and learn more - -We're incredibly excited about this new chapter for Podman Desktop and can't wait to see what we can achieve together with the CNCF community. - -- Visit our website: [https://podman-desktop.io/](https://podman-desktop.io/) - -- Join our community: #podman-desktop on the Kubernetes Slack - -- Contribute to the project: [https://github.com/podman-desktop/podman-desktop/](https://github.com/podman-desktop/podman-desktop/) - -- Read [10 Reasons Why Developers Should Consider Podman Desktop](https://developers.redhat.com/e-books/infographic-10-reasons-why-developer-should-consider-podman-desktop) diff --git a/website/blog/2024-11-26-ai-lab-first-app.md b/website/blog/2024-11-26-ai-lab-first-app.md deleted file mode 100644 index 9fdf0b192b..0000000000 --- a/website/blog/2024-11-26-ai-lab-first-app.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -title: Podman AI Lab - For developers to build AI Applications with LLMs running locally -description: Learn how to create your first AI application, by using Podman AI Lab -slug: podman-ai-lab-create-ai-app-with-llm-running-locally -authors: [phmartin] -tags: [podman-desktop, extension, ai, llm, local] -hide_table_of_contents: false ---- - -![banner](img/ai-lab-first-app/banner.png) - -Red Hat provides an extension to Podman Desktop, Podman AI Lab, which lets developers discover examples of applications by using large language models (LLMs), and gives them a framework to create their own AI-based applications and share them with their team. - -We will discover, through this article, the different steps to create our first AI application, and to add it to the catalog of recipes of Podman AI Lab. - -# Our first AI Application - -For our first experiment, we will work on a micro-service for the podman-desktop.io website. The micro-service would receive the search terms from the website, and would ask the model to find the best matching pages, before returning the result to the website. - -![my first app](./img/ai-lab-first-app/00-my-first-ai-app.png) - -## Preparing Podman Desktop and Podman AI Lab - -If you haven't done it yet, first [install Podman Desktop and its extension Podman AI Lab](https://podman-desktop.io/docs/ai-lab/installing). - -To have a better experience, it is recommended to use the GPU acceleration to serve the model. If you have such a GPU on your machine, you will need to create a Podman machine with the LibKrun provider (on MacOS). More details on [the GPU support for Podman AI Lab](https://developers.redhat.com/articles/2024/09/10/gpu-support-podman-ai-lab). - -At the time of writing, the GPU support is still experimental on Podman AI Lab. You will need to enable the option on the Preferences to enable it. - -![a podman machine running using libkrun](./img/ai-lab-first-app/01-ai-lab-demo-libkrun-machine.png) - -![GPU support for inference servers preference is enabled](./img/ai-lab-first-app/02-ai-lab-demo-gpu-preference.png) - -## Testing a prompt with a model - -Podman AI Lab provides a catalog of open source models that can be used locally. You can go to the `Models > Catalog` page to download the model of your choice. For this article, we will use the `Mistral-7B-instruct` model. - -![Mistral model is downloaded](./img/ai-lab-first-app/03-ai-lab-demo-mistral-model-downloaded.png) - -Once a model is downloaded, we can test and interact with this model to try to find the best prompt for our application. For chat models, Podman AI Lab provides a `Playground`, so we can test different prompts and validate that the responses of the model are adequate. - -Let's start a new playground (from the `Models > Playgrounds` menu), and send our first prompt: - -```text -Give me a list of pages in the website podman-desktop.io related to "build an image" -``` - -The model should reply with some list of pages, in a human-readable form (see the screenshot below, for the response we received). - -![a first prompt with human-readable output](./img/ai-lab-first-app/04-ai-lab-demo-prompt-1.png) - -The problem is that the response is in human-readable form, but we don't want the API to return this response as is. We want to have the name and the url of the pages, and send them to the website, so the website can display these pages with its preferred format. - -For this, we can try to ask the model to reply with a structured response, with the following prompt: - -```text -Give me a list of pages in the website podman-desktop.io related to "build an image" as JSON output as an array of objects with 2 fields name and url -``` - -This time, we received a response in JSON format, which is more suitable for our needs. - -![a prompt with structured output](./img/ai-lab-first-app/05-ai-lab-demo-prompt-json.png) - -We don't expect the user to ask such a precise question, and we would prefer to send to the model the exact question of the user, without modifying it in real time. To achieve this, chat models provide a system prompt feature. The system prompt can be defined at the beginning of the chat session. - -Podman AI Lab supports this feature, let's restart a Playground session with the following system prompt: - -```text -Give me a list of pages in the website podman-desktop.io related to the request as JSON output as an array of objects with 2 fields name and url -``` - -Then, send the prompt `build an image`, to simulate a realistic search input of a user. - -We can see in the screenshot below that the model still returns a response suitable for our use case. - -![a session with a system prompt](./img/ai-lab-first-app/06-ai-lab-demo-system-prompt.png) - -> Please note that this section was not a course on writing the best prompt, I'm sure you will find much more efficient prompts for this purpose. The purpose of this section is to demonstrate how you can iterate with Podman AI Lab to refine the prompts you want to use for your application. - -## Testing a recipe - -Now that we have a suitable prompt to use for our application, it is time to start our application itself. - -Many developers prefer to have a working example of application to start with, and Podman AI Lab provides such examples with a catalog of recipes, visible in the page `AI Apps > Recipe Catalog`. - -Let's select the Chatbot recipe (`More details` link on the Chatbot card), and start it with the Mistral model (by pressing the `Start` button and filling the form). - -Once the application is started, we can access the list of running apps in the `AI Apps > Running` page, and we can access the app's UI by clicking on the `Open AI App` link. - -We can test again by typing our prompt (not the one with a system prompt, as the recipe does not support providing a system prompt), and see that the response is very similar to the one received from the playground. - -![a session on the Chatbot recipe](./img/ai-lab-first-app/07-ai-lab-demo-recipe-session.png) - -Back to the recipe's details page, we can access the sources of the recipe by clicking on the `Open in VSCode` button, the respository's link or the link `Local clone`. - -### Structure of a recipe - -The entrypoint of a recipe is the file `ai-lab.yaml` present in the repository of the recipe. - -Let's examine the content of this file (the syntax of the file is specified in [this documentation](https://github.com/containers/podman-desktop-extension-ai-lab/blob/main/PACKAGING-GUIDE.md#recipe-configuration-file)) for the chatbot example. - -```yaml -version: v1.0 -application: - type: language - name: ChatBot_Streamlit - description: Chat with a model service in a web frontend. - containers: - - name: llamacpp-server - contextdir: ../../../model_servers/llamacpp_python - containerfile: ./base/Containerfile - model-service: true - backend: - - llama-cpp - arch: - - arm64 - - amd64 - ports: - - 8001 - image: quay.io/ai-lab/llamacpp_python:latest - - name: streamlit-chat-app - contextdir: app - containerfile: Containerfile - arch: - - arm64 - - amd64 - ports: - - 8501 - image: quay.io/ai-lab/chatbot:latest -``` - -The file defines two containers, one for the inference server and one for the application itself. - -The first container, for the inference server, is generic and can be reused for any app using a chat model. - -The second one is the one we are particularly interested in. It defines how the container's image for the application is built. It points to the Containerfile used to build the image, on which we can find the source code for the app: in the `app/chatbot_ui.py` file. - -Looking at the Python source file, we can see that the application uses the `streamlit` framework for the UI part, and the `langchain` framework for discussing with the model. - -We can adapt this source code, by replacing the UI part with a framework to make the app a REST service, and keep the `langchain` part. - -An interesting part of the source code is that the recipe does not expose to the user the system prompt, but defines one internally (`You are world class technical advisor`): - -```python -prompt = ChatPromptTemplate.from_messages([ - ("system", "You are world class technical advisor."), - MessagesPlaceholder(variable_name="history"), - ("user", "{input}") -]) -``` - -This is exactly what we want to do in our application, we will be able to indicate here the system prompt we have found earlier. - -## Creating our own app - -Adapting the source code for the purpose of our application is out of the scope of this article, let's see the result in [our app repository](https://github.com/redhat-developer/podman-desktop-demo/blob/main/ai-lab-demo/recipe/app/service.py). - -As discussed in the previous section, we have replaced the `streamlit` part with the `flask` framework to create a REST API with two endpoints: one for the health check on `/` necessary for Podman AI Lab, and another one on `/query`, which will be the endpoint on which the micro-service's user will send requests. - -We have also indicated our own system prompt: - -```python -prompt = ChatPromptTemplate.from_messages([ - ("system", """ - reply in JSON format with an array of objects with 2 fields name and url - (and with no more text than the JSON output), - with a list of pages in the website https://www.podman-desktop.io related to my query - """), - MessagesPlaceholder(variable_name="history"), - ("user", "{input}") -]) -``` - -## Testing my own app locally - -To iterate during the development of our app, we can test our app locally in our host system, while using the model served by Podman AI Lab. For this, we need to start a new model service from the page `Models > Services`, by clicking the `New Model Service`, then choosing the appropriate model (`Mistral-7B-instruct` in our case), and specifying a port number (let's say 56625). - -![a running inference server with Mistral model](./img/ai-lab-first-app/08-ai-lab-demo-inference-server.png) - -Then, we can run our app, by specifying through the `MODEL_ENDPOINT` environment variable how to access the model service. - -![my app running locally](./img/ai-lab-first-app/09-ai-lab-demo-my-app-local.png) - -Finally, we can send a request to this app running locally, and listening in the port 5000, and we can check in the screenshot below that the response is, as expected, a list of pages (name and url) in JSON format: - -![a request to the micro-service](./img/ai-lab-first-app/10-ai-lab-demo-my-app-http-request.png) - -## Creating a recipe - -The last step is to add this application to the Podman AI Lab recipe catalog. - -Podman AI Lab provides a way for a user to extend the provided catalog with its own recipes. This can be done by adding a file in a specific directory, as described [in this documentation](https://github.com/containers/podman-desktop-extension-ai-lab/tree/main?tab=readme-ov-file#-providing-a-custom-catalog). - -```json -{ - "version": "1.0", - "recipes": [ - { - "id": "search-podman-desktop-io", - "description": "Search on Podman-desktop.io website", - "name": "Search Podman-desktop.io", - "repository": "https://github.com/redhat-developer/podman-desktop-demo", - "ref": "main", - "icon": "natural-language-processing", - "categories": ["natural-language-processing"], - "basedir": "ai-lab-demo/recipe", - "readme": "", - "recommended": ["hf.TheBloke.mistral-7b-instruct-v0.2.Q4_K_M"], - "backend": "llama-cpp" - } - ] -} -``` - -By creating the file `$HOME/.local/share/containers/podman-desktop/extensions-storage/redhat.ai-lab/user-catalog.json` with the content above, you should now be able to see a new recipe `Search Podman-desktop.io` in the recipe catalog of Podman AI Lab, and run it as any other recipe. And, of course, you can share this file with your colleagues to share with them your latest experiment. diff --git a/website/blog/2024-12-12-release-1.15.md b/website/blog/2024-12-12-release-1.15.md deleted file mode 100644 index 64264a125f..0000000000 --- a/website/blog/2024-12-12-release-1.15.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -title: Podman Desktop 1.15 Release -description: Podman Desktop 1.15 has been released! -slug: podman-desktop-release-1.15 -authors: [axel7083] -tags: [podman-desktop, release, kubernetes] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.15/banner.png ---- - -import ReactPlayer from 'react-player' -import useBaseUrl from '@docusaurus/useBaseUrl'; -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.15 Release! 🎉 - -![podman-desktop-hero-1.15](/img/blog/podman-desktop-release-1.15/banner.png) - -Podman Desktop 1.15 is now available! [Click here to download it](/downloads)! - -This release brings exciting new features and improvements: - -- **Improved Feedback Form**: Redesigned for seamless issue reporting to GitHub 🪲. -- **New Experimental Task Manager**: A revamped task manager is now available for testing 🔔. -- **Enhanced Kubernetes Events**: Added support for events on resources like Nodes, Services, and Pods 📜. -- **SSH Access to Podman Machine**: Directly connect to your Podman machine from Podman Desktop. - ---- - -## Release Details 🔍 - -### Improved Feedback Form 📝 - -In the 1.15 release, we introduced a redesigned feedback form to streamline issue reporting. With our growing user base, -it’s vital to provide a convenient way to submit feedback directly to GitHub via our main repository: [podman-desktop/podman-desktop/issues](https://github.com/podman-desktop/podman-desktop/issues). - -While retaining our internal anonymous feedback system, this update also enables users to preview their issues on GitHub, ensuring better communication. - - - -#### Feedback Categories - -We now offer three distinct feedback categories: - -- **Direct your words to the developers**: Keeps feedback anonymous, visible only to our team. -- **Feature Request**: Redirects users to GitHub to suggest new features. -- **Bug**: Allows users to report bugs directly on GitHub. - - - -### Experimental Task Manager 🔔 - -Back in the [0.13 release](/blog/podman-desktop-release-0.13#new-task-manager-1724), we introduced the Task Manager. With an increasing number of long-running tasks, we’ve revamped it to improve usability. This updated version is available as an experimental feature! - -#### Enabling the Experimental Task Manager - -To try it out, go to `Settings > Preferences > Tasks` and enable the `Manager` option. - - - -#### What’s New? - -Key changes include: - -- Enhanced usage of available screen width. -- Improved consistency across the application. -- Additional capabilities for managing individual tasks. - - - -### Kubernetes Events 📜 - -This year, we’ve been steadily adding Kubernetes capabilities to Podman Desktop. While this effort is ongoing, -version 1.15 introduces support for [Kubernetes Events](https://kubernetes.io/docs/reference/kubernetes-api/cluster-resources/event-v1/). -These events are now available for resources like Nodes, Services, and Pods. - - - -### SSH Access to Podman Machine - -A frequent user request has been the ability to establish an SSH connection to a Podman machine for debugging or configuration purposes. -In this release, you can now directly connect to your Podman machine from its details page. - - - -## Community thank you - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -release, we received pull requests from the following people: - -- [@Blaimi](https://github.com/Blaimi) made their first contribution in [#9925](https://github.com/podman-desktop/podman-desktop/pull/9925) -- [@Firewall](https://github.com/Firewall) made their first contribution in [#10055](https://github.com/podman-desktop/podman-desktop/pull/10055) -- [@sozercan](https://github.com/sozercan) made their first contribution in [#10082](https://github.com/podman-desktop/podman-desktop/pull/10082) -- [@mhdawson](https://github.com/mhdawson) made their first contribution in [#10178](https://github.com/podman-desktop/podman-desktop/pull/10178) - ---- - -## Final notes - -### Fixed issues - -The complete list of issues fixed in this release is available [here](https://github.com/podman-desktop/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.15.0). - -### Where to download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/podman-desktop/podman-desktop) and see how you can help us make Podman Desktop better. - ---- - -## Detailed release changelog - -#### Documentations 📚 - -- docs(website): edited the doc to provide clarity in using the libkrun… by @shipsing in [#10111](https://github.com/podman-desktop/podman-desktop/pull/10111) -- docs(website): edited the managing objects section by @shipsing in [#9845](https://github.com/podman-desktop/podman-desktop/pull/9845) -- docs(website): fix mac gpu container base by @sozercan in [#10082](https://github.com/podman-desktop/podman-desktop/pull/10082) -- docs(website): highlighted port forwarding through UI by @shipsing in [#10006](https://github.com/podman-desktop/podman-desktop/pull/10006) -- docs(website): presented the info in procedural format by @shipsing in [#9972](https://github.com/podman-desktop/podman-desktop/pull/9972) -- docs(website): updated the docker compatibility section by @shipsing in [#9408](https://github.com/podman-desktop/podman-desktop/pull/9408) -- docs(website): updated the troubleshooting section by @shipsing in [#9918](https://github.com/podman-desktop/podman-desktop/pull/9918) -- docs(website): updates the outdated procedure by @shipsing in [#10090](https://github.com/podman-desktop/podman-desktop/pull/10090) -- docs(website):Added a reference section for PD extensions by @shipsing in [#9607](https://github.com/podman-desktop/podman-desktop/pull/9607) -- docs(website):Added a troubleshooting section for setting up PD on Wi… by @shipsing in [#9894](https://github.com/podman-desktop/podman-desktop/pull/9894) -- docs(website):added a blog about building a kubernetes application by @shipsing in [#9780](https://github.com/podman-desktop/podman-desktop/pull/9780) -- docs: 1.14.0 release notes by @gastoner in [#9628](https://github.com/podman-desktop/podman-desktop/pull/9628) -- docs: add microshift podman desktop example blog by @cdrage in [#9031](https://github.com/podman-desktop/podman-desktop/pull/9031) -- docs: add section about draft PR by @benoitf in [#9965](https://github.com/podman-desktop/podman-desktop/pull/9965) -- docs: blog post on AI Lab recipes by @feloy in [#9856](https://github.com/podman-desktop/podman-desktop/pull/9856) -- docs: fix syntax in CONTRIBUTING by @Blaimi in [#9925](https://github.com/podman-desktop/podman-desktop/pull/9925) -- docs: update extension landing page on index by @cdrage in [#9812](https://github.com/podman-desktop/podman-desktop/pull/9812) -- docs: update release template lowercase headers by @cdrage in [#9703](https://github.com/podman-desktop/podman-desktop/pull/9703) - -### Feature 💡 - -- feat(extensions/kind): update projectcontour to v1.30.1 by @Blaimi in [#9927](https://github.com/podman-desktop/podman-desktop/pull/9927) -- feat(feedback): add enabled extensions in additional-context by @axel7083 in [#10276](https://github.com/podman-desktop/podman-desktop/pull/10276) -- feat(feedback): adding frontend checkbox to include system info by @axel7083 in [#10116](https://github.com/podman-desktop/podman-desktop/pull/10116) -- feat(feedback): adding support to get sys info by @axel7083 in [#10098](https://github.com/podman-desktop/podman-desktop/pull/10098) -- feat: add a bug report feedback form by @SoniaSandler in [#9833](https://github.com/podman-desktop/podman-desktop/pull/9833) -- feat: add a feature request feedback form by @SoniaSandler in [#9955](https://github.com/podman-desktop/podman-desktop/pull/9955) -- feat: add a way to clean logs in container's log page by @benoitf in [#9528](https://github.com/podman-desktop/podman-desktop/pull/9528) -- feat: add category field to feedback by @feloy in [#9761](https://github.com/podman-desktop/podman-desktop/pull/9761) -- feat: add error visual indicator in typeahead component by @gastoner in [#9782](https://github.com/podman-desktop/podman-desktop/pull/9782) -- feat: add error visual indicator pull image input by @gastoner in [#9899](https://github.com/podman-desktop/podman-desktop/pull/9899) -- feat: add new task manager by @benoitf in [#10206](https://github.com/podman-desktop/podman-desktop/pull/10206) -- feat: allow for extensions to navigate to an onboarding screen by @benoitf in [#9759](https://github.com/podman-desktop/podman-desktop/pull/9759) -- feat: allow to cancel a task from the status bar by @benoitf in [#10209](https://github.com/podman-desktop/podman-desktop/pull/10209) -- feat: allow to cancel a task that is cancellable by @benoitf in [#10100](https://github.com/podman-desktop/podman-desktop/pull/10100) -- feat: check connectivity with health check by @feloy in [#10076](https://github.com/podman-desktop/podman-desktop/pull/10076) -- feat: check permissions on resources by @feloy in [#10133](https://github.com/podman-desktop/podman-desktop/pull/10133) -- feat: collect Service events by @feloy in [#9692](https://github.com/podman-desktop/podman-desktop/pull/9692) -- feat: display 3rd party contribution to Docker Compatibility page by @benoitf in [#9777](https://github.com/podman-desktop/podman-desktop/pull/9777) -- feat: display a warning when there is no latest tag for image to pull by @feloy in [#9757](https://github.com/podman-desktop/podman-desktop/pull/9757) -- feat: display nodes events by @feloy in [#9691](https://github.com/podman-desktop/podman-desktop/pull/9691) -- feat: display services events by @feloy in [#9733](https://github.com/podman-desktop/podman-desktop/pull/9733) -- feat: force use of libPod if nvidia device requested by @mhdawson in [#10251](https://github.com/podman-desktop/podman-desktop/pull/10251) -- feat: implement dropdown menu for status bar help button by @dgolovin in [#9867](https://github.com/podman-desktop/podman-desktop/pull/9867) -- feat: improve libpod API support - translate selinux_opts by @mhdawson in [#10178](https://github.com/podman-desktop/podman-desktop/pull/10178) -- feat: improve libpod support - translate devices by @mhdawson in [#10180](https://github.com/podman-desktop/podman-desktop/pull/10180) -- feat: introduce an hidden option kubernetes.statesExperimental by @feloy in [#10018](https://github.com/podman-desktop/podman-desktop/pull/10018) -- feat: move NumberInput to svelte-ui by @feloy in [#9872](https://github.com/podman-desktop/podman-desktop/pull/9872) -- feat: port-forward on deployments by @feloy in [#9946](https://github.com/podman-desktop/podman-desktop/pull/9946) -- feat: provide ability to easily get a shell in a machine - render part by @gastoner in [#9381](https://github.com/podman-desktop/podman-desktop/pull/9381) -- feat: publish the catalog to the website in /extensions directory by @benoitf in [#9804](https://github.com/podman-desktop/podman-desktop/pull/9804) -- feat: show message box on close of feedback form by @gastoner in [#9975](https://github.com/podman-desktop/podman-desktop/pull/9975) -- feat: update to podman v5.3.1 by @benoitf in [#9882](https://github.com/podman-desktop/podman-desktop/pull/9882) - -### Fixes 🔨 - -- fix(CliToolRegistry): notify on register install or update by @axel7083 in [#9813](https://github.com/podman-desktop/podman-desktop/pull/9813) -- fix(ci): exclude podman-remote e2e tests from running as part all tests suite by @odockal in [#9982](https://github.com/podman-desktop/podman-desktop/pull/9982) -- fix(extensions/kind): adjust contour download script for new octokit version by @Blaimi in [#9926](https://github.com/podman-desktop/podman-desktop/pull/9926) -- fix(k8s-port-forward): delete config if start fails by @axel7083 in [#9874](https://github.com/podman-desktop/podman-desktop/pull/9874) -- fix(k8s-port-forward): delete config if start fails by @axel7083 in [#9888](https://github.com/podman-desktop/podman-desktop/pull/9888) -- fix(monaco): make vscode focus border transparent by @axel7083 in [#10053](https://github.com/podman-desktop/podman-desktop/pull/10053) -- fix(status-bar): progress should be indeterminate when task is indeterminate by @axel7083 in [#9941](https://github.com/podman-desktop/podman-desktop/pull/9941) -- fix(ui): better handle errors on `KubePort` component by @axel7083 in [#9876](https://github.com/podman-desktop/podman-desktop/pull/9876) -- fix(ui): round display progress value by @axel7083 in [#10031](https://github.com/podman-desktop/podman-desktop/pull/10031) -- fix: a containerfile outside context can be used when building image by @feloy in [#9910](https://github.com/podman-desktop/podman-desktop/pull/9910) -- fix: add light mode color to toast text by @SoniaSandler in [#9915](https://github.com/podman-desktop/podman-desktop/pull/9915) -- fix: added questionmark by @gastoner in [#9814](https://github.com/podman-desktop/podman-desktop/pull/9814) -- fix: apply 0/0 as uid/gid when sending the tar as build context by @benoitf in [#10282](https://github.com/podman-desktop/podman-desktop/pull/10282) -- fix: cache pod exec websockets by @jeffmaury in [#10165](https://github.com/podman-desktop/podman-desktop/pull/10165) -- fix: compose installation via onboarding and cli tools by @dgolovin in [#10148](https://github.com/podman-desktop/podman-desktop/pull/10148) -- fix: create new connection form dropdown FormData by @SoniaSandler in [#9708](https://github.com/podman-desktop/podman-desktop/pull/9708) -- fix: creating hyperv machine in airgap now does not try to use wsl image by @gastoner in [#9715](https://github.com/podman-desktop/podman-desktop/pull/9715) -- fix: do console logging only if app is not quitting by @SoniaSandler in [#10064](https://github.com/podman-desktop/podman-desktop/pull/10064) -- fix: do not try to monitor machine when autostarting it by @benoitf in [#10308](https://github.com/podman-desktop/podman-desktop/pull/10308) -- fix: ensure system path is created and show notification if not in PATH by @jeffmaury in [#10176](https://github.com/podman-desktop/podman-desktop/pull/10176) -- fix: flaky test waitForPodsDeletion by @feloy in [#9875](https://github.com/podman-desktop/podman-desktop/pull/9875) -- fix: handle promise result by @jeffmaury in [#9700](https://github.com/podman-desktop/podman-desktop/pull/9700) -- fix: improve libpod API translation - extension by @mhdawson in [#10179](https://github.com/podman-desktop/podman-desktop/pull/10179) -- fix: navigation url to use summary by @axel7083 in [#9696](https://github.com/podman-desktop/podman-desktop/pull/9696) -- fix: proxy settings do not take effect when switched to system or disabled by @jeffmaury in [#10062](https://github.com/podman-desktop/podman-desktop/pull/10062) -- fix: rely on configurationValues if it has the key value by @lstocchi in [#9951](https://github.com/podman-desktop/podman-desktop/pull/9951) -- fix: remove extra border in details page by @axel7083 in [#10050](https://github.com/podman-desktop/podman-desktop/pull/10050) -- fix: remove nullable detailsPage binding by @axel7083 in [#9788](https://github.com/podman-desktop/podman-desktop/pull/9788) -- fix: restart container on terminal page by @feloy in [#9796](https://github.com/podman-desktop/podman-desktop/pull/9796) -- fix: skip filesystem flaky test by @axel7083 in [#10075](https://github.com/podman-desktop/podman-desktop/pull/10075) -- fix: sort array of image names, move matches on top by @dgolovin in [#9831](https://github.com/podman-desktop/podman-desktop/pull/9831) -- fix: start secondary informers for current context only by @feloy in [#9735](https://github.com/podman-desktop/podman-desktop/pull/9735) -- fix: status bar progress bar percentage display by @axel7083 in [#9791](https://github.com/podman-desktop/podman-desktop/pull/9791) -- fix: stub every missing function in renderer package tests by @dgolovin in [#9773](https://github.com/podman-desktop/podman-desktop/pull/9773) -- fix: table component should be scoped when searching items by @benoitf in [#10211](https://github.com/podman-desktop/podman-desktop/pull/10211) -- fix: terminal is not restarted if container if not running + state management by @feloy in [#9793](https://github.com/podman-desktop/podman-desktop/pull/9793) -- fix: test:renderer test failure by @jeffmaury in [#10264](https://github.com/podman-desktop/podman-desktop/pull/10264) -- fix: tty when container run with it options by @feloy in [#9745](https://github.com/podman-desktop/podman-desktop/pull/9745) -- fix: update Kubernetes context watchers messages by @jeffmaury in [#10017](https://github.com/podman-desktop/podman-desktop/pull/10017) -- fix: use `utf16le` ecoding in powershell call for Virtual Machine Platform detection by @dgolovin in [#9594](https://github.com/podman-desktop/podman-desktop/pull/9594) -- fix: use correct header for play kube operation by @benoitf in [#10036](https://github.com/podman-desktop/podman-desktop/pull/10036) -- fix: visibility of docker compatibility settings by @gastoner in [#10241](https://github.com/podman-desktop/podman-desktop/pull/10241) -- fix: watch /var/run/docker.sock on macOS by @dgolovin in [#9714](https://github.com/podman-desktop/podman-desktop/pull/9714) -- fix: workaround of LIMA_HOME usage by @tony-sol in [#10086](https://github.com/podman-desktop/podman-desktop/pull/10086) -- fix: wrong pnpm-lock file by @feloy in [#9754](https://github.com/podman-desktop/podman-desktop/pull/9754) diff --git a/website/blog/2024-12-16-cncf-projects.md b/website/blog/2024-12-16-cncf-projects.md deleted file mode 100644 index c99a4c9d06..0000000000 --- a/website/blog/2024-12-16-cncf-projects.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Using CNCF projects with Podman Desktop -description: Learn how utilize Podman Desktop in interacting and using CNCF projects! -slug: cncf-projects -authors: [cdrage] -tags: [podman-desktop, cncf, podman, extensions, compose, containers] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' - -![plane](img/cncf-projects/plane.png) - -# Launching CNCF projects from Podman Desktop - -Podman Desktop serves as a powerful tool for managing and visualizing cloud-native applications and can interact seamlessly with a range of [CNCF (Cloud Native Computing Foundation)](https://www.cncf.io/) projects. - -It's an accessible platform for developers working with single-container applications, multi-container configurations with Compose files, and complex, distributed applications on Kubernetes clusters. - -## Key features of Podman Desktop for CNCF projects - -Podman Desktop brings together three powerful features for managing small to large-scale projects: - -- **Container Management**: Supports creating, running, and monitoring containers. -- **Compose Support**: Allows you to deploy applications defined in [Compose files](https://www.compose-spec.io/). This is particularly useful for managing applications that require multiple services, such as web servers, databases, and caches. -- **Kubernetes Integration**: Offers tools to manage multi-node Kubernetes clusters, making it ideal for handling more complex distributed applications that need orchestration across several pods and services. You can setup your own development cluster with Podman Desktop using our [Minikube](/docs/minikube/installing-extension) or [Kind](/docs/kind/installing-extension) extensions. - -## Minikube - -[Minikube](https://minikube.sigs.k8s.io/docs/) is a local Kubernetes development cluster which allows for an easy way to learn and develop for Kubernetes. - -Minikube can be seamlessly integrated with Podman Desktop, enabling Kubernetes development workflows within Podman’s environment. This is made possible by [installing the Minikube extension](https://podman-desktop.io/docs/minikube/installing-extension), which allows creating, managing, and deploying clusters directly from the Podman Desktop. - -The following video provides a complete guide from installation to cluster creation: - - - -## Backstage - -[Backstage](https://backstage.io/) is an open-source platform for building developer portals, designed by Spotify. It empowers engineering teams to create customized, centralized hubs for managing and documenting their services, applications, and infrastructure. Backstage’s extensible architecture includes features for cataloging software components, organizing documentation, managing cloud resources, and tracking workflows. - -A popular method for deploying Backstage is through a [Helm chart](https://github.com/backstage/charts). Once deployed, you can view Backstage’s services in the Kubernetes Dashboard to monitor components and ensure proper configuration: - -![backstage services](img/cncf-projects/backstage.png) - -You can also access your deployed Backstage instance by using Podman Desktop's port forwarding feature. This feature allows you to securely forward a local port to the Backstage service running on your Kubernetes cluster, making it easy to access the instance from a local browser. - -![backstage port forward](img/cncf-projects/backstage_port.png) - -## Dapr - -[Dapr](https://docs.dapr.io/) (Distributed Application Runtime) is an open-source, event-driven runtime designed to help developers build resilient, stateless, and stateful applications that can run seamlessly on cloud or edge environments. - -Dapr abstracts the complexities of distributed systems, offering building blocks for service invocation, state management, publish/subscribe messaging, and resource bindings, which simplify the development of microservices and cloud-native applications. - -Dapr can be deployed in a local environment using Podman by following their [self-hosted podman setup](https://docs.dapr.io/operations/hosting/self-hosted/self-hosted-with-podman/). - -To initialize Dapr with Podman after installing the Dapr CLI, execute the following command: - -```console -$ dapr init --container-runtime podman -``` - -Once initialized, you can manage and interact with Dapr directly within Podman Desktop: - -![dapr](img/cncf-projects/dapr.png) - -Additionally, Podman Desktop provides a "Launch Browser" button, allowing quick and convenient access to the Dapr UI for monitoring and management: - -![dapr browser](img/cncf-projects/dapr_browser.png) - -## Conclusion - -Whether you’re managing Kubernetes clusters, harnessing the power of Backstage for developer portals, or deploying microservices with Dapr, Podman Desktop provides a unified environment to streamline your workflows. - -Check out the [list of graduate and incubating projects](https://www.cncf.io/projects/) to discover even more possibilities with Podman Desktop. diff --git a/website/blog/2025-01-07-bootc-release-1.6.0.md b/website/blog/2025-01-07-bootc-release-1.6.0.md deleted file mode 100644 index 9eb2b984e4..0000000000 --- a/website/blog/2025-01-07-bootc-release-1.6.0.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Podman Desktop BootC extension 1.6 Release -description: Podman Desktop BootC extension 1.6 has been released! -slug: bootc-release-1.6 -authors: [cdrage] -tags: [podman-desktop, release, rhel, image-mode, bootc, extensions, extension] -hide_table_of_contents: false ---- - -BootC extension 1.6 Release! 🎉 - -![banner](img/bootc-release-1.6/banner.png) - -[BootC (Bootable Container) is an extension](https://github.com/podman-desktop/extension-bootc) for Podman Desktop that builds bootable _container_ disk images. Go from a standard container image to a full bootable-on-a-usb-stick OS! - -You can update or install the extension [via the Podman Desktop extension catalog.](/docs/extensions/install) - -This release introduces exciting new features and improvements: - -- **Detailed example pages:** Each example now has a dedicated page with detailed instructions on how to use it. -- **Interactive build configuration creator:** Easily create your build configuration through a fillable form directly in the GUI. -- **Experimental Linux VM support:** Added support for running Linux VMs on generated images. - ---- - -## Release Details - -### Examples now have detail pages - -Each example now includes a dedicated detail page! Click on **More Details** in the **Examples** section to view step-by-step instructions for each example. - -![example details](img/bootc-release-1.6/examples.png) - -### Interactive build config creator - -No need to manually create a [custom build config](https://github.com/osbuild/bootc-image-builder?tab=readme-ov-file#-build-config). Use our interactive build configuration creator to easily generate your own build config through a user-friendly form. - -![build config interactive](img/bootc-release-1.6/build_config.png) - -### Experimental Linux VM support - -Linux support is now available for running virtual machines on generated images! Look for the new **Virtual Machine (Experimental)** tab or the dedicated VM launch button on the **Disk Images** page. - -![linux support](img/bootc-release-1.6/linux_vm.png) - ---- - -## Detailed release changelog - -### Features 💡 - -- feat: add example details page by @cdrage in [#1017](https://github.com/podman-desktop/extension-bootc/pull/1017) -- feat: add build config configurator by @cdrage in [#1026](https://github.com/podman-desktop/extension-bootc/pull/1026) -- feat: add Linux VM experimental support by @cdrage in [#1102](https://github.com/podman-desktop/extension-bootc/pull/1102) - -### Chores 🛠️ - -- chore: remove yarn references by @deboer-tim in [#969](https://github.com/podman-desktop/extension-bootc/pull/969) -- chore: update to latest UI library by @deboer-tim in [#971](https://github.com/podman-desktop/extension-bootc/pull/971) -- chore: add release process by @deboer-tim in [#970](https://github.com/podman-desktop/extension-bootc/pull/970) -- chore: delete `packages/backend/yarn.lock` by @benoitf in [#1001](https://github.com/podman-desktop/extension-bootc/pull/1001) -- chore: rename team in CODEOWNERS by @benoitf in [#999](https://github.com/podman-desktop/extension-bootc/pull/999) -- chore: refresh dependencies to update to latest versions by @benoitf in [#1003](https://github.com/podman-desktop/extension-bootc/pull/1003) -- chore: add telemetry for examples by @cdrage in [#1098](https://github.com/podman-desktop/extension-bootc/pull/1098) -- chore: update `bootc-image-builder` image by @cdrage in [#1078](https://github.com/podman-desktop/extension-bootc/pull/1078) -- chore: remove HVF acceleration from AMD64 VM command by @cdrage in [#1089](https://github.com/podman-desktop/extension-bootc/pull/1089) -- chore: add READMEs to each example by @cdrage in [#1014](https://github.com/podman-desktop/extension-bootc/pull/1014) -- chore: rename section by @cdrage in [#1015](https://github.com/podman-desktop/extension-bootc/pull/1015) -- chore: revert back to Vite 5 and update Vitest by @cdrage in [#1116](https://github.com/podman-desktop/extension-bootc/pull/1116) - -### Fixes 🔨 - -- fix: E2E tests workflow failure to install PNPM by @dgolovin in [#1085](https://github.com/podman-desktop/extension-bootc/pull/1085) -- fix: E2E main workflow node setup step by @dgolovin in [#1103](https://github.com/podman-desktop/extension-bootc/pull/1103) -- fix: navigation to webview by @cbr7 in [#1052](https://github.com/podman-desktop/extension-bootc/pull/1052) -- fix: bootc E2E tests by @cbr7 in [#998](https://github.com/podman-desktop/extension-bootc/pull/998) - -### Documentation 📚 - -- docs: update release doc by @cdrage in [#1115](https://github.com/podman-desktop/extension-bootc/pull/1115) diff --git a/website/blog/2025-01-28-release-1.16.md b/website/blog/2025-01-28-release-1.16.md deleted file mode 100644 index 0e94cbbb7a..0000000000 --- a/website/blog/2025-01-28-release-1.16.md +++ /dev/null @@ -1,321 +0,0 @@ ---- -title: Podman Desktop 1.16 Release -description: Podman Desktop 1.16 has been released! -slug: podman-desktop-release-1.16 -authors: [phmartin] -tags: [podman-desktop, release, podman, kubernetes] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.16/banner.png ---- - -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.16 Release! 🎉 - -![podman-desktop-hero-1.16](/img/blog/podman-desktop-release-1.16/banner.png) - -Podman Desktop 1.16 is now available! [Click here to download it](/downloads)! - -This release brings exciting new features and improvements: - -- **Experimental Features**: a new "Experimental" section in the Settings provides the list of current experiments, and links to related discussions -- **Providers appear in the Status Bar**: Providers are moved from Dashboard to Status Bar, to increase their visibility (experimental feature) -- **Prune only untagged images**: Choose to Prune "All untagged images" or "All unused images" when Pruning images -- **Search in Container's Logs**: Possibility to search terms in the logs of containers and pods -- **Kubernetes: Monitor current context only**: Only the current context is monitored by default - - - -## Release Details 🔍 - -### Experimental Features - -Previous versions of Podman Desktop introduced some experimental features, which were activable from the Settings. - -In Podman Desktop v1.16, these Experimental Features are visible in the new "Experimental" section of the Settings, -and dedicated Discussion pages have been created and can be accessed from this section. - - - -### Providers appear in the Status Bar - -If you activate the Experimental feature "Statusbar Providers show Providers" (see section above), the providers will -display their status in the status bar. - -Specifically, providers will display a status if they are providing at least one resource, and indicate with an icon what is the status -of their resources: running, or stopped. - -In the screenshot below, the Podman provider indicates that the Podman machine is running, and the Kind provider indicates that the Kind cluster is stopped. - - - -### Prune only untagged images - -When clicking the "Prune" button in the Images List page, it is now possible to select which images you want to be pruned, either all unused images, or all untagged images. - - - -### Search in Container's Logs - -Logs output by containers can be very long, and you might want to search for a specific term on them. Podman Desktop v1.16 now provides this possibility for Podman containers, and for Kubernetes pods. - - - -### Kubernetes: Monitor current context only - -Previous versions of Podman Desktop are trying to connect to all Kubernetes contexts found in the kubeconfig file to display how many Pods and Deployments -exist in each context. - -Many users raise that this is problematic. For example, some users have many contexts defined in their kubeconfig, and connecting to all of them can be considered as spamming by clusters. - -Other users have contexts pointing to clusters hosted by cloud providers, and connecting to these clusters needs a periodic refresh of a token authentication. -Users (or users tools) generally take care of having a fresh token for the current context, but not necesserally for the other ones. The operation of refreshing the token being, for some configurations, designed for CLI tools and not GUI tools, Podman Desktop fails to indicate to the user that this token should be refreshed, and tries infinitely to connect. - -In Podman Desktop version 1.16, the number of Pods and Deployments are displayed only for the current context, and the user can select manually for which other contexts these numbers should be displayed. - - - -## Community thank you - -🎉 We'd like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -release, we received pull requests from the following people: - -- [@fabricepipart1a](https://github.com/fabricepipart1a) made their first contribution in [#10689](https://github.com/podman-desktop/podman-desktop/pull/10689) by adding Amadeus in the ADOPTERS file - -## Final notes - -### Fixed issues - -The complete list of issues fixed in this release is available [here](https://github.com/podman-desktop/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.16.0). - -### Where to download - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/podman-desktop/podman-desktop) and see how you can help us make Podman Desktop better. - -## Detailed release changelog - -### Experimental features - -- feat(backend): adding experimental property to configuration schema by @axel7083 [#10437](https://github.com/podman-desktop/podman-desktop/pull/10437) -- feat: init experimental for preferences items by @axel7083 [#10534](https://github.com/podman-desktop/podman-desktop/pull/10534) -- feat: experimental section in the settings by @axel7083 [#10579](https://github.com/podman-desktop/podman-desktop/pull/10579) -- chore: adding docker compatibility experimental link by @axel7083 in [#10770](https://github.com/podman-desktop/podman-desktop/pull/10770) -- chore: adding status bar task to experimental by @axel7083 in [#10778](https://github.com/podman-desktop/podman-desktop/pull/10778) -- feat(ui): using full record id as title in experimental page by @axel7083 in [#10796](https://github.com/podman-desktop/podman-desktop/pull/10796) -- fix: add experimental link to status bar providers by @deboer-tim in [#10810](https://github.com/podman-desktop/podman-desktop/pull/10810) - -### Logs - -- feat: adding search controls logs window by @axel7083 [#10612](https://github.com/podman-desktop/podman-desktop/pull/10612) -- feat: enable search for pod logs by @axel7083 [#10666](https://github.com/podman-desktop/podman-desktop/pull/10666) - -### Extension development with production Podman Desktop (work in progress) - -- feat: introduce a flag to be able to see 'show DevTools' even using production binaries by @benoitf [#10659](https://github.com/podman-desktop/podman-desktop/pull/10659) -- chore: add a property to store folders used as extension folders by @benoitf [#10712](https://github.com/podman-desktop/podman-desktop/pull/10712) -- chore: add link to /extensions page on /extend by @cdrage [#10382](https://github.com/podman-desktop/podman-desktop/pull/10382) - -### Kubernetes - -- feat: run monitoring on current context only by @feloy [#10335](https://github.com/podman-desktop/podman-desktop/pull/10335) -- feat: start monitoring manually on non current contexts by @feloy [#10345](https://github.com/podman-desktop/podman-desktop/pull/10345) - -### Extension API - -- feat: add containerImageName in context for dashboard/container contribution by @feloy [#10262](https://github.com/podman-desktop/podman-desktop/pull/10262) -- feat: add containerImageName in context for icons/containersList contribution by @feloy [#10261](https://github.com/podman-desktop/podman-desktop/pull/10261) -- chore: allow to set context values for 'DockerCompatibility' scope by @benoitf [#10515](https://github.com/podman-desktop/podman-desktop/pull/10515) - -### UI Components - -- feat(ui): adding iconPosition property to settings nav item component by @axel7083 [#10672](https://github.com/podman-desktop/podman-desktop/pull/10672) -- feat: added option to show svg files in addition to fa icons by @gastoner [#10309](https://github.com/podman-desktop/podman-desktop/pull/10309) -- feat: added optional properties to close button by @gastoner [#10374](https://github.com/podman-desktop/podman-desktop/pull/10374) -- chore: move carousel component to pd svelte lib by @gastoner [#10583](https://github.com/podman-desktop/podman-desktop/pull/10583) - -### Kubernetes Experimental mode (work in progress) - -- feat: dispatch kube resources to frontend by @feloy [#10425](https://github.com/podman-desktop/podman-desktop/pull/10425) -- feat: dispatch permissions to frontend in experimental contexts mode by @feloy [#10383](https://github.com/podman-desktop/podman-desktop/pull/10383) -- feat: dispatch resources count by @feloy [#10455](https://github.com/podman-desktop/podman-desktop/pull/10455) -- feat: experimental health frontend by @feloy [#10349](https://github.com/podman-desktop/podman-desktop/pull/10349) -- feat: informers for Kubernetes experimental by @feloy [#10174](https://github.com/podman-desktop/podman-desktop/pull/10174) - -### Extension/Podman - -- fix(refactor): refactor Powershell 5 interactions in a separate file by @jeffmaury [#10284](https://github.com/podman-desktop/podman-desktop/pull/10284) -- feat: update to podman v5.3.2 by @benoitf in [#10761](https://github.com/podman-desktop/podman-desktop/pull/10761) - -### Extension/Docker - -- chore: move docker cli context select implem from core to docker extension by @benoitf [#10526](https://github.com/podman-desktop/podman-desktop/pull/10526) -- chore: use correct contexts if a custom docker config is set by @benoitf [#10555](https://github.com/podman-desktop/podman-desktop/pull/10555) - -### Adopters - -- fix: refactor adopters list format by @slemeur [#10667](https://github.com/podman-desktop/podman-desktop/pull/10667) -- docs: Amadeus as adopters by @fabricepipart1a [#10689](https://github.com/podman-desktop/podman-desktop/pull/10689) - -### UI - -- fix(push-manifest): terminal overflow by @axel7083 [#10527](https://github.com/podman-desktop/podman-desktop/pull/10527) -- fix(tasks-panel): error overflow ellipsis by @axel7083 [#10528](https://github.com/podman-desktop/podman-desktop/pull/10528) -- fix: add hidden input component in Dropdown for form submissions by @SoniaSandler [#10461](https://github.com/podman-desktop/podman-desktop/pull/10461) -- chore(onboarding): update text on welcome page by @axel7083 [#10563](https://github.com/podman-desktop/podman-desktop/pull/10563) -- chore: add Container providers using ProviderWidget by @SoniaSandler [#10625](https://github.com/podman-desktop/podman-desktop/pull/10625) -- chore: add Kubernetes providers to statusbar by @SoniaSandler [#10699](https://github.com/podman-desktop/podman-desktop/pull/10699) -- chore: add ProviderWidget component for StatusBar by @SoniaSandler [#9724](https://github.com/podman-desktop/podman-desktop/pull/9724) -- chore: add option for given value in NumberItem by @SoniaSandler [#10355](https://github.com/podman-desktop/podman-desktop/pull/10355) -- chore: change order of welcome page to prioritize container engines by @cdrage [#10562](https://github.com/podman-desktop/podman-desktop/pull/10562) -- chore: making icon and title on windows bigger by @gastoner [#10428](https://github.com/podman-desktop/podman-desktop/pull/10428) -- chore: remember collapsed state of learning center by @deboer-tim [#9597](https://github.com/podman-desktop/podman-desktop/pull/9597) -- chore: remove error body from connection error message by @SoniaSandler [#10404](https://github.com/podman-desktop/podman-desktop/pull/10404) -- chore: show only one error enoent message untill podman machine reconnects by @gastoner [#10239](https://github.com/podman-desktop/podman-desktop/pull/10239) -- chore: update styling kubernetes empty page by @cdrage [#10401](https://github.com/podman-desktop/podman-desktop/pull/10401) -- chore: allow to set values for dropdown in DockerCompatibility by @benoitf [#10516](https://github.com/podman-desktop/podman-desktop/pull/10516) -- feat: allow to prune only untagged images (and not only unused) by @benoitf [#10400](https://github.com/podman-desktop/podman-desktop/pull/10400) -- feat(ui): add close button to hide banners by @gastoner [#10414](https://github.com/podman-desktop/podman-desktop/pull/10414) -- feat: additional config value to hide recommended banners by @gastoner [#10322](https://github.com/podman-desktop/podman-desktop/pull/10322) - -### Bug fixes - -- fix(frontend): hide release note image on error by @axel7083 [#10510](https://github.com/podman-desktop/podman-desktop/pull/10510) -- fix: building image without name should have a correct task title by @jeffmaury [#10321](https://github.com/podman-desktop/podman-desktop/pull/10321) -- fix: check connection with health check by @feloy [#10457](https://github.com/podman-desktop/podman-desktop/pull/10457) -- fix: cleanup log files before starting the app by @benoitf [#10500](https://github.com/podman-desktop/podman-desktop/pull/10500) -- fix: copy docker-compose over existing file fails during installation by @dgolovin [#10313](https://github.com/podman-desktop/podman-desktop/pull/10313) -- fix: create task when running push image command by @dgolovin [#10267](https://github.com/podman-desktop/podman-desktop/pull/10267) -- fix: disable build button if no arch is selected by @benoitf [#10686](https://github.com/podman-desktop/podman-desktop/pull/10686) -- fix: fire on\*DidUpdateContainerConnection events after connection is started ans the api is setup by @feloy [#10668](https://github.com/podman-desktop/podman-desktop/pull/10668) -- fix: flathub publish workflow by @axel7083 [#10387](https://github.com/podman-desktop/podman-desktop/pull/10387) -- fix: hide legacy tasks manager when user clicks outside of it by @dgolovin [#10539](https://github.com/podman-desktop/podman-desktop/pull/10539) -- fix: margin in settings nav item by @gastoner [#10442](https://github.com/podman-desktop/podman-desktop/pull/10442) -- fix: preference rendering item not updating with boolean type by @axel7083 [#10580](https://github.com/podman-desktop/podman-desktop/pull/10580) -- fix: push modal dialog error processing by @dgolovin [#10266](https://github.com/podman-desktop/podman-desktop/pull/10266) -- fix: handle property the watch mode for extension in development mode by @benoitf [#10623](https://github.com/podman-desktop/podman-desktop/pull/10623) -- fix: toggle of help menu by @cdrage [#10339](https://github.com/podman-desktop/podman-desktop/pull/10339) -- fix: update spawn command for ui package by @jeffmaury [#10548](https://github.com/podman-desktop/podman-desktop/pull/10548) -- fix: kube health store is inactive in non experimental mode by @feloy [#10415](https://github.com/podman-desktop/podman-desktop/pull/10415) -- fix: use option property to ensure options is treated as state by @deboer-tim [#9953](https://github.com/podman-desktop/podman-desktop/pull/9953) -- chore: fix the method signature for pruneAllImages of Podman by @benoitf [#10388](https://github.com/podman-desktop/podman-desktop/pull/10388) - -### Tests - -- chore(ci): add Mac Update E2E job into PR check and e2e-main workflows by @odockal [#10384](https://github.com/podman-desktop/podman-desktop/pull/10384) -- chore(ci): add mac-update-e2e-test job into e2e-main workflow by @odockal [#10402](https://github.com/podman-desktop/podman-desktop/pull/10402) -- chore(test): introduce a parent class that encapsulates the shared functionality of all cluster creation pages by @amisskii [#10373](https://github.com/podman-desktop/podman-desktop/pull/10373) -- chore(test): Automation of Kubernetes e2e tests by @amisskii [#10320](https://github.com/podman-desktop/podman-desktop/pull/10320) -- chore(test): Parameterize the timeout for resource connection action by @amisskii [#10691](https://github.com/podman-desktop/podman-desktop/pull/10691) -- chore(test): e2e test for autodetect of compose up by @cbr7 [#10658](https://github.com/podman-desktop/podman-desktop/pull/10658) -- chore(test): extract cluster-related operations to a dedicated utility file by @amisskii [#10390](https://github.com/podman-desktop/podman-desktop/pull/10390) -- chore(test): fix flaky kind cluster restart test by @amisskii [#10421](https://github.com/podman-desktop/podman-desktop/pull/10421) -- chore(test): fix flaky volume delete behaviour by @cbr7 [#10347](https://github.com/podman-desktop/podman-desktop/pull/10347) -- chore(test): handle cli tool confirmation dialog by @amisskii [#10532](https://github.com/podman-desktop/podman-desktop/pull/10532) -- chore(test): increase timeout for extension installation from OCI image by @amisskii [#10351](https://github.com/podman-desktop/podman-desktop/pull/10351) -- chore(test): wait more time for image availbility by @cbr7 [#10645](https://github.com/podman-desktop/podman-desktop/pull/10645) -- chore(tests): adjust sandbox name in e2e tests after catalog update by @odockal [#10576](https://github.com/podman-desktop/podman-desktop/pull/10576) -- test(PullImage): adding tests to ensure reactivity by @axel7083 [#10733](https://github.com/podman-desktop/podman-desktop/pull/10733) -- test(front): adding unit tests for PodDetailsLogs by @axel7083 [#10662](https://github.com/podman-desktop/podman-desktop/pull/10662) -- test(ui): adding unit tests for TerminalWindows by @axel7083 [#10531](https://github.com/podman-desktop/podman-desktop/pull/10531) -- test: kind details operations by @amisskii [#10710](https://github.com/podman-desktop/podman-desktop/pull/10710) -- fix(ci): remove duplicated pnpm install steps in e2e jobs by @odockal [#10422](https://github.com/podman-desktop/podman-desktop/pull/10422) -- fix: container details terminal flaky test on macos by @axel7083 [#10407](https://github.com/podman-desktop/podman-desktop/pull/10407) -- fix: do not use window as any by @feloy [#10635](https://github.com/podman-desktop/podman-desktop/pull/10635) -- chore: generate all the mocks of exposed methods during the setup of vitest by @benoitf [#10426](https://github.com/podman-desktop/podman-desktop/pull/10426) -- chore: handle more cases where methods can be mocked by @benoitf [#10424](https://github.com/podman-desktop/podman-desktop/pull/10424) -- chore: add missing mocking method by @benoitf [#10423](https://github.com/podman-desktop/podman-desktop/pull/10423) -- chore: add nsis target for testing daily builds on windows by @odockal [#10684](https://github.com/podman-desktop/podman-desktop/pull/10684) -- chore: update title of the extension by @benoitf in [#10765](https://github.com/podman-desktop/podman-desktop/pull/10765) -- chore(test): simplify pull image test by @axel7083 in [#10798](https://github.com/podman-desktop/podman-desktop/pull/10798) - -### Refactoring - -- refactor(configuration): moving shared constants to `packages/api` by @axel7083 [#10344](https://github.com/podman-desktop/podman-desktop/pull/10344) -- refactor(podman): replace isMac with extension api by @axel7083 [#10417](https://github.com/podman-desktop/podman-desktop/pull/10417) -- refactor(podman-extension): uses api env.isLinux by @axel7083 [#10403](https://github.com/podman-desktop/podman-desktop/pull/10403) -- refactor(ui): preferences navigation component by @axel7083 [#10346](https://github.com/podman-desktop/podman-desktop/pull/10346) -- refactor: cleanup unused kubernetes client code by @feloy [#10458](https://github.com/podman-desktop/podman-desktop/pull/10458) -- refactor: migrate TerminalWindow to svelte5 by @axel7083 [#10728](https://github.com/podman-desktop/podman-desktop/pull/10728) -- refactor: migrate typeahead to svelte5 by @axel7083 [#10727](https://github.com/podman-desktop/podman-desktop/pull/10727) -- refactor: remove search function work from Typeahead component by @SoniaSandler [#10737](https://github.com/podman-desktop/podman-desktop/pull/10737) -- chore: convert PreferencesRenderingItemFormat to svelte 5 runes by @benoitf [#10495](https://github.com/podman-desktop/podman-desktop/pull/10495) -- chore: migrate AuditMessageBox to svelte5 rune by @benoitf [#10493](https://github.com/podman-desktop/podman-desktop/pull/10493) -- chore: migrate ContributionActions to svelte5 runes by @benoitf [#10491](https://github.com/podman-desktop/podman-desktop/pull/10491) -- chore: migrate ExtensionDetailsReadme to svelte 5 runes by @benoitf [#10492](https://github.com/podman-desktop/podman-desktop/pull/10492) -- chore: migrate ListItemButtonIcon to svelte 5 runes by @benoitf [#10490](https://github.com/podman-desktop/podman-desktop/pull/10490) -- chore: migrate LoadingIconButton to svelte 5 runes by @benoitf [#10489](https://github.com/podman-desktop/podman-desktop/pull/10489) -- chore: migrate PreferencesRendering to svelte 5 runes by @benoitf [#10496](https://github.com/podman-desktop/podman-desktop/pull/10496) -- chore: migrate ProviderResultPage (and ImageDetailsCheck) components to svelte 5 by @benoitf [#10446](https://github.com/podman-desktop/podman-desktop/pull/10446) -- chore: move PodsList to svelte5 runes mode by @benoitf [#10459](https://github.com/podman-desktop/podman-desktop/pull/10459) -- chore: update PreferencesRenderingItem to svelte5 runes by @benoitf [#10494](https://github.com/podman-desktop/podman-desktop/pull/10494) -- fix: enable the '@typescript-eslint/no-explicit-any' rule by @jeffmaury [#10172](https://github.com/podman-desktop/podman-desktop/pull/10172) -- fix: remove disablement of no explicit any rule in compose extension by @dgolovin [#10613](https://github.com/podman-desktop/podman-desktop/pull/10613) -- chore: add esnext so can use private # variables by @benoitf [#10529](https://github.com/podman-desktop/podman-desktop/pull/10529) -- chore: enable the @typescript-eslint/explicit-function-return-type for renderer part by @feloy [#10705](https://github.com/podman-desktop/podman-desktop/pull/10705) -- chore: move extension loader settings to api package by @benoitf [#10671](https://github.com/podman-desktop/podman-desktop/pull/10671) -- chore: move extension-\* files to a subfolder extension by @benoitf [#10661](https://github.com/podman-desktop/podman-desktop/pull/10661) -- chore: move extension-updater and extension-catalog as subfolders of the new extension folder by @benoitf [#10664](https://github.com/podman-desktop/podman-desktop/pull/10664) -- chore: update @kubernetes/client-node to v1.0.0 by @feloy [#10436](https://github.com/podman-desktop/podman-desktop/pull/10436) -- chore: do not declare kubernetes-client module as external by @feloy [#10439](https://github.com/podman-desktop/podman-desktop/pull/10439) -- chore: removed unused dependency by @benoitf [#10509](https://github.com/podman-desktop/podman-desktop/pull/10509) -- refactor: extract analyzeExtension to a dedicated class by @benoitf in [#10742](https://github.com/podman-desktop/podman-desktop/pull/10742) -- refactor: deployments list uses svelte 5 by @feloy in [#10760](https://github.com/podman-desktop/podman-desktop/pull/10760) -- chore: remove no-explicit-any by @benoitf in [#10749](https://github.com/podman-desktop/podman-desktop/pull/10749) -- chore: cleanup @typescript-eslint/no-explicit-any by @benoitf in [#10779](https://github.com/podman-desktop/podman-desktop/pull/10779) - -### Doc - -- docs(website): added a troubleshooting section by @shipsing [#10296](https://github.com/podman-desktop/podman-desktop/pull/10296) -- docs(website): added documentation about applying a yaml manifest by @shipsing [#10324](https://github.com/podman-desktop/podman-desktop/pull/10324) -- docs(website): added documentation for the port forwarding feature by @shipsing [#9844](https://github.com/podman-desktop/podman-desktop/pull/9844) -- docs(website): added procedures to view the container logs and access… by @shipsing [#10441](https://github.com/podman-desktop/podman-desktop/pull/10441) -- docs(website): edited the troubleshooting section by @shipsing [#10413](https://github.com/podman-desktop/podman-desktop/pull/10413) -- docs(website): removed outdated icons by @shipsing [#10575](https://github.com/podman-desktop/podman-desktop/pull/10575) -- docs(website): updated outdated procedures in the kind category of th… by @shipsing [#10577](https://github.com/podman-desktop/podman-desktop/pull/10577) -- docs(website): updated outdated procedures in the minikube category o… by @shipsing [#10688](https://github.com/podman-desktop/podman-desktop/pull/10688) -- docs(website): updated the outdated procedures in the containers cate… by @shipsing [#10540](https://github.com/podman-desktop/podman-desktop/pull/10540) -- docs: add Linux documentation for Docker Compatibility by @afbjorklund [#10481](https://github.com/podman-desktop/podman-desktop/pull/10481) -- docs: add blog cncf projects with podman desktop by @cdrage [#9743](https://github.com/podman-desktop/podman-desktop/pull/9743) -- docs: add bootc extension release 1.6.0 blog post by @cdrage [#10547](https://github.com/podman-desktop/podman-desktop/pull/10547) -- docs: fix release notes 1.15 by @SoniaSandler [#10408](https://github.com/podman-desktop/podman-desktop/pull/10408) -- docs: improve /extensions page with more info & architecture by @cdrage [#10337](https://github.com/podman-desktop/podman-desktop/pull/10337) -- docs: in code guidelines, use vi.mocked by @feloy [#10353](https://github.com/podman-desktop/podman-desktop/pull/10353) -- fix(docs): adds horizontal padding to front page by @gastoner [#10622](https://github.com/podman-desktop/podman-desktop/pull/10622) -- fix: update podman remote status command in documentation by @slemeur [#10474](https://github.com/podman-desktop/podman-desktop/pull/10474) -- fix: use prettier for formatting markdown by @afbjorklund [#10486](https://github.com/podman-desktop/podman-desktop/pull/10486) -- fix: update the markdownlint targets by @afbjorklund [#10487](https://github.com/podman-desktop/podman-desktop/pull/10487) -- chore: change GitHub podman desktop links in website to podman-desktop org by @SoniaSandler [#10340](https://github.com/podman-desktop/podman-desktop/pull/10340) -- chore: fix format in md files by @benoitf [#10524](https://github.com/podman-desktop/podman-desktop/pull/10524) -- docs: adding remark to containerEngine#listInfos by @axel7083 in [#10781](https://github.com/podman-desktop/podman-desktop/pull/10781) -- docs(website): testcontainers blog post by @gastoner in [#10440](https://github.com/podman-desktop/podman-desktop/pull/10440) -- docs(website): added a reference section and removed duplicate proced… by @shipsing in [#10740](https://github.com/podman-desktop/podman-desktop/pull/10740) diff --git a/website/blog/2025-01-29-podman-quadlet.md b/website/blog/2025-01-29-podman-quadlet.md deleted file mode 100644 index 850c820160..0000000000 --- a/website/blog/2025-01-29-podman-quadlet.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Podman Quadlets with Podman Desktop -description: Learn how to create & manage and use Quadlets with Podman Desktop -slug: podman-quadlet -authors: [axel7083] -tags: [podman-desktop, extension, podman, quadlet, systemd] -hide_table_of_contents: false ---- - -import ReactPlayer from 'react-player' -import useBaseUrl from '@docusaurus/useBaseUrl'; -import ThemedImage from '@theme/ThemedImage'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -![banner](img/podman-quadlet/banner.png) - -Containers are typically deployed in Kubernetes clusters. -However, for smaller-scale use cases such as on a single-node server or during development, Kubernetes can be overkill. - -What’s a more lightweight solution for running autonomous applications with multiple interacting containers? - -In this blog, we'll dive into what Quadlets are, their benefits, and how to use them within Podman Desktop. - - - -## What Are Quadlets? - -Podman Quadlets allow you to manage containers declaratively using systemd[^1]. -Since version 4.4, Podman can create, start, and manage containers (including pulling images, creating volumes, and managing pods) through systemd. - -Quadlets are simplified configuration files—recognized by their specific extensions, -such as `*.container`, `*.pod`, or `*.image` that are processed during startup or when you reload the daemon using the `systemctl daemon-reload` command. - -Quadlets generate the equivalent systemd unit files, streamlining the container management process. - -[^1]: https://docs.podman.io/en/latest/markdown/podman-systemd.unit.5.html - -### Why Use Quadlets? - -- **Declarative Configuration**: Similar to Compose or Kubernetes manifests, Quadlets allow you to declare what you want to run, simplifying the workload setup. -- **Tight System Integration**: Quadlets align with Podman’s philosophy of integrating seamlessly with Linux, leveraging systemd’s process management capabilities. -- **Ease of Automation**: Quadlets make it simple to configure containers to start at boot, restart on failure, and more. - -### Example: A Quadlet File for Nginx - -Below is an example of an `nginx.container` Quadlet file, which starts an nginx container at boot: - -```editorconfig title="~/.config/containers/systemd/nginx.container" -# nginx.container -[Container] -ContainerName=nginx -Image=nginx -PublishPort=80:8080 - -[Service] -Restart=always -``` - -This configuration ensures the container restarts automatically if stopped, and exposes port 8080. - -## Using the Podman Quadlet Extension in Podman Desktop - -Managing Quadlets directly on non-Linux platforms can be challenging due to virtualized environments (e.g., WSL or Hyper-V). -Fortunately, the Podman Desktop extension Podman Quadlet simplifies this process, enabling you to list, generate, and edit Quadlets visually. - -### Key Features of the Extension - -- **Integration with Podlet**: Generates Quadlets from existing Podman objects[^2]. -- **Quadlet Management UI**: Provides a dedicated interface to list, edit, delete, start, and stop Quadlets. -- **Logs Viewer**: Fetches and displays systemd logs using journalctl for troubleshooting. - -[^2]: https://github.com/containers/podlet - -### Installation - -If you already have the latest version of Podman Desktop, you can **click here to install the Podman Quadlet extension**. - -Alternatively, navigate to the Extensions page within Podman Desktop to install it. - -### List Quadlets :clipboard: - -On the Podman Quadlet page, you can view all the Quadlets available across your Podman machines. To update the list, click **Refresh**. - - -

    - -In Podman Desktop, you can see that a dedicated icon is used for the containers managed by a Quadlet. - - - -### Generate a container Quadlet :hammer: - -1. Start a container using Podman: - -```shell -podman run --name nginx-demo -d -p 80:8080 nginx -``` - -2. In Podman Desktop, find your container on the Containers page. -3. Click the **overflow menu** icon and select **Generate Quadlet**. - - -

    - -4. Click **Generate** to finalize the Quadlet. - - -

    - -5. Optional: Edit the Quadlet configuration details. -6. Click **Load into machine**. - - -

    - -Congrats 🎉 you created your first Quadlet! - -### Edit Quadlets :pen: - -Click the Quadlet **STATUS** icon to view its details page, which has three tabs: - -- **Generated**: View the systemd unit generated by Podman (read-only). -- **Source**: Edit the Quadlet file directly. -- **Logs**: Monitor logs for the service using journalctl. - -You can make changes to the Quadlet’s source file and apply updates as needed. - - - -### View Quadlet Logs :scroll: - -Since a Quadlet's corresponding resource is managed by systemd we can access corresponding unit's logs using journalctl. - - - -## Conclusion - -Podman Quadlets provide a powerful way to manage containers declaratively with systemd, bridging the gap between lightweight container management and full orchestration tools like Kubernetes. - -With the Podman Quadlet extension in Podman Desktop, users gain a convenient interface to manage Quadlets visually, reducing complexity and saving time. - -Try it today and streamline your container workflows! diff --git a/website/blog/2025-03-05-release-1.17.md b/website/blog/2025-03-05-release-1.17.md deleted file mode 100644 index 5dbaeea40c..0000000000 --- a/website/blog/2025-03-05-release-1.17.md +++ /dev/null @@ -1,444 +0,0 @@ ---- -title: Podman Desktop 1.17 Release -description: Podman Desktop 1.17 has been released! -slug: podman-desktop-release-1.17 -authors: [benoitf] -tags: [podman-desktop, release, podman, kubernetes] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.17/banner.png ---- - -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.17 Release! 🎉 - -![podman-desktop-hero-1.17](/img/blog/podman-desktop-release-1.17/banner.png) - -Podman Desktop 1.17 is now available! [Click here to download it](/downloads)! - -This release brings exciting new features and improvements: - -- **New running workflow** 🛠️: Start containers from images in just a few steps. -- **Registry mirror configuration** 🔄: Simplify registry mirroring setup with a dedicated command. -- **Smoother kind cluster experience** ☁️: Spin up a Kubernetes cluster effortlessly, even without a pre-installed kind binary. -- **Podman 5.4** 🚀: Upgrade to the latest Podman engine for enhanced performance and features. -- **Pods, redefined** 📦: Clear separation between Podman pods and Kubernetes pods for better usability. -- **Kubernetes experimental mode** ⚡: Change the way on how the resources are collected and monitored. - - - -## Release Details 🔍 - -This release brings exciting enhancements, making container management smoother than a seal gliding through the waves. Let's dive in! - -### Starting a container from scratch - -Ever wanted to start a container effortlessly? Now, just enter the image (local or remote), select it from a dropdown, and with a single click, you'll get the perfect startup prompt. In no time, your container is swimming happily in its new environment! - - - -### Mirroring of Registries – No More Fishy Workarounds - -When creating a Podman machine from Podman Desktop, we now include a playbook that mounts the host’s `registries.conf` into the VM at `/etc/containers/registries.conf.d/`. To handle this configuration, simply click on 'More Actions' in the Podman machine menu, and you're all set. No more struggling like a seal stuck on an ice floe. - - - - - -### Podman 5.4 - -A new version of Podman is here. Check out the full details: [Podman 5.4.0 Release Notes](https://github.com/containers/podman/releases/tag/v5.4.0). -:::warning[Breaking change] - -As mentioned in the podman 5.4 release notes, support for Mac/Intel is now on a best-effort basis. -::: - -:::note[Note for 2020 Mac/Intel Users] - -There is an open issue where Podman Machine is not booting on certain setups. It is currently under investigation, and you can track the progress here: [Issue #25121](https://github.com/containers/podman/issues/25121). - -In the meantime, here is a quick workaround if 5.4 is not booting: - -```sh -podman machine init --image=docker://quay.io/podman/machine-os:5.2 -``` - -::: - -## Smoother kind cluster experience - -Even if you do not have the `kind` CLI installed, Podman Desktop will guide you through the process of installing it when creating a Kind cluster, -ensuring you can set up your cluster without leaving the workflow. No more diving back to shore for missing tools! - - - -### Kubernetes: Pods and CronJobs - -Since the last version, we have introduced a dedicated Kubernetes navigation bar. Now, we are making things even clearer: - -- [Podman Pods](https://docs.podman.io/en/stable/markdown/podman-pod.1.html) and [Kubernetes Pods](https://kubernetes.io/docs/concepts/workloads/pods/) are now separate. -- A new Pods entry has been added under the Kubernetes section, ensuring you always know which pods you're working with. -- The Pods view only displays Podman pods. - -This means you can now navigate your Kubernetes resources while staying in the Kubernetes section. No more getting lost at sea! - -[CronJobs](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) are now available, making them easier to manage than ever before. - - - -### Kubernetes: new experimental mode - -Using this mode, the update of Kubernetes resources is done using health and permission checks. -One bonus now is the ability to easily troubleshoot resource status. - - - -We would love your feedback! Share your thoughts at [GitHub Discussions](https://github.com/podman-desktop/podman-desktop/discussions/11424) - -## Community thank you - -🎉 A big thank you to all the amazing contributors who helped improve Podman Desktop! Your pull requests made a splash in this release: - -- [@cyqsimon](https://github.com/cyqsimon) made their first contribution in [#11380](https://github.com/podman-desktop/podman-desktop/pull/11380) docs(website): add SELinux info to GPU guide -- [@gaetschwartz](https://github.com/gaetschwartz) made their first contribution in [#10863](https://github.com/podman-desktop/podman-desktop/pull/10863) docs: Add missing -o flag in Importing saved containers to Podman -- [@gnmerritt](https://github.com/gnmerritt) made their first contribution in [#10821](https://github.com/podman-desktop/podman-desktop/pull/10821) docs: fix path in macos docker context create example command -- [@renner0e](https://github.com/renner0e) made their first contribution in [#11022](https://github.com/podman-desktop/podman-desktop/pull/11022) feat: upgrade flatpak runtime to 24.08 -- [@rujutashinde](https://github.com/rujutashinde) made their first contribution in [#11164](https://github.com/podman-desktop/podman-desktop/pull/11164) docs: updated MAINTAINERS.md file with new user - -## Final notes - -### Fixed issues - -The complete list of issues fixed in this release is available [here](https://github.com/podman-desktop/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.17.0). - -### Where to download - -🚀 Grab the latest release from the [Downloads](/downloads) section and power up your development with Podman Desktop! Want to make it even better? Dive into our [GitHub repository](https://github.com/podman-desktop/podman-desktop) and join the action! 🦭🔥 - -## Detailed release changelog - -### Features - -- feat: add a command to configure registries of Podman [#11390](https://github.com/podman-desktop/podman-desktop/pull/11390) -- feat: display connection lost on resources pages [#11381](https://github.com/podman-desktop/podman-desktop/pull/11381) -- feat: display route details in experimental mode [#11377](https://github.com/podman-desktop/podman-desktop/pull/11377) -- feat: expandable component [#11364](https://github.com/podman-desktop/podman-desktop/pull/11364) -- feat: restart monitoring on offline context [#11360](https://github.com/podman-desktop/podman-desktop/pull/11360) -- feat: display if cluster is available in experimental mode in resources pages [#11358](https://github.com/podman-desktop/podman-desktop/pull/11358) -- feat: display Ingress details in experimental mode [#11341](https://github.com/podman-desktop/podman-desktop/pull/11341) -- feat: display CronJob details in experimental mode [#11338](https://github.com/podman-desktop/podman-desktop/pull/11338) -- feat: display PVC details in experimental mode [#11337](https://github.com/podman-desktop/podman-desktop/pull/11337) -- feat: display secret details in experimental mode [#11336](https://github.com/podman-desktop/podman-desktop/pull/11336) -- feat: display deployment details in experimental mode [#11306](https://github.com/podman-desktop/podman-desktop/pull/11306) -- feat: display node details in experimental mode [#11305](https://github.com/podman-desktop/podman-desktop/pull/11305) -- feat: display offline status in Kubernetes Contexts page [#11285](https://github.com/podman-desktop/podman-desktop/pull/11285) -- feat: monitor current context only by default [#11268](https://github.com/podman-desktop/podman-desktop/pull/11268) -- feat: display troubleshooting info for experimental Kubernetes monitoring [#11217](https://github.com/podman-desktop/podman-desktop/pull/11217) -- feat: display service details on Kubernetes experimental mode [#11196](https://github.com/podman-desktop/podman-desktop/pull/11196) -- feat: kubernetes resource navigation [#11166](https://github.com/podman-desktop/podman-desktop/pull/11166) -- feat: add apiVersion and kind to kubernetes objects [#11162](https://github.com/podman-desktop/podman-desktop/pull/11162) -- feat: navigate to kubernetes pods and cron jobs [#11122](https://github.com/podman-desktop/podman-desktop/pull/11122) -- feat(PodsLists): make the age column sortable [#11115](https://github.com/podman-desktop/podman-desktop/pull/11115) -- feat: use KubernetesObjectsList for Kube pods [#11103](https://github.com/podman-desktop/podman-desktop/pull/11103) -- feat: add and register Cronjob resource for Kubernetes experimental mode [#11100](https://github.com/podman-desktop/podman-desktop/pull/11100) -- feat: display ingresses/routes in Kubernetes experimental mode [#11065](https://github.com/podman-desktop/podman-desktop/pull/11065) -- feat: display Nodes list in Kubernetes experimental mode [#11054](https://github.com/podman-desktop/podman-desktop/pull/11054) -- feat: upgrade flatpak runtime to 24.08 [#11022](https://github.com/podman-desktop/podman-desktop/pull/11022) -- feat: display PVCs in Kubernetes experimental mode [#11021](https://github.com/podman-desktop/podman-desktop/pull/11021) -- feat: display services in Kubernetes experimental mode [#11020](https://github.com/podman-desktop/podman-desktop/pull/11020) -- feat: add cronjobs to kubernetes [#10982](https://github.com/podman-desktop/podman-desktop/pull/10982) -- feat: ask to install kind when creating cluster [#10980](https://github.com/podman-desktop/podman-desktop/pull/10980) -- feat: create a KubernetesObjectsList [#10928](https://github.com/podman-desktop/podman-desktop/pull/10928) -- feat: kube pod details [#10927](https://github.com/podman-desktop/podman-desktop/pull/10927) -- feat: expose navigateToCliTools to 3rd party extensions [#10924](https://github.com/podman-desktop/podman-desktop/pull/10924) -- feat: job and cronjob icons [#10910](https://github.com/podman-desktop/podman-desktop/pull/10910) -- feat: display deployments from experimental informers [#10880](https://github.com/podman-desktop/podman-desktop/pull/10880) -- feat: represent non accessible resources in Kubernetes Contexts page [#10856](https://github.com/podman-desktop/podman-desktop/pull/10856) -- feat: kube pods page [#10852](https://github.com/podman-desktop/podman-desktop/pull/10852) - -### Bugfixes - -- fix: revert lost change to have proper higlight/selected colors [#11384](https://github.com/podman-desktop/podman-desktop/pull/11384) -- fix: ensure that chaining calls to showQuickPick is working [#11383](https://github.com/podman-desktop/podman-desktop/pull/11383) -- fix: pass only added env vars to process.exec in Kind and Lima [#11369](https://github.com/podman-desktop/podman-desktop/pull/11369) -- fix: use correct css variable name [#11357](https://github.com/podman-desktop/podman-desktop/pull/11357) -- fix: display of route details [#11349](https://github.com/podman-desktop/podman-desktop/pull/11349) -- fix: pods should have kubernetes empty screen [#11318](https://github.com/podman-desktop/podman-desktop/pull/11318) -- fix: stop informers and delete informers and cache [#11255](https://github.com/podman-desktop/podman-desktop/pull/11255) -- fix: dispose and delete informers when a context is removed from kubeconfig file [#11252](https://github.com/podman-desktop/podman-desktop/pull/11252) -- fix: add priority based sorting for onboarding [#11248](https://github.com/podman-desktop/podman-desktop/pull/11248) -- fix: update missed pd version in mac update e2e [#11238](https://github.com/podman-desktop/podman-desktop/pull/11238) -- fix: copying docker-compose to fails on windows [#11235](https://github.com/podman-desktop/podman-desktop/pull/11235) -- fix: ports accepted type during creation [#11227](https://github.com/podman-desktop/podman-desktop/pull/11227) -- fix: svelte reactivity on Map [#11226](https://github.com/podman-desktop/podman-desktop/pull/11226) -- fix(podman): running deactivate to ensure test isolation [#11206](https://github.com/podman-desktop/podman-desktop/pull/11206) -- fix: handle compose releases with many assets [#11191](https://github.com/podman-desktop/podman-desktop/pull/11191) -- fix: duplicate keys in pnpm-lock.yaml [#11178](https://github.com/podman-desktop/podman-desktop/pull/11178) -- fix(run-image-page): checkbox interactive name used [#11159](https://github.com/podman-desktop/podman-desktop/pull/11159) -- fix: remove check of updateContainerConnection being called [#11158](https://github.com/podman-desktop/podman-desktop/pull/11158) -- fix: handle ApiException error during waitForPod/JobDeletion [#11156](https://github.com/podman-desktop/podman-desktop/pull/11156) -- fix: refresh machine settings after podman installation [#11145](https://github.com/podman-desktop/podman-desktop/pull/11145) -- fix: libkrun is only supported on silicon/M\* macs [#11136](https://github.com/podman-desktop/podman-desktop/pull/11136) -- fix(ListItemButtonIcon): only use inline-flex when hidden is not set [#11110](https://github.com/podman-desktop/podman-desktop/pull/11110) -- fix(feedback): showing modal dialog when closing feedback form [#11089](https://github.com/podman-desktop/podman-desktop/pull/11089) -- fix: do not start informers for non permitted resources [#11063](https://github.com/podman-desktop/podman-desktop/pull/11063) -- fix: support multiple permissions requests [#11058](https://github.com/podman-desktop/podman-desktop/pull/11058) -- fix: show error message dialog in case compose download error [#10987](https://github.com/podman-desktop/podman-desktop/pull/10987) -- fix: autostarted connection fires onDidUpdateContainerConnection [#10970](https://github.com/podman-desktop/podman-desktop/pull/10970) -- fix(preload-index): remove no-explicit-any [#10945](https://github.com/podman-desktop/podman-desktop/pull/10945) -- fix(webview-registry-spec): remove no-explicit-any [#10943](https://github.com/podman-desktop/podman-desktop/pull/10943) -- fix(webview-panel-impl-spec): remove no-explicit-any [#10939](https://github.com/podman-desktop/podman-desktop/pull/10939) -- fix: add kubeconfig flag to create a Kind cluster [#10905](https://github.com/podman-desktop/podman-desktop/pull/10905) -- fix: changed color of text on kubernetes empty page to have better visibility [#10896](https://github.com/podman-desktop/podman-desktop/pull/10896) -- fix: do not check connection after listen [#10877](https://github.com/podman-desktop/podman-desktop/pull/10877) -- fix(spawn-promise-spec): remove no-explicit-any [#10867](https://github.com/podman-desktop/podman-desktop/pull/10867) -- fix(exec-spec): remove no-explicit-any [#10866](https://github.com/podman-desktop/podman-desktop/pull/10866) -- fix(uri-spec): remove no-explicit-any [#10865](https://github.com/podman-desktop/podman-desktop/pull/10865) -- fix(telemetry): remove no-explicit-any [#10864](https://github.com/podman-desktop/podman-desktop/pull/10864) - -### Chore - -- chore: allow to contribute 3rd party actions for container provider connection [#11404](https://github.com/podman-desktop/podman-desktop/pull/11404) -- chore(docs): Add missing step E2E extension tests [#11400](https://github.com/podman-desktop/podman-desktop/pull/11400) -- chore: remove kube badge from pods page [#11392](https://github.com/podman-desktop/podman-desktop/pull/11392) -- chore: remove kubernetes pod watcher [#11391](https://github.com/podman-desktop/podman-desktop/pull/11391) -- chore(test): fix kubernetes e2e tests [#11387](https://github.com/podman-desktop/podman-desktop/pull/11387) -- chore: bump to storybook v8.6.0 [#11382](https://github.com/podman-desktop/podman-desktop/pull/11382) -- chore(test): wait for update dialog to switch instead of closing in e2e [#11362](https://github.com/podman-desktop/podman-desktop/pull/11362) -- chore: update the description of empty forwards configuration [#11356](https://github.com/podman-desktop/podman-desktop/pull/11356) -- chore: use kube icon on dashboard [#11353](https://github.com/podman-desktop/podman-desktop/pull/11353) -- chore(test): Update Kubernetes E2E tests to use Kubernetes pods page [#11350](https://github.com/podman-desktop/podman-desktop/pull/11350) -- chore(test): handle search result delay [#11347](https://github.com/podman-desktop/podman-desktop/pull/11347) -- chore: wait for update dialogs to disappear [#11345](https://github.com/podman-desktop/podman-desktop/pull/11345) -- chore: port forwarding should open kubernetes pod page [#11327](https://github.com/podman-desktop/podman-desktop/pull/11327) -- chore: remove pod environment column [#11324](https://github.com/podman-desktop/podman-desktop/pull/11324) -- chore: remove kubernetesListPods [#11323](https://github.com/podman-desktop/podman-desktop/pull/11323) -- chore(test): create e2e test for cancelable task [#11322](https://github.com/podman-desktop/podman-desktop/pull/11322) -- chore: open Typeahead list on focus of input box [#11319](https://github.com/podman-desktop/podman-desktop/pull/11319) -- chore: kubernetes icon component [#11317](https://github.com/podman-desktop/podman-desktop/pull/11317) -- chore: fix optional argument [#11312](https://github.com/podman-desktop/podman-desktop/pull/11312) -- chore: updated wsl check version message [#11310](https://github.com/podman-desktop/podman-desktop/pull/11310) -- chore: mount the registries.conf inside the podman VM [#11304](https://github.com/podman-desktop/podman-desktop/pull/11304) -- chore: reuse ingress route utils [#11289](https://github.com/podman-desktop/podman-desktop/pull/11289) -- chore: add a method to check if playbook is a command supported on podman CLI [#11280](https://github.com/podman-desktop/podman-desktop/pull/11280) -- chore(test): kubernetes deployment resource e2e test [#11256](https://github.com/podman-desktop/podman-desktop/pull/11256) -- chore: add colors to ProviderWidget tooltip statuses [#11234](https://github.com/podman-desktop/podman-desktop/pull/11234) -- chore: use navigation for kube resource card links [#11233](https://github.com/podman-desktop/podman-desktop/pull/11233) -- chore: fix navigateToRoute args [#11232](https://github.com/podman-desktop/podman-desktop/pull/11232) -- chore: common kubernetes name column [#11230](https://github.com/podman-desktop/podman-desktop/pull/11230) -- chore(test): e2e test for untagged image pruning [#11229](https://github.com/podman-desktop/podman-desktop/pull/11229) -- chore: remove kubernetes from internal navigation api [#11222](https://github.com/podman-desktop/podman-desktop/pull/11222) -- chore(test): extract kubernetes operations to a dedicated utility file [#11221](https://github.com/podman-desktop/podman-desktop/pull/11221) -- chore: common kubernetes ui objects [#11220](https://github.com/podman-desktop/podman-desktop/pull/11220) -- chore: added colors for ai lab tags [#11213](https://github.com/podman-desktop/podman-desktop/pull/11213) -- chore(tests): Port forwarding smoke tests [#11209](https://github.com/podman-desktop/podman-desktop/pull/11209) -- chore(tests): added kube resources table mapping [#11208](https://github.com/podman-desktop/podman-desktop/pull/11208) -- chore: update delete Kind, kubectl, and Compose executables to use process.exec [#11199](https://github.com/podman-desktop/podman-desktop/pull/11199) -- chore: add class prop to tip slot in tooltip [#11195](https://github.com/podman-desktop/podman-desktop/pull/11195) -- chore: update ProviderWidget tooltips [#11194](https://github.com/podman-desktop/podman-desktop/pull/11194) -- chore: update mermaid js component [#11188](https://github.com/podman-desktop/podman-desktop/pull/11188) -- chore: fix transitive dependency on esbuild [#11185](https://github.com/podman-desktop/podman-desktop/pull/11185) -- chore: fix cve on esbuild [#11184](https://github.com/podman-desktop/podman-desktop/pull/11184) -- chore: ensure the update of machine / listing podman machines is sequential [#11179](https://github.com/podman-desktop/podman-desktop/pull/11179) -- chore: add telemetry to kubernetes dashboard cards [#11167](https://github.com/podman-desktop/podman-desktop/pull/11167) -- chore: fix CVE issue in katex [#11161](https://github.com/podman-desktop/podman-desktop/pull/11161) -- chore: fix CVE issue [#11160](https://github.com/podman-desktop/podman-desktop/pull/11160) -- chore: ignore expired certificate for CI test when installing podman 5 [#11150](https://github.com/podman-desktop/podman-desktop/pull/11150) -- chore: update unidici from 6.21.0 to 6.21.1 [#11148](https://github.com/podman-desktop/podman-desktop/pull/11148) -- chore: Update Chocolatey package to 1.16.2 [#11147](https://github.com/podman-desktop/podman-desktop/pull/11147) -- chore: install latest corepack [#11146](https://github.com/podman-desktop/podman-desktop/pull/11146) -- chore: remove commented code [#11144](https://github.com/podman-desktop/podman-desktop/pull/11144) -- chore(test): handle different heading from navbar name [#11137](https://github.com/podman-desktop/podman-desktop/pull/11137) -- chore: increase size of checkboxes [#11135](https://github.com/podman-desktop/podman-desktop/pull/11135) -- chore(test): add e2e test for terminal access validation [#11132](https://github.com/podman-desktop/podman-desktop/pull/11132) -- chore: remove @typescript-eslint/no-explicit-any usage [#11131](https://github.com/podman-desktop/podman-desktop/pull/11131) -- chore: remove kubernetes pods from pods page [#11125](https://github.com/podman-desktop/podman-desktop/pull/11125) -- chore: add a rule to clean prettier cache [#11101](https://github.com/podman-desktop/podman-desktop/pull/11101) -- chore: update podman to v5.4.0 [#11092](https://github.com/podman-desktop/podman-desktop/pull/11092) -- chore: remove no-explicit-any from registries extension [#11090](https://github.com/podman-desktop/podman-desktop/pull/11090) -- chore: bump storybook to tailwindcss v4 [#11059](https://github.com/podman-desktop/podman-desktop/pull/11059) -- chore: use nullish coalescing operator [#11056](https://github.com/podman-desktop/podman-desktop/pull/11056) -- chore: group dependabot updates [#11055](https://github.com/podman-desktop/podman-desktop/pull/11055) -- chore: use nullish coalescing operator [#11041](https://github.com/podman-desktop/podman-desktop/pull/11041) -- chore: remove @typescript-eslint/no-explicit-any usage in markdown [#11038](https://github.com/podman-desktop/podman-desktop/pull/11038) -- chore: remove use of any in extension.ts [#11017](https://github.com/podman-desktop/podman-desktop/pull/11017) -- chore(test): skip test on mac until fix is found [#11015](https://github.com/podman-desktop/podman-desktop/pull/11015) -- chore: remove use of any in registry-setup.ts [#11012](https://github.com/podman-desktop/podman-desktop/pull/11012) -- chore: remove unnecessary no-explicit-any [#11011](https://github.com/podman-desktop/podman-desktop/pull/11011) -- chore: remove use of any in podman-info-helper.spec.ts [#11010](https://github.com/podman-desktop/podman-desktop/pull/11010) -- chore: remove use of any in podman-configuration.ts [#11008](https://github.com/podman-desktop/podman-desktop/pull/11008) -- chore: add summary page cronjobs [#11001](https://github.com/podman-desktop/podman-desktop/pull/11001) -- chore: remove @typescript-eslint/no-explicit-any usage [#10999](https://github.com/podman-desktop/podman-desktop/pull/10999) -- chore: add a compare function prop for resultItems sorting in Typeahead [#10996](https://github.com/podman-desktop/podman-desktop/pull/10996) -- chore: remove use of any [#10995](https://github.com/podman-desktop/podman-desktop/pull/10995) -- chore: remove unnecessary no-explicit-any [#10994](https://github.com/podman-desktop/podman-desktop/pull/10994) -- chore: remove @typescript-eslint/no-explicit-any usage [#10991](https://github.com/podman-desktop/podman-desktop/pull/10991) -- chore: remove @typescript-eslint/no-explicit-any usage [#10989](https://github.com/podman-desktop/podman-desktop/pull/10989) -- chore: remove @typescript-eslint/no-explicit-any usage in renderer [#10986](https://github.com/podman-desktop/podman-desktop/pull/10986) -- chore: remove @typescript-eslint/no-explicit-any usage from image [#10985](https://github.com/podman-desktop/podman-desktop/pull/10985) -- chore: remove @typescript-eslint/no-explicit-any usage from image [#10983](https://github.com/podman-desktop/podman-desktop/pull/10983) -- chore: remove @typescript-eslint/no-explicit-any usage from featured, feedback, and kube [#10981](https://github.com/podman-desktop/podman-desktop/pull/10981) -- chore: remove @typescript-eslint/no-explicit-any usage [#10977](https://github.com/podman-desktop/podman-desktop/pull/10977) -- chore: remove @typescript-eslint/no-explicit-any usage [#10976](https://github.com/podman-desktop/podman-desktop/pull/10976) -- chore: Update Chocolatey package to 1.16.1 [#10974](https://github.com/podman-desktop/podman-desktop/pull/10974) -- chore(test): make locator exact [#10971](https://github.com/podman-desktop/podman-desktop/pull/10971) -- chore: remove @typescript-eslint/no-explicit-any usage [#10969](https://github.com/podman-desktop/podman-desktop/pull/10969) -- chore(test): validate that dialog disappears after dismissal [#10967](https://github.com/podman-desktop/podman-desktop/pull/10967) -- chore: remove @typescript-eslint/no-explicit-any usage [#10965](https://github.com/podman-desktop/podman-desktop/pull/10965) -- chore: update release notes banner to not show for first time users of PD [#10959](https://github.com/podman-desktop/podman-desktop/pull/10959) -- chore: add cronjobs store functions [#10956](https://github.com/podman-desktop/podman-desktop/pull/10956) -- chore: cronjob use native functions rather than custom API call [#10955](https://github.com/podman-desktop/podman-desktop/pull/10955) -- chore: remove @typescript-eslint/no-explicit-any usage [#10948](https://github.com/podman-desktop/podman-desktop/pull/10948) -- chore: remove @typescript-eslint/no-explicit-any usage [#10947](https://github.com/podman-desktop/podman-desktop/pull/10947) -- chore: remove @typescript-eslint/no-explicit-any usage [#10946](https://github.com/podman-desktop/podman-desktop/pull/10946) -- chore: remove @typescript-eslint/no-explicit-any usage [#10944](https://github.com/podman-desktop/podman-desktop/pull/10944) -- chore: replace any by unknown type in onboarding API [#10941](https://github.com/podman-desktop/podman-desktop/pull/10941) -- chore: remove gradient styling from tailwindcss [#10909](https://github.com/podman-desktop/podman-desktop/pull/10909) -- chore: svelte 5 kubernetes columns [#10906](https://github.com/podman-desktop/podman-desktop/pull/10906) -- chore: kubernetes navigation store mocking [#10904](https://github.com/podman-desktop/podman-desktop/pull/10904) -- chore: set tailwind framework as being the 'winner of the CSS' [#10901](https://github.com/podman-desktop/podman-desktop/pull/10901) -- chore: postinstall should only install chromium [#10888](https://github.com/podman-desktop/podman-desktop/pull/10888) -- chore: switch to Tailwind CSS v4.0.1 [#10886](https://github.com/podman-desktop/podman-desktop/pull/10886) -- chore: fix scripts command [#10885](https://github.com/podman-desktop/podman-desktop/pull/10885) -- chore: use podman desktop colors rather than theme colors [#10883](https://github.com/podman-desktop/podman-desktop/pull/10883) -- chore(test,ci): add option to install all extensions before update [#10882](https://github.com/podman-desktop/podman-desktop/pull/10882) -- chore: declare isomorphic-ws as external for vite [#10869](https://github.com/podman-desktop/podman-desktop/pull/10869) -- chore(test): extend timeouts in Kubernetes tests [#10860](https://github.com/podman-desktop/podman-desktop/pull/10860) -- chore: Update Chocolatey package to 1.16.0 [#10859](https://github.com/podman-desktop/podman-desktop/pull/10859) - -### Documentation - -- docs(website): updated the custom extension installation procedure [#11385](https://github.com/podman-desktop/podman-desktop/pull/11385) -- docs(website): add SELinux info to GPU guide [#11380](https://github.com/podman-desktop/podman-desktop/pull/11380) -- docs(website): updated documenation for hyper-v support on windows [#11309](https://github.com/podman-desktop/podman-desktop/pull/11309) -- docs(website): added a tutorial to create an extension [#11253](https://github.com/podman-desktop/podman-desktop/pull/11253) -- docs: updated MAINTAINERS.md file with new user [#11164](https://github.com/podman-desktop/podman-desktop/pull/11164) -- docs(website): edited a troubleshooting section [#11053](https://github.com/podman-desktop/podman-desktop/pull/11053) -- docs(website): added the customizing docker compatibility procedure [#11033](https://github.com/podman-desktop/podman-desktop/pull/11033) -- docs(website): added a troubleshooting section [#10942](https://github.com/podman-desktop/podman-desktop/pull/10942) -- docs(website): added experimental settings details to the discover PD… [#10938](https://github.com/podman-desktop/podman-desktop/pull/10938) -- docs(website): removes periods from headers [#10915](https://github.com/podman-desktop/podman-desktop/pull/10915) -- docs(website): highlighted the search functionality in the container … [#10895](https://github.com/podman-desktop/podman-desktop/pull/10895) -- docs(website): edits the creating a kind cluster procedure [#10874](https://github.com/podman-desktop/podman-desktop/pull/10874) -- docs: Add missing -o flag in `Importing saved containers to Podman` [#10863](https://github.com/podman-desktop/podman-desktop/pull/10863) - -### Dependencies - -- chore(deps-dev): bump electron from 34.2.0 to 34.3.0 [#11415](https://github.com/podman-desktop/podman-desktop/pull/11415) -- chore(deps-dev): bump vite from 6.1.0 to 6.2.0 [#11376](https://github.com/podman-desktop/podman-desktop/pull/11376) -- chore(deps-dev): bump svelte-eslint-parser from 0.43.0 to 1.0.0 [#11374](https://github.com/podman-desktop/podman-desktop/pull/11374) -- chore(deps-dev): bump the tailwindcss group across 1 directory with 3 updates [#11372](https://github.com/podman-desktop/podman-desktop/pull/11372) -- chore(deps-dev): bump typedoc from 0.27.8 to 0.27.9 [#11332](https://github.com/podman-desktop/podman-desktop/pull/11332) -- chore(deps-dev): bump svelte from 5.20.2 to 5.20.4 [#11331](https://github.com/podman-desktop/podman-desktop/pull/11331) -- chore(deps-dev): bump msw from 2.7.1 to 2.7.3 [#11330](https://github.com/podman-desktop/podman-desktop/pull/11330) -- chore(deps-dev): bump typedoc from 0.27.7 to 0.27.8 [#11302](https://github.com/podman-desktop/podman-desktop/pull/11302) -- chore(deps-dev): bump prettier from 3.5.1 to 3.5.2 [#11301](https://github.com/podman-desktop/podman-desktop/pull/11301) -- chore(deps): bump the eslint group with 4 updates [#11299](https://github.com/podman-desktop/podman-desktop/pull/11299) -- chore(deps-dev): bump eslint-import-resolver-typescript from 3.8.2 to 3.8.3 [#11292](https://github.com/podman-desktop/podman-desktop/pull/11292) -- chore(deps-dev): bump msw from 2.7.0 to 2.7.1 [#11291](https://github.com/podman-desktop/podman-desktop/pull/11291) -- chore(deps-dev): bump globals from 15.15.0 to 16.0.0 [#11290](https://github.com/podman-desktop/podman-desktop/pull/11290) -- chore(deps-dev): bump eslint-import-resolver-typescript from 3.8.1 to 3.8.2 [#11246](https://github.com/podman-desktop/podman-desktop/pull/11246) -- chore(deps-dev): bump postcss from 8.5.2 to 8.5.3 [#11244](https://github.com/podman-desktop/podman-desktop/pull/11244) -- chore(deps-dev): bump tsx from 4.19.2 to 4.19.3 [#11243](https://github.com/podman-desktop/podman-desktop/pull/11243) -- chore(deps-dev): bump svelte from 5.20.1 to 5.20.2 [#11242](https://github.com/podman-desktop/podman-desktop/pull/11242) -- chore(deps-dev): bump the vitest group with 2 updates [#11241](https://github.com/podman-desktop/podman-desktop/pull/11241) -- chore(deps-dev): bump the tailwindcss group with 3 updates [#11240](https://github.com/podman-desktop/podman-desktop/pull/11240) -- chore(deps-dev): bump the storybook group with 7 updates [#11239](https://github.com/podman-desktop/podman-desktop/pull/11239) -- chore(deps-dev): bump eslint-import-resolver-typescript from 3.8.0 to 3.8.1 [#11202](https://github.com/podman-desktop/podman-desktop/pull/11202) -- chore(deps-dev): bump eslint-plugin-unicorn from 56.0.1 to 57.0.0 [#11201](https://github.com/podman-desktop/podman-desktop/pull/11201) -- chore(deps-dev): bump the typescript-eslint group with 3 updates [#11200](https://github.com/podman-desktop/podman-desktop/pull/11200) -- chore(deps-dev): bump eslint-import-resolver-typescript from 3.7.0 to 3.8.0 [#11175](https://github.com/podman-desktop/podman-desktop/pull/11175) -- chore(deps): bump @octokit/rest from 21.1.0 to 21.1.1 [#11174](https://github.com/podman-desktop/podman-desktop/pull/11174) -- chore(deps-dev): bump svelte from 5.20.0 to 5.20.1 [#11173](https://github.com/podman-desktop/podman-desktop/pull/11173) -- chore(deps): bump @octokit/graphql from 8.2.0 to 8.2.1 [#11172](https://github.com/podman-desktop/podman-desktop/pull/11172) -- chore(deps-dev): bump octokit from 4.1.1 to 4.1.2 [#11171](https://github.com/podman-desktop/podman-desktop/pull/11171) -- chore(deps-dev): bump the storybook group with 7 updates [#11170](https://github.com/podman-desktop/podman-desktop/pull/11170) -- chore(deps-dev): bump eslint-plugin-sonarjs from 3.0.1 to 3.0.2 [#11143](https://github.com/podman-desktop/podman-desktop/pull/11143) -- chore(deps-dev): bump electron from 34.1.1 to 34.2.0 [#11142](https://github.com/podman-desktop/podman-desktop/pull/11142) -- chore(deps-dev): bump @types/node from 20.17.17 to 20.17.19 [#11141](https://github.com/podman-desktop/podman-desktop/pull/11141) -- chore(deps-dev): bump prettier from 3.5.0 to 3.5.1 [#11140](https://github.com/podman-desktop/podman-desktop/pull/11140) -- chore(deps-dev): bump globals from 15.14.0 to 15.15.0 [#11129](https://github.com/podman-desktop/podman-desktop/pull/11129) -- chore(deps-dev): bump svelte from 5.19.10 to 5.20.0 [#11128](https://github.com/podman-desktop/podman-desktop/pull/11128) -- chore(deps-dev): bump @vitest/eslint-plugin from 1.1.28 to 1.1.31 in the vitest group [#11127](https://github.com/podman-desktop/podman-desktop/pull/11127) -- chore(deps-dev): bump the storybook group with 7 updates [#11126](https://github.com/podman-desktop/podman-desktop/pull/11126) -- chore(deps-dev): bump @testing-library/svelte from 5.2.6 to 5.2.7 [#11097](https://github.com/podman-desktop/podman-desktop/pull/11097) -- chore(deps-dev): bump svelte from 5.19.9 to 5.19.10 [#11096](https://github.com/podman-desktop/podman-desktop/pull/11096) -- chore(deps): bump eslint from 9.20.0 to 9.20.1 in the eslint group [#11095](https://github.com/podman-desktop/podman-desktop/pull/11095) -- chore(deps-dev): bump @vitest/coverage-v8 from 2.1.6 to 3.0.5 in the vitest group [#11086](https://github.com/podman-desktop/podman-desktop/pull/11086) -- chore(deps-dev): bump tailwindcss from 4.0.3 to 4.0.6 in the tailwindcss group [#11085](https://github.com/podman-desktop/podman-desktop/pull/11085) -- chore(deps-dev): bump the storybook group with 7 updates [#11084](https://github.com/podman-desktop/podman-desktop/pull/11084) -- chore(deps-dev): bump postcss from 8.5.1 to 8.5.2 [#11076](https://github.com/podman-desktop/podman-desktop/pull/11076) -- chore(deps-dev): bump @vitest/eslint-plugin from 1.1.27 to 1.1.28 [#11074](https://github.com/podman-desktop/podman-desktop/pull/11074) -- chore(deps-dev): bump @tailwindcss/vite from 4.0.4 to 4.0.6 [#11073](https://github.com/podman-desktop/podman-desktop/pull/11073) -- chore(deps-dev): bump @tailwindcss/postcss from 4.0.4 to 4.0.6 [#11072](https://github.com/podman-desktop/podman-desktop/pull/11072) -- chore(deps-dev): bump the typescript-eslint group with 3 updates [#11071](https://github.com/podman-desktop/podman-desktop/pull/11071) -- chore(deps-dev): bump typedoc from 0.27.6 to 0.27.7 [#11051](https://github.com/podman-desktop/podman-desktop/pull/11051) -- chore(deps-dev): bump @vitest/eslint-plugin from 1.1.25 to 1.1.27 [#11048](https://github.com/podman-desktop/podman-desktop/pull/11048) -- chore(deps): bump eslint from 9.19.0 to 9.20.0 [#11046](https://github.com/podman-desktop/podman-desktop/pull/11046) -- chore(deps-dev): bump prettier from 3.4.2 to 3.5.0 [#11045](https://github.com/podman-desktop/podman-desktop/pull/11045) -- chore(deps-dev): bump typedoc-plugin-markdown from 4.4.1 to 4.4.2 [#11044](https://github.com/podman-desktop/podman-desktop/pull/11044) -- chore(deps-dev): bump @eslint/js from 9.19.0 to 9.20.0 [#11043](https://github.com/podman-desktop/podman-desktop/pull/11043) -- chore(deps-dev): bump svelte from 5.19.8 to 5.19.9 [#11031](https://github.com/podman-desktop/podman-desktop/pull/11031) -- chore(deps): bump electron-context-menu from 4.0.4 to 4.0.5 [#11030](https://github.com/podman-desktop/podman-desktop/pull/11030) -- chore(deps-dev): bump @tailwindcss/vite from 4.0.3 to 4.0.4 [#11028](https://github.com/podman-desktop/podman-desktop/pull/11028) -- chore(deps-dev): bump octokit from 4.1.0 to 4.1.1 [#11027](https://github.com/podman-desktop/podman-desktop/pull/11027) -- chore(deps-dev): bump electron from 34.1.0 to 34.1.1 [#11026](https://github.com/podman-desktop/podman-desktop/pull/11026) -- chore(deps-dev): bump @tailwindcss/postcss from 4.0.3 to 4.0.4 [#11024](https://github.com/podman-desktop/podman-desktop/pull/11024) -- chore(deps-dev): bump vitest from 2.1.9 to 3.0.5 [#11023](https://github.com/podman-desktop/podman-desktop/pull/11023) -- chore(deps-dev): bump vitest from 2.1.6 to 2.1.9 [#11007](https://github.com/podman-desktop/podman-desktop/pull/11007) -- chore(deps-dev): bump svelte from 5.19.7 to 5.19.8 [#11005](https://github.com/podman-desktop/podman-desktop/pull/11005) -- chore(deps-dev): bump vite from 6.0.11 to 6.1.0 [#11004](https://github.com/podman-desktop/podman-desktop/pull/11004) -- chore(deps-dev): bump electron from 34.0.2 to 34.1.0 [#11003](https://github.com/podman-desktop/podman-desktop/pull/11003) -- chore(deps-dev): bump svelte from 5.19.6 to 5.19.7 [#10963](https://github.com/podman-desktop/podman-desktop/pull/10963) -- chore(deps-dev): bump @sveltejs/package from 2.3.9 to 2.3.10 [#10962](https://github.com/podman-desktop/podman-desktop/pull/10962) -- chore(deps-dev): bump @types/node from 20.17.16 to 20.17.17 [#10961](https://github.com/podman-desktop/podman-desktop/pull/10961) -- chore(deps): bump semver from 7.7.0 to 7.7.1 [#10960](https://github.com/podman-desktop/podman-desktop/pull/10960) -- chore(deps-dev): bump the typescript-eslint group across 1 directory with 3 updates [#10958](https://github.com/podman-desktop/podman-desktop/pull/10958) -- chore(deps): bump @octokit/graphql from 8.1.2 to 8.2.0 [#10936](https://github.com/podman-desktop/podman-desktop/pull/10936) -- chore(deps-dev): bump @commitlint/cli from 19.6.1 to 19.7.1 [#10935](https://github.com/podman-desktop/podman-desktop/pull/10935) -- chore(deps): bump docusaurus-plugin-typedoc from 1.2.2 to 1.2.3 [#10934](https://github.com/podman-desktop/podman-desktop/pull/10934) -- chore(deps-dev): bump @playwright/test from 1.50.0 to 1.50.1 [#10933](https://github.com/podman-desktop/podman-desktop/pull/10933) -- chore(deps-dev): bump @commitlint/config-conventional from 19.6.0 to 19.7.1 [#10932](https://github.com/podman-desktop/podman-desktop/pull/10932) -- chore(deps-dev): bump @eslint/compat from 1.2.5 to 1.2.6 [#10931](https://github.com/podman-desktop/podman-desktop/pull/10931) -- chore(deps-dev): bump the storybook group with 7 updates [#10930](https://github.com/podman-desktop/podman-desktop/pull/10930) -- chore(deps-dev): bump svelte from 5.19.4 to 5.19.6 [#10911](https://github.com/podman-desktop/podman-desktop/pull/10911) -- chore(deps): bump semver from 7.6.3 to 7.7.0 [#10890](https://github.com/podman-desktop/podman-desktop/pull/10890) -- chore(deps-dev): bump electron from 34.0.1 to 34.0.2 [#10889](https://github.com/podman-desktop/podman-desktop/pull/10889) -- chore(deps-dev): bump svelte from 5.19.3 to 5.19.4 [#10873](https://github.com/podman-desktop/podman-desktop/pull/10873) -- chore(deps-dev): bump the storybook group with 7 updates [#10854](https://github.com/podman-desktop/podman-desktop/pull/10854) diff --git a/website/blog/2025-03-06-mirror-registry-configuration-blog.md b/website/blog/2025-03-06-mirror-registry-configuration-blog.md deleted file mode 100644 index 7ce5c376f9..0000000000 --- a/website/blog/2025-03-06-mirror-registry-configuration-blog.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Configure a mirror registry -description: Covers the end-to-end workflow to configure a mirror registry -authors: [shipsing] -tags: [podman-desktop, podman, configure-a-mirror-registry] -hide_table_of_contents: false ---- - -# Configure a mirror registry with Podman Desktop - -Lower rate limits imposed on the Docker Hub End-User License Agreement (EULA) made it difficult for community members to access the `docker.io` images frequently. A simple solution to this problem is to configure a mirror for the `docker.io` registry in the `registries.conf` file. This way, they can access the Docker resources whenever needed. - -With the Podman Desktop UI, you can add registry locations and configure their mirrors easily. Configuring a mirror redirects a registry to another location, enabling you to use its content. For example, if you have configured `ghcr.io` as a mirror for `docker.io` and try to pull a resource, Podman pulls the resource from `ghcr.io` instead of `docker.io`. - -The blog covers how to configure a mirror for the `docker.io` registry. - -## Prerequisites - -- Upgrade to the latest version of Podman. -- [Recreate your Podman machine](/docs/podman/creating-a-podman-machine). Otherwise, you get a notification when you set up your registry configuration: - ![notification to mount the registry file](img/mirror-registry-configuration/notification.png) - -## Configuring: A mirror for `docker.io` - -1. Go to the **Settings > Resources** page. -1. Select **More Options > Setup registry configuration** in the Podman tile. A command palette opens. - ![Set up registry configuration](img/mirror-registry-configuration/setting-up-registry-configuration.png) -1. Set up your registry configuration: -1. Select the **Add registry configuration** option from the command palette. - ![adding registry configuration](img/mirror-registry-configuration/add-registry-configuration.png) -1. Type the location of the registry, such as `docker.io`, and press the `Enter` key. The `docker.io` registry is added to the palette. - ![docker.io registry added](img/mirror-registry-configuration/docker-option-added.png) -1. Select `docker.io` from the command palette. -1. Type the location where you want to mirror the registry, such as `ghcr.io`, and press the `Enter` key. The entry for the `docker.io` registry shows the location where it is mirrored. - ![mirrored registry location](img/mirror-registry-configuration/mirrored-registry.png) -1. Select the `End configuring registries` option to end registry configuration. - ![ending registry configuration](img/mirror-registry-configuration/end-configuring-registries.png) - -## Verifying: the mirror - -There is no direct way to verify the mirror configuration from the UI. But, you can use the CLI for verification. - -1. Start an interactive session with the default Podman machine: - -```sh -$ podman machine ssh -``` - -2. Pull an invalid image from `docker.io`. - -```sh -$ podman pull docker.io/invalid -``` - -An error message appears in the terminal indicating a failure to pull the image from the mirror. You receive the error message: `Mirrors have also failed`. diff --git a/website/blog/2025-04-14-release-1.18.md b/website/blog/2025-04-14-release-1.18.md deleted file mode 100644 index 776c0d3b37..0000000000 --- a/website/blog/2025-04-14-release-1.18.md +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: Podman Desktop 1.18 Release -description: Podman Desktop 1.18 has been released! -slug: podman-desktop-release-1.18 -authors: SoniaSandler -tags: [podman-desktop, release, podman] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.18/banner.png ---- - -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.18 Release! 🎉 - -![podman-desktop-hero-1.18](/img/blog/podman-desktop-release-1.18/banner.png) - -Podman Desktop 1.18 is now available! [Click here to download it](/downloads)! - -This release brings exciting new features and improvements: - -- **Refining the Docker Compatibility feature**: The Docker Compatibility page has been moved out of experimental mode, and now it helps you fully set up Podman even when installed through `brew`. It also detects and notifies you when Podman can't bind the Docker socket. -- **Switching Kubernetes namespace**: Switching between Kubernetes namespaces within Podman Desktop is now available. -- **Jobs in Kubernetes**: You can now see Kubernetes jobs in our growing Kubernetes section of Podman Desktop. -- **Experimental status bar providers enhancements**: Status bar providers can be pinned and unpinned based on user preferences and the provider status icons have been updated to be easier to understand. - ---- - -## Release Details - -### Docker Compatibilty mode page is out of experimental mode 🎉 - -The experimental Docker Compatibility page that was introduced in [v1.13](https://podman-desktop.io/blog/podman-desktop-release-1.13#docker-compatibility-mode-page) has been moved out of experimental mode. Now everything related to Docker compatibility can be found in one place. In addition, new Docker compatibility related notifications have been added to make the set up process easier and less guesswork when running into problems. - - - - - -### Switching Kubernetes namepaces - -Until now, only switching Kubernetes contexts was supported in Podman Desktop. With this update, you can now switch between namespaces from within Podman Desktop without having to do anything outside the app. The selection does not modify your Kube config and is persistent across the app, so if you select a specific namespace in one place, it will be used everywhere else in Podman Desktop. - - - -### Kubernetes Jobs - -As part of our ongoing Kubernetes improvement and enhancement efforts, a new tab has been added for Kubernetes Jobs. You can now view the jobs, all in one place, with details page for each one, and have the ability to delete and view condition statuses. - - - - - -### Experimental status bar providers enhancements - -The experimental status bar providers that were introduced in [v1.16](https://podman-desktop.io/blog/podman-desktop-release-1.16#providers-appear-in-the-status-bar) have undergone some changes to make them easier to understand and use. - -The new provider status icons include: - -- white dot - running -- red dot - error -- up arrow - update available -- spinning circle - stopping or starting -- darker icon - stopped - -In addition, you can now choose which available providers to show by pinning or unpinning them from the status bar: - - - -If you have any feedback or thoughts about our experimental features (or any aspect of Podman Desktop at all), feel free to let us know in the relevant [experimental feature github discussion](https://github.com/podman-desktop/podman-desktop/discussions/categories/experimental-features), which can be accessed from the experimental tab in the settings page, or by clicking on the comment icon in the lower right side of Podman Desktop and filling out the feedback form. - -### Additional changes - -:::warning[Breaking change] - -As part of the migration to svelte 5, breaking changes were introduced in the `DetailsPage` and `FormPage` components of the `@podman-desktop/ui-svelte` package v1.18.0. - -Please use `{#snippet ()}` instead of slots in those components -::: - -## Community Thank You - -🎉 We’d like to say a big thank you to everyone who helped to make 🦭 Podman Desktop even better. In this -release we received pull requests from the following new people: - -- [@j-bs](https://github.com/j-bs) made their first contribution in [#11456](https://github.com/podman-desktop/podman-desktop/pull/11456) docs(website): use consistent order in OS tab groups -- [@chkpnt](https://github.com/chkpnt) made their first contribution in [#10975](https://github.com/podman-desktop/podman-desktop/pull/10975) fix: handling of proxy bypass values from system under macOS -- [@AlexonOliveiraRH](https://github.com/AlexonOliveiraRH) made their first contribution in [#11710](https://github.com/podman-desktop/podman-desktop/pull/11710) docs: adding an instruction to enable ip_tables -- [@Virakal](https://github.com/Virakal) made their first contribution in [#11792](https://github.com/podman-desktop/podman-desktop/pull/11792) fix(docs): docker export syntax -- [@fabienengels](https://github.com/fabienengels) made their first contribution in [#12106](https://github.com/podman-desktop/podman-desktop/pull/12106) docs: add EOST as adopters - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.18.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. - -## Detailed release changelog - -### Chores ✅ - -- chore: update release procedure by @benoitf [#12192](https://github.com/podman-desktop/podman-desktop/pull/12192) -- chore: add error indication to statusbar provider by @SoniaSandler [#12191](https://github.com/podman-desktop/podman-desktop/pull/12191) -- chore: exclude one rule for CLOMonitor by @benoitf [#12169](https://github.com/podman-desktop/podman-desktop/pull/12169) -- chore: add badges for CNCF tooling by @benoitf [#12158](https://github.com/podman-desktop/podman-desktop/pull/12158) -- chore: add scorecard by @benoitf [#12147](https://github.com/podman-desktop/podman-desktop/pull/12147) -- chore: website - update footer cncf logo and spacing by @cdrage [#12142](https://github.com/podman-desktop/podman-desktop/pull/12142) -- chore: bump lint memory by @deboer-tim [#12140](https://github.com/podman-desktop/podman-desktop/pull/12140) -- chore: add security insights file by @benoitf [#12138](https://github.com/podman-desktop/podman-desktop/pull/12138) -- chore: website - update font weight and div width by @cdrage [#12117](https://github.com/podman-desktop/podman-desktop/pull/12117) -- chore: website - update download links by @cdrage [#12115](https://github.com/podman-desktop/podman-desktop/pull/12115) -- chore: improve provider widget icons by @SoniaSandler [#12112](https://github.com/podman-desktop/podman-desktop/pull/12112) -- chore(vitest): remove unused files by @axel7083 [#12110](https://github.com/podman-desktop/podman-desktop/pull/12110) -- chore: allow more permissive data in test by @benoitf [#12100](https://github.com/podman-desktop/podman-desktop/pull/12100) -- chore: add missing dispose method in TerminalWindow by @benoitf [#12099](https://github.com/podman-desktop/podman-desktop/pull/12099) -- chore: update estree-util-value-to-estree to v3.3.3 by @benoitf [#12085](https://github.com/podman-desktop/podman-desktop/pull/12085) -- chore: update image-size to 1.2.1 by @benoitf [#12084](https://github.com/podman-desktop/podman-desktop/pull/12084) -- chore(package.json): build should include build:preload:types by @axel7083 [#12059](https://github.com/podman-desktop/podman-desktop/pull/12059) -- chore: Use isUnixLike() instead of isLinux() in some places by @arrowd [#12047](https://github.com/podman-desktop/podman-desktop/pull/12047) -- chore: migrate close button to svelte 5 by @deboer-tim [#12043](https://github.com/podman-desktop/podman-desktop/pull/12043) -- chore: type FetchError removed from library by @feloy [#12042](https://github.com/podman-desktop/podman-desktop/pull/12042) -- chore: enable svelte/require-each-key rule by @SoniaSandler [#12041](https://github.com/podman-desktop/podman-desktop/pull/12041) -- chore: update to podman 5.4.2 by @feloy [#12006](https://github.com/podman-desktop/podman-desktop/pull/12006) -- chore(website): modified community meeting link in the banner and docs by @rujutashinde [#11996](https://github.com/podman-desktop/podman-desktop/pull/11996) -- chore: expose VmProviderConnection and VmProviderConnectionFactory in extension-api by @SoniaSandler [#11982](https://github.com/podman-desktop/podman-desktop/pull/11982) -- chore(dockerode): bump to 3.3.37 by @axel7083 [#11979](https://github.com/podman-desktop/podman-desktop/pull/11979) -- chore: support for changing namespace in experimental mode by @deboer-tim [#11964](https://github.com/podman-desktop/podman-desktop/pull/11964) -- chore: migrate status icon to svelte 5 by @deboer-tim [#11961](https://github.com/podman-desktop/podman-desktop/pull/11961) -- chore: add aria roles and labels into troubleshooting pages by @odockal [#11960](https://github.com/podman-desktop/podman-desktop/pull/11960) -- chore(deps-dev): update eslint-plugin-svelte to v3.5.0 by @benoitf [#11959](https://github.com/podman-desktop/podman-desktop/pull/11959) -- chore: use lcov reporter locally by @feloy [#11951](https://github.com/podman-desktop/podman-desktop/pull/11951) -- chore: retry up to 3 times a failed unit test by @benoitf [#11947](https://github.com/podman-desktop/podman-desktop/pull/11947) -- chore: added dialog for showing manual installation steps on windows by @gastoner [#11946](https://github.com/podman-desktop/podman-desktop/pull/11946) -- chore: add update info to Kind provider by @SoniaSandler [#11936](https://github.com/podman-desktop/podman-desktop/pull/11936) -- chore: migrate provider button onclick by @deboer-tim [#11934](https://github.com/podman-desktop/podman-desktop/pull/11934) -- chore: migrate modal slot to snippet by @deboer-tim [#11932](https://github.com/podman-desktop/podman-desktop/pull/11932) -- chore: migrate tab to svelte 5 by @deboer-tim [#11929](https://github.com/podman-desktop/podman-desktop/pull/11929) -- chore: replace lcov by json output for coverage by @benoitf [#11902](https://github.com/podman-desktop/podman-desktop/pull/11902) -- chore: props interface by @deboer-tim [#11901](https://github.com/podman-desktop/podman-desktop/pull/11901) -- chore: add alert for mac podman socket failures by @cdrage [#11892](https://github.com/podman-desktop/podman-desktop/pull/11892) -- chore: add setup podman-mac-helper to dashboard by @cdrage [#11890](https://github.com/podman-desktop/podman-desktop/pull/11890) -- chore: troubleshooting page improvements - aria by @odockal [#11888](https://github.com/podman-desktop/podman-desktop/pull/11888) -- chore: remove @typescript-eslint/no-explicit-any usage by @jeffmaury [#11883](https://github.com/podman-desktop/podman-desktop/pull/11883) -- chore(refactor): migrate Modal component to Svelte5 by @jeffmaury [#11881](https://github.com/podman-desktop/podman-desktop/pull/11881) -- chore: update ui package icons to svelte 5 by @deboer-tim [#11869](https://github.com/podman-desktop/podman-desktop/pull/11869) -- chore(task-popup): added disapearing task popups by @gastoner [#11867](https://github.com/podman-desktop/podman-desktop/pull/11867) -- chore: update ErrorMessage component to svelte 5 by @SoniaSandler [#11866](https://github.com/podman-desktop/podman-desktop/pull/11866) -- chore: remove old docker compatibility code by @cdrage [#11861](https://github.com/podman-desktop/podman-desktop/pull/11861) -- chore: remove traces by @benoitf [#11853](https://github.com/podman-desktop/podman-desktop/pull/11853) -- chore(Carousel): migrate component to svelte v5 by @benoitf [#11852](https://github.com/podman-desktop/podman-desktop/pull/11852) -- chore: allow optional dock property by @benoitf [#11850](https://github.com/podman-desktop/podman-desktop/pull/11850) -- chore: enable svelte/require-each-key rule - part 5 - image by @SoniaSandler [#11840](https://github.com/podman-desktop/podman-desktop/pull/11840) -- chore(configuration-impl): remove usage of any by @benoitf [#11837](https://github.com/podman-desktop/podman-desktop/pull/11837) -- chore: make dropdown left control clickable by @deboer-tim [#11835](https://github.com/podman-desktop/podman-desktop/pull/11835) -- chore: enable svelte/require-each-key rule - part 4 by @SoniaSandler [#11834](https://github.com/podman-desktop/podman-desktop/pull/11834) -- chore: updated tsconfig and fixed typecheck by @gastoner [#11832](https://github.com/podman-desktop/podman-desktop/pull/11832) -- chore: migrated link component to svelte5 by @gastoner [#11830](https://github.com/podman-desktop/podman-desktop/pull/11830) -- chore: add left snippet to dropdown by @deboer-tim [#11810](https://github.com/podman-desktop/podman-desktop/pull/11810) -- chore(storybook): adding Table with children story by @axel7083 [#11808](https://github.com/podman-desktop/podman-desktop/pull/11808) -- chore(config-registry): remove usage of any by @benoitf [#11807](https://github.com/podman-desktop/podman-desktop/pull/11807) -- chore: enable svelte/require-each-key rule - part 3 by @SoniaSandler [#11795](https://github.com/podman-desktop/podman-desktop/pull/11795) -- chore: enable svelte/require-each-key rule - part 2 - kube and ingresses-routes by @SoniaSandler [#11794](https://github.com/podman-desktop/podman-desktop/pull/11794) -- chore: enable svelte/require-each-key rule- part 1 by @SoniaSandler [#11793](https://github.com/podman-desktop/podman-desktop/pull/11793) -- chore: fix alignment and make provider widget consistent by @deboer-tim [#11781](https://github.com/podman-desktop/podman-desktop/pull/11781) -- chore: clone kubeconfig instead of re-reading from disk by @deboer-tim [#11779](https://github.com/podman-desktop/podman-desktop/pull/11779) -- chore: api to change kubernetes namespace by @deboer-tim [#11753](https://github.com/podman-desktop/podman-desktop/pull/11753) -- chore: enable svelte/require-store-reactive-access rule by @cdrage [#11726](https://github.com/podman-desktop/podman-desktop/pull/11726) -- chore: kind extension - use configuration name when deploying ingress by @cdrage [#11722](https://github.com/podman-desktop/podman-desktop/pull/11722) -- chore: move docker compatibility out of experimental by @SoniaSandler [#11708](https://github.com/podman-desktop/podman-desktop/pull/11708) -- chore: revert #11340 by @axel7083 [#11699](https://github.com/podman-desktop/podman-desktop/pull/11699) -- chore(dependabot): grouping typedoc deps update by @axel7083 [#11692](https://github.com/podman-desktop/podman-desktop/pull/11692) -- chore: Introduce the FreeBSDInfo class into sys-info by @arrowd [#11676](https://github.com/podman-desktop/podman-desktop/pull/11676) -- chore: remove docker compatibility warning from dashboard by @cdrage [#11675](https://github.com/podman-desktop/podman-desktop/pull/11675) -- chore: prepare compatibility with kubernetes-client v1.1.0 by @feloy [#11668](https://github.com/podman-desktop/podman-desktop/pull/11668) -- chore: add jobs name for merge queue by @benoitf [#11661](https://github.com/podman-desktop/podman-desktop/pull/11661) -- chore: handle merge_group event as well for pr-check GH action by @benoitf [#11659](https://github.com/podman-desktop/podman-desktop/pull/11659) -- chore: use latest pnpm v10 by @benoitf [#11656](https://github.com/podman-desktop/podman-desktop/pull/11656) -- chore: refresh dependencies (pnpm upgrade) by @benoitf [#11655](https://github.com/podman-desktop/podman-desktop/pull/11655) -- chore: update to podman 5.4.1 by @feloy [#11653](https://github.com/podman-desktop/podman-desktop/pull/11653) -- chore: update babel dependencies from 7.26.0 to 7.26.10 by @benoitf [#11652](https://github.com/podman-desktop/podman-desktop/pull/11652) -- chore: add merge-group option for PR check by @benoitf [#11646](https://github.com/podman-desktop/podman-desktop/pull/11646) -- chore: update jsonpath-plus by @benoitf [#11644](https://github.com/podman-desktop/podman-desktop/pull/11644) -- chore: update axios by @benoitf [#11643](https://github.com/podman-desktop/podman-desktop/pull/11643) -- chore: update prismjs by @benoitf [#11642](https://github.com/podman-desktop/podman-desktop/pull/11642) -- chore: avoid error saying that we use a variable before its assignment by @benoitf [#11641](https://github.com/podman-desktop/podman-desktop/pull/11641) -- chore: enable svelte/no-immutable-reactive-statements by @feloy [#11624](https://github.com/podman-desktop/podman-desktop/pull/11624) -- chore: prefer nullish coalescing by @benoitf [#11621](https://github.com/podman-desktop/podman-desktop/pull/11621) -- chore: fix nullish coaelescing by @benoitf [#11620](https://github.com/podman-desktop/podman-desktop/pull/11620) -- chore: change the import by @benoitf [#11619](https://github.com/podman-desktop/podman-desktop/pull/11619) -- chore: change the way json files are imported by @benoitf [#11618](https://github.com/podman-desktop/podman-desktop/pull/11618) -- chore: add community meeting banner, remove AI lab banner by @cdrage [#11610](https://github.com/podman-desktop/podman-desktop/pull/11610) -- chore: format unformatted files by @benoitf [#11609](https://github.com/podman-desktop/podman-desktop/pull/11609) -- chore: fix svelte/infinite-reactive-loop by @benoitf [#11607](https://github.com/podman-desktop/podman-desktop/pull/11607) -- chore: use Node.js v22 for website by @benoitf [#11606](https://github.com/podman-desktop/podman-desktop/pull/11606) -- chore(deps-dev): bump electron from 34.3.0 to 35.0.1 and to Node.js v22 by @benoitf [#11603](https://github.com/podman-desktop/podman-desktop/pull/11603) -- chore: refactor spyOn by @benoitf [#11601](https://github.com/podman-desktop/podman-desktop/pull/11601) -- chore: fix nullish coalescing by @benoitf [#11600](https://github.com/podman-desktop/podman-desktop/pull/11600) -- chore: remove any usage by @benoitf [#11599](https://github.com/podman-desktop/podman-desktop/pull/11599) -- chore(code-guidelines): testing style attribute by @axel7083 [#11567](https://github.com/podman-desktop/podman-desktop/pull/11567) -- chore: indicate in provider widget if update is available by @SoniaSandler [#11556](https://github.com/podman-desktop/podman-desktop/pull/11556) -- chore: removed no reactive reassign eslint rule by @gastoner [#11549](https://github.com/podman-desktop/podman-desktop/pull/11549) -- chore: add eyebrows for 1.17 release banner by @cdrage [#11548](https://github.com/podman-desktop/podman-desktop/pull/11548) -- chore(settings): change navigation appearance to icon plus title by default by @axel7083 [#11544](https://github.com/podman-desktop/podman-desktop/pull/11544) -- chore: move terminal service to kubernetes by @deboer-tim [#11520](https://github.com/podman-desktop/podman-desktop/pull/11520) -- chore: clean up PodInfoUI kind by @deboer-tim [#11519](https://github.com/podman-desktop/podman-desktop/pull/11519) -- chore: remove unused kube paths from pods page by @deboer-tim [#11516](https://github.com/podman-desktop/podman-desktop/pull/11516) -- chore: remove unused pod terminal tab by @deboer-tim [#11500](https://github.com/podman-desktop/podman-desktop/pull/11500) -- chore: pods name column cleanup by @deboer-tim [#11499](https://github.com/podman-desktop/podman-desktop/pull/11499) -- chore(deps-dev): bump eslint-plugin-svelte from 2.46.1 to 3.0.2 by @jeffmaury [#11497](https://github.com/podman-desktop/podman-desktop/pull/11497) -- chore: added permission listener for kubernetes permissions by @gastoner [#11492](https://github.com/podman-desktop/podman-desktop/pull/11492) -- chore: remove no-explicit-any from docker extension svelte by @cdrage [#11481](https://github.com/podman-desktop/podman-desktop/pull/11481) -- chore: remove no-explicit-any deployments by @cdrage [#11479](https://github.com/podman-desktop/podman-desktop/pull/11479) -- chore: remove no-explicit-any from lib/dashboard by @cdrage [#11478](https://github.com/podman-desktop/podman-desktop/pull/11478) -- chore: reduce default gap in expandable by @deboer-tim [#11475](https://github.com/podman-desktop/podman-desktop/pull/11475) -- chore: remove explicit-any from config map empty screen by @cdrage [#11435](https://github.com/podman-desktop/podman-desktop/pull/11435) -- chore: remove pod kubernetes actions by @deboer-tim [#11410](https://github.com/podman-desktop/podman-desktop/pull/11410) -- chore(podman): `module` property missing in tsconfig by @axel7083 [#11361](https://github.com/podman-desktop/podman-desktop/pull/11361) -- chore: remove podman pod kubernetes navigation by @deboer-tim [#11352](https://github.com/podman-desktop/podman-desktop/pull/11352) -- chore(electron): enable web security by @axel7083 [#11340](https://github.com/podman-desktop/podman-desktop/pull/11340) -- chore: remove no-explicit-any from lib/appearance by @cdrage [#11282](https://github.com/podman-desktop/podman-desktop/pull/11282) -- chore: remove no-explicit-any from lib/actions by @cdrage [#11278](https://github.com/podman-desktop/podman-desktop/pull/11278) -- chore: remove @typescript-eslint/no-explicit-any usage by @jeffmaury [#10998](https://github.com/podman-desktop/podman-desktop/pull/10998) - -### Tests 🚦 - -- chore(test): parametrize timeout by @cbr7 [#12171](https://github.com/podman-desktop/podman-desktop/pull/12171) -- chore(test): make traces and video deletion configurable by @cbr7 [#12134](https://github.com/podman-desktop/podman-desktop/pull/12134) -- chore(test): add check for error when editting images by @cbr7 [#12128](https://github.com/podman-desktop/podman-desktop/pull/12128) -- fix(tests): fix the heading locator for extensions page by @odockal [#12101](https://github.com/podman-desktop/podman-desktop/pull/12101) -- test(ui): adding svelte legacy usage for button by @axel7083 [#12076](https://github.com/podman-desktop/podman-desktop/pull/12076) -- chore(test): refactor waiters for navigationbar by @cbr7 [#12065](https://github.com/podman-desktop/podman-desktop/pull/12065) -- chore(test): wait for screen by @cbr7 [#12057](https://github.com/podman-desktop/podman-desktop/pull/12057) -- chore(test): switch to polling assert for robustness by @cbr7 [#12038](https://github.com/podman-desktop/podman-desktop/pull/12038) -- chore(test): handle exception thrown from race condition by @cbr7 [#12033](https://github.com/podman-desktop/podman-desktop/pull/12033) -- test: add e2e test for Kind cluster deployment with custom config by @amisskii [#12010](https://github.com/podman-desktop/podman-desktop/pull/12010) -- chore(test): increase timeout to avoid race condition by @cbr7 [#11958](https://github.com/podman-desktop/podman-desktop/pull/11958) -- chore(test): add terminal validation checks by @cbr7 [#11955](https://github.com/podman-desktop/podman-desktop/pull/11955) -- chore(test): fix k8s e2e tests by @amisskii [#11928](https://github.com/podman-desktop/podman-desktop/pull/11928) -- chore(test): add methods to check image badges by @cbr7 [#11897](https://github.com/podman-desktop/podman-desktop/pull/11897) -- chore(test): add troubleshooting pom and smoke e2e test by @odockal [#11891](https://github.com/podman-desktop/podman-desktop/pull/11891) -- test(ui/table): ensuring row with children are covered by @axel7083 [#11886](https://github.com/podman-desktop/podman-desktop/pull/11886) -- test: refactor tests for preload/src/index.ts by @feloy [#11828](https://github.com/podman-desktop/podman-desktop/pull/11828) -- test: e2e test for k8s CronJob resource page by @amisskii [#11812](https://github.com/podman-desktop/podman-desktop/pull/11812) -- chore(test): increase timeout of assertions by @cbr7 [#11788](https://github.com/podman-desktop/podman-desktop/pull/11788) -- test(e2e): added number of expected objects in stress test's case names by @danivilla9 [#11768](https://github.com/podman-desktop/podman-desktop/pull/11768) -- test: k8s ingress e2e test by @amisskii [#11752](https://github.com/podman-desktop/podman-desktop/pull/11752) -- test: new cli e2e tests by @cbr7 [#11750](https://github.com/podman-desktop/podman-desktop/pull/11750) -- chore(test): Docker compatibility e2e test for Windows by @xbabalov [#11721](https://github.com/podman-desktop/podman-desktop/pull/11721) -- chore(test): parametrize strict matching by @cbr7 [#11629](https://github.com/podman-desktop/podman-desktop/pull/11629) -- chore(test): fix k8s e2e tests by @amisskii [#11628](https://github.com/podman-desktop/podman-desktop/pull/11628) -- chore(test): remove obsolete method by @cbr7 [#11617](https://github.com/podman-desktop/podman-desktop/pull/11617) -- test: k8s services e2e test by @amisskii [#11604](https://github.com/podman-desktop/podman-desktop/pull/11604) -- chore(test): extract port-forwarding operations to a dedicated utility file by @amisskii [#11588](https://github.com/podman-desktop/podman-desktop/pull/11588) -- fix(tests): svelte 5.22.5 by @axel7083 [#11568](https://github.com/podman-desktop/podman-desktop/pull/11568) -- chore(tests): enhance pushing image to kind method by @xbabalov [#11494](https://github.com/podman-desktop/podman-desktop/pull/11494) -- chore(test): add push image e2e test by @cbr7 [#11433](https://github.com/podman-desktop/podman-desktop/pull/11433) -- chore(test): skip kind tests that run on rootless mode machines by @danivilla9 [#11388](https://github.com/podman-desktop/podman-desktop/pull/11388) -- test: add E2E test for image manifests by @amisskii [#11186](https://github.com/podman-desktop/podman-desktop/pull/11186) - -### Docs 📖 - -- docs: add more Readme badges by @benoitf [#12166](https://github.com/podman-desktop/podman-desktop/pull/12166) -- docs: add entry for community meetings by @benoitf [#12159](https://github.com/podman-desktop/podman-desktop/pull/12159) -- docs: add EOST as adopters by @fabienengels [#12106](https://github.com/podman-desktop/podman-desktop/pull/12106) -- docs(website): corrected the file name in the troubleshooting doc by @shipsing [#12073](https://github.com/podman-desktop/podman-desktop/pull/12073) -- docs(website): updated the Kubernetes section to reflect how to switc… by @shipsing [#12009](https://github.com/podman-desktop/podman-desktop/pull/12009) -- docs(website): added extension related use cases with API references by @shipsing [#11978](https://github.com/podman-desktop/podman-desktop/pull/11978) -- docs(website): adding a troubleshooting section in the doc by @shipsing [#11950](https://github.com/podman-desktop/podman-desktop/pull/11950) -- docs(website): adding links for the published extensions in the doc by @shipsing [#11945](https://github.com/podman-desktop/podman-desktop/pull/11945) -- docs(website): added details to publish a PD extension to the catalog by @shipsing [#11893](https://github.com/podman-desktop/podman-desktop/pull/11893) -- docs(website): updated the kubernetes section based on latest changes… by @shipsing [#11718](https://github.com/podman-desktop/podman-desktop/pull/11718) -- docs: adding an instruction to enable ip_tables by @AlexonOliveiraRH [#11710](https://github.com/podman-desktop/podman-desktop/pull/11710) -- docs(website): added an uninstall section in the documentation by @shipsing [#11689](https://github.com/podman-desktop/podman-desktop/pull/11689) -- docs: add more information to extend page by @cdrage [#11673](https://github.com/podman-desktop/podman-desktop/pull/11673) -- docs(website): fixed formatting issues by @shipsing [#11638](https://github.com/podman-desktop/podman-desktop/pull/11638) -- docs: update RHEL WSL blog for RHEL 10 by @jeffmaury [#11625](https://github.com/podman-desktop/podman-desktop/pull/11625) -- docs: added demos,presentation and community meeting link to the community page by @rujutashinde [#11554](https://github.com/podman-desktop/podman-desktop/pull/11554) -- docs(website): added a blog to configure a mirror registry by @shipsing [#11542](https://github.com/podman-desktop/podman-desktop/pull/11542) -- docs(website): added a procedure to configure a mirror registry by @shipsing [#11465](https://github.com/podman-desktop/podman-desktop/pull/11465) -- docs: queryBy vs getBy by @feloy [#11446](https://github.com/podman-desktop/podman-desktop/pull/11446) -- docs(website): add blog post for 1.17 release by @benoitf [#11431](https://github.com/podman-desktop/podman-desktop/pull/11431) - -### ci 🔁 - -- ci: Harden GitHub Actions by @step-security-bot [#12162](https://github.com/podman-desktop/podman-desktop/pull/12162) -- ci: Harden GitHub Actions by @step-security-bot [#12160](https://github.com/podman-desktop/podman-desktop/pull/12160) -- ci: adjust codecov report by @benoitf [#11770](https://github.com/podman-desktop/podman-desktop/pull/11770) -- ci: report unit tests coverage to codecov by @benoitf [#11769](https://github.com/podman-desktop/podman-desktop/pull/11769) - -### Feature 💡 - -- feat: navigation api for manifests by @deboer-tim [#12143](https://github.com/podman-desktop/podman-desktop/pull/12143) -- feat(vitest): configure workspace by @axel7083 [#12111](https://github.com/podman-desktop/podman-desktop/pull/12111) -- feat(PinRegistry): adding telemetry events by @axel7083 [#12088](https://github.com/podman-desktop/podman-desktop/pull/12088) -- feat(status-bar): pin-unpin providers by @axel7083 [#11973](https://github.com/podman-desktop/podman-desktop/pull/11973) -- feat: vm provider details / terminal by @feloy [#11948](https://github.com/podman-desktop/podman-desktop/pull/11948) -- feat: create VM provider frontend by @feloy [#11924](https://github.com/podman-desktop/podman-desktop/pull/11924) -- feat: startProvider works for VM connections by @feloy [#11880](https://github.com/podman-desktop/podman-desktop/pull/11880) -- feat: add VM provider lifecycle to frontend by @feloy [#11863](https://github.com/podman-desktop/podman-desktop/pull/11863) -- feat(ProviderButton): adding left slot by @axel7083 [#11862](https://github.com/podman-desktop/podman-desktop/pull/11862) -- feat: update start/stop/editProviderConnectionLifecycle for VM by @feloy [#11839](https://github.com/podman-desktop/podman-desktop/pull/11839) -- feat: create vm provider connection (backend) by @feloy [#11827](https://github.com/podman-desktop/podman-desktop/pull/11827) -- feat: add VM provider info to ProviderInfo structure by @feloy [#11787](https://github.com/podman-desktop/podman-desktop/pull/11787) -- feat: implement setVmProviderConnectionFactory by @feloy [#11777](https://github.com/podman-desktop/podman-desktop/pull/11777) -- feat(frontend): adding class props to provider widget by @axel7083 [#11775](https://github.com/podman-desktop/podman-desktop/pull/11775) -- feat: add control to change the kubernetes namespace by @deboer-tim [#11756](https://github.com/podman-desktop/podman-desktop/pull/11756) -- feat: register VM provider connection by @feloy [#11755](https://github.com/podman-desktop/podman-desktop/pull/11755) -- feat(extensions/kind): update projectcontour to v1.30.2 by @Blaimi [#11734](https://github.com/podman-desktop/podman-desktop/pull/11734) -- feat(pin-registry): expose pin / unpin / list to main world by @axel7083 [#11701](https://github.com/podman-desktop/podman-desktop/pull/11701) -- feat: expose ListImagesOptions to main world by @axel7083 [#11700](https://github.com/podman-desktop/podman-desktop/pull/11700) -- feat: represent not permitted resources in Kubernetes dashboard by @feloy [#11639](https://github.com/podman-desktop/podman-desktop/pull/11639) -- feat: handle error when exec command not found by @feloy [#11608](https://github.com/podman-desktop/podman-desktop/pull/11608) -- feat: display resources count in Kubernetes experimental mode by @feloy [#11529](https://github.com/podman-desktop/podman-desktop/pull/11529) -- feat: Add Jobs to Kubernetes by @cdrage [#11474](https://github.com/podman-desktop/podman-desktop/pull/11474) -- feat: use expandable for learning center by @deboer-tim [#11470](https://github.com/podman-desktop/podman-desktop/pull/11470) -- feat: kubernetes experimental backend provides active resources count by @feloy [#11467](https://github.com/podman-desktop/podman-desktop/pull/11467) -- feat(main): adding pin-registry for providers by @axel7083 [#11422](https://github.com/podman-desktop/podman-desktop/pull/11422) -- feat: add the ability to auto scroll to provider from statusbar by @bmahabirbu [#11420](https://github.com/podman-desktop/podman-desktop/pull/11420) -- feat: added visual indicator for kubernetes permissions by @gastoner [#11307](https://github.com/podman-desktop/podman-desktop/pull/11307) -- feat: kube play build support by @axel7083 [#10801](https://github.com/podman-desktop/podman-desktop/pull/10801) -- feat: add community page by @Firewall [#10551](https://github.com/podman-desktop/podman-desktop/pull/10551) - -### Fixes 🔨 - -- fix: image details navigation by @deboer-tim [#12139](https://github.com/podman-desktop/podman-desktop/pull/12139) -- fix(Button): binding usage and event type by @axel7083 [#12086](https://github.com/podman-desktop/podman-desktop/pull/12086) -- fix(ui): race condition in ImageDetailsCheck tests by @axel7083 [#12077](https://github.com/podman-desktop/podman-desktop/pull/12077) -- fix: use intermediate ca store as well on Windows by @jeffmaury [#12066](https://github.com/podman-desktop/podman-desktop/pull/12066) -- fix: updated configmap secret capitilization by @bmahabirbu [#12048](https://github.com/podman-desktop/podman-desktop/pull/12048) -- fix: remove unnecessary assignment by @feloy [#12036](https://github.com/podman-desktop/podman-desktop/pull/12036) -- fix: reset messageBox before sending result through callback by @jeffmaury [#12008](https://github.com/podman-desktop/podman-desktop/pull/12008) -- fix(Table.svelte): children function usage by @axel7083 [#11980](https://github.com/podman-desktop/podman-desktop/pull/11980) -- fix(PinRegistry): listeners and VM support by @axel7083 [#11976](https://github.com/podman-desktop/podman-desktop/pull/11976) -- fix: tests on namespace dropdown by @feloy [#11971](https://github.com/podman-desktop/podman-desktop/pull/11971) -- fix(ui): make Carousel generic by @axel7083 [#11922](https://github.com/podman-desktop/podman-desktop/pull/11922) -- fix: no current context on Kubernetes dashboard by @feloy [#11903](https://github.com/podman-desktop/podman-desktop/pull/11903) -- fix: do not forget to refresh context list after switching docker context by @benoitf [#11898](https://github.com/podman-desktop/podman-desktop/pull/11898) -- fix: wait for kubeconfig change while creating kind cluster by @deboer-tim [#11868](https://github.com/podman-desktop/podman-desktop/pull/11868) -- fix(dropdown): ensure onChange value are non-undefined by @axel7083 [#11860](https://github.com/podman-desktop/podman-desktop/pull/11860) -- fix: name column alignment by @deboer-tim [#11838](https://github.com/podman-desktop/podman-desktop/pull/11838) -- fix: add delete confirmation dialog for Kubernetes Ingress/Routes resources by @amisskii [#11806](https://github.com/podman-desktop/podman-desktop/pull/11806) -- fix(storybook/expandable): enable rule no-useless-children-snippet and fix one violation by @dgolovin [#11797](https://github.com/podman-desktop/podman-desktop/pull/11797) -- fix: update tooltip position for first provider in statusbar by @SoniaSandler [#11796](https://github.com/podman-desktop/podman-desktop/pull/11796) -- fix(docs): `docker export` syntax by @Virakal [#11792](https://github.com/podman-desktop/podman-desktop/pull/11792) -- fix(frontend): status bar order by @axel7083 [#11773](https://github.com/podman-desktop/podman-desktop/pull/11773) -- fix: remove Kubernetes provider from map when disposed by @feloy [#11767](https://github.com/podman-desktop/podman-desktop/pull/11767) -- fix(extensions/kind): use correct types in download.ts by @Blaimi [#11733](https://github.com/podman-desktop/podman-desktop/pull/11733) -- fix: alignment in kubernetes name column by @deboer-tim [#11732](https://github.com/podman-desktop/podman-desktop/pull/11732) -- fix: update minimal node types to v22 by @odockal [#11730](https://github.com/podman-desktop/podman-desktop/pull/11730) -- fix: enabled svelte no-reactive rule and tested files by @bmahabirbu [#11709](https://github.com/podman-desktop/podman-desktop/pull/11709) -- fix(tools): remove no-explicit-any generator.spec.ts by @axel7083 [#11706](https://github.com/podman-desktop/podman-desktop/pull/11706) -- fix(preload-webview-spec): remove no-explicit-any by @axel7083 [#11705](https://github.com/podman-desktop/podman-desktop/pull/11705) -- fix(preload-webview-index): remove no-explicit-any by @axel7083 [#11704](https://github.com/podman-desktop/podman-desktop/pull/11704) -- fix(renderer): create container from existing with multiple connections by @axel7083 [#11702](https://github.com/podman-desktop/podman-desktop/pull/11702) -- fix: do not declare informer offline on 404 error by @feloy [#11674](https://github.com/podman-desktop/podman-desktop/pull/11674) -- fix: website sprint link by @benoitf [#11663](https://github.com/podman-desktop/podman-desktop/pull/11663) -- fix: grant access to the containers directory for the flatpak build by @vzhukovs [#11654](https://github.com/podman-desktop/podman-desktop/pull/11654) -- fix: removed compose /usr/local/bin in PATH check to align with kubectl by @bmahabirbu [#11651](https://github.com/podman-desktop/podman-desktop/pull/11651) -- fix: do not use ResourceName for experimental kubernetes by @feloy [#11616](https://github.com/podman-desktop/podman-desktop/pull/11616) -- fix: make labels in create new connection form resize properly and update form components width by @SoniaSandler [#11532](https://github.com/podman-desktop/podman-desktop/pull/11532) -- fix: button focus outline by @deboer-tim [#11483](https://github.com/podman-desktop/podman-desktop/pull/11483) -- fix: add back button cursor pointer by @deboer-tim [#11471](https://github.com/podman-desktop/podman-desktop/pull/11471) -- fix: proper validation of input fields by @vzhukovs [#11359](https://github.com/podman-desktop/podman-desktop/pull/11359) -- fix: handling of proxy bypass values from system under macOS by @chkpnt [#10975](https://github.com/podman-desktop/podman-desktop/pull/10975) diff --git a/website/blog/2025-05-05-vs-code-with-podman-desktop.md b/website/blog/2025-05-05-vs-code-with-podman-desktop.md deleted file mode 100644 index f59c09c92a..0000000000 --- a/website/blog/2025-05-05-vs-code-with-podman-desktop.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Supercharge Your Container Development in VS Code with Podman and Podman Desktop -description: How to develop apps with VS Code and Podman Desktop -authors: [firewall] -tags: [podman-desktop, podman, vscode, docker, extension, development, container, tools] -hide_table_of_contents: false ---- - -Developing containerized applications can sometimes feel complex, but with the right tools, it can be a smooth and efficient process. In this blog post, we'll explore how to leverage the power of Visual Studio Code (VS Code) together with Podman and Podman Desktop to streamline your container development workflow. We'll cover setting up and using two VS Code extensions that integrate with Podman. - -## VS Code: Your IDE for Container Development - -VS Code is a popular and versatile code editor that can be extended to enhance its functionality. For container development, several excellent extensions integrate seamlessly with Podman. - -## Prerequisites - -Before we begin, ensure you have the following installed: - -- **Podman:** Follow the installation instructions on the [official website](https://podman.io/). -- **Podman Desktop:** Download and install Podman Desktop from the [official website](https://podman-desktop.io/downloads). -- **Visual Studio Code:** Download and install VS Code from the [official website](https://code.visualstudio.com/download). - -## **VS Code Extensions** - -To integrate VS Code with Podman, we have 2 extensions as options: - -1. [**Microsoft’s Container Tools extension**](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-containers)**:** The "Container Tools" extension provides excellent support for container-related tasks, including building images, managing containers, and working with Containerfiles and Dockerfiles. Microsoft [recently announced](https://techcommunity.microsoft.com/blog/AppsonAzureBlog/major-updates-to-vs-code-docker-introducing-container-tools/4400609) that they will be evolving the Docker extension into the Container Tools extension to support other tools like Podman! -2. [**Pod Manager**](https://marketplace.visualstudio.com/items/?itemName=dreamcatcher45.podmanager): This extension was created by one of the members of our community and is completely open source. It is designed to help you manage Podman containers, images, volumes, and networks directly from the VS Code interface. - -While it's unlikely that you would use both extensions simultaneously, comparing them will help you understand the strengths and weaknesses of each, ultimately making it easier to select the one that best fits your needs. So for this blog post, we'll install them one by one. - -### Option 1: Microsoft’s Container Tools extension - -To install the extension: - -1. Open VS Code. -2. Click on the Extensions icon in the Activity Bar (or press `Ctrl+Shift+X` or `Cmd+Shift+X`). -3. Search for "Container Tools" and install the extension by Microsoft. - -#### Configuring VS Code for Podman - -The Container Tools extension usually automatically detects Podman if Docker isn't running, by looking at the `DOCKER_HOST` environment variable. In Podman Desktop navigate to Settings \> Docker Compatibility \> Third-Party Tool Compatibility and make sure the option is enabled. Learn more about the [Docker Compatibility in our documentation](https://podman-desktop.io/docs/migrating-from-docker/managing-docker-compatibility). - -![enabling docker compatibility in the settings](img/vs-code-podman/docker-compatibility.png) - -### Option 2: Pod Manager - -To install the extension: - -1. Open VS Code. -2. Click on the Extensions icon in the Activity Bar (or press `Ctrl+Shift+X` or `Cmd+Shift+X`). -3. Search for "Pod Manager" and install the extension by dreamcatcher45. - -## Using the VS Code Extensions - -Now that we have the extensions installed and configured, let's see how to use them. - -### Working with Containerfiles and Dockerfiles - -Both extensions provide syntax highlighting, code completion, and linting for Containerfiles and Dockerfiles. Open a Containerfile in VS Code, and you'll immediately benefit from these features. - -You can also build images directly from VS Code: - -1. Right-click on the Containerfile in the Explorer view. -2. Select "Build Image". -3. VS Code will prompt you for an image name and tag. -4. The extension will then build the image using Podman. -5. After that you will see the built image in the sidebar. - -If you are using the CLI commands to build images, you will also see them here. - -![building a Containerfile in vs code using the microsoft extension](img/vs-code-podman/build-image.png) - -### Managing Containers - -The extensions also allow you to manage containers directly from VS Code. You can start, stop, restart, and remove containers, as well as view their logs and inspect their configuration. - -To view the container logs: - -1. Click on the Container icon in the Activity Bar. -2. You'll see a list of your containers, images, and networks. -3. Right-click on a container to perform actions. - -![using Container Tools extension to view the logs of the container](img/vs-code-podman/view-logs.png) - -Similarly using the Pod manager extension we can visually inspect containers, images, and volumes. - -1. Click on the Pod manager icon in the Activity Bar. -2. You'll see a list of your containers, images, and networks. - -![using podmanager to view all the running containers, images and volumes](img/vs-code-podman/podmanager.png) - -and manage the container lifecycle. - -![using podmanager to manage the lifecycle of the container](img/vs-code-podman/interact-with-container.png) - -and of course troubleshoot issues with a visual interface. - -![using podmanager to enter the container](img/vs-code-podman/podmanager-details.png) - -## Conclusion - -In comparison, both extensions provide a nearly identical set of features, so it is really up to you which UI you prefer. Personally I will stick with the Pod Manager because the logo is a seal 🦭. Remember if you encounter any issues using these tools or with Podman Desktop let us know by [starting a discussion](https://github.com/podman-desktop/podman-desktop/discussions) or [creating an issue](https://github.com/podman-desktop/podman-desktop/issues). - -By combining the power of VS Code, Podman, and Podman Desktop, you can create a streamlined and efficient container development workflow. The VS Code extensions provide excellent integration with Podman, allowing you to manage containers, build images, and work with Containerfiles directly from your code editor. We are excited to see that Microsoft is embracing Podman and building support into their ecosystem. Podman Desktop complements this with a visual interface for managing your container environment and will help to move from your development environment to a production Kubernetes environment. Embrace these tools and elevate your container development experience! diff --git a/website/blog/2025-05-15-new-minc-extension-inner-loop.md b/website/blog/2025-05-15-new-minc-extension-inner-loop.md deleted file mode 100644 index 870a971277..0000000000 --- a/website/blog/2025-05-15-new-minc-extension-inner-loop.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: 'MINC: Speed Up Local K8s Dev' -description: Speed up your development inner loop using a local Kubernetes cluster powered by MicroShift in a container. -slug: iterate-quickly-inner-loop-with-a-kubernetes-cluster -authors: [benoitf] -tags: [podman-desktop, podman, images, kubernetes, microshift] -hide_table_of_contents: false -image: /img/blog/iterate-quickly-inner-loop-with-a-kubernetes-cluster/iterate-quickly-inner-loop-with-a-kubernetes-cluster.png ---- - -Developers working locally often seek a fast inner loop for coding, building, and testing their OCI images and Kubernetes applications. - -For instance, when testing a new image in a Kubernetes pod, the image must be accessible either by pushing it to a private or public registry or by ensuring it's available on the Kubernetes cluster nodes. This often requires additional commands such as `kind load docker-image`, `minikube cache add `, or publishing the image to a third-party registry. - -In this blog post, we’ll introduce a new extension called `MINC (MicroShift IN Container)`, which provides a [MicroShift](https://github.com/openshift/microshift) cluster running within an existing Podman Machine environment. - -![hero](/img/blog/iterate-quickly-inner-loop-with-a-kubernetes-cluster/iterate-quickly-inner-loop-with-a-kubernetes-cluster.png) - - - ---- - -## Introduction - -A [previous blog post](https://podman-desktop.io/blog/sharing-podman-images-with-kubernetes-cluster) outlines various methods to improve the inner loop using Kind or Minikube. While these approaches can enhance performance, there are still gaps and certain complexities involved. Achieving a seamless workflow where an image is built with Podman and immediately available in the cluster—often requires additional steps or tricky configurations. - -## podman, kubernetes/cri-o and MicroShift - -The CRI-O project implements the CRI interface and reuses image management from [containers/image](https://github.com/containers/image) project and handles storage using [containers/storage](https://github.com/containers/storage) project. - -Podman uses these same libraries to read and write container images, storing them in the `/var/lib/containers` directory. - -The MINC extension launches a [MicroShift](https://github.com/openshift/microshift) cluster in a more efficient way for developers than previous deployments. Unlike the OpenShift Local extension, which sets up a separate virtual machine (VM), MINC (Microshift IN Container) runs a container inside your existing Podman Machine (on Windows or macOS). This avoids running two VMs, one for Kubernetes and another for Podman. - -A key benefit is that the MINC container shares Podman’s image storage. When you build an image with Podman, it's instantly available in the cluster, no uploading or copying required. - -:::note[MINC requires a rootful Podman machine] -Make sure the machine is running in rootful mode, or create a new one using the `--rootful` option. -::: - -## Creating a Local Kubernetes/MicroShift Cluster - -1. Create the MicroShift cluster - -In the Resources view, click the MINC card and select "Create new…". For now, no customization is needed—just click "Next". The cluster will start automatically. - -1. Verify that now you see in bottom left in the status bar `microshift` as the default Kubernetes context and if you go in the left navbar in the Kubernetes section you can see in the Nodes entry the MicroShift node. - -1. Verify `kubectl` setup using a CLI - -MINC updates your `$HOME/.kube/config` file with a new `microshift` entry. Check in the bottom left of the status bar, you should see a `microshift` entry as the default Kubernetes context. - -Try listing all pods: - -```bash -kubectl get -A pods -``` - -Example output: - -```shell -NAMESPACE NAME READY STATUS -kube-flannel kube-flannel-ds-wjn2f 1/1 Running -kube-proxy kube-proxy-jh7r8 1/1 Running -kube-system csi-snapshot-controller-8d97d878f-l6q6j 1/1 Running -openshift-dns dns-default-zvdw9 2/2 Running -openshift-dns node-resolver-84twk 1/1 Running -openshift-ingress router-default-54c5757547-4jnfd 1/1 Running -openshift-service-ca service-ca-7977bdc4d4-ssjq2 1/1 Running - -``` - -#### Create a Hello World container image - -Create a `Containerfile` - -```Dockerfile -FROM nginx - -RUN echo 'Hello

    Hello from MINC

    ' \ - > /usr/share/nginx/html/index.html -``` - -Or quickly create it with: - -```shell -echo 'FROM nginx:alpine -RUN echo "Hello

    Hello from MINC

    " > /usr/share/nginx/html/index.html' > Containerfile -``` - -Then, using Podman Desktop, build the image providing the name `minc/nginx:hello-world`. - -### Verify Image in the MicroShift Cluster - -Inside the MicroShift container, run: - -```bash -crictl images | grep hello -``` - -You should see something like: - -```bash -docker.io/minc/nginx hello-world e7032b219ab03 51.1MB -``` - -The image is now available in the MicroShift cluster. - -#### Create a Pod from the UI - -Click on the play button from the image to start a new container. Then select the container and click on "Create Pod". - -#### Deploy a pod from this image using the CLI - -Use `kubectl` to deploy a pod from the image: - -```shell -kubectl run hello-world-pod --image=minc/nginx:hello-world --port=80 -``` - -To check if the pod is running, use: - -```shell -kubectl get pods -``` - -Expected output: - -```shell -kubectl get pods -NAME READY STATUS RESTARTS AGE -hello-world-pod 1/1 Running 0 6s -``` - -# Deploy this podman Pod to Kubernetes/MINC - -Select the pod and click on `Deploy to Kubernetes...` in the Kebab menu. -Your Pod is now deployed to the MINC cluster. - -If you want to use the CLI/manual steps : - -**Create a Service for the Pod using the CLI** - -Expose the pod with a service: - -```shell -kubectl expose pod hello-world-pod --port=80 --target-port=80 --name=hello-world-service -``` - -**Create a Route (Requires oc CLI)** - -Assuming the `oc` CLI (OpenShift client) is available in your `PATH`, create a route: - -```shell -oc create route edge hello-world-route --service svc/hello-world-service --port 80 --wildcard-policy=None -``` - -To list all routes: - -``` -oc get routes -``` - -#### Access the Nginx Container - -Clicking on the route in the UI will redirect you to the page. - -Without the UI it's also possible to get that information. - -Check that the nginx container is reachable using the route. Use the `-k` flag with curl to ignore the self-signed certificate warning: - -```shell -curl -k "https://$(oc get route hello-world-route -ojsonpath='{.status.ingress[0].host}'):9443" -``` - -You can also open the URL in your browser (you’ll need to ignore the self-signed certificate warning): - -```shell -open "https://$(oc get route hello-world-route -ojsonpath='{.status.ingress[0].host}'):9443" -``` - -You should see the message: "Hello from MINC" 🎉 - -This confirms that your inner development loop is working. Once the image is built, it’s instantly available in the cluster. - -### Video walkthrough - - - -### Conclusion - -Using MINC and the MINC extension significantly reduces the turnaround time between building and testing container images. - -Have feedback? Share it on the [MINC extension issue tracker](https://github.com/minc-org/minc-extension/issues) diff --git a/website/blog/2025-05-21-podman-ai-lab-openvino.md b/website/blog/2025-05-21-podman-ai-lab-openvino.md deleted file mode 100644 index 430545c3a9..0000000000 --- a/website/blog/2025-05-21-podman-ai-lab-openvino.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Enabling OpenVINO Inference in Podman AI Lab -description: Use Podman AI Lab to run OpenVINO inference workloads locally. -authors: [jeffmaury] -tags: [podman-desktop, podman, extension, ai, llm, local, podman-ai-lab, openvino] -hide_table_of_contents: false ---- - -# Enabling OpenVINO Inference in Podman AI Lab - -## Introduction to Podman AI Lab - -Podman AI Lab is an open-source platform designed to simplify the deployment, management, and experimentation of AI workloads using container technology. It provides a user-friendly interface for running, testing, and scaling AI models locally or in the cloud, leveraging the power and flexibility of Podman containers. - -## What is OpenVINO? - -OpenVINO™ (Open Visual Inference and Neural Network Optimization) is an open-source toolkit developed by Intel to accelerate AI inference on a variety of hardware, including CPUs, GPUs, and specialized accelerators. It optimizes deep learning models for fast, efficient inference, making it a popular choice for edge and cloud AI applications. - -## Using OpenVINO in Podman AI Lab - -Podman AI Lab now supports OpenVINO as an inference provider. This means you can: - -- Select an OpenVINO compatible model when starting an inference server or playground. -- Benefit from hardware-accelerated inference on supported Intel devices. -- Easily switch between different inference providers (e.g., llama-cpp, OpenVINO) for benchmarking and compatibility testing. - -:::warning - -This feature is only available on Intel based systems, as OpenVINO is optimized for Intel hardware. If you are using a non-Intel system, you will not be able to use OpenVINO as an inference provider. - -::: - -**How to use:** - -1. Launch Podman AI Lab and navigate to the model deployment or playground section. -2. When configuring your model, choose an OpenVINO compatible model. -3. Start the inference server or playground. - -## Starting an OpenVINO inference server - -1. Click the Podman AI Lab icon in the navigation bar. -2. In the Podman AI Lab navigation bar, click _Models > Services_ menu item. -3. Click the _New Model Service_ button on the top right. -4. Select an OpenVINO compatible model in the list (e.g. OpenVINO/mistral-7B-instruct-v0.2-int4-ov) in the _Model_ list and click the _Create Service_ button. -5. The inference server for the model is being started and after a while, click on the _Open service_ details button. - -![OpenVINO inference server details](img/podman-ai-lab-openvino/openvino-inference-server-details.png) - -Using the terminal shell, execute the given curl command and see the inference result output. - -## Starting a playground with an OpenVINO compatible model - -1. Click the Podman AI Lab icon in the navigation bar. -2. In the Podman AI Lab navigation bar, click _Models > Playgrounds_ menu item. -3. Click the _New Playground_ button on the top right. -4. Select an OpenVINO compatible model in the list (e.g. OpenVINO/mistral-7B-instruct-v0.2-int4-ov) in the _Model_ list and click the _Create playground_ button. -5. The playground for the model is being started and after a while, a chat interface is displayed. - -![Initial playground on OpenVINO model](img/podman-ai-lab-openvino/openvino-playground1.png) - -Enter 'What is OpenVINO ?' in the prompt and click the _Send_ button. The OpenVINO model will respond with an answer. - -![OpenVINO model response in the playground](img/podman-ai-lab-openvino/openvino-playground2.png) - -## Consistency with OpenShift AI + OpenVINO - -One of the key advantages of using OpenVINO in Podman AI Lab is the consistency it brings when transitioning workloads to OpenShift AI. Both platforms now support OpenVINO, ensuring that: - -- Models tested and optimized locally in Podman AI Lab will behave the same way when deployed to OpenShift AI. -- You can maintain a unified workflow from development to production, reducing surprises and integration issues. -- Performance optimizations and hardware acceleration are preserved across environments. - -## Conclusion - -By enabling OpenVINO as an inference provider, Podman AI Lab empowers users to leverage high-performance AI inference both locally and in the cloud, with a consistent experience across platforms like OpenShift AI. This integration streamlines the AI development lifecycle and unlocks new possibilities for deploying efficient, scalable AI solutions. diff --git a/website/blog/2025-05-22-release-1.19.md b/website/blog/2025-05-22-release-1.19.md deleted file mode 100644 index 817d7d7c8a..0000000000 --- a/website/blog/2025-05-22-release-1.19.md +++ /dev/null @@ -1,393 +0,0 @@ ---- -title: Podman Desktop 1.19 Release -description: Podman Desktop 1.19 has been released! -slug: podman-desktop-release-1.19 -authors: [axel7083] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.19/banner.png ---- - -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.19 Release! 🎉 - -![podman-desktop-hero-1.19](/img/blog/podman-desktop-release-1.19/banner.png) - -Podman Desktop 1.19 is now available. [Click here to download it](/downloads)! - -This release brings exciting new features and improvements, including many extension updates: - -- **Podman v5.5**: The Podman executable shipped with Podman Desktop has been updated to its latest version. -- **Bootc Extension 1.9**: Test and experiment with Bootable Container directly from the extension. -- **AI Lab 1.7.2**: New inference runtime, llama-stack implementation, and Model Context Protocol (MCP). -- **Minc extension**: Podman Desktop extension to deploy MicroShift as container. -- **RHEL lightspeed extension**: Extension to get assistance using RHEL Lightspeed. -- **RHEL VMs extension**: The RHEL VMs extension helps the user run RHEL Virtual machines. -- **Availability on RHEL 10**: Podman Desktop is now directly available on RHEL 10. - -## Release details 🔍 - -### Podman v5.5 - -Podman Desktop aims to work offline, so it ships alongside its binary, the Podman executable. -In this release, the latest Podman 5.5 version provides new features, bug fixes, and better stability while using the tool. - -You can find the full changelog of Podman 5.5 on their release page [containers/podman/releases#v5.5.0](https://github.com/containers/podman/releases/tag/v5.5.0). - -:::info - -While the Podman binary is updated in this release, the existing Podman machines will remain at the version in which they were created. To update your Podman machine, you must remove it and initialize a new one. - -::: - -### Bootc Extension 🦭 - -The Podman Desktop Boot container extension (aka Bootc) has recently seen significant improvements; one of them is the usage of [macadam](https://github.com/crc-org/macadam). - -> Macadam is a cross-platform command-line tool to create and manage virtual machines. It runs on Windows, macOS, and Linux, and uses each OS's native virtualization stacks: WSL2 on Windows, Apple’s Virtualization Framework on macOS, and QEMU on Linux. Macadam reuses Podman machine code. - -As the bootc tool applies a container image as an update to an already running Linux system, allowing the output to boot an entire OS with the container image included. To test the bootc artifact, you either need a dedicated machine or create a virtual machine from the artifact. - -To simplify the developer experience of testing the artifact, -the Podman Desktop Bootc extension offers you a way to directly create a virtual machine from the artifact you created. - - -
    -
    - -:::note - -There is a known issue with the virtualization that can be found in the [bootc extension readme](https://github.com/podman-desktop/extension-bootc?tab=readme-ov-file#known-issues). - -::: - -Another important aspect of presenting a new technology is showcasing its capabilities. Thanks to [Charlie Drage](https://github.com/cdrage) and [Tim deBoer](https://github.com/deboer-tim), a lot of effort has been put into demonstrating bootable containers, such as kernel module management, WiFi modules, systemd unit files, and others. - -You can find ready-to-use examples on the **Bootable Containers > Examples** page. - - -
    - -### AI Lab ✨ - -The Podman Desktop AI Lab extension started as an experiment in 2024, exploring how AI workflows could be containerized, -celebrating more than one year of updates and progress, the extension has grown to support a wide range of tools, -such as Instructlab[^64], llamaCPP[^65], WhisperCPP[^69] and with the latest release OpenVINO, llama-stack and MCP. - -#### OpenVINO - -With the introduction of OpenVINO[^67] as a supported inference runtime, you can now run OpenVINO models[^68] locally through Podman. - - -
    - -#### llama-stack - -Llama Stack standardizes the core building blocks that simplify AI application development[^71]. - -With the latest version of AI Lab, we introduce support for Llama Stack, running in a containerized environment. Inference servers running inside AI Lab will be registered to the Llama Server to be used by their clients. - - -
    - -#### Model Context Protocol (MCP) - -Creating a product from LLMs has proven challenging, and emerging protocols are trying to help, such as MCP. - -> MCP is an open protocol that standardizes how applications provide context to LLMs.[^70] - -The initial steps to support MCP in the AI Lab playground have begun, enabling developers to experiment directly through the GUI before trying to integrate it into their applications. - - -
    - -[^64]: https://instructlab.ai/ - -[^65]: https://github.com/ggml-org/llama.cpp - -[^67]: https://github.com/openvinotoolkit/openvino - -[^68]: https://huggingface.co/OpenVINO - -[^69]: https://github.com/ggml-org/whisper.cpp - -[^70]: https://modelcontextprotocol.io/introduction - -[^71]: https://github.com/meta-llama/llama-stack?tab=readme-ov-file#overview - -### Minc extension 📦 - -[MicroShift](https://github.com/openshift/microshift) is a project that optimizes OpenShift Kubernetes for small form factors and edge computing[^66]. Similar to [Kind](/docs/kind) and [Minikube](/docs/minikube), an extension for creating and managing MicroShift clusters locally through Podman has been created. - -You can learn more about it in the [MicroShift](/docs/openshift/microshift) section. - -[^66]: https://www.redhat.com/en/topics/edge-computing/microshift - -:::tip - -Interested in bootable containers and MicroShift? You can refer to the blog post titled [Creating a MicroShift Bootable Image with Podman Desktop](/blog/2024/11/08/bootc-microshift). - -::: - -### RHEL Lightspeed ⚡ - -Red Hat Lightspeed is a generative AI assistant integrated into Red Hat's hybrid cloud platforms, such as OpenShift and Enterprise Linux.[^100] -It provides natural language guidance within the console to simplify complex tasks, enhance productivity, and bridge IT skills gaps. - -[^100]: [Red Hat Announces General Availability of Red Hat OpenShift Lightspeed to Supercharge Hybrid Cloud Productivity with Generative AI](https://www.redhat.com/en/about/press-releases/red-hat-announces-general-availability-red-hat-openshift-lightspeed-supercharge-hybrid-cloud-productivity-generative-ai) - -Users can choose from various AI models, including OpenAI, Azure OpenAI, WatsonX, or deploy private models on Red Hat platforms. - -With a Red Hat subscription, you can experiment with Red Hat Lightspeed from anywhere, including Podman Desktop, through a new extension called RHEL Lightspeed. - -You can install the extension directly by navigating to the **Extensions > Catalog** page and clicking its **Install** icon. - - -
    -
    - -:::note - -- If you are facing any authentication issues, ensure to use the [Red Hat Authentication](https://github.com/redhat-developer/podman-desktop-redhat-account-ext) extension. -- As this new extension just landed in the Podman Desktop extension catalog, you may share your feedback in the extension repository [redhat-developer/podman-desktop-redhat-lightspeed-ext](https://github.com/redhat-developer/podman-desktop-redhat-lightspeed-ext). - -::: - -### RHEL VMs extension 🗃️ - -Through the Podman executable, Podman Desktop manages virtual machines; the cross-platform code responsible for this has been reused to create the macadam tool mentioned earlier. - -The RHEL VMs extension helps the user run RHEL virtual machines using the macadam tool. - - -
    -
    - -:::note - -On macOS and Linux, you need to manually install the Macadam binary. See [pre-requisites](https://github.com/redhat-developer/podman-desktop-rhel-ext?tab=readme-ov-file#pre-requisites) for more details. - -::: - -### RHEL 10 availability 🚢 - -The Podman Desktop you love - _simplified container management and intuitive UI_ - now extends to RHEL workflows. -For users on RHEL systems, explore the extension channel to install Podman Desktop. You'll get a familiar experience across Windows, Mac and now RHEL. Releases are happening at the same time as other platforms, so you'll get instant access to new features. - -Find more details about [Installing Podman Desktop on RHEL 10](https://podman-desktop.io/docs/installation/linux-install/install-on-rhel10). - ---- - -## Community thank you - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -release, we received pull requests from the following people: - -- [MarsKubeX](https://github.com/MarsKubeX) in [fix: changed wrong link to docker compatibility documentation](https://github.com/podman-desktop/podman-desktop/pull/12573) -- [DIGIX666](https://github.com/DIGIX666) in [feat: download linux binary on the docs](https://github.com/podman-desktop/podman-desktop/pull/12286) - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.19.0) and [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.19.0). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. - -## Detailed release changelog - -### fix 🔨 - -- fix(workflow): release permission by @axel7083 [#12585](https://github.com/podman-desktop/podman-desktop/pull/12585) -- fix(NumberInput): increment button position by @axel7083 [#12578](https://github.com/podman-desktop/podman-desktop/pull/12578) -- fix: changed wrong link to docker compatibility documentation by @MarsKubeX [#12573](https://github.com/podman-desktop/podman-desktop/pull/12573) -- fix: remove unused breadcrumbTitle from Page and related components by @feloy [#12559](https://github.com/podman-desktop/podman-desktop/pull/12559) -- fix: changed props interface defintion to be exported in TestComplexT… by @MarsKubeX [#12547](https://github.com/podman-desktop/podman-desktop/pull/12547) -- fix(website): updated sizes of headings in kubernetes section by @gastoner [#12521](https://github.com/podman-desktop/podman-desktop/pull/12521) -- fix(website): updated icon sizes in kubernetes section by @gastoner [#12520](https://github.com/podman-desktop/podman-desktop/pull/12520) -- fix: use default colours for the terminal output by @vzhukovs [#12494](https://github.com/podman-desktop/podman-desktop/pull/12494) -- fix(website): unified sizes of icons by @gastoner [#12484](https://github.com/podman-desktop/podman-desktop/pull/12484) -- fix: migrate Input, NumberInput and SearchInput to svelte v5 by @dgolovin [#12475](https://github.com/podman-desktop/podman-desktop/pull/12475) -- fix: use default padding for markdown buttons by @vzhukovs [#12451](https://github.com/podman-desktop/podman-desktop/pull/12451) -- fix(container-connection): display terminal tab when shell access is available by @axel7083 [#12417](https://github.com/podman-desktop/podman-desktop/pull/12417) -- fix(webview-api): missing repository and provenance by @axel7083 [#12416](https://github.com/podman-desktop/podman-desktop/pull/12416) -- fix: display VM status by @feloy [#12411](https://github.com/podman-desktop/podman-desktop/pull/12411) -- fix: updated podman image path argument by @gastoner [#12388](https://github.com/podman-desktop/podman-desktop/pull/12388) -- fix(electron-v36): gtk-version on linux by @axel7083 [#12360](https://github.com/podman-desktop/podman-desktop/pull/12360) -- fix: introduce fontId property to ProviderImages interface by @dgolovin [#12357](https://github.com/podman-desktop/podman-desktop/pull/12357) -- fix(electron-updater): disable differential download by @axel7083 [#12353](https://github.com/podman-desktop/podman-desktop/pull/12353) -- fix(ci): fix Podman Desktop binary env. var. name in production e2e t… by @odockal [#12308](https://github.com/podman-desktop/podman-desktop/pull/12308) -- fix: avoid cve in build tool by @benoitf [#12295](https://github.com/podman-desktop/podman-desktop/pull/12295) -- fix(icons): update Props interfaces by @axel7083 [#12232](https://github.com/podman-desktop/podman-desktop/pull/12232) - -### chore ✅ - -- chore: remove brew PR automation by @benoitf [#12563](https://github.com/podman-desktop/podman-desktop/pull/12563) -- chore: use reset for mocking and use waitFor methods by @benoitf [#12560](https://github.com/podman-desktop/podman-desktop/pull/12560) -- chore: adjust website light mode text color to be darker by @SoniaSandler [#12550](https://github.com/podman-desktop/podman-desktop/pull/12550) -- chore: update flathub job by @benoitf [#12545](https://github.com/podman-desktop/podman-desktop/pull/12545) -- chore(workflow): set permissions for release.yaml by @axel7083 [#12532](https://github.com/podman-desktop/podman-desktop/pull/12532) -- chore(workflow): set permissions for daily-testing-build.yaml by @axel7083 [#12516](https://github.com/podman-desktop/podman-desktop/pull/12516) -- chore: update the light mode text color to be darker in the home page by @SoniaSandler [#12503](https://github.com/podman-desktop/podman-desktop/pull/12503) -- chore: remove old sections from home page by @SoniaSandler [#12502](https://github.com/podman-desktop/podman-desktop/pull/12502) -- chore: rename preview into website preview by @benoitf [#12482](https://github.com/podman-desktop/podman-desktop/pull/12482) -- chore: remove netlify deployment as now it's deployed to cloudflare by @benoitf [#12478](https://github.com/podman-desktop/podman-desktop/pull/12478) -- chore: do not publish comment when publishing to cloudflare by @benoitf [#12477](https://github.com/podman-desktop/podman-desktop/pull/12477) -- chore: remove unused imports from index page by @SoniaSandler [#12469](https://github.com/podman-desktop/podman-desktop/pull/12469) -- chore: added footer markdown description to message box options by @gastoner [#12466](https://github.com/podman-desktop/podman-desktop/pull/12466) -- chore(website): added option to specify link in read the docs button by @gastoner [#12464](https://github.com/podman-desktop/podman-desktop/pull/12464) -- chore: remove unused `tools/*` content by @axel7083 [#12456](https://github.com/podman-desktop/podman-desktop/pull/12456) -- chore(next-build): define explicit permissions by @axel7083 [#12455](https://github.com/podman-desktop/podman-desktop/pull/12455) -- chore(pr-check): define explicit permissions by @axel7083 [#12454](https://github.com/podman-desktop/podman-desktop/pull/12454) -- chore: fix argos screenshots github button by @cdrage [#12453](https://github.com/podman-desktop/podman-desktop/pull/12453) -- chore: publish to cloudflare (in addition to netlify) by @benoitf [#12433](https://github.com/podman-desktop/podman-desktop/pull/12433) -- chore: fix github button, remove github link, add spacing. by @cdrage [#12429](https://github.com/podman-desktop/podman-desktop/pull/12429) -- chore(workflow): update prerelease repository in `next-build.yaml` by @axel7083 [#12414](https://github.com/podman-desktop/podman-desktop/pull/12414) -- chore: update jsdom to 27.0.0-beta.1 by @benoitf [#12412](https://github.com/podman-desktop/podman-desktop/pull/12412) -- chore: update podman to v5.5 by @gastoner [#12408](https://github.com/podman-desktop/podman-desktop/pull/12408) -- chore(npm-registry): configure provenance for published packages by @axel7083 [#12398](https://github.com/podman-desktop/podman-desktop/pull/12398) -- chore(e2e): fix skip on kind tests by @danivilla9 [#12397](https://github.com/podman-desktop/podman-desktop/pull/12397) -- chore: indicate failure for error notification by @SoniaSandler [#12389](https://github.com/podman-desktop/podman-desktop/pull/12389) -- chore: remove vale tooling by @benoitf [#12386](https://github.com/podman-desktop/podman-desktop/pull/12386) -- chore: do not minify when deploying production site by @cdrage [#12369](https://github.com/podman-desktop/podman-desktop/pull/12369) -- chore: clear build and cache folders before running website build by @cdrage [#12338](https://github.com/podman-desktop/podman-desktop/pull/12338) -- chore: fix website warning + not building correctly. by @cdrage [#12337](https://github.com/podman-desktop/podman-desktop/pull/12337) -- chore: do not run podman mac helper checks, or socket checks unless ready by @cdrage [#12331](https://github.com/podman-desktop/podman-desktop/pull/12331) -- chore(website): updated kubernetes highlited features by @gastoner [#12305](https://github.com/podman-desktop/podman-desktop/pull/12305) -- chore(website): updated container highlited features by @gastoner [#12304](https://github.com/podman-desktop/podman-desktop/pull/12304) -- chore(website): added read the docs button by @gastoner [#12303](https://github.com/podman-desktop/podman-desktop/pull/12303) -- chore: add FAQ section to the website by @SoniaSandler [#12300](https://github.com/podman-desktop/podman-desktop/pull/12300) -- chore: refactor kube config secret columns by @deboer-tim [#12299](https://github.com/podman-desktop/podman-desktop/pull/12299) -- chore: refactor kube deployment columns by @deboer-tim [#12298](https://github.com/podman-desktop/podman-desktop/pull/12298) -- chore: refactor kube ingress route columns by @deboer-tim [#12297](https://github.com/podman-desktop/podman-desktop/pull/12297) -- chore: add testimonials section to the website by @SoniaSandler [#12296](https://github.com/podman-desktop/podman-desktop/pull/12296) -- chore: refactor kube pvc columns by @deboer-tim [#12294](https://github.com/podman-desktop/podman-desktop/pull/12294) -- chore: refactor kube service columns by @deboer-tim [#12293](https://github.com/podman-desktop/podman-desktop/pull/12293) -- chore: refactor kube nodes column by @deboer-tim [#12292](https://github.com/podman-desktop/podman-desktop/pull/12292) -- chore(deps-dev): udate to jsdom v26.1.0 by @benoitf [#12291](https://github.com/podman-desktop/podman-desktop/pull/12291) -- chore: refactor kube cronjob columns by @deboer-tim [#12290](https://github.com/podman-desktop/podman-desktop/pull/12290) -- chore: refactor kube job columns by @deboer-tim [#12289](https://github.com/podman-desktop/podman-desktop/pull/12289) -- chore: use props in kube columns by @deboer-tim [#12288](https://github.com/podman-desktop/podman-desktop/pull/12288) -- chore: generate linux tgz for Arm64 by @benoitf [#12282](https://github.com/podman-desktop/podman-desktop/pull/12282) -- chore: add more features section to the website by @SoniaSandler [#12276](https://github.com/podman-desktop/podman-desktop/pull/12276) -- chore: update description of docker third party tool by @bmahabirbu [#12265](https://github.com/podman-desktop/podman-desktop/pull/12265) -- chore: add blog post for minc extension by @benoitf [#12260](https://github.com/podman-desktop/podman-desktop/pull/12260) -- chore(website): added highlited label by @gastoner [#12249](https://github.com/podman-desktop/podman-desktop/pull/12249) -- chore: clean up kubernetes status columns by @deboer-tim [#12241](https://github.com/podman-desktop/podman-desktop/pull/12241) -- chore: shared kubernetes status column by @deboer-tim [#12230](https://github.com/podman-desktop/podman-desktop/pull/12230) -- chore: website - update homepage screenshot by @cdrage [#12224](https://github.com/podman-desktop/podman-desktop/pull/12224) -- chore(vitest): enable default reporter on CI by @axel7083 [#12209](https://github.com/podman-desktop/podman-desktop/pull/12209) -- chore: migrate empty screen to svelte 5 by @deboer-tim [#12194](https://github.com/podman-desktop/podman-desktop/pull/12194) -- chore: update contributing guide to use lint-staged by @deboer-tim [#12193](https://github.com/podman-desktop/podman-desktop/pull/12193) -- chore: use duration for image age column by @deboer-tim [#12177](https://github.com/podman-desktop/podman-desktop/pull/12177) -- chore: use duration column for volume age by @deboer-tim [#12175](https://github.com/podman-desktop/podman-desktop/pull/12175) -- chore: image and manifest details navigation by @deboer-tim [#12170](https://github.com/podman-desktop/podman-desktop/pull/12170) -- chore: website - add custom github stars button by @cdrage [#12146](https://github.com/podman-desktop/podman-desktop/pull/12146) -- chore: website - add cncf community banner to front page by @cdrage [#12119](https://github.com/podman-desktop/podman-desktop/pull/12119) -- chore: changed naming of age column in container table by @gastoner [#12083](https://github.com/podman-desktop/podman-desktop/pull/12083) -- chore: migrate dropdown menu to svelte 5 by @cdrage [#12067](https://github.com/podman-desktop/podman-desktop/pull/12067) -- chore: migration of checkbox to svelte 5 by @bmahabirbu [#12055](https://github.com/podman-desktop/podman-desktop/pull/12055) -- chore: added selected option if dropdown was selected by @gastoner [#11991](https://github.com/podman-desktop/podman-desktop/pull/11991) -- chore: adds openExternal command to registered commands by @gastoner [#11990](https://github.com/podman-desktop/podman-desktop/pull/11990) -- chore: added overflow visible property to modal component by @gastoner [#11988](https://github.com/podman-desktop/podman-desktop/pull/11988) -- chore: introduced micromark button image with command argument by @gastoner [#11986](https://github.com/podman-desktop/podman-desktop/pull/11986) -- chore: remove no-explicit-any lib/container by @cdrage [#11477](https://github.com/podman-desktop/podman-desktop/pull/11477) - -### refactor 🛠️ - -- refactor: port Page to svelte5 by @feloy [#12561](https://github.com/podman-desktop/podman-desktop/pull/12561) -- refactor: port NavPage to svelte5 by @feloy [#12555](https://github.com/podman-desktop/podman-desktop/pull/12555) -- refactor(ui): migrate DurationColumn to svelte5 by @axel7083 [#12284](https://github.com/podman-desktop/podman-desktop/pull/12284) -- refactor(ui): migrate SimpleColumn to svelte5 by @axel7083 [#12283](https://github.com/podman-desktop/podman-desktop/pull/12283) -- refactor(StatusIcon): port forward replace Snippet with Component by @axel7083 [#12228](https://github.com/podman-desktop/podman-desktop/pull/12228) -- refactor: moved registering commands to common file by @gastoner [#12207](https://github.com/podman-desktop/podman-desktop/pull/12207) - -### docs 📖 - -- docs(website): added a procedure to install Podman Desktop on RHEL10 by @shipsing [#12531](https://github.com/podman-desktop/podman-desktop/pull/12531) -- docs(website): documenting microshift cluster creation with the minc … by @shipsing [#12406](https://github.com/podman-desktop/podman-desktop/pull/12406) -- docs(website): updated the AI lab documentation by @shipsing [#12384](https://github.com/podman-desktop/podman-desktop/pull/12384) -- docs(podman): adding windows instructions by @axel7083 [#12355](https://github.com/podman-desktop/podman-desktop/pull/12355) -- docs(ai-lab): fix typo in index by @trya2l [#12345](https://github.com/podman-desktop/podman-desktop/pull/12345) -- docs(website): update the Kubernetes section by @shipsing [#12306](https://github.com/podman-desktop/podman-desktop/pull/12306) -- docs: add vscode blogpost by @Firewall [#12258](https://github.com/podman-desktop/podman-desktop/pull/12258) -- docs(website): added a troubleshooting section by @shipsing [#12247](https://github.com/podman-desktop/podman-desktop/pull/12247) -- docs(website): removed an outdated screenshot from the troubleshootin… by @shipsing [#12244](https://github.com/podman-desktop/podman-desktop/pull/12244) -- docs: add 1.18 release notes by @SoniaSandler [#12226](https://github.com/podman-desktop/podman-desktop/pull/12226) -- docs(website): moved the compose blog to the tutorial section by @shipsing [#12157](https://github.com/podman-desktop/podman-desktop/pull/12157) -- docs(website): updated the docker compatibility section based on late… by @shipsing [#11803](https://github.com/podman-desktop/podman-desktop/pull/11803) - -### test 🚦 - -- chore(test): create podman machine using env var settings by @cbr7 [#12480](https://github.com/podman-desktop/podman-desktop/pull/12480) -- chore(test): parameterize runner close timeout by @amisskii [#12470](https://github.com/podman-desktop/podman-desktop/pull/12470) -- chore(test): accept degraded state as valid by @cbr7 [#12407](https://github.com/podman-desktop/podman-desktop/pull/12407) -- chore(test): wait for extension to be deleted by @cbr7 [#12396](https://github.com/podman-desktop/podman-desktop/pull/12396) -- chore(test): increase timeout for pushing image to kind cluster by @amisskii [#12394](https://github.com/podman-desktop/podman-desktop/pull/12394) -- chore(test): wait for button and ensure button press by @cbr7 [#12385](https://github.com/podman-desktop/podman-desktop/pull/12385) -- chore(test): don't create machine on linux by @cbr7 [#12342](https://github.com/podman-desktop/podman-desktop/pull/12342) -- chore(test): volume e2e test wait for navigation to complete by @cbr7 [#12339](https://github.com/podman-desktop/podman-desktop/pull/12339) -- chore(test): fix some flakyness in image e2e test suite by @cbr7 [#12336](https://github.com/podman-desktop/podman-desktop/pull/12336) -- chore(test): skip test suite until bug is fixed by @cbr7 [#12335](https://github.com/podman-desktop/podman-desktop/pull/12335) -- chore(test): ensure default podman machine is restarted at the end of test suite by @cbr7 [#12326](https://github.com/podman-desktop/podman-desktop/pull/12326) -- chore(test): update handling for error message by @cbr7 [#12324](https://github.com/podman-desktop/podman-desktop/pull/12324) -- chore(test): now asserting on correct element by @cbr7 [#12323](https://github.com/podman-desktop/podman-desktop/pull/12323) -- chore(test): try to create and start machine before waiting by @cbr7 [#12322](https://github.com/podman-desktop/podman-desktop/pull/12322) -- chore(test): moving to polling assert for better handling by @cbr7 [#12320](https://github.com/podman-desktop/podman-desktop/pull/12320) -- chore(test): update running providers assertion in troubleshooting by @odockal [#12314](https://github.com/podman-desktop/podman-desktop/pull/12314) -- chore(test): fix some tests that were failing when other tests failed by @cbr7 [#12313](https://github.com/podman-desktop/podman-desktop/pull/12313) -- chore(test): fix flaky behaviours in e2e tests by @cbr7 [#12310](https://github.com/podman-desktop/podman-desktop/pull/12310) -- chore(test): new get extension version method by @amisskii [#12248](https://github.com/podman-desktop/podman-desktop/pull/12248) -- fix(tests): do not manually render svelte snippets by @axel7083 [#12208](https://github.com/podman-desktop/podman-desktop/pull/12208) -- chore(test): parametrize rootless resource machine test for all machine types by @xbabalov [#11954](https://github.com/podman-desktop/podman-desktop/pull/11954) -- chore(test): add ARIA labels to Check tab content on Image details page by @rostalan [#11811](https://github.com/podman-desktop/podman-desktop/pull/11811) - -### feature 💡 - -- feat: added duplicating of contexts by @gastoner [#12463](https://github.com/podman-desktop/podman-desktop/pull/12463) -- feat: added new footer markdown property to message box by @gastoner [#12432](https://github.com/podman-desktop/podman-desktop/pull/12432) -- feat: added editing of contexts by @gastoner [#12415](https://github.com/podman-desktop/podman-desktop/pull/12415) -- feat: download linux binary on the docs by @DIGIX666 [#12286](https://github.com/podman-desktop/podman-desktop/pull/12286) diff --git a/website/blog/2025-06-02-podman-desktop-core.md b/website/blog/2025-06-02-podman-desktop-core.md deleted file mode 100644 index 92d78d375c..0000000000 --- a/website/blog/2025-06-02-podman-desktop-core.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: Containers and Kubernetes development with Podman Desktop -description: A step-by-step guide for your containers and Kubernetes development with Podman Desktop -authors: [firewall] -tags: [podman-desktop, podman, development, container, tools] -hide_table_of_contents: false ---- - -In the world of modern software development, containers and Kubernetes are no longer optional; they are essential. That’s where Podman Desktop comes in, your ultimate tool for building, managing, and deploying containers and Kubernetes clusters with ease and confidence. In this blogpost we will walk through a typical development workflow, creating and building a container and then testing it in a local Kubernetes cluster. Let’s dive in. - -## Building Containerized Applications with Podman Desktop - -First, let's start by building an application in a container with Podman Desktop. We need our application code and a Containerfile. For a simple application, your Containerfile might look something like: - -```Dockerfile -FROM docker.io/nginxinc/nginx-unprivileged - -COPY < - - - Simple NGINX Container - - -

    Hello from my Podman NGINX Container!

    -

    This content is being served by NGINX running in a Podman container.

    - - -EOF -``` - -To enhance security, we utilize the `nginx-unprivileged` image. This helps avoid root access, which is enforced by default in some Kubernetes distributions like OpenShift. The default NGINX image uses port 80, which is forbidden in rootless mode. - -Once your application and Containerfile are ready, Podman Desktop makes the build process straightforward: -![Build image](img/podman-desktop-core-blog/build-image.png) - -1. Navigate to the "Images" section -2. Select your Containerfile -3. Provide a name for your image (e.g. webserver) -4. Click "Build" - -After building your image, you can immediately run it with a single click, and your container will appear in the "Containers" list. - -![Start container](img/podman-desktop-core-blog/start-container.png) - -1. Find your container on the “Images” list -2. Click the “Run Image ▶️” button -3. Give your container a name we will call it: “webserver” - -### Container Management and Log Analysis - -Now that our webserver container is running we can inspect it in the UI -![View container details](img/podman-desktop-core-blog/container-details.png) - -We can click the Open Browser button to see the webserver in our browser: -![Webserver open in browser](img/podman-desktop-core-blog/nginx-in-container.png) - -Monitoring container logs is crucial for debugging and understanding application behavior. Let's use Podman Desktop to view the logs of the newly created container. - -1. Select your running container from the "Containers" list -2. Click on the "Logs" tab to view the container's logs -3. In the newest version of Podman Desktop you can also search in the logs\! - -![View container logs](img/podman-desktop-core-blog/container-logs.png) - -With that, we can continue the development process. We are able to rebuild our container when we have updated code. On top of that, we can share our Containerfile with our team who will be able to reproduce the exact same environment as us to build and test their code. - -## Working with Kubernetes and OpenShift - -Now that we have a development setup up and running, it’s time to get ready for production. In today's world, it is natural for us to move to Kubernetes. Having a locally running cluster, we are able to iterate quickly but still have an environment that is as close to production as possible. This aids in a smoother migration in the future. Podman Desktop is here to help you test and execute that migration. - -With [kind](https://kind.sigs.k8s.io/) or [minikube](https://minikube.sigs.k8s.io/docs/), we are able to have a locally running Kubernetes cluster in minutes. This will allow us to test our application in a Kubernetes environment. Kind comes installed together with Podman Desktop so you will be able to get started instantly. - -1. Start by navigating to the “Kubernetes” page -2. Click on the “Create new Kind cluster” button - -![Create kind cluster](img/podman-desktop-core-blog/create-kind-cluster.png) - -3. Click “Create” -4. Wait until the kind cluster gets created -5. Once the cluster is created, Podman Desktop will automatically switch your Kubernetes context to the new kind cluster. If you want to change your cluster, you can switch contexts in the statusbar. - -We now have a locally running Kubernetes cluster which we can explore on the Kubernetes dashboard. - -![Kubernetes dashboard](img/podman-desktop-core-blog/kubernetes-dashboard.png) - -This dashboard not only gives you an overview of your cluster but it also provides quick access to the different Kubernetes objects that exist in the cluster. - -### Creating the Pod on your local Kubernetes cluster - -With a running Kubernetes cluster, we can now create our pod in the Kubernetes cluster. Using Podman Desktop, we can convert our previously created container into a Pod in our kind cluster. - -First we have to push our image to our kind cluster. -![Push image to kind](img/podman-desktop-core-blog/push-image-to-kind.png) - -Now that the image is available we can use the Podman Desktop UI to Deploy our container to a Pod -![Deploy to Kubernetes](img/podman-desktop-core-blog/deploy-to-kubernetes.png) - -1. Navigate to the “Containers” section -2. On the webserver container, click the “Deploy to Kubernetes” button -3. Choose your kind cluster - -The conversion from containers to Kubernetes manifests is particularly valuable, eliminating the need to manually write YAML files for simple deployments. Podman Desktop adds `imagePullPolicy: IfNotPresent` to the generated Kubernetes YAML. This ensures that we use the image that we just pushed to the cluster is also the one we will use. Make sure to add this to your Kubernetes YAML or use a specific tag on your image to avoid the [default pull policy](https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting) (`Always`). - -## Monitoring Kubernetes Events and Resources - -Understanding what's happening in your Kubernetes cluster is essential for effective development. In the latest version of Podman Desktop, you can now check the events of your Kubernetes Pods in the UI. - -Let's check if our pod was created and running successfully. - -1. Navigate to the “Kubernetes” in the left-navigation -2. Click on the “Pods” sub-navigation -3. Click on the pods you just created -4. Scroll to the bottom and check the “Events” -5. We can see that our Pod was started and is running - -![View pod events](img/podman-desktop-core-blog/pod-details-events.png) - -We can also check out the “Logs” tab to see the logs of the running Pod. -![View pod logs](img/podman-desktop-core-blog/kubernetes-pod-logs.png) - -### Video walkthrough - - - -## Conclusion - -Podman Desktop significantly streamlines the container and Kubernetes development experience by providing: - -- A unified interface for building and managing containers -- Seamless integration with Kubernetes and OpenShift -- Powerful logging and debugging tools -- A bridge between local development and production Kubernetes environments - -Whether you're just starting with containers or managing complex Kubernetes deployments, Podman Desktop offers tools that simplify your workflow and increase productivity. As [a CNCF project](https://podman-desktop.io/blog/2024/11/14/podman-desktop-cncf), it continues to evolve with the needs of the cloud-native community, making it an increasingly valuable tool in any developer's toolkit. - -Try Podman Desktop today and experience how it can transform your container, Kubernetes and OpenShift workflows\! diff --git a/website/blog/2025-07-07-release-1.20.md b/website/blog/2025-07-07-release-1.20.md deleted file mode 100644 index 92da62bd46..0000000000 --- a/website/blog/2025-07-07-release-1.20.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -title: Podman Desktop 1.20 Release -description: Podman Desktop 1.20 has been released! -slug: podman-desktop-release-1.20 -authors: [vzhukovs] -tags: [podman-desktop, release, kubernetes, openshift] -hide_table_of_contents: false -image: /img/blog/podman-desktop-release-1.20/banner.png ---- - -import ThemedImage from '@theme/ThemedImage'; - -Podman Desktop 1.20 Release! 🎉 - -![podman-desktop-hero-1.20](/img/blog/podman-desktop-release-1.20/banner.png) - -Podman Desktop 1.20 is now available. [Click here to download it](/downloads)! - -This release brings exciting new features and improvements: - -- **Start all containers in bulk**: A new bulk-run button allows you to start multiple selected containers at once, saving time when launching your container stack. -- **Switching users and clusters**: Seamlessly switch your active Kubernetes cluster and user context from within Podman Desktop, making multi-cluster workflows much easier. -- **Search by description in extension list**: Find extensions faster by searching not just by name but also through keywords in their descriptions. -- **Update providers from the Resources page**: Easily update your container engines or Kubernetes providers right from the Resources page for a more streamlined upgrade process. -- **Local Extension Development Mode**: The production binary now lets you load and live-test local extensions after enabling Development Mode, eliminating the need to run Podman Desktop in dev/watch mode. -- **Instantly stop live container logs**: Now you can stop live log streaming from containers without closing the logs window. This gives you more control over resource usage and debugging workflows. -- **New Community page website**: A new Community page on our website helps you connect with fellow users, find resources, and get involved with Podman Desktop’s development. - ---- - -## Release details 🔍 - -### Bulk Start All Containers - -If you have several containers to run, you no longer need to start each one individually. Podman Desktop now provides a **“Run All”** button on the Containers view to launch all selected containers with a single click. This makes it much more convenient to bring up multiple services or an entire application stack in one go. Already-running containers are intelligently skipped, so the bulk start action focuses on only starting the ones that are stopped. - - -
    - -### Switch Users and Clusters - -Podman Desktop’s Kubernetes integration now supports easy context switching between different clusters and user accounts. You can change your active Kubernetes cluster and user directly through the application UI without editing config files or using external CLI commands. This is especially useful for developers working with multiple environments – for example, switching from a development cluster to a production cluster (or using different user credentials) is now just a few clicks. It streamlines multi-cluster workflows by letting you hop between contexts seamlessly inside Podman Desktop. - - -
    - -### Extension Search by Description - -The extension marketplace search has been improved to help you discover tools more easily. Previously, searching for extensions only matched against extension names. In Podman Desktop 1.20, the search bar also looks at extension descriptions. This means you can enter a keyword related to an extension’s functionality or topic, and the relevant extensions will appear even if that keyword isn’t in the extension’s name. It’s now much easier to find extensions by what they do, not just what they’re called. - - -
    - -### Provider Updates from Resources Page - -Managing your container and Kubernetes providers just got easier. The **Resources** page in Podman Desktop (which lists your container engines and Kubernetes environments) now allows direct updates for those providers. If a new version of a provider – say Podman, Docker, or a Kubernetes VM – is available, you can trigger the upgrade right from Podman Desktop’s interface. No need to manually run update commands or leave the app; a quick click keeps your development environment up-to-date with the latest releases. - - -
    - -### Local Extension Development Mode - -Extension authors can now toggle Development Mode in Preferences and add a local folder from the new Local Extensions tab. Podman Desktop will watch the folder, load the extension, and keep it tracked across restarts, exactly as it behaves in production. You can start, stop, or untrack the extension directly from the UI, shortening the feedback loop for building and debugging add-ons without extra CLI flags or a special dev build. - - -
    - -### Instantly stop live container logs - -The container logs viewer can now be canceled mid-stream, allowing you to stop tailing logs when they are no longer needed. -Previously, once a container’s logs were opened, the output would continue streaming until the logs window was closed. With this update, an ongoing log stream can be interrupted via a cancel action without closing the logs pane, giving you more control over log monitoring. This improvement helps avoid redundant log output and unnecessary resource usage by letting log streaming be halted on demand. - -### New Community Page - -We’ve launched a new **Community** page on the Podman Desktop website to better connect our users and contributors. This page serves as a central hub for all community-related resources: you can find links to join our Discord channel, participate in GitHub discussions, follow us on social platforms, and more. It also highlights ways to contribute to the project, whether by reporting issues, writing code, or improving documentation. Whether you want to share feedback, meet other Podman Desktop enthusiasts, or get involved in development, the Community page is the place to start. - ---- - -## Community thank you - -🎉 We’d like to say a big thank you to everyone who helped to make Podman Desktop even better. In this -release we received pull requests from the following people: - -- [statickidz](https://github.com/statickidz) in [fix: duplicate title on homepage](https://github.com/podman-desktop/podman-desktop/pull/12802) - -- [jiridostal](https://github.com/jiridostal) in [fix: logs filename has undefined extension](https://github.com/podman-desktop/podman-desktop/pull/12774) - -- [omertuc](https://github.com/omertuc) in [feat(extension): add search by description in extension list](https://github.com/podman-desktop/podman-desktop/pull/12519) - -- [wngtk](https://github.com/wngtk) in [docs(windows): update uninstall instructions](https://github.com/podman-desktop/podman-desktop/pull/12349) - ---- - -## Final notes - -The complete list of issues fixed in this release is available [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.20.0) and [here](https://github.com/containers/podman-desktop/issues?q=is%3Aclosed+milestone%3A1.20.1). - -Get the latest release from the [Downloads](/downloads) section of the website and boost your development journey with Podman Desktop. Additionally, visit the [GitHub repository](https://github.com/containers/podman-desktop) and see how you can help us make Podman Desktop better. - -## Detailed release changelog - -### feat 💡 - -- feat: adds dropdown option to message box by @gastoner [#13049](https://github.com/podman-desktop/podman-desktop/pull/13049) -- feat(table): adding key props by @axel7083 [#12994](https://github.com/podman-desktop/podman-desktop/pull/12994) -- feat(table): adding accessibility labels to collapse button by @axel7083 [#12979](https://github.com/podman-desktop/podman-desktop/pull/12979) -- feat: add badge for in-development extensions by @benoitf [#12951](https://github.com/podman-desktop/podman-desktop/pull/12951) -- feat: new community page website by @cdrage [#12748](https://github.com/podman-desktop/podman-desktop/pull/12748) -- feat: allow provider update from resources page by @SoniaSandler [#12729](https://github.com/podman-desktop/podman-desktop/pull/12729) -- feat(extension-api): support cancellation token for pull image by @axel7083 [#12706](https://github.com/podman-desktop/podman-desktop/pull/12706) -- feat(ui): created universal icon by @gastoner [#12677](https://github.com/podman-desktop/podman-desktop/pull/12677) -- feat: add button to start all containers in bulk by @MarsKubeX [#12646](https://github.com/podman-desktop/podman-desktop/pull/12646) -- feat: support fine-grained window events for EventStore by @feloy [#12636](https://github.com/podman-desktop/podman-desktop/pull/12636) -- feat: make window.logsContainer cancellable by @feloy [#12624](https://github.com/podman-desktop/podman-desktop/pull/12624) -- feat(extension): add search by description in extension list by @omertuc [#12519](https://github.com/podman-desktop/podman-desktop/pull/12519) -- feat: added switching users and clusters by @gastoner [#12445](https://github.com/podman-desktop/podman-desktop/pull/12445) -- feat: allow to use production binary to develop extensions by @benoitf [#10731](https://github.com/podman-desktop/podman-desktop/pull/10731) - -### fix 🔨 - -- fix: pod name not working in k8s by @eqqe [#13066](https://github.com/podman-desktop/podman-desktop/pull/13066) -- fix: do not search full path executable by @feloy [#13060](https://github.com/podman-desktop/podman-desktop/pull/13060) -- fix: don't show table when there are no running container engines by @SoniaSandler [#13051](https://github.com/podman-desktop/podman-desktop/pull/13051) -- fix: increase timeout waiting kubeconfig creation by @feloy [#13050](https://github.com/podman-desktop/podman-desktop/pull/13050) -- fix(ContainerList): same named container group should have individual groups by @axel7083 [#13002](https://github.com/podman-desktop/podman-desktop/pull/13002) -- fix: change podman machine stream close function context by @SoniaSandler [#12982](https://github.com/podman-desktop/podman-desktop/pull/12982) -- fix: make container or vm connection terminal responsive on start and restart by @SoniaSandler [#12981](https://github.com/podman-desktop/podman-desktop/pull/12981) -- fix: test errors after migration to vitest v 3.2.x by @dgolovin [#12965](https://github.com/podman-desktop/podman-desktop/pull/12965) -- fix: add better checks to detect Podman Desktop extension in dev mode by @benoitf [#12954](https://github.com/podman-desktop/podman-desktop/pull/12954) -- fix: update docker compatibility link in compose onboarding by @SoniaSandler [#12923](https://github.com/podman-desktop/podman-desktop/pull/12923) -- fix(patch): patched kubernetes/client-node package by @gastoner [#12919](https://github.com/podman-desktop/podman-desktop/pull/12919) -- fix(frontend): group containers by groupId by @axel7083 [#12915](https://github.com/podman-desktop/podman-desktop/pull/12915) -- fix: change separators for fine grained events by @feloy [#12914](https://github.com/podman-desktop/podman-desktop/pull/12914) -- fix: display VM connections in status bar by @feloy [#12910](https://github.com/podman-desktop/podman-desktop/pull/12910) -- fix: clean up electron-updater cache when it is not needed anymore by @dgolovin [#12870](https://github.com/podman-desktop/podman-desktop/pull/12870) -- fix: dispatch resources counts events when context is offline by @feloy [#12834](https://github.com/podman-desktop/podman-desktop/pull/12834) -- fix(frontend): container list table display race condition by @axel7083 [#12833](https://github.com/podman-desktop/podman-desktop/pull/12833) -- fix: avoid to send status field when patching a resource by @benoitf [#12810](https://github.com/podman-desktop/podman-desktop/pull/12810) -- fix: refresh providers when vm connections are registered/unregistered by @feloy [#12805](https://github.com/podman-desktop/podman-desktop/pull/12805) -- fix: duplicate title on homepage by @statickidz [#12802](https://github.com/podman-desktop/podman-desktop/pull/12802) -- fix: send container connection status to extension API listeners by @jeffmaury [#12794](https://github.com/podman-desktop/podman-desktop/pull/12794) -- fix: remove mistakenly placed archive, update .gitignore by @odockal [#12793](https://github.com/podman-desktop/podman-desktop/pull/12793) -- fix: remove full access to d-bus and add missing --talk-name options by @dgolovin [#12778](https://github.com/podman-desktop/podman-desktop/pull/12778) -- fix: flaky test in BuildImageFromContainerfile.spec.ts by @dgolovin [#12777](https://github.com/podman-desktop/podman-desktop/pull/12777) -- fix: logs filename has undefined extension by @jiridostal [#12774](https://github.com/podman-desktop/podman-desktop/pull/12774) -- fix: minimize on startup plist for macOS by @cblecker [#12768](https://github.com/podman-desktop/podman-desktop/pull/12768) -- fix: error during extension install overlaps with other components by @jiridostal [#12741](https://github.com/podman-desktop/podman-desktop/pull/12741) -- fix: wrong text in component props sending wrong telemetry data by @MarsKubeX [#12737](https://github.com/podman-desktop/podman-desktop/pull/12737) -- fix: wrong margin in the update available modal by @MarsKubeX [#12728](https://github.com/podman-desktop/podman-desktop/pull/12728) -- fix: terminal prompt duplication after saved output restored by @dgolovin [#12725](https://github.com/podman-desktop/podman-desktop/pull/12725) -- fix: clear caches for all resources when one informer is offline by @feloy [#12714](https://github.com/podman-desktop/podman-desktop/pull/12714) -- fix: fine-grained configuration-changed by @feloy [#12700](https://github.com/podman-desktop/podman-desktop/pull/12700) -- fix: uncaught exceptions in ContainerListCompose.spec.ts by @dgolovin [#12681](https://github.com/podman-desktop/podman-desktop/pull/12681) -- fix: uncaught exceptions in ContainerList.spec.ts by @dgolovin [#12680](https://github.com/podman-desktop/podman-desktop/pull/12680) -- fix: update static image link by @SoniaSandler [#12651](https://github.com/podman-desktop/podman-desktop/pull/12651) -- fix: addressed uninstall version error for kubectl installed via onboard by @bmahabirbu [#12426](https://github.com/podman-desktop/podman-desktop/pull/12426) -- fix: make image build cancellable by @dgolovin [#12261](https://github.com/podman-desktop/podman-desktop/pull/12261) -- fix(ci): perform update of ubuntu packages earlier (backport #13177) by @mergify[bot] in [#13181](https://github.com/podman-desktop/podman-desktop/pull/13181) -- revert: 12870 (backport #13152) by @mergify[bot] in [#13180](https://github.com/podman-desktop/podman-desktop/pull/13180) - -### chore ✅ - -- chore: use aggregating way to report activateExtension event by @benoitf [#13071](https://github.com/podman-desktop/podman-desktop/pull/13071) -- chore: use the new aggregate method to track createProviders by @benoitf [#13064](https://github.com/podman-desktop/podman-desktop/pull/13064) -- chore: add an aggregate method for telemetry by @benoitf [#13063](https://github.com/podman-desktop/podman-desktop/pull/13063) -- chore: bring inversify bindings by @benoitf [#13062](https://github.com/podman-desktop/podman-desktop/pull/13062) -- chore(tray): update existing tray icons by @vancura [#13057](https://github.com/podman-desktop/podman-desktop/pull/13057) -- chore: add decorators/annotations for DI of inversify by @benoitf [#13043](https://github.com/podman-desktop/podman-desktop/pull/13043) -- chore: update storybook to v9 by @benoitf [#13037](https://github.com/podman-desktop/podman-desktop/pull/13037) -- chore: modified link to the community meeting recordings by @rujutashinde [#12997](https://github.com/podman-desktop/podman-desktop/pull/12997) -- chore(deps-dev): switch to prettier 3.6.2 by @jeffmaury [#12995](https://github.com/podman-desktop/podman-desktop/pull/12995) -- chore: move disposable group to API package by @benoitf [#12992](https://github.com/podman-desktop/podman-desktop/pull/12992) -- chore: introduce inversify library by @benoitf [#12978](https://github.com/podman-desktop/podman-desktop/pull/12978) -- chore: moved mockclear to beforeeach in containerList.spec.ts by @MarsKubeX [#12963](https://github.com/podman-desktop/podman-desktop/pull/12963) -- chore: update podman to v5.5.2 by @benoitf [#12960](https://github.com/podman-desktop/podman-desktop/pull/12960) -- chore: fix typo by @benoitf [#12955](https://github.com/podman-desktop/podman-desktop/pull/12955) -- chore: allow to remove extension in dev mode without error by @benoitf [#12953](https://github.com/podman-desktop/podman-desktop/pull/12953) -- chore: add color for devMode by @benoitf [#12942](https://github.com/podman-desktop/podman-desktop/pull/12942) -- chore: set oneClick false and perMachine false in nsis by @cdrage [#12941](https://github.com/podman-desktop/podman-desktop/pull/12941) -- chore: add devMode inside extension metadata by @benoitf [#12940](https://github.com/podman-desktop/podman-desktop/pull/12940) -- chore(podman): remove promisify usage for `node:fs` function by @axel7083 [#12906](https://github.com/podman-desktop/podman-desktop/pull/12906) -- chore: removed svelte check warning by @MarsKubeX [#12892](https://github.com/podman-desktop/podman-desktop/pull/12892) -- chore(core): remove unnecessary dns configuration by @axel7083 [#12891](https://github.com/podman-desktop/podman-desktop/pull/12891) -- chore: upgrade biomejs to v2 by @benoitf [#12885](https://github.com/podman-desktop/podman-desktop/pull/12885) -- chore: notify development folder instance when extension id is loaded/removed from extension loader by @benoitf [#12875](https://github.com/podman-desktop/podman-desktop/pull/12875) -- chore: stop sending kubernetesExecIntoContainer event by @MarsKubeX [#12873](https://github.com/podman-desktop/podman-desktop/pull/12873) -- chore: expose a method from extension loader: ensureExtensionIsEnabled by @benoitf [#12871](https://github.com/podman-desktop/podman-desktop/pull/12871) -- chore(website): modified footer color for links in dark mode by @rujutashinde [#12860](https://github.com/podman-desktop/podman-desktop/pull/12860) -- chore: fix the .github issue templates to add project ids by @rujutashinde [#12859](https://github.com/podman-desktop/podman-desktop/pull/12859) -- chore(issue-template): adding 1.19.2 to bug_report.yml by @axel7083 [#12844](https://github.com/podman-desktop/podman-desktop/pull/12844) -- chore: addProviderMenuItem - add provider if it does not exist by @cdrage [#12841](https://github.com/podman-desktop/podman-desktop/pull/12841) -- chore: remove error prone metadata for k8 creation from YAML by @bmahabirbu [#12837](https://github.com/podman-desktop/podman-desktop/pull/12837) -- chore: correctly type guard e.target for micromark listener by @cdrage [#12818](https://github.com/podman-desktop/podman-desktop/pull/12818) -- chore: modified github templates for issues to add projects by @rujutashinde [#12796](https://github.com/podman-desktop/podman-desktop/pull/12796) -- chore(e2e): change expected img num in stress test depending on OS by @danivilla9 [#12789](https://github.com/podman-desktop/podman-desktop/pull/12789) -- chore: update podman to v5.5.1 by @benoitf [#12762](https://github.com/podman-desktop/podman-desktop/pull/12762) -- chore: track all changes from the extension by @benoitf [#12743](https://github.com/podman-desktop/podman-desktop/pull/12743) -- chore: remove notarize option from electron builder config by @odockal [#12719](https://github.com/podman-desktop/podman-desktop/pull/12719) -- chore(e2e): add scrollintoviewifneeded to ui-stress-test by @danivilla9 [#12689](https://github.com/podman-desktop/podman-desktop/pull/12689) -- chore(e2e): refactor extension-installation-smoke test case by @danivilla9 [#12665](https://github.com/podman-desktop/podman-desktop/pull/12665) -- chore: update social networks links by @vancura [#12662](https://github.com/podman-desktop/podman-desktop/pull/12662) -- chore: bump svelte to 5.28.3 by @feloy [#12650](https://github.com/podman-desktop/podman-desktop/pull/12650) -- chore(workflows): set permission for publish-website-pr-cloudflare.yaml by @axel7083 [#12631](https://github.com/podman-desktop/podman-desktop/pull/12631) -- chore(workflows): set permission for e2e-main.yaml by @axel7083 [#12630](https://github.com/podman-desktop/podman-desktop/pull/12630) -- chore(workflows): set permission for e2e-kubernetes-main.yaml by @axel7083 [#12629](https://github.com/podman-desktop/podman-desktop/pull/12629) -- chore(workflows): set permission for downloads-count.yaml by @axel7083 [#12625](https://github.com/podman-desktop/podman-desktop/pull/12625) -- chore: add generate sbom job in release workflow by @SoniaSandler [#12603](https://github.com/podman-desktop/podman-desktop/pull/12603) -- chore: update Flatpak banner by @Eonfge [#12594](https://github.com/podman-desktop/podman-desktop/pull/12594) -- chore: send watch events only after we're starting the watch by @benoitf [#12590](https://github.com/podman-desktop/podman-desktop/pull/12590) -- chore: add border with more contrast in the table component by @SoniaSandler [#12583](https://github.com/podman-desktop/podman-desktop/pull/12583) -- chore: make the file items in preferences clearable by @SoniaSandler [#12473](https://github.com/podman-desktop/podman-desktop/pull/12473) -- chore(deps): bump electron-builder to v26 by @axel7083 [#12351](https://github.com/podman-desktop/podman-desktop/pull/12351) -- chore: remove no-explicit-any from dialogs by @cdrage [#11480](https://github.com/podman-desktop/podman-desktop/pull/11480) - -### refactor 🛠️ - -- refactor(mock): simplify how to call a method on a mock object by @benoitf [#13072](https://github.com/podman-desktop/podman-desktop/pull/13072) -- refactor: move message box interfaces to api package by @benoitf [#13007](https://github.com/podman-desktop/podman-desktop/pull/13007) -- refactor: extract status bar api to api package by @benoitf [#13006](https://github.com/podman-desktop/podman-desktop/pull/13006) -- refactor: move menu api to the API package by @benoitf [#13005](https://github.com/podman-desktop/podman-desktop/pull/13005) -- refactor: move interfaces of configuration to the api package by @benoitf [#12999](https://github.com/podman-desktop/podman-desktop/pull/12999) -- refactor: move event to API side by @benoitf [#12996](https://github.com/podman-desktop/podman-desktop/pull/12996) -- refactor(ui/table): replace filter#map#flat chain by reduce by @axel7083 [#12967](https://github.com/podman-desktop/podman-desktop/pull/12967) -- refactor(podman): move podman-install.ts to proper folder by @axel7083 [#12936](https://github.com/podman-desktop/podman-desktop/pull/12936) -- refactor(table): remove unnecessary binding by @axel7083 [#12934](https://github.com/podman-desktop/podman-desktop/pull/12934) -- refactor(types): adding generics to Table by @axel7083 [#12933](https://github.com/podman-desktop/podman-desktop/pull/12933) -- refactor(podman): extract PodmanInfo to dedicated file by @axel7083 [#12911](https://github.com/podman-desktop/podman-desktop/pull/12911) -- refactor(core): move default protocol configuration to Main by @axel7083 [#12905](https://github.com/podman-desktop/podman-desktop/pull/12905) -- refactor(podman): extract MacOSInstaller to a dedicated file by @axel7083 [#12904](https://github.com/podman-desktop/podman-desktop/pull/12904) -- refactor(podman): extract `WinInstaller` to a dedicated file by @axel7083 [#12899](https://github.com/podman-desktop/podman-desktop/pull/12899) -- refactor(frontend): make ContainerGroupPartInfoUI id property non-nullable by @axel7083 [#12896](https://github.com/podman-desktop/podman-desktop/pull/12896) -- refactor(podman): extract getBundledPodmanVersion interface to a file by @axel7083 [#12894](https://github.com/podman-desktop/podman-desktop/pull/12894) -- refactor(podman): extract `Installer` interface to dedicated file by @axel7083 [#12887](https://github.com/podman-desktop/podman-desktop/pull/12887) -- refactor: move filesystem tree construct to backend by @feloy [#12872](https://github.com/podman-desktop/podman-desktop/pull/12872) -- refactor(podman): extract BaseInstaller to a dedicated file by @axel7083 [#12811](https://github.com/podman-desktop/podman-desktop/pull/12811) -- refactor(podman): extract WinBitCheck to dedicated file by @axel7083 [#12712](https://github.com/podman-desktop/podman-desktop/pull/12712) -- refactor(podman): extract WinVersionCheck to a dedicated file by @axel7083 [#12705](https://github.com/podman-desktop/podman-desktop/pull/12705) -- refactor(podman): extract WinMemoryCheck class to dedicated file by @axel7083 [#12702](https://github.com/podman-desktop/podman-desktop/pull/12702) -- refactor(podman): extract WSL2Check class to dedicated file by @axel7083 [#12699](https://github.com/podman-desktop/podman-desktop/pull/12699) -- refactor(ui): use new icon component in ui svelte lib by @gastoner [#12678](https://github.com/podman-desktop/podman-desktop/pull/12678) -- refactor(extension/podman): extracting WSLVersionCheck by @axel7083 [#12664](https://github.com/podman-desktop/podman-desktop/pull/12664) - -### test 🚦 - -- chore(test): reset podman machine on failure by @cbr7 [#13034](https://github.com/podman-desktop/podman-desktop/pull/13034) -- chore(test): improve auth-utility playwright codebase by @odockal [#13022](https://github.com/podman-desktop/podman-desktop/pull/13022) -- chore(test): clean hanging podman machine in e2e test by @cbr7 [#13019](https://github.com/podman-desktop/podman-desktop/pull/13019) -- chore(test): create exception to check for update test by @cbr7 [#12993](https://github.com/podman-desktop/podman-desktop/pull/12993) -- chore(test): navigation takes longer on cicd by @cbr7 [#12991](https://github.com/podman-desktop/podman-desktop/pull/12991) -- chore(test): fix insufficient assert timeout by @cbr7 [#12977](https://github.com/podman-desktop/podman-desktop/pull/12977) -- chore(test): add timeout param to method call by @cbr7 [#12966](https://github.com/podman-desktop/podman-desktop/pull/12966) -- chore(test): throw error when needed for correct message by @cbr7 [#12964](https://github.com/podman-desktop/podman-desktop/pull/12964) -- chore(test): get error thrown in case of failure by @cbr7 [#12961](https://github.com/podman-desktop/podman-desktop/pull/12961) -- chore(test): wait for full page load before actions by @cbr7 [#12938](https://github.com/podman-desktop/podman-desktop/pull/12938) -- chore(test): increase robustness in prune containers e2e test by @cbr7 [#12922](https://github.com/podman-desktop/podman-desktop/pull/12922) -- chore(test): install ingress controller on Kind cluster only if needed by @amisskii [#12839](https://github.com/podman-desktop/podman-desktop/pull/12839) -- chore(test): increase timeout for cicd by @cbr7 [#12787](https://github.com/podman-desktop/podman-desktop/pull/12787) -- fix(tests): fix KubernetesTerminal flaky test by @dgolovin [#12780](https://github.com/podman-desktop/podman-desktop/pull/12780) -- fix(tests): flaky Typeahead.spec.ts by @dgolovin [#12779](https://github.com/podman-desktop/podman-desktop/pull/12779) -- chore(test): remove race promise by @cbr7 [#12752](https://github.com/podman-desktop/podman-desktop/pull/12752) -- chore(test): share e2e tests authentication functionality by @odockal [#12704](https://github.com/podman-desktop/podman-desktop/pull/12704) -- chore(test): use installed electron binary for external e2e tests by @odockal [#12688](https://github.com/podman-desktop/podman-desktop/pull/12688) -- chore(test): add openshift docker e2e test by @cbr7 [#12676](https://github.com/podman-desktop/podman-desktop/pull/12676) -- chore(test): Stabilize Kubernetes e2e tests on Windows CI by @amisskii [#12554](https://github.com/podman-desktop/podman-desktop/pull/12554) -- test(e2e): push image into kubernetes cluster and reuse it with a pod by @danivilla9 [#12427](https://github.com/podman-desktop/podman-desktop/pull/12427) -- chore(test): Add status bar provider tests by @xbabalov [#12352](https://github.com/podman-desktop/podman-desktop/pull/12352) - -### docs 📖 - -- docs: updated procedural steps in the install in restricted environme… by @shipsing [#12949](https://github.com/podman-desktop/podman-desktop/pull/12949) -- docs: updated the procedural steps based on latest changes by @shipsing [#12907](https://github.com/podman-desktop/podman-desktop/pull/12907) -- docs: removed a blog that no longer works as expected by @shipsing [#12855](https://github.com/podman-desktop/podman-desktop/pull/12855) -- docs(website): updated the tutorial section by @shipsing [#12763](https://github.com/podman-desktop/podman-desktop/pull/12763) -- docs(website): added a procedure to manage a kube context by @shipsing [#12750](https://github.com/podman-desktop/podman-desktop/pull/12750) -- docs(website): fixed a formatting issue by @shipsing [#12672](https://github.com/podman-desktop/podman-desktop/pull/12672) -- docs(website): release note 1.19 by @axel7083 [#12602](https://github.com/podman-desktop/podman-desktop/pull/12602) -- docs(website): added details to customize the UI on the discover PD page by @shipsing [#12575](https://github.com/podman-desktop/podman-desktop/pull/12575) -- docs(website): add podman desktop core blog by @Firewall [#12497](https://github.com/podman-desktop/podman-desktop/pull/12497) -- docs: add Podman AI Lab OpenVINO blog by @jeffmaury [#12496](https://github.com/podman-desktop/podman-desktop/pull/12496) -- docs(windows): update uninstall instructions by @wngtk [#12349](https://github.com/podman-desktop/podman-desktop/pull/12349) - -### ci 🔁 - -- ci: completely hide github button for argos by @cdrage [#12596](https://github.com/podman-desktop/podman-desktop/pull/12596) diff --git a/website/blog/authors.yml b/website/blog/authors.yml deleted file mode 100644 index 668495e90a..0000000000 --- a/website/blog/authors.yml +++ /dev/null @@ -1,107 +0,0 @@ -benoitf: - name: Florent Benoit - title: Principal Software Engineer - url: https://github.com/benoitf - image_url: https://github.com/benoitf.png - -deekay2310: - name: Dev Kumar - title: Technical PMM Intern - url: https://github.com/deekay2310 - image_url: https://github.com/deekay2310.png - -slemeur: - name: Stevan Le Meur - title: Product Manager - url: https://github.com/slemeur - image_url: https://github.com/slemeur.png - -deboer: - name: Tim deBoer - title: Architect - url: https://github.com/deboer-tim - image_url: https://github.com/deboer-tim.png - -themr0c: - name: Fabrice Flore-Thebault - title: Technical writer - url: https://github.com/themr0c - image_url: https://github.com/themr0c.png - -cdrage: - name: Charlie Drage - title: Software Engineer - url: https://github.com/cdrage - image_url: https://github.com/cdrage.png - -dgolovin: - name: Denis Golovin - title: Principal Software Engineer - url: https://github.com/cdrage - image_url: https://github.com/dgolovin.png - -jeffmaury: - name: Jeff Maury - title: Engineering Manager - url: https://github.com/jeffmaury - image_url: https://github.com/jeffmaury.png - -duffy: - name: Máirín Duffy - title: User Experience Designer - url: https://github.com/mairin - image_url: https://github.com/mairin.png - -cedric: - name: Cedric Clyburn - title: Developer Advocate - url: https://github.com/cedricclyburn - image_url: https://github.com/cedricclyburn.png - -SoniaSandler: - name: Sonia Sandler - title: Associate Software Engineer - url: https://github.com/SoniaSandler - image_url: https://github.com/SoniaSandler.png - -gastoner: - name: Evžen Gasta - title: Associate Software Engineer - url: https://github.com/gastoner - image_url: https://github.com/gastoner.png - -shipsing: - name: Shipra Singh - title: Tech writer - url: https://github.com/shipsing - image_url: https://github.com/shipsing.png - -meisele: - name: Markus Eisele - title: Product Marketing - url: https://github.com/myfear - image_url: https://github.com/myfear.png - -phmartin: - name: Philippe Martin - title: Principal Software Engineer - url: https://github.com/feloy - image_url: https://github.com/feloy.png - -axel7083: - name: Axel Stefanini - title: Software Engineer - url: https://github.com/axel7083 - image_url: https://github.com/axel7083.png - -firewall: - name: Matt Demyttenaere - title: Product Manager - url: https://github.com/firewall - image_url: https://github.com/firewall.png - -vzhukovs: - name: Vladyslav Zhukovskyi - title: Senior Software Engineer - url: https://github.com/vzhukovs - image_url: https://github.com/vzhukovs.png diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/5-things-to-know-for-a-docker-user-hero.png b/website/blog/img/5-things-to-know-for-a-docker-user/5-things-to-know-for-a-docker-user-hero.png deleted file mode 100644 index 52caa331f1..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/5-things-to-know-for-a-docker-user-hero.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/compose-containers-in-ui.png b/website/blog/img/5-things-to-know-for-a-docker-user/compose-containers-in-ui.png deleted file mode 100644 index 1984dcf8a6..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/compose-containers-in-ui.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/containers-from-pod.png b/website/blog/img/5-things-to-know-for-a-docker-user/containers-from-pod.png deleted file mode 100644 index 227a94bf59..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/containers-from-pod.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/kube-generate.png b/website/blog/img/5-things-to-know-for-a-docker-user/kube-generate.png deleted file mode 100644 index 82304a0b12..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/kube-generate.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/multiple-container-engines.png b/website/blog/img/5-things-to-know-for-a-docker-user/multiple-container-engines.png deleted file mode 100644 index 54cf826a4b..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/multiple-container-engines.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/play-kubernetes-yaml.png b/website/blog/img/5-things-to-know-for-a-docker-user/play-kubernetes-yaml.png deleted file mode 100644 index e0a82da40e..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/play-kubernetes-yaml.png and /dev/null differ diff --git a/website/blog/img/5-things-to-know-for-a-docker-user/pods-in-ui.png b/website/blog/img/5-things-to-know-for-a-docker-user/pods-in-ui.png deleted file mode 100644 index fa5bd74b05..0000000000 Binary files a/website/blog/img/5-things-to-know-for-a-docker-user/pods-in-ui.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/00-my-first-ai-app.png b/website/blog/img/ai-lab-first-app/00-my-first-ai-app.png deleted file mode 100644 index ea7b614c18..0000000000 Binary files a/website/blog/img/ai-lab-first-app/00-my-first-ai-app.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/01-ai-lab-demo-libkrun-machine.png b/website/blog/img/ai-lab-first-app/01-ai-lab-demo-libkrun-machine.png deleted file mode 100644 index 8ae6814306..0000000000 Binary files a/website/blog/img/ai-lab-first-app/01-ai-lab-demo-libkrun-machine.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/02-ai-lab-demo-gpu-preference.png b/website/blog/img/ai-lab-first-app/02-ai-lab-demo-gpu-preference.png deleted file mode 100644 index d0e80a3efc..0000000000 Binary files a/website/blog/img/ai-lab-first-app/02-ai-lab-demo-gpu-preference.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/03-ai-lab-demo-mistral-model-downloaded.png b/website/blog/img/ai-lab-first-app/03-ai-lab-demo-mistral-model-downloaded.png deleted file mode 100644 index dd414c90ea..0000000000 Binary files a/website/blog/img/ai-lab-first-app/03-ai-lab-demo-mistral-model-downloaded.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/04-ai-lab-demo-prompt-1.png b/website/blog/img/ai-lab-first-app/04-ai-lab-demo-prompt-1.png deleted file mode 100644 index 44ab370091..0000000000 Binary files a/website/blog/img/ai-lab-first-app/04-ai-lab-demo-prompt-1.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/05-ai-lab-demo-prompt-json.png b/website/blog/img/ai-lab-first-app/05-ai-lab-demo-prompt-json.png deleted file mode 100644 index 9cfcd48073..0000000000 Binary files a/website/blog/img/ai-lab-first-app/05-ai-lab-demo-prompt-json.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/06-ai-lab-demo-system-prompt.png b/website/blog/img/ai-lab-first-app/06-ai-lab-demo-system-prompt.png deleted file mode 100644 index c363fbe7c2..0000000000 Binary files a/website/blog/img/ai-lab-first-app/06-ai-lab-demo-system-prompt.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/07-ai-lab-demo-recipe-session.png b/website/blog/img/ai-lab-first-app/07-ai-lab-demo-recipe-session.png deleted file mode 100644 index 7078834f85..0000000000 Binary files a/website/blog/img/ai-lab-first-app/07-ai-lab-demo-recipe-session.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/08-ai-lab-demo-inference-server.png b/website/blog/img/ai-lab-first-app/08-ai-lab-demo-inference-server.png deleted file mode 100644 index 57510a8dd5..0000000000 Binary files a/website/blog/img/ai-lab-first-app/08-ai-lab-demo-inference-server.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/09-ai-lab-demo-my-app-local.png b/website/blog/img/ai-lab-first-app/09-ai-lab-demo-my-app-local.png deleted file mode 100644 index 85c89e3aa8..0000000000 Binary files a/website/blog/img/ai-lab-first-app/09-ai-lab-demo-my-app-local.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/10-ai-lab-demo-my-app-http-request.png b/website/blog/img/ai-lab-first-app/10-ai-lab-demo-my-app-http-request.png deleted file mode 100644 index a75bdf82ef..0000000000 Binary files a/website/blog/img/ai-lab-first-app/10-ai-lab-demo-my-app-http-request.png and /dev/null differ diff --git a/website/blog/img/ai-lab-first-app/banner.png b/website/blog/img/ai-lab-first-app/banner.png deleted file mode 100644 index db37db0523..0000000000 Binary files a/website/blog/img/ai-lab-first-app/banner.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/build.png b/website/blog/img/bootc-microshift/build.png deleted file mode 100644 index 98815191d2..0000000000 Binary files a/website/blog/img/bootc-microshift/build.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/build_bootc.png b/website/blog/img/bootc-microshift/build_bootc.png deleted file mode 100644 index 26faa022df..0000000000 Binary files a/website/blog/img/bootc-microshift/build_bootc.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/build_button.png b/website/blog/img/bootc-microshift/build_button.png deleted file mode 100644 index 26bfaf5c2b..0000000000 Binary files a/website/blog/img/bootc-microshift/build_button.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/cluster.png b/website/blog/img/bootc-microshift/cluster.png deleted file mode 100644 index 41eed328ed..0000000000 Binary files a/website/blog/img/bootc-microshift/cluster.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/install.png b/website/blog/img/bootc-microshift/install.png deleted file mode 100644 index c3889b3a4a..0000000000 Binary files a/website/blog/img/bootc-microshift/install.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/redhat_login.png b/website/blog/img/bootc-microshift/redhat_login.png deleted file mode 100644 index 5ecb94e4e3..0000000000 Binary files a/website/blog/img/bootc-microshift/redhat_login.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/redhat_login2.png b/website/blog/img/bootc-microshift/redhat_login2.png deleted file mode 100644 index a624b93332..0000000000 Binary files a/website/blog/img/bootc-microshift/redhat_login2.png and /dev/null differ diff --git a/website/blog/img/bootc-microshift/redhat_selkie.png b/website/blog/img/bootc-microshift/redhat_selkie.png deleted file mode 100644 index 523aa879ff..0000000000 Binary files a/website/blog/img/bootc-microshift/redhat_selkie.png and /dev/null differ diff --git a/website/blog/img/bootc-release-1.6/banner.png b/website/blog/img/bootc-release-1.6/banner.png deleted file mode 100644 index d5d1e67ccc..0000000000 Binary files a/website/blog/img/bootc-release-1.6/banner.png and /dev/null differ diff --git a/website/blog/img/bootc-release-1.6/build_config.png b/website/blog/img/bootc-release-1.6/build_config.png deleted file mode 100644 index 87ce8f3146..0000000000 Binary files a/website/blog/img/bootc-release-1.6/build_config.png and /dev/null differ diff --git a/website/blog/img/bootc-release-1.6/examples.png b/website/blog/img/bootc-release-1.6/examples.png deleted file mode 100644 index a2703296e6..0000000000 Binary files a/website/blog/img/bootc-release-1.6/examples.png and /dev/null differ diff --git a/website/blog/img/bootc-release-1.6/linux_vm.png b/website/blog/img/bootc-release-1.6/linux_vm.png deleted file mode 100644 index 18fb42e1a6..0000000000 Binary files a/website/blog/img/bootc-release-1.6/linux_vm.png and /dev/null differ diff --git "a/website/blog/img/building-a-kubernetes-application/Screenshot 2024-11-05 at 3.03.27\342\200\257PM.png" "b/website/blog/img/building-a-kubernetes-application/Screenshot 2024-11-05 at 3.03.27\342\200\257PM.png" deleted file mode 100644 index a0b90ba9c4..0000000000 Binary files "a/website/blog/img/building-a-kubernetes-application/Screenshot 2024-11-05 at 3.03.27\342\200\257PM.png" and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/apply-changes-to-cluster.png b/website/blog/img/building-a-kubernetes-application/apply-changes-to-cluster.png deleted file mode 100644 index ef7ce3d591..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/apply-changes-to-cluster.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/copying-containers-to-a-pod.png b/website/blog/img/building-a-kubernetes-application/copying-containers-to-a-pod.png deleted file mode 100644 index dac4eacb87..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/copying-containers-to-a-pod.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/creating-pod-from-containers.png b/website/blog/img/building-a-kubernetes-application/creating-pod-from-containers.png deleted file mode 100644 index 3764910e40..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/creating-pod-from-containers.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/enter-image-name.png b/website/blog/img/building-a-kubernetes-application/enter-image-name.png deleted file mode 100644 index 3678155c2b..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/enter-image-name.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/expose-the-service-locally.png b/website/blog/img/building-a-kubernetes-application/expose-the-service-locally.png deleted file mode 100644 index 0281c31ce3..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/expose-the-service-locally.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/frontend-app-running.png b/website/blog/img/building-a-kubernetes-application/frontend-app-running.png deleted file mode 100644 index 1793f415de..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/frontend-app-running.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/ingress-created.png b/website/blog/img/building-a-kubernetes-application/ingress-created.png deleted file mode 100644 index ce59c2a6f8..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/ingress-created.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/ingress-host-port.png b/website/blog/img/building-a-kubernetes-application/ingress-host-port.png deleted file mode 100644 index aa4a8d67fd..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/ingress-host-port.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/kube-manifest.png b/website/blog/img/building-a-kubernetes-application/kube-manifest.png deleted file mode 100644 index d64b9e46fc..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/kube-manifest.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/overflow-menu-icon.png b/website/blog/img/building-a-kubernetes-application/overflow-menu-icon.png deleted file mode 100644 index 059a8d425a..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/overflow-menu-icon.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/play-kubernetes-yaml.png b/website/blog/img/building-a-kubernetes-application/play-kubernetes-yaml.png deleted file mode 100644 index caff2dbcc5..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/play-kubernetes-yaml.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/pulling-an-image.png b/website/blog/img/building-a-kubernetes-application/pulling-an-image.png deleted file mode 100644 index bb3d58b79d..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/pulling-an-image.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/pulling-from-registry.png b/website/blog/img/building-a-kubernetes-application/pulling-from-registry.png deleted file mode 100644 index c9affb1fd0..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/pulling-from-registry.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/python-app-image.png b/website/blog/img/building-a-kubernetes-application/python-app-image.png deleted file mode 100644 index 735e93aa7b..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/python-app-image.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/redis-running-in-logs.png b/website/blog/img/building-a-kubernetes-application/redis-running-in-logs.png deleted file mode 100644 index 748f9f4eba..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/redis-running-in-logs.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/running-an-image.png b/website/blog/img/building-a-kubernetes-application/running-an-image.png deleted file mode 100644 index bf961ef9bc..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/running-an-image.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/running-application-locally.png b/website/blog/img/building-a-kubernetes-application/running-application-locally.png deleted file mode 100644 index df2e290ce5..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/running-application-locally.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/service-created.png b/website/blog/img/building-a-kubernetes-application/service-created.png deleted file mode 100644 index 6d710d30ce..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/service-created.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/starting-a-backend-container.png b/website/blog/img/building-a-kubernetes-application/starting-a-backend-container.png deleted file mode 100644 index 5125a2ebb8..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/starting-a-backend-container.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/starting-a-frontend-container.png b/website/blog/img/building-a-kubernetes-application/starting-a-frontend-container.png deleted file mode 100644 index 43869c8c58..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/starting-a-frontend-container.png and /dev/null differ diff --git a/website/blog/img/building-a-kubernetes-application/viewing-pod-details.png b/website/blog/img/building-a-kubernetes-application/viewing-pod-details.png deleted file mode 100644 index 51e4e09bf8..0000000000 Binary files a/website/blog/img/building-a-kubernetes-application/viewing-pod-details.png and /dev/null differ diff --git a/website/blog/img/cncf-projects/backstage.png b/website/blog/img/cncf-projects/backstage.png deleted file mode 100644 index 212fa4d65a..0000000000 Binary files a/website/blog/img/cncf-projects/backstage.png and /dev/null differ diff --git a/website/blog/img/cncf-projects/backstage_port.png b/website/blog/img/cncf-projects/backstage_port.png deleted file mode 100644 index 8e1ae1a40e..0000000000 Binary files a/website/blog/img/cncf-projects/backstage_port.png and /dev/null differ diff --git a/website/blog/img/cncf-projects/dapr.png b/website/blog/img/cncf-projects/dapr.png deleted file mode 100644 index fec77f75d1..0000000000 Binary files a/website/blog/img/cncf-projects/dapr.png and /dev/null differ diff --git a/website/blog/img/cncf-projects/dapr_browser.png b/website/blog/img/cncf-projects/dapr_browser.png deleted file mode 100644 index 1bec4b3da8..0000000000 Binary files a/website/blog/img/cncf-projects/dapr_browser.png and /dev/null differ diff --git a/website/blog/img/cncf-projects/plane.png b/website/blog/img/cncf-projects/plane.png deleted file mode 100644 index 043d9cc87a..0000000000 Binary files a/website/blog/img/cncf-projects/plane.png and /dev/null differ diff --git a/website/blog/img/creating-an-extension/compose.png b/website/blog/img/creating-an-extension/compose.png deleted file mode 100644 index 3aeb274303..0000000000 Binary files a/website/blog/img/creating-an-extension/compose.png and /dev/null differ diff --git a/website/blog/img/creating-an-extension/icons.png b/website/blog/img/creating-an-extension/icons.png deleted file mode 100644 index 6e4e2264ac..0000000000 Binary files a/website/blog/img/creating-an-extension/icons.png and /dev/null differ diff --git a/website/blog/img/creating-an-extension/layers_explorer.png b/website/blog/img/creating-an-extension/layers_explorer.png deleted file mode 100644 index 33960a3e00..0000000000 Binary files a/website/blog/img/creating-an-extension/layers_explorer.png and /dev/null differ diff --git a/website/blog/img/creating-an-extension/menus.png b/website/blog/img/creating-an-extension/menus.png deleted file mode 100644 index 3ded32fc81..0000000000 Binary files a/website/blog/img/creating-an-extension/menus.png and /dev/null differ diff --git a/website/blog/img/creating-an-extension/programming.png b/website/blog/img/creating-an-extension/programming.png deleted file mode 100644 index de71a7c1ff..0000000000 Binary files a/website/blog/img/creating-an-extension/programming.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-click-repository.png b/website/blog/img/develop-podman-using-codespaces/codespaces-click-repository.png deleted file mode 100644 index 458f522ec0..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-click-repository.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-edit-website.png b/website/blog/img/develop-podman-using-codespaces/codespaces-edit-website.png deleted file mode 100644 index 056f957fa7..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-edit-website.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-open-novnc.png b/website/blog/img/develop-podman-using-codespaces/codespaces-open-novnc.png deleted file mode 100644 index b488e46579..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-open-novnc.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-open-website.png b/website/blog/img/develop-podman-using-codespaces/codespaces-open-website.png deleted file mode 100644 index 6b6288bbfc..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-open-website.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-preparing-codespace.png b/website/blog/img/develop-podman-using-codespaces/codespaces-preparing-codespace.png deleted file mode 100644 index 00383e6098..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-preparing-codespace.png and /dev/null differ diff --git a/website/blog/img/develop-podman-using-codespaces/codespaces-testing-podman-desktop.png b/website/blog/img/develop-podman-using-codespaces/codespaces-testing-podman-desktop.png deleted file mode 100644 index 9e7ac69454..0000000000 Binary files a/website/blog/img/develop-podman-using-codespaces/codespaces-testing-podman-desktop.png and /dev/null differ diff --git a/website/blog/img/iterate-quickly-inner-loop-with-a-kubernetes-cluster/iterate-quickly-inner-loop-with-a-kubernetes-cluster.png b/website/blog/img/iterate-quickly-inner-loop-with-a-kubernetes-cluster/iterate-quickly-inner-loop-with-a-kubernetes-cluster.png deleted file mode 100644 index 9e45c8c621..0000000000 Binary files a/website/blog/img/iterate-quickly-inner-loop-with-a-kubernetes-cluster/iterate-quickly-inner-loop-with-a-kubernetes-cluster.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/add-registry-configuration.png b/website/blog/img/mirror-registry-configuration/add-registry-configuration.png deleted file mode 100644 index 2eb918643f..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/add-registry-configuration.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/docker-option-added.png b/website/blog/img/mirror-registry-configuration/docker-option-added.png deleted file mode 100644 index d78a2b2e25..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/docker-option-added.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/end-configuring-registries.png b/website/blog/img/mirror-registry-configuration/end-configuring-registries.png deleted file mode 100644 index c362556882..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/end-configuring-registries.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/mirrored-registry.png b/website/blog/img/mirror-registry-configuration/mirrored-registry.png deleted file mode 100644 index 634f9a99ef..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/mirrored-registry.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/notification.png b/website/blog/img/mirror-registry-configuration/notification.png deleted file mode 100644 index 17ad652e33..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/notification.png and /dev/null differ diff --git a/website/blog/img/mirror-registry-configuration/setting-up-registry-configuration.png b/website/blog/img/mirror-registry-configuration/setting-up-registry-configuration.png deleted file mode 100644 index e0a2a23504..0000000000 Binary files a/website/blog/img/mirror-registry-configuration/setting-up-registry-configuration.png and /dev/null differ diff --git a/website/blog/img/podman-ai-lab-openvino/openvino-inference-server-details.png b/website/blog/img/podman-ai-lab-openvino/openvino-inference-server-details.png deleted file mode 100644 index 65346ab06c..0000000000 Binary files a/website/blog/img/podman-ai-lab-openvino/openvino-inference-server-details.png and /dev/null differ diff --git a/website/blog/img/podman-ai-lab-openvino/openvino-playground1.png b/website/blog/img/podman-ai-lab-openvino/openvino-playground1.png deleted file mode 100644 index f365c448f8..0000000000 Binary files a/website/blog/img/podman-ai-lab-openvino/openvino-playground1.png and /dev/null differ diff --git a/website/blog/img/podman-ai-lab-openvino/openvino-playground2.png b/website/blog/img/podman-ai-lab-openvino/openvino-playground2.png deleted file mode 100644 index a62365d165..0000000000 Binary files a/website/blog/img/podman-ai-lab-openvino/openvino-playground2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-cncf/cncf_rocket.png b/website/blog/img/podman-desktop-cncf/cncf_rocket.png deleted file mode 100644 index 928ce41901..0000000000 Binary files a/website/blog/img/podman-desktop-cncf/cncf_rocket.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/build-image.png b/website/blog/img/podman-desktop-core-blog/build-image.png deleted file mode 100644 index 630b1df899..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/build-image.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/container-details.png b/website/blog/img/podman-desktop-core-blog/container-details.png deleted file mode 100644 index ab5cd22be7..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/container-details.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/container-logs.png b/website/blog/img/podman-desktop-core-blog/container-logs.png deleted file mode 100644 index 0935452619..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/container-logs.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/create-kind-cluster.png b/website/blog/img/podman-desktop-core-blog/create-kind-cluster.png deleted file mode 100644 index 12317804d2..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/create-kind-cluster.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/deploy-to-kubernetes.png b/website/blog/img/podman-desktop-core-blog/deploy-to-kubernetes.png deleted file mode 100644 index adb6640d03..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/deploy-to-kubernetes.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/kubernetes-dashboard.png b/website/blog/img/podman-desktop-core-blog/kubernetes-dashboard.png deleted file mode 100644 index 258ec2cc0e..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/kubernetes-dashboard.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/kubernetes-pod-logs.png b/website/blog/img/podman-desktop-core-blog/kubernetes-pod-logs.png deleted file mode 100644 index e34e2f065b..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/kubernetes-pod-logs.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/nginx-in-container.png b/website/blog/img/podman-desktop-core-blog/nginx-in-container.png deleted file mode 100644 index f385604941..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/nginx-in-container.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/pod-details-events.png b/website/blog/img/podman-desktop-core-blog/pod-details-events.png deleted file mode 100644 index 51c70de358..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/pod-details-events.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/push-image-to-kind.png b/website/blog/img/podman-desktop-core-blog/push-image-to-kind.png deleted file mode 100644 index 3561254ff1..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/push-image-to-kind.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-core-blog/start-container.png b/website/blog/img/podman-desktop-core-blog/start-container.png deleted file mode 100644 index 8051498b8b..0000000000 Binary files a/website/blog/img/podman-desktop-core-blog/start-container.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/custom-kubeconfig.png b/website/blog/img/podman-desktop-release-0.10/custom-kubeconfig.png deleted file mode 100644 index 2b8599f42c..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/custom-kubeconfig.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/kebab-menu.png b/website/blog/img/podman-desktop-release-0.10/kebab-menu.png deleted file mode 100644 index b6b2b8138e..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/kebab-menu.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/new_container_creation_wizard.gif b/website/blog/img/podman-desktop-release-0.10/new_container_creation_wizard.gif deleted file mode 100644 index 2912a2bddc..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/new_container_creation_wizard.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/play_k8s_yaml.gif b/website/blog/img/podman-desktop-release-0.10/play_k8s_yaml.gif deleted file mode 100644 index 9438708075..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/play_k8s_yaml.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/registries.png b/website/blog/img/podman-desktop-release-0.10/registries.png deleted file mode 100644 index fc8a1e5689..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/registries.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.10/text-color.gif b/website/blog/img/podman-desktop-release-0.10/text-color.gif deleted file mode 100644 index 88eebd7704..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.10/text-color.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.12/podman-desktop-release-0.12.png b/website/blog/img/podman-desktop-release-0.12/podman-desktop-release-0.12.png deleted file mode 100644 index 800c278cdb..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.12/podman-desktop-release-0.12.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.13/podman-desktop-release-0.13.png b/website/blog/img/podman-desktop-release-0.13/podman-desktop-release-0.13.png deleted file mode 100644 index 3b62114e63..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.13/podman-desktop-release-0.13.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.13/prune-image.png b/website/blog/img/podman-desktop-release-0.13/prune-image.png deleted file mode 100644 index ea1daff58a..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.13/prune-image.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.13/task-manager.png b/website/blog/img/podman-desktop-release-0.13/task-manager.png deleted file mode 100644 index 860372172f..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.13/task-manager.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.14/kind-clusters.png b/website/blog/img/podman-desktop-release-0.14/kind-clusters.png deleted file mode 100644 index d3d01d7476..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.14/kind-clusters.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.14/kind-ingress.png b/website/blog/img/podman-desktop-release-0.14/kind-ingress.png deleted file mode 100644 index 61fbd4521b..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.14/kind-ingress.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.14/podman-desktop-release-0.14.png b/website/blog/img/podman-desktop-release-0.14/podman-desktop-release-0.14.png deleted file mode 100644 index bdff62cb83..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.14/podman-desktop-release-0.14.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.14/push-image-kind.png b/website/blog/img/podman-desktop-release-0.14/push-image-kind.png deleted file mode 100644 index b86f591169..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.14/push-image-kind.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/dialog.png b/website/blog/img/podman-desktop-release-0.15/dialog.png deleted file mode 100644 index 9faee93f42..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/dialog.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/help.png b/website/blog/img/podman-desktop-release-0.15/help.png deleted file mode 100644 index 0b08062b97..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/help.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/markdown.png b/website/blog/img/podman-desktop-release-0.15/markdown.png deleted file mode 100644 index 879308622a..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/markdown.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/navigation.png b/website/blog/img/podman-desktop-release-0.15/navigation.png deleted file mode 100644 index 29574a73cf..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/navigation.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/podman-desktop-release-0.15.webp b/website/blog/img/podman-desktop-release-0.15/podman-desktop-release-0.15.webp deleted file mode 100644 index ed24834c7f..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/podman-desktop-release-0.15.webp and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-0.15/prefs.png b/website/blog/img/podman-desktop-release-0.15/prefs.png deleted file mode 100644 index 42e9c56dd4..0000000000 Binary files a/website/blog/img/podman-desktop-release-0.15/prefs.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/edit-number.png b/website/blog/img/podman-desktop-release-1.0/edit-number.png deleted file mode 100644 index 49b326fb9c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/edit-number.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/external-link.png b/website/blog/img/podman-desktop-release-1.0/external-link.png deleted file mode 100644 index c106eab94a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/external-link.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/featured-extensions.png b/website/blog/img/podman-desktop-release-1.0/featured-extensions.png deleted file mode 100644 index 55a87c8676..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/featured-extensions.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/nav-tooltips.png b/website/blog/img/podman-desktop-release-1.0/nav-tooltips.png deleted file mode 100644 index fc688c3a81..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/nav-tooltips.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/openshift-local.png b/website/blog/img/podman-desktop-release-1.0/openshift-local.png deleted file mode 100644 index ce6eadfb8c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/openshift-local.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/podman-desktop-release-1.0.jpg b/website/blog/img/podman-desktop-release-1.0/podman-desktop-release-1.0.jpg deleted file mode 100644 index 5d7baf63d4..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/podman-desktop-release-1.0.jpg and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/podman-root.png b/website/blog/img/podman-desktop-release-1.0/podman-root.png deleted file mode 100644 index eb401df543..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/podman-root.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.0/sandbox.png b/website/blog/img/podman-desktop-release-1.0/sandbox.png deleted file mode 100644 index 46d2b9c842..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.0/sandbox.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.1/podman-desktop-release-1.1.png b/website/blog/img/podman-desktop-release-1.1/podman-desktop-release-1.1.png deleted file mode 100644 index cd1a8a2aa2..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.1/podman-desktop-release-1.1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.1/update-extensions.png b/website/blog/img/podman-desktop-release-1.1/update-extensions.png deleted file mode 100644 index 365f19e4e7..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.1/update-extensions.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.10/1000000-downloads.png b/website/blog/img/podman-desktop-release-1.10/1000000-downloads.png deleted file mode 100644 index cca5558132..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.10/1000000-downloads.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.10/extension-catalog.png b/website/blog/img/podman-desktop-release-1.10/extension-catalog.png deleted file mode 100644 index 20ee155292..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.10/extension-catalog.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.10/extensions.png b/website/blog/img/podman-desktop-release-1.10/extensions.png deleted file mode 100644 index eeb76faeff..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.10/extensions.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.10/multi-platform-builds.png b/website/blog/img/podman-desktop-release-1.10/multi-platform-builds.png deleted file mode 100644 index f97be14704..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.10/multi-platform-builds.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.11/banner.png b/website/blog/img/podman-desktop-release-1.11/banner.png deleted file mode 100644 index ab0de58395..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.11/banner.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.11/light_mode.png b/website/blog/img/podman-desktop-release-1.11/light_mode.png deleted file mode 100644 index b5618471f9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.11/light_mode.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.11/manifest.png b/website/blog/img/podman-desktop-release-1.11/manifest.png deleted file mode 100644 index fdb09b03e6..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.11/manifest.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.11/nodes.png b/website/blog/img/podman-desktop-release-1.11/nodes.png deleted file mode 100644 index d70df9f72a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.11/nodes.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.11/pvc.png b/website/blog/img/podman-desktop-release-1.11/pvc.png deleted file mode 100644 index 4974f78d9f..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.11/pvc.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/ai_lab.png b/website/blog/img/podman-desktop-release-1.12/ai_lab.png deleted file mode 100644 index c1249e09c2..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/ai_lab.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/banner.png b/website/blog/img/podman-desktop-release-1.12/banner.png deleted file mode 100644 index 713195d706..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/banner.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/configmaps_secrets.png b/website/blog/img/podman-desktop-release-1.12/configmaps_secrets.png deleted file mode 100644 index 207be0729a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/configmaps_secrets.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/libkrun.png b/website/blog/img/podman-desktop-release-1.12/libkrun.png deleted file mode 100644 index 4c15ae53c5..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/libkrun.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/light_mode.png b/website/blog/img/podman-desktop-release-1.12/light_mode.png deleted file mode 100644 index da2c241551..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/light_mode.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.12/remote.png b/website/blog/img/podman-desktop-release-1.12/remote.png deleted file mode 100644 index 097e4f36f4..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.12/remote.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/banner.png b/website/blog/img/podman-desktop-release-1.13/banner.png deleted file mode 100644 index 75b88b13a9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/banner.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/docker_comp_page.png b/website/blog/img/podman-desktop-release-1.13/docker_comp_page.png deleted file mode 100644 index 037fec32d0..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/docker_comp_page.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/images_empty_page.png b/website/blog/img/podman-desktop-release-1.13/images_empty_page.png deleted file mode 100644 index 31fbf1d609..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/images_empty_page.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/kubernetes_menu.png b/website/blog/img/podman-desktop-release-1.13/kubernetes_menu.png deleted file mode 100644 index 84c6681181..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/kubernetes_menu.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/layer_explorer.png b/website/blog/img/podman-desktop-release-1.13/layer_explorer.png deleted file mode 100644 index 442b278408..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/layer_explorer.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.13/search_image.png b/website/blog/img/podman-desktop-release-1.13/search_image.png deleted file mode 100644 index 500ff05d11..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.13/search_image.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.14/banner.png b/website/blog/img/podman-desktop-release-1.14/banner.png deleted file mode 100644 index 11d2fc766a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.14/banner.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.14/kubernetes_dashboard.png b/website/blog/img/podman-desktop-release-1.14/kubernetes_dashboard.png deleted file mode 100644 index 7e1478af68..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.14/kubernetes_dashboard.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.14/port_forwarding.png b/website/blog/img/podman-desktop-release-1.14/port_forwarding.png deleted file mode 100644 index d60a3289c9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.14/port_forwarding.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-dark.png b/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-dark.png deleted file mode 100644 index 3f770e6291..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-light.png b/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-light.png deleted file mode 100644 index 3d0ee324d6..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-opened-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-dark.png b/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-dark.png deleted file mode 100644 index 8ec6f7c93f..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-light.png b/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-light.png deleted file mode 100644 index f448355122..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/experimental-task-manager-preference-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/feedback-categories-dark.png b/website/blog/img/podman-desktop-release-1.15/feedback-categories-dark.png deleted file mode 100644 index 3610d33d7c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/feedback-categories-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/feedback-categories-light.png b/website/blog/img/podman-desktop-release-1.15/feedback-categories-light.png deleted file mode 100644 index 73056a90ec..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/feedback-categories-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/feedback-form-dark.png b/website/blog/img/podman-desktop-release-1.15/feedback-form-dark.png deleted file mode 100644 index 6e2a29e47e..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/feedback-form-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/feedback-form-light.png b/website/blog/img/podman-desktop-release-1.15/feedback-form-light.png deleted file mode 100644 index d8c6e92497..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/feedback-form-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-dark.png b/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-dark.png deleted file mode 100644 index ac30ed4707..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-light.png b/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-light.png deleted file mode 100644 index 71c090e5db..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/kubernetes-node-events-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-dark.png b/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-dark.png deleted file mode 100644 index 4d5ab13999..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-light.png b/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-light.png deleted file mode 100644 index d78020f74c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.15/podman-machine-ssh-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-dark.png b/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-dark.png deleted file mode 100644 index d87a164513..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-light.png b/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-light.png deleted file mode 100644 index e303866a05..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/experimental-features-settings-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/kube-monitoring-dark.png b/website/blog/img/podman-desktop-release-1.16/kube-monitoring-dark.png deleted file mode 100644 index fbe4329514..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/kube-monitoring-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/kube-monitoring-light.png b/website/blog/img/podman-desktop-release-1.16/kube-monitoring-light.png deleted file mode 100644 index 1c82c3d18d..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/kube-monitoring-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/logs-dark.png b/website/blog/img/podman-desktop-release-1.16/logs-dark.png deleted file mode 100644 index 2e5632b185..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/logs-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/logs-light.png b/website/blog/img/podman-desktop-release-1.16/logs-light.png deleted file mode 100644 index 0c89a9449c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/logs-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/provider-status-dark.png b/website/blog/img/podman-desktop-release-1.16/provider-status-dark.png deleted file mode 100644 index fe3efe3b54..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/provider-status-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/provider-status-light.png b/website/blog/img/podman-desktop-release-1.16/provider-status-light.png deleted file mode 100644 index 45b93486c3..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/provider-status-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/prune-images-dark.png b/website/blog/img/podman-desktop-release-1.16/prune-images-dark.png deleted file mode 100644 index 78f3a0cd12..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/prune-images-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.16/prune-images-light.png b/website/blog/img/podman-desktop-release-1.16/prune-images-light.png deleted file mode 100644 index 185703a8b2..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.16/prune-images-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-dark.png b/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-dark.png deleted file mode 100644 index af98a07591..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-light.png b/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-light.png deleted file mode 100644 index 5a8634949f..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/install-kind-cli-create-cluster-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/kube-experimental-dark.png b/website/blog/img/podman-desktop-release-1.17/kube-experimental-dark.png deleted file mode 100644 index 14a1e2a3b7..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/kube-experimental-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/kube-experimental-light.png b/website/blog/img/podman-desktop-release-1.17/kube-experimental-light.png deleted file mode 100644 index c9caeeda6f..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/kube-experimental-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-dark.png b/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-dark.png deleted file mode 100644 index d33ecaee61..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-light.png b/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-light.png deleted file mode 100644 index 9be277b75d..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/kubernetes-pod-view-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-dark.png b/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-dark.png deleted file mode 100644 index 0e7fd87106..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-light.png b/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-light.png deleted file mode 100644 index 148fa56f2a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-dark.png b/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-dark.png deleted file mode 100644 index d8df6eafcf..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-light.png b/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-light.png deleted file mode 100644 index e043806493..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/setup-mirror-registry-step2-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/start-container-from-image-dark.png b/website/blog/img/podman-desktop-release-1.17/start-container-from-image-dark.png deleted file mode 100644 index 1d73466534..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/start-container-from-image-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.17/start-container-from-image-light.png b/website/blog/img/podman-desktop-release-1.17/start-container-from-image-light.png deleted file mode 100644 index 70552cae18..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.17/start-container-from-image-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-dark.png b/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-dark.png deleted file mode 100644 index 8255382a61..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-light.png b/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-light.png deleted file mode 100644 index 329324be59..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/Kubernetes-namespaces-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/docker-socket-dark.png b/website/blog/img/podman-desktop-release-1.18/docker-socket-dark.png deleted file mode 100644 index 4acca0ac97..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/docker-socket-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/docker-socket-light.png b/website/blog/img/podman-desktop-release-1.18/docker-socket-light.png deleted file mode 100644 index 3d15b347a6..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/docker-socket-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/job-details-dark.png b/website/blog/img/podman-desktop-release-1.18/job-details-dark.png deleted file mode 100644 index db34739f60..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/job-details-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/job-details-light.png b/website/blog/img/podman-desktop-release-1.18/job-details-light.png deleted file mode 100644 index dbe8a85028..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/job-details-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/jobs-list-dark.png b/website/blog/img/podman-desktop-release-1.18/jobs-list-dark.png deleted file mode 100644 index 10be45747c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/jobs-list-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/jobs-list-light.png b/website/blog/img/podman-desktop-release-1.18/jobs-list-light.png deleted file mode 100644 index 7047af497c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/jobs-list-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/mac-helper-dark.png b/website/blog/img/podman-desktop-release-1.18/mac-helper-dark.png deleted file mode 100644 index d5bbb97e52..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/mac-helper-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/mac-helper-light.png b/website/blog/img/podman-desktop-release-1.18/mac-helper-light.png deleted file mode 100644 index d54270af60..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/mac-helper-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/statusbar-provider-dark.png b/website/blog/img/podman-desktop-release-1.18/statusbar-provider-dark.png deleted file mode 100644 index e9dcf69e29..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/statusbar-provider-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.18/statusbar-provider-light.png b/website/blog/img/podman-desktop-release-1.18/statusbar-provider-light.png deleted file mode 100644 index 97d7e9d41e..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.18/statusbar-provider-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-dark.png deleted file mode 100644 index b98a6b7dd9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-light.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-light.png deleted file mode 100644 index 81ff8f8382..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-llama-stack-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-dark.png deleted file mode 100644 index 360b6dc01a..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-light.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-light.png deleted file mode 100644 index 3fb3cb04a2..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-mcp-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-dark.png deleted file mode 100644 index 317f67c476..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-light.png b/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-light.png deleted file mode 100644 index 0f24409bb0..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-ai-lab-openvino-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-dark.png deleted file mode 100644 index dbfd2ce0fd..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-light.png b/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-light.png deleted file mode 100644 index 3737c6f0d5..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-bootc-create-vm-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-dark.png deleted file mode 100644 index 9035f0b8d9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-light.png b/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-light.png deleted file mode 100644 index 858d90b9b5..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-bootc-examples-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-dark.png deleted file mode 100644 index 3a28880c12..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-light.png b/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-light.png deleted file mode 100644 index aeb74f5cab..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-lightspeed-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-dark.png b/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-dark.png deleted file mode 100644 index fe3fbb0c72..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-light.png b/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-light.png deleted file mode 100644 index dff52930e9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.19/extension-rhel-vm-create-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.2/podman-desktop-release-1.2.png b/website/blog/img/podman-desktop-release-1.2/podman-desktop-release-1.2.png deleted file mode 100644 index b26cc4b359..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.2/podman-desktop-release-1.2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-dark.png b/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-dark.png deleted file mode 100644 index 037e75c194..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-light.png b/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-light.png deleted file mode 100644 index be15c91100..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/edit-kubernetes-context-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-dark.png b/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-dark.png deleted file mode 100644 index b722d71f55..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-light.png b/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-light.png deleted file mode 100644 index 385166eae9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/search-extension-by-description-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/start-all-containers-dark.png b/website/blog/img/podman-desktop-release-1.20/start-all-containers-dark.png deleted file mode 100644 index 6afaf2cd8b..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/start-all-containers-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/start-all-containers-light.png b/website/blog/img/podman-desktop-release-1.20/start-all-containers-light.png deleted file mode 100644 index 7daea2b195..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/start-all-containers-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/update-provider-button-dark.png b/website/blog/img/podman-desktop-release-1.20/update-provider-button-dark.png deleted file mode 100644 index d949ed0275..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/update-provider-button-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/update-provider-button-light.png b/website/blog/img/podman-desktop-release-1.20/update-provider-button-light.png deleted file mode 100644 index 0cc760a64e..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/update-provider-button-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-dark.png b/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-dark.png deleted file mode 100644 index f8f0163270..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-dark.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-light.png b/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-light.png deleted file mode 100644 index c7835c3eb3..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.20/use-production-binary-to-develop-extensions-light.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.3/title-bug-swatting.png b/website/blog/img/podman-desktop-release-1.3/title-bug-swatting.png deleted file mode 100644 index 14440df25e..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.3/title-bug-swatting.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.4/bash-sh.gif b/website/blog/img/podman-desktop-release-1.4/bash-sh.gif deleted file mode 100644 index 05009a18ef..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.4/bash-sh.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.4/create-volume.gif b/website/blog/img/podman-desktop-release-1.4/create-volume.gif deleted file mode 100644 index 77e863f43d..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.4/create-volume.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.4/juggling.png b/website/blog/img/podman-desktop-release-1.4/juggling.png deleted file mode 100644 index be9a616d33..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.4/juggling.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.4/terminal.gif b/website/blog/img/podman-desktop-release-1.4/terminal.gif deleted file mode 100644 index b5347a892b..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.4/terminal.gif and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.5/onboarding-selkies.png b/website/blog/img/podman-desktop-release-1.5/onboarding-selkies.png deleted file mode 100644 index cf45bf1ba7..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.5/onboarding-selkies.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/cli-tools.png b/website/blog/img/podman-desktop-release-1.6/cli-tools.png deleted file mode 100644 index af8d99c809..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/cli-tools.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/compose-update.png b/website/blog/img/podman-desktop-release-1.6/compose-update.png deleted file mode 100644 index 5a9d8cd2e1..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/compose-update.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/environment-column.png b/website/blog/img/podman-desktop-release-1.6/environment-column.png deleted file mode 100644 index 3a385cf1cf..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/environment-column.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/kubernetes-contexts.png b/website/blog/img/podman-desktop-release-1.6/kubernetes-contexts.png deleted file mode 100644 index 0429f9c254..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/kubernetes-contexts.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/minikube-feature-extension.png b/website/blog/img/podman-desktop-release-1.6/minikube-feature-extension.png deleted file mode 100644 index bb967b2141..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/minikube-feature-extension.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.6/santaseal.png b/website/blog/img/podman-desktop-release-1.6/santaseal.png deleted file mode 100644 index e04a701545..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.6/santaseal.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/build-platform.png b/website/blog/img/podman-desktop-release-1.7/build-platform.png deleted file mode 100644 index 4e3a7c05c2..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/build-platform.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/kube-deployments.png b/website/blog/img/podman-desktop-release-1.7/kube-deployments.png deleted file mode 100644 index 0c518fc6b9..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/kube-deployments.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/kube-ingress-routes.png b/website/blog/img/podman-desktop-release-1.7/kube-ingress-routes.png deleted file mode 100644 index 6d485b0f28..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/kube-ingress-routes.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/kube-preference.png b/website/blog/img/podman-desktop-release-1.7/kube-preference.png deleted file mode 100644 index 1804d4d2cd..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/kube-preference.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/kube-services.png b/website/blog/img/podman-desktop-release-1.7/kube-services.png deleted file mode 100644 index d5d2956a44..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/kube-services.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/pods-table.png b/website/blog/img/podman-desktop-release-1.7/pods-table.png deleted file mode 100644 index bf605f6476..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/pods-table.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/purge.png b/website/blog/img/podman-desktop-release-1.7/purge.png deleted file mode 100644 index af5854f0db..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/purge.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.7/renovations.png b/website/blog/img/podman-desktop-release-1.7/renovations.png deleted file mode 100644 index a1e8da4775..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.7/renovations.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/deployment-edit.png b/website/blog/img/podman-desktop-release-1.8/deployment-edit.png deleted file mode 100644 index 53c4302ecc..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/deployment-edit.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/deployment-summary.png b/website/blog/img/podman-desktop-release-1.8/deployment-summary.png deleted file mode 100644 index 825bec2758..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/deployment-summary.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/deployments.png b/website/blog/img/podman-desktop-release-1.8/deployments.png deleted file mode 100644 index 8efb799cfe..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/deployments.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/global-onboarding.png b/website/blog/img/podman-desktop-release-1.8/global-onboarding.png deleted file mode 100644 index eb1127b1f5..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/global-onboarding.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/learning-center.png b/website/blog/img/podman-desktop-release-1.8/learning-center.png deleted file mode 100644 index 66908c87aa..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/learning-center.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.8/selkie-family.png b/website/blog/img/podman-desktop-release-1.8/selkie-family.png deleted file mode 100644 index 8b44663e92..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.8/selkie-family.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/experimental-podman-5.png b/website/blog/img/podman-desktop-release-1.9/experimental-podman-5.png deleted file mode 100644 index fee01596d3..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/experimental-podman-5.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/juggling.png b/website/blog/img/podman-desktop-release-1.9/juggling.png deleted file mode 100644 index be9a616d33..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/juggling.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-export-1.png b/website/blog/img/podman-desktop-release-1.9/podman-export-1.png deleted file mode 100644 index 844e120596..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-export-1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-export-2.png b/website/blog/img/podman-desktop-release-1.9/podman-export-2.png deleted file mode 100644 index de5f0a1fca..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-export-2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-import-1.png b/website/blog/img/podman-desktop-release-1.9/podman-import-1.png deleted file mode 100644 index a37c4ef559..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-import-1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-import-2.png b/website/blog/img/podman-desktop-release-1.9/podman-import-2.png deleted file mode 100644 index bdd3685c18..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-import-2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-load-1.png b/website/blog/img/podman-desktop-release-1.9/podman-load-1.png deleted file mode 100644 index b6932efe79..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-load-1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-load-2.png b/website/blog/img/podman-desktop-release-1.9/podman-load-2.png deleted file mode 100644 index e1b8e8cdab..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-load-2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-1.png b/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-1.png deleted file mode 100644 index 58ee9ea2c0..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-2.png b/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-2.png deleted file mode 100644 index 91f6bec6df..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-require-onboarding-2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-save-1.png b/website/blog/img/podman-desktop-release-1.9/podman-save-1.png deleted file mode 100644 index b92879f36c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-save-1.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-save-2.png b/website/blog/img/podman-desktop-release-1.9/podman-save-2.png deleted file mode 100644 index be6a5d8dee..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-save-2.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/podman-update-to-v5.png b/website/blog/img/podman-desktop-release-1.9/podman-update-to-v5.png deleted file mode 100644 index 2a94790d6c..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/podman-update-to-v5.png and /dev/null differ diff --git a/website/blog/img/podman-desktop-release-1.9/terminal-pod.png b/website/blog/img/podman-desktop-release-1.9/terminal-pod.png deleted file mode 100644 index 125d5f2544..0000000000 Binary files a/website/blog/img/podman-desktop-release-1.9/terminal-pod.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/banner.png b/website/blog/img/podman-quadlet/banner.png deleted file mode 100644 index 78c2b18acb..0000000000 Binary files a/website/blog/img/podman-quadlet/banner.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/container-icon-quadlet-dark.png b/website/blog/img/podman-quadlet/container-icon-quadlet-dark.png deleted file mode 100644 index fa39369dca..0000000000 Binary files a/website/blog/img/podman-quadlet/container-icon-quadlet-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/container-icon-quadlet-light.png b/website/blog/img/podman-quadlet/container-icon-quadlet-light.png deleted file mode 100644 index 426a596980..0000000000 Binary files a/website/blog/img/podman-quadlet/container-icon-quadlet-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-form-edit-dark.png b/website/blog/img/podman-quadlet/generate-form-edit-dark.png deleted file mode 100644 index 452830ccd5..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-form-edit-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-form-edit-light.png b/website/blog/img/podman-quadlet/generate-form-edit-light.png deleted file mode 100644 index a834b93e71..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-form-edit-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-form-options-dark.png b/website/blog/img/podman-quadlet/generate-form-options-dark.png deleted file mode 100644 index e12aadd956..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-form-options-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-form-options-light.png b/website/blog/img/podman-quadlet/generate-form-options-light.png deleted file mode 100644 index 0ab09215da..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-form-options-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-quadlet-action-dark.png b/website/blog/img/podman-quadlet/generate-quadlet-action-dark.png deleted file mode 100644 index 5f9385fdad..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-quadlet-action-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/generate-quadlet-action-light.png b/website/blog/img/podman-quadlet/generate-quadlet-action-light.png deleted file mode 100644 index c991d43f12..0000000000 Binary files a/website/blog/img/podman-quadlet/generate-quadlet-action-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/podman-quadlet-home-dark.png b/website/blog/img/podman-quadlet/podman-quadlet-home-dark.png deleted file mode 100644 index cbba968b01..0000000000 Binary files a/website/blog/img/podman-quadlet/podman-quadlet-home-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/podman-quadlet-home-light.png b/website/blog/img/podman-quadlet/podman-quadlet-home-light.png deleted file mode 100644 index 675ed21758..0000000000 Binary files a/website/blog/img/podman-quadlet/podman-quadlet-home-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/quadlet-details-logs-dark.png b/website/blog/img/podman-quadlet/quadlet-details-logs-dark.png deleted file mode 100644 index bc79350b01..0000000000 Binary files a/website/blog/img/podman-quadlet/quadlet-details-logs-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/quadlet-details-logs-light.png b/website/blog/img/podman-quadlet/quadlet-details-logs-light.png deleted file mode 100644 index 470bf4c02d..0000000000 Binary files a/website/blog/img/podman-quadlet/quadlet-details-logs-light.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/quadlet-details-source-dark.png b/website/blog/img/podman-quadlet/quadlet-details-source-dark.png deleted file mode 100644 index b3feccfe0b..0000000000 Binary files a/website/blog/img/podman-quadlet/quadlet-details-source-dark.png and /dev/null differ diff --git a/website/blog/img/podman-quadlet/quadlet-details-source-light.png b/website/blog/img/podman-quadlet/quadlet-details-source-light.png deleted file mode 100644 index 0849c18116..0000000000 Binary files a/website/blog/img/podman-quadlet/quadlet-details-source-light.png and /dev/null differ diff --git a/website/blog/img/run-webassembly-wasm-workloads-windows-and-macos/webassembly-podman.webp b/website/blog/img/run-webassembly-wasm-workloads-windows-and-macos/webassembly-podman.webp deleted file mode 100644 index 57d63bf9ef..0000000000 Binary files a/website/blog/img/run-webassembly-wasm-workloads-windows-and-macos/webassembly-podman.webp and /dev/null differ diff --git a/website/blog/img/sharing-podman-images-with-kubernetes-cluster/selkie-family.png b/website/blog/img/sharing-podman-images-with-kubernetes-cluster/selkie-family.png deleted file mode 100644 index 8b44663e92..0000000000 Binary files a/website/blog/img/sharing-podman-images-with-kubernetes-cluster/selkie-family.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine1.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine1.png deleted file mode 100644 index ed1e621485..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine1.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine10.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine10.png deleted file mode 100644 index 40f0b41e32..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine10.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine11.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine11.png deleted file mode 100644 index 9bb0ed91b8..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine11.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine12.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine12.png deleted file mode 100644 index 082d88c682..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine12.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine13.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine13.png deleted file mode 100644 index 929f036a9a..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine13.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine14.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine14.png deleted file mode 100644 index 6ef4539657..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine14.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine15.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine15.png deleted file mode 100644 index 7c93474e56..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine15.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine2.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine2.png deleted file mode 100644 index f804faf88e..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine2.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine3.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine3.png deleted file mode 100644 index 8c48387b95..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine3.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine4.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine4.png deleted file mode 100644 index 9b02f619bf..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine4.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine5.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine5.png deleted file mode 100644 index ee862f8785..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine5.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine6.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine6.png deleted file mode 100644 index a06839d1d3..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine6.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine7.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine7.png deleted file mode 100644 index 1518325070..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine7.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine8.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine8.png deleted file mode 100644 index bde895fd17..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine8.png and /dev/null differ diff --git a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine9.png b/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine9.png deleted file mode 100644 index a521681d53..0000000000 Binary files a/website/blog/img/using-rhel-wsl-podman-machine/rhel-wsl-podman-machine9.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/build-image.png b/website/blog/img/vs-code-podman/build-image.png deleted file mode 100644 index 1d3cbee6d2..0000000000 Binary files a/website/blog/img/vs-code-podman/build-image.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/docker-compatibility.png b/website/blog/img/vs-code-podman/docker-compatibility.png deleted file mode 100644 index 908328d36c..0000000000 Binary files a/website/blog/img/vs-code-podman/docker-compatibility.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/interact-with-container.png b/website/blog/img/vs-code-podman/interact-with-container.png deleted file mode 100644 index 46b6d0f6f1..0000000000 Binary files a/website/blog/img/vs-code-podman/interact-with-container.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/podmanager-details.png b/website/blog/img/vs-code-podman/podmanager-details.png deleted file mode 100644 index 70cc119461..0000000000 Binary files a/website/blog/img/vs-code-podman/podmanager-details.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/podmanager.png b/website/blog/img/vs-code-podman/podmanager.png deleted file mode 100644 index d436864ffb..0000000000 Binary files a/website/blog/img/vs-code-podman/podmanager.png and /dev/null differ diff --git a/website/blog/img/vs-code-podman/view-logs.png b/website/blog/img/vs-code-podman/view-logs.png deleted file mode 100644 index 6d84f1b40a..0000000000 Binary files a/website/blog/img/vs-code-podman/view-logs.png and /dev/null differ diff --git a/website/blog/img/wasm-workloads-on-macos-and-windows-with-podman/wasm-hello-world.png b/website/blog/img/wasm-workloads-on-macos-and-windows-with-podman/wasm-hello-world.png deleted file mode 100644 index 2740d02344..0000000000 Binary files a/website/blog/img/wasm-workloads-on-macos-and-windows-with-podman/wasm-hello-world.png and /dev/null differ diff --git a/website/docs/ai-lab/create-playground.md b/website/docs/ai-lab/create-playground.md deleted file mode 100644 index 1e7299f1cc..0000000000 --- a/website/docs/ai-lab/create-playground.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 5 -title: Creating a playground -description: Creating a playground environment for a model. -keywords: [podman desktop, podman, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Starting an inference server for a model - -The integrated Playground environments allow for experimenting with available models in a local environment. An intuitive user prompt helps in exploring the capabilities and accuracy of various models and aids in finding the best model for the use case at hand. The Playground interface further allows for parameterizing models to further optimize the settings and attributes of each model. - -#### Prerequisites - -- [Podman AI Lab installed](/docs/ai-lab/installing). -- [Model downloaded](/docs/ai-lab/download-model). -- [An inference server created for a model](/docs/ai-lab/start-inference-server). - -#### Procedure - -1. Click the Podman AI Lab icon in the left navigation pane. -1. In the Podman AI Lab navigation bar, click **Playgrounds**. -1. Click the **New Playground** button at the top right corner of the page. -1. Select an inference runtime for the playground. -1. Select the model for which you want to start an inference server from the **Model** list. -1. Click the **Create playground** button. - ![create a playground](img/creating-playground.png) - -#### Verification - -1. View the created playground on the same page. - ![playground created](img/playground.png) -1. Click the name of the playground to experiment with model settings. The Playground page opens. -1. Perform any of the following tasks: - - Edit the value of the model parameters. - - Add a system prompt by clicking the **Edit system prompt** icon. - - Enter your question in the prompt box and analyze the accuracy of the answer. - ![experiment with model settings](img/playground-settings.png) diff --git a/website/docs/ai-lab/download-model.md b/website/docs/ai-lab/download-model.md deleted file mode 100644 index 1b66e0dd4b..0000000000 --- a/website/docs/ai-lab/download-model.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -sidebar_position: 3 -title: Downloading a model -description: Downloading a model. -keywords: [podman desktop, podman, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Downloading a model - -Podman AI Lab provides a curated list of open source AI models and LLMs. Once downloaded, the models are available to be used for AI applications, model services and playgrounds. - -#### Prerequisites - -- [Podman AI Lab installed](/docs/ai-lab/installing). - -#### Procedure - -1. Click the Podman AI Lab icon in the left navigation pane. -1. In the Podman AI Lab navigation bar, click **Catalog**. The curated list of models is now displayed. -1. Click the download icon corresponding to the model you want to use. - ![model downloading](img/model-downloading.png) - -#### Verification - -1. Once the model is downloaded, the curated models list is updated, and new actions become available for use. - ![model downloaded](img/model-downloaded.png) diff --git a/website/docs/ai-lab/img/ai-lab-icon.png b/website/docs/ai-lab/img/ai-lab-icon.png deleted file mode 100644 index c0b22ce02b..0000000000 Binary files a/website/docs/ai-lab/img/ai-lab-icon.png and /dev/null differ diff --git a/website/docs/ai-lab/img/creating-a-service.png b/website/docs/ai-lab/img/creating-a-service.png deleted file mode 100644 index d6da231c87..0000000000 Binary files a/website/docs/ai-lab/img/creating-a-service.png and /dev/null differ diff --git a/website/docs/ai-lab/img/creating-playground.png b/website/docs/ai-lab/img/creating-playground.png deleted file mode 100644 index fc4cb0fd0d..0000000000 Binary files a/website/docs/ai-lab/img/creating-playground.png and /dev/null differ diff --git a/website/docs/ai-lab/img/inference-server-curl.png b/website/docs/ai-lab/img/inference-server-curl.png deleted file mode 100644 index c2ecfaf9da..0000000000 Binary files a/website/docs/ai-lab/img/inference-server-curl.png and /dev/null differ diff --git a/website/docs/ai-lab/img/inference-server-quarkus.png b/website/docs/ai-lab/img/inference-server-quarkus.png deleted file mode 100644 index 33a7a71814..0000000000 Binary files a/website/docs/ai-lab/img/inference-server-quarkus.png and /dev/null differ diff --git a/website/docs/ai-lab/img/model-downloaded.png b/website/docs/ai-lab/img/model-downloaded.png deleted file mode 100644 index e0620b9952..0000000000 Binary files a/website/docs/ai-lab/img/model-downloaded.png and /dev/null differ diff --git a/website/docs/ai-lab/img/model-downloading.png b/website/docs/ai-lab/img/model-downloading.png deleted file mode 100644 index 935ac6798d..0000000000 Binary files a/website/docs/ai-lab/img/model-downloading.png and /dev/null differ diff --git a/website/docs/ai-lab/img/more-details-link.png b/website/docs/ai-lab/img/more-details-link.png deleted file mode 100644 index b5d29d0949..0000000000 Binary files a/website/docs/ai-lab/img/more-details-link.png and /dev/null differ diff --git a/website/docs/ai-lab/img/opening-ai-app.png b/website/docs/ai-lab/img/opening-ai-app.png deleted file mode 100644 index 8aee703814..0000000000 Binary files a/website/docs/ai-lab/img/opening-ai-app.png and /dev/null differ diff --git a/website/docs/ai-lab/img/opening-application-details.png b/website/docs/ai-lab/img/opening-application-details.png deleted file mode 100644 index c8eeea8f94..0000000000 Binary files a/website/docs/ai-lab/img/opening-application-details.png and /dev/null differ diff --git a/website/docs/ai-lab/img/playground-settings.png b/website/docs/ai-lab/img/playground-settings.png deleted file mode 100644 index d3fb91e2ff..0000000000 Binary files a/website/docs/ai-lab/img/playground-settings.png and /dev/null differ diff --git a/website/docs/ai-lab/img/playground.png b/website/docs/ai-lab/img/playground.png deleted file mode 100644 index 0cc2be0494..0000000000 Binary files a/website/docs/ai-lab/img/playground.png and /dev/null differ diff --git a/website/docs/ai-lab/img/starting-a-recipe.png b/website/docs/ai-lab/img/starting-a-recipe.png deleted file mode 100644 index eab06301e9..0000000000 Binary files a/website/docs/ai-lab/img/starting-a-recipe.png and /dev/null differ diff --git a/website/docs/ai-lab/index.md b/website/docs/ai-lab/index.md deleted file mode 100644 index c5f166ab6f..0000000000 --- a/website/docs/ai-lab/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 70 -title: Podman AI Lab -description: Podman AI Lab is an open source extension for Podman Desktop to work with LLMs. -keywords: [podman desktop, podman, containers, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Run LLMs locally - -Podman AI Lab is the easiest way to work with Large Language Models (LLMs) on your local developer workstation. Find a catalog of recipes, leverage a curated list of open source models, experiment and compare the models. Get ahead of the curve and take your development to new heights wth Podman AI Lab! -There are many ways to run models locally. This extension fits perfectly into your local container workflow and exposes LLMs through inference APIs that you can directly access from your application containers. Beyond that you can use playgrounds to optimize your inference parameters and recipes that help you with ready made examples. - -#### Procedure - -First thing you need to do is to install the extension itself: - -1. [Install the `Podman AI Lab` extension](/docs/ai-lab/installing). - -#### Next steps - -1. [Download a model](/docs/ai-lab/download-model). -1. [Start an inference server for a model](/docs/ai-lab/start-inference-server). -1. [Creating a playground to interact with a model](/docs/ai-lab/create-playground). -1. [Start a recipe](/docs/ai-lab/start-recipe). diff --git a/website/docs/ai-lab/installing.md b/website/docs/ai-lab/installing.md deleted file mode 100644 index aeb1f46406..0000000000 --- a/website/docs/ai-lab/installing.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -sidebar_position: 2 -title: Installing Podman AI Lab -description: Podman AI Lab can help you run large language models (LLMs) locally with no pain. -keywords: [podman desktop, podman, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Installing the Podman AI Lab extension - -The AI Lab extension extends the capabilities of Podman Desktop for Large Language Models (LLMs). Its installation is just a click away if you already have Podman Desktop installed. - -#### Prerequisites - -- [Install Podman Desktop](/docs/installation). - -#### Procedure - -Perform one of the following steps: - -- **Click to launch the installation** of Podman AI Lab in Podman Desktop. -- Go to **Extensions > Catalog** and [install the Podman AI Lab extension](/tutorial/running-an-ai-application#installing-the-extension). - -#### Verification - -1. Check the left navigation pane, which shows the Podman AI Lab icon. - ![Podman AI Lab icon](img/ai-lab-icon.png) diff --git a/website/docs/ai-lab/start-inference-server.md b/website/docs/ai-lab/start-inference-server.md deleted file mode 100644 index fca6c108c5..0000000000 --- a/website/docs/ai-lab/start-inference-server.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -sidebar_position: 4 -title: Starting an inference server -description: Starting an inference server for a model. -keywords: [podman desktop, podman, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Starting an inference server for a model - -Once a model is downloaded, a model service can be started. A model service is an inference server that runs in a container and exposes the model through the well-known chat API common to many providers. - -#### Prerequisites - -- [Podman AI Lab installed](/docs/ai-lab/installing). -- [Model downloaded](/docs/ai-lab/download-model). - -#### Procedure - -1. Click the Podman AI Lab icon in the left navigation pane. -1. In the Podman AI Lab navigation bar, click **Services**. -1. Click the **New Model Service** button at the top right corner of the page. The Creating Model service page opens. - - :::note - - On a macOS machine, you get a notification to [create a GPU-enabled Podman machine](/docs/podman/creating-a-podman-machine) to run your GPU workloads. Click the **Create GPU enabled machine** button to proceed. - - ::: - -1. Select the model for which you want to start an inference server from the dropdown list, and edit the port number if needed. -1. Click **Create service**. The inference server for the model is being started, and this requires some time. - ![create a service](img/creating-a-service.png) - -1. Click the **Open service details** button. - -#### Verification - -1. View the details of the inference server. - ![inference server details](img/inference-server-curl.png) -1. Optional: Customize the client code based on your programming language to access the model through the inference server. For example, set the code language to `Java` and `Quarkus Langchain4J`, and view the updated code snippet. - ![inference server details for Quarkus](img/inference-server-quarkus.png) diff --git a/website/docs/ai-lab/start-recipe.md b/website/docs/ai-lab/start-recipe.md deleted file mode 100644 index 25e53fffdc..0000000000 --- a/website/docs/ai-lab/start-recipe.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 6 -title: Starting a recipe -description: Created a recipe also known as an inference server for interaction with a model. -keywords: [podman desktop, podman, ai, llm, generative ai] -tags: [ai, llm, generative ai] ---- - -# Starting a recipe (inference server) for a model - -Once an AI model is available through a well known endpoint, it's easy to imagine a new world of applications that will connect and use the AI model. Podman AI Lab support AI applications as a set of containers that are connected together. - -Podman AI Lab includes a Recipes Catalog that helps you navigate several core AI use cases and problem domains, such as chatbots, code generators, and text summarizers. Each recipe includes detailed explanations and sample applications that can be run with various large language models (LLMs). Experimenting with multiple models allows you to find the optimal one for your use case. - -#### Prerequisites - -- [Podman AI Lab installed](/docs/ai-lab/installing). -- [Model downloaded](/docs/ai-lab/download-model). - -#### Procedure - -1. Click the Podman AI Lab icon in the left navigation pane. -1. In the Podman AI Lab navigation bar, click **Recipe Catalog**. -1. Optional: Filter the recipe catalog based on the required tool, framework, or programming language. -1. Click the **More details** link for the recipe that you want to start. The recipe summary page opens. - ![More details link](img/more-details-link.png) -1. Click **Start**. The Start recipe page opens. - ![start a recipe](img/starting-a-recipe.png) -1. Select a model from the dropdown list. -1. Click **Start ChatBot recipe**. This step might take some time to pull the recipe, copy the model to your Podman machine, start the inference server, load configurations, and create the application. - -#### Verification - -1. Click **Open details** after the processing completes. - ![open AI application details](img/opening-application-details.png) -1. View the created AI application. -1. Click the **Open AI App** icon to view the running application in your browser. - ![Open AI App icon](img/opening-ai-app.png) diff --git a/website/docs/compose/img/compose-in-containers-view.png b/website/docs/compose/img/compose-in-containers-view.png deleted file mode 100644 index f6b919a677..0000000000 Binary files a/website/docs/compose/img/compose-in-containers-view.png and /dev/null differ diff --git a/website/docs/compose/index.md b/website/docs/compose/index.md deleted file mode 100644 index 32a0763d17..0000000000 --- a/website/docs/compose/index.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -sidebar_position: 35 -title: Compose -description: With Podman Desktop, you can install a Compose engine and manage multi-container applications defined in Compose files. -keywords: [compose] -tags: [compose] ---- - -# Working with Compose - -Podman Desktop supports the [Compose specification](https://compose-spec.io), and can: - -1. [Set up Compose](/docs/compose/setting-up-compose). -1. [Manage multi-container applications defined in Compose files](/docs/compose/running-compose). - -Podman Desktop displays the multi-container applications that Compose creates as a container group. - -![Podman Desktop detects the multi-container applications that Compose creates as a container group.](img/compose-in-containers-view.png) diff --git a/website/docs/compose/running-compose.md b/website/docs/compose/running-compose.md deleted file mode 100644 index cafd74cd45..0000000000 --- a/website/docs/compose/running-compose.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Running Compose files -description: With Podman Desktop, you can manage multi-container applications defined in Compose files. -sidebar_position: 3 -keywords: [compose] -tags: [compose] ---- - -With Podman Desktop, you can manage multi-container applications defined in a Compose file. - -#### Prerequisites - -- Podman 4.7.0 or greater. -- [You have set up Compose](/docs/compose/setting-up-compose). -- [You have a Compose file](https://github.com/compose-spec/compose-spec/blob/master/spec.md#compose-file), such as `compose.yaml`. - -#### Procedure - -- Run the command in a terminal: - - ```shell-session - $ podman compose --file compose.yaml up --detach - ``` - -
    - - - (Alternatively) With an older Podman version, run `docker-compose`: - - - 1. [Set the DOCKER_HOST variable](/docs/migrating-from-docker/using-the-docker_host-environment-variable). - 1. Run `docker-compose` rather than `podman compose`: - - ```shell-session - $ docker-compose --file compose.yaml up --detach - ``` - -
    - -
    - - - (Optionally) Learn about Compose commands: - - - - ```shell-session - $ podman compose --help - ``` - -
    - -#### Verification - -1. The Compose engine starts the containers and services, and adds a label to each resource: - - Container label: `com.docker.compose.project` - - Service label: `com.docker.compose.service` - -1. Podman Desktop detects the Compose labels, and displays the container group as a group of containers. - - The Podman Desktop ** Containers** list displays the containers created by Compose grouped in a container group with a `(compose)` suffix, such as `flask-redis (compose)`. - -![img2](img/compose-in-containers-view.png) - -#### Additional resources - -- [Tutorial: Getting started with Compose](/tutorial/getting-started-with-compose). diff --git a/website/docs/compose/setting-up-compose.md b/website/docs/compose/setting-up-compose.md deleted file mode 100644 index b818a4e59c..0000000000 --- a/website/docs/compose/setting-up-compose.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Setting up Compose -description: Podman Desktop can install the Compose engine. -sidebar_position: 2 -keywords: [compose] -tags: [compose] ---- - -# Setting up Compose - -Podman Desktop can install the Compose engine. - -#### Procedure - -1. Go to ** Settings > Resources**. -1. In the **Compose** tile, click **Setup**, and follow the prompts. - -#### Verification - -1. The Compose reference implementation is in your `PATH`, therefore, you can display the Compose engine version in a terminal: - - ```shell-session - $ docker-compose version - ``` - -1. Podman detects the same Compose version: - - ```shell-session - $ podman compose version - ``` - -#### Next steps - -- [Run Compose](/docs/compose/running-compose). - -#### Additional resources - -- (Alternatively) Use an alternative Compose implementation in Python with Podman integration: [install Podman Compose](https://github.com/containers/podman-compose#installation). -- (Alternatively) [Download and install Compose yourself](https://github.com/docker/compose/releases). -- [Tutorial: Getting started with Compose](/tutorial/getting-started-with-compose). diff --git a/website/docs/compose/troubleshooting.md b/website/docs/compose/troubleshooting.md deleted file mode 100644 index 83a5e1f368..0000000000 --- a/website/docs/compose/troubleshooting.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: Troubleshooting Compose -description: Troubleshooting compose issues -sidebar_position: 3 -keywords: [compose] -tags: [compose] ---- - -# Troubleshooting Compose - -## Registry authentication issues - -The Compose binary will prioritize the configuration file `~/.docker/config` over Podman credentials. - -### Issues encountered - -`docker-credential-desktop` missing: - -```console -docker.credentials.errors.InitializationError: docker-credential-desktop not installed or not available in PATH -``` - -Authentication access: - -```console -Error response from daemon: {"message":"denied: requested access to the resource is denied"} -Error: executing /usr/local/bin/docker-compose up: exit status 18 -``` - -### Solution - -Delete the `~/.docker/config` to clear any errors. diff --git a/website/docs/containers/accessing-the-terminal.md b/website/docs/containers/accessing-the-terminal.md deleted file mode 100644 index 38b403e736..0000000000 --- a/website/docs/containers/accessing-the-terminal.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -sidebar_position: 31 -title: Access the terminal -description: Access the terminal of a container. -keywords: [podman desktop, podman, containers, terminal] -tags: [accessing-the-terminal] ---- - -# Accessing the container terminal - -You can access the terminal of a container to view its file system, interact with it using commands, or troubleshoot the environment in which it runs. - -#### Prerequisites - -Make sure you have: - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- [Started a containerized application](/blog/2024/10/05/kubernetes-blog#building-a-containerized-application). - -#### Procedure - -1. Go to the **Containers** component page. -1. Click the name of the container, such as `python-app`. The Container Details page opens. - ![python app container](img/python-app-container.png) -1. Select the **Terminal** tab. -1. Run any commands to interact with the container. For example, run the `ls -al` command to list hidden files. - ![interaction with container](img/interacting-with-container.png) -1. Click the **close** icon on the right side of the page. - - :::note - - Alternatively, click the **overflow menu** icon and then select the **Open Terminal** option to access the container terminal. - ![open the terminal](img/opening-terminal.png) - - ::: diff --git a/website/docs/containers/creating-a-pod.md b/website/docs/containers/creating-a-pod.md deleted file mode 100644 index 97aac4a86a..0000000000 --- a/website/docs/containers/creating-a-pod.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -sidebar_position: 40 -title: Create a pod -description: Creating a pod from selected containers. -keywords: [podman desktop, podman, containers, pods] -tags: [create-a-pod] ---- - -# Creating a pod from selected containers - -With Podman Desktop, you can create a pod from your selected containers and run it on the Podman container engine. - -Consider running containers in a pod to: - -1. Expose your `frontend` application to the public network. -2. Protect your `database` container in a private network. - -#### Prerequisites - -- You are using the Podman container engine. -- Your containers, such as `database` and `frontend`, running or stopped, are available on the Containers page. -- The `frontend` container is configured to access the service exposed by the `database` container on localhost, such as `localhost:5000`. - -#### Procedure - -1. Go to **Containers** from the left navigation pane. -1. Select the containers, such as `database` and `frontend`. - ![selecting containers](img/selecting-containers.png) -1. Click **Create Pod**. -1. Optional: Edit the name of the pod. The default name is `my-pod`. -1. Check that the correct ports are exposed. -1. Click **Create Pod**. - ![create pod button](img/create-pod-button.png) - -#### Verification - -1. View the newly created pod on the **Pods** component page. - ![pod created successfully](img/pod-created-successfully.png) -1. Click the name of the pod to view its logs. -1. Click the **Open Browser** icon. Your browser opens the service exposed by your `frontend-podified` container. - ![open browser icon](img/open-browser-icon.png) - -#### Additional resources - -- [Blog- Creating a pod](https://podman-desktop.io/blog/2024/10/05/kubernetes-blog#creating-a-pod) -- [Managing containers and pods](https://podman-desktop.io/tutorial/managing-your-application-resources#managing-containers-and-pods) diff --git a/website/docs/containers/images/building-an-image.md b/website/docs/containers/images/building-an-image.md deleted file mode 100644 index fb2a7d7221..0000000000 --- a/website/docs/containers/images/building-an-image.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 20 -title: Building an image -description: Building an image on your container engine. -keywords: [podman desktop, podman, containers, images] -tags: [images, building-an-image] ---- - -# Building an image on your container engine - -With Podman Desktop, you can build an image from a Containerfile on your container engine. - -#### Prerequisites - -- Your Containerfile: `Containerfile` or `Dockerfile`. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click **Build**. - ![build button](img/build-button.png) -1. Provide the following details: - - **Containerfile path**: Select the `Containerfile` or `Dockerfile` to build. - - **Build context directory**: The field automatically picks the context directory based on the file path. You can change the directory, if needed. - - **Image Name**: Enter your image name, such as `my-image`. If you want to push the image to a registry, use the fully qualified image name that your registry requires, such as `quay.io/my-repository/my-image`, `ghcr.io/my-repository/my-image`, or `docker.io/my-repository/my-image`. - - **Build arguments**: Pass the key and value for the arguments defined in your `Containerfile` or `Dockerfile`, if needed. - - **Platform**: Select multiple platforms on which you want to build the image. The default platform is `Intel and AMD x86_64 systems`. -1. Click **Build**. - ![building image from containerfile](img/building-image-from-containerfile.png) -1. Click **Done** after the image is built. - -#### Verification - -1. View the newly created image on the same page. - ![newly created image](img/newly-created-image.png) -1. Click the name of the image to view its summary. - ![image summary page](img/image-summary-page.png) -1. Optional: View the history of the image or inspect the image. diff --git a/website/docs/containers/images/img/build-button.png b/website/docs/containers/images/img/build-button.png deleted file mode 100644 index debc375876..0000000000 Binary files a/website/docs/containers/images/img/build-button.png and /dev/null differ diff --git a/website/docs/containers/images/img/building-image-from-containerfile.png b/website/docs/containers/images/img/building-image-from-containerfile.png deleted file mode 100644 index ec4f469784..0000000000 Binary files a/website/docs/containers/images/img/building-image-from-containerfile.png and /dev/null differ diff --git a/website/docs/containers/images/img/image-pulled-successfully.png b/website/docs/containers/images/img/image-pulled-successfully.png deleted file mode 100644 index a1b42f67e1..0000000000 Binary files a/website/docs/containers/images/img/image-pulled-successfully.png and /dev/null differ diff --git a/website/docs/containers/images/img/image-summary-page.png b/website/docs/containers/images/img/image-summary-page.png deleted file mode 100644 index 75c043530d..0000000000 Binary files a/website/docs/containers/images/img/image-summary-page.png and /dev/null differ diff --git a/website/docs/containers/images/img/image-tag-selected.png b/website/docs/containers/images/img/image-tag-selected.png deleted file mode 100644 index ff0f0ee202..0000000000 Binary files a/website/docs/containers/images/img/image-tag-selected.png and /dev/null differ diff --git a/website/docs/containers/images/img/newly-created-image.png b/website/docs/containers/images/img/newly-created-image.png deleted file mode 100644 index 60f2098fbb..0000000000 Binary files a/website/docs/containers/images/img/newly-created-image.png and /dev/null differ diff --git a/website/docs/containers/images/img/pull-button.png b/website/docs/containers/images/img/pull-button.png deleted file mode 100644 index 5181f3877a..0000000000 Binary files a/website/docs/containers/images/img/pull-button.png and /dev/null differ diff --git a/website/docs/containers/images/img/pull-image-button.png b/website/docs/containers/images/img/pull-image-button.png deleted file mode 100644 index f26841feab..0000000000 Binary files a/website/docs/containers/images/img/pull-image-button.png and /dev/null differ diff --git a/website/docs/containers/images/img/pushing-an-image.png b/website/docs/containers/images/img/pushing-an-image.png deleted file mode 100644 index 7bde648a63..0000000000 Binary files a/website/docs/containers/images/img/pushing-an-image.png and /dev/null differ diff --git a/website/docs/containers/images/index.md b/website/docs/containers/images/index.md deleted file mode 100644 index 5efd361448..0000000000 --- a/website/docs/containers/images/index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -sidebar_position: 4 -title: Images -description: Working with container images -tags: [podman-desktop, containers, images] -keywords: [podman desktop, podman, containers, images] -hide_table_of_contents: false ---- - -# Working with container images - -With Podman Desktop, you can work with OCI images for your container engine workloads: - -- [Build an image from a Containerfile on your container engine](/docs/containers/images/building-an-image). -- [Push an image to a registry](/docs/containers/images/pushing-an-image-to-a-registry). -- [Pull an image from a registry](/docs/containers/images/pulling-an-image). diff --git a/website/docs/containers/images/pulling-an-image.md b/website/docs/containers/images/pulling-an-image.md deleted file mode 100644 index bf7e452ab0..0000000000 --- a/website/docs/containers/images/pulling-an-image.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -sidebar_position: 22 -title: Pulling an image -description: Pulling an image to your container engine. -keywords: [podman desktop, podman, containers, images] -tags: [images, pulling-an-image] ---- - -# Pulling an image to your container engine - -With Podman Desktop, you can pull an image from a registry to your container engine. - -#### Prerequisites - -- The image is available in a registry. -- If the registry or the image is not publicly available, you configured access to the registry on Podman Desktop in ** Settings > Registries**. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click **Pull**. - ![pull button](img/pull-button.png) -1. Enter the image name, such as `quay.io/podman/hello`. - - :::note - - Prefer the fully qualified image name that specifies the registry over the short name, which might lead to registry resolution errors. - - ::: - -1. Click **Pull image**. - ![pull button](img/pull-image-button.png) -1. Click **Done**. - -#### Verification - -1. View the pulled image on the same page. - ![image pulled successfully](img/image-pulled-successfully.png) -1. Click the name of the image to view its summary. -1. Optional: View the history of the image or inspect the image. diff --git a/website/docs/containers/images/pushing-an-image-to-a-registry.md b/website/docs/containers/images/pushing-an-image-to-a-registry.md deleted file mode 100644 index 589ca113cd..0000000000 --- a/website/docs/containers/images/pushing-an-image-to-a-registry.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 21 -title: Pushing an image to a registry -description: Pushing an image to a registry. -keywords: [podman desktop, podman, containers, image, registry, registries] -tags: [images, pushing-an-image] ---- - -# Pushing an image to a registry - -With Podman Desktop, you can push an image to registries. - -#### Prerequisites - -- You have configured your registry ** Settings > Registries**. -- You have built an image with the fully qualified name required for your registry, such as `quay.io/my-repository/my-image`, `ghcr.io/my-repository/my-image`, or `docker.io/my-repository/my-image`. -- Ensure that the image name includes the registry where to publish the image. To publish on `quay.io/repository` the image `my-image`, the FQN image name should be `quay.io/repository/my-image`. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click the **overflow menu** icon corresponding to the image you want to push and select **Push Image**. The image tag is auto-selected. - ![pushing an image](img/pushing-an-image.png) -1. Click **Push image**. - ![image tag selected](img/image-tag-selected.png) -1. Click **Done**. - -#### Verification - -1. Go to your container registry. -1. Find your image. diff --git a/website/docs/containers/img/analyzing-the-logs.png b/website/docs/containers/img/analyzing-the-logs.png deleted file mode 100644 index b19da974c8..0000000000 Binary files a/website/docs/containers/img/analyzing-the-logs.png and /dev/null differ diff --git a/website/docs/containers/img/create-pod-button.png b/website/docs/containers/img/create-pod-button.png deleted file mode 100644 index 856304642d..0000000000 Binary files a/website/docs/containers/img/create-pod-button.png and /dev/null differ diff --git a/website/docs/containers/img/hello-world-program-running.png b/website/docs/containers/img/hello-world-program-running.png deleted file mode 100644 index 694eef089b..0000000000 Binary files a/website/docs/containers/img/hello-world-program-running.png and /dev/null differ diff --git a/website/docs/containers/img/interacting-with-container.png b/website/docs/containers/img/interacting-with-container.png deleted file mode 100644 index 54053c89ed..0000000000 Binary files a/website/docs/containers/img/interacting-with-container.png and /dev/null differ diff --git a/website/docs/containers/img/open-browser-icon.png b/website/docs/containers/img/open-browser-icon.png deleted file mode 100644 index bc079c42db..0000000000 Binary files a/website/docs/containers/img/open-browser-icon.png and /dev/null differ diff --git a/website/docs/containers/img/opening-logs.png b/website/docs/containers/img/opening-logs.png deleted file mode 100644 index 414f63d6f6..0000000000 Binary files a/website/docs/containers/img/opening-logs.png and /dev/null differ diff --git a/website/docs/containers/img/opening-terminal.png b/website/docs/containers/img/opening-terminal.png deleted file mode 100644 index 7b2a08275f..0000000000 Binary files a/website/docs/containers/img/opening-terminal.png and /dev/null differ diff --git a/website/docs/containers/img/pod-created-successfully.png b/website/docs/containers/img/pod-created-successfully.png deleted file mode 100644 index 1dba7b12be..0000000000 Binary files a/website/docs/containers/img/pod-created-successfully.png and /dev/null differ diff --git a/website/docs/containers/img/python-app-container.png b/website/docs/containers/img/python-app-container.png deleted file mode 100644 index 34a3506294..0000000000 Binary files a/website/docs/containers/img/python-app-container.png and /dev/null differ diff --git a/website/docs/containers/img/running-an-image.png b/website/docs/containers/img/running-an-image.png deleted file mode 100644 index c4262a5b0a..0000000000 Binary files a/website/docs/containers/img/running-an-image.png and /dev/null differ diff --git a/website/docs/containers/img/selecting-containers.png b/website/docs/containers/img/selecting-containers.png deleted file mode 100644 index 3c9352bd06..0000000000 Binary files a/website/docs/containers/img/selecting-containers.png and /dev/null differ diff --git a/website/docs/containers/img/starting-a-container.png b/website/docs/containers/img/starting-a-container.png deleted file mode 100644 index 558936c378..0000000000 Binary files a/website/docs/containers/img/starting-a-container.png and /dev/null differ diff --git a/website/docs/containers/index.md b/website/docs/containers/index.md deleted file mode 100644 index 7674ea7f24..0000000000 --- a/website/docs/containers/index.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 30 -title: Containers -description: Working with container workloads -tags: [podman-desktop, containers] -keywords: [podman desktop, podman, containers] -hide_table_of_contents: false ---- - -# Working with containers - -With Podman Desktop, you can manage your container engine workloads. - -#### Prerequisites - -- A container engine. - -#### Procedure - -[![Working with containers flow](https://mermaid.ink/img/pako:eNptkt2OmzAQhV_FchVxw0ZAQvi5201VqequtOpWrVpxY_BArDV2ZA_t0ijvXgNNINLe4PGcb47HZk600hxoTlerU6EIEUpgTsaQEE_q5hF-g_Ry4nEou8bz_yt4gBaGdMks3Ga_MyNYKcF6VyMnHY1omen3Wmoz1H1Iy7iqd5fSmfgGbzhTVVUtEAuVVvzWpk5DvlkwCAbFDcKTKKzr92wetOFgZjIIggVWa4WfWCtkP4iWKXtnwYjam4jzsLjPebUqFIoWpFAwRCiB_NDmVaiG_BF4IO40ZE40tlBfoREWjQA7ueTkuSulqC67-849ojOpGLr6Qn1uWTOzD52QfPCtjW7J_uJbCwmznT0MBGrCiJlO62dRymv5Ut4vWpzQF2QGZ1YRMXRSqGfNr8zeAFswxAISXd_c9z3wS1eCUYBgyc_7p0fq0xZMywR3czhOTEHHSSpo7kL3o14L6t7ZcaxD_dKriuZoOvBpd-QM4aNgjWEtzWsmrcsCF6jN0zTY43z79MgUzU_0jeZ3m816k8RRHMfpNgl3qU97mm-y7TrI0iTepmmUhlF89ulfrZ1puA6SLA6iNNplWZJkyWj2a9SGLs7_AF26CAk?type=png)](https://mermaid.live/edit#pako:eNptkt2OmzAQhV_FchVxw0ZAQvi5201VqequtOpWrVpxY_BArDV2ZA_t0ijvXgNNINLe4PGcb47HZk600hxoTlerU6EIEUpgTsaQEE_q5hF-g_Ry4nEou8bz_yt4gBaGdMks3Ga_MyNYKcF6VyMnHY1omen3Wmoz1H1Iy7iqd5fSmfgGbzhTVVUtEAuVVvzWpk5DvlkwCAbFDcKTKKzr92wetOFgZjIIggVWa4WfWCtkP4iWKXtnwYjam4jzsLjPebUqFIoWpFAwRCiB_NDmVaiG_BF4IO40ZE40tlBfoREWjQA7ueTkuSulqC67-849ojOpGLr6Qn1uWTOzD52QfPCtjW7J_uJbCwmznT0MBGrCiJlO62dRymv5Ut4vWpzQF2QGZ1YRMXRSqGfNr8zeAFswxAISXd_c9z3wS1eCUYBgyc_7p0fq0xZMywR3czhOTEHHSSpo7kL3o14L6t7ZcaxD_dKriuZoOvBpd-QM4aNgjWEtzWsmrcsCF6jN0zTY43z79MgUzU_0jeZ3m816k8RRHMfpNgl3qU97mm-y7TrI0iTepmmUhlF89ulfrZ1puA6SLA6iNNplWZJkyWj2a9SGLs7_AF26CAk) - -1. [Work with registries](/docs/containers/registries). - 1. [Authenticate to pre-configured registries](/docs/containers/registries). - 1. [Add an insecure registry](/docs/containers/registries). - -1. [Work with images](/docs/containers/images). - 1. [Build an image from Containerfile](/docs/containers/images/building-an-image). - 1. [Push an image](/docs/containers/images/pushing-an-image-to-a-registry). - 1. [Pull an image](/docs/containers/images/pulling-an-image). -1. [Start a container from an image](/docs/containers/starting-a-container). -1. [Create a pod from selected containers](/docs/containers/creating-a-pod). diff --git a/website/docs/containers/onboarding.md b/website/docs/containers/onboarding.md deleted file mode 100644 index 4d45cdf79b..0000000000 --- a/website/docs/containers/onboarding.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -sidebar_position: 2 -title: Onboarding for containers -description: To run container workloads with Podman Desktop, set up at least one container engine. -tags: [podman-desktop, onboarding] -keywords: [podman desktop, containers, podman, onboarding] ---- - -# Onboarding for container workloads - -To run container workloads, set up at least one container engine. - -Podman Desktop does not automatically set up container engine resources that you might not need. - -#### Procedure - -1. Select a container engine supporting your workload. - - | Workload | Podman | Native Docker | Docker Desktop | - | :------------------ | :----: | :-----------: | :------------: | - | Rootless containers | ✅ yes | ✅ yes | ❌ no | - | Rootful containers | ✅ yes | ✅ yes | ✅ yes | - | Compose | ✅ yes | ✅ yes | ✅ yes | - | Pods | ✅ yes | ❌ no | ❌ no | - - Podman supports rootless container and pods, in addition to rootful containers and Compose. - -2. Select an execution environment supporting your container engine and your operating system. - - Select a Podman execution environment: - - | Host operating system | Native containers | Podman Machine | Lima instance | - | :-------------------- | :---------------: | :-------------: | :-------------: | - | Windows | ❌ no | ✅ yes | ❌ experimental | - | macOS | ❌ no | ✅ yes | ✅ yes | - | Linux | ✅ yes | ❌ experimental | ✅ yes | - - - Select a Docker execution environment: - - | Host operating system | Native containers | Docker Desktop | Lima instance | - | :-------------------- | :---------------: | :------------: | :-------------: | - | Windows | ❌ no | ✅ yes | ❌ experimental | - | macOS | ❌ no | ✅ yes | ✅ yes | - | Linux | ✅ yes | ✅ yes | ✅ yes | - -3. Setup your container engine. - - Podman Desktop assists you to set up Podman and Podman machines on Windows and macOS. - - [Installing Podman](/docs/installation). - - [Creating a Podman machine with Podman Desktop](/docs/podman/creating-a-podman-machine). - - - Podman Desktop consumes your native containers, Lima instance or Docker setup. - - [Creating a Lima instance](/docs/lima/creating-a-lima-instance). - - [Installing Podman on Linux](https://podman.io/docs/installation#installing-on-linux). diff --git a/website/docs/containers/registries/configuring-mirror-registries.md b/website/docs/containers/registries/configuring-mirror-registries.md deleted file mode 100644 index 381be36737..0000000000 --- a/website/docs/containers/registries/configuring-mirror-registries.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -sidebar_position: 4 -title: Configuring a mirror registry -description: Covers the procedure to configure a mirror registry -keywords: [podman desktop, podman, mirroring, registry, configuration] -tags: [registry-configuration, mirroring-a-registry] ---- - -# Configuring a mirror registry - -You can add registry locations and configure their mirrors by using the Podman Desktop UI. By configuring a mirror, you can redirect a registry to another location and use its content, such as images, files, and other resources during development. - -#### Prerequisites - -- [Recreate your Podman machine](/docs/podman/creating-a-podman-machine) to mount the registry configuration file. -- Upgrade to the latest version of Podman. - -#### Procedure - -1. Go to the **Settings > Resources** page. -1. Select **More Options > Setup registry configuration** in the Podman tile. A command palette opens. - ![Set up registry configuration](img/setting-up-registry-configuration.png) -1. Set up your registry configuration: - 1. Select the **Add registry configuration** option from the command palette. - ![adding registry configuration](img/add-registry-configuration.png) - 1. Type the location of the registry, such as `quay.io`, and press the `Enter` key. The `quay.io` registry is added to the palette. - ![quay.io registry added](img/quay-option-added.png) - 1. Select `quay.io` from the command palette. - 1. Type the location where you want to mirror the registry, such as `ghcr.io`, and press the `Enter` key. The entry for the `quay.io` registry shows the location where it is mirrored. - ![mirrored registry location](img/mirrored-registry.png) - 1. Select the `End configuring registries` option to end registry configuration. - ![ending registry configuration](img/end-configuring-registries.png) diff --git a/website/docs/containers/registries/img/add-registry-configuration.png b/website/docs/containers/registries/img/add-registry-configuration.png deleted file mode 100644 index 5710f9ca79..0000000000 Binary files a/website/docs/containers/registries/img/add-registry-configuration.png and /dev/null differ diff --git a/website/docs/containers/registries/img/adding-a-custom-registry.png b/website/docs/containers/registries/img/adding-a-custom-registry.png deleted file mode 100644 index 252ddc0359..0000000000 Binary files a/website/docs/containers/registries/img/adding-a-custom-registry.png and /dev/null differ diff --git a/website/docs/containers/registries/img/authenticating-to-a-preconfigured-registry.png b/website/docs/containers/registries/img/authenticating-to-a-preconfigured-registry.png deleted file mode 100644 index ebb5fd36c5..0000000000 Binary files a/website/docs/containers/registries/img/authenticating-to-a-preconfigured-registry.png and /dev/null differ diff --git a/website/docs/containers/registries/img/end-configuring-registries.png b/website/docs/containers/registries/img/end-configuring-registries.png deleted file mode 100644 index 1ee0a5d2d9..0000000000 Binary files a/website/docs/containers/registries/img/end-configuring-registries.png and /dev/null differ diff --git a/website/docs/containers/registries/img/mirrored-registry.png b/website/docs/containers/registries/img/mirrored-registry.png deleted file mode 100644 index 9dc832bc61..0000000000 Binary files a/website/docs/containers/registries/img/mirrored-registry.png and /dev/null differ diff --git a/website/docs/containers/registries/img/quay-option-added.png b/website/docs/containers/registries/img/quay-option-added.png deleted file mode 100644 index 568505e2d6..0000000000 Binary files a/website/docs/containers/registries/img/quay-option-added.png and /dev/null differ diff --git a/website/docs/containers/registries/img/registries.png b/website/docs/containers/registries/img/registries.png deleted file mode 100644 index 72cd232da2..0000000000 Binary files a/website/docs/containers/registries/img/registries.png and /dev/null differ diff --git a/website/docs/containers/registries/img/registry-warning-insecure.png b/website/docs/containers/registries/img/registry-warning-insecure.png deleted file mode 100644 index bf46f455a1..0000000000 Binary files a/website/docs/containers/registries/img/registry-warning-insecure.png and /dev/null differ diff --git a/website/docs/containers/registries/img/setting-up-registry-configuration.png b/website/docs/containers/registries/img/setting-up-registry-configuration.png deleted file mode 100644 index eaaf9fd118..0000000000 Binary files a/website/docs/containers/registries/img/setting-up-registry-configuration.png and /dev/null differ diff --git a/website/docs/containers/registries/index.md b/website/docs/containers/registries/index.md deleted file mode 100644 index ab79a9e711..0000000000 --- a/website/docs/containers/registries/index.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -sidebar_position: 3 -title: Registries -description: Working with container registries -tags: [podman-desktop, containers] -keywords: [podman desktop, podman, containers, registries] -hide_table_of_contents: false -image: /img/docs/containers/registries/img/registries.png ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Setting up container registries - -:::tip[Before you start] - -Before you start, you should: - -- Get authentication details for your container registry: - - Registry URL. - - User name. - - Password, or OAuth secret. -- Get the fully qualified name of a private image stored in your registry, such as `my-registry.tld/my-repository/my-image`. -- Get the fully qualified image name that your registry requires to push an image, such as `my-registry.tld/my-repository/my-image`. - -::: - -## Setting up a pre-configured registry - -To ease usage of the most popular container registries, Podman Desktop has pre-configured registries, including: - -- Docker Hub -- Red Hat Quay -- GitHub -- Google Container Registry - -If your container registry is in this list, follow the steps: - -1. Go to ** Settings > Registries**. -2. In your registry line, click **Configure**. -3. Enter your registry credentials: - - ![Authenticating to a pre-configured registry](img/authenticating-to-a-preconfigured-registry.png) - 1. **Username**: Enter your user name. - 2. **Password**: Enter your password or OAuth secret. - 3. Click **Login**. - -Podman Desktop logs Podman in with the provided credentials. - -If you enter the wrong credentials, you see an error message: - -1. Enter the correct credentials -2. Click **Login** again. - -## Setting up a custom registry - -You have a custom container registry, or one that is not available in the pre-configured list: we have got you covered. Follow the steps: - -1. Go to ** Settings > Registries**. -1. Click ** Add registry** at the top right corner of the screen. -1. Enter your registry details: - - ![Adding a custom registry](img/adding-a-custom-registry.png) - 1. **Registry Location**: Enter your repository URL, such as `https://myregistry.tld`. - 2. **Username**: Enter your user name. - 3. **Password**: Enter your password or OAuth secret. - 4. Click **Login**. - -Podman Desktop logs Podman in with the provided credentials. - -If you enter the wrong credentials, you see an error message: - -1. Enter the correct credentials -2. Click **Login** again. - -## Setting up a registry with an insecure certificate - -If your registry has an insecure certificate, such as a self-signed certificate, you see a warning when setting up the registry. - -1. In the **Invalid Certificate** window, click **Yes** to add the registry anyway. - - ![Podman Desktop Registry Warning](img/registry-warning-insecure.png) - -1. Tell Podman that it has your authorization to access the insecure registry: edit the `registries.conf` file. - 1. Go to a location where you can edit the `registries.conf` file: - - - - - The configuration file is in the Podman machine: open a terminal in the Podman Machine. - - ```shell-session - $ podman machine ssh --username root [optional-machine-name] - ``` - - - - - The configuration file is in the Podman machine: open a terminal in the Podman Machine. - - ```shell-session - $ podman machine ssh --username root [optional-machine-name] - ``` - - - - - The configuration file is in your host: open a terminal with superuser privileges. - - ```shell-session - $ sudo su - - ``` - - - - - 1. Edit the registry optional configuration file. - - ```shell-session - # vi /etc/containers/registries.conf - ``` - - For each insecure registry, add a `[[registry]]` section that defines: - - `location =`: Enter your registry URL. - - `insecure = true`: Accept the insecure certificate. - - For example, if your have two registries, such as `https://my-registry.tld` and `http://registry.example.com`, add the following lines: - - ```toml - [[registry]] - location = "my-registry.tld" - insecure = true - - [[registry]] - location = "registry.example.com" - insecure = true - ``` - -1. Restart Podman to apply the changes. - - - - 1. Go to ** Settings > Resources**. - 1. Restart the Podman machine. - - - - 1. Go to ** Settings > Resources**. - 1. Restart the Podman machine. - - - - - Stop all Podman processes. - - ```shell-session - $ pkill podman - ``` - - - - - Restart Podman. - - ```shell-session - $ sudo systemctl restart podman - ``` - - - - -## Verifying your registry setup - -To verify your registry has been properly configured, you can do the following steps: - -1. In ** Settings > Registries**, the line with your registry has content in the Username and Password column, and action icons replacing the Configure button. - -1. Pull a private image from the registry. - 1. Get the name of a private image stored in your registry, such as `quay.io/my-repository/my-image`, `ghcr.io/my-repository/my-image`, `docker.io/my-repository/my-image`, or `my-registry.tld/my-repository/my-image`. - 1. Go to **Images**. - 1. Click **Pull an image**. - 1. On the **Image to Pull** screen: - 1. **Image to pull**: Enter the image name. - 1. Click Pull image. - 1. Click Done. - -1. Push an image to the registry: - 1. Get the fully qualified image name that your registry requires, such as `quay.io/my-repository/my-image`, `ghcr.io/my-repository/my-image`, or `docker.io/my-repository/my-image`. - 1. Go to **Images**. - 1. Click **Build an image**. - 1. On the **Build Image from Containerfile** screen - 1. **Containerfile path**: select the Containerfile or Dockerfile to build. - 2. **Image Name**: enter the fully qualified image name that your registry requires. - 3. Click Build. - 4. Click Done. - 1. On your image line, click ****. - - The contextual menu has a **Push Image to _your registry_** entry. - -## Changing your credentials - -To change your registry credentials, you can do the following steps: - -1. Go to ** Settings > Registries**. -1. On your registry line, click ****. -1. Click **Edit password**. -1. Enter your credentials in the **Username** and **Password** fields, and click **Login**. - -Podman Desktop logs Podman in with the updated credentials. - -## Removing a registry - -To remove your registry, you can do the following steps: - -1. Go to ** Settings > Registries**. -1. On your registry line, click ****. -1. Click Remove. - -Podman Desktop removes the registry from the settings, and logs Podman out from the registry. - -## Finding Podman registry configuration files - -Podman has two files to configure registries: - -- `auth.json` defines the authentication to registries. - - Podman Desktop stores this file directly on your host, in `$HOME/.config/containers/auth.json`. - - The Podman machine mounts the authentication configuration file to access it. - - When you delete the Podman machine, the registry configuration is not lost: it stays on your host. - -- `registries.conf` defines optional features, such as allowing insecure certificates. - - The Podman machine stores the file in `/etc/containers/registries.conf`. - - When you delete the Podman machine, this file is deleted. - -## Next steps - -Consider completing some other common tasks that depend registries: - -- Pulling an image. -- Pushing an image to a registry. diff --git a/website/docs/containers/starting-a-container.md b/website/docs/containers/starting-a-container.md deleted file mode 100644 index 32d344f718..0000000000 --- a/website/docs/containers/starting-a-container.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -sidebar_position: 30 -title: Start a container -description: Starting a container on your container engine. -keywords: [podman desktop, podman, containers, images] -tags: [starting-a-container] ---- - -# Starting a container on your container engine - -With Podman Desktop, you can start a container from an image on your container engine. -You can interact with the running container by using the terminal or by opening your browser to the exposed ports. - -#### Prerequisites - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- The **Images** list has your image, such as `quay.io/podman/hello`. See [Pulling an image](/docs/containers/images/pulling-an-image). - -#### Procedure - -1. Go to the **Images** component page. -1. Click the **Run Image** icon corresponding to the image you want to run. For example, `quay.io/podman/hello`. - ![Run Image icon](img/running-an-image.png) -1. Review or edit the container configuration details. -1. Click **Start Container**. The Container Details page opens. - ![starting a container](img/starting-a-container.png) -1. Select the **Logs** tab to view the program running. - ![program running](img/hello-world-program-running.png) -1. Click the **close** icon on the right side of the page. - -#### Verification - -1. Go to the **Containers** component page. -1. ****: Enter your image name, such as `quay.io/podman/hello`, to find your running container. -1. Click your running container name to perform any of the following tasks: - - [View the logs](/docs/containers/viewing-container-logs) - - Inspect the container - 1. Go to **Inspect**. - 1. Click the content area to activate it. - 1. Press Ctrl + F on Windows and Linux, or + F on macOS to start searching the content. - - Generate a Kubernetes YAML manifest when your container engine is Podman: - 1. Go to **Kube**. - 1. Click the content area to activate it. - 1. Press Ctrl + F on Windows and Linux, or + F on macOS to start searching the content. - 1. Optional: Copy the content to a YAML file. - 1. Optional: Reuse this file to create a pod that you can run on a Podman engine or a Kubernetes cluster. See [Pod creation with Kubernetes YAML](https://podman-desktop.io/blog/2024/10/05/kubernetes-blog#creating-a-pod). - - [Access the terminal](/docs/containers/accessing-the-terminal) - - Access the application by exposing a port: - 1. Click ****. - 1. View the running application at `localhost:port` in your browser. - - [Deploy the container to a Kubernetes cluster](/docs/kubernetes/deploying-a-pod-to-kubernetes) - -#### Additional resources - -- [Building a containerized application](/blog/2024/10/05/kubernetes-blog#building-a-containerized-application) -- [Running a pod using a container or docker file](/tutorial/running-a-pod-using-a-container-docker-file) -- [Managing containers and pods](/tutorial/managing-your-application-resources#managing-containers-and-pods) diff --git a/website/docs/containers/viewing-container-logs.md b/website/docs/containers/viewing-container-logs.md deleted file mode 100644 index 99b473e33c..0000000000 --- a/website/docs/containers/viewing-container-logs.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -sidebar_position: 32 -title: View the logs -description: View the logs of a container. -keywords: [podman desktop, podman, containers, logs] -tags: [viewing-container-logs, container-logs] ---- - -# Viewing the container logs - -You can view the logs of a container to identify and resolve any issues with the container or examine the record of triggered events. - -#### Prerequisites - -Make sure you have: - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- [Started a containerized application](/blog/2024/10/05/kubernetes-blog#building-a-containerized-application). - -#### Procedure - -1. Go to the **Containers** component page. -1. Click the name of the container, such as `python-app`. - ![python app container](img/python-app-container.png) -1. Select the **Logs** tab to analyze the logs of the container. -1. Optional: Type any keyword in the box to search for it in the logs. - ![analyze the logs](img/analyzing-the-logs.png) -1. Click the **close** icon on the right side of the page. - - :::note - - Alternatively, click the **overflow menu** icon and then select the **Open Logs** option to view the container logs. - ![open the logs](img/opening-logs.png) - - ::: diff --git a/website/docs/discover-podman-desktop.md b/website/docs/discover-podman-desktop.md deleted file mode 100644 index 60b99b4a14..0000000000 --- a/website/docs/discover-podman-desktop.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -sidebar_position: 2 -title: Discover Podman Desktop -description: Discovering the Podman Desktop application -keywords: [podman desktop, podman, Discover features] -tags: [podman-desktop, discover-podman-desktop, features] ---- - -# Discover Podman Desktop - -Podman Desktop is a developer tool that you can use to perform basic and advanced tasks. From creating a container or pod to setting up a kubernetes cluster to integrating your local tools using extensions, Podman Desktop offers all these features to ease your daily development tasks. But, what makes Podman Desktop different from other community-driven tools? - -The key differentiator is its intuitive UI that allows you to visualize your development environment, such as the number of pods or Kubernetes clusters running or the number of services exposed. - -In addition, you can run this tool on three different operating systems; macOS, Windows, and Linux. Podman Desktop runs your workloads on a Podman engine and therefore, provides you Podman-native capabilities to interact with your applications. But, this does not mean you cannot run your Docker applications. You can use the Docker compatibility UI feature for a smooth transition to Podman Desktop. - -Let’s quickly explore this tool’s key functionalities: - -- [Manage containerized environment](/docs/discover-podman-desktop#manage-containerized-environment) -- [Podify your containers](/docs/discover-podman-desktop#podify-your-containers) -- [Manage images](/docs/discover-podman-desktop#manage-images) -- [Manage Kubernetes-based environment](/docs/discover-podman-desktop#manage-kubernetes-based-environment) -- [Manage Docker compatibility](/docs/discover-podman-desktop#manage-docker-compatibility) -- [Integrate your tools using extensions](/docs/discover-podman-desktop#integrate-your-tools-using-extensions) -- [Manage settings](/docs/discover-podman-desktop#manage-settings) -- [Customize the UI](/docs/discover-podman-desktop#customize-the-ui) - -### Manage containerized environment - -![containers](img/containers-component.png) - -Access the **Containers** page in the UI to do the container-related tasks: - -- Create a container with a Containerfile or Dockerfile or from an existing image. -- Create a pod from a list of containers. -- [Deploy a container to a Kubernetes cluster](/docs/kubernetes/deploying-a-pod-to-kubernetes). -- Search, start, restart, stop, or delete a container. -- Bulk deletion of selected containers. -- Export a container to your local machine. -- View the summary and [logs](/docs/containers/viewing-container-logs) of a container. -- View the Kubernetes pod definition of the container. -- [Interact with the container using a terminal](/docs/containers/accessing-the-terminal). -- View containers based on their status, whether they are running or stopped. -- Remove all unused containers for efficient memory usage. - -For more details, refer to [Working with containers](/docs/containers). - -### Podify your containers - -![pods](img/pods-component.png) - -Access the **Pods** page in the UI to do the pod-related tasks: - -- [Create a pod](/blog/2024/10/05/kubernetes-blog#creating-a-pod) from a Kubernetes YAML file or selected containers. -- Search, start, restart, stop, or delete a pod. -- View the status of containers that are part of the pod, whether they are running, created, exited, or in waiting state. -- View the summary and logs of the pods -- Generate and view configuration for a Kubernetes pod. -- [Deploy a pod to a Kubernetes cluster](/docs/kubernetes/deploying-a-pod-to-kubernetes). -- Interact with the pod containers using a terminal. -- View pods based on their status, whether they are running or stopped. -- Remove all unused pods for efficient memory usage. - -### Manage images - -![images](img/images-component.png) - -Access the **Images** page in the UI to do the image-related tasks: - -- Build an image from a Containerfile or Dockerfile. -- Pull an image from a registry. -- Search or delete an image. -- Create a container from the image. -- Push an image to a configured registry. -- Edit the image name and tag. -- View the image history. -- Save an image to your local machine. -- Push an image to a cluster internal registry. -- Import images from your local machine into the container engine. - -For more details, refer to [Working with container images](/docs/containers/images). - -### Manage Kubernetes-based environment - -![kubernetes](img/kubernetes-component.png) - -Access the **Kubernetes** page in the UI to do the Kubernetes-related tasks: - -- Create a Kubernetes resource after [applying a Kubernetes YAML configuration](/docs/kubernetes/applying-a-yaml-manifest). -- Search or delete a Kubernetes resource. -- View the summary and Kubernetes configuration of the Kubernetes resource. -- [Edit the configuration](/docs/kubernetes/configuring-editing-kube-object#procedure-updating-an-existing-object) of the Kubernetes resource and apply those changes to your cluster directly from the UI. - -For more details, refer to [From containers to Kubernetes](/docs/kubernetes). - -### Manage Docker compatibility - -Docker compatibility is a way to configure an environment in which you can run your Docker applications, commands, and tools on a Podman engine without reconfiguration. It encompasses the following stages: - -1. [Import your saved containers](/docs/migrating-from-docker/importing-saved-containers) into Podman Desktop using the CLI. -1. Enable the [Docker compatibility](/docs/migrating-from-docker/customizing-docker-compatibility#enable-docker-compatibility) feature. -1. Access the [Docker Compatibility settings](/docs/migrating-from-docker/managing-docker-compatibility) to configure a Docker-compatible environment. - -### Integrate your tools using extensions - -![extensions](img/extentions-component.png) - -Podman Desktop provides a wide range of extensions that can be used to integrate your local tools with Podman Desktop. After installing the required extension, you can do development tasks like creating a Kubernetes cluster, creating an AI application, or creating a bootable container. List of extensions available: - -#### Built-in extensions - -- Compose -- Podman -- Docker -- Kind -- Minikube -- Lima -- Kube Context -- Kubectl CLI -- Registries - -#### Other extensions - -- Bootable containers -- Image Layers Explorer -- Headlamp -- Podman AI Lab -- Red Hat extensions - -#### Manage extensions - -You can enable or disable an extension, if needed. You can also create your own custom extensions to add icons, UI components, or menus to your application front-end page. - -For more details, refer to [Extensions](/docs/extensions). - -### Manage settings - -![settings](img/settings.png) - -Access the **Settings** page in the UI to do these tasks: - -- Create resources for your development environment - - [Creating a Podman machine](/docs/podman/creating-a-podman-machine) - - [Creating a Kind cluster](/docs/kind/creating-a-kind-cluster) - - [Creating a Minikube cluster](/docs/minikube/creating-a-minikube-cluster) -- [Configure proxy URLs](/docs/proxy#using-a-proxy) -- [Configure a registry](/docs/containers/registries) -- [Configure a CLI tool](/tutorial/managing-your-application-resources#managing-other-resources) -- [Configure a Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context#procedure-using-the-podman-desktop-settings) - -#### Experimental features - -The experimental features are in-progress features released to collect user feedback. The Podman Desktop UI offers a dedicated page for viewing all experimental features. Access the page by navigating to **Settings > Experimental**. - -![experimental features page](img/experimental-features.png) - -The page helps you to: - -- Enable all experimental features simultaneously or enable an experimental feature individually. -- Share any feedback or suggestions on the dedicated discussion pages created on GitHub. - -### Customize the UI - -You can tailor the UI to keep your workspace tidy. For example, the left navigation pane may become cluttered as new extensions are added. To improve your experience, customize it to show only the components you need. - -**Customize the left navigation pane** - -By default, all UI components are selected and visible in the pane. To deselect any UI components that you do not want to view: - -- Linux and Windows: Right-click the left navigation pane. -- macOS: Press Control and click. - -**Customize other UI elements** - -Go to **Settings > Preferences** and configure the required settings. You can set the following: - -- UI appearance -- Navigation bar layout -- Provider's view in the status bar (experimental) -- Running tasks' view in the status bar (experimental) diff --git a/website/docs/extensions/api/index.md b/website/docs/extensions/api/index.md deleted file mode 100644 index a1a0a440d7..0000000000 --- a/website/docs/extensions/api/index.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -sidebar_position: 3 -title: API Reference -description: API Reference -tags: [podman-desktop, extension, api] -keywords: [podman desktop, extension, api] ---- - -The extension API reference is located [here](/api). - -Based on your use cases, you can add these common capabilities to your extension: - -- Create a new CLI tool instance and register it with Podman Desktop, see the details [here](https://podman-desktop.io/api/@podman-desktop/namespaces/cli/functions/createCliTool). You can add the `createCliTool()` function with relevant parameters in your `src/extension.ts` file. - - Register the state of a CLI tool, see the [resource](/api/type-aliases/CliToolState). -- Add operations related to a container engine, see the usage of the [containerEngine](https://podman-desktop.io/api/@podman-desktop/namespaces/containerEngine) API. -- Add a new Kubernetes provider connection, use the [`KubernetesProviderConnection`](/api/interfaces/KubernetesProviderConnection) interface. -- Enable navigation to a CLI tool page, use the function [`navigateToCliTools()`](https://podman-desktop.io/api/@podman-desktop/namespaces/navigation/functions/navigateToCliTools). -- Enable navigation to a specific page in the UI, refer to the [navigation APIs](https://podman-desktop.io/api/@podman-desktop/namespaces/navigation) category. -- Create and show a new webview panel in the UI, use the [`createWebviewPanel()`](https://podman-desktop.io/api/@podman-desktop/namespaces/window/functions/createWebviewPanel) function. -- Add options for running a command, use the [`RunOptions`](/api/interfaces/RunOptions) interface. - -You can also create your own custom extensions and add them to the catalog for others to use. For more details, see [Developing an extension](/docs/extensions/developing) and [Packaging and publishing an extension](/docs/extensions/publish). diff --git a/website/docs/extensions/debugging-an-extension.md b/website/docs/extensions/debugging-an-extension.md deleted file mode 100644 index d1a9a73e06..0000000000 --- a/website/docs/extensions/debugging-an-extension.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 3 -title: Debugging -description: Debugging a local extension -keywords: [podman desktop, podman, debugging an extension, extension] -tags: [debugging-an-extension, testing-an-extension] ---- - -# Debugging a local extension - -After developing an extension, you can start and debug the extension from the UI. This reduces the time spent identifying and fixing issues within the extension's code before it is deployed. When you add a local folder that contains your extension to the UI, Podman Desktop watches the folder, loads the extension, and keeps track of it. You can also stop or untrack the extension. - -#### Prerequisites - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- You have [developed an extension](/docs/extensions/developing) locally. - -#### Procedure - -1. Go to **Settings > Preferences > Extensions**. -1. Click the toggle button to enable the development mode. -1. Go to **Extensions** and select the **Local Extensions** tab. -1. Click **Add a local folder extension...**. -1. Select the folder that contains your extension. -1. Check the extension is in the `started` state on the same page. - ![debugging an extension](img/debugging-an-extension.png) - -#### Verification - -1. Go to **Extensions**, search for the local extension, and confirm that it is `ACTIVE`. -1. Check your extension's functionality in the UI. diff --git a/website/docs/extensions/developing/adding-icons.md b/website/docs/extensions/developing/adding-icons.md deleted file mode 100644 index f11258d771..0000000000 --- a/website/docs/extensions/developing/adding-icons.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -sidebar_position: 4 -title: Adding icons -description: Podman Desktop and resources icons -tags: [podman-desktop, extension, writing, icons] -keywords: [podman desktop, extension, writing, icons] ---- - -# Adding icons - -Podman Desktop allows extensions to register custom icons that can be used for resources based on certain condition defined by a [when clause](when-clause-context.md). - -For example, the Kind extension register a custom icons by using the following instruction. - -```json -"icons": { - "kind-icon": { - "description": "Kind icon", - "default": { - "fontPath": "kind-icon.woff2", - "fontCharacter": "\\EA01" - } - } -} -``` - -We restrict the format to the [Web Open Font Format 2 (aka woff2)](https://www.w3.org/TR/WOFF2/) to use icons as text, to keep consistency across the UI, as the color and size is managed by Podman-Desktop. - -### Creating a .woff2 file - -You probably have an existing `.svg` file that you want to use, to make it possible you can use the tool [svgiconfont](https://nfroidure.github.io/svgiconfont/) made by [@nfroidure](https://twitter.com/nfroidure). - -To ensure the produced `.woff2` file contains the expected characters you created from your svg file(s). You can use the tool [fontforge.org](https://fontforge.org/) to visualize it. - -:::info - -To find the `fontCharacter` where your icons has been saved, you can search inside the FontForge tool by the name of the svg file you used. - -::: diff --git a/website/docs/extensions/developing/adding-ui-components.md b/website/docs/extensions/developing/adding-ui-components.md deleted file mode 100644 index 1120335c99..0000000000 --- a/website/docs/extensions/developing/adding-ui-components.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -sidebar_position: 5 -title: Adding UI components -description: Adding different components in UI -tags: [podman-desktop, extension, writing, web-components] -keywords: [podman desktop, extension, writing, web-components] ---- - -# Adding UI components - -You can create your own customized extension and add different UI components to your application front-end page. For example, you can add a new UI component of the type `Button` . If you have your own template, you can use the [ready-to-use code](https://podman-desktop.io/storybook?id=button--docs) to add primary, secondary, loading, or other types of buttons. - -:::note - -If you do not have hands-on experience and want to explore, use the [minimal, webview, or full template](/docs/extensions/templates) to create a Podman Desktop extension. - -::: - -#### Procedure - -1. Add the `@podman-desktop/ui-svelte` package to your application source code. -1. Open the [storybook link](https://podman-desktop.io/storybook). -1. Go to **Docs** and copy the code for a particular UI component. - ![UI component](../img/button-component.png) -1. Paste it in your UI source configuration file, such as `UIextension.svelte`. -1. Save the configuration changes. -1. Run your extension and debug it if required. - -#### Verification - -- Check that the UI component is added in the webview of your extension. diff --git a/website/docs/extensions/developing/command-palette.md b/website/docs/extensions/developing/command-palette.md deleted file mode 100644 index c58d85f292..0000000000 --- a/website/docs/extensions/developing/command-palette.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Command palette -description: Podman Desktop command palette -tags: [podman-desktop, extension] -keywords: [podman desktop, extension] ---- - -# Command Palette - -Podman Desktop provides a shortcut to list all available commands using the **F1** key. - -![Command Palette](img/command_palette.png) - -All commands displayed are defined in the "commands" section of `package.json`. For additional information, visit the [documentation page](/docs/extensions/developing/commands). - -### Procedure - -1. Press **F1**. -2. Select the command you would like to execute from the list. - -### Additional Resources - -- [Command Extension Documentation](/docs/extensions/developing/commands) diff --git a/website/docs/extensions/developing/commands.md b/website/docs/extensions/developing/commands.md deleted file mode 100644 index 6a92866b7e..0000000000 --- a/website/docs/extensions/developing/commands.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: Commands -description: Podman Desktop command reference -tags: [podman-desktop, extension] -keywords: [podman desktop, extension] ---- - -# Commands - -## Configuration details - -This section describes new commands added to the extension, which enable enhanced interaction and automation within the development environment. These commands can be used programmatically through the API. - -### `package.json` Example - -This example shows how new commands are added to `package.json`, enabling them for use within the extension. Each command is defined with a unique identifier and a descriptive title that appears in the command palette. - -```json -{ - "contributes": { - "commands": [ - { - "command": "extension.exampleCommand", - "title": "Extension: Example Command" - }, - { - "command": "extension.anotherCommand", - "title": "Extension: Another Command" - } - ] - } -} -``` - -And within the TypeScript code, you can use the commands like so: - -```typescript -const exampleCommand = extensionApi.commands.registerCommand('extension.exampleCommand', async () => { - // Implementation logic here - console.log('Executing Example Command'); -}); - -const anotherCommand = extensionApi.commands.registerCommand('extension.anotherCommand', () => { - // Synchronous logic can be used if async processing is not required - console.log('Another Command Executed'); -}); -``` - -### JSON Schema - -```json -{ - "contributes": { - "commands": [ - { - "command": "string", - "title": "string", - "category": "string (optional cateogry for prefix title)", - "enablement": "myProperty === myValue" - } - ] - } -} -``` - -### Additional Resources - -When you add the command, it will be listed on the command palette. See the [command palette](/docs/extensions/developing/command-palette) for more information. - -### Verification - -To verify that your commands are working as expected: - -1. Install the extension in your development environment. -2. Add a command to `package.json`. -3. Execute the commands and check for the expected outputs / logging. diff --git a/website/docs/extensions/developing/config.md b/website/docs/extensions/developing/config.md deleted file mode 100644 index 210b631b79..0000000000 --- a/website/docs/extensions/developing/config.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Configuration -description: Podman Desktop configuration reference -tags: [podman-desktop, extension] -keywords: [podman desktop, extension] ---- - -# Commands - -## Configuration details - -This section details the configurable settings introduced by the extension to enhance or modify its behavior. The settings allow users to customize aspects of the extension's functionality. For example a modified path to a binary, or a performance setting. - -### `package.json` Example - -This example illustrates how configuration settings are structured within `package.json` for the extension. It includes various settings related to the environment and hardware resources that the extension will manage or monitor. - -```json -{ - "contributes": { - "configuration": { - "title": "Podman", - "properties": { - "podman.binary.path": { - "type": "string", - "format": "file", - "default": "", - "description": "Custom path to Podman binary (Default is blank)" - }, - "podman.machine.cpus": { - "type": "number", - "format": "cpu", - "minimum": 1, - "default": "HOST_HALF_CPU_CORES", - "maximum": "HOST_TOTAL_CPU", - "scope": "ContainerConnection", - "description": "CPU(s)" - } - } - } - } -} -``` - -And within the TypeScript code, you can retrieve as well as use the configurations as so: - -```typescript -// Get configuration for this connection -const containerConfiguration = extensionApi.configuration.getConfiguration('podman', containerProviderConnection); - -// Set a value -await containerConfiguration.update('machine.cpus', machineInfo.cpus); - -// Get a value -await containerConfiguration.get('machine.cpus'); - -// Has a value -await containerConfiguration.has('machine.cpus'); -``` - -### JSON Schema - -Within the schema, you may add any type of value such as `"foo":"bar"` which can be retrieved similar to the above TypeScript example. - -```json -{ - "contributes": { - "configuration": { - "title": "string", - "properties": { - "string": { - "type": "string", - "default": "integer if type is integer, string if type is string, etc.", - "format": "string", - "minimum": "string or int", - "maximum": "string or int", - "description": "string", - "scope": "string or array, ex. ['DEFAULT', 'ONBOARDING']", - "hidden": "boolean", - "placeholder": "string", - "markdownDescription": "string", - "readonly": "boolean", - "enum": "array", - "step": "number", - "when": "string" - } - } - } - } -} -``` - -### Verification - -To verify that your commands are working as expected: - -1. Adjust the configuration settings within package.json -2. Restart the extension or Podman Desktop -3. Verify the change within the Settings page. diff --git a/website/docs/extensions/developing/img/command_palette.png b/website/docs/extensions/developing/img/command_palette.png deleted file mode 100644 index 5e986a3b79..0000000000 Binary files a/website/docs/extensions/developing/img/command_palette.png and /dev/null differ diff --git a/website/docs/extensions/developing/index.md b/website/docs/extensions/developing/index.md deleted file mode 100644 index babfc437e9..0000000000 --- a/website/docs/extensions/developing/index.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -sidebar_position: 2 -title: Developing -description: Developing a Podman Desktop extension -tags: [podman-desktop, extension, writing] -keywords: [podman desktop, extension, writing] ---- - -# Developing a Podman Desktop extension - -Podman Desktop is organized so that you can modularly add new functionality in the form of "extensions" as well as the corresponding extension-api. This allows you to communicate with Podman Desktop without having to know the internal workings. You look for the API call and Podman Desktop will do the rest. - -It is recommended that an extension be written in `TypeScript` for type checking, but extensions can be written in `JavaScript`. - -Most extensions are externally loaded; however, we also dogfood our own API by loading them as [internal extensions](https://github.com/podman-desktop/podman-desktop/tree/main/extensions) that use the same API. These internally maintained extensions can be used as an example and basis for how to build an externally loaded extension. - -## Overview of creating a new extension - -We try to simplify extension creation as much as possible by utilizing `package.json` and by keeping activations simple within the extension, providing only two entrypoints: `activate()` and `deactivate()`. - -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 API code is located [here](https://github.com/podman-desktop/podman-desktop/blob/main/packages/extension-api/src/extension-api.d.ts), while the website representation of the code can be found [here](https://podman-desktop.io/api). - -### Activating - -When activating an extension, Podman Desktop will: - -1. Search and load the `JavaScript` file specified in `main` entry of the `package.json` file in the extension directory (typically `extension.js`). -2. Run the exported `activate` function. - -### Deactivating - -When deactivating an extension, Podman Desktop will: - -1. Run the optionally exported `deactivate` function. -2. Dispose of any resources that have been added to `extensionContext.subscriptions`, see `deactivateExtension` in [extension-loader.ts](https://github.com/podman-desktop/podman-desktop/blob/main/packages/main/src/plugin/extension-loader.ts). - -### Example boilerplate code - -This is an example `extensions/foobar/src/extensions.ts` file with the basic `activate` and `deactivate` functionalities, provided that you already have a `package.json` created as well: - -```ts -import * as extensionApi from '@podman-desktop/api'; - -// Activate the extension asynchronously -export async function activate(extensionContext: extensionApi.ExtensionContext): Promise { - // Create a provider with an example name, ID and icon - const provider = extensionApi.provider.createProvider({ - name: 'FooBar', - id: 'foobar', - status: 'unknown', - images: { - icon: './icon.png', - logo: './icon.png', - }, - }); - - // Push the new provider to Podman Desktop - extensionContext.subscriptions.push(provider); -} - -// Deactivate the extension -export function deactivate(): void { - console.log('stopping FooBar extension'); -} -``` - -### Interacting with the UI - -The extension "hooks" into the Podman Desktop UI by various means, which include: - -- Registering the extension as a specific provider (authentication, registry, Kubernetes, containers, CLI tools, or others). -- Registering to specific events (with functions starting with `onDid...`). -- Adding entries to menus (tray menu, status bar, and other types of menus). -- Adding fields to the configuration panel. -- Watching files in the filesystem. - -When the extension code is accessed through these different registrations, the extension can use utility functions provided by the API to: - -- Get values of configuration fields. -- Interact with the user through input boxes and quick picks. -- Display information, warnings, error messages, and notifications to the user. -- Get information about the environment (OS, telemetry, system clipboard). -- Execute the process in the system. -- Send data to the telemetry. -- Set data in the context, which is propagated in the UI. - -## Creating and running your extension - -You can create and run an extension by performing the following end-to-end tasks: - -1. [Initializing an extension](/tutorial/creating-an-extension#initializing-an-extension) -1. [Writing the extension's features](/tutorial/creating-an-extension#writing-the-extension-entry-point) -1. [Build dependencies](/tutorial/creating-an-extension#build-dependencies) -1. [Running the extension](/tutorial/creating-an-extension#running-the-extension) -1. [Verifying the extension's features](/tutorial/creating-an-extension#verifying-the-extensions-features) - -## Expanding your extension - -Below is documentation and/or "boiler-plate" code that can help expand your extension. - -### Using `ProviderStatus` - -Podman Desktop runs each provider via series of statuses from [extension-api](https://github.com/podman-desktop/podman-desktop/blob/main/packages/extension-api/src/extension-api.d.ts). - -```ts -export type ProviderStatus = - | 'not-installed' - | 'installed' - | 'configured' - | 'ready' - | 'started' - | 'stopped' - | 'starting' - | 'stopping' - | 'error' - | 'unknown'; -``` - -`ProviderStatus` supplies information to the main Provider page detailing whether or not that Provider is installed, ready, started, stopped, etc. - -This can be updated throughout your extension by calling for example: `provider.updateStatus('installed')`. Podman Desktop will show the status on the main screen. - -> **_NOTE:_** ProviderStatus is for information purposes only and can be used from within the extension to keep track if `activate()` and `deactivate()` are working correctly. - -### Using `ProviderConnectionStatus` - -```ts -export type ProviderConnectionStatus = 'started' | 'stopped' | 'starting' | 'stopping' | 'unknown'; -``` - -> **_NOTE:_** The `unknown` status is unique as it will not show in the extension section of Podman Desktop, it will also not be accessible via API calls. Unknown statuses typically happen when Podman Desktop is unable to load the extension. - -`ProviderConnectionStatus` is the main "Lifecycle" of your extension. The status is updated automatically by Podman Desktop and reflected within the provider. - -Upon a successful start up via the `activate` function within your extension, `ProviderConnectionStatus` will be reflected as 'started'. - -`ProviderConnectionStatus` statuses are used in two areas, [extension-loader.ts](https://github.com/podman-desktop/podman-desktop/blob/main/packages/main/src/plugin/extension-loader.ts) and [tray-menu.ts](https://github.com/podman-desktop/podman-desktop/blob/main/packages/main/src/tray-menu.ts): - -- `extension-loader.ts`: Attempts to load the extension and sets the status accordingly (either `started`, `stopped`, `starting` or `stopping`). If an unknown error has occurred, the status is set to `unknown`. `extension-loader.ts` also sends an API call to Podman Desktop to update the UI of the extension. -- `tray-menu.ts`: If `extensionApi.tray.registerMenuItem(item);` API call has been used, a tray menu of the extension will be created. When created, Podman Desktop will use the `ProviderConnectionStatus` to indicate the status within the tray menu. - -### Adding commands - -## Commands - -Declare commands using `contributes` section of package.json file. - -```json - "contributes": { - "commands": [ - { - "command": "my.command", - "title": "This is my command", - "category": "Optional category to prefix title", - "enablement": "myProperty === myValue" - }, - ], - } -``` - -If optional `enablement` property evaluates to false, command palette will not display this command. - -To register the callback of the command, use the following code: - -```ts -import * as extensionApi from '@podman-desktop/api'; - -extensionContext.subscriptions.push(extensionApi.commands.registerCommand('my.command', async () => { - // callback of your command - await extensionApi.window.showInformationMessage('Clicked on my command'); -}); -); -``` - -### Expanding the `extension-api` API - -Sometimes you'll need to add new functionality to the API in order to make an internal change within Podman Desktop. An example would be a new UI/UX component that happens within the renderer, you'd need to expand the API in order to make that change to Podman Desktop's inner-workings. - -Please note that an API contribution is **subject to approval** as we want to maintain sustainability / consistency in the API. A discussion within an issue would be beneficial before writing code. - -In this example, we'll add a new function to simply display: "hello world" in the console. - -1. Add the new function to `/packages/extension-api/src/extension-api.d.ts`, under a namespace. This will make it accessible within the API when it's being called within your extension: - -```ts -export namespace foobar { - // ... - export function hello(input: string): void; -} -``` - -2. The `packages/main/src/plugin/extension-loader.ts` acts as an extension loader that defines all the actions needed by the API. Modify it to add the main functionality of `hello()` under the `foobar` namespace const: - -```ts -// It's recommended you define a class that you retrieve from a separate file -// see Podman and Kubernetes examples for implementation. - -// Add the class to the constructor of the extension loader -import type { FoobarClient } from './foobar'; - -export class ExtensionLoader { - // ... - constructor( - private foobarClient: FoobarClient, - // ... - ) {} -// .. -} - -// Initialize the 'foobar' client -const foobarClient = this.foobarClient; - -// The "containerDesktopAPI.foobar" call is the namespace you previously defined within `extension-api.d.ts` -const foobar: typeof containerDesktopAPI.foobar = { - - // Define the function that you are implementing and call the function from the class you created. - hello(input: string): void => { - return foobarClient.hello(input); - }, -}; - -// Add 'foobar' to the list of configurations being returned by `return ` -return { - foobar -}; -``` - -3. The above code won't work until we've created the class! So let's create a `packages/main/src/plugin/foobar-client.ts` file with the functionality: - -```ts -export class FoobarClient { - hello(input: string) { - console.log('hello ' + input); - } -} -``` - -4. An instance of this class needs to be created and passed to the constructor of the `ExtensionLoader`, in `packages/main/src/plugin/index.ts`: - -```ts -const foobarClient = new FoobarClient(); -this.extensionLoader = new ExtensionLoader( - /* ... */ - foobarClient, -); -``` - -5. In package.json you can register some setting through the configuration settings property - -For example if you contribute a property named `podman.binary.path` it will display `Path` in Podman Desktop UI setting, and if you change it to `podman.binary.pathToBinary` it becomes `Path To Binary` in the title. - -```ts - - "configuration": { - "title": "Podman", - "properties": { - "podman.binary.path": { - "name": "Path to Podman Binary", - "type": "string", - "format": "file", - "default": "", - "description": "Custom path to Podman binary (Default is blank)" - }, -``` - -6. Last step! Call the new API call to the extension you are implementing from your extension: - -```ts -export async function activate(extensionContext: extensionApi.ExtensionContext): Promise { - // Define the provider - const provider = extensionApi.provider.createProvider({ - name: 'FooBar', - id: 'foobar', - status: 'unknown', - images: { - icon: './icon.png', - logo: './icon.png', - }, - }); - - // Push the new provider to Podman Desktop - extensionContext.subscriptions.push(provider); - - // Call the "hello world" function that'll output to the console - extensionContext.foobar.hello('world'); -} -``` - -## Additional resources - -- Consider a packer such as [Rollup](https://rollupjs.org) or [Webpack](https://webpack.js.org) to shrink the size of the artifact. - -## Next steps - -- [Publishing a Podman Desktop extension](/docs/extensions/publish) diff --git a/website/docs/extensions/developing/menu.md b/website/docs/extensions/developing/menu.md deleted file mode 100644 index 71bff1d684..0000000000 --- a/website/docs/extensions/developing/menu.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Menus -description: Podman Desktop menu reference -tags: [podman-desktop, extension] -keywords: [podman desktop, extension, menu] ---- - -# Menus - -## Menu Details - -This section describes how menus are integrated into the extension. These menus are configured to appear in specific parts of the user interface and are tied to commands defined in the extension. - -### `package.json` Example - -This example shows how to integrate a menu into the Podman Desktop extension through the `package.json` file. Here, a menu item is added under the "dashboard/image" context. Meaning that the command will appear next to the image when `selectImageId` is not in `imagePushInProgressToKind`. - -```json -{ - "contributes": { - "menus": { - "dashboard/image": [ - { - "command": "kind.image.move", - "title": "Push image to Kind cluster", - "when": "selectedImageId not in imagesPushInProgressToKind" - } - ] - } - } -} -``` - -### JSON Schema - -```json -{ - "contributes": { - "menus": { - "": [ - { - "command": "string", - "title": "string", - "when": "string", - "disabled": "boolean" - } - ] - } - } -} -``` - -#### MenuContext available - -- 'dashboard/image': Item menu on image actions -- 'dashboard/container': Item menu on container actions -- 'dashboard/pod': Item menu on pod actions -- 'dashboard/compose': Item menu on compose actions - -### Verification - -To verify that your menus are functioning correctly: - -1. Navigate to the dashboard within Podman Desktop. -2. Right-click on an image to see the context menu. -3. Select "Push image to Kind cluster" and verify that the action completes successfully, ensuring no errors occur during the operation. diff --git a/website/docs/extensions/developing/onboarding-workflow.md b/website/docs/extensions/developing/onboarding-workflow.md deleted file mode 100644 index 5367cb71ca..0000000000 --- a/website/docs/extensions/developing/onboarding-workflow.md +++ /dev/null @@ -1,543 +0,0 @@ ---- -sidebar_position: 1 -title: Onboarding workflow -description: Podman Desktop onboarding workflow reference -tags: [podman-desktop, extension, writing, onboarding] -keywords: [podman desktop, extension, writing, onboarding] ---- - -# Onboarding - -A Podman Desktop extension can offer an onboarding workflow to guide users in installing and setting up all the necessary tools for the extension to work, and optionally to provide explanations about the capabilities of the extension. - -Adding onboarding to an extension is as simple as writing JSON in the `package.json`. Podman Desktop will convert the JSON object into actual code to render all items. - -Onboarding consists of a title, a description, media (image), an enablement clause, and a list of steps. Only the title, enablement clause, and the steps are mandatory, as they constitute the minimum information required to define a workflow. -Before getting into the details, let's examine the JSON schema. - -```json -{ - "title": "onboarding", - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "media": { - "path": { - "type": "string" - }, - "altText": { - "type": "string" - } - }, - "enablement": { - "type": "string" - }, - "steps": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "media": { - "path": { - "type": "string" - }, - "altText": { - "type": "string" - } - }, - "command": { - "type": "string" - }, - "completionEvents": { - "type": "array", - "items": { - "type": "string" - } - }, - "content": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "highlight": { - "type": "boolean" - }, - "when": { - "type": "string" - } - }, - "required": ["value"] - } - } - }, - "when": { - "type": "string" - }, - "component": { - "type": "string", - "enum": ["createContainerProviderConnection", "createKubernetesProviderConnection"] - }, - "state": { - "type": "string", - "enum": ["completed", "failed"] - } - }, - "required": ["id", "title"] - } - } - }, - "required": ["title", "enablement", "steps"] -} -``` - -### Title, Description and Media - -The **title**, the **description** and the **media** are all placed in the top left of the onboarding page. -Only the title is required. The description and the media are optional. -If the media is not specified, Podman Desktop will display the default icon set by the extension in its `package.json`. - -This is how this JSON is defined: - -```json -"icon": "icon.png", -... -"onboarding": { - "title": "Podman Setup", -} -``` - -![img0](/img/extensions/developing/title_media_description.png) - -### Enablement - -The enablement clause allows Podman Desktop to determine when the onboarding should be enabled. -When this condition is met, the user will find a setup button within the resources page. Clicking on it will initiate the onboarding workflow. - -![img1](/img/extensions/developing/setup_button.png) - -The enablement clause is mandatory and must be written by using [when clauses](/docs/extensions/developing/when-clause-context). - -In the following example, we specify that the onboarding needs to be enabled if and only if the user's OS is Linux, and the `podmanIsNotInstalled` context value is true. Alternatively, if the user's OS is different from Linux, that the `podmanMachineExists` context value must be false. Essentially, if the user is on Linux, the onboarding must be enabled only if podman is not installed; for all other operating systems, it should be enabled if there is no Podman machine. - -```json -"enablement": "(isLinux && onboardingContext:podmanIsNotInstalled) || (!isLinux && !onboardingContext:podmanMachineExists)" -``` - -### Steps - -The steps property is required and includes the actual content that will be displayed to the user during the workflow. - -Each step can contribute to the onboarding process in various ways. -You can choose to display content explaining concepts to the user, incorporate input elements (such as buttons or textboxes) to encourage user interaction, run commands to perform installations, or showcase settings to be configured. - -Let's look again at its schema: - -```json -"type": "object", -"properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "media": { - "path": { - "type": "string" - }, - "altText": { - "type": "string" - }, - }, - "command": { - "type": "string" - }, - "completionEvents": { - "type": "array", - "items": { - "type": "string" - } - }, - "content": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "highlight": { - "type": "boolean" - }, - "when": { - "type": "string" - } - }, - "required": ["value"] - } - } - }, - "when": { - "type": "string" - }, - "component": { - "type": "string", - "enum": ["createContainerProviderConnection", "createKubernetesProviderConnection"] - }, - "state": { - "type": "string", - "enum": ["completed", "failed"] - } -}, -"required": ["id", "title"] -``` - -A step has only two mandatory fields - id and title. All other properties are optional. - -#### Id - -The **id** must be unique to identify a step, and it is never displayed directly to the user. - -To analyze more easily in telemetry the steps executed by users, the **id** values must respect some rules. -To help developers respect these rules, a warning is displayed in case of non-repsect when Podman Destop loads the onboarding. - -The rules are: - -- for a step defining a command, the id must terminate with `Command`, -- for a state defining `state='failed'`, the id must terminate with `Failure`, -- for a state defining `state='completed'`, the id must terminate with `Success`, -- for any other step, the id must termminate with `View`. - -#### Title, description and media - -The **title**, **description** and **media** works as explained earlier. The only difference is their placement - they will appear in the top-center of the body. - -![img2](/img/extensions/developing/step_title_description_media.png) - -**Note:** If media is not specified, Podman Desktop will display the icon of the extension providing the onboarding. - -#### Command - -The **command** field allows you to declare the name of a command that must be run when the step becomes active. -The command must be registered by the extension beforehand, or it will result in an error. - -In the example below, we tell Podman Desktop to call `podman.onboarding.checkPodmanInstalled` when the `checkPodmanInstalled` step becomes active. -Based on the result, we can then prompt the user to move to another step or display a message. - -```json -"commands": [ - { - "command": "podman.onboarding.checkPodmanInstalled", - "title": "Podman: Check podman installation" - }, -], -"onboarding": { - "title": "Podman Setup", - "steps": [ - { - "id": "checkPodmanInstalled", - "title": "Checking for Podman installation", - "command": "podman.onboarding.checkPodmanInstalled", - }, - ... - ], - ... -} -``` - -During the execution of the command, the user will see a spinner next to the title. - -![img3](/img/extensions/developing/spinner_title.png) - -#### CompletionEvents - -CompletionEvents define the conditions under which a step should be considered complete. - -It currently supports `onboardingContext` and `onCommand` events. -The former can be used to evaluate a context value, such as `onboardingContext:podmanIsInstalled`. The latter checks if the command has been executed - `onCommand:podman.onboarding.installPodman`. - -A practical example of progressing the user to the next step after the command finishes its execution is: - -```json -"commands": [ - { - "command": "podman.onboarding.checkPodmanInstalled", - "title": "Podman: Check podman installation" - }, -], -"onboarding": { - "title": "Podman Setup", - "steps": [ - { - "id": "checkPodmanInstalled", - "title": "Checking for Podman installation", - "command": "podman.onboarding.checkPodmanInstalled", - "completionEvents": [ - "onCommand:podman.onboarding.checkPodmanInstalled" - ] - }, - ... - ], - ... -} -``` - -When the `checkPodmanInstalled` step becomes active, the command `podman.onboarding.checkPodmanInstalled` is invoked. Upon completion of its execution, the step is considered complete, and the user is then moved to the next one. - -Here's another example, this time using a context value: - -```json -"commands": [ - { - "command": "podman.onboarding.checkPodmanInstalled", - "title": "Podman: Check podman installation" - }, -], -"onboarding": { - "title": "Podman Setup", - "steps": [ - { - "id": "checkPodmanInstalled", - "title": "Checking for Podman installation", - "command": "podman.onboarding.checkPodmanInstalled", - "completionEvents": [ - "onboardingContext:podmanVersion == 4.7.2" - ] - }, - ... - ], - ... -} -``` - -When the `checkPodmanInstalled` step becomes active, the command `podman.onboarding.checkPodmanInstalled` is invoked. As soon as the context value `podmanVersion` equals `4.7.2`, the step is marked as completed, and the user is moved to the next one. - -You might wonder: who or what sets the context value? If you use a custom context value, it should be your extension's job to set it. Following the example above, we could set the context value during the execution of `podman.onboarding.checkPodmanInstalled` such as - -```js -extensionApi.commands.registerCommand( - 'podman.onboarding.checkPodmanInstalled', - async () => { - // do something - ... - // set podmanVersion context value so we can mark the step as complete - extensionApi.context.setValue('podmanVersion', '4.7.2', 'onboarding'); - } -) -``` - -After updating the context, the UI is refreshed, and Podman Desktop moves the user to the new step. - -#### Content - -The **content** property is an array of arrays where each item in the parent array defines a row, and each item in the child arrays defines a cell. - -```js -content = [ - ['cell', 'cell'], //row - ['cell', 'cell', 'cell'], //row -]; -``` - -The JSON schema for a content cell entry is - -```json -"type": "object", -"properties": { - "value": { - "type": "string" - }, - "highlight": { - "type": "boolean" - }, - "when": { - "type": "string" - } -}, -"required": ["value"] -``` - -**Value** is the only mandatory field and it can be a simple string or a Markdown string to render advanced objects. - -In addition to all the standard Markdown syntax, Podman Desktop provides 3 custom Markdown components: button, link, and warnings list. - -1 - You can create a button that executes a command (syntax - `:button[Name of the button]{command=command.example title="tooltip text"}`) or behaves like a link (syntax - `:button[Name of the button]{href=http://my-link title="tooltip text"}`). - -E.g.: - -```json -"value": ":button[Check requirements again]{command=podman.onboarding.checkPodmanRequirements}" -``` - -![img4](/img/extensions/developing/button_micromark.png) - -2 - Similarly, you can create a link that executes a command (syntax `:link[Name of the command link]{command=command.example title="tooltip text"}`) or behaves like a normal link (syntax - `:link[Name of the command link]{href=http://my-link title="tooltip text"}`) - -E.g.: - -```json -"value": "To install Podman please follow these :link[installation instructions]{href=https://podman.io/docs/installation#installing-on-linux}" -``` - -![img5](/img/extensions/developing/link_micromark.png) - -3 - The warning component allows displaying a list of items (syntax - `:warnings[[item]]`), where an item consists of: - -```json -"type": "object", -"properties": { - "state": { - "type": "string" - }, - "description": { - "type": "string" - }, - "command": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "title": { - "type": "string" - } - }, - "required": [ - "id", - "title" - ] - }, - "docDescription": { - "type": "string" - }, - "docLinks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "url": { - "type": "string" - }, - "group": { - "type": "string" - } - }, - "required": [ - "title", - "url", - "group" - ] - } - }, -} -``` - -Adding a complete list in the `package.json` can be confusing, so a better approach is to use a context value - -```json -"value": ":warnings[${onboardingContext:warningsMarkdown}]" -``` - -at runtime, `${onboardingContext:warningsMarkdown}` is replaced by the actual list filled in the backend - -```js -const warnings = []; -... -const warning = { - state: res.successful ? 'successful' : 'failed', - description: res.description, - docDescription: res.docLinksDescription, - docLinks: res.docLinks, - command: res.fixCommand, -}; -warnings.push(warning); - -extensionApi.context.setValue('warningsMarkdown', warnings, 'onboarding'); -``` - -![img6](/img/extensions/developing/warnings_micromark.png) - -The **highlight** and **when** properties are optional. They are used to change the background color or define when the content column should be visible. - -#### Component - -Podman Desktop has some built-in components that can fit perfectly into an onboarding workflow, such as the `create new connection` wizard. -If you are working on an extension that allows creating a Kubernetes cluster, it would not make sense to re-create a page where the user can add the name, the resources to use, and so on. This is when the component field comes in handy. - -By specifying the component you want to import, all the elements, styling, and actions are embedded into the step. - -Currently, Podman Desktop only supports two types of components for onboarding: `createContainerProviderConnection` and `createKubernetesProviderConnection`. - -An example can be seen in the Podman extension, where you can create a Podman machine during the workflow. - -```json -{ - "id": "createPodmanMachine", - "title": "Create a Podman machine", - "when": "!onboardingContext:podmanMachineExists && !isLinux", - "completionEvents": [ - "onboardingContext:podmanMachineExists" - ], - "component": "createContainerProviderConnection" -}, -``` - -![img7](/img/extensions/developing/component_field.png) - -**Note:** when using the **component** field, you should omit the **content** - -#### When - -The **when** property defines when a step must be visible. You can use any when clause, and Podman Desktop will evaluate it any time the context changes. - -#### State - -The **state**, when set, allows Podman Desktop to distinguish a normal step from a special one. It is used to associate a step with a failed state (`failed`) or, alternatively, with a complete state (`completed`). - -**Note:** the last workflow step should have `completed` state. - -Based on the **state**, Podman Desktop might show some default objects. - -When a step with a failed state is encountered, Podman Desktop displays a `Retry` button, allowing the user to restart the workflow. - -```json -{ - "id": "podmanFailedInstallation", - "title": "Failed installing Podman", - "when": "onboardingContext:podmanFailedInstallation", - "state": "failed" -}, -{ - "id": "podmanSuccessfullySetup", - "title": "Podman successfully setup", - "when": "onboardingContext:podmanIsInstalled", - "state": "completed" -} -``` diff --git a/website/docs/extensions/developing/when-clause-context.md b/website/docs/extensions/developing/when-clause-context.md deleted file mode 100644 index 9b1f41aa8a..0000000000 --- a/website/docs/extensions/developing/when-clause-context.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -sidebar_position: 1 -title: When clause contexts -description: Podman Desktop when clause contexts reference -tags: [podman-desktop, extension, writing, when clause] -keywords: [podman desktop, extension, writing, when clause] ---- - -# When clause contexts - -Podman Desktop uses when clauses to enable or disable extensions command and UI customizations, such as views. - -For example, the Kind extension adds a custom icon to a container that has a label equals to `io.x-k8s.kind.cluster` by using the following instruction. - -```json -"views": { - "icons/containersList": [ - { - "when": "io.x-k8s.kind.cluster in containerLabelKeys", - "icon": "${kind-icon}" - } - ] - } -``` - -A when clause can consist of a context key (such as `isLinux`) or complex expressions to define a specific state. - -### Available context keys - -Podman Desktop has a set of context keys that are evaluated to Boolean true/false. - -| Context key | True when | -| ----------------------------- | ---------------------------- | -| **Operating system contexts** | | -| isLinux | True when the OS is Linux. | -| isWindows | True when the OS is Windows. | -| isMac | True when the OS is macOS. | - -Podman Desktop also provides context keys that return values that can be used to create meaningful expressions - -| Context key | Value in it | -| ------------------ | ----------------------------------------------------------------------------------------------------------------------- | -| containerLabelKeys | A list of all labels belonging to the current container. Example: `"value in containerLabelKeys"` | -| selectedImageId | The image id which the dashboard/image menu opened belong to. Example `"selectedImageId in imagesPushInProgressToKind"` | - -### Add a custom when clause context - -If you are creating your own extension and none of the existing keys suit your needs, you can set your own context key by calling the function `setValue(key: string, value: any, scope?: 'onboarding')` provided by the `context` namespace in the Podman Desktop API. - -The scope, if specified, triggers a custom behavior to avoid any type of collisions between different extensions for that specific scope. Podman Desktop is responsible for handling its state and cleans it accordingly when necessary. - -If omitted, the key/value is set globally. For this reason it is recommended to use the extension id as part of the key to avoid unexpected collisions with other extensions. - -The first example below sets the key `"podmanIsInstalled"` to true globally while the second example sets the key `"toolInstalled"` to `oc.exe` using the onboarding scope. - -```js -extensionsAPI.context.setValue('podmanIsInstalled', true); - -extensionsAPI.context.setValue('toolInstalled', 'oc.exe', 'onboarding'); -``` - -After setting the `toolInstalled` to `oc.exe`, you could use this information in the `when` clause to enable something - -```json -{ - "when": "onboardingContext:toolInstalled == oc.exe" -} -``` - -### Conditional operators - -To create `when` clauses a bit more complex Podman Desktop offers a set of operators that can be combined with each other. - -#### Logical operators - -Logical operators allow combining simple context keys or when-clause expressions that include other operators - -| Operator | Symbol | Example | -| -------- | ------ | ----------------------------------------------------------- | -| Not | `!` | `!podmanIsInstalled` or `!(podmanIsInstalled && isWindows)` | -| And | `&&` | `podmanIsInstalled && isWindows` | -| Or | `\|\|` | `isLinux \|\| isWindows` | - -#### Equality operators - -Equality operators allow checking for equality of a context key's value against a specified value. - -**Note:** the right side is a value and not considered as a context key, so no value is searched in the context. If it contains whitespaces, it must be wrapped in single-quotes (for example `'my tool.exe'`) - -| Operator | Symbol | Example | -| ---------- | ------ | ------------------------------------------- | -| Equality | `==` | `onboardingContext:toolInstalled == oc.exe` | -| Inequality | `!=` | `onboardingContext:toolInstalled != oc.exe` | - -#### Comparison operators - -Comparison operator allow comparing a context key's value against a number. - -**Note:** the left and right side of the operator must be separated by whitespace - `bar < 2`, but not `bar<2` - -| Operator | Symbol | Example | -| ------------ | --------- | -------------------------------------- | -| Greater than | `>`, `>=` | `onboardingContext:toolInstalled > 2` | -| Less than | `<`, `<=` | `onboardingContext:toolInstalled <= 3` | - -#### In and not in - -The `in`/`not in` operators allow checking if a value exists/not exists within the other. The right should be a context key, which value is retrieved in the context. The left can be a value or a context key. - -| Operator | Symbol | Example | -| -------- | -------- | --------------------------------- | -| In | `in` | `label in containerLabelKeys` | -| Not | `not in` | `label not in containerLabelKeys` | - -#### Match operator - -The match operator allow treating the right side item as a regular expression literal to match against the left side. - -| Operator | Symbol | Example | -| -------- | ------ | -------------------- | -| Matches | `=~` | `label =~ /podman$/` | diff --git a/website/docs/extensions/img/architecture.png b/website/docs/extensions/img/architecture.png deleted file mode 100644 index f53a3722ad..0000000000 Binary files a/website/docs/extensions/img/architecture.png and /dev/null differ diff --git a/website/docs/extensions/img/browse-catalog.png b/website/docs/extensions/img/browse-catalog.png deleted file mode 100644 index 60e95c91bc..0000000000 Binary files a/website/docs/extensions/img/browse-catalog.png and /dev/null differ diff --git a/website/docs/extensions/img/button-component.png b/website/docs/extensions/img/button-component.png deleted file mode 100644 index 00c93fefee..0000000000 Binary files a/website/docs/extensions/img/button-component.png and /dev/null differ diff --git a/website/docs/extensions/img/debugging-an-extension.png b/website/docs/extensions/img/debugging-an-extension.png deleted file mode 100644 index 9ee3ad481e..0000000000 Binary files a/website/docs/extensions/img/debugging-an-extension.png and /dev/null differ diff --git a/website/docs/extensions/img/extension-installed-successfully.png b/website/docs/extensions/img/extension-installed-successfully.png deleted file mode 100644 index ee66f4aabe..0000000000 Binary files a/website/docs/extensions/img/extension-installed-successfully.png and /dev/null differ diff --git a/website/docs/extensions/img/extensions-icon.png b/website/docs/extensions/img/extensions-icon.png deleted file mode 100644 index 1ff35b5fc8..0000000000 Binary files a/website/docs/extensions/img/extensions-icon.png and /dev/null differ diff --git a/website/docs/extensions/img/full.png b/website/docs/extensions/img/full.png deleted file mode 100644 index 56f99bd924..0000000000 Binary files a/website/docs/extensions/img/full.png and /dev/null differ diff --git a/website/docs/extensions/img/install-a-custom-extension.png b/website/docs/extensions/img/install-a-custom-extension.png deleted file mode 100644 index 1f7ec9fc61..0000000000 Binary files a/website/docs/extensions/img/install-a-custom-extension.png and /dev/null differ diff --git a/website/docs/extensions/img/install-custom.png b/website/docs/extensions/img/install-custom.png deleted file mode 100644 index 7675e02905..0000000000 Binary files a/website/docs/extensions/img/install-custom.png and /dev/null differ diff --git a/website/docs/extensions/img/minimal.png b/website/docs/extensions/img/minimal.png deleted file mode 100644 index 315f00a01d..0000000000 Binary files a/website/docs/extensions/img/minimal.png and /dev/null differ diff --git a/website/docs/extensions/img/more-details-link.png b/website/docs/extensions/img/more-details-link.png deleted file mode 100644 index 9395222775..0000000000 Binary files a/website/docs/extensions/img/more-details-link.png and /dev/null differ diff --git a/website/docs/extensions/img/webview.png b/website/docs/extensions/img/webview.png deleted file mode 100644 index 9b0ce5d0a4..0000000000 Binary files a/website/docs/extensions/img/webview.png and /dev/null differ diff --git a/website/docs/extensions/index.md b/website/docs/extensions/index.md deleted file mode 100644 index 2d64ba84c0..0000000000 --- a/website/docs/extensions/index.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -sidebar_position: 110 -title: Extensions -description: Installing, developing, or publishing extensions. -keywords: [podman desktop, podman, extension] -tags: [podman-desktop-extensions, building-an-extension] ---- - -# Podman Desktop extensions - -Extensions are designed to be modular and easily pluggable into Podman Desktop. They allow you to use and manipulate a wide range of Podman Desktop functionalities [via our API](/api). Not only can you customize every component of Podman Desktop, but you can also extend its functionality with these extensions. - -With extensions, you can add: - -- Support for container engines, such as Podman or Docker. -- Virtual machine integrations, such as Lima. -- Podman Desktop extension points, including tray icon menus, status bar items, icons, menus, and commands. -- Integration with third-party tools, such as Kind or Compose. - -You can view a complete list of the extensions and their details on the [website](/extensions) and install them for use. - -## Architecture - -Below is an example of the architecture for the "Podman" extension that integrates with Podman Desktop: - -![architecture](img/architecture.png) - -Each extension is isolated and communicates entirely through the Podman Desktop API, ensuring modularity and extensibility for Podman Desktop. - -## What can a Podman Desktop extension do? - -Here are some examples of what you can achieve with the Podman Desktop extension API: - -- [Create your own onboarding workflow.](/docs/extensions/developing/onboarding-workflow) -- [Add non-native Podman Desktop commands.](/docs/extensions/developing/commands) -- [Create configuration settings for your extension.](/docs/extensions/developing/config) -- [Add menus to areas, such as pushing images.](/docs/extensions/developing/menu) - -The possibilities are endless. You can leverage [our API](/api) to expand your extension's capabilities even further. - -## How to build an extension - -To help you get started, we've provided templates ranging from a minimal "Hello World" example to a full web-view extension. - -Here are some examples from [our templates documentation](/docs/extensions/templates): - -- [Basic "Hello World" example](https://github.com/podman-desktop/extension-template-minimal). -- [Simple webview template](https://github.com/podman-desktop/extension-template-webview). See the code for the published Podman Desktop extensions that use this template: - - [Kind](https://github.com/podman-desktop/podman-desktop/tree/main/extensions/kind/src) - - [Compose](https://github.com/podman-desktop/podman-desktop/tree/main/extensions/compose/src) - - [Registries](https://github.com/podman-desktop/podman-desktop/tree/main/extensions/registries/src) -- [Full-stack webview template](https://github.com/podman-desktop/extension-template-full). See the code for the published Podman Desktop extensions that use this template: - - [Podman AI Lab](https://github.com/containers/podman-desktop-extension-ai-lab/tree/main/packages) - - [Bootable Containers](https://github.com/podman-desktop/extension-bootc/tree/main/packages) - - [Kreate](https://github.com/podman-desktop/extension-kreate/tree/main/packages) - -Have questions or need assistance? Join our community on Discord for support! - -## Next Steps - -- [Writing a Podman Desktop extension entry point](/docs/extensions/developing) -- [Publishing a Podman Desktop extension](/docs/extensions/publish) -- [Installing a Podman Desktop extension](/docs/extensions/install) diff --git a/website/docs/extensions/install/index.md b/website/docs/extensions/install/index.md deleted file mode 100644 index e39bf188b4..0000000000 --- a/website/docs/extensions/install/index.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 1 -title: Installing -description: Install Podman Desktop extension -tags: [podman-desktop, extension, publishing] -keywords: [podman desktop, extension, installing-an-extension] ---- - -# Installing a Podman Desktop extension - -Installing an extension is a great way to expand the capabilities of Podman Desktop. You have the option to install an extension from the catalog or a custom extension of your choice. - -#### Procedure - -1. Click the **Extensions** button. - - ![Extension Screenshot](../img/extensions-icon.png) - -2. Perform one of the following steps: - - Browse the **Catalog** and install the required extension. - - ![Catalog Screenshot](../img/browse-catalog.png) - - - Click the **Install custom...** button to install an extension from a container image: - 1. Go to any registry, such as Docker Hub, GitHub, or any other registry. - 1. Find your extension. - 1. Copy the OCI image name of the extension, such as `redhatdeveloper/openshift-dd-ext:0.0.1-100`, and paste it into the **OCI Image** field. - ![Install Custom Extension Dialoge](../img/install-custom.png) - 1. Click **Install**. A successful operation notification opens. - ![install a custom extension](../img/install-a-custom-extension.png) - 1. Click **Done**. - ![extension installed successfully](../img/extension-installed-successfully.png) - -#### Verification - -- Verify the extension by checking the **Installed** tab on the Extensions page. - -- Depending on the extension, items can appear in the status bar, tray menu, or other areas of Podman Desktop. diff --git a/website/docs/extensions/install/using-extension.md b/website/docs/extensions/install/using-extension.md deleted file mode 100644 index 1e490a6b42..0000000000 --- a/website/docs/extensions/install/using-extension.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_position: 2 -title: Usage -description: Use extensions for daily development tasks -keywords: [podman desktop, podman, extensions] -tags: [using-extensions-for-development-tasks] ---- - -# Using extensions for development tasks - -Podman Desktop provides a wide range of extensions that can be used to integrate your local tools with the UI. By installing extensions, you can customize your development capabilities based on your needs. For example, if you want to run a Minikube cluster, you can install the Minikube extension. List of extensions available: - -**_Engine extensions_** - -- Podman: Handles creation and monitoring of Podman machines. It connects to the Podman socket so that you can see containers, images, volumes, and other resources in the Podman Desktop UI. See [Podman](/docs/podman). - -- Docker: Auto-registers the socket to Podman Desktop if any Docker engine is running on your machine. After registeration, you can monitor containers, images, volumes, and other resources from the UI. See [Migrating from Docker](/docs/migrating-from-docker). - -**_Kubernetes extensions_** - -- Kind: Enables creating a Kubernetes cluster that you can run on a Podman engine. As a prerequisite, you must install the `kind` binary before using the extension. See [Kind](/docs/kind) and [Tutorial- Deploying a Kubernetes application](/tutorial/deploying-a-kubernetes-application). - -- Minikube: Enables creating a Kubernetes cluster that you can run on a Podman engine. As a prerequisite, you must install the `minikube` binary before using the extension. See [Minikube](/docs/minikube) and [Tutorial- Running a Kubernetes cluster](/tutorial/running-a-kubernetes-cluster). - -- Lima: Enables creating a Lima virtual machine that you can run on a Podman engine. As a prerequisite, you must install the `lima` binary before using the extension. See [Lima](/docs/lima). - -- Kube Context: Enables viewing and changing the current kubernetes context. See [Viewing and selecting Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). - -- Headlamp: Enables creating an extensible Kubernetes web UI. For more details, click the **More details** link in the **Catalog** tab of the Extensions page. - - ![more details link](../img/more-details-link.png) - -**_CLI extensions_** - -- Compose: Enables setting up `compose` binary so that you can run `podman compose` commands. See [Compose](/docs/compose) and [Getting started with Compose](/tutorial/getting-started-with-compose). - -- Kubectl CLI: Enables setting up `kubectl` binary so that you can run `kubectl` commands. See [Managing your CLI tools](/tutorial/managing-your-application-resources#managing-other-resources). - -**_Red Hat extensions_** - -Red Hat extension pack is a set of the following extensions that a developer can use for authentication and development purposes: - -- Podman AI Lab: Enables working and experimenting with Large Language Models (LLMs) in your local development environment. See [Running LLMs locally](/docs/ai-lab) and [Tutorial- Running an AI application](/tutorial/running-an-ai-application). - -- Bootable containers: Enables building a bootable disk image from your container image. For more details, click the **More details** link in the **Catalog** tab of the Extensions page. - -- Red Hat Account Extension: Enables you to sign in with Red Hat Single Sign-On (SSO). You can consume Red Hat content, such as RHEL container images and RPM packages without any cost. For more details, click the **More details** link in the **Catalog** tab of the Extensions page. - -- OpenShift Local: Enables running a Red Hat OpenShift Local cluster. You can manage the cluster configuration and run an application on the cluster from the UI. See [Creating an OpenShift Local instance](/docs/openshift/openshift-local). - -- Developer Sandbox: Enables deploying your application to a shared-OpenShift Container Platform (OCP) cluster for 30 days without any cost. After configuring a sandbox environment, you can switch to the developer sandbox context and perform tasks, such as deploying a pod or container from the UI. See [Configuring access to a developer sandbox](/docs/openshift/developer-sandbox). - -- OpenShift Checker: Analyzes a Containerfile and highlights the directives and commands that could cause an unexpected behavior when you run it on an OCP cluster. After identifying issues, you can update your Containerfile to make it OpenShift-compliant. For more details, click the **More details** link in the **Catalog** tab of the Extensions page. - - :::note - - You can also install the above extensions individually from the catalog rather than installing the entire pack. - - ::: - -**_Other extensions_** - -- Registries: Provides some default registries so that you can connect to them with your credentials. See [Managing registries](/docs/containers/registries). - -- Image Layers Explorer: Use this extension to explore and analyze different layers of the container image. For more details, click the **More details** link in the **Catalog** tab of the Extensions page. - -**_Custom extensions_** - -You can also create your own customized extension using [these templates](/docs/extensions/templates). For more details, see [Developing a Podman Desktop extension](/docs/extensions/developing) and [Publishing a Podman Desktop extension](/docs/extensions/publish). diff --git a/website/docs/extensions/publish/index.md b/website/docs/extensions/publish/index.md deleted file mode 100644 index cac7fbb082..0000000000 --- a/website/docs/extensions/publish/index.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -sidebar_position: 3 -title: Publishing -description: Publishing a Podman Desktop extension to the catalog -tags: [podman-desktop, extension, publishing] -keywords: [podman desktop, extension, publishing-an-extension] ---- - -# Packaging and publishing a Podman Desktop extension - -To enable users to install your extension from the catalog, push your extension to an Open Container Initiative (OCI) image registry. After pushing your extension, add the extension details to the `podman-desktop-catalog/static/api/extensions.json` file in the [catalog repository](https://github.com/podman-desktop/podman-desktop-catalog). - -#### Prerequisites - -- The extension builds successfully. - See [Developing a Podman Desktop extension](/docs/extensions/developing) and [Creating an extension](/tutorial/creating-an-extension). - -- All runtime dependencies are inside the final binary. - -- An OCI image registry to publish to, such as `quay.io/username/my-extension`. - -- (Optional) The OCI image registry is public to enable anybody to fetch the image. - -#### Procedure: Push your extension to an OCI registry - -1. Create and edit a `Containerfile` file. - -1. Use a scratch image. - The extension requires no runtime: - - ```dockerfile - FROM scratch - ``` - -1. Apply mandatory Podman Desktop metadata on the `OCI` image: - - ```dockerfile - LABEL org.opencontainers.image.title="My extension" \ - org.opencontainers.image.description="Example of extension" \ - org.opencontainers.image.vendor="podman-desktop" \ - io.podman-desktop.api.version=">= 0.12.0" - ``` - - `io.podman-desktop.api.version=">= 0.12.0"` sets the minimal Podman Desktop version that the extension requires to run. - -1. Copy the extension assembly, including the metadata, icon, and production binary, to the `/extension` folder inside the image: - - ```dockerfile - COPY package.json /extension/ - COPY icon.png /extension/ - COPY dist /extension/dist - ``` - -1. Build an image: - - ```shell-session - $ podman build -t quay.io/username/my-extension . - ``` - -1. Push the image and manifest to the OCI image registry: - - ```shell-session - $ podman push quay.io/username/my-extension - ``` - -#### Adding platform-specific files - -You may want to add a system-native executable to the extension's image, so the extension can execute it. - -In contrast to the extension's code (transpiled into JavaScript) which is executable in any platform, you will need to prepare several OCI images, one for each platform (OS and architecture) you want the extension to support. - -For this, you will need to create: - -- One Containerfile for each platform or a common Containerfile with parameters to create - one image per platform. -- One manifest to reference all images created at the previous step. - -The URL you need to share with the users to install the extension is the URL of the manifest. - -If the manifest does not contain an image for the platform of the user, Podman Desktop will install the -image for Linux (amd64 or arm64 depending on the architecture of the user's platform). - -You can leverage the [Buildah Build action](https://github.com/redhat-actions/buildah-build) to build this manifest. - -#### Procedure: Publish the extension to the catalog - -1. Fork and then clone the [Podman Desktop catalog repository](https://github.com/podman-desktop/podman-desktop-catalog). -1. Checkout a new branch. -1. Add a license, a readme file, and the extension icon to the `podman-desktop-catalog/static/api/extensions/podman-desktop/` directory. -1. Edit the [`extensions.json`](https://github.com/podman-desktop/podman-desktop-catalog/blob/main/static/api/extensions.json) file to add your extension details. [Here](https://github.com/podman-desktop/podman-desktop-catalog/blob/main/static/api/extensions.json#L406) is an example of adding a Podman Desktop extension named Minkibe to the catalog. -1. Create a PR with your changes. -1. Get your PR merged to make your extension available in the catalog. - -Podman Desktop has an in-built automatic update mechanism to refresh the catalog list with the newly added extensions. - -#### Additional resources - -- [Installing a Podman Desktop extension](/docs/extensions/install) -- [Create an extension](/tutorial/creating-an-extension) diff --git a/website/docs/extensions/templates/index.md b/website/docs/extensions/templates/index.md deleted file mode 100644 index 2a503632a0..0000000000 --- a/website/docs/extensions/templates/index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -sidebar_position: 1 -title: Templates -description: Templates that you can use for Podman Desktop extension development -tags: [podman-desktop, extension, publishing] -keywords: [podman desktop, extension, publishing] ---- - -# Templates for creating an extension - -Below are a list of officially maintained templates to be used for Podman Desktop extension development. - -### Minimal template - -![minimal](../img/minimal.png) - -**Link:** [podman-desktop-extension-minimal-template](https://github.com/podman-desktop/podman-desktop-extension-minimal-template) - -This template provides a minimal template on how to build a Podman Desktop extension. More information can be found on our official extension documentation on how to further expand your extension. - -With this template, on activating a "Hello World!" dialog will appear. - -### Webview template - -![webview](../img/webview.png) - -**Link:** [podman-desktop-extension-webview-template](https://github.com/podman-desktop/podman-desktop-extension-webview-template) - -This template provides a webview template on how to build a Podman Desktop extension showcasing a frontend. - -More information can be found on our [official extension documentation](https://podman-desktop.io/docs/extensions) on how to further expand your extension. - -With this template, you will see a new button on the navbar that says "Hello World!" and provides a frontend.A - -### Full template - -![full](../img/full.png) - -**Link:** [podman-desktop-extension-full-template](https://github.com/podman-desktop/podman-desktop-extension-full-template) - -This template provides a "full" example of creating an extension with a webview that utilizes multiple packages. Within this template, we use three separate packages to distinguish between the frontend, backend, and shared code that connects the frontend and backend. - -The "full" template is meant to showcase a full production example which includes multiple frontend and backend technologies such as TypeScript, Svelte and TailwindCSS. - -All backend-related code can be separated into its own package, which improves both security and code organization. - -The template offers flexibility in creating a Podman Desktop extension that can use the underlying Podman Desktop API and pre-built UI components via @podman-desktop/ui-svelte. diff --git a/website/docs/img/containers-component.png b/website/docs/img/containers-component.png deleted file mode 100644 index 2d178de79a..0000000000 Binary files a/website/docs/img/containers-component.png and /dev/null differ diff --git a/website/docs/img/experimental-features.png b/website/docs/img/experimental-features.png deleted file mode 100644 index 11ca1cc0f0..0000000000 Binary files a/website/docs/img/experimental-features.png and /dev/null differ diff --git a/website/docs/img/extentions-component.png b/website/docs/img/extentions-component.png deleted file mode 100644 index e680df1cc9..0000000000 Binary files a/website/docs/img/extentions-component.png and /dev/null differ diff --git a/website/docs/img/images-component.png b/website/docs/img/images-component.png deleted file mode 100644 index 0773256ace..0000000000 Binary files a/website/docs/img/images-component.png and /dev/null differ diff --git a/website/docs/img/kubernetes-component.png b/website/docs/img/kubernetes-component.png deleted file mode 100644 index f710bd2c83..0000000000 Binary files a/website/docs/img/kubernetes-component.png and /dev/null differ diff --git a/website/docs/img/pods-component.png b/website/docs/img/pods-component.png deleted file mode 100644 index 1af53138af..0000000000 Binary files a/website/docs/img/pods-component.png and /dev/null differ diff --git a/website/docs/img/settings.png b/website/docs/img/settings.png deleted file mode 100644 index d69f53c6dd..0000000000 Binary files a/website/docs/img/settings.png and /dev/null differ diff --git a/website/docs/installation/img/click-and-drag.png b/website/docs/installation/img/click-and-drag.png deleted file mode 100644 index dc5471d3d8..0000000000 Binary files a/website/docs/installation/img/click-and-drag.png and /dev/null differ diff --git a/website/docs/installation/img/create-a-podman-machine.png b/website/docs/installation/img/create-a-podman-machine.png deleted file mode 100644 index cd0c200396..0000000000 Binary files a/website/docs/installation/img/create-a-podman-machine.png and /dev/null differ diff --git a/website/docs/installation/img/create.png b/website/docs/installation/img/create.png deleted file mode 100644 index e0522b611a..0000000000 Binary files a/website/docs/installation/img/create.png and /dev/null differ diff --git a/website/docs/installation/img/download-dmg.png b/website/docs/installation/img/download-dmg.png deleted file mode 100644 index 72beee7d9e..0000000000 Binary files a/website/docs/installation/img/download-dmg.png and /dev/null differ diff --git a/website/docs/installation/img/homescreen.png b/website/docs/installation/img/homescreen.png deleted file mode 100644 index 2b39cc8bac..0000000000 Binary files a/website/docs/installation/img/homescreen.png and /dev/null differ diff --git a/website/docs/installation/img/initialize.png b/website/docs/installation/img/initialize.png deleted file mode 100644 index 23d8bb4c8c..0000000000 Binary files a/website/docs/installation/img/initialize.png and /dev/null differ diff --git a/website/docs/installation/img/machine.png b/website/docs/installation/img/machine.png deleted file mode 100644 index 193b15bc58..0000000000 Binary files a/website/docs/installation/img/machine.png and /dev/null differ diff --git a/website/docs/installation/img/pd-before-podman.png b/website/docs/installation/img/pd-before-podman.png deleted file mode 100644 index 69d4d5adb0..0000000000 Binary files a/website/docs/installation/img/pd-before-podman.png and /dev/null differ diff --git a/website/docs/installation/img/podman-desktop-app.png b/website/docs/installation/img/podman-desktop-app.png deleted file mode 100644 index 3838777667..0000000000 Binary files a/website/docs/installation/img/podman-desktop-app.png and /dev/null differ diff --git a/website/docs/installation/img/starting.png b/website/docs/installation/img/starting.png deleted file mode 100644 index cd3201d98f..0000000000 Binary files a/website/docs/installation/img/starting.png and /dev/null differ diff --git a/website/docs/installation/img/system-pass.png b/website/docs/installation/img/system-pass.png deleted file mode 100644 index 3fd024d5b2..0000000000 Binary files a/website/docs/installation/img/system-pass.png and /dev/null differ diff --git a/website/docs/installation/img/windows/podman-desktop-ready.png b/website/docs/installation/img/windows/podman-desktop-ready.png deleted file mode 100644 index 222b4144ce..0000000000 Binary files a/website/docs/installation/img/windows/podman-desktop-ready.png and /dev/null differ diff --git a/website/docs/installation/img/windows/podman-install.png b/website/docs/installation/img/windows/podman-install.png deleted file mode 100644 index de7313e098..0000000000 Binary files a/website/docs/installation/img/windows/podman-install.png and /dev/null differ diff --git a/website/docs/installation/img/windows/prereq-wsl2.png b/website/docs/installation/img/windows/prereq-wsl2.png deleted file mode 100644 index dafb514b32..0000000000 Binary files a/website/docs/installation/img/windows/prereq-wsl2.png and /dev/null differ diff --git a/website/docs/installation/img/wsl_after_reboot.png b/website/docs/installation/img/wsl_after_reboot.png deleted file mode 100644 index d633d326c9..0000000000 Binary files a/website/docs/installation/img/wsl_after_reboot.png and /dev/null differ diff --git a/website/docs/installation/img/wsl_before_reboot_1.png b/website/docs/installation/img/wsl_before_reboot_1.png deleted file mode 100644 index 30ffd1ccd6..0000000000 Binary files a/website/docs/installation/img/wsl_before_reboot_1.png and /dev/null differ diff --git a/website/docs/installation/img/wsl_before_reboot_2.png b/website/docs/installation/img/wsl_before_reboot_2.png deleted file mode 100644 index cb86dc3cb4..0000000000 Binary files a/website/docs/installation/img/wsl_before_reboot_2.png and /dev/null differ diff --git a/website/docs/installation/index.md b/website/docs/installation/index.md deleted file mode 100644 index ceef553d1a..0000000000 --- a/website/docs/installation/index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -sidebar_position: 2 -title: Installation -description: You can install Podman Desktop on Windows, macOS, and Linux. -tags: [podman-desktop, installing] -keywords: [podman desktop, containers, podman, installing, installation] ---- - -# Installing Podman Desktop - -You can install Podman Desktop on: - -- [Windows](/docs/installation/windows-install) -- [macOS](/docs/installation/macos-install) -- [Linux](/docs/installation/linux-install) - -#### Next steps - -- [Onboard for container workloads](/docs/containers). -- [Onboard for Kubernetes workloads](/docs/kubernetes). diff --git a/website/docs/installation/linux-install/index.md b/website/docs/installation/linux-install/index.md deleted file mode 100644 index 49b1caf18f..0000000000 --- a/website/docs/installation/linux-install/index.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -sidebar_position: 5 -title: Linux -description: You can install Podman Desktop on Linux from Flathub, a Flatpak bundle, or a compressed tar file. -tags: [podman-desktop, installing, linux, flathub, flatpak] -keywords: [podman desktop, podman, containers, installing, installation, linux, flathub, flatpak] ---- - -# Installing Podman Desktop on Linux - -Consider installing the Podman Desktop from Flathub to have: - -- One command installation -- Package updates - -Alternatively, you can install Podman Desktop from: - -- [A Flatpak bundle](/docs/installation/linux-install/installing-podman-desktop-from-a-flatpak-bundle) -- [A compressed tar file](/docs/proxy) - -#### Prerequisites - -- [Flatpak](https://flatpak.org/setup/) -- [Podman](https://podman.io/) stable version - -#### Procedure - -1. Verify the Flathub repository is enabled, and add it if required: - - ```shell-session - $ flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo - ``` - -2. Install Podman Desktop from Flathub: - - ```shell-session - $ flatpak install --user flathub io.podman_desktop.PodmanDesktop - ``` - -#### Verification - -- Open Podman Desktop from a terminal: - - ```shell-session - $ flatpak run io.podman_desktop.PodmanDesktop - ``` - -#### Update - -- Update Podman Desktop from Flathub: - - ```shell-session - $ flatpak update --user io.podman_desktop.PodmanDesktop - ``` - -#### Additional resources - -- [Podman Desktop Flathub package](https://flathub.org/apps/details/io.podman_desktop.PodmanDesktop) -- [Using Flatpak](https://docs.flatpak.org/en/latest/using-flatpak.html) - -#### Next steps - -- [Working with containers](/docs/containers) diff --git a/website/docs/installation/linux-install/install-on-rhel10.md b/website/docs/installation/linux-install/install-on-rhel10.md deleted file mode 100644 index bec10f2579..0000000000 --- a/website/docs/installation/linux-install/install-on-rhel10.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -sidebar_position: 3 -title: Installing on RHEL 10 -description: Install Podman Desktop on a RHEL 10 machine -keywords: [podman desktop, podman, install, RHEL 10] -tags: [install-on-rhel-10, podman-desktop-installation] ---- - -# Installing on RHEL 10 - -You can use the subscription manager package to install Podman Desktop on a Red Hat Enterprise Linux (RHEL) 10 machine. - -#### Prerequisites - -- A RHEL 10 machine. -- Register with the subscription manager using either your [account details](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/configuring_basic_system_settings/assembly_registering-the-system-and-managing-subscriptions_configuring-basic-system-settings#registering-a-system-by-using-the-command-line_assembly_registering-the-system-and-managing-subscriptions) or the [activation key](https://docs.redhat.com/en/documentation/subscription_central/1-latest/html/getting_started_with_rhel_system_registration/basic-reg-rhel-cli#proc-reg-rhel-rhc-act-key_). - -#### Procedure - -1. Open a terminal, and enable the RHEL extensions repository: - - ```sh - $ sudo subscription-manager repos --enable rhel-10-for-$(arch)-extensions-rpms - ``` - -1. Enter your password when prompted. -1. Install Podman Desktop: - ```sh - $ sudo dnf install podman-desktop - ``` -1. Enter `y` to confirm that the installed size is okay. -1. Enter `y` to import the GPG key and complete the installation. - -#### Verification - -1. Enter Podman Desktop in the search box at the top of your home screen, and click the application to open it. -1. Follow the prompts to complete a quick onboarding process with the application. - - :::note - - Podman is included with a RHEL subscription, and the application automatically detects and runs it. - - ::: - -1. Run basic tasks, such as: - - [Start a container](/docs/containers/starting-a-container) - - [Create a Kubernetes cluster](/docs/kubernetes/creating-a-kube-cluster) diff --git a/website/docs/installation/linux-install/installing-podman-desktop-from-a-flatpak-bundle.md b/website/docs/installation/linux-install/installing-podman-desktop-from-a-flatpak-bundle.md deleted file mode 100644 index b5185d8f78..0000000000 --- a/website/docs/installation/linux-install/installing-podman-desktop-from-a-flatpak-bundle.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -sidebar_position: 2 -title: Installing from a Flatpak bundle -description: You can install Podman Desktop on Linux from a Flatpak bundle. -tags: [podman-desktop, installing, linux, flathub, flatpak] -keywords: [podman desktop, podman, containers, installing, installation, linux, flathub, flatpak] ---- - -# Installing Podman Desktop from a Flatpak bundle {#flatpak-bundle} - -Consider installing a Flatpak bundle rather than [from Flathub](/docs/installation/linux-install) when: - -- You cannot use Flathub. -- You want to install an unreleased version. - -#### Prerequisites - -- [Flatpak](https://flatpak.org/setup/) -- [Podman](https://podman.io/) stable version - -#### Procedure - -1. Download the Flatpak bundle to a `$HOME/Downloads/podman-desktop-.flatpak` file from: - - [Downloads page](/downloads/linux) - - - [Git repository release assets](https://github.com/podman-desktop/podman-desktop/releases) - -2. Install Podman Desktop from the downloaded Flatpak bundle: - - ```shell-session - $ flatpak install --user $HOME/Downloads/podman-desktop-.flatpak - ``` - -#### Verification - -- Open Podman Desktop from a terminal: - - ```shell-session - $ flatpak run io.podman_desktop.PodmanDesktop - ``` - -#### Additional resources - -- [Using Flatpak](https://docs.flatpak.org/en/latest/using-flatpak.html) - -#### Next steps - -- [Getting started](/docs/containers). diff --git a/website/docs/installation/macos-install.md b/website/docs/installation/macos-install.md deleted file mode 100644 index f70c2612ab..0000000000 --- a/website/docs/installation/macos-install.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -sidebar_position: 3 -title: macOS -description: How to install Podman Desktop and Podman on macOS. -tags: [podman-desktop, installing, macOS] -keywords: [podman desktop, containers, podman, installing, installation, macOS] ---- - -# MacOS - -This page contains information regarding installation of Podman Desktop on MacOS. - -You can install Podman Desktop on macOS: - -1. Using the .dmg file -2. Using Homebrew - -:::info[Prerequisite] -Podman Desktop requires [Podman Engine](https://docs.podman.io/en/latest/index.html). If you don't have Podman Engine installed, Podman Desktop will prompt you to do so at a later stage. -::: - -## Installing Podman Desktop on macOS using .dmg file - -1. Download the `.dmg` file from the [Downloads](/downloads/macos) section of this website. - - While we recommend getting the "universal" binary file which will work irrespective of the chip architecture your Mac possesses, you also have the option to get the applicable .dmg file depending on your Mac Hardware Architecture (that is Intel or Apple M1). - -1. Locate the downloaded file, and double-click on it. (Usually, you will find the downloaded file in the Downloads folder) - - ![img0](img/download-dmg.png) - -1. Drag Podman Desktop icon to the Applications folder. - - ![img1](img/click-and-drag.png) - -1. Start Podman Desktop from the 'Launchpad' or Mac's `Applications` directory. - - ![img2](img/podman-desktop-app.png) - -1. Install Podman from Podman Desktop, if not yet installed. - - When you open Podman Desktop for the first time, click on the "View detection checks" button to scan if all the prerequisites to use Podman Desktop are met. If it says `❌ podman cli was not found in the PATH`, then you need to install the Podman CLI/Engine which can be done within the application. - - ![img3](img/pd-before-podman.png) - -1. Click on the "Install" button next to the "View detection checks" button, and follow the instructions on screen. -1. You will be redirected to the Podman Installer. Follow the instructions on screen and enter your system password when asked. - - ![img4](img/system-pass.png) - -1. After the installation is complete, close the installation program. Podman Engine has been installed and you are now ready to use Podman Desktop. - -## Installing Podman Desktop on macOS using Homebrew - -:::info[Prerequisite] - -- [Homebrew](https://brew.sh/) - - ::: - -### Installation steps - -1. Open a terminal on your Mac. -2. Run the command mentioned below. - - ```sh - brew install podman-desktop - ``` - - Homebrew will also install the Podman Engine along with the Podman Desktop application, in case you don't have it installed yet. - - After the command is executed, you can find the Podman Desktop application in the `Applications` directory of macOS. - -## Getting Started - -Learn more on how to get started with Podman Desktop by clicking [here](/docs/containers). diff --git a/website/docs/installation/windows-install/img/dashboard-podman-is-running.png b/website/docs/installation/windows-install/img/dashboard-podman-is-running.png deleted file mode 100644 index 74318028ad..0000000000 Binary files a/website/docs/installation/windows-install/img/dashboard-podman-is-running.png and /dev/null differ diff --git a/website/docs/installation/windows-install/img/dashboard-podman-needs-set-up.png b/website/docs/installation/windows-install/img/dashboard-podman-needs-set-up.png deleted file mode 100644 index 7728547f2e..0000000000 Binary files a/website/docs/installation/windows-install/img/dashboard-podman-needs-set-up.png and /dev/null differ diff --git a/website/docs/installation/windows-install/img/podman-desktop-setup-installing.png b/website/docs/installation/windows-install/img/podman-desktop-setup-installing.png deleted file mode 100644 index 65a8d531c5..0000000000 Binary files a/website/docs/installation/windows-install/img/podman-desktop-setup-installing.png and /dev/null differ diff --git a/website/docs/installation/windows-install/img/podman-machine-hyperv.png b/website/docs/installation/windows-install/img/podman-machine-hyperv.png deleted file mode 100644 index c965641fe1..0000000000 Binary files a/website/docs/installation/windows-install/img/podman-machine-hyperv.png and /dev/null differ diff --git a/website/docs/installation/windows-install/index.md b/website/docs/installation/windows-install/index.md deleted file mode 100644 index 09a63b3e33..0000000000 --- a/website/docs/installation/windows-install/index.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -sidebar_position: 1 -title: Windows -description: How to install Podman Desktop and Podman on Windows. -tags: [podman-desktop, installing, windows] -keywords: [podman desktop, containers, podman, installing, installation, windows] ---- - -# Installing Podman Desktop and Podman on Windows - -## Installing Podman Desktop - -To install Podman Desktop: - -1. [Download the Windows installer](/downloads/windows). - -1. To start the Podman Desktop installer, open the downloaded file. - - ![Podman Desktop Setup installing](img/podman-desktop-setup-installing.png) - -
    - -Alternate installation methods: -- Silent Windows installer -- Chocolatey -- Scoop -- Winget - - -#### Silent Windows installer - -1. [Download the Windows installer](/downloads/windows). - -1. To install without user interaction, run the Windows installer with the silent flag `/S` from the Command Prompt: - - ```shell-session - > podman-desktop-1.6.4-setup-x64.exe /S - ``` - -#### Chocolatey - -1. Install the [Chocolatey package manager](https://chocolatey.org/install). - -1. Install from the terminal: - - ```shell-session - > choco install podman-desktop - ``` - -#### Scoop package manager for Windows - -1. [Install the Scoop package manager](https://github.com/ScoopInstaller/Install#readme). - -1. Install from the terminal: - - ```shell-session - > scoop bucket add extras - > scoop install podman-desktop - ``` - -#### Winget - -1. [Install the Winget Package manager for Windows](https://aka.ms/getwinget). - -1. Install from the terminal: - - ```shell-session - > winget install -e --id RedHat.Podman-Desktop - ``` - -
    - -## Installing Podman - -On Windows, running the Podman container engine requires running a Linux distribution on a virtual machine. - -### Use WSL2 as machine provider - -Podman Desktop creates a [Windows Subsystem for Linux version 2 (WSL 2)](https://learn.microsoft.com/en-us/windows/wsl/about#what-is-wsl-2) virtual machine: the Podman Machine. - -Main benefits are: - -- Ease of use. -- WSL 2 native virtualization performance. - -Check that your environment has: - -- 6 GB RAM for the Podman Machine. -- Windows Subsystem for Linux version 2 (WSL 2) prerequisites. See [Enabling WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install), [WSL basic commands](https://learn.microsoft.com/en-us/windows/wsl/basic-commands), and [Troubleshooting WSL 2](https://learn.microsoft.com/en-us/windows/wsl/troubleshooting#error-0x80370102-the-virtual-machine-could-not-be-started-because-a-required-feature-is-not-installed): - - The Windows user has administrator privileges. - - Windows 64bit. - - Windows 10 Build 19043 or greater, or Windows 11. - - On a virtual machine: [Nested Virtualization enabled](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization#configure-nested-virtualization). - -To install the Podman Machine: - -1. To prepare your system, enable the WSL feature, without installing the default Ubuntu distribution of Linux. - - Open the Command Prompt, and run:. - - ```shell-session - > wsl --update - > wsl --install --no-distribution - ``` - - :::note - - If you run the Podman Desktop setup on a Windows 10 LTSC version, you require to install a specific WSL distribution. See [Troubleshooting Podman on Windows](/docs/troubleshooting/troubleshooting-podman-on-windows#windows-10-enterprise-ltsc-version-21h2-podman-desktop-is-unable-to-detect-wsl2-machine) - - ::: - -1. Restart your computer. - -1. The **Dashboard** screen displays: _ Podman needs to be set up_. - - ![Podman needs set up screen](img/dashboard-podman-needs-set-up.png) - -1. Click the **Set up** button. - -1. Review and validate all confirmation screens to set up the Podman machine. - -1. When necessary, follow the instructions to install system prerequisites. - -To verify that Podman is set up: - -- In the **Dashboard**, the **Podman** tile displays _Podman is running_. - - ![Podman is running screen](img/dashboard-podman-is-running.png) - -### Use Hyper-V as machine provider - -As an administrator, you can set up a Podman machine using Hyper-V as the machine provider type. To do so, select `hyperv` from the **Provider Type** dropdown list when [creating a Podman machine](/docs/podman/creating-a-podman-machine). - -:::note - -If you already have a running Podman machine with the WSL provider type, you will be prompted to set the Hyper-V machine as the default machine to avoid CLI errors. - -::: - -#### Verification - -1. Go to **Settings > Resources**. -1. View the created machine in the Podman tile. - ![Podman machine with the hyperv provide type](img/podman-machine-hyperv.png) - -#### Next steps - -- [Work with containers](/docs/containers). -- [Work with Kubernetes](/docs/kubernetes). diff --git a/website/docs/intro.md b/website/docs/intro.md deleted file mode 100644 index 67548dbd2e..0000000000 --- a/website/docs/intro.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Introduction - -Podman Desktop is an innovative desktop tool that brings the power of containers and Kubernetes to your computer, making it easy to create, manage, and run containerized applications visually. Intuitive interfaces and smart integration with the most important container technologies, support for Mac, Windows and Linux, Podman Desktop helps you to use containers, pods and Kubernetes on your local machine. - -As you read through this documentation, you'll learn how to: - -- Install and set up Podman Desktop on your system -- The basics about Podman and how to create a machine -- How to migrate from Docker and what to look for -- Create and manage containers, registries and pods -- Setting up, running and troubleshooting Compose -- How to deploy containers to Kubernetes using Kind, Lima, Minikube or OpenShift -- And many more details about the general extension mechanism as well as working with LLMs and AI Lab. - -Whether you're new to containerization or an experienced developer this documentation helps you get started with Podman Desktop quickly. Select what you want to do next: - -- [Install Podman Desktop](/docs/installation). -- [Onboarding container workloads](/docs/containers/onboarding). -- [Manage your container engine workloads](/docs/containers). -- [Work with Kubernetes locally and remote](/docs/kubernetes). diff --git a/website/docs/kind/building-an-image-and-testing-it-in-kind.md b/website/docs/kind/building-an-image-and-testing-it-in-kind.md deleted file mode 100644 index 49c26a2222..0000000000 --- a/website/docs/kind/building-an-image-and-testing-it-in-kind.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -sidebar_position: 10 -title: Building and testing an image -description: Building an image and testing it in Kind -keywords: [podman desktop, podman, containers, pods, building an image, kubernetes, kind] -tags: [building-an-image, testing-an-image-on-kind] ---- - -# Building an image and testing it in Kind - -With Podman Desktop, you can build an image with your container engine, and test it in your local Kind-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a container engine](/docs/containers). -- [You onboarded a Kind cluster](/docs/kind). -- [You have set your Kubernetes context to your local Kind-powered Kubernetes cluster](/docs/kind/working-with-your-local-kind-cluster). -- A container definition file: `Containerfile` or `Dockerfile`. - -#### Procedure - -1. Build your image: - 1. Go to **Images** from the left navigation pane. - 1. Click **Build**. - 1. Provide the relevant details, such as **Containerfile path**, **Image name**, and **Build arguments** to build the image. For example, use the image name `my-custom-image`. - 1. Click **Build**. Wait for the image build to finish. - ![building an image](img/build-image-from-containerfile.png) - 1. Click **Done** to view the new image on the same page. - -1. Push your image to your Kind cluster: - 1. Click the **overflow menu** icon corresponding to `my-custom-image` and select **Push image to Kind cluster**. A successful operation notification opens. - ![pushing an image to Kind](img/pushing-an-image-to-kind.png) - 1. Click **OK**. - -1. Test your image by creating a container: - 1. Click the **Run Image** icon corresponding to the image `my-custom-image`. - ![running an image](img/running-an-image.png) - 1. **Container name**: enter `my-custom-image-container`. - 1. Review the parameters that Podman Desktop has detected from your image definition or edit them, if required. - 1. Click **Start Container**. - ![starting a container](img/starting-a-container.png) - 1. Select the **Summary** tab to view the details of the new container. - 1. Click the **Close** icon. - -1. Test your image and container on your Kind cluster: - 1. Click the overflow menu icon corresponding to the container and select **Deploy to Kubernetes**. - ![deploying to Kubernetes](img/deploying-to-kubernetes.png) - 1. Provide the following details: - - **Pod Name**: Keep the proposed value `my-custom-image-container-pod`. - - **Expose service locally using Kubernetes Ingress**: Select the checkbox to expose the service locally using the ingress controller. - - Optional: If your container exposes more than one port, select the port to expose from the dropdown list. - 1. Click **Deploy**. Wait for the pod to reach the state: **Phase: Running**. - ![deploy button](img/deploy-button.png) - 1. Click **Done**. - -#### Verification - -1. Go to **Pods** from the left navigation pane. -1. View the running `my-custom-image-container-pod` pod. - ![running pod](img/my-custom-image-container-pod.png) -1. Click the pod name to view its details and logs. -1. Optional: If your container exposes a port, go to `http://localhost:`: your application is running. diff --git a/website/docs/kind/configuring-podman-for-kind-on-windows.md b/website/docs/kind/configuring-podman-for-kind-on-windows.md deleted file mode 100644 index 711fc456ac..0000000000 --- a/website/docs/kind/configuring-podman-for-kind-on-windows.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -sidebar_position: 3 -title: Configuring Podman -description: Configuring Podman for Kind on Windows Subsystem for Linux (WSL). -keywords: [podman desktop, podman, containers, migrating, kubernetes, kind] -tags: [configuring-podman-for-kind, kind] ---- - -# Configuring Podman for Kind on Windows Subsystem for Linux (WSL) - -When you create a Podman machine, Podman creates the machine in rootless mode. - -With a Podman machine running on WSL, Kind: - -- Requires the rootful machine. - -Therefore, set the Podman machine to rootful mode. - -#### Procedure - -1. Stop the Podman machine: - - ```shell-session - $ podman machine stop - ``` - -2. Set the Podman machine in rootful mode: - - ```shell-session - $ podman machine set --rootful - ``` - -3. Start the Podman machine: - - ```shell-session - $ podman machine start - ``` diff --git a/website/docs/kind/creating-a-kind-cluster.md b/website/docs/kind/creating-a-kind-cluster.md deleted file mode 100644 index 76c7a18e83..0000000000 --- a/website/docs/kind/creating-a-kind-cluster.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 4 -title: Creating a cluster -description: Creating a local Kind-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, creating a cluster, kubernetes, kind] -tags: [creating-a-kind-cluster, kind] ---- - -# Creating a local Kind-powered Kubernetes cluster - -You can create multiple local Kind-powered Kubernetes clusters. - -#### Prerequisites - -- [On Windows, you configured Podman](/docs/kind/configuring-podman-for-kind-on-windows). -- [You installed Kind](/docs/kind/installing). - - :::note - - If you have not installed the Kind CLI, you will be prompted to install it when you create a Kind cluster. - - ::: - -#### Procedure - -1. Go to ** Settings > Resources** -1. In the Kind tile, click the **Create new ...** button. -1. Perform one of the following steps: - - Create a Kind cluster with a _customized_ configuration by providing a path to the configuration file. The Kind cluster is created based on the values specified in the configuration file. - ![creating a Kind cluster with custom configuration](img/kind-cluster-with-custom-configuration.png) - - :::note - - When you provide the configuration file, the values specified in that file take precedence over the default values. - - ::: - - - Create a Kind cluster with the _default_ configuration. However, you can edit the default configuration, if needed. For example, you can edit the port number or any other configuration details. - ![creating a Kind cluster with default configuration](img/kind-cluster-with-default-configuration.png) - -1. Click the **Create** button. -1. Optional: Click the **Show logs** button to view the logs. -1. After successful creation, click the **Go back to resources** button. - -#### Verification - -1. Go to ** Settings > Resources**, and view your running `` instance in the **Kind** tile. - ![running Kind cluster instance](img/kind-cluster-running.png) -1. In the Podman Desktop tray, select the **Kubernetes** menu; you can set the context to your Kind cluster: `kind-`. - - :::note - - Alternatively, use the status bar or the Podman Desktop **Settings** to set your Kubernetes context. For more details, see [Viewing and selecting the current Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). - - ::: diff --git a/website/docs/kind/deleting-your-kind-cluster.md b/website/docs/kind/deleting-your-kind-cluster.md deleted file mode 100644 index ab08dd3143..0000000000 --- a/website/docs/kind/deleting-your-kind-cluster.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 6 -title: Deleting a cluster -description: Deleting your local Kind-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, migrating, kubernetes, kind] -tags: [deleting-a-kind-cluster, kind] ---- - -# Deleting your local Kind-powered Kubernetes cluster - -#### Prerequisites - -- [You configured Podman](/docs/kind/creating-a-kind-cluster). -- [You installed Kind](https://kind.sigs.k8s.io/). - -#### Procedure - -1. Open ** Settings > Resources**. -1. Find the Kind cluster to delete. -1. Click to stop the cluster. -1. Once the cluster is stopped, click to delete it. - -#### Verification - -1. In ** Settings > Resources**, the deleted Kind cluster is not visible. diff --git a/website/docs/kind/img/build-image-from-containerfile.png b/website/docs/kind/img/build-image-from-containerfile.png deleted file mode 100644 index 58ad7a04aa..0000000000 Binary files a/website/docs/kind/img/build-image-from-containerfile.png and /dev/null differ diff --git a/website/docs/kind/img/create-pod-from-kube-yaml.png b/website/docs/kind/img/create-pod-from-kube-yaml.png deleted file mode 100644 index 29de10b07e..0000000000 Binary files a/website/docs/kind/img/create-pod-from-kube-yaml.png and /dev/null differ diff --git a/website/docs/kind/img/deploy-button.png b/website/docs/kind/img/deploy-button.png deleted file mode 100644 index 69df5a88b6..0000000000 Binary files a/website/docs/kind/img/deploy-button.png and /dev/null differ diff --git a/website/docs/kind/img/deploying-to-kubernetes.png b/website/docs/kind/img/deploying-to-kubernetes.png deleted file mode 100644 index 47e75589d3..0000000000 Binary files a/website/docs/kind/img/deploying-to-kubernetes.png and /dev/null differ diff --git a/website/docs/kind/img/kind-cluster-running.png b/website/docs/kind/img/kind-cluster-running.png deleted file mode 100644 index 5c9ace426f..0000000000 Binary files a/website/docs/kind/img/kind-cluster-running.png and /dev/null differ diff --git a/website/docs/kind/img/kind-cluster-with-custom-configuration.png b/website/docs/kind/img/kind-cluster-with-custom-configuration.png deleted file mode 100644 index 6147361cac..0000000000 Binary files a/website/docs/kind/img/kind-cluster-with-custom-configuration.png and /dev/null differ diff --git a/website/docs/kind/img/kind-cluster-with-default-configuration.png b/website/docs/kind/img/kind-cluster-with-default-configuration.png deleted file mode 100644 index e29d6bd333..0000000000 Binary files a/website/docs/kind/img/kind-cluster-with-default-configuration.png and /dev/null differ diff --git a/website/docs/kind/img/kind-resource.png b/website/docs/kind/img/kind-resource.png deleted file mode 100644 index 2a0b5b8555..0000000000 Binary files a/website/docs/kind/img/kind-resource.png and /dev/null differ diff --git a/website/docs/kind/img/kind-status-bar.png b/website/docs/kind/img/kind-status-bar.png deleted file mode 100644 index 8ef44ca714..0000000000 Binary files a/website/docs/kind/img/kind-status-bar.png and /dev/null differ diff --git a/website/docs/kind/img/my-custom-image-container-pod.png b/website/docs/kind/img/my-custom-image-container-pod.png deleted file mode 100644 index 09cf8cdf0a..0000000000 Binary files a/website/docs/kind/img/my-custom-image-container-pod.png and /dev/null differ diff --git a/website/docs/kind/img/push-image-to-kind.png b/website/docs/kind/img/push-image-to-kind.png deleted file mode 100644 index 593214ece4..0000000000 Binary files a/website/docs/kind/img/push-image-to-kind.png and /dev/null differ diff --git a/website/docs/kind/img/pushing-an-image-to-kind.png b/website/docs/kind/img/pushing-an-image-to-kind.png deleted file mode 100644 index 934a386b2d..0000000000 Binary files a/website/docs/kind/img/pushing-an-image-to-kind.png and /dev/null differ diff --git a/website/docs/kind/img/restart-using-the-containers-page.png b/website/docs/kind/img/restart-using-the-containers-page.png deleted file mode 100644 index 860ce13e70..0000000000 Binary files a/website/docs/kind/img/restart-using-the-containers-page.png and /dev/null differ diff --git a/website/docs/kind/img/restart-using-the-settings-page.png b/website/docs/kind/img/restart-using-the-settings-page.png deleted file mode 100644 index 336b04d701..0000000000 Binary files a/website/docs/kind/img/restart-using-the-settings-page.png and /dev/null differ diff --git a/website/docs/kind/img/running-an-image.png b/website/docs/kind/img/running-an-image.png deleted file mode 100644 index 2e406d0635..0000000000 Binary files a/website/docs/kind/img/running-an-image.png and /dev/null differ diff --git a/website/docs/kind/img/select-a-kind-cluster.png b/website/docs/kind/img/select-a-kind-cluster.png deleted file mode 100644 index 5cd3a3ae5e..0000000000 Binary files a/website/docs/kind/img/select-a-kind-cluster.png and /dev/null differ diff --git a/website/docs/kind/img/starting-a-container.png b/website/docs/kind/img/starting-a-container.png deleted file mode 100644 index f408429ccc..0000000000 Binary files a/website/docs/kind/img/starting-a-container.png and /dev/null differ diff --git a/website/docs/kind/img/verify-my-image-pod-running.png b/website/docs/kind/img/verify-my-image-pod-running.png deleted file mode 100644 index ba9033869c..0000000000 Binary files a/website/docs/kind/img/verify-my-image-pod-running.png and /dev/null differ diff --git a/website/docs/kind/index.md b/website/docs/kind/index.md deleted file mode 100644 index 8a7796fca0..0000000000 --- a/website/docs/kind/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 70 -title: Kind -description: Kind is one way to get Kubernetes running on your workstation. -keywords: [podman desktop, podman, containers, migrating, kubernetes, kind] -tags: [migrating-to-kubernetes, kind] ---- - -# Running Kubernetes on your workstation with Kind and Podman - -Podman Desktop can help you run [Kind-powered](https://kind.sigs.k8s.io/) local Kubernetes clusters on a container engine, such as Podman. - -#### Procedure - -1. [Install the `kind` CLI](/docs/kind/installing). -2. [On Windows, configure Podman in rootful mode](/docs/kind/configuring-podman-for-kind-on-windows). -3. [Create a Kind cluster](/docs/kind/creating-a-kind-cluster). - -#### Next steps - -1. [Set your Kubernetes context to your local Kind-powered Kubernetes cluster](/docs/kind/working-with-your-local-kind-cluster). -1. [Build an image and test it in Kind](/docs/kind/building-an-image-and-testing-it-in-kind). -1. [Push an image to Kind](/docs/kind/pushing-an-image-to-kind). -1. [Restart your Kind cluster](/docs/kind/restarting-your-kind-cluster). -1. [Delete your Kind cluster](/docs/kind/deleting-your-kind-cluster). diff --git a/website/docs/kind/installing-extension.md b/website/docs/kind/installing-extension.md deleted file mode 100644 index 2d47a6ba5c..0000000000 --- a/website/docs/kind/installing-extension.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -sidebar_position: 1 -title: Installing the Kind extension -description: Tutorial on how to install the Kind extension and use it -keywords: [podman desktop, podman, kind, kubernetes] -tags: [podman, kubernetes, kind] ---- - -import ReactPlayer from 'react-player' - -# Installing the Kind extension - -:::note - -Kind is a _built-in_ extension to Podman Desktop and no installation is necessary. - -::: - -The Kind extension provides the capability of creating a local Kubernetes cluster using only containers rather than a Virtual Machine. - -#### Procedure - -[Install Podman Desktop](/docs/installation) to have the extension available. To confirm the built-in extension is installed, you can find Kind within the **Extensions** section of Podman Desktop. - -#### Tutorial - -The following video provides a complete guide on creating a single-node cluster: - - - -:::note - -If you are running Podman Desktop in a Linux system host and enabling the Contour ingress controller, you need to ensure that the `ip_tables` module is loaded, otherwise the `envoy` pod will fail to insert an iptable rule that it needs in order to be deployed (`/usr/sbin/iptables -t nat -S CNI-HOSTPORT-SETMARK 1 --wait`). To accomplish that, run the command `sudo modprobe ip_tables` to enable the required module, and then the command `lsmod | grep ip_tables` to check if it is enabled. - -::: diff --git a/website/docs/kind/installing.md b/website/docs/kind/installing.md deleted file mode 100644 index d09641930e..0000000000 --- a/website/docs/kind/installing.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 2 -title: Installing the CLI -description: Kind is one way to get Kubernetes running on your workstation. -keywords: [podman desktop, podman, containers, installing CLI, kubernetes, kind] -tags: [installing-the-kind-CLI, kind] ---- - -# Installing the `kind` CLI - -#### Procedure - -- In the status bar, click on **Kind**, and follow the prompts. - ![Kind in the status bar](img/kind-status-bar.png) - -#### Verification - -1. The status bar does not display **Kind**. -1. ** Settings > Resources** contain a **Kind** tile. - ![Kind resource tile](img/kind-resource.png) -1. You can run the `kind` CLI: - - ```shell-session - $ kind get clusters - ``` diff --git a/website/docs/kind/pushing-an-image-to-kind.md b/website/docs/kind/pushing-an-image-to-kind.md deleted file mode 100644 index 73ed0fe5b1..0000000000 --- a/website/docs/kind/pushing-an-image-to-kind.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -sidebar_position: 10 -title: Pushing an image -description: Pushing an image to your Kind cluster -keywords: [podman desktop, podman, containers, images, pushing an image, kubernetes] -tags: [pushing-an-image, images] ---- - -# Pushing an image to your local Kind-powered Kubernetes cluster - -With Podman Desktop, you can push an image to your local Kind-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a container engine](/docs/containers). -- [You onboarded a Kind cluster](/docs/kind). -- [You have set your Kubernetes context to your local Kind-powered Kubernetes cluster](/docs/kind/working-with-your-local-kind-cluster). -- Your image is available on the **Images** page: `:`. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click the **overflow menu** icon corresponding to the image you want to push and select **Push image to Kind cluster**. - ![pushing an image to Kind](img/push-image-to-kind.png) - -1. Optional: If you created multiple Kind clusters, select the required Kind cluster from the context dropdown list. - ![selecting a Kind cluster](img/select-a-kind-cluster.png) - A successful operation notification opens. -1. Click **OK**. - -#### Verification - -Kind does not enable you to list loaded images. -Therefore, create a pod that uses the loaded image. - -1. Create a `verify_my_image.yaml` Kubernetes YAML file on your workstation. - Replace the placeholders: - - Pod `name` and container `name` values must consist of lowercase alphanumeric characters, '-', or '.', and must start and end with an alphanumeric character. - - Container `image` value is the image you pushed. You can click the name of the image to check its name and tag. - - ```yaml - apiVersion: v1 - kind: Pod - metadata: - name: - spec: - containers: - - name: - image: : - imagePullPolicy: Never - ``` - -1. Go to **Pods** from the left navigation pane. -1. Click **Play Kubernetes YAML** and provide the following details: - - **Kubernetes YAML file**: select your `verify_my_image.yaml` file. - - Set **Runtime** to **Kubernetes cluster**. -1. Click **Play**. - ![play a Kubernetes YAML](img/create-pod-from-kube-yaml.png) -1. Click **Done**. -1. View the created pod `verify-my-image` on the same page. The pod **STATUS** is **RUNNING**. - ![play a Kubernetes YAML](img/verify-my-image-pod-running.png) diff --git a/website/docs/kind/restarting-your-kind-cluster.md b/website/docs/kind/restarting-your-kind-cluster.md deleted file mode 100644 index 124dff6533..0000000000 --- a/website/docs/kind/restarting-your-kind-cluster.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -sidebar_position: 5 -title: Restarting a cluster -description: Restarting your local Kind-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, restarting, kubernetes, kind] -tags: [restarting-a-cluster, kind] ---- - -# Restarting your local Kind-powered Kubernetes cluster - -With Podman Desktop, you can restart your local Kind-powered Kubernetes cluster. - -#### Procedure - -Perform one of the following steps: - -- Restart using the **Settings** page - 1. Open ** Settings > Resources**. - 1. Find the Kind cluster to restart. - 1. Click the **Restart** icon. - ![restart using the settings page](img/restart-using-the-settings-page.png) -- Restart using the **Containers** page: - 1. Open **Containers** from the left navigation pane. - 1. Click the **overflow menu** icon corresponding to the Kind cluster container and select **Restart Container**. - ![restart using the Containers page](img/restart-using-the-containers-page.png) - -#### Verification - -1. Open **Containers** from the left navigation pane. -1. Find the Kind cluster that restarted. The cluster **Age** is consistent with the restart time. -1. Open **Pods** from the left navigation pane. -1. Find the pods that are running on your Kind cluster. - -#### Workaround - -Kind has no command to restart a cluster. -Therefore, Podman Desktop stops the Kind cluster, starts it again, and hopes for the best. -The Kind cluster might not restart successfully. -In that case: - -- Consider replacing Kind with a local Kubernetes cluster that you can restart, such as [OpenShift Local](https://developers.redhat.com/products/openshift-local/). -- Consider [deleting your Kind cluster](/docs/kind/deleting-your-kind-cluster), and [creating a Kind cluster](/docs/kind/creating-a-kind-cluster). diff --git a/website/docs/kind/working-with-your-local-kind-cluster.md b/website/docs/kind/working-with-your-local-kind-cluster.md deleted file mode 100644 index 341d6b80bb..0000000000 --- a/website/docs/kind/working-with-your-local-kind-cluster.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -sidebar_position: 5 -title: Working with a cluster -description: Working with your local Kind-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, setting context, kubernetes, kind] -tags: [working-with-a-kind-cluster, kind] ---- - -# Working with your local Kind-powered Kubernetes cluster - -Set your Kubernetes context to your local Kind-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a Kind cluster](/docs/kind). -- [You have set your Kubernetes context to your local Kind-powered Kubernetes cluster](/docs/kind/working-with-your-local-kind-cluster). - -#### Procedure - -1. Open the Podman Desktop tray. -2. Go to **Kubernetes**. -3. Click the Kubernetes context with the `kind` prefix. - -:::note - -Alternatively, use the status bar or the Podman Desktop **Settings** to set your Kubernetes context. For more details, see [Viewing and selecting the current Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). - -::: - -#### Verification - -- The Kubernetes CLI reports that the current context is your cluster with the `kind` suffix: - - ```shell-session - $ kubectl config current-context - ``` diff --git a/website/docs/kubernetes/applying-a-yaml-manifest.md b/website/docs/kubernetes/applying-a-yaml-manifest.md deleted file mode 100644 index a1cbe59960..0000000000 --- a/website/docs/kubernetes/applying-a-yaml-manifest.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_position: 12 -title: Applying a YAML manifest -description: How to apply a YAML manifest to create a kubernetes object -keywords: [podman desktop, podman, objects, kubernetes] -tags: [applying-a-yaml-manifest, creating-an-object] ---- - -# Applying a YAML manifest - -You can deploy a YAML manifest to create a Kubernetes object, such as `Node`, `Deployment`, `Service`, `PersistentVolumeClaim`, and others. For example, a Kubernetes deployment that requires sensitive data storage, you can apply a YAML manifest to create a `Secret` object. - -#### Prerequisites - -Make sure you have: - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- [A running Kubernetes cluster](/docs/kind/creating-a-kind-cluster). -- Created a YAML manifest file using the following code, if you do not have one on your machine: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: my-nginx -spec: - selector: - matchLabels: - run: my-nginx - replicas: 3 - template: - metadata: - labels: - run: my-nginx - spec: - containers: - - name: my-nginx - image: nginx - ports: - - containerPort: 80 -``` - -This YAML manifest creates three pods that run the NGINX web server. - -#### Procedure: Applying a YAML manifest to create a `Deployment` object - -1. Go to the **Kubernetes** component page. -1. Click **Deployments** in the left navigation pane. -1. Select a namespace in which you want to create the object. -1. Click **Apply YAML** and select the YAML manifest file. A confirmation notification opens. - ![applying a yaml manifest](img/applying-a-yaml-manifest.png) -1. Click **OK**. - ![confirmation notification](img/confirmation-notification.png) - -#### Verification - -1. View the created `my-nginx` deployment on the same page. - ![nginx deployment](img/my-nginx-deployment.png) - - The deployment might take some time to make pods available for use. - -1. Click **Pods** in the Kubernetes explorer to view three instances of the NGINX web server running. - ![nginx pods running](img/nginx-pods-running.png) - - :::note - - When you apply any other YAML manifest, you can view the created object on the corresponding object page. - - ::: diff --git a/website/docs/kubernetes/configuring-editing-kube-object.md b/website/docs/kubernetes/configuring-editing-kube-object.md deleted file mode 100644 index 50457209cc..0000000000 --- a/website/docs/kubernetes/configuring-editing-kube-object.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -sidebar_position: 12 -title: Managing objects -description: Creating and updating a kubernetes object -keywords: [podman desktop, podman, deploying, objects, kubernetes] -tags: [managing-kubernetes, objects] ---- - -# Managing Kubernetes objects - -With Podman Desktop, you can easily transition from containers to Kubernetes and deploy a local Kubernetes environment with necessary objects. As a developer, you have the ability to: - -- Manage your application resources visually. -- Configure the following Kubernetes objects: - - `Node`: Use this object to set up a node on which the necessary pods can run within a kubernetes cluster. - - `Deployment`: Use this object to create necessary pods for execution and scale the number of pods. - - `Pod`: Use this object to create a set of one or more containers with shared storage and network resources. - - `Service`: Use this object to expose your application to users and define policies for application access. - - `Ingress`: Use this object to define routing rules and manage user access to the services running in a Kubernetes cluster. - - `PersistentVolumeClaim`: Use this object to request `PersistentVolume` resources for storage and define volume access modes within your Kubernetes cluster. - - `ConfigMap`: Use this object to define non-sensitive configuration data for initializing or executing your application. - - `Secret`: Use this object to store and manage sensitive data, such as passwords, OAuth tokens, and SSH keys for your application. - - `Jobs`: Use this object to create one or more pods and run them in parallel. - - `CronJob`: Use this object to run a job, such as backup and report generation periodically on a given schedule. -- View and analyze real-time information about the connection status of the resources configured within the cluster. -- View the dashboard for analyzing object metrics and reading Kubernetes articles and blog posts. -- Get resource details using the _Summary_ and _Inspect_ tabs. -- Edit and apply configuration changes directly using the _Kube_ tab. -- Select multiple configuration files and apply them to your cluster in a single step. -- Configure port forwarding for a Kubernetes service and view the port forwarding details. - -#### Prerequisites - -- A valid [Kubernetes context and connection](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). -- A pod creation example for reference: - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: my-pod -spec: - containers: - - name: my-container - image: nginx:1.14.2 - ports: - - containerPort: 80 -``` - -#### Procedure: Creating an object - -1. Go to the **Kubernetes** component page. -2. Click one of the options to open the object page. - - ![kube objects](img/kube-objects.png) - -3. Click the **Apply YAML** button and select an object configuration file. A confirmation notification opens. - ![configuring a node](img/example-config-node.png) -4. Click **Ok**. - -#### Procedure: Updating an existing object - -1. Go to the **Kubernetes** component page. -2. Click one of the options to open the object page. -3. Click the name of the object. -4. Select the **Kube** tab and edit the configuration file. - ![editing a node](img/example-edit-node.png) -5. Click **Apply changes to cluster**. - -#### Verification - -1. View the created object: - - View the `Pod` object on the **Kubernetes > Pods** object page. - - View other Kubernetes objects on the related object page. For example, if you have created a `Node` object, you can view it on the **Kubernetes > Nodes** page. - -2. Optional: Click the name of the object to view its detailed summary. - ![summary tab](img/summary-tab.png) diff --git a/website/docs/kubernetes/creating-a-kube-cluster.md b/website/docs/kubernetes/creating-a-kube-cluster.md deleted file mode 100644 index 2c669a4ec6..0000000000 --- a/website/docs/kubernetes/creating-a-kube-cluster.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -sidebar_position: 11 -title: Creating a Kubernetes cluster -description: Creating a Kubernetes cluster -keywords: [podman desktop, podman, creating, cluster, kubernetes] -tags: [creating-kubernetes-cluster, extentions] ---- - -# Creating a Kubernetes cluster using extensions - -Podman Desktop provides extensions, such as Kind, MiniKube, and others to start a local Kubernetes development cluster. The following table covers the procedural sections for setting up a Kubernetes cluster: - -| Extension | Procedural sections to follow | -| :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| Kind | [Install the Kind extension](/docs/extensions/install) > [Install the Kind CLI](/docs/kind/installing) > [Configure Podman on WSL](/docs/kind/configuring-podman-for-kind-on-windows) > [Create a Kind cluster](/docs/kind/creating-a-kind-cluster) | -| Minikube | [Install the Minikube extension](/docs/extensions/install) > [Install the Minikube CLI](/docs/minikube/installing) > [Configure Podman on WSL](/docs/minikube/configuring-podman-for-minikube-on-windows) > [Create a Minikube cluster](/docs/minikube/creating-a-minikube-cluster) | -| Lima | [Install the Lima extention](/docs/extensions/install) > [Install the Lima CLI](/docs/lima/installing) > [Create a Lima instance for Kubernetes](/docs/lima/creating-a-kubernetes-instance) | - -:::note - -The _Configure Podman on WSL_ procedure is applicable only if you have installed the Podman Desktop application on a Windows machine. For MacOS and Linux, you can skip this procedure. - -::: diff --git a/website/docs/kubernetes/deploying-a-pod-to-kubernetes.md b/website/docs/kubernetes/deploying-a-pod-to-kubernetes.md deleted file mode 100644 index 02b3408c40..0000000000 --- a/website/docs/kubernetes/deploying-a-pod-to-kubernetes.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -sidebar_position: 10 -title: Deploying a pod or container -description: Deploying a pod or container to Kubernetes -keywords: [podman desktop, podman, containers, pods, migrating, kubernetes] -tags: [migrating-to-kubernetes, deploying-a-pod, deploying-a-container] ---- - -# Deploying a pod or container to Kubernetes - -With Podman Desktop, you can deploy a pod to your Kubernetes cluster with an active connection. Any container that is part of a pod is also deployable to your cluster. - -#### Prerequisites - -- You are using the Podman container engine. -- Your pod, running or stopped, is available on the **Pods** page: _<your_pod>_. -- You registered the Kubernetes cluster in your kubeconfig file: _<your_kubernetes_cluster>_. For example, [Creating a kind cluster](/docs/kind/creating-a-kind-cluster). -- The cluster is reachable. - - Go to **Settings > Kubernetes**, and click **Connect** in the cluster tile. -- The Kubernetes namespace to deploy to already exists. -- Your container has a port that is exposed correctly to generate a service. - -#### Procedure - -1. Select your [Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). -1. Go to the **Pods** or **Containers** component page. -1. Click the overflow menu icon corresponding to the pod or container. - ![overflow menu icon](img/overflow-menu-icon.png) -1. Select the **Deploy to Kubernetes** option from the dropdown list. -1. Select the checkbox to expose the service locally by using the default ingress controller. - ![expose the service locally](img/expose-the-service-locally.png) -1. Conditional: When you configure custom port mapping while running an image, you have the option to select an Ingress host port from the dropdown list. - ![ingress-host-port](img/ingress-host-port.png) - Otherwise, you do not see the option. -1. Optional: Select a namespace from the dropdown list. - - :::note - - By default, your pod or container is deployed to the `default` namespace. - - ::: - -1. Click **Deploy** and then **Done**. - -#### Verification - -1. On the **Deploy generated pod to Kubernetes** screen, the created pod status is _Phase: Running_ - ![Deploying a pod](img/deploying-a-pod.png) - -1. Go to **Kubernetes > Pods**: your Kubernetes pod is in the list. - ![kube pod in the list](img/kube-pod-in-the-list.png) - -1. Optional: Check the running service on the **Kubernetes > Services** page. - ![running service](img/running-service.png) diff --git a/website/docs/kubernetes/existing-kubernetes/index.md b/website/docs/kubernetes/existing-kubernetes/index.md deleted file mode 100644 index 25b5d2d094..0000000000 --- a/website/docs/kubernetes/existing-kubernetes/index.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 2 -title: Existing Kubernetes -description: Configuring access to a Kubernetes cluster -keywords: [podman desktop, podman, containers, pods, migrating, kubernetes] -tags: [migrating-to-kubernetes] ---- - -# Configuring access to a Kubernetes cluster - -Podman Desktop configures the access to Kubernetes clusters automatically when: - -- [Creating a Kind-powered local Kubernetes cluster](/docs/kind/creating-a-kind-cluster). -- [Creating an OpenShift Local cluster](/docs/openshift/openshift-local). -- [Configuring access to a Developer Sandbox](/docs/openshift/developer-sandbox). - -You can also use the Kubernetes CLI to configure access to your Kubernetes cluster: - -#### Prerequisites - -- You have credentials for your Kubernetes cluster. - -#### Procedure - -1. (Optionally) Go to ** Settings > Preferences > Kubernetes** to adapt your kubeconfig file location, when different from the default `$HOME/.kube/config`. -1. Register your _``_ Kubernetes cluster: - - ```shell-session - $ kubectl config set-cluster --server= - ``` - -#### Verification - -- You can [view and select the Kubernetes cluster in Podman Desktop](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context) - -#### Additional resopurces - -- [Kubernetes documentation: Configure access to multiple clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/) diff --git a/website/docs/kubernetes/img/applying-a-yaml-manifest.png b/website/docs/kubernetes/img/applying-a-yaml-manifest.png deleted file mode 100644 index 8a160a1f01..0000000000 Binary files a/website/docs/kubernetes/img/applying-a-yaml-manifest.png and /dev/null differ diff --git a/website/docs/kubernetes/img/confirmation-notification.png b/website/docs/kubernetes/img/confirmation-notification.png deleted file mode 100644 index 6952dc5f09..0000000000 Binary files a/website/docs/kubernetes/img/confirmation-notification.png and /dev/null differ diff --git a/website/docs/kubernetes/img/context-details.png b/website/docs/kubernetes/img/context-details.png deleted file mode 100644 index 5875ec55c2..0000000000 Binary files a/website/docs/kubernetes/img/context-details.png and /dev/null differ diff --git a/website/docs/kubernetes/img/delete-icon-on-port-forwarding-page.png b/website/docs/kubernetes/img/delete-icon-on-port-forwarding-page.png deleted file mode 100644 index 9852de9354..0000000000 Binary files a/website/docs/kubernetes/img/delete-icon-on-port-forwarding-page.png and /dev/null differ diff --git a/website/docs/kubernetes/img/deploying-a-container.png b/website/docs/kubernetes/img/deploying-a-container.png deleted file mode 100644 index ed2a484736..0000000000 Binary files a/website/docs/kubernetes/img/deploying-a-container.png and /dev/null differ diff --git a/website/docs/kubernetes/img/deploying-a-pod.png b/website/docs/kubernetes/img/deploying-a-pod.png deleted file mode 100644 index 48b7f72183..0000000000 Binary files a/website/docs/kubernetes/img/deploying-a-pod.png and /dev/null differ diff --git a/website/docs/kubernetes/img/edit-context-icon.png b/website/docs/kubernetes/img/edit-context-icon.png deleted file mode 100644 index 016e3b1cb8..0000000000 Binary files a/website/docs/kubernetes/img/edit-context-icon.png and /dev/null differ diff --git a/website/docs/kubernetes/img/edit-context-window.png b/website/docs/kubernetes/img/edit-context-window.png deleted file mode 100644 index d245a574fe..0000000000 Binary files a/website/docs/kubernetes/img/edit-context-window.png and /dev/null differ diff --git a/website/docs/kubernetes/img/example-config-node.png b/website/docs/kubernetes/img/example-config-node.png deleted file mode 100644 index abc1a9e4e8..0000000000 Binary files a/website/docs/kubernetes/img/example-config-node.png and /dev/null differ diff --git a/website/docs/kubernetes/img/example-edit-node.png b/website/docs/kubernetes/img/example-edit-node.png deleted file mode 100644 index 9656c119da..0000000000 Binary files a/website/docs/kubernetes/img/example-edit-node.png and /dev/null differ diff --git a/website/docs/kubernetes/img/expose-the-service-locally.png b/website/docs/kubernetes/img/expose-the-service-locally.png deleted file mode 100644 index 0281c31ce3..0000000000 Binary files a/website/docs/kubernetes/img/expose-the-service-locally.png and /dev/null differ diff --git a/website/docs/kubernetes/img/forward-button.png b/website/docs/kubernetes/img/forward-button.png deleted file mode 100644 index f8c1b383a0..0000000000 Binary files a/website/docs/kubernetes/img/forward-button.png and /dev/null differ diff --git a/website/docs/kubernetes/img/ingress-host-port.png b/website/docs/kubernetes/img/ingress-host-port.png deleted file mode 100644 index aa4a8d67fd..0000000000 Binary files a/website/docs/kubernetes/img/ingress-host-port.png and /dev/null differ diff --git a/website/docs/kubernetes/img/kube-objects.png b/website/docs/kubernetes/img/kube-objects.png deleted file mode 100644 index b2ff069776..0000000000 Binary files a/website/docs/kubernetes/img/kube-objects.png and /dev/null differ diff --git a/website/docs/kubernetes/img/kube-pod-in-the-list.png b/website/docs/kubernetes/img/kube-pod-in-the-list.png deleted file mode 100644 index 6d6fcdb4a1..0000000000 Binary files a/website/docs/kubernetes/img/kube-pod-in-the-list.png and /dev/null differ diff --git a/website/docs/kubernetes/img/my-nginx-deployment.png b/website/docs/kubernetes/img/my-nginx-deployment.png deleted file mode 100644 index 9d3b05af2b..0000000000 Binary files a/website/docs/kubernetes/img/my-nginx-deployment.png and /dev/null differ diff --git a/website/docs/kubernetes/img/new-kubernetes-pod.png b/website/docs/kubernetes/img/new-kubernetes-pod.png deleted file mode 100644 index 55531806ff..0000000000 Binary files a/website/docs/kubernetes/img/new-kubernetes-pod.png and /dev/null differ diff --git a/website/docs/kubernetes/img/nginx-pods-running.png b/website/docs/kubernetes/img/nginx-pods-running.png deleted file mode 100644 index 4100c90659..0000000000 Binary files a/website/docs/kubernetes/img/nginx-pods-running.png and /dev/null differ diff --git a/website/docs/kubernetes/img/open-button.png b/website/docs/kubernetes/img/open-button.png deleted file mode 100644 index 9f3881b820..0000000000 Binary files a/website/docs/kubernetes/img/open-button.png and /dev/null differ diff --git a/website/docs/kubernetes/img/overflow-menu-icon.png b/website/docs/kubernetes/img/overflow-menu-icon.png deleted file mode 100644 index 059a8d425a..0000000000 Binary files a/website/docs/kubernetes/img/overflow-menu-icon.png and /dev/null differ diff --git a/website/docs/kubernetes/img/play-a-yaml-file.png b/website/docs/kubernetes/img/play-a-yaml-file.png deleted file mode 100644 index ec39187fe7..0000000000 Binary files a/website/docs/kubernetes/img/play-a-yaml-file.png and /dev/null differ diff --git a/website/docs/kubernetes/img/play-kubernetes-yaml.png b/website/docs/kubernetes/img/play-kubernetes-yaml.png deleted file mode 100644 index 2f0e0d83cb..0000000000 Binary files a/website/docs/kubernetes/img/play-kubernetes-yaml.png and /dev/null differ diff --git a/website/docs/kubernetes/img/running-service.png b/website/docs/kubernetes/img/running-service.png deleted file mode 100644 index 78297c650e..0000000000 Binary files a/website/docs/kubernetes/img/running-service.png and /dev/null differ diff --git a/website/docs/kubernetes/img/selecting-context-from-dropdown.png b/website/docs/kubernetes/img/selecting-context-from-dropdown.png deleted file mode 100644 index fc220a4e38..0000000000 Binary files a/website/docs/kubernetes/img/selecting-context-from-dropdown.png and /dev/null differ diff --git a/website/docs/kubernetes/img/selecting-context.png b/website/docs/kubernetes/img/selecting-context.png deleted file mode 100644 index 3638b1db70..0000000000 Binary files a/website/docs/kubernetes/img/selecting-context.png and /dev/null differ diff --git a/website/docs/kubernetes/img/stop-port-forwarding-pod.png b/website/docs/kubernetes/img/stop-port-forwarding-pod.png deleted file mode 100644 index f3d34d866e..0000000000 Binary files a/website/docs/kubernetes/img/stop-port-forwarding-pod.png and /dev/null differ diff --git a/website/docs/kubernetes/img/summary-tab.png b/website/docs/kubernetes/img/summary-tab.png deleted file mode 100644 index f98d37b02c..0000000000 Binary files a/website/docs/kubernetes/img/summary-tab.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-icon-on-macos.png b/website/docs/kubernetes/img/tray-icon-on-macos.png deleted file mode 100644 index b15272e4a0..0000000000 Binary files a/website/docs/kubernetes/img/tray-icon-on-macos.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-icon-on-windows-10.png b/website/docs/kubernetes/img/tray-icon-on-windows-10.png deleted file mode 100644 index 42a49cc1fc..0000000000 Binary files a/website/docs/kubernetes/img/tray-icon-on-windows-10.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-kubernetes-on-linux.png b/website/docs/kubernetes/img/tray-kubernetes-on-linux.png deleted file mode 100644 index 16c891049a..0000000000 Binary files a/website/docs/kubernetes/img/tray-kubernetes-on-linux.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-kubernetes-on-macos.png b/website/docs/kubernetes/img/tray-kubernetes-on-macos.png deleted file mode 100644 index 03b701517f..0000000000 Binary files a/website/docs/kubernetes/img/tray-kubernetes-on-macos.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-kubernetes-on-windows-10.png b/website/docs/kubernetes/img/tray-kubernetes-on-windows-10.png deleted file mode 100644 index 65b10c821d..0000000000 Binary files a/website/docs/kubernetes/img/tray-kubernetes-on-windows-10.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-main-menu-on-linux.png b/website/docs/kubernetes/img/tray-main-menu-on-linux.png deleted file mode 100644 index 70f2dfdc37..0000000000 Binary files a/website/docs/kubernetes/img/tray-main-menu-on-linux.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-main-menu-on-macos.png b/website/docs/kubernetes/img/tray-main-menu-on-macos.png deleted file mode 100644 index dc675f67a8..0000000000 Binary files a/website/docs/kubernetes/img/tray-main-menu-on-macos.png and /dev/null differ diff --git a/website/docs/kubernetes/img/tray-main-menu-on-windows-10.png b/website/docs/kubernetes/img/tray-main-menu-on-windows-10.png deleted file mode 100644 index 5d7110b544..0000000000 Binary files a/website/docs/kubernetes/img/tray-main-menu-on-windows-10.png and /dev/null differ diff --git a/website/docs/kubernetes/img/verifying-the-port-forwarding-details.png b/website/docs/kubernetes/img/verifying-the-port-forwarding-details.png deleted file mode 100644 index da4b323249..0000000000 Binary files a/website/docs/kubernetes/img/verifying-the-port-forwarding-details.png and /dev/null differ diff --git a/website/docs/kubernetes/index.md b/website/docs/kubernetes/index.md deleted file mode 100644 index 93bc3b4478..0000000000 --- a/website/docs/kubernetes/index.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -sidebar_position: 60 -title: Kubernetes -description: Migrate transparently from Podman to Kubernetes, and continue using familiar workflows. -keywords: [podman desktop, podman, containers, migrating, kubernetes] -tags: [migrating-to-kubernetes] ---- - -# From containers to Kubernetes - -Podman Desktop and Podman have many features allowing easy migration from containers to Kubernetes. - -#### Procedure - -- Setup at least one Kubernetes context: - - [Red Hat Developer Sandbox](/docs/openshift/developer-sandbox) - - [Existing Kubernetes](/docs/kubernetes/existing-kubernetes) - - [Kind](/docs/kind) - - [Lima](/docs/lima) - - [Minikube](/docs/minikube) - - [Red Hat OpenShift Local](/docs/openshift/openshift-local) - -#### Next steps - -1. [Migrate containers to Kubernetes](/docs/kubernetes). -2. [Select your Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). -3. [Deploy a pod or container](/docs/kubernetes/deploying-a-pod-to-kubernetes). diff --git a/website/docs/kubernetes/managing-a-kube-context.md b/website/docs/kubernetes/managing-a-kube-context.md deleted file mode 100644 index c61734e676..0000000000 --- a/website/docs/kubernetes/managing-a-kube-context.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -sidebar_position: 3 -title: Managing a context -description: Covers the procedure to manage a Kubernetes context -keywords: [podman desktop, podman, manage kubernetes contexts] -tags: [editing-a-kubernetes-context, managing-a-context, duplicate-a-context] ---- - -# Managing a Kubernetes context - -Within Kubernetes, a context is useful to: - -- Manage multiple development, testing, and production environments. -- Simplify your interaction when working with multiple clusters, users, and namespaces. - -You can edit or duplicate a context using the UI. This helps in defining contexts with different configurations within your Kubernetes configuration file. Having contexts with different configurations enables easy switching between environments during development. - -#### Prerequisites - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- [A Kubernetes cluster](/docs/kubernetes/creating-a-kube-cluster). - -#### Procedure: Edit a context - -1. Go to the **Settings > Kubernetes** page. -1. Click the **Edit Context** icon. The Edit Context popup window opens. - ![edit context icon](img/edit-context-icon.png) -1. Edit any of the following context details: - - Name - - Cluster - - User - - Namespace - ![edit context window](img/edit-context-window.png) -1. Click **Save**. - -#### Procedure: Duplicate a context - -1. Go to the **Settings > Kubernetes** page. -1. Click the **Duplicate Context** icon. - -#### Verification - -- _Edit a context_: View the updated context details on the same page. - ![updated context details](img/context-details.png) -- _Duplicate a context_: View the duplicated context on the same page. - -#### Additional resources - -- [Viewing and selecting the current Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context) diff --git a/website/docs/kubernetes/port-forwarding.md b/website/docs/kubernetes/port-forwarding.md deleted file mode 100644 index 0120448d68..0000000000 --- a/website/docs/kubernetes/port-forwarding.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -sidebar_position: 13 -title: Configuring port forwarding -description: Configuring port forwarding for a Kubernetes pod or service -keywords: [podman desktop, podman, port forwarding, objects, kubernetes] -tags: [Configuring-port-forwarding, port-forwarding] ---- - -# Configuring port forwarding - -Within a Kubernetes cluster, you can access an application by its internal IP address. But, if you want to access the application from your local machine, use the port forwarding feature. Using UI, you can forward a local port to a port on the pod that runs your application. This way you can interact with the application running in a Kubernetes cluster from your local machine for debugging and testing purposes. - -You can use the port forwarding feature for the pods and services running on a Kubernetes cluster. Also, you can perform port forwarding for any exposed ports. - -#### Prerequisites - -Make sure you have: - -- A [running Podman machine](/docs/podman/creating-a-podman-machine). -- A running Kubernetes cluster, such as [Kind](/docs/kind/creating-a-kind-cluster) or [Minikube](/docs/minikube/creating-a-minikube-cluster). -- Connected to the cluster. - - Go to **Settings > Kubernetes**, and click **Connect** in the cluster tile. -- Created a YAML configuration file with an exposed port: - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: static-web -spec: - containers: - - image: nginx - name: web - ports: - - containerPort: 80 - name: web - protocol: TCP -``` - -#### Procedure: Start port forwarding - -1. Create a Kubernetes pod: - 1. Go to the **Pods** component page. - 1. Click **Play Kubernetes YAML**. - ![play kubernetes yaml](img/play-kubernetes-yaml.png) - 1. Select a Kubernetes YAML file, such as `pod.yaml` from your local machine. - 1. Set the **Runtime** field to `Kubernetes cluster`. - ![set runtime field](img/play-a-yaml-file.png) - 1. Click **Play** and then **Done**. - 1. Go to **Kubernetes > Pods** to view the created Kubernetes pod. - ![view the newly created pod](img/new-kubernetes-pod.png) - 1. Click the name of the pod and view the **Summary** tab. - -1. Click the **Forward...** button corresponding to the port you want to use for port forwarding. - ![forward button](img/forward-button.png) -1. Click the **Open** button to view the running application in a web browser. - ![open button](img/open-button.png) - - :::note - - You can also start port forwarding from the **Kubernetes > Services** component page. After creating a service, click the name of the service and configure port forwarding in the **Summary** tab. - - ::: - -#### Procedure: Stop port forwarding - -You can stop port forwarding by using one of the following ways: - -**_Use the Pods or Services page_** - -1. Go to the **Kubernetes > Pods** or **Kubernetes > Services** page. -1. Click the name of the Kubernetes pod or service for which you want to stop port forwarding. -1. Click **Remove** in the Summary tab. The entry is removed from the **Kubernetes > Port Forwarding** page. - ![using component page](img/stop-port-forwarding-pod.png) - -**_Use the Port Forwarding page_** - -1. Go to the **Kubernetes > Port Forwarding** page. -1. Click the **Delete** icon corresponding to the pod or service for which you want to stop port forwarding. The entry is removed from the page. - ![using port forwarding page](img/delete-icon-on-port-forwarding-page.png) - -#### Verification - -1. Go to the **Kubernetes > Port Forwarding** page. -1. View the port forwarding details for Kubernetes pods and services. - ![verifying port forwarding details](img/verifying-the-port-forwarding-details.png) diff --git a/website/docs/kubernetes/pushing-an-image.md b/website/docs/kubernetes/pushing-an-image.md deleted file mode 100644 index f2203173e0..0000000000 --- a/website/docs/kubernetes/pushing-an-image.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -sidebar_position: 14 -title: Pushing an image -description: Pushing an image to a cluster -keywords: [podman desktop, podman, pushing an image, kubernetes] -tags: [pushing-an-image, Lima, Minikube, Kind] ---- - -# Pushing an image to a cluster - -You can push an image to a Kubernetes cluster, such as Kind, MiniKube, or Lima. After building the image with your container engine, you can test it in your Kubernetes cluster and verify the created pod. - -See the procedural details in the following sections: - -- [Pushing an image to Kind](/docs/kind/pushing-an-image-to-kind) -- [Pushing an image to Minikube](/docs/minikube/pushing-an-image-to-minikube) -- [Pushing an image to Lima](/docs/lima/pushing-an-image-to-lima) - -#### Additional resources - -- [Building an image and testing it in Kind](/docs/kind/building-an-image-and-testing-it-in-kind) -- [Building an image and testing it in Minikube](/docs/minikube/building-an-image-and-testing-it-in-minikube) diff --git a/website/docs/kubernetes/viewing-and-selecting-current-kubernetes-context.md b/website/docs/kubernetes/viewing-and-selecting-current-kubernetes-context.md deleted file mode 100644 index 17d7d6d363..0000000000 --- a/website/docs/kubernetes/viewing-and-selecting-current-kubernetes-context.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -sidebar_position: 2 -title: Selecting a context -description: Viewing and selecting the current Kubernetes context -keywords: [podman desktop, podman, containers, pods, context, kubernetes] -tags: [selecting-context, kubernetes] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Viewing and selecting the current Kubernetes context - -With Podman Desktop, you can view and select your current Kubernetes context to access: - -- The Kubernetes cluster you want to interact with -- The namespace where you define your application resources - -Based on your configurations, Podman Desktop automatically populates the context list. For example, if you have created a Minikube, Kind, or Developer Sandbox cluster, a respective Kubernetes context is automatically added in the Podman Desktop application. Then, you can easily switch from one context to another if needed. - -:::note - -When you select a Kubernetes context, you can access all the namespaces associated with it. You can switch from one namespace to another using the Kubernetes dashboard page or the Kubernetes object page. - -::: - -#### Prerequisites - -- You have a Kubernetes context in your kubeconfig file: _<your_kubernetes_cluster>_. - For example, [Creating a kind cluster](/docs/kind/creating-a-kind-cluster). - -#### Procedure: Using the status bar - -1. To view your current Kubernetes context, in the **Podman Desktop** main window status bar, see the name next to the **Current Kubernetes context** icon. - -2. Optional: To change your Kubernetes context: - 1. Click the context in the status bar. - 2. Select a Kubernetes context to activate from the dropdown list. - ![selecting a context from dropdown](img/selecting-context-from-dropdown.png) - -#### Procedure: Using the Podman Desktop Settings - -1. Go to **Settings > Kubernetes** to view the configured Kubernetes contexts. -1. Optional: Click the **Set as Current Context** icon to change your context. - ![selecting a context](img/selecting-context.png) - -#### Procedure: Using the tray menu - -1. Open the **Podman Desktop tray** menu. - - - - - In the task bar, click **Show hidden icons**. - - ![Podman Desktop tray](img/tray-icon-on-windows-10.png) - - Right-click the **Podman Desktop tray** icon to open the menu. - - ![Podman Desktop tray](img/tray-main-menu-on-windows-10.png) - - - - - ![Podman Desktop tray](img/tray-main-menu-on-macos.png) - - - - - ![Podman Desktop tray](img/tray-main-menu-on-linux.png) - - - - - -1. Click **Kubernetes** to see your current Kubernetes context. - - - - - ![Podman Desktop tray](img/tray-kubernetes-on-windows-10.png) - - - - - ![Podman Desktop tray](img/tray-kubernetes-on-macos.png) - - - - - ![Podman Desktop tray](img/tray-kubernetes-on-linux.png) - - - - - -1. (Optionally) To change your Kubernetes context, click on the context name to activate. diff --git a/website/docs/lima/creating-a-kubernetes-instance.md b/website/docs/lima/creating-a-kubernetes-instance.md deleted file mode 100644 index c5107ccae6..0000000000 --- a/website/docs/lima/creating-a-kubernetes-instance.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -sidebar_position: 3 -title: Lima instance for Kubernetes -description: Podman Desktop can assist you to create a custom Lima instance on Linux and macOS. -tags: [podman-desktop, kubernetes, onboarding, linux, macOS] -keywords: [podman desktop, kubernetes, lima, onboarding, linux, macos] ---- - -# Creating a Lima instance for Kubernetes workloads with Podman Desktop - -To use the Lima provider you need a Lima instance running a Linux machine. - -In the future, Podman Desktop might be able to create Lima instances. - -Consider creating a custom Lima instance to: - -- Control the assigned resources: CPUs, memory, and disk size. -- Control which Kubernetes distribution (`template`) to install. - -#### Prerequisites - -- [Install the `lima` CLI](/docs/lima/installing). - -#### Procedure - -1. In a terminal, create the Lima instance. - - To create a single-node Kubernetes cluster running [k3s](https://k3s.io/): - - ```shell-session - $ limactl start template://k3s - ``` - - - To create a single-node Kubernetes cluster running [k8s](https://k8s.io/): - - ```shell-session - $ limactl start template://k8s - ``` - - - To select the number of CPUs, the memory, and the disk size, add the options to the `limactl start` command: - - ```shell-session - --cpus=4 --memory=4 --disk=100 - ``` - -2. Wait for the instance to start, and restart the Lima extension. - - Go to ** Settings > Preferences > Extension: Lima** to change the instance name and type. - - k3s - - Type: kubernetes - - Name: k3s - ![Lima preferences k3s](img/lima-preferences-k3s.png) - - - k8s - - Type: kubernetes - - Name: k8s - ![Lima preferences k8s](img/lima-preferences-k8s.png) - - - Go to ** Settings > Extensions > Lima** to disable or enable the extension after changes. - -#### Verification - -1. When the installation is done, the location of the KUBECONFIG file is printed. See [Configuring access to a Kubernetes cluster](/docs/kubernetes/existing-kubernetes). - - Go to ** Settings > Preferences > Kubernetes** to set the path of the file. - -1. Use the `kubectl.lima` wrapper script to connect to the cluster: - - ```shell-session - $ kubectl.lima version - ``` diff --git a/website/docs/lima/creating-a-lima-instance.md b/website/docs/lima/creating-a-lima-instance.md deleted file mode 100644 index a1c5c9b959..0000000000 --- a/website/docs/lima/creating-a-lima-instance.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -sidebar_position: 2 -title: Lima instance for containers -description: Podman Desktop can assist you to create a custom Lima instance on Linux and macOS. -tags: [podman-desktop, podman, docker, containers, onboarding, linux, macOS] -keywords: [podman desktop, containers, lima, onboarding, linux, macos] ---- - -# Creating a Lima instance for container workloads with Podman Desktop - -To use the Lima provider you need a Lima instance running a Linux machine. - -In the future, Podman Desktop might be able to create Lima instances. - -Consider creating a custom Lima instance to: - -- Control the assigned resources: CPUs, memory, and disk size. -- Use the rootful connection by default, for example to run Kind. - -#### Prerequisites - -- [Install the `lima` CLI](/docs/lima/installing). - -#### Procedure - -1. In a terminal, create the Lima instance. - - To create a Lima instance with rootless Podman, use the `podman` template: - - ```shell-session - $ limactl start --name=podman template://podman - ``` - - - To create a Lima instance with rootful Podman, use the `podman-rootful` template: - - ```shell-session - $ limactl start --name=podman template://podman-rootful - ``` - - - To create an Lima instance with rootless Docker, use the `docker` template: - - ```shell-session - $ limactl start --name=docker template://docker - ``` - - - To create an Lima instance with rootful Docker, use the `docker-rootful` template: - - ```shell-session - $ limactl start --name=docker template://docker-rootful - ``` - - - To select the number of CPUs, the memory, and the disk size, add the options to the `limactl start` command: - - ```shell-session - --cpus=2 --memory=2 --disk=50 - ``` - -2. Wait for the instance to start, and restart the Lima extension. - - Go to ** Settings > Preferences > Extension: Lima** to change the instance name and type. - - Podman (default) - - Type: podman - - Name: podman - ![Lima preferences Podman](img/lima-preferences-podman.png) - - - Docker - - Type: docker - - Name: docker - ![Lima preferences Docker](img/lima-preferences-docker.png) - - - Go to ** Settings > Preferences > Extension: Lima** to configure a custom socket name. - - The default socket name is: - - `podman.sock` for Podman - - - `docker.sock` for Docker - - - To use a custom socket name: - - ![Lima preferences Socket](img/lima-preferences-socket.png) - - - Go to ** Settings > Extensions > Lima** to disable or enable the extension after changes. - -#### Verification - -- To verify the connection to a running "podman" instance: - - ```shell-session - $ podman.lima version - ``` - -- To verify the connection to a running "docker" instance: - - ```shell-session - $ docker.lima version - ``` diff --git a/website/docs/lima/customizing.md b/website/docs/lima/customizing.md deleted file mode 100644 index 124cd3478e..0000000000 --- a/website/docs/lima/customizing.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -sidebar_position: 4 -title: Customizing Lima instance -description: Customizing Lima -keywords: [podman desktop, containers, kubernetes, lima] -tags: [lima] ---- - -# Customizing the Lima instance for varying workloads - -You can customize Lima instance, using YAML and `yq`. - -For more information on yq, see the yq [documentation](https://mikefarah.gitbook.io/yq/). - -#### Procedure - -- To create a new instance: - - ```shell-session - $ limactl create - ``` - -- To edit an existing instance: - - ```shell-session - $ limactl edit - ``` - -Some of the things you can edit: - -- Change the number of CPUs, the memory, and the disk size -- Change the operating system (the Linux distribution) -- Modify the cluster setup (the Kubernetes distribution) -- Run **both** of container workloads and Kubernetes workloads - -See also: - -- [Lima documentation](https://lima-vm.io/docs/) -- [Lima GUI](https://github.com/afbjorklund/lima-gui) (with editor) - -### Directory - -To find the location of the instance directory (`Dir`): - -```bash -limactl list --format '{{.Dir}}' -``` - -### Resources - -The resources can be set per instance, or as a global default. - -```yaml -# CPUs -# 🟢 Builtin default: min(4, host CPU cores) -cpus: null - -# Memory size -# 🟢 Builtin default: min("4GiB", half of host memory) -memory: null - -# Disk size -# 🟢 Builtin default: "100GiB" -disk: null -``` - -To set the default, edit `_config/default.yaml` in Lima home. - -```yaml -# The builtin defaults can be changed globally by creating a $LIMA_HOME/_config/default.yaml -# file. It will be used by ALL instances under the same $LIMA_HOME, and it -# will be applied on each `limactl start`, so can affect instance restarts. - -# A similar mechanism is $LIMA_HOME/_config/override.yaml, which will take -# precedence even over the settings in an instances lima.yaml file. -# It too applies to ALL instances under the same $LIMA_HOME, and is applied -# on each restart. It can be used to globally override settings, e.g. make -# the mount of the home directory writable. -``` - -### VM and Mount - -Any virtual machine (or server) with ssh can be used for Lima. - -Most compatible mount type is "reverse-sshfs" (from [sshocker](https://github.com/lima-vm/sshocker)). - -Optionally you can use "qemu" vm with "9p" (aka virtfs) mount. - -On macOS 13+, you can also use "vz" vm with "virtiofs" mount. - -```yaml -# VM type: "qemu" or "vz" (on macOS 13 and later). -# The vmType can be specified only on creating the instance. -# The vmType of existing instances cannot be changed. -# 🟢 Builtin default: "qemu" -vmType: null - -# Mount type for above mounts, such as "reverse-sshfs" (from sshocker), -# "9p" (EXPERIMENTAL, from QEMU’s virtio-9p-pci, aka virtfs), -# or "virtiofs" (EXPERIMENTAL, needs `vmType: vz`) -# 🟢 Builtin default: "reverse-sshfs" (for QEMU), "virtiofs" (for vz) -mountType: null -``` - -### Containers - -You can install a container engine, in addition to the existing runtime. - -For instance you can install [Podman Engine](https://github.com/containers/podman), -or you can install [Docker Engine](https://github.com/docker/docker). -After that you can port forward the socket, to the host `Dir`. - -#### Docker - -```yaml -portForwards: - - guestSocket: '/var/run/docker.sock' - hostSocket: '{{.Dir}}/sock/docker.sock' -``` - -- `/var/run/docker.sock` - -```bash -export DOCKER_HOST="unix://{{.Dir}}/sock/docker.sock" -``` - -#### Podman - -```yaml -portForwards: - - guestSocket: '/run/podman/podman.sock' - hostSocket: '{{.Dir}}/sock/podman.sock' -``` - -- `/run/podman/podman.sock` - -```bash -export CONTAINER_HOST="unix://{{.Dir}}/sock/podman.sock" -``` - -### Kubernetes - -You can install Kubernetes, on top of the existing container engine. - -For instance you can use [CRI-O](https://github.com/cri-o/cri-o) for Podman, -or [CRI-Dockerd](https://github.com/Mirantis/cri-dockerd) for Docker. -After that you can copy the `kubeconfig.yaml` file, to the host `Dir`. - -#### k3s.io - -```yaml -copyToHost: - - guest: '/etc/rancher/k3s/k3s.yaml' - host: '{{.Dir}}/copied-from-guest/kubeconfig.yaml' - deleteOnStop: true -``` - -- `/etc/rancher/k3s/k3s.yaml` - -```bash -export KUBECONFIG="{{.Dir}}/copied-from-guest/kubeconfig.yaml" -``` - -#### k8s.io - -```yaml -copyToHost: - - guest: '/etc/kubernetes/admin.conf' - host: '{{.Dir}}/copied-from-guest/kubeconfig.yaml' - deleteOnStop: true -``` - -- `/etc/kubernetes/admin.conf` - -```bash -export KUBECONFIG="{{.Dir}}/copied-from-guest/kubeconfig.yaml" -``` diff --git a/website/docs/lima/img/create-pod-from-kube-yaml.png b/website/docs/lima/img/create-pod-from-kube-yaml.png deleted file mode 100644 index 5bf15251d5..0000000000 Binary files a/website/docs/lima/img/create-pod-from-kube-yaml.png and /dev/null differ diff --git a/website/docs/lima/img/lima-preferences-docker.png b/website/docs/lima/img/lima-preferences-docker.png deleted file mode 100644 index 6ce9d06b2b..0000000000 Binary files a/website/docs/lima/img/lima-preferences-docker.png and /dev/null differ diff --git a/website/docs/lima/img/lima-preferences-k3s.png b/website/docs/lima/img/lima-preferences-k3s.png deleted file mode 100644 index 12da42e1ca..0000000000 Binary files a/website/docs/lima/img/lima-preferences-k3s.png and /dev/null differ diff --git a/website/docs/lima/img/lima-preferences-k8s.png b/website/docs/lima/img/lima-preferences-k8s.png deleted file mode 100644 index 330d4e15a2..0000000000 Binary files a/website/docs/lima/img/lima-preferences-k8s.png and /dev/null differ diff --git a/website/docs/lima/img/lima-preferences-podman.png b/website/docs/lima/img/lima-preferences-podman.png deleted file mode 100644 index 48895f14e2..0000000000 Binary files a/website/docs/lima/img/lima-preferences-podman.png and /dev/null differ diff --git a/website/docs/lima/img/lima-preferences-socket.png b/website/docs/lima/img/lima-preferences-socket.png deleted file mode 100644 index 5d737c037a..0000000000 Binary files a/website/docs/lima/img/lima-preferences-socket.png and /dev/null differ diff --git a/website/docs/lima/img/push-image-to-lima.png b/website/docs/lima/img/push-image-to-lima.png deleted file mode 100644 index 92fe6154ee..0000000000 Binary files a/website/docs/lima/img/push-image-to-lima.png and /dev/null differ diff --git a/website/docs/lima/img/verify-my-image-pod-running.png b/website/docs/lima/img/verify-my-image-pod-running.png deleted file mode 100644 index 5dbe3a469a..0000000000 Binary files a/website/docs/lima/img/verify-my-image-pod-running.png and /dev/null differ diff --git a/website/docs/lima/index.md b/website/docs/lima/index.md deleted file mode 100644 index 0dbb24b049..0000000000 --- a/website/docs/lima/index.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -sidebar_position: 90 -title: Lima -description: Podman Desktop can assist you to create a custom Lima instance on Linux and macOS. -tags: [podman-desktop, containers, kubernetes, onboarding, linux, macOS] -keywords: [podman desktop, containers, kubernetes, lima, onboarding, linux, macos] ---- - -# Lima - -Lima launches Linux virtual machines with automatic file sharing and port forwarding (similar to WSL2). - -With Podman Desktop, you can work on [Lima-powered](https://lima-vm.io/) custom instances or local Kubernetes clusters. - -#### Next steps - -1. [Installing Lima](/docs/lima/installing). -1. [Create a Lima instance for container workloads](/docs/lima/creating-a-lima-instance). -1. [Create a Lima instance for Kubernetes workloads](/docs/lima/creating-a-kubernetes-instance). diff --git a/website/docs/lima/installing.md b/website/docs/lima/installing.md deleted file mode 100644 index 6cf179fff5..0000000000 --- a/website/docs/lima/installing.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 1 -title: Installing the CLI -description: Installing Lima -keywords: [podman desktop, podman, containers, migrating, kubernetes, lima] -tags: [migrating-to-kubernetes, lima] ---- - -# Installing the `lima` CLI - -#### Procedure - -- The `limactl` executable is installed. - See [Installing Lima](https://lima-vm.io/docs/installation/). - - ```shell-session - $ brew install lima - ``` - -#### Verification - -1. You can run the `limactl` CLI: - - ```shell-session - $ limactl list - ``` - -1. (Optionally) To open a shell: - - ```shell-session - $ # requires a running instance - $ export LIMA_INSTANCE= - $ lima - ``` diff --git a/website/docs/lima/pushing-an-image-to-lima.md b/website/docs/lima/pushing-an-image-to-lima.md deleted file mode 100644 index ef19a915a3..0000000000 --- a/website/docs/lima/pushing-an-image-to-lima.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -sidebar_position: 5 -title: Pushing an image -description: Pushing an image to your Lima cluster -keywords: [podman desktop, podman, containers, images, pushing an image, kubernetes] -tags: [pushing-an-image, lima] ---- - -# Pushing an image to your local Lima-powered Kubernetes cluster - -With Podman Desktop, you can push an image to your local Lima-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a container engine](/docs/containers). -- [You onboarded a Lima cluster](/docs/lima). -- [You have set your Kubernetes context to your local Lima-powered Kubernetes cluster](/docs/kubernetes/existing-kubernetes). -- Your image is available on the **Images** page: `:`. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click the **overflow menu** icon corresponding to the image you want to push and select **Push image to Lima cluster**. A successful operation notification opens. - ![pushing an image to Lima](img/push-image-to-lima.png) -1. Click **OK**. - -#### Verification - -Lima enables you to list loaded images: - -```shell-session -$ LIMA_INSTANCE= lima sudo crictl images -``` - -You can also create a pod that uses the loaded image: - -1. Create a `verify_my_image.yaml` Kubernetes YAML file on your workstation. - Replace the placeholders: - -- Pod `name` and container `name` values must consist of lowercase alphanumeric characters, '-', or '.', and must start and end with an alphanumeric character. -- Container `image` value is the image you pushed. You can click the name of the image to check its name and tag. - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: -spec: - containers: - - name: - image: : - imagePullPolicy: Never -``` - -1. Go to **Pods** from the left navigation pane. -1. Click **Play Kubernetes YAML** and provide the following details: - -- **Kubernetes YAML file**: select your `verify_my_image.yaml` file. -- Set **Runtime** to **Kubernetes cluster**. - -1. Click **Play**. - ![play a Kubernetes YAML](img/create-pod-from-kube-yaml.png) -1. Click **Done**. -1. View the created pod `verify-my-image` on the same page. The pod **STATUS** is **RUNNING**. - ![play a Kubernetes YAML](img/verify-my-image-pod-running.png) diff --git a/website/docs/migrating-from-docker/customizing-docker-compatibility.md b/website/docs/migrating-from-docker/customizing-docker-compatibility.md deleted file mode 100644 index fa9bf69f56..0000000000 --- a/website/docs/migrating-from-docker/customizing-docker-compatibility.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -sidebar_position: 2 -title: Customizing Docker compatibility -description: How to customize the Docker compatibility feature -keywords: [podman desktop, podman, Docker compatibility, Kubernetes] -tags: [enable-docker-compatibility, disable-docker-compatibility] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Customizing Docker compatibility - -With Podman Desktop, you can customize the Docker compatibility feature. If you want to run your Docker applications on a Podman engine, you can enable the feature. - - - - -The binding between the Podman machine and the system socket is not known. So, Podman Desktop displays only the server information on the Docker Compatibility page. - -![system socket details on Windows](img/system-socket-details-windows.png) - - - - -Podman Desktop uses the `podman-mac-helper` utility to automatically link the Docker socket to the Podman machine. This utility provides a compatibility layer that allows you to: - -- Run Docker commands on a Podman engine. -- Make your tools, such as [Maven](https://maven.apache.org/) or [Testcontainers](https://www.testcontainers.com/) communicate with Podman without reconfiguration. - -Podman Desktop provides information about the Podman machine that emulates the Docker socket on the Docker Compatibility page. Click the **Podman details** icon to view the details of the Podman machine. - -![system socket details on macOS](img/system-socket-details-macOS.png) - - - - -The binding between the Podman machine and the system socket is not known. So, Podman Desktop displays only the server information on the Docker Compatibility page. - -![system socket details on Linux](img/system-socket-details-linux.png) - - - - -## Enable Docker compatibility - -#### Prerequisites - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). - - - - -#### Procedure - -- Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - -A Docker Compatibility section is added to the list of **Settings**. - -#### Verification - -Perform any of the following steps: - -- Run the following command to check the output returns the Podman version rather than the Docker version: - - ```shell-session - $ docker info --format=json | jq -r .ServerVersion - ``` - -- Run the `docker context list` command to check that the Docker CLI context is set to the default value `npipe:////./pipe/docker_engine`. - - - - -#### Procedure - -The Docker socket compatibility mode is enabled by default. However, if you have disabled the Docker socket and want to re-enable it, you must enable the **Third-Party Docker Tool Compatibility** setting. - -1. Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - ![enable toggle button - Preferences](img/enable-using-preferences.png) - A Docker Compatibility section is added to the list of **Settings**. -1. Click **Docker Compatibility** in the Settings. The Docker Compatibility page opens. -1. Click **Enable** in the **Third-Party Docker Tool Compatibility** setting. -1. Enter your machine password when prompted. A successful operation notification opens. -1. Click **OK**. A notification prompts you to restart your Podman machine. - ![enable docker compatibility](img/enable-docker-compatibility.png) -1. Click **Yes**. - ![restart Podman machine](img/restart-podman-machine.png) - -#### Verification - -Perform any of the following steps: - -- Check whether the Docker socket is a symbolic link for the Podman socket: - - ```shell-session - $ ls -la /var/run/docker.sock - ``` - - The output points to a `podman.sock` file, as shown below: - - ```shell-session - /var/run/docker.sock -> /Users/username/.local/share/containers/podman/machine/podman.sock - ``` - -- Run the following command to check the output returns the Podman version rather than the Docker version: - - ```shell-session - $ curl -s --unix-socket /var/run/docker.sock "http://v1.41/info" | jq -r .ServerVersion - ``` - -- Run the `docker context list` command to check that the Docker CLI context is set to `unix:///var/run/docker.sock`. - - - - -#### Procedure - -- Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - -A Docker Compatibility section is added to the list of **Settings**. - -#### Verification - -Perform any of the following steps: - -- Run the following command to check the output returns the Podman version rather than the Docker version: - - ```shell-session - $ docker info --format=json | jq -r .ServerVersion - ``` - -- Run the `docker context list` command to check that the Docker CLI context is set to the default value `unix:///var/run/docker.sock`. - - - - -## Disable Docker compatibility - -#### Prerequisites - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). - - - - -#### Procedure - -- Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - - The Docker Compatibility section is removed from the list of **Settings**. - -#### Verification - -- Run the following command to check the output returns the Docker version rather than the Podman version: - - ```shell-session - $ docker info --format=json | jq -r .ServerVersion - ``` - - - - -#### Procedure - -1. Click **Docker Compatibility** in the Settings. The Docker Compatibility page opens. -1. Click **Disable** in the **Third-Party Docker Tool Compatibility** setting. -1. Enter your machine password when prompted. A successful operation notification opens. -1. Click **OK**. A notification prompts you to restart your Podman machine. -1. Click **Yes**. -1. Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - The Docker Compatibility section is removed from the **Settings** list. - -#### Verification - -Perform any of the following steps: - -- Check that the Docker socket is not a symbolic link for the Podman socket: - - ```shell-session - $ ls -la /var/run/docker.sock - ``` - - The output returns the following: - - ```shell-session - ls: /var/run/docker.sock: No such file or directory - ``` - -- Run the following command to check the output returns the Docker version rather than the Podman version: - - ```shell-session - $ curl -s --unix-socket /var/run/docker.sock "http://v1.41/info" | jq -r .ServerVersion - ``` - - - - -#### Procedure - -- Go to **Settings > Preferences > Docker Compatibility**, and click the toggle button. - - The Docker Compatibility section is removed from the list of **Settings**. - -#### Verification - -- Run the following command to check the output returns the Docker version rather than the Podman version: - - ```shell-session - $ docker info --format=json | jq -r .ServerVersion - ``` - - - - -#### Additional resources - -- [Managing Docker compatibility](/docs/migrating-from-docker/managing-docker-compatibility) -- [`podman-mac-helper` source](https://github.com/containers/podman/tree/main/cmd/podman-mac-helper) diff --git a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-linux.png b/website/docs/migrating-from-docker/img/docker-compatibility-page-on-linux.png deleted file mode 100644 index b47eb20b19..0000000000 Binary files a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-linux.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-macOS.png b/website/docs/migrating-from-docker/img/docker-compatibility-page-on-macOS.png deleted file mode 100644 index 57ac966f63..0000000000 Binary files a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-macOS.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-windows.png b/website/docs/migrating-from-docker/img/docker-compatibility-page-on-windows.png deleted file mode 100644 index 5d8e5847ed..0000000000 Binary files a/website/docs/migrating-from-docker/img/docker-compatibility-page-on-windows.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/enable-docker-compatibility.png b/website/docs/migrating-from-docker/img/enable-docker-compatibility.png deleted file mode 100644 index 99f8a9b1d0..0000000000 Binary files a/website/docs/migrating-from-docker/img/enable-docker-compatibility.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/enable-using-preferences.png b/website/docs/migrating-from-docker/img/enable-using-preferences.png deleted file mode 100644 index ecbe1c160e..0000000000 Binary files a/website/docs/migrating-from-docker/img/enable-using-preferences.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/restart-podman-machine.png b/website/docs/migrating-from-docker/img/restart-podman-machine.png deleted file mode 100644 index d914fdbe4e..0000000000 Binary files a/website/docs/migrating-from-docker/img/restart-podman-machine.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/system-socket-details-linux.png b/website/docs/migrating-from-docker/img/system-socket-details-linux.png deleted file mode 100644 index 40fdf9b52b..0000000000 Binary files a/website/docs/migrating-from-docker/img/system-socket-details-linux.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/system-socket-details-macOS.png b/website/docs/migrating-from-docker/img/system-socket-details-macOS.png deleted file mode 100644 index 4e16af9e1c..0000000000 Binary files a/website/docs/migrating-from-docker/img/system-socket-details-macOS.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/img/system-socket-details-windows.png b/website/docs/migrating-from-docker/img/system-socket-details-windows.png deleted file mode 100644 index 6b42e2e539..0000000000 Binary files a/website/docs/migrating-from-docker/img/system-socket-details-windows.png and /dev/null differ diff --git a/website/docs/migrating-from-docker/importing-saved-containers.md b/website/docs/migrating-from-docker/importing-saved-containers.md deleted file mode 100644 index 6d93b6a5a7..0000000000 --- a/website/docs/migrating-from-docker/importing-saved-containers.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -sidebar_position: 1 -title: Importing saved containers -description: Import saved containers to migrate transparently to Podman, and continue using familiar containers. -keywords: [podman desktop, podman, containers, importing] -tags: [migrating-from-docker] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Importing saved containers to Podman - -Consider importing saved containers to continue using familiar containers. - -#### Prerequisites - -- Podman - -- You saved your existing containers by running the command: - - - - - ```shell-session - $ podman save > .tar - ``` - - - - - ```shell-session - $ docker export -o .tar - ``` - - - - -#### Procedure - -- Import your existing containers into Podman. - Run the command for each container archive: - - ```shell-session - $ podman import .tar - ``` - -#### Verification - -- Your imported containers appear in the Podman Desktop _Images_ section. - -#### Additional resources - -- [`docker save` reference documentation](https://docs.docker.com/engine/reference/commandline/save/) -- [`podman save` reference documentation](https://docs.podman.io/en/latest/markdown/podman-save.1.html) -- [`podman import` reference documentation](https://docs.podman.io/en/latest/markdown/podman-import.1.html) diff --git a/website/docs/migrating-from-docker/index.md b/website/docs/migrating-from-docker/index.md deleted file mode 100644 index 208cacef98..0000000000 --- a/website/docs/migrating-from-docker/index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -sidebar_position: 25 -title: Migrating from Docker -description: Migrate transparently from Docker to Podman, and continue using familiar workflows. -keywords: [podman desktop, podman, containers, migrating, docker] -tags: [migrating-from-docker] ---- - -# Migration from Docker to Podman Desktop - -If you have used Docker in the past, you can continue using familiar workflows with the Docker compatibility feature of Podman Desktop. - -Docker compatibility is a way to configure an environment in which you can run your Docker applications, commands, and tools on a Podman engine without reconfiguration. It encompasses these stages: - -- [Import your saved containers](/docs/migrating-from-docker/importing-saved-containers) into Podman Desktop using CLI. -- Enable the [Docker Compatibility](/docs/migrating-from-docker/customizing-docker-compatibility#enable-docker-compatibility) feature. -- [Access the Docker Compatibility settings](/docs/migrating-from-docker/managing-docker-compatibility) to configure a Docker-compatible environment. diff --git a/website/docs/migrating-from-docker/managing-docker-compatibility.md b/website/docs/migrating-from-docker/managing-docker-compatibility.md deleted file mode 100644 index 2d150da925..0000000000 --- a/website/docs/migrating-from-docker/managing-docker-compatibility.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -sidebar_position: 5 -title: Managing Docker compatibility -description: Covers configurations available to manage docker compatibility -keywords: [podman desktop, podman, docker, compatibility] -tags: [managing-docker-compatibility] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Managing Docker compatibility - -With Podman Desktop, you can configure a Docker-compatible environment using the **Settings** page. After configuration, your Docker tools are directed to use a Podman engine and your Compose applications can be run using the Podman CLI. - -As a developer, you can: - -- Check the system socket mapping status to know whether the socket is reachable. The default socket path is: - - _/var/run/docker.sock_ on macOS and Linux - - _npipe:////./pipe/docker_engine_ on Windows - -- Use all Docker tools, including CLI, with the Podman engine without any reconfiguration. These tools connect to the default Podman socket, thereby enabling you to use all Docker commands with Podman. For example, you can run the `docker run` command on the Podman engine to start a container. - - On macOS, the **Third-Party Docker Tool Compatibility** setting is enabled by default. - - On Windows and Linux, the **Third-Party Docker Tool Compatibility** setting is not available. You can [use the `DOCKER_HOST` environment variable](/docs/migrating-from-docker/using-the-docker_host-environment-variable) to let your tools communicate directly with the Podman socket. - -- Use the Podman CLI to run Compose applications by installing and setting up the [Compose extension](/docs/compose). For example, you can run the `docker compose up` command on the Podman engine to start your Compose v2 application. Also, ensure to place your Docker Compose file in a working directory, such as your home directory. - - If the Compose CLI is not installed, you get the install option in the settings. - -- Select and use a Docker-compatible socket context. You can also view the socket details, such as name and socket path. - -#### Prerequisites - -Make sure you have: - -- [A running Podman machine](/docs/podman/creating-a-podman-machine). -- Enabled the [Docker compatibility](/docs/migrating-from-docker/customizing-docker-compatibility#enable-docker-compatibility) feature. - - - - -#### Procedure - -1. Go to **Settings > Docker Compatibility**. -2. **System socket status** setting: View the socket mapping status to check whether the socket is reachable. -3. **Docker CLI Context** setting: Select a socket context to work with from the dropdown list. -4. **Podman Compose CLI Support** setting: Check whether the Podman Compose CLI is supported. If not, use the **Setup...** button to install and set up the CLI. - ![Docker compatibility page on Windows](img/docker-compatibility-page-on-windows.png) - - - - -#### Procedure - -1. Go to **Settings > Docker Compatibility**. -2. **System socket status** setting: View the socket mapping status to check whether the socket is reachable. -3. **Docker CLI Context** setting: Select a socket context to work with from the dropdown list. -4. **Podman Compose CLI Support** setting: Check whether the Podman Compose CLI is supported. If not, use the **Setup...** button to install and set up the CLI. -5. **Third-Party Docker Tool Compatibility** setting: Customize the setting, if needed. When enabled, you can use third-party Docker tools with Podman. - ![Docker compatibility page on macOS](img/docker-compatibility-page-on-macOS.png) - - - - -#### Procedure - -1. Go to **Settings > Docker Compatibility**. -2. **System socket status** setting: View the socket mapping status to check whether the socket is reachable. -3. **Docker CLI Context** setting: Select a socket context to work with from the dropdown list. -4. **Podman Compose CLI Support** setting: Check whether the Podman Compose CLI is supported. If not, use the **Setup...** button to install and set up the CLI. - ![Docker compatibility page on Linux](img/docker-compatibility-page-on-linux.png) - - - - -#### Verification - -- Run `podman` or `podman compose` commands for your Compose applications to check if they run fine. diff --git a/website/docs/migrating-from-docker/using-the-docker_host-environment-variable.md b/website/docs/migrating-from-docker/using-the-docker_host-environment-variable.md deleted file mode 100644 index 2c9f54feec..0000000000 --- a/website/docs/migrating-from-docker/using-the-docker_host-environment-variable.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -sidebar_position: 6 -title: Using the `DOCKER_HOST` environment variable -description: Using the `DOCKER_HOST` environment variable can make it easier to migrate from Docker to Podman Desktop, as it allows you to continue using familiar Docker commands while taking advantage of the benefits of Podman. -keywords: [podman desktop, podman, containers, docker_host, environment, variable] -tags: [migrating-from-docker] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Using the `DOCKER_HOST` environment variable - -Consider using the `DOCKER_HOST` environment variable to migrate transparently from Docker to Podman Desktop on all platforms. - -- Continue using familiar Docker commands. -- Take advantage of the benefits of Podman. -- Your tools, such as [Gradle](https://gradle.org/) or [Testcontainers](https://www.testcontainers.com/), communicate with Podman without reconfiguration. - -Using the `DOCKER_HOST` environment variable, you can connect your Docker CLI to the Podman socket. - -:::note - -Alternatively, you can add a `podman` context by using the `docker context create` command. - -- For example, set the value of the context in this pattern on a macOS machine: - - `docker context create podman --docker "host=unix://${HOME}/.local/share/containers/podman/machine/podman.sock"` - - Where, the path specified after the `unix://` scheme denotes the `DOCKER_HOST` value. - -Then, you can run the `docker context use podman` command to switch to that context. This way you can use your Docker CLI to run your tasks on a remote Podman engine. When you run the `docker ps` command, it queries the Podman socket specified in the current context. - -::: - -#### Prerequisites - -- Podman - -#### Procedure - - - - -1. Identify the location of your Podman pipe - -```shell-session -$ podman machine inspect --format '{{.ConnectionInfo.PodmanPipe.Path}}' -``` - -2. Set the `DOCKER_HOST` environment variable to your Podman pipe location. You'll need to replace back slashes with forward slashes and add the `npipe://` scheme to the path retrieved previously: - - > Example: - > - > **prefix**podman-pipe - > - > **npipe://**//./pipe/podman-machine-default - - Depending on your terminal emulator of preference, there is a little variation between the commands to set a session level environment variable: - - ##### cmd - Command Prompt - - ```cmd - set DOCKER_HOST=npipe:// - ``` - - ##### Git Bash - - ```bash - export DOCKER_HOST=npipe:// - ``` - - ##### Powershell - - Don't miss the quotes used with the value. Otherwise, powershell will interpret it as a separate command instead of a value. - - ```powershell - $env:DOCKER_HOST="npipe://" - ``` - - Ideally, you should set `DOCKER_HOST` at the system or user level environment variables (or even load it in your CL emulator init script of choice). - -:::note - -Setting the `DOCKER_HOST` environment variable isn't necessary on Windows because Podman also listens to the default `docker_engine` pipe. But, it might be necessary if you get the following error: **Error: socket of machine is not set**, while trying to use the `podman compose` command. - -::: - - - - -1. Identify the location of your Podman socket. - -```shell-session -$ podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}' -``` - -2. Set the `DOCKER_HOST` environment variable to your Podman socket location. Be sure to add the `unix://` scheme to the path retrieved previously: - -```shell-session -$ export DOCKER_HOST=unix:// -``` - - - - -1. Identify the location of your Podman socket. - -```shell-session -$ podman info --format '{{.Host.RemoteSocket.Path}}' -``` - -2. Set the `DOCKER_HOST` environment variable to your Podman socket location. Be sure to add the `unix://` scheme to the path retrieved previously: - -```shell-session -$ export DOCKER_HOST=unix:// -``` - - - - - -#### Verification - -- Your tools using the `DOCKER_HOST` environment variable, such as [Gradle](https://gradle.org/) or [Testcontainers](https://www.testcontainers.com/), communicate with Podman without reconfiguration. diff --git a/website/docs/minikube/building-an-image-and-testing-it-in-minikube.md b/website/docs/minikube/building-an-image-and-testing-it-in-minikube.md deleted file mode 100644 index 5be8f0c29d..0000000000 --- a/website/docs/minikube/building-an-image-and-testing-it-in-minikube.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -sidebar_position: 10 -title: Building and testing an image -description: Building an image and testing it in Minikube -keywords: [podman desktop, podman, containers, pods, building an image, kubernetes, minikube] -tags: [building-an-image, testing-an-image-on-minikube] ---- - -# Building an image and testing it in Minikube - -With Podman Desktop, you can build an image with your container engine, and test it in your local Minikube-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a container engine](/docs/containers). -- [You onboarded a Minikube cluster](/docs/minikube). -- [You have set your Kubernetes context to your Minikube cluster](/docs/minikube/working-with-your-local-minikube-cluster). -- A container definition file: `Containerfile` or `Dockerfile`. - -#### Procedure - -1. Build your image: - 1. Go to **Images** from the left navigation pane. - 1. Click **Build**. - 1. Provide the relevant details, such as **Containerfile path**, **Image name**, and **Build arguments** to build the image. For example, use the image name `my-custom-image`. - 1. Click **Build**. Wait for the image build to finish. - ![building an image](img/build-image-from-containerfile.png) - 1. Click **Done** to view the new image on the same page. - -1. Push your image to your Minikube cluster: - 1. Click the **overflow menu** icon corresponding to `my-custom-image` and select **Push image to minikube cluster**. A successful operation notification opens. - ![pushing an image to Minikube](img/pushing-an-image-to-minikube.png) - 1. Click **OK**. - -1. Test your image by creating a container: - 1. Click the **Run Image** icon corresponding to the image `my-custom-image`. - ![running an image](img/running-an-image.png) - 1. **Container name**: enter `my-custom-image-container`. - 1. Review the parameters that Podman Desktop has detected from your image definition or edit them, if required. - 1. Click **Start Container**. - ![starting a container](img/starting-a-container.png) - 1. Select the **Summary** tab to view the details of the new container. - 1. Click the **Close** icon. - -1. Test your image and container on your Minikube cluster: - 1. Click the overflow menu icon corresponding to the container and select **Deploy to Kubernetes**. - ![deploying to Kubernetes](img/deploying-to-kubernetes.png) - 1. Provide the following details: - - **Pod Name**: Keep the proposed value `my-custom-image-container-pod`. - - **Expose service locally using Kubernetes Ingress**: Select the checkbox to expose the service locally using the ingress controller. - - Optional: If your container exposes more than one port, select the port to expose from the dropdown list. - 1. Click **Deploy**. Wait for the pod to reach the state: **Phase: Running**. - ![deploy button](img/deploy-button-minikube.png) - 1. Click **Done**. - -#### Verification - -1. Go to **Pods** from the left navigation pane. -1. View the running `my-custom-image-container-pod` pod. - ![running pod](img/my-custom-image-container-pod.png) -1. Click the pod name to view its details and logs. -1. Optional: If your container exposes a port, go to `http://localhost:`: your application is running. diff --git a/website/docs/minikube/configuring-podman-for-minikube-on-windows.md b/website/docs/minikube/configuring-podman-for-minikube-on-windows.md deleted file mode 100644 index 5823f0cd71..0000000000 --- a/website/docs/minikube/configuring-podman-for-minikube-on-windows.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -sidebar_position: 3 -title: Configuring Podman -description: Configuring Podman for Minikube on Windows Subsystem for Linux (WSL). -keywords: [podman desktop, podman, containers, migrating, kubernetes, minikube] -tags: [configuring-podman-for-minikube, minikube] ---- - -# Configuring Podman for Minikube on Windows Subsystem for Linux (WSL) - -When you create a Podman machine, Podman creates the machine in rootless mode. - -With a Podman machine running on WSL, Minikube: - -- Requires a rootful machine. - -Therefore, set the Podman machine to rootful mode. - -#### Procedure - -1. Stop the Podman machine: - - ```shell-session - $ podman machine stop - ``` - -2. Set the Podman machine in rooful mode: - - ```shell-session - $ podman machine set --rootful - ``` - -3. Start the Podman machine: - - ```shell-session - $ podman machine start - ``` diff --git a/website/docs/minikube/creating-a-minikube-cluster.md b/website/docs/minikube/creating-a-minikube-cluster.md deleted file mode 100644 index a61d3f9b4b..0000000000 --- a/website/docs/minikube/creating-a-minikube-cluster.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -sidebar_position: 4 -title: Creating a cluster -description: Creating a local Minikube-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, kubernetes, minikube] -tags: [creating-a-cluster, minikube] ---- - -# Creating a local Minikube-powered Kubernetes cluster - -You can create multiple local Minikube-powered Kubernetes clusters. - -#### Prerequisites - -- [You installed Minikube](/docs/minikube/installing). -- [On Windows, you configured Podman](/docs/minikube/configuring-podman-for-minikube-on-windows). - -#### Procedure - -1. Go to ** Settings > Resources** -1. In the Minikube tile, click the **Create new ...** button. -1. Edit the default configuration, if needed. -1. Click the **Create** button. - ![creating a Minikiube cluster](img/creating-a-minikube-cluster.png) -1. Optional: Click the **Show logs** button to view the logs. -1. After successful creation, click the **Go back to resources** button. - -#### Verification - -1. Go to ** Settings > Resources**, and view your running `` instance in the **Minikube** tile. - ![running a Minikube cluster instance](img/minikube-cluster-running.png) -1. In the Podman Desktop tray, select the **Kubernetes** menu; you can set the context to your Minikube cluster: `minikube`. - - :::note - - Alternatively, use the status bar or the Podman Desktop **Settings** to set your Kubernetes context. For more details, see [Viewing and selecting the current Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). - - ::: diff --git a/website/docs/minikube/deleting-your-minikube-cluster.md b/website/docs/minikube/deleting-your-minikube-cluster.md deleted file mode 100644 index 100c33fdd9..0000000000 --- a/website/docs/minikube/deleting-your-minikube-cluster.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 7 -title: Deleting a cluster -description: Deleting your local Minikube-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, migrating, kubernetes, minikube] -tags: [deleting-a-minikube-cluster, minikube] ---- - -# Deleting your local Minikube-powered Kubernetes cluster - -#### Prerequisites - -- [You configured Podman](./creating-a-minikube-cluster). -- [You installed Minikube](https://minikube.sigs.k8s.io/). - -#### Procedure - -1. Open ** Settings > Resources**. -1. Find the Minikube cluster to delete. -1. Click to stop the cluster. -1. Once the cluster is stopped, click to delete it. - -#### Verification - -1. In ** Settings > Resources**, the deleted Minikube cluster is not visible. diff --git a/website/docs/minikube/img/build-image-from-containerfile.png b/website/docs/minikube/img/build-image-from-containerfile.png deleted file mode 100644 index 58ad7a04aa..0000000000 Binary files a/website/docs/minikube/img/build-image-from-containerfile.png and /dev/null differ diff --git a/website/docs/minikube/img/create-pod-from-kube-yaml.png b/website/docs/minikube/img/create-pod-from-kube-yaml.png deleted file mode 100644 index 70c83a8523..0000000000 Binary files a/website/docs/minikube/img/create-pod-from-kube-yaml.png and /dev/null differ diff --git a/website/docs/minikube/img/creating-a-minikube-cluster.png b/website/docs/minikube/img/creating-a-minikube-cluster.png deleted file mode 100644 index c189f056c6..0000000000 Binary files a/website/docs/minikube/img/creating-a-minikube-cluster.png and /dev/null differ diff --git a/website/docs/minikube/img/deploy-button-minikube.png b/website/docs/minikube/img/deploy-button-minikube.png deleted file mode 100644 index d5e51a787e..0000000000 Binary files a/website/docs/minikube/img/deploy-button-minikube.png and /dev/null differ diff --git a/website/docs/minikube/img/deploying-to-kubernetes.png b/website/docs/minikube/img/deploying-to-kubernetes.png deleted file mode 100644 index 47e75589d3..0000000000 Binary files a/website/docs/minikube/img/deploying-to-kubernetes.png and /dev/null differ diff --git a/website/docs/minikube/img/minikube-cluster-running.png b/website/docs/minikube/img/minikube-cluster-running.png deleted file mode 100644 index e9f5a673f9..0000000000 Binary files a/website/docs/minikube/img/minikube-cluster-running.png and /dev/null differ diff --git a/website/docs/minikube/img/minikube-resource.png b/website/docs/minikube/img/minikube-resource.png deleted file mode 100644 index bfb3d341e9..0000000000 Binary files a/website/docs/minikube/img/minikube-resource.png and /dev/null differ diff --git a/website/docs/minikube/img/minikube-status-bar.png b/website/docs/minikube/img/minikube-status-bar.png deleted file mode 100644 index a1793a37e4..0000000000 Binary files a/website/docs/minikube/img/minikube-status-bar.png and /dev/null differ diff --git a/website/docs/minikube/img/my-custom-image-container-pod.png b/website/docs/minikube/img/my-custom-image-container-pod.png deleted file mode 100644 index 22dbbc3ac4..0000000000 Binary files a/website/docs/minikube/img/my-custom-image-container-pod.png and /dev/null differ diff --git a/website/docs/minikube/img/push-image-to-minikube.png b/website/docs/minikube/img/push-image-to-minikube.png deleted file mode 100644 index bb65759a0c..0000000000 Binary files a/website/docs/minikube/img/push-image-to-minikube.png and /dev/null differ diff --git a/website/docs/minikube/img/pushing-an-image-to-minikube.png b/website/docs/minikube/img/pushing-an-image-to-minikube.png deleted file mode 100644 index fddc69c6fb..0000000000 Binary files a/website/docs/minikube/img/pushing-an-image-to-minikube.png and /dev/null differ diff --git a/website/docs/minikube/img/restart-using-the-containers-page.png b/website/docs/minikube/img/restart-using-the-containers-page.png deleted file mode 100644 index 446557aeed..0000000000 Binary files a/website/docs/minikube/img/restart-using-the-containers-page.png and /dev/null differ diff --git a/website/docs/minikube/img/restart-using-the-settings-page.png b/website/docs/minikube/img/restart-using-the-settings-page.png deleted file mode 100644 index 61e0080fca..0000000000 Binary files a/website/docs/minikube/img/restart-using-the-settings-page.png and /dev/null differ diff --git a/website/docs/minikube/img/running-an-image.png b/website/docs/minikube/img/running-an-image.png deleted file mode 100644 index 2e406d0635..0000000000 Binary files a/website/docs/minikube/img/running-an-image.png and /dev/null differ diff --git a/website/docs/minikube/img/starting-a-container.png b/website/docs/minikube/img/starting-a-container.png deleted file mode 100644 index f408429ccc..0000000000 Binary files a/website/docs/minikube/img/starting-a-container.png and /dev/null differ diff --git a/website/docs/minikube/img/verify-my-image-pod-running.png b/website/docs/minikube/img/verify-my-image-pod-running.png deleted file mode 100644 index 7df4d83580..0000000000 Binary files a/website/docs/minikube/img/verify-my-image-pod-running.png and /dev/null differ diff --git a/website/docs/minikube/index.md b/website/docs/minikube/index.md deleted file mode 100644 index 9862cf318d..0000000000 --- a/website/docs/minikube/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 80 -title: Minikube -description: Minikube is one way to get Kubernetes running on your workstation. -keywords: [podman desktop, podman, containers, migrating, kubernetes, minikube] -tags: [migrating-to-kubernetes, minikube] ---- - -# Running Kubernetes on your workstation with Minikube and Podman - -Podman Desktop can help you run [Minikube-powered](https://minikube.sigs.k8s.io/) local Kubernetes clusters on a container engine, such as Podman. - -#### Procedure - -1. [Install the `minikube` CLI](/docs/minikube/installing). -1. [On Windows, configure Podman in rootful mode](/docs/minikube/configuring-podman-for-minikube-on-windows). -1. [Create a Minikube cluster](/docs/minikube/creating-a-minikube-cluster). - -#### Next steps - -1. [Set your Kubernetes context to your Minikube cluster](/docs/minikube/working-with-your-local-minikube-cluster). -1. [Build an image and test it in Minikube](/docs/minikube/building-an-image-and-testing-it-in-minikube). -1. [Push an image to Minikube](/docs/minikube/pushing-an-image-to-minikube). -1. [Restart your Minikube cluster](/docs/minikube/restarting-your-minikube-cluster). -1. [Delete your Minikube cluster](/docs/minikube/deleting-your-minikube-cluster). diff --git a/website/docs/minikube/installing-extension.md b/website/docs/minikube/installing-extension.md deleted file mode 100644 index 8405a84e62..0000000000 --- a/website/docs/minikube/installing-extension.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 1 -title: Installing the Minikube extension -description: Tutorial on how to install the Minikube extension and use it -keywords: [podman desktop, podman, minikube, kubernetes] -tags: [podman, kubernetes, minikube] ---- - -import ReactPlayer from 'react-player' - -# Installing the Minikube extension - -The Minikube extension provides the capability of creating a local Kubernetes cluster. This extension can be installed and used as a one-click deployment for a test cluster. - -#### Procedure - -If you have Podman Desktop already installed, **click to launch the installation** of Minikube in Podman Desktop. - -If not, please [install Podman Desktop](/docs/installation) first. - -#### Tutorial - -The following video provides a complete guide from installation to cluster creation: - - diff --git a/website/docs/minikube/installing.md b/website/docs/minikube/installing.md deleted file mode 100644 index 4fc2808212..0000000000 --- a/website/docs/minikube/installing.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 2 -title: Installing the CLI -description: Minikube is one way to get Kubernetes running on your workstation. -keywords: [podman desktop, podman, containers, installing CLI, kubernetes, minikube] -tags: [installing-the-minikube-CLI, minikube] ---- - -# Installing the `minikube` CLI - -#### Procedure - -- In the status bar, click on **Minikube**, and follow the prompts. - ![Minikube in the status bar](img/minikube-status-bar.png) - -#### Verification - -1. The status bar doesn't display **Minikube**. -1. ** Settings > Resources** contain a **Minikube** tile. - ![Minikube resource tile](img/minikube-resource.png) -1. You can run the `minikube` CLI: - - ```shell-session - $ minikube profile list - ``` diff --git a/website/docs/minikube/pushing-an-image-to-minikube.md b/website/docs/minikube/pushing-an-image-to-minikube.md deleted file mode 100644 index 0504aa125c..0000000000 --- a/website/docs/minikube/pushing-an-image-to-minikube.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -sidebar_position: 10 -title: Pushing an image -description: Pushing an image to your Minikube cluster -keywords: [podman desktop, podman, containers, images, pushing an image, kubernetes] -tags: [pushing-an-image, minikube] ---- - -# Pushing an image to your local Minikube-powered Kubernetes cluster - -With Podman Desktop, you can push an image to your local Minikube-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a container engine](/docs/containers). -- [You onboarded a Minikube cluster](/docs/minikube). -- [You have set your Kubernetes context to your Minikube cluster](/docs/minikube/working-with-your-local-minikube-cluster). -- Your image is available on the **Images** page: `:`. - -#### Procedure - -1. Go to **Images** from the left navigation pane. -1. Click the **overflow menu** icon corresponding to the image you want to push and select **Push image to minikube cluster**. A successful operation notification opens. - ![pushing an image to Minikube](img/push-image-to-minikube.png) -1. Click **OK**. - -#### Verification - -Minikube enables you to list loaded images: - -```command -$ minikube image list -``` - -You can also create a pod that uses the loaded image: - -1. Create a `verify_my_image.yaml` Kubernetes YAML file on your workstation. - Replace the placeholders: - - Pod `name` and container `name` values must consist of lowercase alphanumeric characters, '-', or '.', and must start and end with an alphanumeric character. - - Container `image` value is the image you pushed. You can click the name of the image to check its name and tag. - - ```yaml - apiVersion: v1 - kind: Pod - metadata: - name: - spec: - containers: - - name: - image: : - imagePullPolicy: Never - ``` - -1. Go to **Pods** from the left navigation pane. -1. Click **Play Kubernetes YAML** and provide the following details: - - **Kubernetes YAML file**: select your `verify_my_image.yaml` file. - - Set **Runtime** to **Kubernetes cluster**. -1. Click **Play**. - ![play a Kubernetes YAML](img/create-pod-from-kube-yaml.png) -1. Click **Done**. -1. View the created pod `verify-my-image` on the same page. The pod **STATUS** is **RUNNING**. - ![play a Kubernetes YAML](img/verify-my-image-pod-running.png) diff --git a/website/docs/minikube/restarting-your-minikube-cluster.md b/website/docs/minikube/restarting-your-minikube-cluster.md deleted file mode 100644 index b03d3d912a..0000000000 --- a/website/docs/minikube/restarting-your-minikube-cluster.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -sidebar_position: 6 -title: Restarting a cluster -description: Restarting your local Minikube-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, restarting, kubernetes, minikube] -tags: [restarting-a-cluster, minikube] ---- - -# Restarting your local Minikube-powered Kubernetes cluster - -With Podman Desktop, you can restart your local Minikube-powered Kubernetes cluster. - -#### Procedure - -Perform one of the following steps: - -- Restart using the **Settings** page - 1. Open ** Settings > Resources**. - 1. Find the Minikube cluster to restart. - 1. Click the **Restart** icon. - ![restart using the settings page](img/restart-using-the-settings-page.png) - -- Restart using the **Containers** page: - 1. Open **Containers** from the left navigation pane. - 1. Click the **overflow menu** icon corresponding to the Minikube cluster container and select **Restart Container**. - ![restart using the Containers page](img/restart-using-the-containers-page.png) - -#### Verification - -1. Open **Containers** from the left navigation pane. -1. Find the Minikube cluster that restarted. The cluster **Age** is consistent with the restart time. -1. Open **Pods** from the left navigation pane. -1. Find the pods that are running on your Minikube cluster. - -#### Workaround - -Minikube has no command to restart a cluster. -Therefore, Podman Desktop stops the Minikube cluster, and starts it again. -The Minikube cluster might not restart successfully. -In that case: - -- Consider replacing Minikube with a local Kubernetes cluster that you can restart, such as [OpenShift Local](https://developers.redhat.com/products/openshift-local/). -- Consider [deleting your Minikube cluster](/docs/minikube/deleting-your-minikube-cluster), and [creating a Minikube cluster](/docs/minikube/creating-a-minikube-cluster). diff --git a/website/docs/minikube/working-with-your-local-minikube-cluster.md b/website/docs/minikube/working-with-your-local-minikube-cluster.md deleted file mode 100644 index 43d270d9a3..0000000000 --- a/website/docs/minikube/working-with-your-local-minikube-cluster.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -sidebar_position: 5 -title: Working with a cluster -description: Working with your local Minikube-powered Kubernetes cluster. -keywords: [podman desktop, podman, containers, setting context, kubernetes, minikube] -tags: [working-with-a-minikube-cluster, minikube] ---- - -# Working with your local Minikube-powered Kubernetes cluster - -Set your Kubernetes context to your local Minikube-powered Kubernetes cluster. - -#### Prerequisites - -- [You onboarded a Minikube cluster](/docs/minikube/installing). - -#### Procedure - -1. Open the Podman Desktop tray. -2. Go to **Kubernetes**. -3. Click the Kubernetes context named `minikube`. - -:::note - -Alternatively, use the status bar or the Podman Desktop **Settings** to set your Kubernetes context. For more details, see [Viewing and selecting the current Kubernetes context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). - -::: - -#### Verification - -- The Kubernetes CLI reports that the current context is your cluster with the `minikube` name: - - ```shell-session - $ kubectl config current-context - ``` diff --git a/website/docs/openshift/developer-sandbox/img/resources-developer-sandbox-running.png b/website/docs/openshift/developer-sandbox/img/resources-developer-sandbox-running.png deleted file mode 100644 index c06195272b..0000000000 Binary files a/website/docs/openshift/developer-sandbox/img/resources-developer-sandbox-running.png and /dev/null differ diff --git a/website/docs/openshift/developer-sandbox/index.md b/website/docs/openshift/developer-sandbox/index.md deleted file mode 100644 index d63960c133..0000000000 --- a/website/docs/openshift/developer-sandbox/index.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -sidebar_position: 101 -title: Developer Sandbox -description: Configuring access to a Developer Sandbox -keywords: [podman desktop, podman, containers, pods, migrating, kubernetes, openshift] -tags: [migrating-to-kubernetes, openshift] ---- - -# Configuring access to a Developer Sandbox - -The [Developer Sandbox](https://developers.redhat.com/developer-sandbox) is a free, private OpenShift environment including one project and a resource quota of 14 GB RAM, and 40 GB storage. -It lasts 30 days. - -With Podman Desktop, you can configure access to your Developer Sandbox instances. - -#### Prerequisites - -- [Register a Red Hat account](https://developers.redhat.com/register). - -#### Procedure - -1. Install the _Developer Sandbox_ extension: go to **Dashboard**, and click **Developer Sandbox **. -1. Go to ** Settings > Resources**. -1. In the **Developer Sandbox** tile, click **Create new**. -1. In the **Create a Developer Sandbox** screen, click **Log into Developer Sandbox**. -1. In the **Open external website** dialog, click **Yes**. -1. In the [Developer Sandbox website](https://developers.redhat.com/developer-sandbox): - 1. Click **Start your sandbox for free** - 1. If you never used this service, you might get through a verification workflow. - 1. In the **Login with...** screen, click **DevSandbox**. - 1. In your Developer Sandbox Console, click on **your login name > Copy login command** from the menu. - 1. In the **Login with...** screen, click **DevSandbox**. - 1. Click **Display Token**. - 1. Copy the **Log in with this token** full login command, similar to: `oc login --token=sha256~ --server=https://api.sandbox-m2..openshiftapps.com:6443`. -1. Get back to Podman Desktop **Create a Developer Sandbox** screen. - 1. **Context name**: Enter a name such as `Developer Sandbox`. - 2. **Login command**: Paste the `oc login` command that you copied previously. -1. The **Creation** screen displays _Successful operation_. Click **Go back to Resources**. - -#### Verification - -1. On the ** Settings > Resources** screen, your Developer Sandbox is running. - - ![Developer Sandbox is running](img/resources-developer-sandbox-running.png) - -1. [Select your Developer Sandbox in the Podman Desktop tray](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context) -1. Run basic tasks such as: - - [Deploying a container](/docs/kubernetes/deploying-a-pod-to-kubernetes) - - [Deploying a pod](/docs/kubernetes/deploying-a-pod-to-kubernetes) diff --git a/website/docs/openshift/index.md b/website/docs/openshift/index.md deleted file mode 100644 index 0348f4a007..0000000000 --- a/website/docs/openshift/index.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -sidebar_position: 100 -title: OpenShift -description: Red Hat OpenShift introduction -tags: [openshift, kubernetes] -keywords: [openshift, kubernetes] ---- - -# Red Hat OpenShift - -OpenShift is a hybrid cloud platform built around Linux containers, orchestrated and managed by Kubernetes on a foundation of Red Hat Enterprise Linux. - -You can try OpenShift online for free with the Developer Sandbox, or install it locally via OpenShift Local. Also, you can create a MicroShift cluster within your Podman environment to run your OpenShift workloads. - -#### Next steps - -1. [OpenShift Developer Sandbox](/docs/openshift/developer-sandbox) -1. [OpenShift Local](/docs/openshift/openshift-local) -1. [MicroShift](/docs/openshift/microshift) diff --git a/website/docs/openshift/microshift/img/cluster-creation-with-custom-port.png b/website/docs/openshift/microshift/img/cluster-creation-with-custom-port.png deleted file mode 100644 index 12985515d2..0000000000 Binary files a/website/docs/openshift/microshift/img/cluster-creation-with-custom-port.png and /dev/null differ diff --git a/website/docs/openshift/microshift/img/creating-a-microshift-cluster.png b/website/docs/openshift/microshift/img/creating-a-microshift-cluster.png deleted file mode 100644 index 6e230f4700..0000000000 Binary files a/website/docs/openshift/microshift/img/creating-a-microshift-cluster.png and /dev/null differ diff --git a/website/docs/openshift/microshift/img/microshift-instance-running.png b/website/docs/openshift/microshift/img/microshift-instance-running.png deleted file mode 100644 index 2bb7e27055..0000000000 Binary files a/website/docs/openshift/microshift/img/microshift-instance-running.png and /dev/null differ diff --git a/website/docs/openshift/microshift/index.md b/website/docs/openshift/microshift/index.md deleted file mode 100644 index fc7402c78f..0000000000 --- a/website/docs/openshift/microshift/index.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -sidebar_position: 104 -title: MicroShift -description: Create a MicroShift cluster with Podman Desktop -keywords: [podman desktop, podman, minc extension, microshift] -tags: [install-minc-extension, create-a-microshift-cluster] ---- - -# Creating a MicroShift cluster - -With Podman Desktop, you can use the MicroShift in a Container (MINC) extension to create a lightweight OpenShift Kubernetes cluster that runs on minimal resources, such as CPU, RAM, and storage. The cluster is deployed as a container within your Podman environment and resembles a miniaturized version of OpenShift. After setting up a MicroShift cluster, you can: - -- Develop OpenShift applications in a resource-constrained environment. -- Provision lightweight OpenShift control planes. -- Run your OpenShift workloads at the edge or even in network-constrained situations, such as low connectivity or node access issues. -- Access the images built with Podman from your cluster. -- Have a consistent development and management experience with an OpenShift cluster. - -#### Prerequisites - -- A running [Podman machine](/docs/podman/creating-a-podman-machine) with root privileges. -- [Install the MINC extension](/docs/extensions/install). -- On Windows: [Enable the `cgroup v2` kernel feature](https://learn.microsoft.com/en-us/windows/wsl/wsl-config) in the Windows Subsystem for Linux (WSL). - 1. Open the `.wslconfig` file located in the `%UserProfile%` directory. - 1. Append the kernel command-line argument `cgroup_no_v1=all` to the `kernelCommandLine` key under the `[wsl2]` section. - -#### Procedure: Create a MicroShift cluster - -1. Go to **Settings > Resources**. -1. In the MicroShift tile, click the **Create new ...** button. - ![creating a microshift cluster](img/creating-a-microshift-cluster.png) -1. Optional: Edit the port number for the routes. -1. Click **Create**. - ![creation with custom ports](img/cluster-creation-with-custom-port.png) -1. Optional: Click the **Show Logs** button to view the logs. -1. After successful creation, click the **Go back to resources** button. - -#### Verification - -- On the **Settings > Resources** page, your MicroShift instance is running. - ![microshift instance running](img/microshift-instance-running.png) -- [Set your current context](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context) to `microshift` for Kubernetes development. -- Run basic tasks, such as [deploying a pod or container](/docs/kubernetes/deploying-a-pod-to-kubernetes) to Kubernetes. - -#### Additional resources - -- [minc-extension repository](https://github.com/minc-org/minc-extension) -- [MINC blog](/blog/iterate-quickly-inner-loop-with-a-kubernetes-cluster) diff --git a/website/docs/openshift/openshift-local/img/resources-openshift-local-running.png b/website/docs/openshift/openshift-local/img/resources-openshift-local-running.png deleted file mode 100644 index 12dba31263..0000000000 Binary files a/website/docs/openshift/openshift-local/img/resources-openshift-local-running.png and /dev/null differ diff --git a/website/docs/openshift/openshift-local/index.md b/website/docs/openshift/openshift-local/index.md deleted file mode 100644 index 7cf0874ef1..0000000000 --- a/website/docs/openshift/openshift-local/index.md +++ /dev/null @@ -1,86 +0,0 @@ ---- -sidebar_position: 102 -title: OpenShift Local -description: Creating an OpenShift Local instance -keywords: [podman desktop, podman, containers, pods, migrating, kubernetes, openshift] -tags: [migrating-to-kubernetes, openshift] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Creating an OpenShift Local instance - -[Red Hat OpenShift Local](https://developers.redhat.com/products/openshift-local/overview) manages a minimal OpenShift or MicroShift cluster on your workstation for local development and testing. - -With Podman Desktop and the OpenShift Local extension, you can manage your OpenShift Local instances. - -#### Prerequisites - -- [Register a Red Hat account](https://developers.redhat.com/register). - -#### Procedure - -1. Install the _OpenShift Local_ extension: on to **Dashboard**, click **OpenShift Local **. -1. Install the OpenShift Local binaries, when on the **Dashboard**, you see _Podman Desktop was not able to find an installation of OpenShift Local_. - - - - 1. In the **OpenShift Local** tile, click ** Install**. - 1. When prerequisites are missing, follow the instructions. - 1. In the **Red Hat OpenShift Local** screen, click **Yes** to start the installation. - 1. Follow the installation program instructions. - 1. Reboot to finalize system changes. - - - - 1. In the **OpenShift Local** tile, click ** Install**. - 1. When prerequisites are missing, follow the instructions. - 1. In the **Red Hat OpenShift Local** screen, click **Yes** to start the installation. - 1. Follow the installation program instructions. - 1. Reboot to finalize system changes. - - - - 1. Go to the [Red Hat OpenShift local download page](https://console.redhat.com/openshift/create/local). - 1. Select your platform. - 1. Click **Download OpenShift Local**. - 1. Extract the archive. - 1. Copy the `crc` binary to a directory in your`$PATH`, such as `/usr/local/bin`. - 1. To configure your system, run the command: - - ```shell-session - $ crc setup - ``` - - 1. Exit and restart Podman Desktop. - - - - -1. (Optionally) Review the extension settings in ** Settings > Preferences > Extension: Red Hat OpenShift Local**. -1. On the **Dashboard**, click **Initialize and start**. - 1. Select your OpenShift Local Virtual machine preset, if not set in ** Settings > Preferences > Extension: Red Hat OpenShift Local > Preset**. - - _MicroShift_ (experimental): provides a lightweight and optimized environment with a limited set of services. - - _OpenShift_: provides a single node OpenShift cluster with a fuller set of services, including a web console (requires more resources). - 2. Provide a pull secret, required to pull container images from the registry: - 1. Open the [Red Hat OpenShift Local download page](https://cloud.redhat.com/openshift/create/local). - 1. Click **Copy pull secret**. - 1. Get back to Podman Desktop. - 1. Paste the pull secret, and press `Enter`. - -#### Verification - -1. On the **Dashboard** screen, _OpenShift Local is running_. -1. On the **Settings > Resources** screen, your OpenShift Local instance is running. - - ![Developer Sandbox is running](img/resources-openshift-local-running.png) - -1. [Select your OpenShift Local instance the Podman Desktop tray](/docs/kubernetes/viewing-and-selecting-current-kubernetes-context). -1. Run basic tasks such as: - - [Deploying a container](/docs/kubernetes/deploying-a-pod-to-kubernetes) - - [Deploying a pod](/docs/kubernetes/deploying-a-pod-to-kubernetes) - -#### Additional resources - -- [Red Hat OpenShift Local extension repository](https://github.com/crc-org/crc-extension) diff --git a/website/docs/podman/accessing-podman-from-another-wsl-instance.md b/website/docs/podman/accessing-podman-from-another-wsl-instance.md deleted file mode 100644 index af02e3de3c..0000000000 --- a/website/docs/podman/accessing-podman-from-another-wsl-instance.md +++ /dev/null @@ -1,283 +0,0 @@ ---- -sidebar_position: 60 -title: Accessing Podman from another WSL distribution (Windows) -description: On Windows, access your Podman Desktop containers from another Windows Subsystem for Linux (WSL) distribution. ---- - -# Accessing Podman from another WSL distribution - -On Windows, [Podman Desktop creates a Windows Subsystem for Linux (WSL) virtual machine: the Podman Machine](/docs/podman/creating-a-podman-machine.md). -It also configures the Windows Podman client to communicate with the Podman Machine. -However, it does not configure your other WSL distributions. - -You might have other WSL distributions running, and want to access from there to your Podman Desktop containers. - -This tutorial focuses on the most common context to walk you through the steps to configure your WSL distribution: - -- Ubuntu distribution of Linux. -- Default Podman Machine. - -In foldable details, you can find alternative steps for least common contexts: - -- Custom WSL distribution. -- Custom Podman Machine. - -## Configuring your WSL distribution - -1. Start a session in your WSL distribution: - - ```shell-session - > wsl --distribution your-distribution-name - ``` - -1. To communicate with the remote Podman Machine, you need a Podman client. - - To benefit from the latest features, such as `podman kube` subcommands, use a recent Podman version rather than the `podman` package from the distribution. - - The Podman client is available with a full `podman` installation or with the `podman-remote` version 4.x and higher. On Ubuntu it is generally easier to install `podman-remote`. - - With `podman-remote` you also enable the remote mode by default. - - Check for the latest release which includes the `podman-remote` binary from the [Podman releases page](https://github.com/containers/podman/releases/latest). - - Download and unpack the binary: - - ```shell-session - $ wget https://github.com/containers/podman/releases/download/v4.9.1/podman-remote-static-linux_amd64.tar.gz - $ sudo tar -C /usr/local -xzf podman-remote-static-linux_amd64.tar.gz - ``` - - Make this executable as `podman` with the following addition to `.bashrc`: - - ```shell-session - $ export PATH="$PATH:/usr/local/bin" - $ alias podman='podman-remote-static-linux_amd64' - ``` - -1. Configure the Podman client in your WSL distribution to communicate with the remote Podman machine defined by Podman Desktop. - - This will ensure consistency when you are working with Podman from all your different environments - - Set the default Podman system connection to your Podman Machine (assuming Podman Desktop is configured with the default of Podman Machine enabled with root privileges): - - ```shell-session - $ podman system connection add --default podman-machine-default-root unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock - ``` - -
    - - - On a custom Podman Machine, the remote Podman Machine destination might be different. - - Two parameters can change: - - The machine name might differ from `podman-machine-default`. - - The socket name is different when the Podman machine has root privileges disabled (rootless mode). - - Find your Podman Machine name and connection path: - - -
    - 1. Identify the sockets available in your WSL distribution. - - The Podman machine shares sockets in a `/mnt/wsl/podman-sockets/` subdirectory named after the Podman machine name. - - In your WSL session, list the available sockets: - - ```shell-session - $ find /mnt/wsl/podman-sockets/ -name '*.sock' - ``` - - Each Podman Machine has a socket for: - - Rootful Podman: `podman-root.sock` - - Rootless Podman: `podman-user.sock` - - Sample output: - - ```shell-session - /mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock - /mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock - ``` - - 1. Identify the socket that Podman Desktop uses. - - Podman Desktop defaults to rootful Podman. - However, consider identifying the active socket. - - The active socket is the default Podman system connection in your Windows session. - - Open a new Command Prompt, and list your Podman system connections: - - ```shell-session - > podman system connection list - ``` - - The default connection line ends with `true`. - - Identify your Podman Machine socket by its URI in Windows: - - Rootful Podman: `ssh://root@127.0.0.1:59292/run/podman/podman.sock` - - Rootless Podman: `ssh://user@127.0.0.1:59292/run/user/1000/podman/podman.sock` - - Sample output: - - ```shell-session - Name URI Identity Default - - podman-machine-default ssh://user@127.0.0.1:59292/run/user/1000/podman/podman.sock C:\Users\Podman Desktop User\.ssh\podman-machine-default false - podman-machine-default-root ssh://root@127.0.0.1:59292/run/podman/podman.sock C:\Users\Podman Desktop User\.ssh\podman-machine-default true - ``` - - 1. To define the Podman machine remote destination, prepend with `unix://` the socket path that is available in your WSL, and corresponds to the Podman Desktop active socket: - - For the default Podman machine: - - Rootful Podman: `unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock` - - Rootless Podman: `unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock` - -
    -
    - -1. The communication channel between your WSL distribution and the Podman Machine is a special file (a socket). - The Podman Machine creates this file with specific permissions. - To communicate with the Podman Machine from your WSL distribution your user must have write permissions for the socket. - - To give access to the remote Podman machine to your user: create the group if necessary, assign group membership, and exit your session on the WSL distribution to apply the new group membership: - - ```shell-session - $ sudo usermod --append --groups 10 $(whoami) - $ exit - ``` - -## Testing the connection - -Verify that, on your WSL distribution, the Podman CLI communicates with your Podman machine. - -1. Start a session in your WSL distribution: - - ```shell-session - > wsl - ``` - -1. Verify that your user is member of the group delivering access to the remote Podman Machine socket: - - ```shell-session - $ groups - ``` - - On the default Ubuntu WSL, the list contains the `uucp` group. - -
    - - On a custom WSL distribution, the group name might be different. - - Find the required group name: - -
    - The required group id is the same on any WSL distribution. - - However, the group name might be different on a custom WSL distribution. - - On the Podman Machine, which runs on a Fedora distribution: - - Rootful Podman: GID `10` name is `wheel`. - - Rootless Podman: GID `1000` name is `user`. - - On the Ubuntu distribution: - - Rootful Podman: GID `10` name is `uucp`. - - Rootless Podman: GID `1000` name is the same as the user name you chose when creating the WSL machine. - - On a custom WSL distribution, find the group name for: - - Rootful Podman: - - ```shell-session - $ getent group 10 - ``` - - - Rootless Podman: - - ```shell-session - $ getent group 1000 - ``` - -
    -
    - -1. Verify that Podman default system connections is set to your remote Podman machine: - - ```shell-session - $ podman system connection list - ``` - -1. Verify that Podman has a `Server` version corresponding to your Podman Machine version: - - ```shell-session - $ podman version - ``` - - Sample output: - - ```shell-session - Client: - Version: 3.4.4 - API Version: 3.4.4 - Go Version: go1.18.1 - Built: Thu Jan 1 01:00:00 1970 - OS/Arch: linux/amd64 - - Server: - Version: 4.8.3 - API Version: 4.8.3 - Go Version: go1.21.5 - Built: Wed Jan 3 15:11:40 2024 - OS/Arch: linux/amd64 - ``` - - :::info - - On your environment, the Podman version might be different. - - ::: - -1. Verify that you can list running containers. - - On your WSL distribution, start a container such as `quay.io/podman/hello`, and list the name of the last running container: - - ```shell-session - $ podman run quay.io/podman/hello - $ podman ps -a --no-trunc --last 1 - ``` - - On **Podman Desktop > Containers**, the output lists the same container (same name, same image). - -## Changing the connection - -Podman Desktop only has visibility to either rootless or rootful containers but not both at the same time. - -To change the active connection: - -1. In your Windows terminal, change the connection: - - To set the connection to rootless: - - ```shell-session - $ podman machine set --rootful=false - ``` - - - To set the connection to rootful: - - ```shell-session - $ podman machine set --rootful=true - ``` - -1. In your WSL session, Change the Podman system connection configuration: - - To set the connection to rootless: - - ```shell-session - $ podman system connection add --default podman-machine-default-user unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-user.sock - ``` - - - To set the connection to rootful: - - ```shell-session - $ podman system connection add --default podman-machine-default-root unix:///mnt/wsl/podman-sockets/podman-machine-default/podman-root.sock - ``` - -## Next steps - -- From your WSL distribution, [work with containers](/docs/containers). diff --git a/website/docs/podman/adding-certificates-to-a-podman-machine.md b/website/docs/podman/adding-certificates-to-a-podman-machine.md deleted file mode 100644 index 18f5a9020c..0000000000 --- a/website/docs/podman/adding-certificates-to-a-podman-machine.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -sidebar_position: 51 -title: Adding certificates to a Podman machine -description: Adding certificates to a Podman machine -keywords: [podman desktop, podman, certificates] -tags: [adding-certificates] ---- - -# Adding certificates to a Podman machine - -You can add certificates from your local certificate authority (CA) or from a third-party vendor into a Podman machine. After adding these certificates, you can use them in your images to: - -- Secure the communication channel between the running applications in your container and the external host system -- Validate the SSL or TLS certificates provided by external services for authentication - -#### Prerequisites - -- A running [Podman machine](/docs/podman/creating-a-podman-machine). -- Obtained the required certificates for installation, such as _certificate.pem_ or _certificate.crt_. - -#### Procedure - -1. Start an interactive session with the default Podman machine: - -```sh -$ podman machine ssh -``` - -2. Optional: Switch to a root shell only if Podman runs in the default rootless mode: - -```sh -$ sudo su - -``` - -3. Change to the directory where the certificates must be placed: - -```sh -$ cd /etc/pki/ca-trust/source/anchors -``` - -4. Perform one of the following steps: - -- Use the `curl` command to download a certificate: - - ```sh - $ curl [-k] -o https:// - ``` - -- Use any editor, such as Notepad or Vim to create a certificate file with .crt, .cer, or .pem extension. - - :::note - - You can convert a certificate file to a text file and copy its content to the editor. - - ::: - -5. Add the certificate to the list of trusted certificates: - -```sh -$ update-ca-trust -``` - -6. Optional: Run the `exit` command to exit the root shell. - -```sh -$ exit -``` - -7. Run the `exit` command to exit the Podman machine. diff --git a/website/docs/podman/creating-a-podman-machine.md b/website/docs/podman/creating-a-podman-machine.md deleted file mode 100644 index b4937eeec4..0000000000 --- a/website/docs/podman/creating-a-podman-machine.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -sidebar_position: 20 -title: Creating a Podman machine -description: Podman Desktop can assist you to create custom Podman machine on Windows and macOS. -tags: [podman, installing, windows, macOS] -keywords: [podman desktop, containers, podman, installing, installation, windows, macos, podman machine] ---- - -# Creating a Podman machine - -On macOS and Windows, running the Podman container engine requires running a Linux virtual machine. - -By default, Podman Desktop initializes a Podman machine with a standard configuration. - -Consider creating a custom Podman machine to: - -- Control the assigned resources: CPUs, memory, and disk size. -- Use a custom boot image. -- Use the rootful connection by default, for example to run Kind. -- (On Windows) Route the traffic through the network connection from your Windows session. - -#### Prerequisites - -- The Podman executable is installed. - - :::note - - On the macOS ARM64 platform, you might get a warning that the `krunkit` binary is unavailable on the _Create Podman machine_ page. To resolve the warning, [install `krunkit` manually](https://github.com/containers/krunkit?tab=readme-ov-file#installation) or [install Podman using the GitHub installer](https://github.com/containers/podman/releases). - - ::: - -#### Procedure - -1. Go to **Settings > Resources**. -1. In the **Podman** tile, click **Create new**. -1. In the **Create a Podman machine** screen: - 1. **Name**: - Enter a name, such as `podman-machine-default`. - 1. **CPU(s)**: - Select the number of CPUs. - 1. **Memory**: - Select the memory size. - 1. **Disk size**: - Select the disk size. - 1. Optional: Provide a bootable image using one of the following options: - - **Image Path**: Select an image, such as `podman-machine.aarch64.applehv.raw.zst` from your local machine. - - **Image URL or image reference**: Enter an image URL or a registry path. You can use an image URL from the [Podman releases page](https://github.com/containers/podman/releases) or use a valid registry path in the format `registry/repo/image:version`. - 1. **Machine with root privileges**: - Enable to use the rootful connection by default. - Required to use Kind on Windows. - 1. Additional settings based on your operating system: - - (On Windows) - - **User mode networking (traffic relayed by a user process)**: Enable to route the traffic through the network connection from your Windows session. This setting is required to access resources behind your VPN connection. - - **Provider Type**: The setting is visible only to administrators, and its default value is `wsl`. - - (On macOS) **Provider Type**: - - For the macOS ARM64 platform, the default value is `GPU enabled (LibKrun)`. However, you can switch to `Apple HyperVisor` when needed. - - For the macOS AMD64 platform, the default value is `Apple HyperVisor`, and you cannot use the `GPU enabled (LibKrun)` provider. - - 1. Click **Create**. - - ![Create a Podman machine](img/create-a-podman-machine.png) diff --git a/website/docs/podman/gpu.md b/website/docs/podman/gpu.md deleted file mode 100644 index 5d01b22b35..0000000000 --- a/website/docs/podman/gpu.md +++ /dev/null @@ -1,250 +0,0 @@ ---- -sidebar_position: 20 -title: GPU container access -description: GPU passthrough utilization within Windows, macOS and Linux ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# GPU container access - -Leveraging GPU capabilities within a Podman container provides a powerful and efficient method for running GPU-accelerated workloads. Below are instructions on how to get started setting up your OS to utilize the GPU. - - - - -#### Prerequisites - -- NVIDIA Graphics Card (Pascal or later) -- WSL2 (Hyper-V is not supported) - -#### Procedure - -1. The [most up-to-date NVIDIA GPU Driver](https://www.nvidia.com/Download/index.aspx) will support WSL 2. You are not required to download anything else on your host machine for your NVIDIA card. - -2. [Verify that WSL2 was installed when installing Podman Desktop.](/docs/installation/windows-install) - -3. [Create your Podman Machine.](/docs/podman/creating-a-podman-machine) - -4. Install NVIDIA Container Toolkit onto the Podman Machine: - -Podman Machine requires the NVIDIA Container Toolkit to be installed. - -This can be installed by following the [official NVIDIA guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-with-yum-or-dnf) or running the steps below: - -SSH into the Podman Machine: - -```sh -$ podman machine ssh -``` - -Run the following commands **on the Podman Machine, not the host system**: - -```sh -$ curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \ - tee /etc/yum.repos.d/nvidia-container-toolkit.repo && \ - yum install -y nvidia-container-toolkit && \ - nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml && \ - nvidia-ctk cdi list -``` - -:::info - -A configuration change might occur when you create or remove Multi-Instance GPU (MIG) devices, or upgrade the Compute Unified Device Architecture (CUDA) driver. In such cases, you must generate a new Container Device Interface (CDI) specification. - -::: - -#### Verification - -To verify that containers created can access the GPU, you can use `nvidia-smi` from within a container with NVIDIA drivers installed. - -Run the following official NVIDIA container on your host machine: - -```sh -$ podman run --rm --device nvidia.com/gpu=all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi -``` - -Example output: - -```sh -PS C:\Users\admin> podman run --rm --device nvidia.com/gpu=all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi -Fri Aug 16 18:58:14 2024 -+---------------------------------------------------------------------------------------+ -| NVIDIA-SMI 545.36 Driver Version: 546.33 CUDA Version: 12.3 | -|-----------------------------------------+----------------------+----------------------+ -| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | -| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | -| | | MIG M. | -|=========================================+======================+======================| -| 0 NVIDIA GeForce RTX 3060 On | 00000000:07:00.0 On | N/A | -| 0% 34C P8 20W / 170W | 886MiB / 12288MiB | 1% Default | -| | | N/A | -+-----------------------------------------+----------------------+----------------------+ - -+---------------------------------------------------------------------------------------+ -| Processes: | -| GPU GI CI PID Type Process name GPU Memory | -| ID ID Usage | -|=======================================================================================| -| 0 N/A N/A 33 G /Xwayland N/A | -+---------------------------------------------------------------------------------------+ -``` - -#### Troubleshooting - -#### Version mismatch - -You might encounter the following error inside the containers: - -```console -# nvidia-smi -Failed to initialize NVML: N/A -``` - -This problem is related to a mismatch between the Container Device Interface (CDI) and the installed version. - -To fix this problem, generate a new CDI specification by running the following inside the Podman machine: - -```shell -nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml -``` - -:::info - -You might need to restart your Podman machine. - -::: - -#### Additional resources - -- [NVIDIA Container Toolkit Installation](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-with-yum-or-dnf) - - - - -#### Prerequisites - -- macOS Silicon (M1 or later) - -#### Procedure - -**Important to note** that using the "Metal" GPU on macOS utilizes specialized software to achieve this. Specifically a **virtualized GPU** from within the Podman Machine that provides translation support from [Vulkan](https://www.vulkan.org/) and [MoltenVK](https://github.com/KhronosGroup/MoltenVK) calls to MSL (Metal Shading Language), Apples GPU. - -1. Create a Podman Machine that uses `libkrun`: - -![libkrun](img/libkrun.png) - -#### Verification - -Using the GPU functionality requires a specialized Containerfile containing a [patched MESA driver](https://copr.fedorainfracloud.org/coprs/slp/mesa-krunkit/). - -1. Create the following Containerfile: - -```Dockerfile -FROM fedora:40 -USER 0 - -RUN dnf -y install dnf-plugins-core && \ - dnf -y install dnf-plugin-versionlock && \ - dnf -y install mesa-vulkan-drivers vulkan-loader-devel vulkan-headers vulkan-tools vulkan-loader glslc && \ - dnf -y copr enable slp/mesa-krunkit fedora-40-aarch64 && \ - dnf -y downgrade mesa-vulkan-drivers.aarch64 --repo=copr:copr.fedorainfracloud.org:slp:mesa-krunkit && \ - dnf versionlock mesa-vulkan-drivers && \ - dnf clean all -``` - -2. Build the image: - -![build_libkrun_image](img/build_image.png) - -3. Verify you can see the GPU by running a test container: - -```sh -$ podman run --rm -it --device /dev/dri --name gpu-info vulkaninfo | grep "GPU" -``` - -Example output: - -```sh -$ podman run --rm -it --device /dev/dri --name gpu-info quay.io/slopezpa/fedora-vgpu vulkaninfo | grep "GPU" - GPU id = 0 (Virtio-GPU Venus (Apple M1 Pro)) - GPU id = 1 (llvmpipe (LLVM 17.0.6, 128 bits)) -GPU0: - deviceType = PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU - deviceName = Virtio-GPU Venus (Apple M1 Pro) -GPU1: -``` - -#### Additional resources - -Important note that the virtualized GPU (Virtio-GPU Venus (Apple M1 Pro)) only supports vulkan compute shaders, not rendering / draw. For more information on the available GPU features, see `vulkaninfo` from within the container. - -- [Enabling containers to access the GPU on macOS](https://sinrega.org/2024-03-06-enabling-containers-gpu-macos/) -- [libkrun](https://github.com/containers/libkrun) - - - - -#### Prerequisites - -- NVIDIA Graphics Card (Pascal or later) - -#### Procedure - -1. Install the latest NVIDIA GPU Driver for your OS. - -2. Follow the instructions on [installing the NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) in relation to your Linux distribution. - -3. Generate the CDI Specification file for Podman: - - This file is saved either to /etc/cdi or /var/run/cdi on your Linux distribution and is used for Podman to detect your GPU(s). - - Generate the CDI file: - - ```sh - $ nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml - ``` - - Check the list of generated devices: - - ```sh - $ nvidia-ctk cdi list - ``` - - More information as well as troubleshooting tips can be found [on the official NVIDIA CDI guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html). - -4. Configure SELinux (if applicable) - - On SELinux-enabled OSes, such as OSs from the Fedora family, the default policy usually disallows containers to have direct access to devices. We make sure it's allowed. - - Check whether SELinux is installed and enabled: - - ```sh - $ getenforce - ``` - - - If `getenforce` is not found or its output is `Permissive` or `Disabled`, no action is needed. - - If the output is `Enforcing`, configure SELinux to enable device access for containers: - - ```sh - $ sudo setsebool -P container_use_devices true - ``` - -#### Verification - -To verify that containers created can access the GPU, you can use `nvidia-smi` from within a container with NVIDIA drivers installed. - -Run the following official NVIDIA container on your host machine: - -```sh -$ podman run --rm --device nvidia.com/gpu=all nvidia/cuda:11.0.3-base-ubuntu20.04 nvidia-smi -``` - -#### Additional resources - -- [NVIDIA CDI guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/cdi-support.html) -- [NVIDIA Container Toolkit installation](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html#installing-with-yum-or-dnf) - - - diff --git a/website/docs/podman/img/build_image.png b/website/docs/podman/img/build_image.png deleted file mode 100644 index 9fbcf41821..0000000000 Binary files a/website/docs/podman/img/build_image.png and /dev/null differ diff --git a/website/docs/podman/img/create-a-podman-machine.png b/website/docs/podman/img/create-a-podman-machine.png deleted file mode 100644 index b58ded13d4..0000000000 Binary files a/website/docs/podman/img/create-a-podman-machine.png and /dev/null differ diff --git a/website/docs/podman/img/libkrun.png b/website/docs/podman/img/libkrun.png deleted file mode 100644 index 7223382770..0000000000 Binary files a/website/docs/podman/img/libkrun.png and /dev/null differ diff --git a/website/docs/podman/img/remote.png b/website/docs/podman/img/remote.png deleted file mode 100644 index 097e4f36f4..0000000000 Binary files a/website/docs/podman/img/remote.png and /dev/null differ diff --git a/website/docs/podman/img/rosetta.png b/website/docs/podman/img/rosetta.png deleted file mode 100644 index 35ec4850b6..0000000000 Binary files a/website/docs/podman/img/rosetta.png and /dev/null differ diff --git a/website/docs/podman/index.md b/website/docs/podman/index.md deleted file mode 100644 index 10f486d802..0000000000 --- a/website/docs/podman/index.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -sidebar_position: 20 -title: Podman -description: Podman introduction -tags: [onboarding] -keywords: [containers, podman, onboarding] ---- - -# Podman - -Podman is a daemonless, open source, container engine that makes it easy to build, find, and deploy containerized applications. - -#### Next steps - -1. [Installing Podman](/docs/installation). -1. [Creating a Podman machine](/docs/podman/creating-a-podman-machine). -1. [Setting Podman machine default connection](/docs/podman/setting-podman-machine-default-connection). diff --git a/website/docs/podman/podman-remote.md b/website/docs/podman/podman-remote.md deleted file mode 100644 index e2c6fb2d08..0000000000 --- a/website/docs/podman/podman-remote.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -sidebar_position: 21 -title: Remote access -description: Podman Desktop can access remote instances of Podman. -tags: [podman, installing, windows, macOS] -keywords: [podman desktop, containers, podman, remote] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Remote access - -Podman Desktop can manage remote Podman connections. This is facilitated through a list of connections using the command `podman system connection ls`. - -Containers can be created, started, stopped, and deleted as if managed locally. - -This functionality is enabled by connecting via SSH to the Podman socket on the remote host. - -**ed25519** keys, an **SSH** connection, and an enabled **Podman Socket** are required for remote access. - -[RSA keys are not supported](https://github.com/mscdex/ssh2/issues/1375); ed25519 keys are the recommended and only current method to set up a remote connection. - -#### Prerequisites - -- SSH access to a Linux machine with Podman installed - -#### Procedure - -Podman Desktop will automatically detect and show any `podman system connection ls` connections within the GUI by enabling the setting: - -![Enable the remote setting](img/remote.png) - -If you have not added a remote podman connection yet, you can follow the [official Podman guide](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md) or follow the steps below: - -1. Generate a local ed25519 key: - -```sh -$ ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -``` - -2. Copy your **public** ed25519 key to the server: - -Your public SSH key needs to be copied to the `~/.ssh/authorized_keys` file on the Linux server: - - - - - -```sh -$ type ~\.ssh\id_ed25519.pub | ssh user@my-server-ip "cat >> .ssh/authorized_keys" -``` - - - - - -```sh -$ ssh-copy-id -i ~/.ssh/id_ed25519.pub user@my-server-ip -``` - - - - -3. Enable the Podman socket on the remote connection: - -By default, the podman.socket is **disabled** in Podman installations. Enabling the systemd socket allows remote clients to control Podman. - -```sh -$ systemctl enable podman.socket -$ systemctl start podman.socket -``` - -Confirm that the socket is enabled by checking the status: - -```sh -$ systemctl status --user podman.socket -``` - -4. Add the connection to `podman system connection ls`: - -It's important to know which socket path you are using, as this varies between regular users and root. - -Use `podman info` to determine the correct socket path: - -```sh -$ ssh user@my-server-ip podman info | grep sock - path: /run/user/1000/podman/podman.sock -``` - -If you are using root, it may appear as: - -```sh -$ ssh root@my-server-ip podman info | grep sock - path: /run/podman/podman.sock -``` - -Now you are ready to add the connection. Add it with a distinct name to the Podman system connection list: - -```sh -# non-root -$ podman system connection add my-remote-machine --identity ~/.ssh/id_ed25519 ssh://myuser@my-server-ip/run/user/1000/podman/podman.sock - -# root -$ podman system connection add my-remote-machine --identity ~/.ssh/id_ed25519 ssh://root@my-server-ip/run/podman/podman.sock -``` - -:::warning - -On Windows, you need to use an absolute path for the identities; a path with ~ will not resolve. - -::: - -5. Check within Podman Desktop such as the **Containers** section that you can now access your remote instance. - -#### Verification - -**GUI verification:** - -1. Run a helloworld container on the remote machine: - -```sh -$ ssh user@my-server-ip podman run -d quay.io/podman/hello -``` - -2. Within Podman Desktop, check that your container appears in the **Containers** section. - -**CLI verification:** - -1. Set your remote connection as the default: - -```sh -$ podman system connection default my-remote-machine -``` - -2. Verify that the container appears in the CLI: - -```sh -$ podman ps -``` - -:::note - -You can also use the `--connection` argument to target only the connection you want, for example, `podman --connection=my-remote-machine ps`. - -::: - -#### Additional resources - -- [podman:docs/tutorials/remote_client.md](https://github.com/containers/podman/blob/main/docs/tutorials/remote_client.md) diff --git a/website/docs/podman/rosetta.md b/website/docs/podman/rosetta.md deleted file mode 100644 index c11b819b24..0000000000 --- a/website/docs/podman/rosetta.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -sidebar_position: 20 -title: Native Apple Rosetta translation layer (macOS) -description: Use Apple Rosetta to speed up cross-architecture containers ---- - -# Native Apple Rosetta translation layer - -On macOS, Podman machine creates a virtual machine that uses the native Apple hypervisor `applehv` with Rosetta enabled by default. This increases the speed of any `x86_64` builds or containers to near-native levels by using a translation layer. - -Rosetta support is enabled by default on all new Podman machine installations. If you disable Rosetta, [qemu](https://www.qemu.org/) will instead be used. - -#### Prerequisites - -- macOS Silicon - -#### Procedure - -To enable Rosetta support, re-create your Podman machine instance: - -1. Delete your Podman machine. - -2. Enable Rosetta support under **Settings**: - -![rosetta](img/rosetta.png) - -3. Re-create your Podman machine. - -#### Verification - -To verify that Rosetta has been enabled or disabled, check your `~/.config/containers/containers.conf` configuration. - -You will see the `rosetta` configuration parameter with either `true` or `false`. If the parameter does _not_ exist, Rosetta is already enabled by default. - -#### Additional resources - -- [Official Apple Rosetta documentation](https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta) diff --git a/website/docs/podman/setting-podman-machine-default-connection.md b/website/docs/podman/setting-podman-machine-default-connection.md deleted file mode 100644 index 2945f4a46c..0000000000 --- a/website/docs/podman/setting-podman-machine-default-connection.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -sidebar_position: 50 -title: Setting Podman machine default connection -description: How to set Podman machine default connection. ---- - -# Setting Podman machine default connection - -Each Podman machine exposes two connections: - -- rootless -- rootful - -Podman has one default connection. - -Podman Desktop, and other tools, such as Kind, connect to the default connection. - -After an event that might have changed the default Podman machine connection, such as creating another Podman machine, consider verifying and setting the default connection. - -#### Procedure - -1. List Podman machine connections, in a terminal: - - ```shell-session - $ podman system connection ls - ``` - -1. Set the Podman machine default connection to your desired connection, such as `podman-machine-default-root`, in a terminal: - - ```shell-session - $ podman system connection default podman-machine-default-root - ``` - -1. List Podman machine connections, to verify which is the default, in a terminal: - - ```shell-session - $ podman system connection ls - ``` - -1. Restart the Podman machine that has the default connection: - - ```shell-session - $ podman machine stop - $ podman machine start - ``` - -1. Refresh Podman Desktop connection to Podman: click the **** icon to open the **Troubleshooting** page, and click the **Reconnect providers** button. - -#### Verification - -- Podman Desktop lists images, containers, and pods that are accessible via the desired Podman machine connection. diff --git a/website/docs/proxy/img/dashboard-podman-is-running.png b/website/docs/proxy/img/dashboard-podman-is-running.png deleted file mode 100644 index 0d1d752564..0000000000 Binary files a/website/docs/proxy/img/dashboard-podman-is-running.png and /dev/null differ diff --git a/website/docs/proxy/img/dashboard-podman-needs-set-up.png b/website/docs/proxy/img/dashboard-podman-needs-set-up.png deleted file mode 100644 index e71baab7ff..0000000000 Binary files a/website/docs/proxy/img/dashboard-podman-needs-set-up.png and /dev/null differ diff --git a/website/docs/proxy/img/proxy-settings.png b/website/docs/proxy/img/proxy-settings.png deleted file mode 100644 index c03e9e76eb..0000000000 Binary files a/website/docs/proxy/img/proxy-settings.png and /dev/null differ diff --git a/website/docs/proxy/img/proxy-update-notification.png b/website/docs/proxy/img/proxy-update-notification.png deleted file mode 100644 index ae3220551b..0000000000 Binary files a/website/docs/proxy/img/proxy-update-notification.png and /dev/null differ diff --git a/website/docs/proxy/index.md b/website/docs/proxy/index.md deleted file mode 100644 index a0d5e62ed9..0000000000 --- a/website/docs/proxy/index.md +++ /dev/null @@ -1,345 +0,0 @@ ---- -sidebar_position: 2 -title: Restricted environments -description: Using Podman Desktop behind a proxy requiring custom Certificate Authorities (CA). -tags: [podman-desktop, installing, windows, macos, linux] -keywords: [podman desktop, containers, podman, installing, installation, windows, macos, linux] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Installing in a restricted environment - -In a restricted environment you might face the following challenges: - -- The default Podman Desktop and Podman installation methods download assets during the setup. - - However, a network restricted environment might refuse access to these external resources. - - Consider rather using the restricted environment installation method. - -- The Podman Machine receives a network address distinct to your computer network address. - - When you are using a VPN, you might have problems to access, from your host, resources that the Podman Machine exposes. - - Consider enabling the **User mode networking** option when creating your Podman Machine to route the network traffic through your host. - -- The Podman Machine connects directly to the external network. - - However, a restricted environment might block all traffic no going to a proxy. - - Consider configuring Podman Desktop and your Podman Machine to route the traffic through a proxy. - -This tutorial is guiding you through the required steps to work in a restricted environment. - -## Installing Podman Desktop and Podman - - - - -The Windows _Installer for restricted environments_ has all artifacts required to install Podman Desktop and Podman. It does not require Internet access to download resources during installation. -However, it does not contain additional utilities, such as Compose or Kind. - -1. Check that your environment has: - - 6 GB RAM for the Podman Machine. - - Windows Subsystem for Linux version 2 (WSL 2) prerequisites. See [Enabling WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install), [WSL basic commands](https://learn.microsoft.com/en-us/windows/wsl/basic-commands), and [Troubleshooting WSL 2](https://learn.microsoft.com/en-us/windows/wsl/troubleshooting#error-0x80370102-the-virtual-machine-could-not-be-started-because-a-required-feature-is-not-installed): - - The Windows user has administrator privileges. - - Windows 64bit. - - Windows 10 Build 19043 or greater, or Windows 11. - - On a virtual machine: [Nested Virtualization enabled](https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/nested-virtualization#configure-nested-virtualization). - -1. Prepare your system. - - Enable the WSL feature, without installing the default Ubuntu distribution of Linux. - - Open the Command Prompt, and run:. - - ```shell-session - > wsl --install --no-distribution - ``` - -1. Restart your computer. - -1. Download the _Installer for restricted environments_ from to the [Windows downloads page](/downloads/windows). - -1. Copy the downloaded file to the Windows host in a restricted environment, and run it. - -1. Go to **Dashboard** from the left navigation pane. The screen displays: _ Podman needs to be set up_. - - ![Podman needs set up screen](img/dashboard-podman-needs-set-up.png) - -1. Click the **Set up** button. - -1. Review and validate all confirmation screens to set up the Podman Machine. - -
    - - - Optionally, when you are using a VPN, consider enabling user mode networking: - - -
    - - When you are using a VPN, you might have problems to access, from your host, resources that the Podman Machine exposes. - - To enable access from your host to resources on your Podman Machine, in the **Create Podman machine** screen, enable the **User mode networking (traffic relayed by a user process)** option. - -
    -
    - -
    - - -The macOS _Disk Image for restricted environments_ has all artifacts required to install Podman Desktop and Podman. It does not require Internet access to download resources during installation. -However, it does not contain additional utilities, such as Compose or Kind. - -1. Download the _Disk Image for restricted environments_ from the [macOS downloads page](/downloads/macos). - -1. Copy the downloaded file to the macOS host in a restricted environment, and double-click it. - -1. Drag the Podman Desktop icon to the Applications folder. - -1. Go to **Dashboard** from the left navigation pane.The screen displays: _ Podman needs to be set up_. - - ![Podman needs set up screen](img/dashboard-podman-needs-set-up.png) - -1. Click the **Set up** button. - -1. Review and validate all confirmation screens to set up the Podman Machine. - - :::tip - - To route the network traffic through your VPN, in the **Create Podman machine** screen, enable the **User mode networking (traffic relayed by a user process)** option. - - ::: - - - - -The Linux _AMD64 binary (tar.gz)_ has all artifacts required to install Podman Desktop. It does not require Internet access to download resources during installation. -However, it does not contain additional utilities, such as Podman CLI, Compose, or Kind. - -1. The Podman Destkop archive for restricted environments does not contain Podman CLI. - - To install Podman, go to [the Podman website](https://podman.io/), and follow the installation instructions. - -1. Download the _AMD64 binary (tar.gz)_ archive from [the Linux Downloads page](https://podman-desktop.io/downloads/linux). - -1. Copy the downloaded file to the Linux host in a restricted environment, and extract the archive content. - -1. In the extracted directory, open the `podman-desktop` executable file. - - -
    - -#### Verification - -- In the **Dashboard**, the **Podman** tile displays _Podman is running_. - - ![Podman is running screen](img/dashboard-podman-is-running.png) - -## Using a proxy - -Requirements: - -- ``: Your proxy URL. -- Optional: your proxy Certificate Authorities (CA) in Privacy-Enhanced Mail (PEM) format. - - - - -1. Go to **Settings > Proxy** from the left navigation pane. -1. Select a proxy configuration: **System**, **Manual**, or **Disabled**. - - If you select **Manual**, configure your proxy URLs and bypass proxy settings details. - ![Proxy settings](img/proxy-settings.png) - -1. Click **Update**. A notification opens. -1. Click **OK**. - ![Proxy update notification](img/proxy-update-notification.png) - -
    - - - Optionally, to use a proxy requiring a custom Certificate Authorities: - - -
    - 1. Store your proxy Certificate Authorities (CA), in Privacy-Enhanced Mail (PEM) format, in the `proxy_ca.pem` file. - 2. Copy the certificate to the Podman machine: - - ```shell-session - $ cat proxy_ca.pem | podman machine ssh podman-machine-default "cat > proxy_ca.pem" - ``` - - 3. Open a shell prompt on the Podman machine: - - ```shell-session - $ podman machine ssh - ``` - - 4. Add the custom Certificate Authorities (CA) for your proxy: - - ```shell-session - $ sudo cp /etc/pki/ca-trust/source/anchors/ - $ sudo update-ca-trust - ``` - -
    -
    - -
    - - - Optionally, to use a proxy in your containers: - - -
    - 1. Open a shell prompt on the Podman machine: - - ```shell-session - $ podman machine ssh - ``` - - 2. Edit the `containers.conf` file to pass the proxy environment variables to Podman CLI. - - The file location depends on your connection mode: - - `rootless`: `$HOME/.config/containers/containers.conf` - - - `rootful`: `/etc/containers/containers.conf` - - 3. Set the proxy environment variables to pass into the containers: - - ```toml - [containers] - http_proxy = true - env = ["http_proxy=", "https_proxy="] - ``` - -
    -
    - -1. Go to **Settings > Resources** and restart the Podman machine. - -
    - - -1. Go to **Settings > Proxy** from the left navigation pane. -1. Select a proxy configuration: **System**, **Manual**, or **Disabled**. - - If you select **Manual**, configure your proxy URLs and bypass proxy settings details. - ![Proxy settings](img/proxy-settings.png) -1. Click **Update**. A notification opens. -1. Click **OK**. - ![Proxy update notification](img/proxy-update-notification.png) - -
    - - - Optionally, to use a proxy requiring a custom Certificate Authorities: - - -
    - 1. Store your proxy Certificate Authorities (CA) in Privacy-Enhanced Mail (PEM) format, in your home directory, in the `proxy_ca.pem` file. - 2. Copy the certificate to the Podman machine: - - ```shell-session - $ cat proxy_ca.pem | podman machine ssh podman-machine-default "cat > proxy_ca.pem" - ``` - - 3. Open a shell prompt on the Podman machine: - - ```shell-session - $ podman machine ssh - ``` - - 4. Add the custom Certificate Authorities (CA) for your proxy: - - ```shell-session - $ sudo cp /etc/pki/ca-trust/source/anchors/ - $ sudo update-ca-trust - ``` - -
    -
    - -
    - - - Optionally, to use a proxy in your containers: - - -
    - 1. Open a shell prompt on the Podman machine: - - ```shell-session - $ podman machine ssh - ``` - - 2. Edit the `containers.conf` file to pass the proxy environment variables to Podman CLI. - - The file location depends on your connection mode: - - `rootless`: `$HOME/.config/containers/containers.conf` - - - `rootful`: `/etc/containers/containers.conf` - - 3. Set the proxy environment variables to pass into the containers: - - ```toml - [containers] - http_proxy = true - env = ["http_proxy=", "https_proxy="] - ``` - -
    -
    - -1. Go to **Settings > Resources** and restart the Podman machine. - -
    - - -On Linux, Podman Desktop **Proxy** settings have no effect on Podman. - -Configure Podman: - -1. Edit the `containers.conf` file to pass the proxy environment variables to Podman CLI. - - The file location depends on your connection mode: - - `rootless`: `$HOME/.config/containers/containers.conf` - - - `rootful`: `/etc/containers/containers.conf` - -1. Set the proxy environment variables to pass into the Podman engine: - - ```toml - [engine] - env = ["http_proxy=", "https_proxy="] - ``` - -1. Add the custom Certificate Authorities (CA) for your proxy: - - ```shell-session - $ sudo cp /etc/pki/ca-trust/source/anchors/ - $ sudo update-ca-trust - ``` - -1. Restart all `podman` processes. - - ```shell-session - $ pkill podman - ``` - - -
    - -#### Verification - -1. Podman can pull images. - 1. Go to **Images**. - 1. Click **Pull an image**. - 1. **Image to Pull**: Enter an image name, such as `quay.io/podman/hello`. - 1. Click **Pull image**. Podman Desktop reports `Download complete` after a while. - 1. Click **Done**. - -1. You can install extensions such as: - - [Installing Compose](/docs/compose/setting-up-compose) - - [Installing Kind](/docs/kind/installing). diff --git a/website/docs/troubleshooting/access-logs.md b/website/docs/troubleshooting/access-logs.md deleted file mode 100644 index 6433e1e6a7..0000000000 --- a/website/docs/troubleshooting/access-logs.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -sidebar_position: 10 -title: Access logs -description: Access Podman Desktop logs to troubleshoot -keywords: [podman desktop, podman, access logs, troubleshoot] -tags: [acessing-podman-desktop-logs, troubleshooting-podman-desktop] ---- - -# Access Podman Desktop logs - -When you face any connection issues or any other problems with your task execution, you can access the Podman Desktop logs to troubleshoot. In addition, you can also resolve those issues using the **Repair & Connections** and **Stores** tabs. - -Stores denote the front-end objects that capture the event logs from the back-end side. For example, if a container is missing from the **Containers** component page, click the **containers** store link to check the event that triggered the last refresh. After comparing the number of containers in the store with those on the **Containers** page, you can identify whether a recent event is captured. If not, use the **Refresh** button to refresh the store data. - -If you do not want to track the previous event logs, you can remove them from the history of the store. - -#### Procedure: Access and save logs - -1. Click the **Troubleshooting** icon in the status bar. -1. Select the **Logs** tab to view the logs. - ![accessing logs](img/access-logs.png) -1. Optional: Select the **Gather Logs** tab to save all the logs into a .zip file. - 1. Click **collect and save logs as .zip**. - 1. Browse the location where you want to save the logs. - 1. Click **Save**. You get a successful operation notification. - -#### Procedure: Resolve connection issues - -1. Click the **Troubleshooting** icon in the status bar. -1. Optional: Click **Cleanup/Purge data** to delete all resources from the engine. - ![Repair & Connections tab](img/repair-and-connections-tab.png) -1. Optional: Check container connections: - 1. Click **Ping** to view the response time of the container engine. - 1. Click **Check containers** to view the response time of the available containers. -1. Optional: Click **Reconnect Providers** to reconnect to the container engine socket. - -#### Procedure: Resolve event-related issues - -1. Click the **Troubleshooting** icon in the status bar. -1. Select the **Stores** tab to view the stores associated with Podman Desktop. - ![store tab](img/stores-tab.png) -1. Click a store link. -1. Click **Refresh** to refresh the event logs. - ![refresh the event logs](img/refresh-event-logs.png) -1. Optional: Click **Clear** to delete the event logs. -1. Click **OK**. diff --git a/website/docs/troubleshooting/img/access-logs.png b/website/docs/troubleshooting/img/access-logs.png deleted file mode 100644 index 27fe884a2a..0000000000 Binary files a/website/docs/troubleshooting/img/access-logs.png and /dev/null differ diff --git a/website/docs/troubleshooting/img/refresh-event-logs.png b/website/docs/troubleshooting/img/refresh-event-logs.png deleted file mode 100644 index d10746546a..0000000000 Binary files a/website/docs/troubleshooting/img/refresh-event-logs.png and /dev/null differ diff --git a/website/docs/troubleshooting/img/repair-and-connections-tab.png b/website/docs/troubleshooting/img/repair-and-connections-tab.png deleted file mode 100644 index 9bcc84a6cc..0000000000 Binary files a/website/docs/troubleshooting/img/repair-and-connections-tab.png and /dev/null differ diff --git a/website/docs/troubleshooting/img/stores-tab.png b/website/docs/troubleshooting/img/stores-tab.png deleted file mode 100644 index e665f4dbb6..0000000000 Binary files a/website/docs/troubleshooting/img/stores-tab.png and /dev/null differ diff --git a/website/docs/troubleshooting/index.md b/website/docs/troubleshooting/index.md deleted file mode 100644 index ae04661ac3..0000000000 --- a/website/docs/troubleshooting/index.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -sidebar_position: 120 -title: Troubleshooting -description: How to investigate when Podman Desktop does not work as expected. ---- - -# Troubleshooting - -If you cannot find your issue here or in the documentation, please fill an issue on our [repository](https://github.com/podman-desktop/podman-desktop/issues). You can also explore the [discussions](https://github.com/podman-desktop/podman-desktop/discussions) and do a search on similar issues on the [repository](https://github.com/podman-desktop/podman-desktop/issues). - -Access the following pages to troubleshoot your issues: - -- [Accessing logs](/docs/troubleshooting/access-logs) -- [Troubleshooting Podman](/docs/troubleshooting/troubleshooting-podman) -- [Troubleshooting Podman on Windows](/docs/troubleshooting/troubleshooting-podman-on-windows) -- [Troubleshooting Podman on macOS](/docs/troubleshooting/troubleshooting-podman-on-macos) -- [Troubleshooting Podman on Linux](/docs/troubleshooting/troubleshooting-podman-on-linux) -- [Troubleshooting OpenShift Local](/docs/troubleshooting/troubleshooting-openshift-local) -- [Troubleshooting extension-related issues](/docs/troubleshooting/troubleshooting-extension-issues) diff --git a/website/docs/troubleshooting/troubleshooting-extension-issues.md b/website/docs/troubleshooting/troubleshooting-extension-issues.md deleted file mode 100644 index f0e27e135a..0000000000 --- a/website/docs/troubleshooting/troubleshooting-extension-issues.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -sidebar_position: 102 -title: Extensions -description: Troubleshoot extension-related issues -keywords: [podman desktop, podman, extensions, troubleshoot] -tags: [troubleshooting-extension-issues] ---- - -# Troubleshooting extension-related issues - -## Podman Desktop failed to create a Minikube cluster - -#### Issue - -You might get this error message `Failed to create minikube cluster. E0125 05:58:08.614734 408 cache.go:189] Error downloading kic artifacts: not yet implemented` when creating a new Minikube cluster. You get the error due to instability of the Minikube tool itself. - -#### Solution - -1. Run the following command to delete the Minikube cluster. - - ```shell-session - $ minikube delete - ``` - -2. Create a new [Minikube cluster](/docs/minikube/installing-extension) using the Podman Desktop UI. diff --git a/website/docs/troubleshooting/troubleshooting-openshift-local.md b/website/docs/troubleshooting/troubleshooting-openshift-local.md deleted file mode 100644 index 961488cdeb..0000000000 --- a/website/docs/troubleshooting/troubleshooting-openshift-local.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -sidebar_position: 100 -title: Podman on OpenShift -description: How to investigate when Podman does not work as expected. ---- - -# Troubleshooting OpenShift Local - -You can find here troubleshooting help for issues specific to OpenShift Local. - -1. To verify that your user can run `crc`, verify that the `crc` binary is available in the user PATH (`/usr/local/bin/crc` on macOS and Linux). - -2. To verify that the host is ready to run OpenShift Local, in a terminal, run this command and verify the output has no errors: - - ```shell-session - $ crc setup --check-only - ``` - - Sample output: - - ```shell-session - INFO Using bundle path - INFO Checking if running as non-root - INFO Checking if running inside WSL2 - INFO Checking if crc-admin-helper executable is cached - crc-admin-helper executable is not cached - ``` - -3. To verify the configured preset, in a terminal, run: - - ```shell-session - $ crc config get preset - ``` diff --git a/website/docs/troubleshooting/troubleshooting-podman-on-linux.md b/website/docs/troubleshooting/troubleshooting-podman-on-linux.md deleted file mode 100644 index 26671b5194..0000000000 --- a/website/docs/troubleshooting/troubleshooting-podman-on-linux.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -sidebar_position: 40 -title: Podman on Linux -description: How to investigate when Podman does not work as expected. ---- - -# Troubleshooting Podman on Linux - -## Podman Desktop does not manage native Podman - -On Linux, Podman usually runs natively on the host. -Podman might also run in a virtual machine. - -Podman Desktop does only connect to the native rootless podman connection. - -Podman Desktop does not manage podman native configuration or podman machine (create, configure, start, stop, delete). - -Podman Desktop might manage configuration relative to connections to registries and proxies. diff --git a/website/docs/troubleshooting/troubleshooting-podman-on-macos.md b/website/docs/troubleshooting/troubleshooting-podman-on-macos.md deleted file mode 100644 index 0e6f6e6f63..0000000000 --- a/website/docs/troubleshooting/troubleshooting-podman-on-macos.md +++ /dev/null @@ -1,297 +0,0 @@ ---- -sidebar_position: 30 -title: Podman on MacOS -description: How to investigate when Podman does not work as expected. ---- - -# Troubleshooting Podman on macOS - -## Unable to set custom binary path for Podman on macOS - -#### Issue - -When setting a custom binary path (under Preferences -> Custom binary path), Podman is unable to find `gvproxy` and `podman-mac-helper`: - -```sh -Error: unable to start host networking: "could not find \"gvproxy\" in one of [/usr/local/opt/podman/libexec /opt/homebrew/bin /opt/homebrew/opt/podman/libexec /usr/local/bin /usr/local/libexec/podman /usr/local/lib/podman /usr/libexec/podman /usr/lib/podman $BINDIR/../libexec/podman]. To resolve this error, set the helper_binaries_dir key in the `[engine]` section of containers.conf to the directory containing your helper binaries." -``` - -#### Solution - -1. Download `gvproxy` from the [gvisor-tap-vsock release page](https://github.com/containers/gvisor-tap-vsock/releases). -2. Build the `podman-mac-helper` from the source code on the [Podman GitHub page](https://github.com/containers/podman/tree/main/cmd/podman-mac-helper). -3. Add the `helpers_binaries_dir` entry to `~/.config/containers/containers.conf`: - -```sh -[containers] - -helper_binaries_dir=["/Users/user/example_directory"] -``` - -**NOTE**: A pre-built binary will be added to the Podman release page so you do not have to build `podman-mac-helper`. An [issue is open for this](https://github.com/containers/podman/issues/16746). - -## Unable to locate Podman Engine - -#### Issue - -Despite having Podman Engine installed, you might receive an error as follows - -`Error: No such keg: /usr/local/Cellar/podman` -or any similar error denoting that Podman Engine does not exist. - -#### Explanation - -The Podman Installer and Homebrew use different locations to store the Podman Engine files in the file system. For example, Podman Installer installs Podman Engine in the path `/opt/podman` whereas Homebrew uses the path `/usr/local` for macOS Intel, `/opt/homebrew` for Apple Silicon and `/home/linuxbrew/.linuxbrew` for Linux. - -#### Solution - -To check where exactly is your Podman Engine installed, run the command- - -```sh -which podman -``` - -This returns the path where the Podman Engine would be installed. This would help determine further action. - -For example, if you’re looking to completely uninstall Podman Engine from your system for a fresh installation, running `which podman` returns the exact path where Podman still exists. This could be the path where Podman Installer stores Podman Engine, such as `/opt/podman`. Once you know the path, run: - -```sh -sudo rm -rf /opt/podman -``` - -Or - -```sh -sudo rm -rf path-where-podman-exists -``` - -Here, you would replace `path-where-podman-exists` with the output of `which podman`. - -You can now proceed for a fresh installation of Podman Desktop - -## Podman machine on Apple Silicon - -#### Issue - -If you are using an Apple Silicon and brew, you might encounter the following error when starting Podman from Podman Desktop - -```shell-session -Error: qemu exited unexpectedly with exit code 1, stderr: qemu-system-x86_64: invalid accelerator hvf -qemu-system-x86_64: falling back to tcg -qemu-system-x86_64: unable to find CPU model 'host' -``` - -#### Explanation - -Podman machine is running as a `x86_64` process and it could be due to a dual install of homebrew: one for `x86_64` and one for `arm64`. - -#### Solution - -You can - -1. Uninstall Podman machine on your `x86_64` brew install (for example from a terminal running under rosetta) `brew uninstall podman` -2. or uninstall brew `x86_64` as most brew receipe have now arm64 support: follow [these instructions](https://github.com/homebrew/install#uninstall-homebrew) from a terminal running under rosetta - -Then run a terminal in native mode (default) and install Podman machine `brew install podman` - -Finally clean the Podman machine VMs that had been previously created, and create new ones. - -```shell-session -$ podman machine rm podman-machine-default -$ podman machine init -``` - -You should be a happy camper from here. - -## Recovering from a failed start - -After a failed start, the Podman machine might be unable to start because a QEMU process is still running and the PID file is in use. - -#### Workaround - -1. Kill the remaining QEMU process and stop the Podman machine: - - ```shell-session - $ ps -edf | grep qemu-system | grep -v grep | awk '{print $2}' | xargs -I{} kill -9 {}; podman machine stop - ``` - -2. Start the Podman machine. - -#### Solution - -Use Podman 4.6.1 or greater. - -## Podman machine not starting with QEMU 8.1.0 from brew - -When you installed Podman and QEMU with brew, and QEMU version is 8.1.0, Podman machine might fail to start with an error such as: -`Error: qemu exited unexpectedly with exit code -1, stderr: qemu-system-x86_64: Error: HV_DENIED` - -#### Solution - -- [Install Podman Desktop and Podman using the .dmg installer](/docs/installation/macos-install) rather than brew. - The Podman installer has a QEMU binary that has been tested with Podman. - -#### Workaround - -Keep your brew-based installation and apply one of these workarounds: - -- Rollback the QEMU brew package to v8.0.3. - - ```shell-session - $ brew uninstall qemu - $ curl -OSL https://raw.githubusercontent.com/Homebrew/homebrew-core/dc0669eca9479e9eeb495397ba3a7480aaa45c2e/Formula/qemu.rb - $ brew install ./qemu.rb - ``` - -- Alternatively, sign the QEMU brew binary locally: - - ```shell-session - $ cat >entitlements.xml < - - - - com.apple.security.hypervisor - - - - EOF - $ codesign --sign - --entitlements entitlements.xml --force /usr/local/bin/qemu-system-$(uname -m | sed -e s/arm64/aarch64/) - ``` - -#### Additional resources - -- [Homebrew issue #140244](https://github.com/Homebrew/homebrew-core/issues/140244). -- [Podman issue #19708](https://github.com/containers/podman/issues/19708). - -## On Apple Silicon, the Podman Machine does not start - -On Apple Silicon, when Podman Machine starts, it stays indefinitely blocked with a _Waiting for VM_ message. - -#### Solution - -For M1 and M2 processors: - -- Update to Podman 4.9. - -#### Workaround - -For M3 processors: - -1. To get a clean environment, remove all Podman and qemu artifacts: - 1. Remove eventual installation from podman/podman desktop installer: - - ```shell-session - $ sudo rm -rf opt/podman - ``` - - 1. Remove brew installations: - - ```shell-session - $ brew uninstall podman-desktop - $ brew uninstall podman - $ brew uninstall qemu - ``` - - 1. Remove Podman files: - - ```shell-session - $ rm -rf ~/.ssh/podman-machine-default - $ rm -rf ~/.ssh/podman-machine-default.pub - $ rm -rf ~/.local/share/containers - $ rm -rf ~/.config/containers - ``` - -1. Reinstall Podman using brew: - - ```shell-session - $ brew install podman - ``` - -1. Install bunzip2: - - ```shell-session - $ brew install bzip2 - ``` - -1. Install QEMU 8.2.0 to `/opt/homebrew/Cellar/qemu/8.2.0`: - - ```shell-session - $ curl -sL https://github.com/AkihiroSuda/qemu/raw/704f7cad5105246822686f65765ab92045f71a3b/pc-bios/edk2-aarch64-code.fd.bz2 | bunzip2 > /opt/homebrew/Cellar/qemu/8.2.0/share/qemu/edk2-aarch64-code.fd - ``` - -1. Install patched EDK2. - Download [EDK2](https://github.com/lima-vm/edk2-patched.tmp/releases/download/edk2-stable202311%2Blima.0/edk2-aarch64-code.fd.gz) from [lima-vm/edk2-patched.tmp/releases](https://github.com/lima-vm/edk2-patched.tmp/releases). - -1. Init podman machine. - -1. Find QEMU configuration directory to define _`qemu-config-directory`_ in next step: - - ```shell-session - $ podman machine info | grep MachineConfigDir - - ``` - -1. Update podman machine config json: - - ```shell-session - $ sed -i 's@file=.\*edk2-aarch64-code.fd@file=/path/to/downloaded/edk2-aarch64-code.fd@g' qemu-config-directory/podman-machine-default.json - ``` - -1. Start Podman machine. - -#### Additional resources - -- [Issue #20776](https://github.com/containers/podman/issues/20776) - -## `podman machine` CLI commands do not work with `libkrun` provider type without manual configuration - -When you create a Podman machine with the `GPU enabled (LibKrun)` provider type, all the `podman machine` CLI commands stop working. - -**_Podman machine is not listed_** - -```shell-session -$ podman machine list -NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE -``` - -**_Error: interacting with the default Podman machine_** - -```shell-session -$ podman machine ssh -Error: vm podman-machine-default not found: podman-machine-default: VM does not exist -``` - -#### Workaround - -- Prefix each `podman machine` command with - `CONTAINERS_MACHINE_PROVIDER=libkrun`. For example, `CONTAINERS_MACHINE_PROVIDER=libkrun podman machine ls`. -- Manually configure the `containers.conf` file or the `CONTAINERS_MACHINE_PROVIDER` environment variable. See [Using `libkrun` as machine provider](/docs/installation/macos-install#using-libkrun-as-machine-provider). - -#### Additional resources - -- [Issue #9860](https://github.com/podman-desktop/podman-desktop/issues/9860) - -## Running out of disk space on macOS - -On macOS, `.plist` files append Podman Desktop data to the previous log files, increasing their size. - -With Podman Desktop 1.16, the log files are automatically cleaned at the restart of the application. This ensures the log files do not increase indefinitely in size. However, you can check the size of the log files to manually troubleshoot disk space issues at any time. - -#### Solution - -Check the size of the Podman Desktop log files to troubleshoot: - -```sh -$ ls -la ~/Library/Logs/Podman\ Desktop/*.log -``` - -With Podman Desktop 1.16.0 or later versions, your computer might require a restart to truncate the Podman Desktop log files. -To avoid restarting your computer, run these commands one by one: - -```shell-session -$ launchctl unload ~/Library/LaunchAgents/io.podman_desktop.PodmanDesktop.plist -``` - -```shell-session -$ launchctl load ~/Library/LaunchAgents/io.podman_desktop.PodmanDesktop.plist -``` diff --git a/website/docs/troubleshooting/troubleshooting-podman-on-windows.md b/website/docs/troubleshooting/troubleshooting-podman-on-windows.md deleted file mode 100644 index 38ab973d31..0000000000 --- a/website/docs/troubleshooting/troubleshooting-podman-on-windows.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -sidebar_position: 20 -title: Podman on Windows -description: How to investigate when Podman does not work as expected. ---- - -# Troubleshooting Podman on Windows - -You can find here troubleshooting help for issues specific to Windows. - -## Deleting a corrupted Podman Machine - -#### Issue - -1. You are not able to stop your Podman Machine. - - ```shell-session - $ podman machine stop - ``` - -2. The Logs contain this error: - - ```shell-session - Error: Error stopping sysd: exit status 1 - ``` - -#### Workaround - -1. To display the active Windows Subsystem for Linux (WSL) distribution list: in the terminal, run: - - ```shell-session - $ wsl --list - ``` - -1. The command returns the list of active WSL distributions. Identify your Podman Machine in the list, such as `podman-machine-default`. - -1. To stop, and uninstall your Podman Machine: in the terminal, replace `podman-machine-default` by your Podman machine name, and run: - - ```shell-session - $ wsl --unregister podman-machine-default - ``` - -#### Additional resources - -- [WSL documentation: Uninstall a Linux distribution](https://learn.microsoft.com/en-us/windows/wsl/basic-commands#unregister-or-uninstall-a-linux-distribution) - -## The terminal session attaches to Podman Desktop when launching it from the command line - -#### Issue - -1. When you start Podman Desktop from the command line in Windows the terminal session attaches to it. -1. When you quit the terminal, it kills Podman Desktop. - -#### Workaround - -- Set the environment variable `ELECTRON_NO_ATTACH_CONSOLE` to true before launching Podman Desktop. - -## When the host is behind a VPN, Podman cannot access network resources - -When the host is behind a VPN, Podman might fail to access network resources, and display errors such as _Temporary failure in name resolution_. - -#### Solution - -See [Accessing resources behind a VPN with Podman on Windows](/docs/proxy). - -## Older WSL versions might lead to networking issues - -Older versions of WSL might cause networking issues, such as the `Get-NetTCPConnection` error, indicating that the WSL loopback forwarding facility is not functioning correctly. Recent versions of WSL do not have this issue. - -#### Solution - -1. Update Windows to either the 21H1, 21H2, or 22H2 version of Windows 10, or to the 21H1 version of Windows 11, or greater. - -2. Update WSL: - - ```shell-session - wsl --update - ``` - -3. Optionally, delete your Podman machine, and create a new one. - -## Windows 10 Enterprise LTSC version 21H2: Podman Desktop is unable to detect WSL2 machine - -On a Windows 10 LTSC version, running the `wsl --install --no-distribution` command does not work, and the Podman Desktop setup does not run smoothly. - -You must install a specific Windows Subsystem for Linux (WSL) distribution to make the Podman Desktop setup run smoothly. After setting up Podman Desktop, you can unintsall the WSL distribution. - -#### Solution: Enable Podman Desktop setup to run smoothly - -**_Windows 11 or later version_** - -1. Run the `wsl --update` command to update the WSL kernel. -1. Run the `wsl --install --no-distribution` command to not install any WSL distribution. -1. Restart your machine. - -**_Windows 10 LTSC version_** - -1. Run the `wsl --update` command. -1. Run the `wsl --install -d ` command to install a specific WSL distribution. - - Replace `distro` with any official WSL distribution, such as `ubuntu-24.04`. -1. Restart your machine. -1. (Optional): Run the `wsl --unregister ` to uninstall the WSL distribution. - -## Unable to install Compose extension 2.33.x on Windows due to certificate signature failure - -Installing the Compose extension version 2.33.x fails, and you receive an error message: `Unable to fetch the available extensions: certificate signature failure`. - -#### Workaround - -The reason for the failure could be that the Windows Certificate Store has been improperly loaded with the corporate certificate chain. Usually, all the certificates are placed in one location, namely TrustedRootCA, which results in the failure to import the certificates into the Podman Desktop application. - -To get root CA self-signed certificates to work, you can identify where these certificates have been placed in the Windows Certificate Store. Then, you can [clean the Store](https://learn.microsoft.com/en-us/windows-hardware/drivers/install/trusted-root-certification-authorities-certificate-store) to ensure those certificates are in their proper locations. diff --git a/website/docs/troubleshooting/troubleshooting-podman.md b/website/docs/troubleshooting/troubleshooting-podman.md deleted file mode 100644 index fe4db8cdc1..0000000000 --- a/website/docs/troubleshooting/troubleshooting-podman.md +++ /dev/null @@ -1,273 +0,0 @@ ---- -sidebar_position: 10 -title: Podman -description: How to investigate when Podman does not work as expected. ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Troubleshooting Podman - -## Podman Desktop does not find your Podman installation - -#### Issue - -To install Podman, you can choose between multiple installation methods: - -- Install from Podman Desktop. -- Podman installer. -- Operating system specific installer: Brew, Chocolatey, Scoop, Winget. -- Installer for restricted environment. - -Podman Desktop might fail to detect your Podman installation. - -#### Solution - -Try following steps to verify your Podman installation. -After each step, quit and restart Podman Desktop to ensure that it can detect your Podman installation. - -1. In a terminal, verify you can access the Podman CLI, and verify the version. - - ```shell-session - $ podman version - ``` - -1. Update Podman to the latest stable version by using your installation method. -1. Search for errors in the installation logs (if your installation method is providing logs). -1. Reinstall Podman with the same installation method. -1. Reinstall Podman with the Podman Desktop installer. -1. Reinstall Podman with the Podman installer. -1. Reinstall Podman with another method. - -## Podman Desktop fails to create a Podman machine - -#### Issue - -Podman Desktop might fail creating a Podman machine. - -#### Workaround - -1. In a terminal, create the Podman machine with the Podman CLI: - - ```shell-session - $ podman machine init - ``` - -1. If the creation fails, read the logs carefully to continue troubleshooting. - -## Podman Desktop fails starting a Podman machine - -#### Issue - -Podman Desktop might fail starting a Podman machine. On the **Settings > Resources** page, the machine does not switch to the running phase for quite some time. - -#### Workaround - -1. In a terminal, start the Podman machine with the Podman CLI: - - ```shell-session - $ podman machine start - ``` - -1. If the start fails, read the logs carefully to continue troubleshooting. - -Alternatively, you can run the `podman machine reset` command, and [create a new machine](/docs/podman/creating-a-podman-machine) using the Podman Desktop UI. - -## Podman Desktop fails to list images or containers - -Podman Desktop might fail listing images or container. - -#### Prerequisites - -- Podman 4.1.0 or later is needed. Podman Desktop requires the Podman machine to expose the socket on the host for macOS, and on a named pipe for Windows - -#### Procedure - -1. On Windows and macOS: In a terminal, verify that at least one Podman machine is running: - - ```shell-session - $ podman machine list - ``` - -1. To verify that you can connect using the CLI, open a terminal and run the `hello` container: - - ```shell-session - $ podman run quay.io/podman/hello - ``` - -## Podman Desktop fails listing containers - -#### Issue - -Podman Desktop might display "No containers" on the Containers page, even if active containers are running in the background. - -#### Solution - -1. Stop and restart Podman Desktop. -1. In Podman Desktop, restart the Podman machine. - -1. In a terminal, restart the Podman machine: - - ```shell-session - $ podman machine stop - $ podman machine start - ``` - -1. If the previous step did not work for you, delete your Podman machine, and create a new one: - - ```shell-session - $ podman machine rm - $ podman machine init - ``` - -1. If the previous steps did not work for you, delete your Podman configuration files, and create a new Podman machine: - - ```shell-session - $ rm -rf ~/.local/share/containers/podman - $ rm -rf ~/.config/containers/ - $ podman machine init - ``` - -## Podman Desktop is failing to display the images or containers from a rootful Podman machine - -The rootful configuration for a Podman machine depends on the Podman machine default connection. -The default connection can be modified by external events, or when creating a new Podman machine. -Podman Desktop might then reconnect in rootless mode, and fail to display the images or containers. - -#### Workaround - -1. Verify that the Podman default connection is the rootful connection to your Podman machine: - - ```shell-session - $ podman system connection ls - ``` - - The default connection has `true` at the end of the line. - - The rootful connection has a `-root` name suffix, and a `ssh://root@` URI prefix. - - Example default rootful connection: - - ```shell-session - Name URI Identity Default - podman-machine-default ssh://user@127.0.0.1:54826/run/user/1000/podman/podman.sock c:\Users\username\.ssh\podman-machine-default false - podman-machine-default-root ssh://root@127.0.0.1:54826/run/podman/podman.sock c:\Users\username\.ssh\podman-machine-default true - ``` - - Example default rootless connection: - - ```shell-session - Name URI Identity Default - podman-machine-default ssh://user@127.0.0.1:54826/run/user/1000/podman/podman.sock c:\Users\username\.ssh\podman-machine-default true - podman-machine-default-root ssh://root@127.0.0.1:54826/run/podman/podman.sock c:\Users\username\.ssh\podman-machine-default false - ``` - - Continue with the next steps only if the default connection is not the rootful connection to your Podman machine. - -1. Set the Podman machine in rootful mode: - - ```shell-session - $ podman machine set --rootful - ``` - -1. Restart the Podman machine: - - ```shell-session - $ podman machine stop - $ podman machine start - ``` - -1. Verify that Podman default connection points to the rootful connection: - - ```shell-session - $ podman system connection ls - ``` - - Continue with the next steps only if the default connection is not the rootful connection to your Podman machine. - -1. Set the Podman machine, such as `podman-machine-default` in rootful mode: - - ```shell-session - $ podman system connection default podman-machine-default-root - ``` - -1. Restart the Podman machine: - - ```shell-session - $ podman machine stop - $ podman machine start - ``` - -#### Verification - -1. The Podman default connection is the rootful connection to your Podman machine: - - ```shell-session - $ podman system connection ls - ``` - -## Warning about Docker compatibility mode - -#### Issue - -When running the Podman provider, a warning shows regarding Docker compatibility mode on the dashboard: - -``` -⚠️ Docker Socket Compatibility: Podman is not emulating the default Docker socket path: '/var/run/docker.sock'. Docker-specific tools may not work. See troubleshooting page on podman-desktop.io for more information. -``` - -This might appear when either: - -- The Docker socket is not mounted correctly. -- Docker Desktop is also being ran at the same time. - -#### Solution - -1. Stop Docker Desktop (if installed). -2. On macOS, Run the `podman-mac-helper` binary: - - ```shell-session - $ sudo podman-mac-helper install - ``` - -3. Restart the Podman machine to recreate and activate the default Docker socket path. - -_Note:_ If Docker Desktop is started again, it will automatically re-alias the default Docker socket location and the Podman compatibility warning will re-appear. - -## Uninstalling Podman Desktop preserves the old configuration data - -#### Issue - -After uninstalling Podman Desktop, the configuration data persists even though it is not needed for a fresh installation. - -#### Solution - -**_Deleting Podman Desktop configuration_** - -1. Go to the `$HOME/.local/share/containers` directory, where `$HOME` denotes the home folder of the current user. -1. Delete the `podman-desktop` folder. - -**_Deleting Podman configuration_** - -Podman stores its configuration files in the `$HOME/.config/containers` directory. Options available to delete Podman configuration: - -- Using CLI - - Run the `podman machine reset` command. -- Using UI - 1. Click the **Troubleshooting** icon in the status bar. - 1. Click the **Cleanup/Purge data** button to delete all resources from the engine. - -## Kubernetes clusters are not reachable from Podman Desktop - -#### Issue - -When you connect to a Kubernetes cluster, such as Amazon Web Services (AWS) or Oracle Cloud Infrastructure (OCI), you might get this error: `spawnSync ENOENT`. - -Kubernetes clusters from cloud providers require an executable installed on the user's machine for authentication tokens. When you add the path of this executable to the `PATH` for the shell session, this change does not apply to Podman Desktop. This prevents Podman Desktop from obtaining new tokens, making clusters inaccessible. - -#### Solution - -1. Move the binary located in your `.kube/config` file to a system bin directory, such as `/usr/local/bin/`. - -1. Set the value of the `command` parameter to the full path of the executable in your Kubernetes configuration file. For example, `command: /usr/local/bin/`, where `cloud-provider-binary` denotes the binary name, such as `aws` or `oci`. diff --git a/website/docs/uninstall/index.md b/website/docs/uninstall/index.md deleted file mode 100644 index f1ceeea25c..0000000000 --- a/website/docs/uninstall/index.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -sidebar_position: 117 -title: Uninstallation -description: How to uninstall Podman Desktop on Windows, macOS, and Linux. -tags: [podman-desktop, uninstall] -keywords: [podman desktop, containers, podman, unistall-on-windows, unistall-on-macOS, uninstall-on-linux] ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Uninstall Podman Desktop - -Uninstalling Podman Desktop does not automatically remove the created Kubernetes clusters, Podman, or other resources. To uninstall, perform the following tasks: - -1. Optional: Remove cluster resources -1. Uninstall Podman -1. Uninstall Podman Desktop - -## Optional: Remove cluster resources - -- For Kind, use the UI to [delete the Kind cluster](/docs/kind/deleting-your-kind-cluster) or run the `kind delete clusters --all` command. -- For Minikube, run `minikube delete` and then remove the `~/.minikube` folder. - - :::note - - You can find the installed binaries and plugins for Podman Desktop at `~/.local/share/containers/podman-desktop/extensions-storage/`, and you may delete them if necessary. - - ::: - -## Uninstall Podman - - - - -You can delete all pods, containers, and images by removing the Podman machine. - -1. Remove all Podman machines: - ```sh - $ podman machine reset -f - ``` -1. Uninstall Podman from the Start menu, Settings, or Control Panel. For more details, see the [resource](https://support.microsoft.com/en-us/windows/uninstall-or-remove-apps-and-programs-in-windows-4b55f974-2cc6-2d2b-d092-5905080eaf98). -1. Remove Podman files and configurations: - ```powershell - rm -Recurse -Force ~/.local/share/containers/podman - rm -Recurse -Force ~/.config/containers/ - rm -Recurse -Force ~/AppData/Roaming/containers - ``` - - - - -1. Remove all Podman machines: - ```sh - $ podman machine reset -f - ``` -1. Perform one of the following steps based on your installation: - - If you have installed Podman using `brew`, run the following command: - ```sh - $ brew uninstall podman - ``` - - If you have installed Podman using the Podman Desktop setup, run the following commands one by one: - ```sh - $ sudo /opt/podman/bin/podman-mac-helper uninstall - $ sudo rm /etc/paths.d/podman-pkg - $ sudo rm -rfv /opt/podman - ``` -1. Remove the Podman files and configurations: - ```sh - $ rm -rf ~/.local/share/containers/podman - $ rm -rf ~/.config/containers/podman - ``` -1. Optional: Delete storage configuration: - ```sh - $ rm -rf ~/.local/share/containers/storage - ``` - - - - -By default, Podman is available on Linux distributions, such as CentOS Stream, Fedora, and Red Hat Enterprise Linux. However, you can remove Podman resources, including containers, pods, images, and Podman machines. - -- To remove all containers, run the `podman rm -a -f` command. -- To remove all pods, run the `podman pod rm -a -f` command. -- To remove all images, run the `podman rmi -a -f` command. -- To remove all Podman machines, run the `podman machine reset -f` command. - - - - -## Uninstall Podman Desktop - - - - -1. Choose an uninstall method based on how Podman Desktop was installed and perform the necessary step: - -
    - - uninstall methods: - - Installer - - Chocolatey - - Scoop - - Winget - - - #### Installer - - Uninstall Podman Desktop from the Start menu, Settings, or Control Panel. For more details, see the [resource](https://support.microsoft.com/en-us/windows/uninstall-or-remove-apps-and-programs-in-windows-4b55f974-2cc6-2d2b-d092-5905080eaf98). - - #### Chocolatey - - Run the following command: - - ```shell-session - > choco uninstall podman-desktop - ``` - - #### Scoop package manager for Windows - - Run the following command: - - ```shell-session - > scoop uninstall podman-desktop - ``` - - #### Winget - - Run the following command: - - ```shell-session - > winget uninstall -e --id RedHat.Podman-Desktop - ``` - -
    - -1. Remove the Podman Desktop configuration files: - ```powershell - $ rm -Recurse -Force ~/.local/share/containers/podman-desktop/ - $ rm -Recurse -Force ~/AppData/Roaming/Podman Desktop - ``` -1. Remove temporary files, caches, and blobs: - ```powershell - $ rm -Recurse -Force ~/AppData/Roaming/Podman Desktop - ``` - -
    - - -1. Perform one of the following steps based on your installation: - - If you have installed Podman Desktop using `brew`, run the following command: - ```sh - $ brew uninstall podman-desktop - ``` - - If you have installed Podman Desktop using the `.dmg` file, perform the following steps: - 1. Locate the Podman Desktop `.dmg` file. - 1. Drag the Podman Desktop icon and drop it to the trash folder. - -1. Remove the Podman Desktop configuration files: - ```sh - $ rm -rf ~/.local/share/containers/podman-desktop - ``` - - - - -1. Uninstall Podman Desktop using flatpak or flathub: - - ```sh - $ flatpak uninstall io.podman_desktop.PodmanDesktop - ``` - -1. Remove the Podman Desktop configuration folder: - ```sh - $ rm -rf ~/.local/share/containers/podman-desktop - ``` - - -
    diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js deleted file mode 100644 index 8a3682c30e..0000000000 --- a/website/docusaurus.config.js +++ /dev/null @@ -1,543 +0,0 @@ -// @ts-check -// Note: type annotations allow type checking and IDEs autocompletion -import { resolve } from 'node:path'; -import { createNotesFiles } from './release-notes-parser'; -import Storybook from './storybook'; - -const lightCodeTheme = require('prism-react-renderer').themes.github; -const darkCodeTheme = require('prism-react-renderer').themes.dracula; - -const title = 'podman desktop'; - -const inDevMode = process.env.NODE_ENV === 'development'; - -/** @type {import('@docusaurus/types').Config} */ -const config = { - title: 'Podman Desktop', - url: inDevMode ? 'http://localhost:3000' : 'https://podman-desktop.io', - baseUrl: '/', - onBrokenLinks: 'throw', - onBrokenMarkdownLinks: 'warn', - favicon: 'img/favicon.ico', - organizationName: 'containers', - projectName: 'podman-desktop', - deploymentBranch: 'gh-pages', - trailingSlash: false, - markdown: { - mermaid: true, - parseFrontMatter: async params => { - return createNotesFiles(params); - }, - }, - themes: ['@docusaurus/theme-mermaid'], - plugins: [ - async () => { - return { - name: 'docusaurus-tailwindcss', - configurePostCss(postcssOptions) { - postcssOptions.plugins.push(require('@tailwindcss/postcss')); - return postcssOptions; - }, - }; - }, - 'docusaurus-plugin-goatcounter', - [ - '@docusaurus/plugin-client-redirects', - { - redirects: [ - { - to: '/downloads/windows', - from: '/downloads/Windows', - }, - { - to: '/downloads/macos', - from: '/downloads/macOS', - }, - { - to: '/downloads/linux', - from: '/downloads/Linux', - }, - { - to: '/docs/intro', - from: '/docs', - }, - { - to: '/docs/installation', - from: '/docs/Installation', - }, - { - to: '/docs/installation/windows-install', - from: [ - '/docs/installation/windows-install/installing-podman-desktop-silently-with-the-windows-installer', - '/docs/installation/windows-install/installing-podman-desktop-with-chocolatey', - '/docs/installation/windows-install/installing-podman-desktop-with-scoop', - '/docs/installation/windows-install/installing-podman-desktop-with-winget', - '/docs/installation/windows-install/installing-podman-with-openshift-local', - '/docs/installation/windows-install/installing-podman-with-podman-desktop', - '/docs/onboarding-for-containers/installing-podman-with-openshift-local-on-windows', - '/docs/onboarding-for-containers/installing-podman', - '/docs/onboarding/containers/installing-podman-with-openshift-local-on-windows', - '/docs/onboarding/containers/installing-podman-with-podman-desktop-on-windows', - '/docs/onboarding/containers/installing-podman', - '/docs/podman/installing-podman-with-openshift-local-on-windows', - '/docs/podman/installing', - ], - }, - { - to: '/docs/proxy', - from: [ - '/docs/installation/windows-install/installing-podman-desktop-and-podman-in-a-restricted-environment', - '/docs/installation/linux-install/installing-podman-desktop-from-a-compressed-tar-file', - '/docs/proxy/using-a-proxy-in-your-containers', - '/docs/proxy/using-a-proxy-on-linux', - '/docs/proxy/using-a-proxy-requiring-a-custom-ca', - '/docs/proxy/using-a-proxy', - '/docs/proxy/using-a-vpn-on-windows', - ], - }, - { - to: '/docs/compose', - from: ['/docs/podman-compose', '/docs/compose/compose-spec', '/docs/compose/podman-compose'], - }, - { - to: '/docs/kubernetes', - from: '/docs/onboarding-for-kubernetes', - }, - { - to: '/docs/kind/pushing-an-image-to-kind', - from: '/docs/kubernetes/kind/pushing-an-image-to-kind', - }, - { - to: '/docs/minikube/pushing-an-image-to-minikube', - from: '/docs/kubernetes/minikube/pushing-an-image-to-minikube', - }, - { - to: '/docs/lima/pushing-an-image-to-lima', - from: '/docs/kubernetes/lima/pushing-an-image-to-lima', - }, - { - to: '/docs/kubernetes/deploying-a-pod-to-kubernetes', - from: '/docs/kubernetes/deploying-a-container-to-kubernetes', - }, - { - to: '/docs/containers/onboarding', - from: ['/docs/onboarding-for-containers', '/docs/onboarding', '/docs/onboarding/containers'], - }, - { - to: '/docs/lima/creating-a-lima-instance', - from: [ - '/docs/onboarding-for-containers/creating-a-lima-instance-with-podman-desktop', - '/docs/Installation/creating-a-lima-instance-with-podman-desktop', - '/docs/onboarding/containers/creating-a-lima-instance-with-podman-desktop', - ], - }, - { - to: '/docs/podman/creating-a-podman-machine', - from: [ - '/docs/onboarding-for-containers/creating-a-podman-machine-with-podman-desktop', - '/docs/Installation/creating-a-podman-machine-with-podman-desktop', - '/docs/onboarding/containers/creating-a-podman-machine-with-podman-desktop', - ], - }, - { - to: '/docs/migrating-from-docker/managing-docker-compatibility', - from: [ - '/docs/migrating-from-docker/using-podman-mac-helper', - '/docs/migrating-from-docker/emulating-docker-cli-with-podman', - '/docs/migrating-from-docker/verifying-your-tools-are-using-podman', - ], - }, - { - to: '/docs/openshift/developer-sandbox', - from: [ - '/docs/onboarding-for-kubernetes/developer-sandbox', - '/docs/kubernetes/openshift/configuring-access-to-a-developer-sandbox', - '/docs/onboarding/kubernetes/developer-sandbox', - ], - }, - { - to: '/docs/kubernetes/existing-kubernetes', - from: [ - '/docs/onboarding-for-kubernetes/existing-kubernetes', - '/docs/kubernetes/configuring-access-to-a-kubernetes-cluster', - '/docs/onboarding/kubernetes/existing-kubernetes', - ], - }, - { - to: '/docs/kind/installing', - from: [ - '/docs/onboarding-for-kubernetes/kind/installing-kind', - '/docs/kubernetes/kind/installing-kind', - '/docs/onboarding/kubernetes/kind/installing-kind', - ], - }, - { - to: '/docs/kind/configuring-podman-for-kind-on-windows', - from: [ - '/docs/onboarding-for-kubernetes/kind/configuring-podman-for-kind-on-windows', - '/docs/kubernetes/kind/configuring-podman-for-kind-on-windows', - '/docs/onboarding/kubernetes/kind/configuring-podman-for-kind-on-windows', - ], - }, - { - to: '/docs/kind/creating-a-kind-cluster', - from: [ - '/docs/onboarding-for-kubernetes/kind/creating-a-kind-cluster', - '/docs/kubernetes/kind/creating-a-kind-cluster', - '/docs/onboarding/kubernetes/kind/creating-a-kind-cluster', - ], - }, - { - to: '/docs/kind/restarting-your-kind-cluster', - from: [ - '/docs/onboarding-for-kubernetes/kind/restarting-your-kind-cluster', - '/docs/kubernetes/kind/restarting-your-kind-cluster', - '/docs/onboarding/kubernetes/kind/restarting-your-kind-cluster', - ], - }, - { - to: '/docs/kind/deleting-your-kind-cluster', - from: [ - '/docs/onboarding-for-kubernetes/kind/deleting-your-kind-cluster', - '/docs/kubernetes/kind/deleting-your-kind-cluster', - '/docs/onboarding/kubernetes/kind/deleting-your-kind-cluster', - ], - }, - { - to: '/docs/lima', - from: [ - '/docs/onboarding-for-kubernetes/lima', - '/docs/onboarding/kubernetes/creating-a-lima-instance-with-podman-desktop', - '/docs/onboarding/kubernetes/lima', - ], - }, - { - to: '/docs/minikube/installing', - from: [ - '/docs/onboarding-for-kubernetes/minikube/installing-minikube', - '/docs/kubernetes/minikube/installing-minikube', - '/docs/onboarding/kubernetes/minikube/installing-minikube', - ], - }, - { - to: '/docs/minikube/configuring-podman-for-minikube-on-windows', - from: [ - '/docs/onboarding-for-kubernetes/minikube/configuring-podman-for-minikube-on-windows', - '/docs/kubernetes/minikube/configuring-podman-for-minikube-on-windows', - '/docs/onboarding/kubernetes/minikube/configuring-podman-for-minikube-on-windows', - ], - }, - { - to: '/docs/minikube/creating-a-minikube-cluster', - from: [ - '/docs/onboarding-for-kubernetes/minikube/creating-a-minikube-cluster', - '/docs/kubernetes/minikube/creating-a-minikube-cluster', - '/docs/onboarding/kubernetes/minikube/creating-a-minikube-cluster', - ], - }, - { - to: '/docs/minikube/restarting-your-minikube-cluster', - from: [ - '/docs/onboarding-for-kubernetes/minikube/restarting-your-minikube-cluster', - '/docs/kubernetes/minikube/restarting-your-minikube-cluster', - '/docs/onboarding/kubernetes/minikube/restarting-your-minikube-cluster', - ], - }, - { - to: '/docs/minikube/deleting-your-minikube-cluster', - from: [ - '/docs/onboarding-for-kubernetes/minikube/deleting-your-minikube-cluster', - '/docs/kubernetes/minikube/deleting-your-minikube-cluster', - '/docs/onboarding/kubernetes/minikube/deleting-your-minikube-cluster', - ], - }, - { - to: '/docs/openshift/openshift-local', - from: [ - '/docs/onboarding-for-kubernetes/openshift-local', - '/docs/kubernetes/openshift/creating-an-openshift-local-cluster', - '/docs/onboarding/kubernetes/openshift-local', - ], - }, - { - to: '/docs/containers', - from: ['/docs/working-with-containers', '/docs/getting-started/getting-started', '/docs/getting-started'], - }, - { - to: '/docs/containers/registries', - from: [ - '/docs/containers/registries/authenticating-to-a-preconfigured-registry', - '/docs/containers/registries/insecure-registry', - '/docs/getting-started/authenticating-to-a-preconfigured-registry', - '/docs/getting-started/insecure-registry', - '/docs/working-with-containers/registries/authenticating-to-a-preconfigured-registry', - '/docs/working-with-containers/registries/insecure-registry', - ], - }, - { - to: '/docs/containers/images/building-an-image', - from: ['/docs/working-with-containers/images/building-an-image', '/docs/getting-started/building-an-image'], - }, - { - to: '/docs/containers/images/pushing-an-image-to-a-registry', - from: [ - '/docs/working-with-containers/images/pushing-an-image-to-a-registry', - '/docs/getting-started/pushing-an-image-to-a-registry', - ], - }, - { - to: '/docs/containers/images/pulling-an-image', - from: ['/docs/working-with-containers/images/pulling-an-image', '/docs/getting-started/pulling-an-image'], - }, - { - to: '/docs/containers/starting-a-container', - from: ['/docs/working-with-containers/starting-a-container', '/docs/getting-started/starting-a-container'], - }, - { - to: '/docs/containers/creating-a-pod', - from: ['/docs/working-with-containers/creating-a-pod', '/docs/getting-started/creating-a-pod'], - }, - { - to: '/docs/podman/setting-podman-machine-default-connection', - from: [ - '/docs/working-with-containers/switching-podman-machine-default-connection', - '/docs/getting-started/switching-podman-machine-default-connection', - ], - }, - { - to: '/docs/openshift', - from: '/docs/kubernetes/openshift', - }, - { - to: '/docs/extensions/developing', - from: [ - '/docs/extensions/write/', - '/docs/extensions/write/onboarding-workflow', - '/docs/extensions/write/when-clause-context', - '/docs/extensions/write/adding-icons', - ], - }, - { - to: '/', - from: '/core-values', - }, - ], - }, - ], - [ - '@docusaurus/plugin-content-docs', - { - id: 'api', - path: 'api', - routeBasePath: 'api', - sidebarPath: resolve('./sidebars-api.js'), - }, - ], - [ - '@docusaurus/plugin-content-docs', - { - id: 'tutorial', - path: 'tutorial', - routeBasePath: 'tutorial', - }, - ], - './src/plugins/github-metadata-plugin.ts', - [ - 'docusaurus-plugin-typedoc', - { - id: 'api', - plugin: ['typedoc-plugin-markdown'], - entryPoints: [resolve('../packages/extension-api/src/extension-api.d.ts')], - out: 'api', - hideBreadcrumbs: true, - readme: 'none', - tsconfig: resolve('../packages/extension-api/tsconfig.json'), - hideGenerator: true, - }, - ], - // Custom Storybook integration - [ - Storybook, - { - id: 'storybook-docusaurus-integration', - output: 'src/pages/storybook', - storybookStatic: '../storybook/storybook-static', - }, - ], - ], - presets: [ - [ - 'classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - ({ - docs: { - sidebarCollapsed: false, - sidebarPath: require.resolve('./sidebars.js'), - editUrl: 'https://github.com/podman-desktop/podman-desktop/tree/main/website', - }, - blog: { - blogTitle: 'Podman Desktop blog!', - blogDescription: 'Discover articles about Podman Desktop', - postsPerPage: 'ALL', - blogSidebarTitle: 'All blog posts', - blogSidebarCount: 'ALL', - feedOptions: { - type: 'all', - copyright: `Copyright © ${new Date().getFullYear()} Podman Desktop`, - }, - }, - theme: { - customCss: require.resolve('./src/css/custom.css'), - }, - }), - ], - ], - - clientModules: [require.resolve('./src/scripts/fontAwesomeIcons.ts')], - - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - ({ - colorMode: { - defaultMode: 'dark', - disableSwitch: false, - respectPrefersColorScheme: true, - }, - docs: { - sidebar: { - hideable: true, - }, - }, - navbar: { - title, - logo: { - alt: 'Podman Desktop Logo', - src: 'img/logo.svg', - height: '56', - }, - items: [ - { - type: 'docSidebar', - sidebarId: 'mySidebar', - position: 'left', - label: 'Documentation', - }, - { - type: 'custom-telemetryLink', - position: 'left', - to: '/downloads', - eventPath: '/download', - eventTitle: 'navigation-download', - }, - { to: '/community', label: 'Community', position: 'left' }, - { - type: 'dropdown', - label: 'Resources', - position: 'left', - items: [ - { to: '/features', label: 'Features' }, - { to: '/tutorial', label: 'Tutorials' }, - { to: '/extend', label: 'Extend' }, - ], - }, - { to: '/blog', label: 'Blog', position: 'left' }, - { - type: 'custom-githubStarsButton', - position: 'right', - }, - { - type: 'custom-downloadButton', - position: 'right', - }, - ], - }, - footer: { - links: [ - { - title: 'Documentation', - items: [ - { - label: 'Installing Podman Desktop', - to: '/docs/installation', - }, - { - label: 'Migrating from Docker', - to: '/docs/migrating-from-docker', - }, - { - label: 'Working with Kubernetes', - to: '/docs/kubernetes', - }, - { - label: 'Troubleshooting', - to: '/docs/troubleshooting', - }, - ], - }, - { - title: 'Links', - items: [ - { - label: 'GitHub', - href: 'https://github.com/podman-desktop/podman-desktop', - }, - { - label: 'Chat (bridged): #podman-desktop on Discord', - href: 'https://discord.com/invite/x5GzFF6QH4', - }, - { - label: 'Other ways to Communicate', - href: 'https://github.com/podman-desktop/podman-desktop#communication', - }, - { - label: 'Current Sprint', - href: 'https://github.com/orgs/podman-desktop/projects/4/views/3', - }, - ], - }, - ], - copyright: ` -
    -
    - Cloud Native Computing Foundation - -
    -
    -

    We are a Cloud Native Computing Foundation sandbox project.

    -

    © Copyright Podman Desktop Contributors ${new Date().getFullYear()}. © ${new Date().getFullYear()} The Linux Foundation. All rights reserved.

    -

    The Linux Foundation has registered trademarks and uses trademarks. - For a list of trademarks of The Linux Foundation, please see our - Trademark Usage page.

    -
    -
    - `, - }, - prism: { - theme: lightCodeTheme, - darkTheme: darkCodeTheme, - additionalLanguages: ['docker', 'shell-session', 'bash', 'json'], - }, - algolia: { - // The application ID provided by Algolia - appId: 'MR01ANKQ9S', - - // Public API key: it is safe to commit it - apiKey: '20bda7620dbcebd6a354840b4f92ac8e', - - // The index name to query - indexName: 'podman-desktop', - - // Optional - contextualSearch: true, - - // Optional - searchPagePath: 'search', - }, - goatcounter: { - code: 'podman-desktop-website', - }, - image: 'img/banner_podman-desktop.png', - }), -}; - -module.exports = config; diff --git a/website/package.json b/website/package.json deleted file mode 100644 index c6286597ab..0000000000 --- a/website/package.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "docs", - "version": "0.0.0", - "private": true, - "scripts": { - "docusaurus": "docusaurus", - "start": "docusaurus start", - "build": "docusaurus build", - "swizzle": "docusaurus swizzle", - "deploy": "docusaurus deploy", - "clear": "docusaurus clear", - "serve": "docusaurus serve", - "write-translations": "docusaurus write-translations", - "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc", - "format:check": "biome format && prettier --cache --check '**/*.md'", - "format:fix": "biome format --write && prettier --cache --write --check '**/*.md'", - "markdownlint:check": "markdownlint-cli2 --config ../.markdownlint.yaml \"**/*.md\" \"#node_modules\" \"#api\" ", - "markdownlint:fix": "markdownlint-cli2 --fix --config ../.markdownlint.yaml \"**/*.md\" \"#node_modules\"", - "lint:check": "cd .. && eslint --cache --cache-location website/.eslintcache website --ext js,ts,tsx", - "lint:fix": "cd .. && eslint --cache --cache-location website/.eslintcache website --fix --ext js,ts,tsx", - "lint:clean": "rimraf .eslintcache" - }, - "dependencies": { - "@docusaurus/core": "^3.7.0", - "@docusaurus/plugin-client-redirects": "^3.7.0", - "@docusaurus/preset-classic": "^3.7.0", - "@docusaurus/theme-common": "^3.7.0", - "@docusaurus/theme-mermaid": "^3.7.0", - "@fortawesome/fontawesome-svg-core": "^7.0.0", - "@fortawesome/free-brands-svg-icons": "^7.0.0", - "@fortawesome/free-solid-svg-icons": "^7.0.0", - "@fortawesome/react-fontawesome": "^0.2.3", - "@mdx-js/react": "^3.1.0", - "clsx": "^2.1.1", - "docusaurus-plugin-goatcounter": "^4.0.0", - "docusaurus-plugin-typedoc": "^1.3.0", - "prism-react-renderer": "^2.4.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-player": "^3.3.1" - }, - "devDependencies": { - "@docusaurus/module-type-aliases": "^3.7.0", - "@docusaurus/tsconfig": "^3.7.0", - "@docusaurus/types": "^3.7.0", - "@octokit/rest": "^22.0.0", - "@tailwindcss/postcss": "^4.1.11", - "markdownlint": "^0.38.0", - "markdownlint-cli2": "^0.18.1", - "markdownlint-cli2-formatter-summarize": "^0.0.7", - "msw": "^2.10.4", - "postcss": "^8.5.6", - "tailwindcss": "^4.1.11", - "typedoc": "^0.28.3", - "typedoc-plugin-markdown": "^4.6.2", - "typescript": "^5.8.3", - "vitest": "^3.2.4" - }, - "browserslist": { - "production": [ - ">0.5%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - } -} diff --git a/website/release-notes-parser.spec.ts b/website/release-notes-parser.spec.ts deleted file mode 100644 index e6d67075c9..0000000000 --- a/website/release-notes-parser.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 { beforeEach, expect, test, vi } from 'vitest'; - -import { createNotesFiles } from './release-notes-parser'; - -const mocks = vi.hoisted(() => ({ - existsSyncMock: vi.fn(), - mkdirMock: vi.fn(), - readFileMock: vi.fn(), - writeFileMock: vi.fn(), -})); - -vi.mock('node:fs', () => { - return { - default: { - existsSync: mocks.existsSyncMock, - promises: { - mkdir: mocks.mkdirMock, - readFile: mocks.readFileMock, - writeFile: mocks.writeFileMock, - }, - }, - }; -}); - -const notesInfo = - '--- title: test1\nslug: podman-desktop-release-test1\nimage: /img/blog/podman-desktop-release-test1/test1.png\n---'; - -const notesText = 'import a\ntest1 release title!\n![test1-release](img1)\nello world\n'; - -const notesPoints = - '- **line1**:line1 info\n- **line2**:line2 info\n- **line3**:line3 info\n- **line4**:line4 info\n- **line5**:line5 info\n'; - -const jsonResult = { - image: 'https://podman-desktop.io/img/blog/podman-desktop-release-test1/test1.png', - blog: 'https://podman-desktop.io/blog/podman-desktop-release-test1', - title: 'test1 release title!', - summary: '- **line1**:line1 info\n- **line2**:line2 info\n- **line3**:line3 info\n- **line4**:line4 info', -}; - -const defaultParseFrontMatterMock = vi.fn(); -const defaultParseFrontMatterResultMock = { - frontMatter: { - title: 'Release notes 1.0', - }, - content: '', -}; - -const params = { - filePath: 'file/test1file', - fileContent: notesInfo + notesText + notesPoints, - defaultParseFrontMatter: defaultParseFrontMatterMock, -}; - -beforeEach(() => { - vi.resetAllMocks(); - mocks.readFileMock.mockReturnValue(notesInfo + notesText + notesPoints); - mocks.existsSyncMock.mockReturnValue(true); - defaultParseFrontMatterMock.mockResolvedValue(defaultParseFrontMatterResultMock); -}); - -test('create release-notes directory when it does not exist', async () => { - mocks.existsSyncMock.mockReturnValue(false); - await createNotesFiles(params); - expect(mocks.mkdirMock).toHaveBeenCalled(); -}); - -test('Do not create release-nnotes directory when it exists', async () => { - await createNotesFiles(params); - expect(mocks.mkdirMock).not.toHaveBeenCalled(); -}); - -test('parse provided release notes as expected', async () => { - await createNotesFiles(params); - expect(mocks.writeFileMock).toBeCalledWith('./static/release-notes/1.0.json', JSON.stringify(jsonResult)); -}); diff --git a/website/release-notes-parser.ts b/website/release-notes-parser.ts deleted file mode 100644 index 4716fd0453..0000000000 --- a/website/release-notes-parser.ts +++ /dev/null @@ -1,80 +0,0 @@ -/********************************************************************** - * Copyright (C) 2024 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 fs from 'node:fs'; - -import type { - DefaultParseFrontMatter, - ParseFrontMatterParams, - ParseFrontMatterResult, -} from '@docusaurus/types/src/config'; - -export async function createNotesFiles( - params: ParseFrontMatterParams & { - defaultParseFrontMatter: DefaultParseFrontMatter; - }, -): Promise { - const result = await params.defaultParseFrontMatter(params); - // eslint-disable-next-line sonarjs/slow-regex - const versionRegex = /\d+\.\d+/; - if ( - result.frontMatter.title && - /[Rr]elease/.exec(String(result.frontMatter.title)) && - versionRegex.exec(String(result.frontMatter.title)) - ) { - const versionMatch = versionRegex.exec(String(result.frontMatter.title)) ?? []; - const version = versionMatch ? versionMatch[0] : ''; - if (version) { - const folderName = './static/release-notes'; - const fileContent = await fs.promises.readFile(params.filePath, { encoding: 'utf-8' }); - const resultText = fileContent.split('---', 3); - - // get release image url - const imagePath = /image: (.+)\n/.exec(resultText[1]); - const imageName = imagePath ? imagePath[1] : ''; - const imageUrl = imageName ? `https://podman-desktop.io${imageName}` : ''; - const pageName = /slug: (.+)\n/.exec(resultText[1]); - const blogName = pageName ? pageName[1] : `podman-desktop-release-${version}`; - const blogUrl = `https://podman-desktop.io/blog/${blogName}`; - - // get summary part of release notes - const text = resultText[2] - .replace(/!\[.+\)\n/, '') // remove image link - .replace(/\[Click here to download it\]\(.+\)!/, '') //remove download new version - .replace(/\n(\n)+/g, '\n') // change all multi-newlines chars to one newline char - .split('\n'); - - const summary = text.filter(line => line.includes('- **')); // all summary bullet points start with "- **" - const summaryText = summary.slice(0, 4).join('\n'); // limit the number of bullet points to 4 - const titleText = text.filter(line => !line.includes('import') && line)[0]; - - const jsonInput = { image: imageUrl, blog: blogUrl, title: titleText, summary: summaryText }; - - if (!fs.existsSync(folderName)) { - try { - await fs.promises.mkdir(folderName); - } catch (error) { - // directory already exists - } - } - - await fs.promises.writeFile(`${folderName}/${version}.json`, JSON.stringify(jsonInput)); - } - } - return result; -} diff --git a/website/sidebars-api.js b/website/sidebars-api.js deleted file mode 100644 index a5f3a9528e..0000000000 --- a/website/sidebars-api.js +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 - ***********************************************************************/ - -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { - typedocSidebar: [ - { - type: 'category', - label: 'Podman-Desktop API', - link: { - type: 'doc', - id: 'index', - }, - items: require('./api/typedoc-sidebar.cjs'), - }, - ], -}; -module.exports = sidebars; diff --git a/website/sidebars.js b/website/sidebars.js deleted file mode 100644 index ed5db20359..0000000000 --- a/website/sidebars.js +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************** - * Copyright (C) 2023-2024 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 - ***********************************************************************/ - -// @ts-check - -/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ -const sidebars = { - // Generate sidebar from the docs folder (or versioned_docs/) - mySidebar: [ - { - type: 'autogenerated', - dirName: '.', - }, - ], -}; - -module.exports = sidebars; diff --git a/website/src/components/CNCFCommunityBanner.tsx b/website/src/components/CNCFCommunityBanner.tsx deleted file mode 100644 index 70b36c7e79..0000000000 --- a/website/src/components/CNCFCommunityBanner.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Link from '@docusaurus/Link'; - -export function CNCFCommunityBanner(): JSX.Element { - return ( -
    -
    -
    -

    - A Community-Driven Project Designed for Developers -

    -

    - Built by a community of developers and shaped by user feedback, Podman Desktop offers a vendor-neutral - platform that empowers Containers and Kubernetes workflows without lock-in. Trusted as part of the CNCF, it - delivers flexibility, reliability, and innovation tailored for developers. -

    - - {/* CTA button with floating container icons inside */} -
    -
    - {/* Floating containers around the button */} - Left Container - Right Container 1 - Right Container 2 - Right Container 3 - - {/* CTA Link */} -
    - - Interested to join the tribe? - -
    -
    -
    - - {/* CNCF Logo */} -
    - Cloud Native Computing Foundation - Cloud Native Computing Foundation -
    -
    -
    -
    - ); -} diff --git a/website/src/components/CommunityBanner.tsx b/website/src/components/CommunityBanner.tsx deleted file mode 100644 index 7901091da6..0000000000 --- a/website/src/components/CommunityBanner.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; - -function Banner(): JSX.Element { - return ( -
    -
    - Large box - Small box - Join us for the{' '} - - Next Podman Desktop Community Meeting - - Large box -
    -
    - ); -} - -export default Banner; diff --git a/website/src/components/DownloadButton.tsx b/website/src/components/DownloadButton.tsx deleted file mode 100644 index eb3b10ab70..0000000000 --- a/website/src/components/DownloadButton.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import Link from '@docusaurus/Link'; -import type { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { faApple, faLinux, faWindows } from '@fortawesome/free-brands-svg-icons'; -import { faDownload, faEllipsis } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; - -import { TelemetryLink } from '../components/TelemetryLink'; - -// Utility to detect platform -function getClientPlatform(): { - os: string; - url: string; - icon: IconProp; -} | null { - const ua = navigator.userAgent; - - if (ua.includes('Windows')) { - return { os: 'Windows', url: 'windows', icon: faWindows }; - } - if (ua.includes('Mac')) { - return { os: 'macOS', url: 'macos', icon: faApple }; - } - if (ua.includes('Linux')) { - return { os: 'Linux', url: 'linux', icon: faLinux }; - } - return null; -} - -export function MainDownloadButton(): JSX.Element { - const platform = getClientPlatform(); - - if (!platform) { - return ( -
    - - Download Page - -
    - ); - } - - const { os, url } = platform; - - return ( -
    - - Download Now - - - For {os} (browser detected) - -
    - ); -} - -// Same as MainDownloadButton but instead it's "Download" and has no fontawesome icon -export function HeaderDownloadButton(): JSX.Element { - const platform = getClientPlatform(); - - // If we cannot get the platform, make the link just go to downloads. - if (!platform) { - return ( -
    - - Download - -
    - ); - } - - const { url } = platform; - - return ( -
    - - Download - -
    - ); -} - -export function OtherDownloadLink(): JSX.Element { - const platforms = [ - { name: 'macOS', icon: faApple, url: '/downloads/macos' }, - { name: 'Linux', icon: faLinux, url: '/downloads/linux' }, - { name: 'Windows', icon: faWindows, url: '/downloads/windows' }, - { name: 'Other', icon: faEllipsis, url: '/downloads' }, - ]; - - return ( -
    - {platforms.map(({ name, icon, url }) => ( - - - - ))} -
    - ); -} - -export function DownloadClientLinks(): JSX.Element { - const platform = getClientPlatform(); - - return ( -
    - - {platform && } -
    - ); -} - -export function DownloadGenericLinks(): JSX.Element { - return ( -
    - - Download Page - -
    - ); -} diff --git a/website/src/components/ExpandableFAQ.tsx b/website/src/components/ExpandableFAQ.tsx deleted file mode 100644 index e4bba88cc8..0000000000 --- a/website/src/components/ExpandableFAQ.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; - -export type ExpandableFAQProps = { - title: string; - text: JSX.Element; -}; - -export const ExpandableFAQ = (props: ExpandableFAQProps): JSX.Element => { - const [expanded, setExpanded] = React.useState(false); - - return ( -
    - -
    - {expanded &&

    {props.text}

    } -
    - ); -}; diff --git a/website/src/components/GitHubStarsButton.tsx b/website/src/components/GitHubStarsButton.tsx deleted file mode 100644 index ba63c0f50a..0000000000 --- a/website/src/components/GitHubStarsButton.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { usePluginData } from '@docusaurus/useGlobalData'; -import { faGithub } from '@fortawesome/free-brands-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import type { GitHubMetadata } from '@site/src/plugins/github-metadata'; - -export function GitHubStarsButton(): JSX.Element { - const { stargazersCount } = usePluginData('docusaurus-plugin-github-metadata') as GitHubMetadata; - - return ( - - - Star - - {stargazersCount >= 1000 ? `${(stargazersCount / 1000).toFixed(1)}k` : `${stargazersCount}`} - - - ); -} diff --git a/website/src/components/GradientButton.tsx b/website/src/components/GradientButton.tsx deleted file mode 100644 index d72d7c84ef..0000000000 --- a/website/src/components/GradientButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Link from '@docusaurus/Link'; -import React from 'react'; - -interface GradientButtonProps { - readonly href: string; - readonly children: React.ReactNode; - readonly className?: string; - readonly solid?: boolean; -} - -// Creates a gradient button that can either be "solid" or "outlined" with a gradient border. -export default function GradientButton({ - href, - children, - className = '', - solid = false, -}: GradientButtonProps): JSX.Element { - const outerClasses = solid - ? 'w-fit z-10 relative' - : 'p-[2px] rounded border-gradient bg-gradient-to-r from-sky-500 to-purple-500 w-fit z-10 relative'; - - const innerClasses = - 'inline-block p-3 rounded font-semibold text-base transition-colors duration-200 ' + - (solid - ? 'bg-gradient-to-r from-sky-600 to-purple-600 text-white hover:opacity-90' - : 'bg-white dark:bg-charcoal-800 text-charcoal-300 dark:text-white hover:bg-purple-600 hover:text-white') + - ' ' + - className; - - return ( -
    - - {children} - -
    - ); -} diff --git a/website/src/components/GradientIcon.tsx b/website/src/components/GradientIcon.tsx deleted file mode 100644 index 2965dfa484..0000000000 --- a/website/src/components/GradientIcon.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'; -import React from 'react'; - -interface GradientIconProps { - readonly icon: IconDefinition; - readonly size?: number; - readonly className?: string; -} - -// By default we cannot "gradient" the icons because they are not SVGs, so we use a workaround -export default function GradientIcon({ icon, size = 12, className = '' }: GradientIconProps): JSX.Element { - return ( -
    - ); -} - -// The absolute minimal SVG renderer for solid icons, in order for us to "gradient" them. -function renderToSvg(icon: IconDefinition): string { - const [width, height, , , pathData] = icon.icon; - return ` - - - - `; -} diff --git a/website/src/components/GradientText.tsx b/website/src/components/GradientText.tsx deleted file mode 100644 index 98fb6c1c41..0000000000 --- a/website/src/components/GradientText.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -export type GradientTextProps = { - content: string; - colorFrom: string; - colorTo: string; - gradientAngle?: number; -}; - -export function getGradient(colorFrom: string, colorTo: string, gradientAngle?: number): string { - return `linear-gradient(${gradientAngle ?? 0}deg, ${colorFrom}, ${colorTo})`; -} - -export const GradientText = (props: GradientTextProps): JSX.Element => { - return ( - - {props.content} - - ); -}; diff --git a/website/src/components/ReadTheDocsButton.tsx b/website/src/components/ReadTheDocsButton.tsx deleted file mode 100644 index 774b1f34f8..0000000000 --- a/website/src/components/ReadTheDocsButton.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import Link from '@docusaurus/Link'; - -export function ReadTheDocsButton(props: Readonly<{ to: string }>): JSX.Element { - return ( -
    - - Read the Docs - -
    - ); -} diff --git a/website/src/components/TailWindThemeSelector/index.tsx b/website/src/components/TailWindThemeSelector/index.tsx deleted file mode 100644 index 87a23ea60f..0000000000 --- a/website/src/components/TailWindThemeSelector/index.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; -import React, { useEffect } from 'react'; - -function TailWindThemeSelector(): JSX.Element { - function updadeTailwindDarkTheme(): void { - if (!document?.documentElement) { - return; - } - - const html = document.documentElement; - - if (html.dataset?.theme === 'dark') { - html.classList.add('dark'); - setTimeout(() => { - html.classList.add('dark'); - }, 100); - } else { - html.classList.remove('dark'); - setTimeout(() => { - html.classList.remove('dark'); - }, 100); - } - } - useEffect(() => { - if (ExecutionEnvironment.canUseDOM) { - updadeTailwindDarkTheme(); - } - }, [ExecutionEnvironment.canUseDOM]); - - // monitor the attribute managed by docusaurus - useEffect(() => { - if (!ExecutionEnvironment.canUseDOM) { - return; - } - const mutationObserver = new MutationObserver(mutations => { - mutations.forEach(mutation => { - if ( - mutation.type === 'attributes' && - (mutation.attributeName === 'data-rh' || mutation.attributeName === 'data-theme') - ) { - updadeTailwindDarkTheme(); - } - }); - }); - mutationObserver.observe(document.documentElement, { - attributes: true, - childList: false, - subtree: false, - }); - return (): void => { - mutationObserver.disconnect(); - }; - }, [ExecutionEnvironment.canUseDOM]); - - return
    ; -} - -export default TailWindThemeSelector; diff --git a/website/src/components/TelemetryLink.tsx b/website/src/components/TelemetryLink.tsx deleted file mode 100644 index 03bb485768..0000000000 --- a/website/src/components/TelemetryLink.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import Link from '@docusaurus/Link'; -import type { ReactNode } from 'react'; - -type TelemetryLinkProps = { - to: string; - eventPath: string; - eventTitle: string; - className: string; - children?: ReactNode[]; -}; - -const sendGoatCounterEvent = (path: string, title: string): void => { - window.goatcounter?.count({ - path: path, - title: title, - event: true, - }); -}; - -export const TelemetryLink = (props: TelemetryLinkProps): JSX.Element => { - return ( - sendGoatCounterEvent(props.eventPath, props.eventTitle)}> - {props.children} - - ); -}; diff --git a/website/src/components/TestimonialCard.tsx b/website/src/components/TestimonialCard.tsx deleted file mode 100644 index 478d5d222b..0000000000 --- a/website/src/components/TestimonialCard.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { faCircleUser } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import React from 'react'; - -export type TestimonialCardProps = { - username: string; - text: string; - source?: string; - date?: string; - userImage?: string; -}; - -export const TestimonialCard = (props: TestimonialCardProps): JSX.Element => { - let separator = ''; - if (props.source && props.date) { - separator = ', '; - } - - const [showIcon, setShowIcon] = React.useState(false); - - return ( -
    - Quote symbol -
    -
    -
    - {props.userImage && !showIcon ? ( - setShowIcon(true)} - alt="user profile image" - className="w-13 h-13 rounded-full" - /> - ) : ( - - )} -
    -

    @{props.username}

    - {(props?.source ?? props?.date) && ( -

    - {props.source} {separator} {props.date} -

    - )} -
    -
    -
    -

    {props.text}

    -
    -
    -
    - ); -}; diff --git a/website/src/css/custom.css b/website/src/css/custom.css deleted file mode 100644 index 4c63897506..0000000000 --- a/website/src/css/custom.css +++ /dev/null @@ -1,240 +0,0 @@ -/** - * docusaurus uses Infima for CSS but here we load tailwind with - * 'important' configuration so default style will be Tailwind css - */ - -@import 'tailwindcss'; -@config '../../tailwind.config.js'; - -/* Manrope latin subset font */ -/* This font is a subset of Manrope from Google Fonts, optimized for Latin characters */ -@font-face { - font-family: 'Manrope'; - font-style: normal; - font-weight: normal; - font-display: swap; - src: url('../../static/fonts/manrope-subset-latin.woff2') format('woff2'); - /* The unicode-range property tells the browser which characters this font should be used for. - Instead of loading the entire font with all possible characters, we only load the ones we need: - - U+0000-00FF: Basic Latin (a-z, A-Z, numbers, basic punctuation) - U+0131: Turkish dotless i - U+0152-0153: Latin ligatures (Œ, œ) - U+02BB-02BC: Modifier letters - U+02C6, U+02DA, U+02DC: Diacritical marks - U+0304, U+0308, U+0329: Combining diacritical marks - U+2000-206F: General punctuation - U+20AC: Euro symbol (€) - U+2122: Trademark symbol (™) - U+2191, U+2193: Arrows (↑, ↓) - U+2212, U+2215: Mathematical operators (−, ∕) - U+FEFF: Zero-width no-break space - U+FFFD: Replacement character - - This optimization reduces the font file size by ~70% while maintaining all characters needed for the website. */ - unicode-range: - U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, - U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* You can override the default Infima variables here. */ -:root { - --ifm-font-family-base: 'Manrope', sans-serif; - --ifm-color-primary: #6d28d9; - --ifm-color-primary-dark: #7c3aed; - --ifm-color-primary-darker: #5c21ba; - --ifm-color-primary-darkest: #4c1b99; - --ifm-color-primary-light: #7c3edd; - --ifm-color-primary-lighter: #8349df; - --ifm-color-primary-lightest: #996ae5; - --ifm-code-font-size: 95%; - --ifm-navbar-background-color: #fff; - --ifm-navbar-height: 85px; - --ifm-navbar-shadow: none; - --ifm-divider: #e2e2e3; - --ifm-footer-link-color: #464649; -} - -/* For readability concerns, you should choose a lighter palette in dark mode. */ -[data-theme='dark'] { - --ifm-color-primary: #a778fa; - --ifm-color-primary-dark: #7c3aed; - --ifm-color-primary-darker: #5c21ba; - --ifm-color-primary-darkest: #4c1b99; - --ifm-color-primary-light: #7c3edd; - --ifm-color-primary-lighter: #8349df; - --ifm-color-primary-lightest: #996ae5; - --ifm-navbar-background-color: #1b1b1d; - --ifm-divider: #2e2e32; - --ifm-footer-link-color: #9494b3; -} - -.docusaurus-highlight-code-line { - background-color: rgba(0, 0, 0, 0.1); - display: block; - margin: 0 calc(-1 * var(--ifm-pre-padding)); - padding: 0 var(--ifm-pre-padding); -} - -[data-theme='dark'] .docusaurus-highlight-code-line { - background-color: rgba(0, 0, 0, 0.3); -} - -svg.iconExternalLink_node_modules-\@docusaurus-theme-classic-lib-next-theme-IconExternalLink-styles-module { - display: inline; -} - -/* Add icon for github instead of label */ -.header-github-link:hover { - opacity: 0.6; -} -.header-github-link::before { - content: ''; - width: 24px; - height: 24px; - display: flex; - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; -} -[data-theme='dark'] .header-github-link::before { - background: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='white' d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; -} - -svg.darkToggleIcon_node_modules-\@docusaurus-theme-classic-lib-next-theme-ColorModeToggle-styles-module { - width: 16px; - color: #343e50; -} - -svg.lightToggleIcon_node_modules-\@docusaurus-theme-classic-lib-next-theme-ColorModeToggle-styles-module { - width: 16px; - color: #343e50; -} - -/* inline icons after the link name */ -li.footer__item a svg { - display: inline; -} - -.navbar__title { - font-size: 1.2em; - font-weight: 500; - padding-right: 1em; -} - -.navbar__logo img { - padding-right: 7px; -} - -.navbar__logo { - min-height: 56px; -} - -.navbar__item { - font-size: 0.9em; -} - -.pagination-nav { - display: none; -} - -/** - Using base font color for all links - */ -.menu__link { - color: var(--ifm-font-color-base) !important; -} - -/** - The first level of collapsable should have a left bar - */ -.theme-doc-sidebar-item-category-level-1 > .menu__list { - border-left: 1px solid var(--ifm-divider); - margin-left: var(--ifm-menu-link-padding-horizontal); - padding-left: 0px; -} - -/** - Decrease the size of the chevron icon from 2rem to 1.5 - */ -.menu__caret:before { - background: var(--ifm-menu-link-sublist-icon) 50% / 1.5rem 1.5rem; -} - -/** - Hovering an item should change its color and not create a gray bg - */ -.menu__link:hover, -.menu__list-item-collapsible { - color: var(--ifm-color-primary) !important; - background-color: transparent !important; -} - -/** - We should highlight the active item (only last child) - */ -.menu__link--active:not(.menu__link--sublist) { - color: var(--ifm-color-primary) !important; - background-color: transparent !important; -} - -@media (min-width: 1230px) { - .navbar__title { - font-size: 1.7em; - } - - .navbar__item { - font-size: 1em; - } -} - -.breadcrumbs { - display: flex; -} - -.breadcrumbs__item { - display: flex; - align-items: center; -} - -a { - color: var(--ifm-link-color); -} - -/* headings */ - -article .markdown h1 { - font-size: 1.5rem; - font-weight: bolder; -} - -nav h2, -article .markdown h2 { - font-size: 1.2rem; - font-weight: bolder; -} - -nav h3, -article .markdown h3 { - font-size: 1.1rem; - font-weight: bolder; -} - -/* lists */ -article .markdown ul, -article .markdown ol { - margin-block-start: 1em; - margin-block-end: 1em; - margin-inline-start: 0px; - margin-inline-end: 0px; - padding-inline-start: 40px; -} -article .markdown ul { - list-style-type: disc; -} -article .markdown ol { - list-style-type: decimal; -} -article .markdown ol ol { - list-style-type: lower-roman; -} diff --git a/website/src/pages/community/index.tsx b/website/src/pages/community/index.tsx deleted file mode 100644 index 90cf78bf0a..0000000000 --- a/website/src/pages/community/index.tsx +++ /dev/null @@ -1,190 +0,0 @@ -import useBaseUrl from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { faBluesky, faDiscord, faGithub, faLinkedin, faMastodon, faXTwitter } from '@fortawesome/free-brands-svg-icons'; -import { faCalendar, faCamera, faClock } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import CommunityBanner from '@site/src/components/CommunityBanner'; -import GradientButton from '@site/src/components/GradientButton'; -import GradientIcon from '@site/src/components/GradientIcon'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import Layout from '@theme/Layout'; -import React from 'react'; - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - - return ( - - - - -
    -
    - Podman Desktop Community Mascots -
    - -
    -

    Podman Desktop Community

    -

    - Welcome to the Podman Desktop Community! We're excited to have you here. This is the place to connect with - fellow developers, share your experiences, contribute to the project, and help shape the future of Podman - Desktop. -

    -
    - -
    -

    Why Join the Community?

    -

    Podman Desktop is powered by an amazing open-source community. By joining, you can:

    -
    -
    -

    Learn

    -

    Discover tips, tricks, and best practices for working with containers.

    -
    -
    -

    Contribute

    -

    Help build the project by reporting bugs, submitting code, or enhancing documentation.

    -
    -
    -

    Collaborate

    -

    Share your feedback and ideas to improve Podman Desktop.

    -
    -
    -

    Connect

    -

    Meet like-minded developers who are passionate about containers and open-source.

    -
    -
    -
    - -
    -

    Get Involved

    -

    Connect with the community through our official channels:

    -
    - {/* Row 1 */} -
    - - Join our Discord -
    -
    - - - GitHub discussions - -
    -
    - - Follow us on Bluesky -
    -
    - - Follow us on X -
    - - {/* Row 2: Center the last two */} -
    -
    - - - Connect on LinkedIn - -
    -
    - - Follow us on Mastodon -
    -
    -
    -
    - -
    -

    Contribute to the Project

    -

    We welcome contributions of all kinds! Here's how you can get started:

    -
    -
    - - Contribute code - -

    - Tackle open issues, add features, or improve performance by contributing code to our GitHub repository. -

    -
    -
    - - Documentation - -

    - Enhance our docs by clarifying instructions, adding guides, or fixing mistakes for the benefit of all - users. -

    -
    -
    - - Report issues - -

    - Help us improve by reporting bugs or suggesting features through our issue tracker. -

    -
    -
    - - Tutorials - -

    - Create or update tutorials to help users get started or explore advanced Podman Desktop features. -

    -
    -
    - - Presentations and demos - -

    - Share or watch community presentations and demos to learn and inspire others with your workflows. -

    -
    -
    -
    - -
    -

    Attend Community Events

    -

    Join us for upcoming meetups, webinars, and conferences.

    -
    -
    -

    Latest meeting

    -
    - -
    -
    - - Watch recording - -
    -
    -
    -

    Join community meeting

    -
    - - Every 4th Thursday of the month -
    -
    - - 9–10 am EST -
    -
    - - Join Us! - -
    -
    -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/downloads/index.tsx b/website/src/pages/downloads/index.tsx deleted file mode 100644 index 523e94d291..0000000000 --- a/website/src/pages/downloads/index.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import { LinuxDownloads } from '@site/src/pages/downloads/linux'; -import { MacOSDownloads } from '@site/src/pages/downloads/macos'; -import { WindowsDownloads } from '@site/src/pages/downloads/windows'; -import Layout from '@theme/Layout'; -import React from 'react'; - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - return ( - -
    -
    -
    -

    - Downloads -

    -
    -
    - - - - -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/downloads/linux.tsx b/website/src/pages/downloads/linux.tsx deleted file mode 100644 index 69b4fdc80c..0000000000 --- a/website/src/pages/downloads/linux.tsx +++ /dev/null @@ -1,140 +0,0 @@ -/********************************************************************** - * 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 useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { usePluginData } from '@docusaurus/useGlobalData'; -import { faLinux } from '@fortawesome/free-brands-svg-icons'; -import { faDownload, faPaste, faTerminal } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import { TelemetryLink } from '@site/src/components/TelemetryLink'; -import type { GitHubMetadata } from '@site/src/plugins/github-metadata'; -import Layout from '@theme/Layout'; -import React from 'react'; - -export function LinuxDownloads(): JSX.Element { - const { - latestRelease: { linux, version }, - } = usePluginData('docusaurus-plugin-github-metadata') as GitHubMetadata; - - const copyFlathubInstructions = async (): Promise => { - await navigator.clipboard.writeText('flatpak install flathub io.podman_desktop.PodmanDesktop'); - }; - - return ( -
    - -

    Linux

    -
    -
    -

    Podman Desktop for Linux

    -
    - - - Download Now - - - Linux *.flatpak, version {version} - -
    -
    -
    Other Linux downloads:
    - - - AMD64 binary (tar.gz) - - - - ARM64 binary (tar.gz) - -
    -
    -
    -

    - Using{' '} - - Flathub - {' '} - ? Install in one command: -

    -
    -

    - - - -

    -
    -
    - - flatpak install flathub io.podman_desktop.PodmanDesktop -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - ); -} - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - return ( - - -
    -
    -

    - Linux Downloads -

    -
    - -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/downloads/macos.tsx b/website/src/pages/downloads/macos.tsx deleted file mode 100644 index c0f72e109c..0000000000 --- a/website/src/pages/downloads/macos.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/********************************************************************** - * 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 useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { usePluginData } from '@docusaurus/useGlobalData'; -import { faApple } from '@fortawesome/free-brands-svg-icons'; -import { faBeer, faDownload, faPaste, faTerminal } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import { TelemetryLink } from '@site/src/components/TelemetryLink'; -import type { GitHubMetadata } from '@site/src/plugins/github-metadata'; -import Layout from '@theme/Layout'; -import React from 'react'; - -export function MacOSDownloads(): JSX.Element { - const { - latestRelease: { macos, version }, - } = usePluginData('docusaurus-plugin-github-metadata') as GitHubMetadata; - - const copyBrewInstructions = async (): Promise => { - await navigator.clipboard.writeText('brew install podman-desktop'); - }; - - return ( -
    - -

    macOS

    -
    -
    -

    Podman Desktop for macOS

    -
    - - - Download Now - - - Universal *.dmg, version {version} - -
    -
    -
    Other macOS downloads:
    - - - Intel - - - - Apple silicon - -
    -
    -
    Installer for restricted environments:
    -
    - - - Intel - - - - Apple silicon - -
    -
    - -
    -
    -

    - Using Brew? Install in one command: -

    -
    -

    - -

    -
    -

    - - brew install podman-desktop - -

    -
    -
    -
    -
    -
    -
    -
    - ); -} - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - - return ( - - -
    -
    -

    - macOS Downloads -

    -
    - -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/downloads/windows.tsx b/website/src/pages/downloads/windows.tsx deleted file mode 100644 index d0dc5136fa..0000000000 --- a/website/src/pages/downloads/windows.tsx +++ /dev/null @@ -1,193 +0,0 @@ -/********************************************************************** - * 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 Link from '@docusaurus/Link'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { usePluginData } from '@docusaurus/useGlobalData'; -import { faMicrosoft, faWindows } from '@fortawesome/free-brands-svg-icons'; -import { faDownload, faPaste, faTerminal } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import { TelemetryLink } from '@site/src/components/TelemetryLink'; -import type { GitHubMetadata } from '@site/src/plugins/github-metadata'; -import Layout from '@theme/Layout'; -import React from 'react'; - -export function WindowsDownloads(): JSX.Element { - const { - latestRelease: { windows, version }, - } = usePluginData('docusaurus-plugin-github-metadata') as GitHubMetadata; - - const copyCliInstructions = async (): Promise => { - await navigator.clipboard.writeText('winget install -e --id RedHat.Podman-Desktop'); - }; - - return ( -
    - -

    Windows

    -
    -
    -

    Podman Desktop for Windows

    -
    - - - Download Now - - - Windows installer x64, version {version} - -
    -
    -
    Other Windows downloads:
    - -
    -
    Installer:
    -
    - - - x64 - - - - arm64 - -
    -
    - -
    -
    Portable binary:
    -
    - - - x64 - - - - arm64 - -
    -
    - -
    -
    Installer for restricted environments:
    -
    - - - x64 - - - - arm64 - -
    -
    - - - - Package Managers Guide - -
    -
    -
    -

    - Using winget? Install in one command: -

    -
    -

    - -

    -
    -
    - - winget install -e --id RedHat.Podman-Desktop -
    -
    - -
    -
    -
    -
    -
    -
    -
    -
    - ); -} - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - - return ( - - -
    -
    -

    - Windows Downloads -

    -
    - -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/extend/index.tsx b/website/src/pages/extend/index.tsx deleted file mode 100644 index a93dec1f16..0000000000 --- a/website/src/pages/extend/index.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import useBaseUrl from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import { faBook, faCertificate, faCloudArrowDown, faGears, faRocket } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import Layout from '@theme/Layout'; -import ThemedImage from '@theme/ThemedImage'; -import React from 'react'; - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - - return ( - - - -
    -
    -
    -

    - Extensibility Documentation and Resources -

    -

    Explore our comprehensive guides on extending Podman Desktop:

    - - -

    - What are Extensions? -

    -

    - Extensions in Podman Desktop allow users to enhance and customize their container management experience. - They enable new functionalities such as: -

    -
      -
    • Adding support for different container engines like Docker, Lima, or Kubernetes.
    • -
    • Providing command-line integrations with tools like Helm or Kubectl.
    • -
    • Introducing UI elements such as custom actions, badges, views, and workflows.
    • -
    • Automating tasks and integrating with external services.
    • -
    - -

    - Getting Started with Extensions -

    -

    - You can easily extend the capabilities of Podman Desktop by installing or developing extensions. Here’s - how: -

    -

    1. Install Extensions

    -

    - Browse the{' '} - - Extensions Catalog - {' '} - to find and install pre-built extensions. -

    - -

    2. Develop Your Own Extension

    -

    - Want to create something new? Check out our guide on{' '} - - Developing an Extension - {' '} - to learn how to build and contribute your own. -

    - -

    3. Publish Your Extension

    -

    - Share your extension with the community by publishing it to the catalog. Learn more about the process{' '} - - here - - . -

    - -

    - Visualizing Extension Capabilities -

    - - -

    - Extend Podman Desktop with Docker Desktop Extensions -

    -

    - Podman Desktop can also leverage Docker Desktop UI extensions through a built-in wrapper that intercepts - API calls, making integration seamless. Use Docker Desktop extensions to further enhance Podman Desktop’s - capabilities. -

    - -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/extensions/ai-lab/index.tsx b/website/src/pages/extensions/ai-lab/index.tsx deleted file mode 100644 index 184ba12529..0000000000 --- a/website/src/pages/extensions/ai-lab/index.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import type { IconDefinition } from '@fortawesome/fontawesome-svg-core'; -import { - faArrowRight, - faFileLines, - faMagnifyingGlassChart, - faMessage, - faMicrophone, - faRocket, -} from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { getGradient, GradientText } from '@site/src/components/GradientText'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import Layout from '@theme/Layout'; -import React from 'react'; - -interface Recipe { - id: string; - name: string; - content: string; - icon: IconDefinition; - colorFrom: string; - colorTo: string; - iconColor: string; -} - -const RECIPES: Recipe[] = [ - { - id: 'chatbot', - name: 'LLM Chatbot', - content: 'Experiment prompting locally', - icon: faMessage, - colorFrom: '#9bfacc', - colorTo: '#fff', - iconColor: '#36f8a7', - }, - { - id: 'summarizer', - name: 'Text Summarizer', - content: 'Get insight on large text corpus', - icon: faFileLines, - colorFrom: '#9cf4fd', - colorTo: '#fff', - iconColor: '#2fecfc', - }, - { - id: 'speech-to-text', - name: 'Speech to text', - content: 'Get transcript from any audio file', - icon: faMicrophone, - colorFrom: '#f4acce', - colorTo: '#fff', - iconColor: '#e54b95', - }, - { - id: 'obj-detection', - name: 'Object Detection', - content: 'Get started with computer vision', - icon: faMagnifyingGlassChart, - colorFrom: '#fbe696', - colorTo: '#fff', - iconColor: '#fccf44', - }, -]; - -export default function Home(): JSX.Element { - const { siteConfig } = useDocusaurusContext(); - - return ( - - - -
    -
    -
    - {/* header section */} -

    - Run{' '} - {' '} - locally with Podman AI Lab -

    -

    - Podman AI Lab is the easiest way to work with Large Language Models (LLMs) on your local developer - workstation. Find a catalog of recipes, leverage a curated list of open source models, experiment and - compare the models. Get ahead of the curve and take your development to new heights wth Podman AI Lab! -

    - {/* buttons section */} -
    - - - Get started - - - - Learn more on GitHub - - -
    - - {/* Application video */} -
    -
    - -
    -
    -
    - {/* Recipes sections */} -
    -

    - Experiment with free{' '} - recipes -

    -
    - {RECIPES.map(recipe => ( -
    -
    - -
    {recipe.name}
    -
    -
    {recipe.content}
    -
    - ))} -
    -

    - All built to run locally on your laptop -

    -
    - {/* Videos sections */} -
    -
    -
    -
    - -
    -
    -
    Recipes Catalog
    -
    - Collection of pre-built solutions for various AI use cases and problem domains. Each recipe includes - detailed explanations and sample applications that can be run using different large language models - (LLMs). Get inspired by use cases and learn how to integrate AI in an optimal way. Recipes are - kubernetes ready. -
    -
    -
    -
    -
    -
    Models Catalog
    -
    - Curated list of open source large language models available out of the box. Check license and - required resources. Import your own models. -
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    Model Serving
    -
    - Run models locally with an inference server. Get OpenAI compatible endpoints, use code snippets and - start integrating AI in your application. -
    -
    -
    -
    -
    -
    Playground Environments
    -
    - Experiment with large language models with a dedicated UI. Configure the models settings, system - prompts to test and validate your prompt workflows. Compare behavior of different models. -
    -
    -
    - -
    -
    -
    -
    -
    -
    -
    - ); -} diff --git a/website/src/pages/extensions/index.tsx b/website/src/pages/extensions/index.tsx deleted file mode 100644 index 44cdd02c87..0000000000 --- a/website/src/pages/extensions/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import TailWindThemeSelector from '@site/src/components/TailWindThemeSelector'; -import Layout from '@theme/Layout'; - -function ExtensionListFromIframe(): JSX.Element { - return ( -