Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Do not allow beta to override stable repository if both active #2935

Merged
merged 7 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 135 additions & 116 deletions packages/cli/src/lib/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ import { CLIMessage } from '@/lib/cli/cliMessage.js';
import { CLIPlugin } from '@/lib/cli/cliPlugin.js';
import { error as CLIError } from '@/lib/cli/messages.js';
import type { CLICommandContext, CLICommandOptions } from '@/lib/cli/cliCommand.js';
import { getRepository, ignoreVersion, recognizeVersion } from '@/lib/setup/utils.js';
import {
BETA_REPO_URL,
getRepository,
ignoreVersion,
isIntegerLikeInput,
recognizeVersion,
STABLE_REPO_URL,
} from '@/lib/setup/utils.js';
import { dbConnect, dbConnectAsync, exitApplicationSave } from '@/lib/setup/dbConnection.js';
import { IoBrokerError } from '@/lib/setup/customError.js';
import type { ListType } from '@/lib/setup/setupList.js';
Expand All @@ -30,6 +37,7 @@ import * as events from 'node:events';
// eslint-disable-next-line unicorn/prefer-module
const thisDir = url.fileURLToPath(new URL('.', import.meta.url || `file://${__filename}`));
import { createRequire } from 'node:module';
import { SYSTEM_CONFIG_ID, SYSTEM_REPOSITORIES_ID } from '@iobroker/js-controller-common-db/constants';
// eslint-disable-next-line unicorn/prefer-module
const require = createRequire(import.meta.url || `file://${__filename}`);

