Loading README.md +11 −7 Changes for README.md: 11 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -28,13 +28,13 @@ The following step is required before using the tool: <!-- readme-help-start --> ```yaml usage: gitlab-projects-settings [-h] [--version] [--settings] [--set GROUP KEY VAL] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups] [--exclude-projects] [--available-features] [--reset-features [KEEP_FEATURES]] [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members] [--set-avatar FILE] [--set-description TEXT] [--update-description] [--run-housekeeping] [--archive-project | --unarchive-project] [--delete-group] [--delete-project] [--protect-branches] [--protect-tags LEVEL] [--] usage: gitlab-projects-settings [-h] [--version] [--update-check] [--settings] [--set GROUP KEY VAL] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups] [--exclude-projects] [--available-features] [--reset-features [KEEP_FEATURES]] [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members] [--set-avatar FILE] [--set-description TEXT] [--update-description] [--run-housekeeping] [--archive-project | --unarchive-project] [--delete-group] [--delete-project] [--protect-branches] [--protect-tags LEVEL] [--] [gitlab] [path] gitlab-projects-settings: Configure GitLab groups and projects settings automatically Loading @@ -42,6 +42,7 @@ gitlab-projects-settings: Configure GitLab groups and projects settings automati internal arguments: -h, --help # Show this help message --version # Show the current version --update-check # Check for newer package updates --settings # Show the current settings path and contents --set GROUP KEY VAL # Set settings specific 'VAL' value to [GROUP] > KEY # or unset by using 'UNSET' as 'VAL' Loading Loading @@ -92,6 +93,8 @@ positional arguments: `gitlab-projects-settings` creates a `settings.ini` configuration file in a userspace folder. For example, it allows to disable the automated updates daily check (`[updates] > enabled`) The `settings.ini` file location and contents can be shown with the following command: ```bash Loading @@ -105,6 +108,7 @@ gitlab-projects-settings --settings - [colored](https://pypi.org/project/colored/): Terminal colors and styles - [python-gitlab](https://pypi.org/project/python-gitlab/): A python wrapper for the GitLab API - [setuptools](https://pypi.org/project/setuptools/): Build and manage Python packages - [update-checker](https://pypi.org/project/update-checker/): Check for package updates on PyPI --- Loading requirements/runtime.txt +1 −0 Changes for requirements/runtime.txt: 1 added line, 0 removed lines. Original line number Diff line number Diff line colored>=1.4.2 python-gitlab>=4.4.0 setuptools>=45.1.0 update_checker>=0.18.0 src/cli/main.py +20 −0 Changes for src/cli/main.py: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ from sys import exit as sys_exit # Components from ..package.bundle import Bundle from ..package.settings import Settings from ..package.updates import Updates from ..package.version import Version from ..prints.colors import Colors from ..system.platform import Platform Loading Loading @@ -53,6 +54,12 @@ def main() -> None: action='store_true', help='Show the current version', ) group.add_argument( '--update-check', dest='update_check', action='store_true', help='Check for newer package updates', ) group.add_argument( '--settings', dest='settings', Loading Loading @@ -262,6 +269,9 @@ def main() -> None: settings.show() sys_exit(0) # Instantiate updates updates: Updates = Updates(name=Bundle.NAME, settings=settings) # Version informations if options.version: print( Loading @@ -270,6 +280,12 @@ def main() -> None: Platform.flush() sys_exit(0) # Check for current updates if options.update_check: if not updates.check(): updates.check(older=True) sys_exit(0) # Arguments validation if not options.token or not options.gitlab or not options.path: print(' ') Loading @@ -289,6 +305,10 @@ def main() -> None: print(' ') Platform.flush() # Check for daily updates if updates.enabled and updates.daily: updates.check() # Result if result in [Entrypoint.Result.SUCCESS, Entrypoint.Result.FINALIZE]: sys_exit(0) Loading src/package/bundle.py +7 −0 Changes for src/package/bundle.py: 7 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,5 +12,12 @@ class Bundle: # Sources REPOSITORY: str = 'https://gitlab.com/AdrianDC/gitlab-projects-settings' # Releases RELEASE_FIRST_TIMESTAMP: int = 1579337311 # Environment ENV_DEBUG_UPDATES_DAILY: str = 'DEBUG_UPDATES_DAILY' ENV_DEBUG_UPDATES_DISABLE: str = 'DEBUG_UPDATES_DISABLE' ENV_DEBUG_UPDATES_FAKE: str = 'DEBUG_UPDATES_FAKE' ENV_DEBUG_UPDATES_OFFLINE: str = 'DEBUG_UPDATES_OFFLINE' ENV_GITLAB_TOKEN: str = 'GITLAB_TOKEN' src/package/updates.py 0 → 100644 +216 −0 Changes for src/package/updates.py: 216 added lines, 0 removed lines. Original line number Diff line number Diff line #!/usr/bin/env python3 # Standard libraries from datetime import datetime from os import access, environ, W_OK from time import localtime, strftime, time from typing import Optional # Components from ..prints.boxes import Boxes from ..prints.colors import Colors from ..system.platform import Platform from .bundle import Bundle from .settings import Settings from .version import Version # Updates class class Updates: # Members __enabled: bool __name: str __settings: Settings # Constructor def __init__(self, name: str, settings: Settings) -> None: # Initialize members self.__name = name self.__settings = settings # Detect migration self.__migration() # Prepare enabled if not self.__settings.has('updates', 'enabled'): self.__settings.set_bool('updates', 'enabled', True) # Check enabled self.__enabled = bool(self.__settings.get_bool('updates', 'enabled')) and \ (Bundle.ENV_DEBUG_UPDATES_DISABLE not in environ \ or not environ[Bundle.ENV_DEBUG_UPDATES_DISABLE]) # Migration def __migration(self) -> None: # Acquire versions current_version = Version.get() package_version = self.__settings.get('package', 'version') if not package_version: package_version = '0.0.0' # Refresh package version if not package_version or current_version != package_version: self.__settings.set('package', 'version', current_version) # Checker def check(self, older: bool = False) -> bool: # Reference version version = '0.0.0' if older else Version.get() # Fake test updates if Bundle.ENV_DEBUG_UPDATES_FAKE in environ: available = environ[Bundle.ENV_DEBUG_UPDATES_FAKE] if available >= version: # Show updates message release_date = datetime.utcfromtimestamp(Bundle.RELEASE_FIRST_TIMESTAMP) Updates.message(name=self.__name, older=older, available=available, date=release_date) return True # Check if not offline if Bundle.ENV_DEBUG_UPDATES_OFFLINE not in environ: # Modules libraries, pylint: disable=import-outside-toplevel from update_checker import UpdateChecker # Check for updates check = UpdateChecker(bypass_cache=True).check(self.__name, version) if check: # Show updates message Updates.message(name=self.__name, older=older, available=check.available_version, date=check.release_date) return True # Older offline failure if older: # Show offline message Updates.message(name=self.__name, offline=True) return True # Result return False # Daily @property def daily(self) -> bool: # Acquire updates check last timestamp last = self.__settings.get('updates', 'last_timestamp') # Fake test updates if Bundle.ENV_DEBUG_UPDATES_DAILY in environ: last = None # Handle daily checks current = int(time()) if not last or strftime('%Y-%m-%d', localtime(current)) != strftime( '%Y-%m-%d', localtime(int(last))): self.__settings.set('updates', 'last_timestamp', current) return True # Default fallback return False # Enabled @property def enabled(self) -> bool: return self.__enabled # Message, pylint: disable=too-many-arguments @staticmethod def message( name: str, offline: bool = False, older: bool = False, available: Optional[str] = None, date: Optional[datetime] = None, recommended: Optional[str] = None, ) -> None: # Modules libraries, pylint: disable=import-outside-toplevel from update_checker import pretty_date # Create message box box = Boxes() # Acquire current version version = Version.get() # Detect package installer package_install: str = '' if Platform.PATH_SEPARATOR + 'pipx' + Platform.PATH_SEPARATOR in Version.path(): package_install = 'pipx upgrade' # pragma: no cover else: package_install = 'pip3 install -U' # pragma: no cover # Detect package ownership writable = access(__file__, W_OK) if Platform.IS_USER_SUDO or not writable: package_install = 'sudo ' + package_install # pragma: no cover # Prepare package specification package_specification: str = '' if 'pipx' in package_install: package_specification = f'{name}' # pragma: no cover else: package_specification = f'{name}>={available}' # pragma: no cover # Evaluate same version same = available and available == version # Version message prefix version_outdated = not offline and available and not older and not same version_prefix = f'{Colors.YELLOW_LIGHT}Version: {Colors.BOLD}{name}' \ f' {Colors.RED if version_outdated else Colors.GREEN}{version}' # Recommended version message if recommended: available = recommended box.add(f'{version_prefix} {Colors.BOLD}older than recommended version ' f'{Colors.RED}{recommended}{Colors.BOLD}') # Offline version message elif offline: box.add(f'{version_prefix} {Colors.BOLD}not found, network might be down') # Updated version message elif same: box.add( f'{version_prefix} {Colors.BOLD}was released {pretty_date(date)}{Colors.BOLD}!' ) # Older version message elif older: box.add( f'{version_prefix} {Colors.BOLD}newer than {Colors.RED}{available}' \ f' {Colors.BOLD}from {pretty_date(date)}{Colors.BOLD}!' ) # Newer version message else: box.add( f'{version_prefix} {Colors.BOLD}updated {pretty_date(date)}' \ f' to {Colors.GREEN}{available}{Colors.BOLD}!' ) # Changelog message box.add( f'{Colors.YELLOW_LIGHT}Changelog: {Colors.CYAN}{Bundle.REPOSITORY}/-/releases' ) # Update message if available: box.add( f'{Colors.YELLOW_LIGHT}Update: {Colors.BOLD}' \ f"Run {Colors.GREEN}{package_install} '{package_specification}'" ) # Print message box box.print() Loading
README.md +11 −7 Changes for README.md: 11 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -28,13 +28,13 @@ The following step is required before using the tool: <!-- readme-help-start --> ```yaml usage: gitlab-projects-settings [-h] [--version] [--settings] [--set GROUP KEY VAL] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups] [--exclude-projects] [--available-features] [--reset-features [KEEP_FEATURES]] [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members] [--set-avatar FILE] [--set-description TEXT] [--update-description] [--run-housekeeping] [--archive-project | --unarchive-project] [--delete-group] [--delete-project] [--protect-branches] [--protect-tags LEVEL] [--] usage: gitlab-projects-settings [-h] [--version] [--update-check] [--settings] [--set GROUP KEY VAL] [-t TOKEN] [--dry-run] [--exclude-group] [--exclude-subgroups] [--exclude-projects] [--available-features] [--reset-features [KEEP_FEATURES]] [--disable-features FEATURES] [--enable-features FEATURES] [--reset-members] [--set-avatar FILE] [--set-description TEXT] [--update-description] [--run-housekeeping] [--archive-project | --unarchive-project] [--delete-group] [--delete-project] [--protect-branches] [--protect-tags LEVEL] [--] [gitlab] [path] gitlab-projects-settings: Configure GitLab groups and projects settings automatically Loading @@ -42,6 +42,7 @@ gitlab-projects-settings: Configure GitLab groups and projects settings automati internal arguments: -h, --help # Show this help message --version # Show the current version --update-check # Check for newer package updates --settings # Show the current settings path and contents --set GROUP KEY VAL # Set settings specific 'VAL' value to [GROUP] > KEY # or unset by using 'UNSET' as 'VAL' Loading Loading @@ -92,6 +93,8 @@ positional arguments: `gitlab-projects-settings` creates a `settings.ini` configuration file in a userspace folder. For example, it allows to disable the automated updates daily check (`[updates] > enabled`) The `settings.ini` file location and contents can be shown with the following command: ```bash Loading @@ -105,6 +108,7 @@ gitlab-projects-settings --settings - [colored](https://pypi.org/project/colored/): Terminal colors and styles - [python-gitlab](https://pypi.org/project/python-gitlab/): A python wrapper for the GitLab API - [setuptools](https://pypi.org/project/setuptools/): Build and manage Python packages - [update-checker](https://pypi.org/project/update-checker/): Check for package updates on PyPI --- Loading
requirements/runtime.txt +1 −0 Changes for requirements/runtime.txt: 1 added line, 0 removed lines. Original line number Diff line number Diff line colored>=1.4.2 python-gitlab>=4.4.0 setuptools>=45.1.0 update_checker>=0.18.0
src/cli/main.py +20 −0 Changes for src/cli/main.py: 20 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ from sys import exit as sys_exit # Components from ..package.bundle import Bundle from ..package.settings import Settings from ..package.updates import Updates from ..package.version import Version from ..prints.colors import Colors from ..system.platform import Platform Loading Loading @@ -53,6 +54,12 @@ def main() -> None: action='store_true', help='Show the current version', ) group.add_argument( '--update-check', dest='update_check', action='store_true', help='Check for newer package updates', ) group.add_argument( '--settings', dest='settings', Loading Loading @@ -262,6 +269,9 @@ def main() -> None: settings.show() sys_exit(0) # Instantiate updates updates: Updates = Updates(name=Bundle.NAME, settings=settings) # Version informations if options.version: print( Loading @@ -270,6 +280,12 @@ def main() -> None: Platform.flush() sys_exit(0) # Check for current updates if options.update_check: if not updates.check(): updates.check(older=True) sys_exit(0) # Arguments validation if not options.token or not options.gitlab or not options.path: print(' ') Loading @@ -289,6 +305,10 @@ def main() -> None: print(' ') Platform.flush() # Check for daily updates if updates.enabled and updates.daily: updates.check() # Result if result in [Entrypoint.Result.SUCCESS, Entrypoint.Result.FINALIZE]: sys_exit(0) Loading
src/package/bundle.py +7 −0 Changes for src/package/bundle.py: 7 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,5 +12,12 @@ class Bundle: # Sources REPOSITORY: str = 'https://gitlab.com/AdrianDC/gitlab-projects-settings' # Releases RELEASE_FIRST_TIMESTAMP: int = 1579337311 # Environment ENV_DEBUG_UPDATES_DAILY: str = 'DEBUG_UPDATES_DAILY' ENV_DEBUG_UPDATES_DISABLE: str = 'DEBUG_UPDATES_DISABLE' ENV_DEBUG_UPDATES_FAKE: str = 'DEBUG_UPDATES_FAKE' ENV_DEBUG_UPDATES_OFFLINE: str = 'DEBUG_UPDATES_OFFLINE' ENV_GITLAB_TOKEN: str = 'GITLAB_TOKEN'
src/package/updates.py 0 → 100644 +216 −0 Changes for src/package/updates.py: 216 added lines, 0 removed lines. Original line number Diff line number Diff line #!/usr/bin/env python3 # Standard libraries from datetime import datetime from os import access, environ, W_OK from time import localtime, strftime, time from typing import Optional # Components from ..prints.boxes import Boxes from ..prints.colors import Colors from ..system.platform import Platform from .bundle import Bundle from .settings import Settings from .version import Version # Updates class class Updates: # Members __enabled: bool __name: str __settings: Settings # Constructor def __init__(self, name: str, settings: Settings) -> None: # Initialize members self.__name = name self.__settings = settings # Detect migration self.__migration() # Prepare enabled if not self.__settings.has('updates', 'enabled'): self.__settings.set_bool('updates', 'enabled', True) # Check enabled self.__enabled = bool(self.__settings.get_bool('updates', 'enabled')) and \ (Bundle.ENV_DEBUG_UPDATES_DISABLE not in environ \ or not environ[Bundle.ENV_DEBUG_UPDATES_DISABLE]) # Migration def __migration(self) -> None: # Acquire versions current_version = Version.get() package_version = self.__settings.get('package', 'version') if not package_version: package_version = '0.0.0' # Refresh package version if not package_version or current_version != package_version: self.__settings.set('package', 'version', current_version) # Checker def check(self, older: bool = False) -> bool: # Reference version version = '0.0.0' if older else Version.get() # Fake test updates if Bundle.ENV_DEBUG_UPDATES_FAKE in environ: available = environ[Bundle.ENV_DEBUG_UPDATES_FAKE] if available >= version: # Show updates message release_date = datetime.utcfromtimestamp(Bundle.RELEASE_FIRST_TIMESTAMP) Updates.message(name=self.__name, older=older, available=available, date=release_date) return True # Check if not offline if Bundle.ENV_DEBUG_UPDATES_OFFLINE not in environ: # Modules libraries, pylint: disable=import-outside-toplevel from update_checker import UpdateChecker # Check for updates check = UpdateChecker(bypass_cache=True).check(self.__name, version) if check: # Show updates message Updates.message(name=self.__name, older=older, available=check.available_version, date=check.release_date) return True # Older offline failure if older: # Show offline message Updates.message(name=self.__name, offline=True) return True # Result return False # Daily @property def daily(self) -> bool: # Acquire updates check last timestamp last = self.__settings.get('updates', 'last_timestamp') # Fake test updates if Bundle.ENV_DEBUG_UPDATES_DAILY in environ: last = None # Handle daily checks current = int(time()) if not last or strftime('%Y-%m-%d', localtime(current)) != strftime( '%Y-%m-%d', localtime(int(last))): self.__settings.set('updates', 'last_timestamp', current) return True # Default fallback return False # Enabled @property def enabled(self) -> bool: return self.__enabled # Message, pylint: disable=too-many-arguments @staticmethod def message( name: str, offline: bool = False, older: bool = False, available: Optional[str] = None, date: Optional[datetime] = None, recommended: Optional[str] = None, ) -> None: # Modules libraries, pylint: disable=import-outside-toplevel from update_checker import pretty_date # Create message box box = Boxes() # Acquire current version version = Version.get() # Detect package installer package_install: str = '' if Platform.PATH_SEPARATOR + 'pipx' + Platform.PATH_SEPARATOR in Version.path(): package_install = 'pipx upgrade' # pragma: no cover else: package_install = 'pip3 install -U' # pragma: no cover # Detect package ownership writable = access(__file__, W_OK) if Platform.IS_USER_SUDO or not writable: package_install = 'sudo ' + package_install # pragma: no cover # Prepare package specification package_specification: str = '' if 'pipx' in package_install: package_specification = f'{name}' # pragma: no cover else: package_specification = f'{name}>={available}' # pragma: no cover # Evaluate same version same = available and available == version # Version message prefix version_outdated = not offline and available and not older and not same version_prefix = f'{Colors.YELLOW_LIGHT}Version: {Colors.BOLD}{name}' \ f' {Colors.RED if version_outdated else Colors.GREEN}{version}' # Recommended version message if recommended: available = recommended box.add(f'{version_prefix} {Colors.BOLD}older than recommended version ' f'{Colors.RED}{recommended}{Colors.BOLD}') # Offline version message elif offline: box.add(f'{version_prefix} {Colors.BOLD}not found, network might be down') # Updated version message elif same: box.add( f'{version_prefix} {Colors.BOLD}was released {pretty_date(date)}{Colors.BOLD}!' ) # Older version message elif older: box.add( f'{version_prefix} {Colors.BOLD}newer than {Colors.RED}{available}' \ f' {Colors.BOLD}from {pretty_date(date)}{Colors.BOLD}!' ) # Newer version message else: box.add( f'{version_prefix} {Colors.BOLD}updated {pretty_date(date)}' \ f' to {Colors.GREEN}{available}{Colors.BOLD}!' ) # Changelog message box.add( f'{Colors.YELLOW_LIGHT}Changelog: {Colors.CYAN}{Bundle.REPOSITORY}/-/releases' ) # Update message if available: box.add( f'{Colors.YELLOW_LIGHT}Update: {Colors.BOLD}' \ f"Run {Colors.GREEN}{package_install} '{package_specification}'" ) # Print message box box.print()