Skip to content

Commit

Permalink
chore: podman install ci tests by container matrix
Browse files Browse the repository at this point in the history
  • Loading branch information
jgresham committed May 6, 2024
1 parent a49819a commit 3b59a2a
Show file tree
Hide file tree
Showing 17 changed files with 246 additions and 84 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/podman-install-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Podman Install on Linux Distros
on:
push:
# branches: [ main ]
workflow_dispatch:

jobs:
container-test-job:
runs-on: ubuntu-latest
strategy:
matrix:
container: [
"ubuntu:24",
"ubuntu:23",
"ubuntu:22",
"debian:12",
"fedora:40",
"fedora:39"
]

container:
image: ${{ matrix.container }}
env:
NODE_ENV: test
steps:
- name: print os version details
run: cat /etc/os-release

- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20

- name: 🧱 Install Dependencies
run: |
npm ci
- name: 🧱 Run tests
run: |
npm run testCi
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ arch options include: ia32, x64, armv7l, arm64, mips64el, universal

Unit tests with `npm run test`

### Frontend (React) tests
`npm run test -- --config vite.renderer.config.ts --dir src/__tests__/react`

### Backend (Nodejs) tests
`npm run test -- --config vite.main.config.ts --dir src/__tests__/node`

### End-to-end (e2e) tests

For e2e tests, we use webdriver and an electron plugin to automate testing.
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
"lint": "biome lint ./src",
"safeFix": "biome check --apply ./src",
"unsafeFix": "biome check --apply-unsafe ./src",
"test": "vitest --run",
"testWatch": "vitest --watch",
"coverage": "vitest run --coverage",
"test": "npm run testFrontend && npm run testBackend",
"testFrontend": "vitest run --config vite.renderer.config.ts --dir src/__tests__/react",
"testBackend": "vitest run --config vite.main.config.ts --dir src/__tests__/node",
"testCi": "vitest run --dir src/__tests__/ci --config vite.main.config.ts",
"coverage": "npm run testFrontend -- --coverage && npm run testBackend -- --coverage",
"prepare": "husky",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
Expand Down
125 changes: 125 additions & 0 deletions src/__tests__/ci/installPodman.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { execSync } from 'node:child_process';
import { beforeAll, describe, expect, it, vi } from 'vitest';
import { isLinux } from '../../main/platform.js';
import {
PODMAN_MIN_VERSION,
getInstalledPodmanVersion,
} from '../../main/podman/install/install.js';
// import installPodman from '../../main/podman/install/install.js';
import installOnLinux from '../../main/podman/install/installOnLinux.js';
import uninstallOnLinux from '../../main/podman/uninstall/uninstallOnLinux.js';

beforeAll(async () => {
// called once before all tests run
if (!isLinux()) {
throw new Error('This test suite is only for Linux');
}

vi.mock('../../main/logger.js', () => {
return {
default: {
info: vi.fn((msg: string) => {
console.log(msg);
}),
error: vi.fn((msg: string) => {
console.log(msg);
}),
},
autoUpdateLogger: vi.fn(),
};
});

vi.mock('../../main/execHelper.js', () => {
return {
execAwait: vi.fn((command: string) => {
return execSync(command);
}),
};
});

vi.mock('electron', () => {
return {
app: {
getAppPath: vi.fn(() => {
return '/we/out/here/nice-node';
}),
getPath: vi.fn((pathName: string) => {
return pathName;
}),
isPackaged: false,
on: vi.fn(),
getName: () => 'NiceNode',
},
powerMonitor: {
isOnBatteryPower: vi.fn(),
},
autoUpdater: vi.fn(),
nativeTheme: {
on: vi.fn(),
},
};
});
vi.mock('@sentry/electron/main', () => {
return {
init: vi.fn(() => {
return {};
}),
};
});
vi.mock('../../main/i18nMain', () => {
return {
default: {
// Mock any other properties or methods if needed
getFixedT: vi.fn((x, y) => {
return {
t: (k: any) => k, // just return the key for simplicity
};
}),
},
// changeLanguage: vi.fn(),
// init: vi.fn(),
// use: vi.fn(() => i18nMock), // for chaining .use() calls
};
});
});
const versionRegex = /(\d+\.\d+\.\d+)/;

