503 lines
15 KiB
Python
503 lines
15 KiB
Python
#!/usr/bin/python3
|
|
|
|
"""
|
|
Copyright (c) 2022, Ian Santopietro
|
|
All rights reserved.
|
|
|
|
This file is part of RepoLib.
|
|
|
|
RepoLib is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
RepoLib is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with RepoLib. If not, see <https://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
from argparse import SUPPRESS
|
|
|
|
from .command import Command, RepolibCommandError
|
|
from .. import util, system
|
|
|
|
class Modify(Command):
|
|
"""Modify Subcommand
|
|
|
|
Makes modifications to the specified repository
|
|
|
|
Options:
|
|
--enable, -e
|
|
--disable, -d
|
|
--default-mirror
|
|
--name
|
|
--add-suite
|
|
--remove-suite
|
|
--add-component
|
|
--remove-component
|
|
--add-uri
|
|
--remove-uri
|
|
|
|
Hidden Options
|
|
--add-option
|
|
--remove-option
|
|
--default-mirror
|
|
"""
|
|
|
|
@classmethod
|
|
def init_options(cls, subparsers):
|
|
""" Sets up this command's options parser"""
|
|
|
|
sub = subparsers.add_parser(
|
|
'modify',
|
|
help='Change a configured repository.'
|
|
)
|
|
sub.add_argument(
|
|
'repository',
|
|
nargs='*',
|
|
default=['system'],
|
|
help='The repository to modify. Default is the system repository.'
|
|
)
|
|
|
|
modify_enable = sub.add_mutually_exclusive_group(
|
|
required=False
|
|
)
|
|
modify_enable.add_argument(
|
|
'-e',
|
|
'--enable',
|
|
action='store_true',
|
|
help='Enable the repository, if disabled.'
|
|
)
|
|
modify_enable.add_argument(
|
|
'-d',
|
|
'--disable',
|
|
action='store_true',
|
|
help=(
|
|
'Disable the repository, if enabled. The system repository cannot '
|
|
'be disabled.'
|
|
)
|
|
)
|
|
|
|
modify_source = sub.add_mutually_exclusive_group(
|
|
required=False
|
|
)
|
|
|
|
modify_source.add_argument(
|
|
'--source-enable',
|
|
action='store_true',
|
|
help='Enable source code for the repository, if disabled.'
|
|
)
|
|
modify_source.add_argument(
|
|
'--source-disable',
|
|
action='store_true',
|
|
help='Disable source code for the repository, if enabled.'
|
|
)
|
|
|
|
sub.add_argument(
|
|
'--default-mirror',
|
|
help=SUPPRESS
|
|
#help='Sets the default mirror for the system source.'
|
|
)
|
|
|
|
# Name
|
|
sub.add_argument(
|
|
'-n',
|
|
'--name',
|
|
help='Set the repository name to NAME'
|
|
)
|
|
|
|
# Suites
|
|
sub.add_argument(
|
|
'--add-suite',
|
|
metavar='SUITE[,SUITE]',
|
|
help=(
|
|
'Add the specified suite(s) to the repository. Multiple '
|
|
'repositories should be separated with commas. NOTE: Legacy deb '
|
|
'repositories may only contain one suite.'
|
|
)
|
|
)
|
|
sub.add_argument(
|
|
'--remove-suite',
|
|
metavar='SUITE[,SUITE]',
|
|
help=(
|
|
'Remove the specified suite(s) from the repository. Multiple '
|
|
'repositories should be separated with commas. NOTE: The last '
|
|
'suite in a repository cannot be removed.'
|
|
)
|
|
)
|
|
|
|
# Components
|
|
sub.add_argument(
|
|
'--add-component',
|
|
metavar='COMPONENT[,COMPONENT]',
|
|
help=(
|
|
'Add the specified component(s) to the repository. Multiple '
|
|
'repositories should be separated with commas.'
|
|
)
|
|
)
|
|
sub.add_argument(
|
|
'--remove-component',
|
|
metavar='COMPONENT[,COMPONENT]',
|
|
help=(
|
|
'Remove the specified component(s) from the repository. Multiple '
|
|
'repositories should be separated with commas. NOTE: The last '
|
|
'component in a repository cannot be removed.'
|
|
)
|
|
)
|
|
|
|
# URIs
|
|
sub.add_argument(
|
|
'--add-uri',
|
|
metavar='URI[,URI]',
|
|
help=(
|
|
'Add the specified URI(s) to the repository. Multiple '
|
|
'repositories should be separated with commas. NOTE: Legacy deb '
|
|
'repositories may only contain one uri.'
|
|
)
|
|
)
|
|
sub.add_argument(
|
|
'--remove-uri',
|
|
metavar='URI[,URI]',
|
|
help=(
|
|
'Remove the specified URI(s) from the repository. Multiple '
|
|
'repositories should be separated with commas. NOTE: The last '
|
|
'uri in a repository cannot be removed.'
|
|
)
|
|
)
|
|
|
|
# Options
|
|
sub.add_argument(
|
|
'--add-option',
|
|
metavar='OPTION=VALUE[,OPTION=VALUE]',
|
|
help=SUPPRESS
|
|
)
|
|
sub.add_argument(
|
|
'--remove-option',
|
|
metavar='OPTION=VALUE[,OPTION=VALUE]',
|
|
help=SUPPRESS
|
|
)
|
|
|
|
def finalize_options(self, args):
|
|
super().finalize_options(args)
|
|
self.count = 0
|
|
self.repo = ' '.join(args.repository)
|
|
self.enable = args.enable
|
|
self.disable = args.disable
|
|
self.source_enable = args.source_enable
|
|
self.source_disable = args.source_disable
|
|
|
|
self.actions:dict = {}
|
|
|
|
self.actions['endisable'] = None
|
|
for i in ['enable', 'disable']:
|
|
if getattr(args, i):
|
|
self.actions['endisable'] = i
|
|
|
|
self.actions['source_endisable'] = None
|
|
for i in ['source_enable', 'source_disable']:
|
|
if getattr(args, i):
|
|
self.actions['source_endisable'] = i
|
|
|
|
for arg in [
|
|
'default_mirror',
|
|
'name',
|
|
'add_uri',
|
|
'add_suite',
|
|
'add_component',
|
|
'remove_uri',
|
|
'remove_suite',
|
|
'remove_component',
|
|
]:
|
|
self.actions[arg] = getattr(args, arg)
|
|
|
|
system.load_all_sources()
|
|
|
|
def run(self):
|
|
"""Run the command"""
|
|
self.log.info('Modifying repository %s', self.repo)
|
|
|
|
self.system_source = False
|
|
try:
|
|
self.source = util.sources[self.repo]
|
|
except KeyError:
|
|
self.log.error(
|
|
'The source %s could not be found. Check the spelling',
|
|
self.repo
|
|
)
|
|
return False
|
|
|
|
if self.source.ident == 'system':
|
|
self.system_source = True
|
|
|
|
self.log.debug('Actions to take:\n%s', self.actions)
|
|
self.log.debug('Source before:\n%s', self.source)
|
|
|
|
rets = []
|
|
for action in self.actions:
|
|
ret = getattr(self, action)(self.actions[action])
|
|
rets.append(ret)
|
|
|
|
self.log.debug('Results: %s', rets)
|
|
self.log.debug('Source after: \n%s', self.source)
|
|
|
|
if True in rets:
|
|
self.source.file.save()
|
|
return True
|
|
else:
|
|
self.log.warning('No valid changes specified, no actions taken.')
|
|
return False
|
|
|
|
def default_mirror(self, value:str) -> bool:
|
|
"""Checks if this is the system source, then set the default mirror"""
|
|
if not value:
|
|
return False
|
|
|
|
if self.system_source:
|
|
self.source['X-Repolib-Default-Mirror'] = value
|
|
return True
|
|
return False
|
|
|
|
def name(self, value:str) -> bool:
|
|
"""Sets the source name"""
|
|
if not value:
|
|
return False
|
|
|
|
self.log.info('Setting name for %s to %s', self.repo, value)
|
|
self.source.name = value
|
|
return True
|
|
|
|
def endisable(self, value:str) -> bool:
|
|
"""Enable or disable the source"""
|
|
if not value:
|
|
return False
|
|
|
|
self.log.info('%sing source %s', value[:-1], self.repo)
|
|
if value == 'disable':
|
|
self.source.enabled = False
|
|
return True
|
|
|
|
self.source.enabled = True
|
|
return True
|
|
|
|
def source_endisable(self, value:str) -> bool:
|
|
"""Enable/disable source code for the repo"""
|
|
if not value:
|
|
return False
|
|
|
|
self.log.info('%sing source code for source %s', value[7:-1], self.repo)
|
|
if value == 'source_disable':
|
|
self.source.sourcecode_enabled = False
|
|
return True
|
|
|
|
self.source.sourcecode_enabled = True
|
|
return True
|
|
|
|
def add_uri(self, values:str) -> bool:
|
|
"""Adds URIs to the source, if not already present."""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Adding URIs: %s', values)
|
|
uris = self.source.uris
|
|
|
|
for uri in values.split():
|
|
if not util.url_validator(uri):
|
|
raise RepolibCommandError(
|
|
f'Cannot add URI {uri} to {self.repo}. The URI is '
|
|
'malformed'
|
|
)
|
|
|
|
if uri not in uris:
|
|
uris.append(uri)
|
|
self.log.debug('Added URI %s', uri)
|
|
|
|
else:
|
|
self.log.warning(
|
|
'The URI %s was already present in %s',
|
|
uri,
|
|
self.repo
|
|
)
|
|
|
|
if uris != self.source.uris:
|
|
self.source.uris = uris
|
|
return True
|
|
return False
|
|
|
|
def remove_uri(self, values:str) -> bool:
|
|
"""Remove URIs from the soruce, if they are added."""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Removing URIs %s from source %s', values, self.repo)
|
|
uris = self.source.uris
|
|
self.log.debug('Starting uris: %s', uris)
|
|
|
|
for uri in values.split():
|
|
try:
|
|
uris.remove(uri)
|
|
self.log.debug('Removed URI %s', uri)
|
|
|
|
except ValueError:
|
|
self.log.warning(
|
|
'The URI %s was not present in %s',
|
|
uri,
|
|
self.repo
|
|
)
|
|
|
|
if len(uris) == 0:
|
|
self.log.error(
|
|
'Cannot remove the last URI from %s. If you meant to delete the source, try REMOVE instead.',
|
|
self.repo
|
|
)
|
|
return False
|
|
|
|
if uris != self.source.uris:
|
|
self.source.uris = uris
|
|
return True
|
|
|
|
return False
|
|
|
|
def add_suite(self, values:str) -> bool:
|
|
"""Adds a suite to the source"""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Adding suites: %s', values)
|
|
suites = self.source.suites
|
|
|
|
for suite in values.split():
|
|
if suite not in suites:
|
|
suites.append(suite)
|
|
self.log.debug('Added suite %s', suite)
|
|
|
|
else:
|
|
self.log.warning(
|
|
'The suite %s was already present in %s',
|
|
suite,
|
|
self.repo
|
|
)
|
|
|
|
if suites != self.source.suites:
|
|
self.source.suites = suites
|
|
return True
|
|
return False
|
|
|
|
def remove_suite(self, values:str) -> bool:
|
|
"""Remove a suite from the source"""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Removing suites %s from source %s', values, self.repo)
|
|
suites = self.source.suites
|
|
self.log.debug('Starting suites: %s', suites)
|
|
|
|
for suite in values.split():
|
|
try:
|
|
suites.remove(suite)
|
|
self.log.debug('Removed suite %s', suite)
|
|
|
|
except ValueError:
|
|
self.log.warning(
|
|
'The suite %s was not present in %s',
|
|
suite,
|
|
self.repo
|
|
)
|
|
|
|
if len(suites) == 0:
|
|
self.log.error(
|
|
'Cannot remove the last suite from %s. If you meant to delete the source, try REMOVE instead.',
|
|
self.repo
|
|
)
|
|
return False
|
|
|
|
if suites != self.source.suites:
|
|
self.source.suites = suites
|
|
return True
|
|
|
|
return False
|
|
|
|
def add_component(self, values:str) -> bool:
|
|
"""Adds components to the source"""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Adding components: %s', values)
|
|
components = self.source.components
|
|
|
|
for component in values.split():
|
|
if component not in components:
|
|
components.append(component)
|
|
self.log.debug('Added component %s', component)
|
|
|
|
else:
|
|
self.log.warning(
|
|
'The component %s was already present in %s',
|
|
component,
|
|
self.repo
|
|
)
|
|
|
|
if len(components) > 1:
|
|
if self.source.file.format == util.SourceFormat.LEGACY:
|
|
self.log.warning(
|
|
'Adding multiple components to a legacy source is not '
|
|
'supported. Consider converting the source to DEB822 format.'
|
|
)
|
|
|
|
if components != self.source.components:
|
|
self.source.components = components
|
|
return True
|
|
return False
|
|
|
|
def remove_component(self, values:str) -> bool:
|
|
"""Removes components from the source"""
|
|
if not values:
|
|
return False
|
|
|
|
self.log.info('Removing components %s from source %s', values, self.repo)
|
|
components = self.source.components
|
|
self.log.debug('Starting components: %s', components)
|
|
|
|
for component in values.split():
|
|
try:
|
|
components.remove(component)
|
|
self.log.debug('Removed component %s', component)
|
|
|
|
except ValueError:
|
|
self.log.warning(
|
|
'The component %s was not present in %s',
|
|
component,
|
|
self.repo
|
|
)
|
|
|
|
if len(components) == 0:
|
|
self.log.error(
|
|
'Cannot remove the last component from %s. If you meant to delete the source, try REMOVE instead.',
|
|
self.repo
|
|
)
|
|
return False
|
|
|
|
if components != self.source.components:
|
|
self.source.components = components
|
|
return True
|
|
|
|
return False
|
|
|
|
def add_option(self, values) -> bool:
|
|
"""TODO: Support options"""
|
|
raise NotImplementedError(
|
|
'Options have not been implemented in this version of repolib yet. '
|
|
f'Please edit the file {self.source.file.path} manually.'
|
|
)
|
|
|
|
|
|
def remove_option(self, values) -> bool:
|
|
"""TODO: Support options"""
|
|
raise NotImplementedError(
|
|
'Options have not been implemented in this version of repolib yet. '
|
|
f'Please edit the file {self.source.file.path} manually.'
|
|
)
|