Skip to content

Commit

Permalink
[Feature] Do not allow beta to override stable repository if both act…
Browse files Browse the repository at this point in the history
…ive (#2935)

* Do not allow to be active together the stable and beta repositories.
Allow addressing the repositories by name

* Added documentation

* Changed the detection of stable and beta repositories

* Adjustments for Repo-CLI PR (#2958)

* cleanup for repo PR

* do not prevent normal cli workflow -> unset -> set

---------

Co-authored-by: Max Hauser <moritz.heusinger@gmail.com>
  • Loading branch information
GermanBluefox and foxriver76 authored Nov 19, 2024
1 parent 48cb996 commit 1e3f92f
Show file tree
Hide file tree
Showing 3 changed files with 334 additions and 192 deletions.
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

0 comments on commit 1e3f92f

Please sign in to comment.