describe.sequential('Install and Uninstall Podman', () => {
it('If pre-installed, uninstall to test install next', async () => {
const versionBefore = await getInstalledPodmanVersion();
if (versionBefore !== undefined) {
expect(versionBefore).toMatch(versionRegex);
const resultInstall = await uninstallOnLinux();
expect(resultInstall).toEqual(true);
const versionAfter = await getInstalledPodmanVersion();
expect(versionAfter).toBeUndefined();
}
});

it('Install is successful and returns true', async () => {
const versionBefore = await getInstalledPodmanVersion();
expect(versionBefore).toBeUndefined();
const resultInstall = await installOnLinux();
expect(resultInstall).toEqual(true);
const versionAfter = await getInstalledPodmanVersion();
expect(versionAfter).toMatch(versionRegex);
expect(versionAfter > PODMAN_MIN_VERSION).toBeTruthy();
}, 20000); // 20 seconds timeout

it('Uninstall is successful and returns true', async () => {
const versionBefore = await getInstalledPodmanVersion();
expect(versionBefore).toMatch(versionRegex);
expect(versionBefore > PODMAN_MIN_VERSION).toBeTruthy();
// expect(versionBefore).toEqual('4.3.1');
const resultInstall = await uninstallOnLinux();
expect(resultInstall).toEqual(true);
const versionAfter = await getInstalledPodmanVersion();
expect(versionAfter).toBeUndefined();
});
// check not installed before
// version?
// it('Is successful and returns true', async () => {
// const resultInstall = await installPodman();
// expect(resultInstall).toEqual(true);
// });
});
4 changes: 4 additions & 0 deletions src/main/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,5 +230,9 @@ export const getAssetsFolder = (): string => {
return path.resolve(__dirname, '..', '..', 'assets');
}

if (process.env.NODE_ENV === 'test') {
return 'assets';
}

return path.resolve((process as any).resourcesPath, 'assets');
};
19 changes: 4 additions & 15 deletions src/main/podman/install/debianInstallScript.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
// Same script as on https://podman.io/docs/installation, except the 'sudo' is removed as
// sudo-prompt will not execute a command with sudo
export const script = `apt-get -y update -qq
apt-get -y install curl
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Unstable/Release.key \
| gpg --dearmor \
| tee /etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg > /dev/null
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg]\
https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/Debian_Unstable/ /" \
| tee /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list > /dev/null
apt-get -y update
apt-get -y upgrade
apt-get -y install podman`;
// Reference https://podman.io/docs/installation
export const script = `rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list
apt -y update -qq
apt install -y podman`;
2 changes: 1 addition & 1 deletion src/main/podman/install/install.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import installOnMac from './installOnMac';
import installOnWindows from './installOnWindows';

export const PODMAN_LATEST_VERSION = '5.0.2';
export const PODMAN_MIN_VERSION = '4.6.0';
export const PODMAN_MIN_VERSION = '4.3.0';

const installPodman = async (): Promise<any> => {
logger.info('Starting podman install...');
Expand Down
18 changes: 4 additions & 14 deletions src/main/podman/install/ubuntuInstallScript.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
// Same script as on https://podman.io/docs/installation, except the 'sudo' is removed as
// sudo-prompt will not execute a command with sudo
export const script = `apt-get -y update -qq
apt-get -y -qq install curl
mkdir -p /etc/apt/keyrings
curl -fsSL https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_22.04/Release.key \
| gpg --dearmor \
| tee /etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg > /dev/null
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/devel_kubic_libcontainers_unstable.gpg]\
https://download.opensuse.org/repositories/devel:kubic:libcontainers:unstable/xUbuntu_22.04/ /" \
| tee /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list > /dev/null
apt-get update -qq
apt-get -qq -y install podman`;
// Reference https://podman.io/docs/installation
export const script = `rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list
apt -y update -qq
apt install -y podman`;
2 changes: 2 additions & 0 deletions src/main/podman/uninstall/aptUninstallScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const script = `rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list
apt remove -y podman`;
1 change: 1 addition & 0 deletions src/main/podman/uninstall/dnfUninstallScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const script = 'dnf remove -y podman';
35 changes: 18 additions & 17 deletions src/main/podman/uninstall/uninstallOnLinux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import { reportEvent } from '../../events';
import { execAwait } from '../../execHelper';
// import { app } from 'electron';
import logger from '../../logger';
import {
type PackageManager,
findPackageManager,
} from '../../nn-auto-updater/findPackageManager.js';
import { script as aptUninstallScript } from './aptUninstallScript';
import { script as dnfUninstallScript } from './dnfUninstallScript';
import { script as yumUninstallScript } from './yumUninstallScript';

/**
* Uninstall podman by removing binaries and various configuration files
Expand All @@ -12,29 +19,23 @@ const uninstallOnLinux = async (): Promise<boolean | { error: string }> => {
// Returns /Users/<user> (Ex. /Users/johns)
// const userHome = app.getPath('home');

// todo: delete more?
const foldersToDelete = [
'/etc/apt/sources.list.d/devel:kubic:libcontainers:unstable.list',
// '/opt/podman',
// `${userHome}/.local/share/containers`,
// `${userHome}/.config/containers`,
// `${userHome}/.ssh/*podman*`,
// `${userHome}/.ssh/*nicenode*`,
// `/private/etc/paths.d/podman-pkg`,
// `/usr/local/podman`,
// `/Library/LaunchDaemons/*podman*`,
];
// 1. (applies to mac, also linux?) This can throw return an error if a file or folder doesn't exist.
// This is ok, because it will still delete the other folders that do exist.
// 2. Combine sudo commands so that user is only prompted for password once
const rmCommand = `
rm -rf ${foldersToDelete.join(' ')}
apt-get remove -y podman`;
const { stdout, stderr } = await execAwait(rmCommand, {
const pkgManager: PackageManager = await findPackageManager();
let uninstallScript = '';
if (pkgManager === 'dpkg') {
uninstallScript = aptUninstallScript;
} else if (pkgManager === 'dnf') {
uninstallScript = dnfUninstallScript;
} else if (pkgManager === 'yum') {
uninstallScript = yumUninstallScript;
}
const { stdout, stderr } = await execAwait(uninstallScript, {
log: true,
sudo: true,
});
logger.info(`${rmCommand} stdout, stderr ${stdout} ${stderr}`);
logger.info(`${uninstallScript} stdout, stderr ${stdout} ${stderr}`);
return true;
} catch (err: any) {
logger.error(err);
Expand Down
1 change: 1 addition & 0 deletions src/main/podman/uninstall/yumUninstallScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const script = 'yum remove -y podman';
24 changes: 13 additions & 11 deletions vite.base.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,20 @@ export function getDefineKeys(names: string[]) {

export function getBuildDefine(env: ConfigEnv<'build'>) {
const { command, forgeConfig } = env;
const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name!);
const defineKeys = getDefineKeys(names);
const define = Object.entries(defineKeys).reduce((acc, [name, keys]) => {
const { VITE_DEV_SERVER_URL, VITE_NAME } = keys;
const def = {
[VITE_DEV_SERVER_URL]: command === 'serve' ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined,
[VITE_NAME]: JSON.stringify(name),
};
return { ...acc, ...def };
}, {} as Record<string, any>);
if(forgeConfig?.renderer) {
const names = forgeConfig.renderer.filter(({ name }) => name != null).map(({ name }) => name!);
const defineKeys = getDefineKeys(names);
const define = Object.entries(defineKeys)?.reduce((acc, [name, keys]) => {
const { VITE_DEV_SERVER_URL, VITE_NAME } = keys;
const def = {
[VITE_DEV_SERVER_URL]: command === 'serve' ? JSON.stringify(process.env[VITE_DEV_SERVER_URL]) : undefined,
[VITE_NAME]: JSON.stringify(name),
};
return { ...acc, ...def };
}, {} as Record<string, any>);

return define;
return define;
}
}

export function pluginExposeRenderer(name: string): Plugin {
Expand Down
10 changes: 7 additions & 3 deletions vite.main.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ConfigEnv, UserConfig } from 'vite';
import type { ConfigEnv, Plugin, PluginOption, UserConfig } from 'vite';
import { defineConfig, mergeConfig } from 'vite';
import { getBuildConfig, getBuildDefine, external, pluginHotRestart } from './vite.base.config.js';

Expand All @@ -9,10 +9,14 @@ export default defineConfig((env) => {
const forgeEnv = env as ConfigEnv<'build'>;
const { forgeConfigSelf } = forgeEnv;
const define = getBuildDefine(forgeEnv);
const plugins: PluginOption[] = []
if(process.env.NODE_ENV !== 'test') {
plugins.push(pluginHotRestart('restart'))
}
const config: UserConfig = {
build: {
lib: {
entry: forgeConfigSelf.entry!,
entry: forgeConfigSelf?.entry,
fileName: () => '[name].js',
formats: ['es'],
},
Expand All @@ -23,7 +27,7 @@ export default defineConfig((env) => {
// main process static assets
// ./assets dir is copied to .vite/build dir
publicDir: './assets',
plugins: [pluginHotRestart('restart')],
plugins,
define,
resolve: {
// Load the Node.js entry.
Expand Down
Loading

0 comments on commit 3b59a2a

Please sign in to comment.