From da4942396ea61876270c276c4c18b7f828fe7cd6 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 1 Apr 2023 01:01:48 -0500 Subject: [PATCH 1/4] Initial development of the 'Environment' --- README.md | 23 +- im/pydeps.svg | 625 +++++++++++++++++++++++----------- local/configs/package.yaml | 2 + rcmpy/commands/all.py | 8 +- rcmpy/commands/apply.py | 27 +- rcmpy/commands/variant.py | 29 ++ rcmpy/data/schemas/State.yaml | 4 + rcmpy/environment/__init__.py | 65 ++++ rcmpy/state/__init__.py | 14 +- tests/commands/test_apply.py | 2 +- tests/resources.py | 7 +- 11 files changed, 573 insertions(+), 233 deletions(-) create mode 100644 rcmpy/commands/variant.py create mode 100644 rcmpy/environment/__init__.py diff --git a/README.md b/README.md index 2701380..c74b550 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ===================================== generator=datazen version=3.1.0 - hash=7fc24edd9b7b2970dad106d7fa968eb1 + hash=c7403ee8217c0260dff97f3404f3af77 ===================================== --> @@ -43,21 +43,24 @@ This package is tested on the following platforms: ``` $ ./venv3.8/bin/rcmpy -h -usage: rcmpy [-h] [--version] [-v] [-C DIR] {apply,use,noop} ... +usage: rcmpy [-h] [--version] [-v] [-C DIR] {apply,use,variant,noop} ... A configuration-file management system. optional arguments: - -h, --help show this help message and exit - --version show program's version number and exit - -v, --verbose set to increase logging verbosity - -C DIR, --dir DIR execute from a specific directory + -h, --help show this help message and exit + --version show program's version number and exit + -v, --verbose set to increase logging verbosity + -C DIR, --dir DIR execute from a specific directory commands: - {apply,use,noop} set of available commands - apply apply any pending changes from the active data repository - use set the directory to use as the rcmpy data repository - noop command stub (does nothing) + {apply,use,variant,noop} + set of available commands + apply apply any pending changes from the active data + repository + use set the directory to use as the rcmpy data repository + variant set the variant of configuration data to use + noop command stub (does nothing) ``` diff --git a/im/pydeps.svg b/im/pydeps.svg index a4e9c0b..ba98f47 100644 --- a/im/pydeps.svg +++ b/im/pydeps.svg @@ -4,378 +4,611 @@ - - + + G - + rcmpy___main__ - -rcmpy.__main__ + +rcmpy.__main__ rcmpy_app - -rcmpy.app + +rcmpy.app - + rcmpy_entry - -rcmpy.entry + +rcmpy.entry rcmpy_app->rcmpy_entry - - + + rcmpy_commands - -rcmpy.commands + +rcmpy.commands rcmpy_commands->rcmpy_app - - + + rcmpy_commands_all - -rcmpy. -commands. -all + +rcmpy. +commands. +all rcmpy_commands_all->rcmpy_app - + + + rcmpy_commands_apply - -rcmpy. -commands. -apply + +rcmpy. +commands. +apply rcmpy_commands_apply->rcmpy_commands_all - - + + rcmpy_commands_use - -rcmpy. -commands. -use + +rcmpy. +commands. +use rcmpy_commands_use->rcmpy_commands_all - - + + - + +rcmpy_commands_variant + +rcmpy. +commands. +variant + + + +rcmpy_commands_variant->rcmpy_commands_all + + + + + rcmpy_config - -rcmpy.config + +rcmpy.config + + + +rcmpy_config->rcmpy_commands_apply + - + rcmpy_entry->rcmpy___main__ - - + + + + + +rcmpy_paths + +rcmpy.paths + + + +rcmpy_paths->rcmpy_commands_use + + + + + +rcmpy_state + +rcmpy.state + + + +rcmpy_paths->rcmpy_state + + - + rcmpy_schemas - -rcmpy.schemas + +rcmpy.schemas - + rcmpy_schemas->rcmpy_config - - + + + + + +rcmpy_schemas->rcmpy_state + + + + + +rcmpy_state->rcmpy_commands_apply + + + + + + +rcmpy_state->rcmpy_commands_use + + + + + + +rcmpy_state->rcmpy_commands_variant + + + + + +rcmpy_xdg + +rcmpy.xdg + + + +rcmpy_xdg->rcmpy_paths + + - + vcorelib - -vcorelib + +vcorelib - + vcorelib->rcmpy_app - + - + vcorelib->rcmpy_commands_all - - - + + - + vcorelib->rcmpy_commands_apply - - - + + + + - + vcorelib->rcmpy_commands_use - - - + + + + +vcorelib->rcmpy_commands_variant + + - + vcorelib->rcmpy_config - - + + + + + +vcorelib->rcmpy_paths + + - + vcorelib->rcmpy_schemas - - + + + + + +vcorelib->rcmpy_state + + + + + + + +vcorelib->rcmpy_xdg + + - + vcorelib_args - -vcorelib.args + +vcorelib.args - + vcorelib_args->rcmpy_app - - - - - - + + - + vcorelib_args->rcmpy_commands_all - - + + + - + vcorelib_args->rcmpy_commands_apply - - + + - + vcorelib_args->rcmpy_commands_use - - + + + + + +vcorelib_args->rcmpy_commands_variant + + + - + vcorelib_dict - -vcorelib.dict + +vcorelib.dict + + + +vcorelib_dict->rcmpy_config + + + + + + + + + + +vcorelib_dict->rcmpy_paths + + - + vcorelib_dict->rcmpy_schemas - - - - + + + + + + + +vcorelib_dict->rcmpy_state + + - + vcorelib_io - -vcorelib.io + +vcorelib.io - + vcorelib_dict->vcorelib_io - - + + - + vcorelib_io_types - -vcorelib. -io. -types + +vcorelib. +io. +types - + vcorelib_dict->vcorelib_io_types - - + + + + + +vcorelib_dict_cache + +vcorelib. +dict. +cache + + + +vcorelib_dict_cache->rcmpy_state + + - + vcorelib_dict_codec - -vcorelib. -dict. -codec + +vcorelib. +dict. +codec + + + +vcorelib_dict_codec->rcmpy_config + - + vcorelib_dict_codec->rcmpy_schemas - - - - -vcorelib_io->rcmpy_config - - + + + + +vcorelib_io->rcmpy_commands_apply + + + + +vcorelib_io->rcmpy_state + + + + + + +vcorelib_io->vcorelib_dict_cache + + - + vcorelib_io->vcorelib_dict_codec - - - - + - + vcorelib_schemas - -vcorelib. -schemas + +vcorelib. +schemas - + vcorelib_io->vcorelib_schemas - - - + + + + - + vcorelib_schemas_base - -vcorelib. -schemas. -base + +vcorelib. +schemas. +base - + vcorelib_io->vcorelib_schemas_base - + + - + vcorelib_schemas_json - -vcorelib. -schemas. -json + +vcorelib. +schemas. +json - + vcorelib_io->vcorelib_schemas_json - - - - - -vcorelib_io_types->rcmpy_config - - - - + + + + + + +vcorelib_io_types->rcmpy_commands_apply + + + + + + + + +vcorelib_io_types->rcmpy_state + + + + + +vcorelib_io_types->vcorelib_dict_cache + + - + vcorelib_io_types->vcorelib_dict_codec - + + + + - + vcorelib_io_types->vcorelib_io - - + + - + vcorelib_io_types->vcorelib_schemas - - + + - + vcorelib_io_types->vcorelib_schemas_base - - - + + + - + vcorelib_io_types->vcorelib_schemas_json - - + + + + +vcorelib_paths + +vcorelib.paths + + + +vcorelib_paths->rcmpy_state + + + + + +vcorelib_paths->rcmpy_xdg + + + + + + +vcorelib_paths->vcorelib_dict_cache + + + + + + +vcorelib_paths->vcorelib_dict_codec + + + + + +vcorelib_paths->vcorelib_io + + + + + + +vcorelib_paths->vcorelib_io_types + + + + + + +vcorelib_paths->vcorelib_schemas_base + + + + + +vcorelib_paths->vcorelib_schemas_json + - + vcorelib_schemas->rcmpy_schemas - - + - + vcorelib_schemas->vcorelib_dict_codec - - + - + vcorelib_schemas_base->rcmpy_schemas - - + + - + vcorelib_schemas_base->vcorelib_dict_codec - - + + - + vcorelib_schemas_base->vcorelib_schemas - - + + - + vcorelib_schemas_base->vcorelib_schemas_json - - + + - + vcorelib_schemas_json->rcmpy_schemas - - - + diff --git a/local/configs/package.yaml b/local/configs/package.yaml index e6bb169..3b489a8 100644 --- a/local/configs/package.yaml +++ b/local/configs/package.yaml @@ -14,3 +14,5 @@ commands: description: apply any pending changes from the active data repository - name: use description: set the directory to use as the rcmpy data repository + - name: variant + description: set the variant of configuration data to use diff --git a/rcmpy/commands/all.py b/rcmpy/commands/all.py index 584b19f..d30a435 100644 --- a/rcmpy/commands/all.py +++ b/rcmpy/commands/all.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.0 -# hash=2fe3aee53e45a111b6f9b09b1e34008b +# hash=1a37f6440c7e2e970222817d08f27c24 # ===================================== """ @@ -18,6 +18,7 @@ # internal from rcmpy.commands.apply import add_apply_cmd from rcmpy.commands.use import add_use_cmd +from rcmpy.commands.variant import add_variant_cmd def commands() -> _List[_Tuple[str, str, _CommandRegister]]: @@ -34,5 +35,10 @@ def commands() -> _List[_Tuple[str, str, _CommandRegister]]: "set the directory to use as the rcmpy data repository", add_use_cmd, ), + ( + "variant", + "set the variant of configuration data to use", + add_variant_cmd, + ), ("noop", "command stub (does nothing)", lambda _: lambda _: 0), ] diff --git a/rcmpy/commands/apply.py b/rcmpy/commands/apply.py index e20b5d0..858d9db 100644 --- a/rcmpy/commands/apply.py +++ b/rcmpy/commands/apply.py @@ -5,41 +5,22 @@ # built-in from argparse import ArgumentParser as _ArgumentParser from argparse import Namespace as _Namespace -from logging import getLogger # third-party from vcorelib.args import CommandFunction as _CommandFunction -from vcorelib.io.types import FileExtension # internal -from rcmpy import PKG_NAME -from rcmpy.config import Config -from rcmpy.state import load_state - -LOG = getLogger(__name__) +from rcmpy.environment import load_environment def apply_cmd(_: _Namespace) -> int: """Execute the apply command.""" - with load_state() as state: - config_base = state.directory.joinpath(PKG_NAME) - - config_candidates = list( - FileExtension.data_candidates(config_base, exists_only=True) - ) - - if not config_candidates: - LOG.error( - "No config found: %s.", - list(FileExtension.data_candidates(config_base)), - ) + with load_environment() as env: + if not env.config_loaded: return 1 - assert len(config_candidates) == 1, config_candidates - - config = Config.decode(config_candidates[0]) - print(config) + print(env.config) return 0 diff --git a/rcmpy/commands/variant.py b/rcmpy/commands/variant.py new file mode 100644 index 0000000..51728a6 --- /dev/null +++ b/rcmpy/commands/variant.py @@ -0,0 +1,29 @@ +""" +An entry-point for the 'variant' command. +""" + +# built-in +from argparse import ArgumentParser as _ArgumentParser +from argparse import Namespace as _Namespace + +# third-party +from vcorelib.args import CommandFunction as _CommandFunction + +# internal +from rcmpy.state import load_state + + +def variant_cmd(args: _Namespace) -> int: + """Execute the variant command.""" + + with load_state() as state: + state.set_variant(args.variant) + + return 0 + + +def add_variant_cmd(parser: _ArgumentParser) -> _CommandFunction: + """Add variant-command arguments to its parser.""" + + parser.add_argument("variant", help="new variant to use") + return variant_cmd diff --git a/rcmpy/data/schemas/State.yaml b/rcmpy/data/schemas/State.yaml index c9a0645..2516ec2 100644 --- a/rcmpy/data/schemas/State.yaml +++ b/rcmpy/data/schemas/State.yaml @@ -5,3 +5,7 @@ additionalProperties: false properties: directory: type: string + + variant: + type: string + default: "" diff --git a/rcmpy/environment/__init__.py b/rcmpy/environment/__init__.py new file mode 100644 index 0000000..9ce1e1a --- /dev/null +++ b/rcmpy/environment/__init__.py @@ -0,0 +1,65 @@ +""" +A module implementing the package's runtime environment. +""" + +# built-in +from contextlib import contextmanager +from typing import Iterator, Optional + +# third-party +from vcorelib.io.types import FileExtension +from vcorelib.logging import LoggerMixin +from vcorelib.paths import Pathlike + +# internal +from rcmpy import PKG_NAME +from rcmpy.config import Config +from rcmpy.state import State, load_state + + +class Environment(LoggerMixin): + """A class implementing this package's runtime environment.""" + + def __init__(self, state: State) -> None: + """Initialize this instance.""" + + super().__init__() + self.state = state + self._config: Optional[Config] = None + + config_base = state.directory.joinpath(PKG_NAME) + + config_candidates = list( + FileExtension.data_candidates(config_base, exists_only=True) + ) + + if not config_candidates: + self.logger.error( + "No config found: %s.", + list(FileExtension.data_candidates(config_base)), + ) + else: + assert len(config_candidates) == 1, config_candidates + self._config = Config.decode(config_candidates[0]) + self.logger.info("Loaded config '%s'.", config_candidates[0]) + + @property + def config_loaded(self) -> bool: + """Determine if this environment has loaded a config.""" + return self._config is not None + + @property + def config(self) -> Config: + """Get this environment's configuration object.""" + assert self._config is not None, "Config not loaded!" + return self._config + + +@contextmanager +def load_environment( + root: Pathlike = None, name: str = "state.json" +) -> Iterator[Environment]: + """A wrapper for loading an environment with default state data.""" + + with load_state(root=root, name=name) as state: + yield Environment(state) diff --git a/rcmpy/state/__init__.py b/rcmpy/state/__init__.py index 295ac8a..b7806f7 100644 --- a/rcmpy/state/__init__.py +++ b/rcmpy/state/__init__.py @@ -25,6 +25,7 @@ class State(_RcmpyDictCodec): """The top-level configuration object for the package.""" directory: Path + variant: str def init(self, data: _JsonObject) -> None: """Perform implementation-specific initialization.""" @@ -37,6 +38,10 @@ def init(self, data: _JsonObject) -> None: self.logger.info("Using directory '%s'.", self.directory) + self.variant: str = cast(str, data["variant"]) + if self.variant: + self.logger.info("Using variant '%s'.", self.variant) + def set_directory(self, path: Pathlike) -> None: """Set a new directory to use as the data repository.""" @@ -49,10 +54,17 @@ def set_directory(self, path: Pathlike) -> None: self.directory = new_dir self.logger.info("Set directory to '%s'.", new_dir) + def set_variant(self, variant: str) -> None: + """Set a new variant value.""" + + if variant != self.variant: + self.variant = variant + self.logger.info("Updating variant to '%s'.", variant) + def asdict(self) -> _JsonObject: """Obtain a dictionary representing this instance.""" - return {"directory": str(self.directory)} + return {"directory": str(self.directory), "variant": self.variant} @contextmanager diff --git a/tests/commands/test_apply.py b/tests/commands/test_apply.py index 8fac08a..a1af561 100644 --- a/tests/commands/test_apply.py +++ b/tests/commands/test_apply.py @@ -15,7 +15,7 @@ def test_apply_command_basic(): args = [PKG_NAME, "apply"] - with scenario("empty"): + with scenario("empty", variant="test"): assert rcmpy_main(args) != 0 with scenario("simple"): diff --git a/tests/resources.py b/tests/resources.py index 6c345bd..0821e2c 100644 --- a/tests/resources.py +++ b/tests/resources.py @@ -33,7 +33,7 @@ def resource( @contextmanager -def scenario(name: str) -> Iterator[Path]: +def scenario(name: str, variant: str = None) -> Iterator[Path]: """Ensure a specific test scenario is set up for execution.""" path = resource("scenarios", name) @@ -41,4 +41,9 @@ def scenario(name: str) -> Iterator[Path]: # Use a temporary directory for state. with override_environ_tempdir("XDG_STATE_HOME"): assert rcmpy_main([PKG_NAME, "use", str(path)]) == 0 + + # Set the variant if it was provided. + if variant is not None: + assert rcmpy_main([PKG_NAME, "variant", variant]) == 0 + yield path From 6573e74f16868134711a1823fcfb282813138996 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 1 Apr 2023 16:28:11 -0500 Subject: [PATCH 2/4] Working on some initial docs --- .github/workflows/python-package.yml | 1 + README.md | 83 +++- im/pydeps.svg | 668 ++++++++++++++------------ local/configs/python.yaml | 2 +- local/includes/sub_commands.yaml | 15 + local/templates/README.md.j2 | 48 ++ local/templates/data_repository.md.j2 | 5 + manifest.yaml | 10 + md/data_repository.md | 13 + pyproject.toml | 1 + setup.py | 3 +- 11 files changed, 533 insertions(+), 316 deletions(-) create mode 100644 local/includes/sub_commands.yaml create mode 100644 local/templates/data_repository.md.j2 create mode 100644 md/data_repository.md diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c65d7ef..3c1c720 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -21,6 +21,7 @@ jobs: - "3.8" - "3.9" - "3.10" + - "3.11" system: - ubuntu-latest - macos-latest diff --git a/README.md b/README.md index c74b550..9b3ba15 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ===================================== generator=datazen version=3.1.0 - hash=c7403ee8217c0260dff97f3404f3af77 + hash=fe3e09e0d89218f917b0f128f402465c ===================================== --> @@ -27,6 +27,7 @@ This package is tested with the following Python minor versions: * [`python3.8`](https://docs.python.org/3.8/) * [`python3.9`](https://docs.python.org/3.9/) * [`python3.10`](https://docs.python.org/3.10/) +* [`python3.11`](https://docs.python.org/3.11/) ## Platform Support @@ -38,6 +39,41 @@ This package is tested on the following platforms: # Introduction +This project aims to simplify management of user configuration files, user (or +system-wide) package installations, system settings and more on a variety of +platforms. + +Reasons to use it: +1. Simplify bootstrapping a fresh system to a "developer workstation" in as few +steps as possible + 1. This can be a simpler options for teams or individuals who don't have + infrastructure to manage system-install images, system-distribution build + systems, other kinds of provisioning automation, etc. +1. Normalize software configurations for a team of developers, but still allow +personalized overrides when desired (e.g. text-editor or terminal configs) + 1. This can help make your team or organization's development environment + more approachable to new or inexperienced developers (ask yourself this: + are the code changes usually even the hard part?) +1. Relies on a minimal [data repository](md/data_repository.md) that can be +managed with version control, shared by multiple people, open source to provide +examples to the community, etc. + 1. This enables a workflow for adding, removing, updating and re-configuring + software used by a project-ecosystem over time + +# Getting Started + +This package attempts to adhere to the +[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). +It keeps stateful information that needs to persist between command invocations +in a `rcmpy` sub-directory in the user-state directory controlled +by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). + +One tracked piece of stateful information is the location of the current +[data repository](md/data_repository.md). If this is not changed (via the `use` +command), it checks a `rcmpy/default` sub-directory in the +user-config directory controlled by `XDG_CONFIG_HOME` (or the default: +`$HOME/.config`). + # Command-line Options ``` @@ -64,6 +100,51 @@ commands: ``` +## Sub-command Options + +### `apply` + +``` +$ ./venv3.8/bin/rcmpy apply -h + +usage: rcmpy apply [-h] + +optional arguments: + -h, --help show this help message and exit + +``` + +### `use` + +``` +$ ./venv3.8/bin/rcmpy use -h + +usage: rcmpy use [-h] [-d] [directory] + +positional arguments: + directory the directory to use + +optional arguments: + -h, --help show this help message and exit + -d, --default sets the directory back to the package default + +``` + +### `variant` + +``` +$ ./venv3.8/bin/rcmpy variant -h + +usage: rcmpy variant [-h] variant + +positional arguments: + variant new variant to use + +optional arguments: + -h, --help show this help message and exit + +``` + # Internal Dependency Graph A coarse view of the internal structure and scale of diff --git a/im/pydeps.svg b/im/pydeps.svg index ba98f47..e90787c 100644 --- a/im/pydeps.svg +++ b/im/pydeps.svg @@ -4,611 +4,653 @@ - - + + G - + rcmpy___main__ - -rcmpy.__main__ + +rcmpy.__main__ rcmpy_app - -rcmpy.app + +rcmpy.app rcmpy_entry - -rcmpy.entry + +rcmpy.entry rcmpy_app->rcmpy_entry - - + + rcmpy_commands - -rcmpy.commands + +rcmpy.commands rcmpy_commands->rcmpy_app - - + + rcmpy_commands_all - -rcmpy. -commands. -all + +rcmpy. +commands. +all rcmpy_commands_all->rcmpy_app - - - + rcmpy_commands_apply - -rcmpy. -commands. -apply + +rcmpy. +commands. +apply rcmpy_commands_apply->rcmpy_commands_all - - + + rcmpy_commands_use - -rcmpy. -commands. -use + +rcmpy. +commands. +use rcmpy_commands_use->rcmpy_commands_all - - + + rcmpy_commands_variant - -rcmpy. -commands. -variant + +rcmpy. +commands. +variant rcmpy_commands_variant->rcmpy_commands_all - - + + rcmpy_config - -rcmpy.config + +rcmpy.config - + + +rcmpy_environment + +rcmpy. +environment + + -rcmpy_config->rcmpy_commands_apply - +rcmpy_config->rcmpy_environment + + rcmpy_entry->rcmpy___main__ - - + + + + + +rcmpy_environment->rcmpy_commands_apply + + + - + rcmpy_paths - -rcmpy.paths + +rcmpy.paths - + rcmpy_paths->rcmpy_commands_use - - + - + rcmpy_state - -rcmpy.state + +rcmpy.state - + rcmpy_paths->rcmpy_state - - + + - + rcmpy_schemas - -rcmpy.schemas + +rcmpy.schemas - + rcmpy_schemas->rcmpy_config - - + + - -rcmpy_schemas->rcmpy_state - - - - -rcmpy_state->rcmpy_commands_apply - - - +rcmpy_schemas->rcmpy_state + + rcmpy_state->rcmpy_commands_use - - - + + + rcmpy_state->rcmpy_commands_variant - - + + + + + + +rcmpy_state->rcmpy_environment + + - + rcmpy_xdg - -rcmpy.xdg + +rcmpy.xdg - + rcmpy_xdg->rcmpy_paths - - + + - + vcorelib - -vcorelib + +vcorelib - + vcorelib->rcmpy_app - + - + vcorelib->rcmpy_commands_all - - + + - + vcorelib->rcmpy_commands_apply - - - - + - + vcorelib->rcmpy_commands_use - + + + - + vcorelib->rcmpy_commands_variant - - + + - + vcorelib->rcmpy_config - - + + + + + +vcorelib->rcmpy_environment + + + - + vcorelib->rcmpy_paths - - + + - + vcorelib->rcmpy_schemas - - + + + - + vcorelib->rcmpy_state - - - - + + + + + - + vcorelib->rcmpy_xdg - - + + - + vcorelib_args - -vcorelib.args + +vcorelib.args - + vcorelib_args->rcmpy_app - - + + + + - + vcorelib_args->rcmpy_commands_all - - - + + - + vcorelib_args->rcmpy_commands_apply - - + + + + - + vcorelib_args->rcmpy_commands_use - - + + - + vcorelib_args->rcmpy_commands_variant - - - + + - + vcorelib_dict - -vcorelib.dict + +vcorelib.dict - + vcorelib_dict->rcmpy_config - - - - - - - + + + - + vcorelib_dict->rcmpy_paths - - + + - + vcorelib_dict->rcmpy_schemas - - - - + + + - + vcorelib_dict->rcmpy_state - - + - + vcorelib_io - -vcorelib.io + +vcorelib.io - + vcorelib_dict->vcorelib_io - - + - + vcorelib_io_types - -vcorelib. -io. -types + +vcorelib. +io. +types - + vcorelib_dict->vcorelib_io_types - - + + + - + vcorelib_dict_cache - -vcorelib. -dict. -cache + +vcorelib. +dict. +cache - + vcorelib_dict_cache->rcmpy_state - - + - + vcorelib_dict_codec - -vcorelib. -dict. -codec + +vcorelib. +dict. +codec - + vcorelib_dict_codec->rcmpy_config - + + - + vcorelib_dict_codec->rcmpy_schemas - + + - - -vcorelib_io->rcmpy_commands_apply - + + +vcorelib_io->rcmpy_environment + + - + vcorelib_io->rcmpy_state - - - + + + - + vcorelib_io->vcorelib_dict_cache - - + + + - + vcorelib_io->vcorelib_dict_codec - + - + vcorelib_schemas - -vcorelib. -schemas + +vcorelib. +schemas - + vcorelib_io->vcorelib_schemas - - - - + + + - + vcorelib_schemas_base - -vcorelib. -schemas. -base + +vcorelib. +schemas. +base - + vcorelib_io->vcorelib_schemas_base - - + + + - + vcorelib_schemas_json - -vcorelib. -schemas. -json + +vcorelib. +schemas. +json - + vcorelib_io->vcorelib_schemas_json - - - + + - - -vcorelib_io_types->rcmpy_commands_apply - - - - - + + +vcorelib_io_types->rcmpy_environment + + - + vcorelib_io_types->rcmpy_state - - + + + - + vcorelib_io_types->vcorelib_dict_cache - - + - + vcorelib_io_types->vcorelib_dict_codec - - - - + + - + vcorelib_io_types->vcorelib_io - - + + - + vcorelib_io_types->vcorelib_schemas - - + + - + vcorelib_io_types->vcorelib_schemas_base - - - + + - + vcorelib_io_types->vcorelib_schemas_json - + + + + + + +vcorelib_logging + +vcorelib. +logging + + + +vcorelib_logging->rcmpy_environment + - + vcorelib_paths - -vcorelib.paths + +vcorelib.paths + + + +vcorelib_paths->rcmpy_environment + + + + + + + - + vcorelib_paths->rcmpy_state - - + + - + vcorelib_paths->rcmpy_xdg - - - + + - + vcorelib_paths->vcorelib_dict_cache - - - + - + vcorelib_paths->vcorelib_dict_codec - - + + + + - + vcorelib_paths->vcorelib_io - - - + + + - + vcorelib_paths->vcorelib_io_types - - - + + - + vcorelib_paths->vcorelib_schemas_base - - + + - + vcorelib_paths->vcorelib_schemas_json - + + + + - + vcorelib_schemas->rcmpy_schemas - + + + + - + vcorelib_schemas->vcorelib_dict_codec - + + + - + vcorelib_schemas_base->rcmpy_schemas - - + + - + vcorelib_schemas_base->vcorelib_dict_codec - - + - + vcorelib_schemas_base->vcorelib_schemas - - + + - + vcorelib_schemas_base->vcorelib_schemas_json - - + + - + vcorelib_schemas_json->rcmpy_schemas - + diff --git a/local/configs/python.yaml b/local/configs/python.yaml index f490574..ebd8df9 100644 --- a/local/configs/python.yaml +++ b/local/configs/python.yaml @@ -3,7 +3,7 @@ author_info: name: Vaughn Kottler email: vaughnkottler@gmail.com username: vkottler -versions: ["3.7", "3.8", "3.9", "3.10"] +versions: ["3.7", "3.8", "3.9", "3.10", "3.11"] systems: - macos-latest diff --git a/local/includes/sub_commands.yaml b/local/includes/sub_commands.yaml new file mode 100644 index 0000000..42aac4e --- /dev/null +++ b/local/includes/sub_commands.yaml @@ -0,0 +1,15 @@ +# yamllint disable-file +--- +default_dirs: false + +commands: +{% for command in ["apply", "use", "variant"] %} + - name: help-{{command}} + command: "./venv{{python_version}}/bin/{{entry}}" + force: true + arguments: + - {{command}} + - "-h" + dependencies: + - commands-install-local +{% endfor %} diff --git a/local/templates/README.md.j2 b/local/templates/README.md.j2 index e2dda53..78835db 100644 --- a/local/templates/README.md.j2 +++ b/local/templates/README.md.j2 @@ -1,4 +1,39 @@ {{python_readme_header_md}} + +This project aims to simplify management of user configuration files, user (or +system-wide) package installations, system settings and more on a variety of +platforms. + +Reasons to use it: +1. Simplify bootstrapping a fresh system to a "developer workstation" in as few +steps as possible + 1. This can be a simpler options for teams or individuals who don't have + infrastructure to manage system-install images, system-distribution build + systems, other kinds of provisioning automation, etc. +1. Normalize software configurations for a team of developers, but still allow +personalized overrides when desired (e.g. text-editor or terminal configs) + 1. This can help make your team or organization's development environment + more approachable to new or inexperienced developers (ask yourself this: + are the code changes usually even the hard part?) +1. Relies on a minimal [data repository](md/data_repository.md) that can be +managed with version control, shared by multiple people, open source to provide +examples to the community, etc. + 1. This enables a workflow for adding, removing, updating and re-configuring + software used by a project-ecosystem over time + +# Getting Started + +This package attempts to adhere to the +[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). +It keeps stateful information that needs to persist between command invocations +in a `{{package["name"]}}` sub-directory in the user-state directory controlled +by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). + +One tracked piece of stateful information is the location of the current +[data repository](md/data_repository.md). If this is not changed (via the `use` +command), it checks a `{{package["name"]}}/default` sub-directory in the +user-config directory controlled by `XDG_CONFIG_HOME` (or the default: +`$HOME/.config`). {% if "help" in global %} # Command-line Options @@ -10,4 +45,17 @@ $ {{" ".join(help["args"])}} ``` {% endif %} +## Sub-command Options +{% for command in package["commands"] %} + +### `{{command["name"]}}` +{% set help_data = global["help-" + command["name"]] %} + +``` +$ {{" ".join(help_data["args"])}} + +{{help_data["stdout"]}} +``` +{% endfor %} + {{python_readme_dep_graph_md}} diff --git a/local/templates/data_repository.md.j2 b/local/templates/data_repository.md.j2 new file mode 100644 index 0000000..2d0997b --- /dev/null +++ b/local/templates/data_repository.md.j2 @@ -0,0 +1,5 @@ +# Data Repository + +([back](../README.md#getting-started)) + +TODO. diff --git a/manifest.yaml b/manifest.yaml index dec08aa..104755d 100644 --- a/manifest.yaml +++ b/manifest.yaml @@ -10,6 +10,7 @@ includes: - config/includes/python.yaml - config/includes/license.yaml - config/includes/funding.yaml + - local/includes/sub_commands.yaml templates: - local/templates @@ -29,6 +30,14 @@ renders: - renders-python_readme_header.md - renders-python_readme_dep_graph.md - commands-help + - commands-help-apply + - commands-help-use + - commands-help-variant + + - name: data_repository.md + output_dir: "md" + dependencies: + - compiles-local - name: app.py output_dir: "{{project}}" @@ -47,5 +56,6 @@ groups: - groups-license - groups-funding - renders-README.md + - renders-data_repository.md - renders-app.py - renders-all.py diff --git a/md/data_repository.md b/md/data_repository.md new file mode 100644 index 0000000..1c10c66 --- /dev/null +++ b/md/data_repository.md @@ -0,0 +1,13 @@ + + +# Data Repository + +([back](../README.md#getting-started)) + +TODO. diff --git a/pyproject.toml b/pyproject.toml index 33f8c23..2e991c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS", "Operating System :: POSIX :: Linux", diff --git a/setup.py b/setup.py index f3fc7ee..3103347 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # ===================================== # generator=datazen # version=3.1.0 -# hash=416bc5bcfd6143aac0a78a91308857db +# hash=dc0d9f835ecbdaf7ba7d62dc617acb8d # ===================================== """ @@ -32,6 +32,7 @@ "3.8", "3.9", "3.10", + "3.11", ], } setup( From 8a16edd1bd27dc8ea2ceb8d6037c306d20721ccb Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 1 Apr 2023 19:24:07 -0500 Subject: [PATCH 3/4] Add information to README --- README.md | 41 ++++++++++++++++++++++++++++++++++-- local/templates/README.md.j2 | 39 +++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9b3ba15..c5518cc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ===================================== generator=datazen version=3.1.0 - hash=fe3e09e0d89218f917b0f128f402465c + hash=69570046b0ce602c62d5d9c8831c75d9 ===================================== --> @@ -64,7 +64,7 @@ examples to the community, etc. This package attempts to adhere to the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). -It keeps stateful information that needs to persist between command invocations +It keeps stateful formation that needs to persist between command invocations in a `rcmpy` sub-directory in the user-state directory controlled by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). @@ -74,6 +74,43 @@ command), it checks a `rcmpy/default` sub-directory in the user-config directory controlled by `XDG_CONFIG_HOME` (or the default: `$HOME/.config`). +## System Requirements + +1. It is assumed that the system has a [Python](https://www.python.org/) +executable and [pip](https://pypi.org/project/pip/) is available to it as +an installed package (can be checked with: `python[3][.exe] -m pip --version` +or `pip[3][.exe] --version`). +2. A mechanism to obtain a [data repository](md/data_repository.md) via a +network connection, if one won't be created from scratch (e.g. a +[git](https://git-scm.com/) client). + +## Installation and Setup + +1. Install the package with `pip[3][.exe] --user rcmpy` or +`python[3][.exe] -m pip --user rcmpy`. +1. Test that `rcmpy` is now a shell command with +`rcmpy --version`. + 1. If not, you may need to invoke + `rcmpy` directly from `$HOME/.local/bin` or + `%APPDATA%\Python`. +(see the +[pip documentation](https://pip.pypa.io/en/stable/user_guide/?highlight=--user#user-installs) +for more info). +1. Run `rcmpy use` to view the default +[data repository](md/data_repository.md) location (printed to the console): + +``` +$ rcmpy use +rcmpy.state - INFO - Using directory '/home/vkottler/.config/rcmpy/default'. +``` + +4. Begin setting up your data repository in this location, or: + 1. Download (or `git clone`) your data repository to that default location. + 1. Create a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) at + that location, pointing to your data repository. + 1. Run `rcmpy use ` to point `rcmpy` at + an existing data repository at any arbitrary location. + # Command-line Options ``` diff --git a/local/templates/README.md.j2 b/local/templates/README.md.j2 index 78835db..91b1d07 100644 --- a/local/templates/README.md.j2 +++ b/local/templates/README.md.j2 @@ -25,7 +25,7 @@ examples to the community, etc. This package attempts to adhere to the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). -It keeps stateful information that needs to persist between command invocations +It keeps stateful formation that needs to persist between command invocations in a `{{package["name"]}}` sub-directory in the user-state directory controlled by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). @@ -34,6 +34,43 @@ One tracked piece of stateful information is the location of the current command), it checks a `{{package["name"]}}/default` sub-directory in the user-config directory controlled by `XDG_CONFIG_HOME` (or the default: `$HOME/.config`). + +## System Requirements + +1. It is assumed that the system has a [Python](https://www.python.org/) +executable and [pip](https://pypi.org/project/pip/) is available to it as +an installed package (can be checked with: `python[3][.exe] -m pip --version` +or `pip[3][.exe] --version`). +2. A mechanism to obtain a [data repository](md/data_repository.md) via a +network connection, if one won't be created from scratch (e.g. a +[git](https://git-scm.com/) client). + +## Installation and Setup + +1. Install the package with `pip[3][.exe] --user {{package["name"]}}` or +`python[3][.exe] -m pip --user {{package["name"]}}`. +1. Test that `{{package["name"]}}` is now a shell command with +`{{package["name"]}} --version`. + 1. If not, you may need to invoke + `{{package["name"]}}` directly from `$HOME/.local/bin` or + `%APPDATA%\Python`. +(see the +[pip documentation](https://pip.pypa.io/en/stable/user_guide/?highlight=--user#user-installs) +for more info). +1. Run `{{package["name"]}} use` to view the default +[data repository](md/data_repository.md) location (printed to the console): + +``` +$ rcmpy use +rcmpy.state - INFO - Using directory '/home/vkottler/.config/rcmpy/default'. +``` + +4. Begin setting up your data repository in this location, or: + 1. Download (or `git clone`) your data repository to that default location. + 1. Create a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) at + that location, pointing to your data repository. + 1. Run `{{package["name"]}} use ` to point `{{package["name"]}}` at + an existing data repository at any arbitrary location. {% if "help" in global %} # Command-line Options From be5a91bac397aa4c57952f027246e3c007cd36d4 Mon Sep 17 00:00:00 2001 From: Vaughn Kottler Date: Sat, 1 Apr 2023 19:42:32 -0500 Subject: [PATCH 4/4] More documentation work --- README.md | 15 +++++++++------ local/templates/README.md.j2 | 13 ++++++++----- local/templates/data_repository.md.j2 | 20 +++++++++++++++++++- md/data_repository.md | 22 ++++++++++++++++++++-- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index c5518cc..a55ddde 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ===================================== generator=datazen version=3.1.0 - hash=69570046b0ce602c62d5d9c8831c75d9 + hash=dde35c48192e3d587fd62db12dcf92bb ===================================== --> @@ -65,7 +65,7 @@ examples to the community, etc. This package attempts to adhere to the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). It keeps stateful formation that needs to persist between command invocations -in a `rcmpy` sub-directory in the user-state directory controlled +in a `rcmpy` sub-directory of the user-state directory controlled by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). One tracked piece of stateful information is the location of the current @@ -104,12 +104,15 @@ $ rcmpy use rcmpy.state - INFO - Using directory '/home/vkottler/.config/rcmpy/default'. ``` -4. Begin setting up your data repository in this location, or: - 1. Download (or `git clone`) your data repository to that default location. +4. Begin setting up your [data repository](md/data_repository.md) in this +location, or: + 1. Download (or `git clone`) one to that default location. 1. Create a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) at - that location, pointing to your data repository. + that location, pointing to one. 1. Run `rcmpy use ` to point `rcmpy` at - an existing data repository at any arbitrary location. + an existing one at any arbitrary location. +5. Run `rcmpy apply` to perform tasks specified in the +[top-level configuration file](md/data_repository.md#top-level-configuration). # Command-line Options diff --git a/local/templates/README.md.j2 b/local/templates/README.md.j2 index 91b1d07..5c671e1 100644 --- a/local/templates/README.md.j2 +++ b/local/templates/README.md.j2 @@ -26,7 +26,7 @@ examples to the community, etc. This package attempts to adhere to the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). It keeps stateful formation that needs to persist between command invocations -in a `{{package["name"]}}` sub-directory in the user-state directory controlled +in a `{{package["name"]}}` sub-directory of the user-state directory controlled by `XDG_STATE_HOME` (or the default: `$HOME/.local/state`). One tracked piece of stateful information is the location of the current @@ -65,12 +65,15 @@ $ rcmpy use rcmpy.state - INFO - Using directory '/home/vkottler/.config/rcmpy/default'. ``` -4. Begin setting up your data repository in this location, or: - 1. Download (or `git clone`) your data repository to that default location. +4. Begin setting up your [data repository](md/data_repository.md) in this +location, or: + 1. Download (or `git clone`) one to that default location. 1. Create a [symbolic link](https://en.wikipedia.org/wiki/Symbolic_link) at - that location, pointing to your data repository. + that location, pointing to one. 1. Run `{{package["name"]}} use ` to point `{{package["name"]}}` at - an existing data repository at any arbitrary location. + an existing one at any arbitrary location. +5. Run `rcmpy apply` to perform tasks specified in the +[top-level configuration file](md/data_repository.md#top-level-configuration). {% if "help" in global %} # Command-line Options diff --git a/local/templates/data_repository.md.j2 b/local/templates/data_repository.md.j2 index 2d0997b..404ef13 100644 --- a/local/templates/data_repository.md.j2 +++ b/local/templates/data_repository.md.j2 @@ -2,4 +2,22 @@ ([back](../README.md#getting-started)) -TODO. +The data repository contains all source files and configuration data that +describes the work that `{{package["name"]}}` should do. + +Design goals: +- Integrates easily with version-control software by using only text data +- Can be shared by multiple active users or developers + - Configuration files and data can be isolated to a particular user's + "variant" + - User-specific additions can be promoted to common, shared assets easily + (e.g. the "new default" for users that don't tailor their own settings) +- Able to describe software/packages that should be installed, and at what +version +- The single-source-of-truth and backbone of "what software does my workflow or +entire ecosystem use" +- Be as intuitive to understand in organization and function as possible + +## Top-level Configuration + +See also: [schema](../rcmpy/data/schemas/Config.yaml). diff --git a/md/data_repository.md b/md/data_repository.md index 1c10c66..11c8792 100644 --- a/md/data_repository.md +++ b/md/data_repository.md @@ -2,7 +2,7 @@ ===================================== generator=datazen version=3.1.0 - hash=46e9e1c466d851096049561754127690 + hash=4e92d9930b81742f094d65fc35fb1b4a ===================================== --> @@ -10,4 +10,22 @@ ([back](../README.md#getting-started)) -TODO. +The data repository contains all source files and configuration data that +describes the work that `rcmpy` should do. + +Design goals: +- Integrates easily with version-control software by using only text data +- Can be shared by multiple active users or developers + - Configuration files and data can be isolated to a particular user's + "variant" + - User-specific additions can be promoted to common, shared assets easily + (e.g. the "new default" for users that don't tailor their own settings) +- Able to describe software/packages that should be installed, and at what +version +- The single-source-of-truth and backbone of "what software does my workflow or +entire ecosystem use" +- Be as intuitive to understand in organization and function as possible + +## Top-level Configuration + +See also: [schema](../rcmpy/data/schemas/Config.yaml).