Expand Down Expand Up @@ -395,10 +403,10 @@ function initYargs(): ReturnType<typeof yargs> {
.command('all', 'Show entire config')
.command('<adapter>[.<instance>]', 'Status of a specified adapter instance');
})
.command('repo [<name>]', 'Show repo information', yargs => {
.command('repo', 'Show repo information', yargs => {
yargs
.command('set <name>', 'Set active repository')
.command('del <name>', 'Remove repository')
.command('set <name>|<index>', 'Set active repository')
.command('del <name>|<index>', 'Remove repository')
.command('add <name> <url>', 'Add repository')
.command('addset <name> <url>', 'Add repository and set it as active one')
.command('show', 'List repositories');
Expand Down Expand Up @@ -670,8 +678,8 @@ async function processCommand(
const repo = new Repo({ objects, states });

try {
await repo.rename('default', 'stable', 'http://download.iobroker.net/sources-dist.json');
await repo.rename('latest', 'beta', 'http://download.iobroker.net/sources-dist-latest.json');
await repo.rename('default', 'stable', STABLE_REPO_URL);
await repo.rename('latest', 'beta', BETA_REPO_URL);
} catch (err) {
console.warn(`Cannot rename: ${err.message}`);
}
Expand Down Expand Up @@ -2514,9 +2522,9 @@ async function processCommand(
}

case 'repo': {
let repoUrlOrCommand = args[0]; // Repo url or name or "add" / "del" / "set" / "show" / "addset" / "unset"
const repoName = args[1]; // Repo url or name
let repoUrl = args[2]; // Repo url or name
let repoUrlOrCommand: string | undefined = args[0]; // Repo url or name or "add" / "del" / "set" / "show" / "addset" / "unset"
let repoName: string | undefined = args[1]; // Repo url or name
let repoUrl: string | undefined = args[2]; // Repo url or name
if (
repoUrlOrCommand !== 'add' &&
repoUrlOrCommand !== 'del' &&
Expand All @@ -2529,91 +2537,102 @@ async function processCommand(
repoUrlOrCommand = 'show';
}

dbConnect(params, async ({ objects, states }) => {
const { Repo } = await import('./setup/setupRepo.js');
const repo = new Repo({
objects,
states,
});
const { objects, states } = await dbConnectAsync(false, params);
const { Repo } = await import('./setup/setupRepo.js');
const repo = new Repo({
objects,
states,
});

if (repoUrlOrCommand === 'show') {
if (repoUrlOrCommand === 'show') {
try {
await repo.showRepoStatus();
return void callback();
} catch (e) {
console.error(`Cannot show repository status: ${e.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
}

if (
repoUrlOrCommand === 'add' ||
repoUrlOrCommand === 'del' ||
repoUrlOrCommand === 'set' ||
repoUrlOrCommand === 'addset' ||
repoUrlOrCommand === 'unset'
) {
if (!repoName || !repoName.match(/[-_\w]+/)) {
console.error(`Invalid repository name: "${repoName}"`);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}

if (isIntegerLikeInput(repoName)) {
try {
await repo.showRepoStatus();
return void callback();
} catch (err) {
console.error(`Cannot show repository status: ${err.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
repoName = await repo.getNameByIndex(parseInt(repoName));
} catch (e) {
console.error(e.message);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
} else if (
repoUrlOrCommand === 'add' ||
repoUrlOrCommand === 'del' ||
repoUrlOrCommand === 'set' ||
repoUrlOrCommand === 'addset' ||
repoUrlOrCommand === 'unset'
) {
if (!repoName || !repoName.match(/[-_\w\d]+/)) {
console.error(`Invalid repository name: "${repoName}"`);
return void callback();
}

if (repoUrlOrCommand === 'add' || repoUrlOrCommand === 'addset') {
if (!repoUrl) {
console.warn(
`Please define repository URL or path: ${tools.appName.toLowerCase()} add <repoName> <repoUrlOrPath>`,
);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
if (repoUrlOrCommand === 'add' || repoUrlOrCommand === 'addset') {
if (!repoUrl) {
console.warn(
`Please define repository URL or path: ${tools.appName.toLowerCase()} add <repoName> <repoUrlOrPath>`,
);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
try {
await repo.add(repoName, repoUrl);
try {
await repo.add(repoName, repoUrl);

if (repoUrlOrCommand === 'addset') {
await repo.setActive(repoName);
console.log(`Repository "${repoName}" set as active: "${repoUrl}"`);
await repo.showRepoStatus();
return void callback();
}
console.log(`Repository "${repoName}" added as "${repoUrl}"`);
await repo.showRepoStatus();
return void callback();
} catch (err) {
console.error(`Cannot add repository location: ${err.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'set') {
try {
if (repoUrlOrCommand === 'addset') {
await repo.setActive(repoName);
console.log(`Repository "${repoName}" set as active.`);
console.log(`Repository "${repoName}" set as active: "${repoUrl}"`);
await repo.showRepoStatus();
return void callback();
} catch (err) {
console.error(`Cannot activate repository: ${err.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'del') {
try {
await repo.del(repoName);
console.log(`Repository "${repoName}" deleted.`);
await repo.showRepoStatus();
return void callback();
} catch (err) {
console.error(`Cannot remove repository: ${err.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'unset') {
try {
await repo.setInactive(repoName);
console.log(`Repository "${repoName}" deactivated.`);
await repo.showRepoStatus();
return void callback();
} catch (err) {
console.error(`Cannot deactivate repository: ${err.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else {
console.warn(`Unknown repo command: ${repoUrlOrCommand as string}`);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
console.log(`Repository "${repoName}" added as "${repoUrl}"`);
await repo.showRepoStatus();
return void callback();
} catch (e) {
console.error(`Cannot add repository location: ${e.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'set') {
try {
await repo.setActive(repoName);
console.log(`Repository "${repoName}" set as active.`);
await repo.showRepoStatus();
return void callback();
} catch (e) {
console.error(`Cannot activate repository: ${e.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'del') {
try {
await repo.del(repoName);
console.log(`Repository "${repoName}" deleted.`);
await repo.showRepoStatus();
return void callback();
} catch (e) {
console.error(`Cannot remove repository: ${e.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else if (repoUrlOrCommand === 'unset') {
try {
await repo.setInactive(repoName);
console.log(`Repository "${repoName}" deactivated.`);
await repo.showRepoStatus();
return void callback();
} catch (e) {
console.error(`Cannot deactivate repository: ${e.message}`);
return void callback(EXIT_CODES.INVALID_REPO);
}
} else {
console.warn(`Unknown repo command: ${repoUrlOrCommand as string}`);
return void callback(EXIT_CODES.INVALID_ARGUMENTS);
}
});
}
break;
}

Expand Down Expand Up @@ -2788,10 +2807,10 @@ const OBJECTS_THAT_CANNOT_BE_DELETED = [
'alias.0',
'enum.functions',
'enum.rooms',
'system.config',
SYSTEM_CONFIG_ID,
'system.group.administrator',
'system.group.user',
'system.repositories',
SYSTEM_REPOSITORIES_ID,
'system.user.admin',
];

Expand Down Expand Up @@ -2869,40 +2888,40 @@ async function cleanDatabase(isDeleteDb: boolean): Promise<number> {
return keysCount;
}

function unsetup(params: Record<string, any>, callback: ExitCodeCb): void {
dbConnect(params, ({ objects }) => {
objects.delObject('system.meta.uuid', err => {
if (err) {
console.log(`uuid cannot be deleted: ${err.message}`);
} else {
console.log('system.meta.uuid deleted');
}
objects.getObject('system.config', (_err, obj) => {
if (obj?.common.licenseConfirmed || obj?.common.language || obj?.native?.secret) {
obj.common.language = 'en';
// allow with parameter --keepsecret to not delete the secret
// This is very specific use case for vendors and must not be described in documentation
if (!params.keepsecret) {
obj.common.licenseConfirmed = false;
obj.native && delete obj.native.secret;
}
async function unsetup(params: Record<string, any>, callback: ExitCodeCb): Promise<void> {
const { objects } = await dbConnectAsync(false, params);

obj.from = `system.host.${tools.getHostName()}.cli`;
obj.ts = new Date().getTime();
objects.delObject('system.meta.uuid', err => {
if (err) {
console.log(`uuid cannot be deleted: ${err.message}`);
} else {
console.log('system.meta.uuid deleted');
}
objects.getObject(SYSTEM_CONFIG_ID, (_err, obj) => {
if (obj?.common.licenseConfirmed || obj?.common.language || obj?.native?.secret) {
obj.common.language = 'en';
// allow with parameter --keepsecret to not delete the secret
// This is very specific use case for vendors and must not be described in documentation
if (!params.keepsecret) {
obj.common.licenseConfirmed = false;
obj.native && delete obj.native.secret;
}

objects.setObject('system.config', obj as any, err => {
if (err) {
console.log(`not found: ${err.message}`);
return void callback(EXIT_CODES.CANNOT_SET_OBJECT);
}
console.log('system.config reset');
return void callback();
});
} else {
console.log('system.config is OK');
obj.from = `system.host.${tools.getHostName()}.cli`;
obj.ts = new Date().getTime();

objects.setObject(SYSTEM_CONFIG_ID, obj, err => {
if (err) {
console.log(`not found: ${err.message}`);
return void callback(EXIT_CODES.CANNOT_SET_OBJECT);
}
console.log(`${SYSTEM_CONFIG_ID} reset`);
return void callback();
}
});
});
} else {
console.log(`${SYSTEM_CONFIG_ID} is OK`);
return void callback();
}
});
});
}
Expand Down
Loading
Loading