diff --git a/.editorconfig b/.editorconfig index d4a2c44..0776bd6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,18 +4,18 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf -[*.bat] -indent_style = tab -end_of_line = crlf +[*.py] +indent_style = space +indent_size = 4 [LICENSE] -insert_final_newline = false +insert_final_newline = true [Makefile] indent_style = tab diff --git a/.env b/.env index 3222049..7714016 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -KXGR_GROUP=group1 +SUGAR_GROUP=group1 diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 49729f3..30e5bec 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,11 +1,11 @@ -* sugar version: -* Python version: -* Operating System: +- sugar version: +- Python version: +- Operating System: ### Description -Describe what you were trying to get done. -Tell us what happened, what went wrong, and what you expected to happen. +Describe what you were trying to get done. Tell us what happened, what went +wrong, and what you expected to happen. ### What I Did diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3d3c4b9..a5e3c9e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,5 @@ ## Pull Request description + -* ```...``` +- `...` + ## Pull Request checklists This PR is a: + - [ ] bug-fix - [ ] new feature - [ ] maintenance About this PR: + - [ ] it includes tests. - [ ] the tests are executed on CI. - [ ] the tests generate log file(s) (path). @@ -34,8 +38,10 @@ About this PR: - [ ] this PR requires a project documentation update. Author's checklist: + - [ ] I have reviewed the changes and it contains no misspelling. -- [ ] The code is well commented, especially in the parts that contain more complexity. +- [ ] The code is well commented, especially in the parts that contain more + complexity. - [ ] New and old tests passed locally. ## Additional information diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 93c209a..8f93861 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -2,9 +2,9 @@ name: main on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: check-branch: @@ -39,48 +39,48 @@ jobs: strategy: matrix: python_version: - - "3.8" - - "3.9" - - "3.10" - - "3.11" - - "3.12" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" concurrency: group: ci-tests-${{ matrix.python_version }}-${{ github.ref }} cancel-in-progress: true env: - KXGR_PROJECT_NAME: ${{ github.run_id }}-${{ github.sha }} + SUGAR_PROJECT_NAME: ${{ github.run_id }}-${{ github.sha }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - uses: conda-incubator/setup-miniconda@v3 - with: - miniconda-version: "latest" - environment-file: conda/dev.yaml - channels: nodefaults,conda-forge - channel-priority: strict - activate-environment: sugar - auto-update-conda: true - conda-solver: libmamba - python-version: "${{ matrix.python_version }}" + - uses: conda-incubator/setup-miniconda@v3 + with: + miniconda-version: "latest" + environment-file: conda/dev.yaml + channels: nodefaults,conda-forge + channel-priority: strict + activate-environment: sugar + auto-update-conda: true + conda-solver: libmamba + python-version: "${{ matrix.python_version }}" - - name: check poetry lock - run: poetry check + - name: check poetry lock + run: poetry check - - name: Install dependencies - run: poetry install --verbose + - name: Install dependencies + run: poetry install --verbose - - name: run unit tests - run: makim --verbose tests.unit + - name: run unit tests + run: makim --verbose tests.unit - - name: CLI tests - run: makim --verbose tests.smoke + - name: CLI tests + run: makim --verbose tests.smoke - - name: Setup tmate session - if: "${{ failure() && (contains(github.event.pull_request.labels.*.name, 'ci:enable-debugging')) }}" - uses: mxschmitt/action-tmate@v3 + - name: Setup tmate session + if: "${{ failure() && (contains(github.event.pull_request.labels.*.name, 'ci:enable-debugging')) }}" + uses: mxschmitt/action-tmate@v3 linter: runs-on: ubuntu-latest @@ -95,21 +95,21 @@ jobs: cancel-in-progress: true steps: - - uses: actions/checkout@v4 - - - uses: conda-incubator/setup-miniconda@v3 - with: - miniconda-version: "latest" - environment-file: conda/dev.yaml - channels: nodefaults,conda-forge - channel-priority: strict - activate-environment: sugar - auto-update-conda: true - conda-solver: libmamba - - - name: Install dependencies - run: | - poetry install --verbose - - - name: Run style checks - run: makim tests.linter + - uses: actions/checkout@v4 + + - uses: conda-incubator/setup-miniconda@v3 + with: + miniconda-version: "latest" + environment-file: conda/dev.yaml + channels: nodefaults,conda-forge + channel-priority: strict + activate-environment: sugar + auto-update-conda: true + conda-solver: libmamba + + - name: Install dependencies + run: | + poetry install --verbose + + - name: Run style checks + run: makim tests.linter diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6ffb875..03e0918 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -3,9 +3,9 @@ name: Release on: workflow_dispatch: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: release: diff --git a/.makim.yaml b/.makim.yaml index bd6cefc..f05bea0 100644 --- a/.makim.yaml +++ b/.makim.yaml @@ -151,12 +151,12 @@ groups: dependencies: - task: docker.killall run: | - export KXGR_PROJECT_NAME="test-`python -c 'from uuid import uuid4; print(uuid4().hex[:7])'`" - echo $KXGR_PROJECT_NAME + export SUGAR_PROJECT_NAME="test-`python -c 'from uuid import uuid4; print(uuid4().hex[:7])'`" + echo $SUGAR_PROJECT_NAME sugar build --verbose --group group-defaults sugar ext start --verbose --group group-defaults --options -d sugar ext restart --verbose --group group-defaults --options -d - docker ps|grep $KXGR_PROJECT_NAME + docker ps|grep $SUGAR_PROJECT_NAME sugar ext stop --verbose --group group-defaults smoke-final: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bd9747e..70ba85e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,53 +7,58 @@ repos: hooks: - id: end-of-file-fixer + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v3.1.0" + hooks: + - id: prettier + - repo: local hooks: - - id: ruff-format - name: ruff-format - entry: ruff format - exclude: | - (?x)( - docs - ) - language: system - pass_filenames: true - types: - - python + - id: ruff-format + name: ruff-format + entry: ruff format + exclude: | + (?x)( + docs + ) + language: system + pass_filenames: true + types: + - python - - id: ruff-linter - name: ruff-linter - entry: ruff check --fix - language: system - exclude: "docs/" - pass_filenames: true - types: - - python + - id: ruff-linter + name: ruff-linter + entry: ruff check --fix + language: system + exclude: "docs/" + pass_filenames: true + types: + - python - - id: mypy - name: mypy - entry: mypy - language: system - files: "src/sugar/" - pass_filenames: true - types: - - python + - id: mypy + name: mypy + entry: mypy + language: system + files: "src/sugar/" + pass_filenames: true + types: + - python - - id: bandit - name: bandit - entry: bandit - language: system - args: ['--configfile', 'pyproject.toml'] - pass_filenames: true - types: - - python + - id: bandit + name: bandit + entry: bandit + language: system + args: ["--configfile", "pyproject.toml"] + pass_filenames: true + types: + - python - - id: vulture - name: vulture - entry: vulture - language: system - files: "src/sugar/" - description: Find unused Python code. - pass_filenames: true - types: - - python + - id: vulture + name: vulture + entry: vulture + language: system + files: "src/sugar/" + description: Find unused Python code. + pass_filenames: true + types: + - python diff --git a/.sugar.yaml b/.sugar.yaml index c88430b..6bae818 100644 --- a/.sugar.yaml +++ b/.sugar.yaml @@ -2,11 +2,11 @@ version: 1.0 compose-app: docker-compose env-file: .env defaults: - group: {{ env.KXGR_GROUP }} - project-name: sugar-{{ env.KXGR_PROJECT_NAME }} + group: ${{ env.SUGAR_GROUP }} + project-name: sugar-${{ env.SUGAR_PROJECT_NAME }} groups: group1: - project-name: project1 # optional + project-name: project1 # optional compose-path: tests/containers/group1/compose.yaml env-file: .env services: @@ -17,7 +17,7 @@ groups: - name: service1-3 group2: - project-name: null # optional + project-name: null # optional compose-path: tests/containers/group2/compose.yaml env-file: .env services: @@ -27,7 +27,7 @@ groups: - name: service2-2 group-mix: - project-name: null # optional + project-name: null # optional compose-path: - tests/containers/group1/compose.yaml - tests/containers/group2/compose.yaml diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1be68bb..504688b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -6,8 +6,8 @@ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. +nationality, personal appearance, race, religion, or sexual identity and +orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community +- Focusing on what is best not just for us as individuals, but for the overall + community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -60,8 +60,8 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -opensciencelabs@gmail.com. -All complaints will be reviewed and investigated promptly and fairly. +opensciencelabs@gmail.com. All complaints will be reviewed and investigated +promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. @@ -82,15 +82,15 @@ behavior was inappropriate. A public apology may be requested. ### 2. Warning -**Community Impact**: A violation through a single incident or series -of actions. +**Community Impact**: A violation through a single incident or series of +actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. +like social media. Violating these terms may lead to a temporary or permanent +ban. ### 3. Temporary Ban @@ -106,11 +106,11 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. -**Consequence**: A permanent ban from any sort of public interaction within -the community. +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution @@ -118,8 +118,8 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org diff --git a/README.md b/README.md index 2a4f48d..34b09c5 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,27 @@ Simplify the usage of containers. -You may be thinking, why do I need a new library that wrap-up -docker-compose or podman-compose if they are already really simple to use? +You may be thinking, why do I need a new library that wrap-up docker-compose or +podman-compose if they are already really simple to use? -Yes, they are simple to use, but if you have some other parameters to -the compose command line, it could be very tedious to write them every time -such as `--env-file`, `--project-name`, `--file`, etc. +Yes, they are simple to use, but if you have some other parameters to the +compose command line, it could be very tedious to write them every time such as +`--env-file`, `--project-name`, `--file`, etc. So, in this case we could use something like a script or `make`, right? -Yes, and just for one project it would be good enough. But, if you maintain -or collaborate a bunch of projects, it would be like a boiler plate. +Yes, and just for one project it would be good enough. But, if you maintain or +collaborate a bunch of projects, it would be like a boiler plate. -Additionally, if you are maintaining some extra scripts in order to improve -your containers stack, these scripts would be like a boilerplate as well. +Additionally, if you are maintaining some extra scripts in order to improve your +containers stack, these scripts would be like a boilerplate as well. -So, the idea of this project is to organize your stack of containers, -gathering some useful scripts and keeping this information centralized in a -configuration file. So the command line would be very simple. +So, the idea of this project is to organize your stack of containers, gathering +some useful scripts and keeping this information centralized in a configuration +file. So the command line would be very simple. - -* Software License: BSD 3 Clause -* Documentation: https://osl-incubator.github.io/sugar +- Software License: BSD 3 Clause +- Documentation: https://osl-incubator.github.io/sugar ## How to Install @@ -35,52 +34,51 @@ $ pip install containers-sugar The commands from docker-compose available are: -* build -* config -* create -* down -* events -* exec -* images -* kill -* logs -* pause -* port -* ps -* pull -* push -* restart -* rm -* run -* start -* stop -* top -* unpause -* up -* version - -These commands are available in the main profile/plugin, so -you don't need to specify any extra parameter to access them. - -For extra commands, we are gathering them into a profile/plugin called -`ext`, so you can access them using something like: `sugar ext restart`. +- build +- config +- create +- down +- events +- exec +- images +- kill +- logs +- pause +- port +- ps +- pull +- push +- restart +- rm +- run +- start +- stop +- top +- unpause +- up +- version + +These commands are available in the main profile/plugin, so you don't need to +specify any extra parameter to access them. + +For extra commands, we are gathering them into a profile/plugin called `ext`, so +you can access them using something like: `sugar ext restart`. The current available **ext** commands are: -* start -> alias for `up` -* restart -> runs `stop` and `up` - +- start -> alias for `up` +- restart -> runs `stop` and `up` ## How to use it -First you need to place the config file `.sugar.yaml` in the root -of your project. This is an example of a configuration file: +First you need to place the config file `.sugar.yaml` in the root of your +project. This is an example of a configuration file: ```yaml version: 1.0 compose-app: docker compose default: - group: {{ env.ENV }} + group: ${{ env.ENV }} groups: group1: project-name: project1 @@ -105,25 +103,23 @@ groups: Some examples of how to use it: -* build the defaults services (service1,service3) for group1: +- build the defaults services (service1,service3) for group1: `sugar build --group group1` -* build the all services (there is no default service defined) for group2: +- build the all services (there is no default service defined) for group2: `sugar build --group group2` -* build all services (ignore default) for group1: +- build all services (ignore default) for group1: `sugar build --group group1 --all` -* start the default services for group1: - `sugar ext start --group group1` +- start the default services for group1: `sugar ext start --group group1` -* restart all services (ignore defaults) for group1: +- restart all services (ignore defaults) for group1: `sugar ext restart --group group1 --all` -* restart service1 and service2 for group1: +- restart service1 and service2 for group1: `sugar ext restart --group group1 --services service1,service2` - -**NOTE**: If you use: ```default: group: {{ env.ENV }}```, you don't need to -give `--group `, except if you want a different group than the -default one. +**NOTE**: If you use: `default: group: ${{ env.ENV }}`, you don't need to give +`--group `, except if you want a different group than the default +one. diff --git a/conda/dev.yaml b/conda/dev.yaml index f09bb56..cf1b97a 100644 --- a/conda/dev.yaml +++ b/conda/dev.yaml @@ -7,5 +7,5 @@ dependencies: - python - poetry >=1.5 - pip: - # fix the distlib issue - - paginate >=0.5 + # fix the distlib issue + - paginate >=0.5 diff --git a/conda/release.yaml b/conda/release.yaml index 52e1f3a..eb1e12d 100644 --- a/conda/release.yaml +++ b/conda/release.yaml @@ -5,7 +5,7 @@ channels: dependencies: - python - poetry >=1.5 - - nodejs >=18.17 # used by semantic-release + - nodejs >=18.17 # used by semantic-release - pip: - # fix the distlib issue - - paginate >=0.5 + # fix the distlib issue + - paginate >=0.5 diff --git a/docs/contributing.md b/docs/contributing.md index 152d594..3ab51a1 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -17,10 +17,10 @@ at: When reporting a bug, kindly include the following information to aid in the issue's resolution: -- The name and version of your operating system. -- Any relevant details about your setup that might assist in diagnosing the - issue. -- A step-by-step guide to reproduce the bug. +- The name and version of your operating system. +- Any relevant details about your setup that might assist in diagnosing the + issue. +- A step-by-step guide to reproduce the bug. ### Fix Bugs @@ -44,12 +44,12 @@ sharing knowledge via blog posts, articles, and other media. Feedback is crucial for project improvement. To submit feedback or propose a feature: -- File an issue at - [https://github.com/osl-incubator/sugar/issues](https://github.com/osl-incubator/sugar/issues). -- For feature proposals, please provide a detailed explanation of how the - feature would function, aim for a narrow scope to facilitate easier - implementation, and remember, **Sugar** is a volunteer-driven project, and - we welcome contributions. +- File an issue at + [https://github.com/osl-incubator/sugar/issues](https://github.com/osl-incubator/sugar/issues). +- For feature proposals, please provide a detailed explanation of how the + feature would function, aim for a narrow scope to facilitate easier + implementation, and remember, **Sugar** is a volunteer-driven project, and we + welcome contributions. ## Requirements @@ -64,55 +64,55 @@ Conda is a versatile tool that provides package, dependency, and environment management for various programming languages. In the Sugar project, we leverage Conda to manage virtual environments and package dependencies effectively. -- **Environment Setup**: We strongly advise using a Conda environment while - working with Sugar. If Conda is not installed on your system, you can - download it from [Miniforge](https://github.com/conda-forge/miniforge). For - an introductory overview of Conda, consider watching this - [Conda Basics video](https://learning.anaconda.cloud/conda-basics). -- **Best Practices**: Avoid installing packages in the base Conda environment. - Always create and activate a new environment for each project to prevent - dependency conflicts and ensure a clean workspace. +- **Environment Setup**: We strongly advise using a Conda environment while + working with Sugar. If Conda is not installed on your system, you can download + it from [Miniforge](https://github.com/conda-forge/miniforge). For an + introductory overview of Conda, consider watching this + [Conda Basics video](https://learning.anaconda.cloud/conda-basics). +- **Best Practices**: Avoid installing packages in the base Conda environment. + Always create and activate a new environment for each project to prevent + dependency conflicts and ensure a clean workspace. ### Git Our collaborative efforts are facilitated through Git and GitHub. Understanding the fundamentals of Git is crucial for effective participation. -- **Learning Resources**: If you're new to Git, we recommend starting with the - [Software Carpentry Git Lesson](https://swcarpentry.github.io/git-novice/), - which covers essential Git concepts and workflows. -- **Quick Reference**: For a concise summary of common Git commands, refer to - this - [Git Cheat Sheet](https://education.github.com/git-cheat-sheet-education.pdf) - provided by GitHub. -- **Configuration Tips**: - - To streamline your workflow, configure Git to use `rebase` by default - for pulling changes with `git config --global pull.rebase true`. - - Familiarize yourself with the `git rebase` command for updating branches - from a remote repository. Although more complex, it is preferred over - the default merge commit strategy. For an in-depth explanation, visit - [Atlassian's guide on merging vs. rebasing](https://www.atlassian.com/git/tutorials/merging-vs-rebasing). -- **Workflow**: The standard open-source development workflow includes forking - a repository, cloning the fork locally, and configuring an `upstream` remote - for the original repository. Detailed instructions can be found in - [GitHub's guide to configuring a remote for a fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork). +- **Learning Resources**: If you're new to Git, we recommend starting with the + [Software Carpentry Git Lesson](https://swcarpentry.github.io/git-novice/), + which covers essential Git concepts and workflows. +- **Quick Reference**: For a concise summary of common Git commands, refer to + this + [Git Cheat Sheet](https://education.github.com/git-cheat-sheet-education.pdf) + provided by GitHub. +- **Configuration Tips**: + - To streamline your workflow, configure Git to use `rebase` by default for + pulling changes with `git config --global pull.rebase true`. + - Familiarize yourself with the `git rebase` command for updating branches + from a remote repository. Although more complex, it is preferred over the + default merge commit strategy. For an in-depth explanation, visit + [Atlassian's guide on merging vs. rebasing](https://www.atlassian.com/git/tutorials/merging-vs-rebasing). +- **Workflow**: The standard open-source development workflow includes forking a + repository, cloning the fork locally, and configuring an `upstream` remote for + the original repository. Detailed instructions can be found in + [GitHub's guide to configuring a remote for a fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork). ### Python Familiarity with Python and adherence to best practices is important for contributing to Sugar. -- **Style Guide**: Follow the PEP 8 style guide for Python code, available at - [PEP8](https://peps.python.org/pep-0008/). -- **Best Practices**: pyOpenSci offers a comprehensive guide for writing - Python packages, which can be found - [here](https://www.pyopensci.org/python-package-guide/index.html). -- **Advanced Learning**: To deepen your understanding of Python and general - programming concepts, consider enrolling in the - [Design of Computer Programs](https://www.udacity.com/course/design-of-computer-programs--cs212) - course on Udacity. Though challenging and based on Python 2, it provides - valuable insights into advanced Python usage and computer programming - principles. +- **Style Guide**: Follow the PEP 8 style guide for Python code, available at + [PEP8](https://peps.python.org/pep-0008/). +- **Best Practices**: pyOpenSci offers a comprehensive guide for writing Python + packages, which can be found + [here](https://www.pyopensci.org/python-package-guide/index.html). +- **Advanced Learning**: To deepen your understanding of Python and general + programming concepts, consider enrolling in the + [Design of Computer Programs](https://www.udacity.com/course/design-of-computer-programs--cs212) + course on Udacity. Though challenging and based on Python 2, it provides + valuable insights into advanced Python usage and computer programming + principles. ### Docker @@ -153,26 +153,26 @@ environment: 2. **Clone Your Fork Locally**: Clone the forked repository to your local machine and navigate into the project directory. - ```bash - $ git clone git@github.com:your_username/sugar.git - $ cd sugar - ``` + ```bash + $ git clone git@github.com:your_username/sugar.git + $ cd sugar + ``` 3. **Install Dependencies**: Use `mamba` to create a Conda environment and `poetry` for managing Python dependencies. - ```bash - $ mamba env create --file conda/dev.yaml --force - $ poetry config virtualenvs.create false - $ poetry install - ``` + ```bash + $ mamba env create --file conda/dev.yaml --force + $ poetry config virtualenvs.create false + $ poetry install + ``` 4. **Create a Development Branch**: Make a dedicated branch for your bugfix or feature. - ```bash - $ git checkout -b name-of-your-bugfix-or-feature - ``` + ```bash + $ git checkout -b name-of-your-bugfix-or-feature + ``` 5. **Make Changes Locally**: You are now ready to implement your changes or improvements. @@ -181,45 +181,45 @@ environment: ensure code quality. Install them locally and they will automatically run on each commit. - ```bash - $ pre-commit install - $ pre-commit run --all-files - ``` + ```bash + $ pre-commit install + $ pre-commit run --all-files + ``` - To bypass the hooks temporarily, use `git commit` with `--no-verify`. + To bypass the hooks temporarily, use `git commit` with `--no-verify`. 7. **Run Smoke Tests**: Quickly validate the functionality of your changes with smoke tests. - ```bash - $ makim tests.smoke - ``` + ```bash + $ makim tests.smoke + ``` - Always complement smoke tests with thorough unit testing to ensure code - integrity. + Always complement smoke tests with thorough unit testing to ensure code + integrity. 8. **Unit Testing with `pytest`**: `sugar` leverages `pytest` for unit testing, along with `pytest-cov` for coverage analysis. Run unit tests using: - ```bash - $ python -m pytest - ``` + ```bash + $ python -m pytest + ``` - or + or - ```bash - $ makim tests.unittest - ``` + ```bash + $ makim tests.unittest + ``` 9. **Commit and Push Changes**: Stage, commit, and push your changes to GitHub. After setting the upstream branch once, subsequent pushes only require `git push`. - ```bash - $ git add . - $ git commit -m "Detailed description of your changes." - $ git push --set-upstream origin - ``` + ```bash + $ git add . + $ git commit -m "Detailed description of your changes." + $ git push --set-upstream origin + ``` 10. **Submit a Pull Request**: Once your changes are pushed, go to the GitHub website to submit a pull request for review. diff --git a/docs/images/site.webmanifest b/docs/images/site.webmanifest index 1dd9112..fa99de7 100644 --- a/docs/images/site.webmanifest +++ b/docs/images/site.webmanifest @@ -1 +1,19 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/docs/index.md b/docs/index.md index 73e53f9..a3491da 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,28 +2,27 @@ Simplify the usage of containers. -You may be thinking, why do I need a new library that wrap-up -docker-compose or podman-compose if they are already really simple to use? +You may be thinking, why do I need a new library that wrap-up docker-compose or +podman-compose if they are already really simple to use? -Yes, they are simple to use, but if you have some other parameters to -the compose command line, it could be very tedious to write them every time -such as `--env-file`, `--project-name`, `--file`, etc. +Yes, they are simple to use, but if you have some other parameters to the +compose command line, it could be very tedious to write them every time such as +`--env-file`, `--project-name`, `--file`, etc. So, in this case we could use something like a script or `make`, right? -Yes, and just for one project it would be good enough. But, if you maintain -or collaborate a bunch of projects, it would be like a boiler plate. +Yes, and just for one project it would be good enough. But, if you maintain or +collaborate a bunch of projects, it would be like a boiler plate. -Additionally, if you are maintaining some extra scripts in order to improve -your containers stack, these scripts would be like a boilerplate as well. +Additionally, if you are maintaining some extra scripts in order to improve your +containers stack, these scripts would be like a boilerplate as well. -So, the idea of this project is to organize your stack of containers, -gathering some useful scripts and keeping this information centralized in a -configuration file. So the command line would be very simple. +So, the idea of this project is to organize your stack of containers, gathering +some useful scripts and keeping this information centralized in a configuration +file. So the command line would be very simple. - -* License: BSD 3 Clause -* Documentation: https://osl-incubator.github.io/sugar +- License: BSD 3 Clause +- Documentation: https://osl-incubator.github.io/sugar ## How to Install @@ -35,52 +34,51 @@ $ pip install containers-sugar The commands from docker-compose available are: -* build -* config -* create -* down -* events -* exec -* images -* kill -* logs -* pause -* port -* ps -* pull -* push -* restart -* rm -* run -* start -* stop -* top -* unpause -* up -* version - -These commands are available in the main profile/plugin, so -you don't need to specify any extra parameter to access them. - -For extra commands, we are gathering them into a profile/plugin called -`ext`, so you can access them using something like: `sugar ext restart`. +- build +- config +- create +- down +- events +- exec +- images +- kill +- logs +- pause +- port +- ps +- pull +- push +- restart +- rm +- run +- start +- stop +- top +- unpause +- up +- version + +These commands are available in the main profile/plugin, so you don't need to +specify any extra parameter to access them. + +For extra commands, we are gathering them into a profile/plugin called `ext`, so +you can access them using something like: `sugar ext restart`. The current available **ext** commands are: -* start -> alias for `up` -* restart -> runs `stop` and `up` - +- start -> alias for `up` +- restart -> runs `stop` and `up` ## How to use it -First you need to place the config file `.sugar.yaml` in the root -of your project. This is an example of a configuration file: +First you need to place the config file `.sugar.yaml` in the root of your +project. This is an example of a configuration file: ```yaml version: 1.0 compose-app: docker compose default: - group: {{ "{{ env.ENV }}" }} + group: ${{ "${{ env.ENV }}" }} groups: group1: project-name: project1 @@ -106,28 +104,25 @@ groups: - name: service2 ``` - Some examples of how to use it: -* build the defaults services (service1,service3) for group1: +- build the defaults services (service1,service3) for group1: `sugar build --group group1` -* build the all services (there is no default service defined) for group2: +- build the all services (there is no default service defined) for group2: `sugar build --group group2` -* build all services (ignore default) for group1: +- build all services (ignore default) for group1: `sugar build --group group1 --all` -* start the default services for group1: - `sugar ext start --group group1` +- start the default services for group1: `sugar ext start --group group1` -* restart all services (ignore defaults) for group1: +- restart all services (ignore defaults) for group1: `sugar ext restart --group group1 --all` -* restart service1 and service2 for group1: +- restart service1 and service2 for group1: `sugar ext restart --group group1 --services service1,service2` - -**NOTE**: If you use: ```default: group: {{ "{{ env.ENV }}" }}```, you don't need to -give `--group `, except if you want a different group than the -default one. +**NOTE**: If you use: `default: group: ${{ env.ENV }}`, you don't need to give +`--group `, except if you want a different group than the default +one. diff --git a/docs/installation.md b/docs/installation.md index 96dbfa9..a38f71b 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -14,13 +14,13 @@ Sugar is also available on conda-forge $ conda install -c conda-forge containers-sugar ``` -This is the preferred method to install sugar, -as it will always install the most recent stable release. +This is the preferred method to install sugar, as it will always install the +most recent stable release. ## From sources -The sources for sugar can be downloaded from -the [Github repo](https://github.com/osl-incubator/sugar.git). +The sources for sugar can be downloaded from the +[Github repo](https://github.com/osl-incubator/sugar.git). You can either clone the public repository: diff --git a/poetry.lock b/poetry.lock index 27f0370..a84efb2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1655,14 +1655,19 @@ files = [ [[package]] name = "paginate" -version = "0.5.6" +version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" files = [ - {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, ] +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + [[package]] name = "pandocfilters" version = "1.5.1" diff --git a/pyproject.toml b/pyproject.toml index 49dad2e..6f7f88e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,15 @@ testpaths = [ ] [tool.mypy] +python_version = "3.8" +check_untyped_defs = true +strict = true ignore_missing_imports = true +warn_unused_ignores = true +warn_redundant_casts = true +warn_unused_configs = true +show_error_codes = true +exclude = ["scripts/"] [tool.ruff] @@ -105,6 +113,7 @@ quote-style = "single" [tool.bandit] exclude_dirs = ["tests"] targets = "src/sugar/" +skips = ["B102", "B701"] [tool.vulture] exclude = ["tests", "src/sugar/cli.py"] diff --git a/src/sugar/__main__.py b/src/sugar/__main__.py index 85f699c..5642fd3 100644 --- a/src/sugar/__main__.py +++ b/src/sugar/__main__.py @@ -1,5 +1,7 @@ """The definition of the action for `python -m` command.""" +from __future__ import annotations + from sugar.cli import app if __name__ == '__main__': diff --git a/src/sugar/cli.py b/src/sugar/cli.py index 824bccb..ee6b22d 100644 --- a/src/sugar/cli.py +++ b/src/sugar/cli.py @@ -6,25 +6,26 @@ import sys from pathlib import Path -from typing import Any, Dict, Tuple +from typing import Any import typer from typer import Argument, Option -from sugar.core import Sugar, __version__ +from sugar import __version__ +from sugar.core import Sugar -flags_state: Dict[str, bool] = { +flags_state: dict[str, bool] = { 'verbose': False, } -opt_state: Dict[str, list[Any]] = { +opt_state: dict[str, list[Any]] = { 'options': [], 'cmd': [], } -def extract_options_and_cmd_args() -> Tuple[list, list]: +def extract_options_and_cmd_args() -> tuple[list[str], list[str]]: """Extract arg `options` and `cmd` from the CLI calling.""" args = list(sys.argv) total_args = len(args) @@ -81,7 +82,7 @@ def extract_options_and_cmd_args() -> Tuple[list, list]: return options_args, cmd_args -def create_main_group(sugar_app: typer.Typer): +def create_main_group(sugar_app: typer.Typer) -> None: """ Create the main plugin command group. @@ -120,7 +121,7 @@ def attach( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Attach to a service's running container. @@ -180,7 +181,7 @@ def build( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Build or rebuild services.""" args = ctx.params args['plugin'] = 'main' @@ -227,7 +228,7 @@ def config( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Parse, resolve and render compose file in canonical format.""" args = ctx.params args['plugin'] = 'main' @@ -269,7 +270,7 @@ def cp( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Copy files/folders between a services and local filesystem. @@ -322,7 +323,7 @@ def create( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Create containers for a service.""" args = ctx.params args['plugin'] = 'main' @@ -362,7 +363,7 @@ def down( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Stop and remove containers, networks.""" args = ctx.params args['plugin'] = 'main' @@ -409,7 +410,7 @@ def events( help='Specify a custom location for the config file.', is_flag=True, ), - ): + ) -> None: """Receive real time events from containers.""" args = ctx.params args['plugin'] = 'main' @@ -454,7 +455,7 @@ def exec_command( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Execute a command in a running container.""" args = ctx.params args['plugin'] = 'main' @@ -504,7 +505,7 @@ def images( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """List images used by the created containers.""" args = ctx.params args['plugin'] = 'main' @@ -554,7 +555,7 @@ def kill( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Force stop service containers.""" args = ctx.params args['plugin'] = 'main' @@ -604,7 +605,7 @@ def logs( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """View output from containers.""" args = ctx.params args['plugin'] = 'main' @@ -644,7 +645,7 @@ def ls( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ List running compose projects. @@ -697,7 +698,7 @@ def pause( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Pause services.""" args = ctx.params args['plugin'] = 'main' @@ -727,7 +728,7 @@ def port( help='Specify a custom location for the config file.', is_flag=True, ), - ): + ) -> None: """Print the public port for a port binding.""" args = ctx.params args['plugin'] = 'main' @@ -774,7 +775,7 @@ def ps( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """List containers.""" args = ctx.params args['plugin'] = 'main' @@ -824,7 +825,7 @@ def pull( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Pull service images.""" args = ctx.params args['plugin'] = 'main' @@ -874,7 +875,7 @@ def push( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Push service images.""" args = ctx.params args['plugin'] = 'main' @@ -924,7 +925,7 @@ def restart( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Restart service containers.""" args = ctx.params args['plugin'] = 'main' @@ -973,7 +974,7 @@ def rm( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Remove stopped service containers. @@ -1026,7 +1027,7 @@ def run( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Run a one-off command on a service.""" args = ctx.params args['plugin'] = 'main' @@ -1066,7 +1067,7 @@ def scale( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Scale services. @@ -1119,7 +1120,7 @@ def start( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Start services.""" args = ctx.params args['plugin'] = 'main' @@ -1169,7 +1170,7 @@ def stop( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Stop services.""" args = ctx.params args['plugin'] = 'main' @@ -1219,7 +1220,7 @@ def top( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Display the running processes.""" args = ctx.params args['plugin'] = 'main' @@ -1269,7 +1270,7 @@ def unpause( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Unpause services.""" args = ctx.params args['plugin'] = 'main' @@ -1319,7 +1320,7 @@ def up( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Create and start containers.""" args = ctx.params args['plugin'] = 'main' @@ -1353,7 +1354,7 @@ def version( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Show the Docker Compose version information.""" args = ctx.params args['plugin'] = 'main' @@ -1403,7 +1404,7 @@ def wait( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Block until the first service container stops. @@ -1456,7 +1457,7 @@ def watch( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """ Watch build context. @@ -1477,7 +1478,7 @@ def watch( Sugar(args, options_args=opts_args).run() -def create_ext_group(sugar_app: typer.Typer): +def create_ext_group(sugar_app: typer.Typer) -> None: """ Create a command group for ext plugin. @@ -1507,7 +1508,7 @@ def get_ip( help='Specify a custom location for the config file.', is_flag=True, ), - ): + ) -> None: """Get the IP for given service (NOT IMPLEMENTED YET).""" args = ctx.params args['plugin'] = 'ext' @@ -1554,7 +1555,7 @@ def ext_start( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Run `up` main command (alias).""" args = ctx.params args['plugin'] = 'ext' @@ -1604,7 +1605,7 @@ def ext_stop( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Run the main stop command (alias).""" args = ctx.params args['plugin'] = 'ext' @@ -1654,7 +1655,7 @@ def ext_restart( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Run `down` and `up` sequentially.""" args = ctx.params args['plugin'] = 'ext' @@ -1692,7 +1693,7 @@ def wait( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Wait until the service are healthy (NOT IMPLEMENTED YET).""" args = ctx.params args['plugin'] = 'ext' @@ -1709,7 +1710,7 @@ def wait( sugar_app.add_typer(ext_group, name='ext', rich_help_panel='Plugins') -def create_stats_group(sugar_app: typer.Typer): +def create_stats_group(sugar_app: typer.Typer) -> None: """Instantiate the stats command group.""" stats_group = typer.Typer( help='Use the `stats` plugin.', @@ -1738,7 +1739,7 @@ def plot( is_eager=True, help='Show the command executed.', ), - ): + ) -> None: """Plot stats in real-time for given services (EXPERIMENTAL).""" args = ctx.params args['plugin'] = 'stats' @@ -1755,7 +1756,7 @@ def plot( sugar_app.add_typer(stats_group, name='stats', rich_help_panel='Plugins') -def create_app(): +def create_app() -> typer.Typer: """Create app function to instantiate the typer app dinamically.""" options_args, cmd_args = extract_options_and_cmd_args() opt_state['options'] = options_args @@ -1782,7 +1783,7 @@ def create_app(): create_stats_group(sugar_app) # -- Callbacks -- - def version_callback(version: bool): + def version_callback(version: bool) -> None: """ Version callback function. diff --git a/src/sugar/console.py b/src/sugar/console.py index 485ef18..ac9330b 100644 --- a/src/sugar/console.py +++ b/src/sugar/console.py @@ -1,9 +1,11 @@ """Functions about console.""" +from __future__ import annotations + import os -def get_terminal_size(): +def get_terminal_size() -> tuple[int, int]: """Return the height (number of lines) of the terminal using os module.""" size = os.get_terminal_size() try: diff --git a/src/sugar/core.py b/src/sugar/core.py index 88483de..8aa0834 100644 --- a/src/sugar/core.py +++ b/src/sugar/core.py @@ -4,10 +4,9 @@ import os -from typing import Dict, Optional, Type, cast +from typing import Optional, Type, cast -from sugar import __version__ -from sugar.logs import KxgrErrorType, KxgrLogs +from sugar.logs import SugarErrorType, SugarLogs from sugar.plugins.base import SugarBase, SugarDockerCompose from sugar.plugins.ext import SugarExt @@ -21,7 +20,7 @@ class Sugar(SugarBase): """Sugar main class.""" - plugins_definition: Dict[str, Type[SugarBase]] = { + plugins_definition: dict[str, Type[SugarBase]] = { 'main': SugarDockerCompose, 'ext': SugarExt, **{'stats': SugarStats for i in range(1) if SugarStats is not None}, @@ -30,9 +29,9 @@ class Sugar(SugarBase): def __init__( self, - args: dict, - options_args: list = [], - cmd_args: list = [], + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], ): """Initialize the Sugar object according to the plugin used.""" plugin_name = args.get('plugin', '') @@ -61,18 +60,18 @@ def __init__( cmd_args, ) - def _verify_args(self): + def _verify_args(self) -> None: if self.args.get('plugin') not in self.plugins_definition: plugins_name = [k for k in self.plugins_definition.keys()] - KxgrLogs.raise_error( + SugarLogs.raise_error( f'`plugin` parameter `{ self.args.get("plugin") }` ' f'not found. Options: { plugins_name }.', - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) os._exit(1) - def get_actions(self) -> list: + def get_actions(self) -> list[str]: """Get a list of the available actions.""" actions = [] @@ -81,22 +80,16 @@ def get_actions(self) -> list: return actions - def _load_compose_args(self): + def _load_compose_args(self) -> None: pass - def _load_service_names(self): + def _load_service_names(self) -> None: pass - def run(self): + def run(self) -> None: """Run sugar command.""" if not self.args.get('action'): return - return self.plugin.run() - - # actions available - - def _version(self): - KxgrLogs.print_info('sugar version:' + str(__version__)) - KxgrLogs.print_info('container program path: ' + str(self.compose_app)) - self._call_compose_app('--version') + if self.plugin: + self.plugin.run() diff --git a/src/sugar/logs.py b/src/sugar/logs.py index ff0c580..9ea94d0 100644 --- a/src/sugar/logs.py +++ b/src/sugar/logs.py @@ -1,5 +1,7 @@ """Logs classes and function for sugar system.""" +from __future__ import annotations + import os from enum import Enum @@ -7,35 +9,35 @@ from colorama import Fore -class KxgrErrorType(Enum): - """KxgrErrorType group all error types handled by the system.""" +class SugarErrorType(Enum): + """SugarErrorType group all error types handled by the system.""" SH_ERROR_RETURN_CODE = 1 SH_KEYBOARD_INTERRUPT = 2 - KXGR_COMPOSE_APP_NOT_SUPPORTED = 3 - KXGR_COMPOSE_APP_NOT_FOUNDED = 4 - KXGR_INVALID_PARAMETER = 5 - KXGR_MISSING_PARAMETER = 6 - KXGR_INVALID_CONFIGURATION = 7 - KXGR_ACTION_NOT_IMPLEMENTED = 8 - KXGR_NO_SERVICES_RUNNING = 9 + SUGAR_COMPOSE_APP_NOT_SUPPORTED = 3 + SUGAR_COMPOSE_APP_NOT_FOUNDED = 4 + SUGAR_INVALID_PARAMETER = 5 + SUGAR_MISSING_PARAMETER = 6 + SUGAR_INVALID_CONFIGURATION = 7 + SUGAR_ACTION_NOT_IMPLEMENTED = 8 + SUGAR_NO_SERVICES_RUNNING = 9 -class KxgrLogs: - """KxgrLogs is responsible for handling system messages.""" +class SugarLogs: + """SugarLogs is responsible for handling system messages.""" @staticmethod - def raise_error(message: str, message_type: KxgrErrorType): + def raise_error(message: str, message_type: SugarErrorType) -> None: """Print error message and exit with given error code.""" print(Fore.RED, f'[EE] {message}', Fore.RESET) os._exit(message_type.value) @staticmethod - def print_info(message: str): + def print_info(message: str) -> None: """Print info message.""" print(Fore.BLUE, message, Fore.RESET) @staticmethod - def print_warning(message: str): + def print_warning(message: str) -> None: """Print warning message.""" print(Fore.YELLOW, message, Fore.RESET) diff --git a/src/sugar/plugins/base.py b/src/sugar/plugins/base.py index 97af231..7e7fcb9 100644 --- a/src/sugar/plugins/base.py +++ b/src/sugar/plugins/base.py @@ -14,56 +14,53 @@ import sh import yaml # type: ignore -from jinja2 import Template +from jinja2 import Environment -from sugar.logs import KxgrErrorType, KxgrLogs +from sugar import __version__ +from sugar.logs import SugarErrorType, SugarLogs - -def escape_template_tag(v: str) -> str: - """Escape template tags for template rendering.""" - return v.replace('{{', r'\{\{').replace('}}', r'\}\}') - - -def unescape_template_tag(v: str) -> str: - """Unescape template tags for template rendering.""" - return v.replace(r'\{\{', '{{').replace(r'\}\}', '}}') +TEMPLATE = Environment( + autoescape=False, + variable_start_string='${{', + variable_end_string='}}', +) class SugarBase: """SugarBase defined the base structure for the Sugar classes.""" actions: list[str] = [] - args: dict = {} + args: dict[str, str] = {} config_file: str = '' - config: dict = {} + config: dict[str, Any] = {} # note: it starts with a simple command # it is replaced later in the execution compose_app: sh.Command = sh.echo - compose_args: list = [] - defaults: dict = {} - env: dict = {} - options_args: list = [] - cmd_args: list = [] - service_group: dict = {} - service_names: list = [] + compose_args: list[str] = [] + defaults: dict[str, Any] = {} + env: dict[str, str] = {} + options_args: list[str] = [] + cmd_args: list[str] = [] + service_group: dict[str, Any] = {} + service_names: list[str] = [] def __init__( self, - args: dict, - options_args: list = [], - cmd_args: list = [], - ): + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], + ) -> None: """Initialize SugarBase instance.""" self.args = deepcopy(args) self.options_args = deepcopy(options_args) self.cmd_args = deepcopy(cmd_args) self.config_file = self.args.get('config_file', '') - self.config = dict() - self.compose_args = list() - self.defaults = dict() - self.env = dict() - self.service_group = dict() - self.service_names = list() + self.config: dict[str, Any] = {} + self.compose_args: list[str] = [] + self.defaults: dict[str, Any] = {} + self.env: dict[str, str] = {} + self.service_group: dict[str, Any] = {} + self.service_names: list[str] = [] self._load_config() self._load_env() @@ -77,8 +74,8 @@ def __init__( def _call_compose_app_core( self, - *args, - services: list = [], + *args: str, + services: list[str] = [], options_args: list[str] = [], cmd_args: list[str] = [], _out: Union[io.TextIOWrapper, io.StringIO, Any] = sys.stdout, @@ -114,18 +111,18 @@ def _call_compose_app_core( try: p.wait() except sh.ErrorReturnCode as e: - KxgrLogs.raise_error(str(e), KxgrErrorType.SH_ERROR_RETURN_CODE) + SugarLogs.raise_error(str(e), SugarErrorType.SH_ERROR_RETURN_CODE) except KeyboardInterrupt: pid = p.pid p.kill() - KxgrLogs.raise_error( - f'Process {pid} killed.', KxgrErrorType.SH_KEYBOARD_INTERRUPT + SugarLogs.raise_error( + f'Process {pid} killed.', SugarErrorType.SH_KEYBOARD_INTERRUPT ) def _call_compose_app( self, - *args, - services: list = [], + *args: str, + services: list[str] = [], ) -> None: self._call_compose_app_core( *args, @@ -134,11 +131,11 @@ def _call_compose_app( _err=sys.stderr, ) - def _check_config_file(self): + def _check_config_file(self) -> bool: return Path(self.config_file).exists() # Check if services item is given - def _check_services_item(self): + def _check_services_item(self) -> bool: return hasattr(self.config, 'services') # set default group main @@ -165,16 +162,16 @@ def _load_root_services(self) -> None: self.service_group = deepcopy(self.config['groups']['main']) del self.config['services'] - def _filter_service_group(self): + def _filter_service_group(self) -> None: groups = self.config['groups'] if not self.args.get('service_group'): default_group = self.defaults.get('group') if not default_group: - KxgrLogs.raise_error( + SugarLogs.raise_error( 'The service group parameter or default ' "group configuration weren't defined.", - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) selected_group_name = default_group else: @@ -203,38 +200,38 @@ def _filter_service_group(self): self.service_group = group_data return - KxgrLogs.raise_error( + SugarLogs.raise_error( f'The given group service "{group_name}" was not found ' 'in the configuration file.', - KxgrErrorType.KXGR_MISSING_PARAMETER, + SugarErrorType.SUGAR_MISSING_PARAMETER, ) - def _load_config(self): + def _load_config(self) -> None: with open(self.config_file, 'r') as f: # escape template tags - content = escape_template_tag(f.read()) + content = f.read() f_content = io.StringIO(content) self.config = yaml.safe_load(f_content) # check if either services or groups are present if not (self.config.get('services') or self.config.get('groups')): - KxgrLogs.raise_error( + SugarLogs.raise_error( 'Either `services` OR `groups` flag must be given', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) # check if both services and groups are present if self.config.get('services') and self.config.get('groups'): - KxgrLogs.raise_error( + SugarLogs.raise_error( '`services` and `groups` flags given, only 1 is allowed.', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) - def _load_compose_app(self): + def _load_compose_app(self) -> None: compose_cmd = self.config.get('compose-app', '') if compose_cmd.replace(' ', '-') != 'docker-compose': - KxgrLogs.raise_error( + SugarLogs.raise_error( f'"{self.config["compose-app"]}" not supported yet.', - KxgrErrorType.KXGR_COMPOSE_APP_NOT_SUPPORTED, + SugarErrorType.SUGAR_COMPOSE_APP_NOT_SUPPORTED, ) if compose_cmd == 'docker-compose': @@ -243,7 +240,7 @@ def _load_compose_app(self): self.compose_app = sh.docker self.compose_args.append('compose') - def _load_compose_args(self): + def _load_compose_args(self) -> None: self._filter_service_group() if self.service_group.get('env-file'): @@ -258,11 +255,11 @@ def _load_compose_args(self): elif isinstance(compose_path_arg, list): compose_path.extend(compose_path_arg) else: - self.KxgrLogs.raise_error( + SugarLogs.raise_error( 'The attribute compose-path` just supports the data ' f'types `string` or `list`, {type(compose_path_arg)} ' 'received.', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) for p in compose_path: @@ -273,21 +270,19 @@ def _load_compose_args(self): ['--project-name', self.service_group['project-name']] ) - def _load_defaults(self): + def _load_defaults(self) -> None: _defaults = self.config.get('defaults', {}) for k, v in _defaults.items(): - unescaped_value = ( - unescape_template_tag(v) if isinstance(v, str) else str(v) - ) + unescaped_value = v if isinstance(v, str) else str(v) _defaults[k] = yaml.safe_load( - Template(unescaped_value).render(env=self.env) + TEMPLATE.from_string(unescaped_value).render(env=self.env) ) self.defaults = _defaults - def _load_env(self): + def _load_env(self) -> None: self.env = dict(os.environ) env_file = self.config.get('env-file', '') @@ -301,13 +296,13 @@ def _load_env(self): env_file = str(Path(self.config_file).parent / env_file) if not Path(env_file).exists(): - KxgrLogs.raise_error( + SugarLogs.raise_error( 'The given env-file was not found.', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) - self.env.update(dotenv.dotenv_values(env_file)) + self.env.update(dotenv.dotenv_values(env_file)) # type: ignore - def _load_service_names(self): + def _load_service_names(self) -> None: services = self.service_group['services'] if self.args.get('all'): @@ -318,49 +313,54 @@ def _load_service_names(self): ) ] elif self.args.get('services') == '': - KxgrLogs.raise_error( + SugarLogs.raise_error( 'If you want to execute the operation for all services, ' 'use --all parameter.', - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) elif self.args.get('services'): - self.service_names = self.args.get('services').split(',') + self.service_names = self.args.get('services', '').split(',') elif services.get('default'): - self.service_names = services['default'].split(',') + self.service_names = services.get('default', '').split(',') - def _verify_args(self): + def _verify_args(self) -> None: if not self._check_config_file(): - KxgrLogs.raise_error( + SugarLogs.raise_error( 'Config file .sugar.yaml not found.', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) if ( self.args.get('action') and self.args.get('action') not in self.actions ): - KxgrLogs.raise_error( + SugarLogs.raise_error( f'The given action `{self.args.get("action")}` is not ' f'valid. Use one of them: {",".join(self.actions)}.', - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) - def _verify_config(self): + def _verify_config(self) -> None: if not len(self.config['groups']): - KxgrLogs.raise_error( + SugarLogs.raise_error( 'No service groups found.', - KxgrErrorType.KXGR_INVALID_CONFIGURATION, + SugarErrorType.SUGAR_INVALID_CONFIGURATION, ) - def run(self): + def run(self) -> None: """Run the given sugar command.""" - action = self.args.get('action') + action = self.args.get('action', '') if not isinstance(action, str): - KxgrLogs.raise_error( + SugarLogs.raise_error( 'The given action is not valid.', - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) - return getattr(self, f'_{action.replace("-", "_")}')() + getattr(self, f'_{action.replace("-", "_")}')() + + def _version(self) -> None: + SugarLogs.print_info(f'Sugar Version: {__version__}') + SugarLogs.print_info(f'Container Program Path: {self.compose_app}') + self._call_compose_app('version', services=[]) class SugarDockerCompose(SugarBase): @@ -439,32 +439,39 @@ class SugarDockerCompose(SugarBase): 'watch', ] - def __init__(self, *args, **kwargs): + def __init__( + self, + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], + ): """Initialize SugarDockerCompose instance.""" - super().__init__(*args, **kwargs) + super().__init__(args, options_args=options_args, cmd_args=cmd_args) # container commands - def _attach(self): - self._call_compose_app('attach', services=[self.args.get('service')]) + def _attach(self) -> None: + service_name = self.args.get('service', '') + service_name_list: list[str] = [service_name] if service_name else [] + self._call_compose_app('attach', services=service_name_list) - def _build(self): + def _build(self) -> None: self._call_compose_app('build', services=self.service_names) - def _config(self): + def _config(self) -> None: self._call_compose_app('config', services=self.service_names) - def _cp(self): + def _cp(self) -> None: self._call_compose_app('cp', services=[]) - def _create(self): + def _create(self) -> None: self._call_compose_app('create', services=self.service_names) - def _down(self): + def _down(self) -> None: if self.args.get('all') or self.args.get('services'): - KxgrLogs.raise_error( + SugarLogs.raise_error( "The `down` sub-command doesn't accept `--all` " 'neither `--services` parameters.', - KxgrErrorType.KXGR_INVALID_PARAMETER, + SugarErrorType.SUGAR_INVALID_PARAMETER, ) self._call_compose_app( @@ -473,96 +480,100 @@ def _down(self): services=[], ) - def _events(self): + def _events(self) -> None: # port is not complete supported - if not self.args.get('service'): - KxgrLogs.raise_error( + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( '`exec` sub-command expected --service parameter.', - KxgrErrorType.KXGR_MISSING_PARAMETER, + SugarErrorType.SUGAR_MISSING_PARAMETER, ) - self._call_compose_app('events', services=[self.args.get('service')]) + service_name_list = [service_name] if service_name else [] + self._call_compose_app('events', services=service_name_list) - def _exec(self): - if not self.args.get('service'): - KxgrLogs.raise_error( + def _exec(self) -> None: + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( '`exec` sub-command expected --service parameter.', - KxgrErrorType.KXGR_MISSING_PARAMETER, + SugarErrorType.SUGAR_MISSING_PARAMETER, ) - self._call_compose_app('exec', services=[self.args.get('service')]) + service_name_list: list[str] = [service_name] if service_name else [] + self._call_compose_app('exec', services=service_name_list) - def _images(self): + def _images(self) -> None: self._call_compose_app('images', services=self.service_names) - def _kill(self): + def _kill(self) -> None: self._call_compose_app('kill', services=self.service_names) - def _logs(self): + def _logs(self) -> None: self._call_compose_app('logs', services=self.service_names) - def _ls(self): + def _ls(self) -> None: self._call_compose_app('ls', services=[]) - def _pause(self): + def _pause(self) -> None: self._call_compose_app('pause', services=self.service_names) - def _port(self): + def _port(self) -> None: # port is not complete supported - if not self.args.get('service'): - KxgrLogs.raise_error( + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( '`exec` sub-command expected --service parameter.', - KxgrErrorType.KXGR_MISSING_PARAMETER, + SugarErrorType.SUGAR_MISSING_PARAMETER, ) # TODO: check how private port could be passed - self._call_compose_app('port', services=[self.args.get('service')]) + service_name_list: list[str] = [service_name] if service_name else [] + self._call_compose_app('port', services=service_name_list) - def _ps(self): + def _ps(self) -> None: self._call_compose_app('ps', services=self.service_names) - def _pull(self): + def _pull(self) -> None: self._call_compose_app('pull', services=self.service_names) - def _push(self): + def _push(self) -> None: self._call_compose_app('push', services=self.service_names) - def _restart(self): + def _restart(self) -> None: self._call_compose_app('restart', services=self.service_names) - def _rm(self): + def _rm(self) -> None: self._call_compose_app('rm', services=self.service_names) - def _run(self): - if not self.args.get('service'): - KxgrLogs.raise_error( + def _run(self) -> None: + service_name = self.args.get('service', '') + if not service_name: + SugarLogs.raise_error( '`run` sub-command expected --service parameter.', - KxgrErrorType.KXGR_MISSING_PARAMETER, + SugarErrorType.SUGAR_MISSING_PARAMETER, ) + service_name_list: list[str] = [service_name] if service_name else [] + self._call_compose_app('run', services=service_name_list) - self._call_compose_app('run', services=[self.args.get('service')]) - - def _scale(self): + def _scale(self) -> None: self._call_compose_app('ls', services=[]) - def _start(self): + def _start(self) -> None: self._call_compose_app('start', services=self.service_names) - def _stop(self): + def _stop(self) -> None: self._call_compose_app('stop', services=self.service_names) - def _top(self): + def _top(self) -> None: self._call_compose_app('top', services=self.service_names) - def _unpause(self): + def _unpause(self) -> None: self._call_compose_app('unpause', services=self.service_names) - def _up(self): + def _up(self) -> None: self._call_compose_app('up', services=self.service_names) - def _version(self): - self._call_compose_app('version', services=[]) - - def _wait(self): + def _wait(self) -> None: self._call_compose_app('wait', services=self.service_names) - def _watch(self): + def _watch(self) -> None: self._call_compose_app('watch', services=self.service_names) diff --git a/src/sugar/plugins/ext.py b/src/sugar/plugins/ext.py index b74d49d..88355fd 100644 --- a/src/sugar/plugins/ext.py +++ b/src/sugar/plugins/ext.py @@ -2,14 +2,19 @@ from __future__ import annotations -from sugar.logs import KxgrErrorType, KxgrLogs +from sugar.logs import SugarErrorType, SugarLogs from sugar.plugins.base import SugarDockerCompose class SugarExt(SugarDockerCompose): """SugarExt provides special commands not available on docker-compose.""" - def __init__(self, *args, **kwargs): + def __init__( + self, + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], + ) -> None: """Initialize the SugarExt class.""" self.actions += [ 'get-ip', @@ -19,26 +24,26 @@ def __init__(self, *args, **kwargs): 'wait', ] - super().__init__(*args, **kwargs) + super().__init__(args, options_args=options_args, cmd_args=cmd_args) - def _get_ip(self): - KxgrLogs.raise_error( + def _get_ip(self) -> None: + SugarLogs.raise_error( '`get-ip` mot implemented yet.', - KxgrErrorType.KXGR_ACTION_NOT_IMPLEMENTED, + SugarErrorType.SUGAR_ACTION_NOT_IMPLEMENTED, ) - def _restart(self): + def _restart(self) -> None: options = self.options_args self.options_args = [] self._stop() self.options_args = options self._start() - def _start(self): + def _start(self) -> None: self._up() - def _wait(self): - KxgrLogs.raise_error( + def _wait(self) -> None: + SugarLogs.raise_error( '`wait` not implemented yet.', - KxgrErrorType.KXGR_ACTION_NOT_IMPLEMENTED, + SugarErrorType.SUGAR_ACTION_NOT_IMPLEMENTED, ) diff --git a/src/sugar/plugins/stats.py b/src/sugar/plugins/stats.py index 2405fe8..d404c9a 100644 --- a/src/sugar/plugins/stats.py +++ b/src/sugar/plugins/stats.py @@ -5,8 +5,9 @@ import datetime import io +from dataclasses import dataclass, field from itertools import tee -from typing import Iterable +from typing import Any, Iterable import plotille @@ -18,13 +19,22 @@ from sugar.console import get_terminal_size from sugar.inspect import get_container_name, get_container_stats -from sugar.logs import KxgrErrorType, KxgrLogs +from sugar.logs import SugarErrorType, SugarLogs from sugar.plugins.base import SugarDockerCompose CHART_WINDOW_DURATION = 60 CHART_TIME_INTERVAL = 1 +@dataclass +class StatsData: + """Record stats from containers.""" + + times: list[datetime.datetime] = field(default_factory=list) + mem_usages: list[float] = field(default_factory=list) + cpu_usages: list[float] = field(default_factory=list) + + class StatsPlot: """Plot containers statistic data.""" @@ -53,20 +63,20 @@ def __init__( self.create_chart() self.reset_data() - def create_chart(self): + def create_chart(self) -> None: """Create a new chart.""" self.fig_mem = plotille.Figure() self.fig_cpu = plotille.Figure() self.resize_chart() - self.chart_colors: tuple[Iterable, Iterable] = { + self.chart_colors: dict[str, tuple[Iterable[Any], ...]] = { 'mem': tee(self.fig_mem._color_seq), 'cpu': tee(self.fig_cpu._color_seq), } - self.stats: dict[str, dict[str, list[str]]] = { - name: {'times': [], 'mem_usages': [], 'cpu_usages': []} + self.stats: dict[str, StatsData] = { + name: StatsData(times=[], mem_usages=[], cpu_usages=[]) for name in self.container_names } @@ -74,17 +84,17 @@ def create_chart(self): container_stats = self.stats[name] # Add data to plots self.fig_mem.plot( - container_stats['times'], - container_stats['mem_usages'], + container_stats.times, + container_stats.mem_usages, label=name, ) self.fig_cpu.plot( - container_stats['times'], - container_stats['cpu_usages'], + container_stats.times, + container_stats.cpu_usages, label=name, ) - def resize_chart(self): + def resize_chart(self) -> None: """Resize chart.""" console_width, console_height = get_terminal_size() @@ -96,7 +106,7 @@ def resize_chart(self): self.fig_cpu.width = chart_width self.fig_cpu.height = chart_height - def reset_chart(self): + def reset_chart(self) -> None: """Reset chart state.""" self.fig_mem._plots.clear() self.fig_cpu._plots.clear() @@ -106,20 +116,20 @@ def reset_chart(self): self.resize_chart() - def reset_data(self): + def reset_data(self) -> None: """Generate a clean data.""" current_time = datetime.datetime.now() for name in self.container_names: container_stats = self.stats[name] - container_stats['mem_usages'].clear() - container_stats['cpu_usages'].clear() - container_stats['times'].clear() + container_stats.mem_usages.clear() + container_stats.cpu_usages.clear() + container_stats.times.clear() - container_stats['mem_usages'].extend([0.0] * CHART_WINDOW_DURATION) - container_stats['cpu_usages'].extend([0.0] * CHART_WINDOW_DURATION) - container_stats['times'].extend( + container_stats.mem_usages.extend([0.0] * CHART_WINDOW_DURATION) + container_stats.cpu_usages.extend([0.0] * CHART_WINDOW_DURATION) + container_stats.times.extend( [ current_time - datetime.timedelta(seconds=i * CHART_TIME_INTERVAL) @@ -129,17 +139,17 @@ def reset_data(self): # Add data to plots self.fig_mem.plot( - container_stats['times'], - container_stats['mem_usages'], + container_stats.times, + container_stats.mem_usages, label=name, ) self.fig_cpu.plot( - container_stats['times'], - container_stats['cpu_usages'], + container_stats.times, + container_stats.cpu_usages, label=name, ) - def plot_stats(self): + def plot_stats(self) -> None: """ Plot containers statistic. @@ -153,17 +163,17 @@ def plot_stats(self): # Update and maintain window for stats container_stats = self.stats[name] - container_stats['times'].append(current_time) - container_stats['mem_usages'].append(round(mem_usage, 2)) - container_stats['cpu_usages'].append(round(cpu_usage, 2)) + container_stats.times.append(current_time) + container_stats.mem_usages.append(round(mem_usage, 2)) + container_stats.cpu_usages.append(round(cpu_usage, 2)) - container_stats['times'] = container_stats['times'][ + container_stats.times = container_stats.times[ -self.window_duration : ] - container_stats['mem_usages'] = container_stats['mem_usages'][ + container_stats.mem_usages = container_stats.mem_usages[ -self.window_duration : ] - container_stats['cpu_usages'] = container_stats['cpu_usages'][ + container_stats.cpu_usages = container_stats.cpu_usages[ -self.window_duration : ] @@ -173,18 +183,18 @@ def plot_stats(self): container_stats = self.stats[name] # Add data to plots self.fig_mem.plot( - container_stats['times'], - container_stats['mem_usages'], + container_stats.times, + container_stats.mem_usages, label=name, ) self.fig_cpu.plot( - container_stats['times'], - container_stats['cpu_usages'], + container_stats.times, + container_stats.cpu_usages, label=name, ) -class StatsPlotWidget(Widget): +class StatsPlotWidget(Widget): # type: ignore """Plot Docker Stats Widget.""" content: Reactive[str] = Reactive('') @@ -196,7 +206,9 @@ class StatsPlotWidget(Widget): } """ - def __init__(self, container_names: list[str], *args, **kwargs) -> None: + def __init__( + self, container_names: list[str], *args: str, **kwargs: Any + ) -> None: """Initialize StatsPlotWidget.""" self.container_names = container_names super().__init__(*args, **kwargs) @@ -229,13 +241,15 @@ def render(self) -> Text: return Text.from_ansi(self.content) -class StatsPlotApp(App[str]): +class StatsPlotApp(App[str]): # type: ignore """StatsPlotApp app class.""" TITLE = 'Sugar Containers Stats' container_names: list[str] - def __init__(self, container_names: list[str], *args, **kwargs) -> None: + def __init__( + self, container_names: list[str], *args: str, **kwargs: Any + ) -> None: """Initialize StatsPlotApp.""" self.container_names = container_names super().__init__(*args, **kwargs) @@ -249,15 +263,20 @@ def compose(self) -> ComposeResult: class SugarStats(SugarDockerCompose): """SugarStats provides special commands not available on docker-compose.""" - def __init__(self, *args, **kwargs): + def __init__( + self, + args: dict[str, str], + options_args: list[str] = [], + cmd_args: list[str] = [], + ) -> None: """Initialize the SugarExt class.""" self.actions += [ 'plot', ] - super().__init__(*args, **kwargs) + super().__init__(args, options_args=options_args, cmd_args=cmd_args) - def _plot(self): + def _plot(self) -> None: """Call the plot command.""" _out = io.StringIO() _err = io.StringIO() @@ -274,9 +293,9 @@ def _plot(self): if not raw_out: service_names = ', '.join(self.service_names) - KxgrLogs.raise_error( + SugarLogs.raise_error( f'No container found for the services: {service_names}', - KxgrErrorType.KXGR_NO_SERVICES_RUNNING, + SugarErrorType.SUGAR_NO_SERVICES_RUNNING, ) containers_ids = [cids for cids in raw_out.split('\n') if cids] diff --git a/tests/containers/.services.sugar.yaml b/tests/containers/.services.sugar.yaml index b0aaa4b..e6aafff 100644 --- a/tests/containers/.services.sugar.yaml +++ b/tests/containers/.services.sugar.yaml @@ -1,9 +1,9 @@ version: 1.0 compose-app: docker-compose defaults: - project-name: sugar-{{ env.KXGR_PROJECT_NAME }} + project-name: sugar-{{ env.SUGAR_PROJECT_NAME }} services: - project-name: project1 # optional + project-name: project1 # optional compose-path: tests/containers/group1/compose.yaml default: service1-1,service1-3 available: diff --git a/tests/containers/group1/compose.yaml b/tests/containers/group1/compose.yaml index d8b268f..fca3cfc 100644 --- a/tests/containers/group1/compose.yaml +++ b/tests/containers/group1/compose.yaml @@ -1,4 +1,4 @@ -version: '3.4' +version: "3.4" services: service1-1: diff --git a/tests/containers/group2/compose.yaml b/tests/containers/group2/compose.yaml index dd2c44f..90c9d08 100644 --- a/tests/containers/group2/compose.yaml +++ b/tests/containers/group2/compose.yaml @@ -1,4 +1,4 @@ -version: '3.4' +version: "3.4" services: service2-1: