Merge pull request #161 from meienberger/fix/cleanup-before-install
feat: cleanup folder before install
This commit is contained in:
commit
73d369442a
9 changed files with 257 additions and 14 deletions
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -65,7 +65,6 @@ jobs:
|
|||
- name: Run tests
|
||||
run: pnpm -r test
|
||||
|
||||
- uses: codecov/codecov-action@v2
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/system-api/coverage/clover.xml,./packages/dashboard/coverage/clover.xml
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@ -1,6 +1,7 @@
|
|||
import path from 'path';
|
||||
const fs: {
|
||||
__createMockFiles: typeof createMockFiles;
|
||||
__resetAllMocks: typeof resetAllMocks;
|
||||
readFileSync: typeof readFileSync;
|
||||
existsSync: typeof existsSync;
|
||||
writeFileSync: typeof writeFileSync;
|
||||
|
@ -9,6 +10,7 @@ const fs: {
|
|||
readdirSync: typeof readdirSync;
|
||||
copyFileSync: typeof copyFileSync;
|
||||
copySync: typeof copyFileSync;
|
||||
createFileSync: typeof createFileSync;
|
||||
} = jest.genMockFromModule('fs-extra');
|
||||
|
||||
let mockFiles = Object.create(null);
|
||||
|
@ -45,12 +47,14 @@ const mkdirSync = (p: string) => {
|
|||
mockFiles[p] = Object.create(null);
|
||||
};
|
||||
|
||||
const rmSync = (p: string, options: { recursive: boolean }) => {
|
||||
if (options.recursive) {
|
||||
delete mockFiles[p];
|
||||
} else {
|
||||
delete mockFiles[p][Object.keys(mockFiles[p])[0]];
|
||||
const rmSync = (p: string) => {
|
||||
if (mockFiles[p] instanceof Array) {
|
||||
mockFiles[p].forEach((file: string) => {
|
||||
delete mockFiles[path.join(p, file)];
|
||||
});
|
||||
}
|
||||
|
||||
delete mockFiles[p];
|
||||
};
|
||||
|
||||
const readdirSync = (p: string) => {
|
||||
|
@ -85,6 +89,14 @@ const copySync = (source: string, destination: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
const createFileSync = (p: string) => {
|
||||
mockFiles[p] = '';
|
||||
};
|
||||
|
||||
const resetAllMocks = () => {
|
||||
mockFiles = Object.create(null);
|
||||
};
|
||||
|
||||
fs.readdirSync = readdirSync;
|
||||
fs.existsSync = existsSync;
|
||||
fs.readFileSync = readFileSync;
|
||||
|
@ -93,6 +105,8 @@ fs.mkdirSync = mkdirSync;
|
|||
fs.rmSync = rmSync;
|
||||
fs.copyFileSync = copyFileSync;
|
||||
fs.copySync = copySync;
|
||||
fs.createFileSync = createFileSync;
|
||||
fs.__createMockFiles = createMockFiles;
|
||||
fs.__resetAllMocks = resetAllMocks;
|
||||
|
||||
module.exports = fs;
|
||||
|
|
|
@ -54,6 +54,7 @@ const createApp = async (props: IProps) => {
|
|||
MockFiles[`${config.ROOT_FOLDER}/.env`] = 'TEST=test';
|
||||
MockFiles[`${config.ROOT_FOLDER}/repos/repo-id`] = '';
|
||||
MockFiles[`${config.ROOT_FOLDER}/repos/repo-id/apps/${appInfo.id}/config.json`] = JSON.stringify(appInfo);
|
||||
MockFiles[`${config.ROOT_FOLDER}/repos/repo-id/apps/${appInfo.id}/docker-compose.yml`] = 'compose';
|
||||
MockFiles[`${config.ROOT_FOLDER}/repos/repo-id/apps/${appInfo.id}/metadata/description.md`] = 'md desc';
|
||||
|
||||
if (installed) {
|
||||
|
|
|
@ -109,6 +109,32 @@ describe('Install app', () => {
|
|||
expect(envMap.get('RANDOM_FIELD')).toBeDefined();
|
||||
expect(envMap.get('RANDOM_FIELD')).toHaveLength(32);
|
||||
});
|
||||
|
||||
it('Should correctly copy app from repos to apps folder', async () => {
|
||||
await AppsService.installApp(app1.id, { TEST_FIELD: 'test' });
|
||||
const appFolder = fs.readdirSync(`${config.ROOT_FOLDER}/apps/${app1.id}`);
|
||||
|
||||
expect(appFolder).toBeDefined();
|
||||
expect(appFolder.indexOf('docker-compose.yml')).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
it('Should cleanup any app folder existing before install', async () => {
|
||||
const { MockFiles, appInfo } = await createApp({});
|
||||
app1 = appInfo;
|
||||
MockFiles[`/tipi/apps/${appInfo.id}/docker-compose.yml`] = 'test';
|
||||
MockFiles[`/tipi/apps/${appInfo.id}/test.yml`] = 'test';
|
||||
MockFiles[`/tipi/apps/${appInfo.id}`] = ['test.yml', 'docker-compose.yml'];
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(MockFiles);
|
||||
|
||||
expect(fs.existsSync(`${config.ROOT_FOLDER}/apps/${app1.id}/test.yml`)).toBe(true);
|
||||
|
||||
await AppsService.installApp(app1.id, { TEST_FIELD: 'test' });
|
||||
|
||||
expect(fs.existsSync(`${config.ROOT_FOLDER}/apps/${app1.id}/test.yml`)).toBe(false);
|
||||
expect(fs.existsSync(`${config.ROOT_FOLDER}/apps/${app1.id}/docker-compose.yml`)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Uninstall app', () => {
|
||||
|
|
|
@ -65,7 +65,7 @@ const installApp = async (id: string, form: Record<string, string>): Promise<App
|
|||
if (app) {
|
||||
await startApp(id);
|
||||
} else {
|
||||
ensureAppFolder(id);
|
||||
ensureAppFolder(id, true);
|
||||
const appIsValid = await checkAppRequirements(id);
|
||||
|
||||
if (!appIsValid) {
|
||||
|
|
|
@ -15,6 +15,7 @@ export enum AppCategoriesEnum {
|
|||
DATA = 'data',
|
||||
MUSIC = 'music',
|
||||
FINANCE = 'finance',
|
||||
GAMING = 'gaming',
|
||||
}
|
||||
|
||||
export enum FieldTypes {
|
||||
|
|
200
packages/system-api/src/modules/fs/__tests__/fs.helpers.test.ts
Normal file
200
packages/system-api/src/modules/fs/__tests__/fs.helpers.test.ts
Normal file
|
@ -0,0 +1,200 @@
|
|||
import childProcess from 'child_process';
|
||||
import config from '../../../config';
|
||||
import { getAbsolutePath, readJsonFile, readFile, readdirSync, fileExists, writeFile, createFolder, deleteFolder, runScript, getSeed, ensureAppFolder } from '../fs.helpers';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
jest.mock('fs-extra');
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
fs.__resetAllMocks();
|
||||
});
|
||||
|
||||
describe('Test: getAbsolutePath', () => {
|
||||
it('should return the absolute path', () => {
|
||||
expect(getAbsolutePath('/test')).toBe(`${config.ROOT_FOLDER}/test`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: readJsonFile', () => {
|
||||
it('should return the json file', () => {
|
||||
// Arrange
|
||||
const rawFile = '{"test": "test"}';
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/test-file.json`]: rawFile,
|
||||
};
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
// Act
|
||||
const file = readJsonFile('/test-file.json');
|
||||
|
||||
// Assert
|
||||
expect(file).toEqual({ test: 'test' });
|
||||
});
|
||||
|
||||
it('should return null if the file does not exist', () => {
|
||||
expect(readJsonFile('/test')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: readFile', () => {
|
||||
it('should return the file', () => {
|
||||
const rawFile = 'test';
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/test-file.txt`]: rawFile,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
expect(readFile('/test-file.txt')).toEqual('test');
|
||||
});
|
||||
|
||||
it('should return empty string if the file does not exist', () => {
|
||||
expect(readFile('/test')).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: readdirSync', () => {
|
||||
it('should return the files', () => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/test/test-file.txt`]: 'test',
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
expect(readdirSync('/test')).toEqual(['test-file.txt']);
|
||||
});
|
||||
|
||||
it('should return empty array if the directory does not exist', () => {
|
||||
expect(readdirSync('/test')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: fileExists', () => {
|
||||
it('should return true if the file exists', () => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/test-file.txt`]: 'test',
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
expect(fileExists('/test-file.txt')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if the file does not exist', () => {
|
||||
expect(fileExists('/test-file.txt')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: writeFile', () => {
|
||||
it('should write the file', () => {
|
||||
const spy = jest.spyOn(fs, 'writeFileSync');
|
||||
|
||||
writeFile('/test-file.txt', 'test');
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(`${config.ROOT_FOLDER}/test-file.txt`, 'test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: createFolder', () => {
|
||||
it('should create the folder', () => {
|
||||
const spy = jest.spyOn(fs, 'mkdirSync');
|
||||
|
||||
createFolder('/test');
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(`${config.ROOT_FOLDER}/test`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: deleteFolder', () => {
|
||||
it('should delete the folder', () => {
|
||||
const spy = jest.spyOn(fs, 'rmSync');
|
||||
|
||||
deleteFolder('/test');
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(`${config.ROOT_FOLDER}/test`, { recursive: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: runScript', () => {
|
||||
it('should run the script', () => {
|
||||
const spy = jest.spyOn(childProcess, 'execFile');
|
||||
const callback = jest.fn();
|
||||
|
||||
runScript('/test', [], callback);
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(`${config.ROOT_FOLDER}/test`, [], {}, callback);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: getSeed', () => {
|
||||
it('should return the seed', () => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/state/seed`]: 'test',
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
expect(getSeed()).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test: ensureAppFolder', () => {
|
||||
beforeEach(() => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/repos/${config.APPS_REPO_ID}/apps/test`]: ['test.yml'],
|
||||
};
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
});
|
||||
|
||||
it('should copy the folder from repo', () => {
|
||||
// Act
|
||||
ensureAppFolder('test');
|
||||
|
||||
// Assert
|
||||
const files = fs.readdirSync(`${config.ROOT_FOLDER}/apps/test`);
|
||||
expect(files).toEqual(['test.yml']);
|
||||
});
|
||||
|
||||
it('should not copy the folder if it already exists', () => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/repos/${config.APPS_REPO_ID}/apps/test`]: ['test.yml'],
|
||||
[`${config.ROOT_FOLDER}/apps/test`]: ['docker-compose.yml'],
|
||||
[`${config.ROOT_FOLDER}/apps/test/docker-compose.yml`]: 'test',
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
// Act
|
||||
ensureAppFolder('test');
|
||||
|
||||
// Assert
|
||||
const files = fs.readdirSync(`${config.ROOT_FOLDER}/apps/test`);
|
||||
expect(files).toEqual(['docker-compose.yml']);
|
||||
});
|
||||
|
||||
it('Should overwrite the folder if clean up is true', () => {
|
||||
const mockFiles = {
|
||||
[`${config.ROOT_FOLDER}/repos/${config.APPS_REPO_ID}/apps/test`]: ['test.yml'],
|
||||
[`${config.ROOT_FOLDER}/apps/test`]: ['docker-compose.yml'],
|
||||
[`${config.ROOT_FOLDER}/apps/test/docker-compose.yml`]: 'test',
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
fs.__createMockFiles(mockFiles);
|
||||
|
||||
// Act
|
||||
ensureAppFolder('test', true);
|
||||
|
||||
// Assert
|
||||
const files = fs.readdirSync(`${config.ROOT_FOLDER}/apps/test`);
|
||||
expect(files).toEqual(['test.yml']);
|
||||
});
|
||||
});
|
|
@ -42,9 +42,13 @@ export const getSeed = () => {
|
|||
return seed.toString();
|
||||
};
|
||||
|
||||
export const ensureAppFolder = (appName: string) => {
|
||||
export const ensureAppFolder = (appName: string, cleanup = false) => {
|
||||
if (cleanup) {
|
||||
deleteFolder(`/apps/${appName}`);
|
||||
}
|
||||
|
||||
if (!fileExists(`/apps/${appName}/docker-compose.yml`)) {
|
||||
fs.removeSync(getAbsolutePath(`/apps/${appName}`));
|
||||
deleteFolder(`/apps/${appName}`);
|
||||
// Copy from apps repo
|
||||
fs.copySync(getAbsolutePath(`/repos/${config.APPS_REPO_ID}/apps/${appName}`), getAbsolutePath(`/apps/${appName}`));
|
||||
}
|
||||
|
|
|
@ -25,7 +25,5 @@ rm -rf "${ROOT_FOLDER}/app-data"
|
|||
rm -rf "${ROOT_FOLDER}/data/postgres"
|
||||
mkdir -p "${ROOT_FOLDER}/app-data"
|
||||
|
||||
# Put {"installed":""} in state/apps.json
|
||||
echo '{"installed":""}' >"${ROOT_FOLDER}/state/apps.json"
|
||||
|
||||
cd "$ROOT_FOLDER"
|
||||
"${ROOT_FOLDER}/scripts/start.sh"
|
||||
|
|
Loading…
Add table
Reference in a new issue