Skip to content

The ops project Tool¤

After installation of devapps you have the ops project command available1. The tool allows to install and maintain projects and resources.

Project¤

To use the tool, first activate your environment via poetry shell or `. .venv/bin/activte' in case of a development installation (virtual environ activation in case of a uv / pip(x) based install).

Let's now create a (new) directory for the project:

$ mkdir $HOME/myproject && cd $HOME/myproject && ls -lta
total 8
drwxr-xr-x  2 runner docker 4096 Aug 11 17:21 .
drwxr-x--- 14 runner docker 4096 Aug 11 17:21 ..

Resources¤

devapps (and derived packages) contain resource defining python modules named resources.py.

Resources of devapps¤

Here are the resources defined in devapps. They are required for running the tests but also provide some tools:

$ ops project list

$ ops project list
08-11 17:21:06 [info     ] Directories                    [list] conda_prefix=/home/runner/micromamba fs_dir=/home/runner/micromamba/fs(default)
08-11 17:21:06 [error    ] Not found                      [list] cmd=/home/runner/micromamba/envs/lc_tools/bin/git
08-11 17:21:06 [error    ] Not found                      [list] cmd=/home/runner/micromamba/envs/lc_tools_kf/bin/rotatelogs

╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /home/runner/work/devapps/devapps/src/devapp/tools/__init__.py:1449 in root  │
│                                                                              │
│   1446 │   │   #     return r                                                │
│   1447 │   │   p = project.dir_home                                          │
│   1448 │   │   if not p:                                                     │
│ ❱ 1449 │   │   │   p = project.set_project_dir(no_fail=no_fail)              │
│   1450 │   │   │   if p:                                                     │
│   1451 │   │   │   │   b = p + '/bin:'                                       │
│   1452 │   │   │   │   if b not in envget('PATH', ''):                       │
│                                                                              │
│ ╭──── locals ─────╮                                                          │
│ │ no_fail = False │                                                          │
│ │       p = None  │                                                          │
│ ╰─────────────────╯                                                          │
│                                                                              │
│ /home/runner/work/devapps/devapps/src/devapp/tools/__init__.py:1478 in       │
│ set_project_dir                                                              │
│                                                                              │
│   1475 │   │   │   return                                                    │
│   1476 │   │   from devapp.app import app                                    │
│   1477 │   │                                                                 │
│ ❱ 1478 │   │   app.die('could not determine project root dir')               │
│   1479 │                                                                     │
│   1480 │   def fn_resources():                                               │
│   1481 │   │   return project.root() + '/.resources.json'                    │
│                                                                              │
│ ╭──────────── locals ─────────────╮                                          │
│ │     app = {"DevApp": "project"} │                                          │
│ │       d = '/'                   │                                          │
│ │     dir = None                  │                                          │
│ │      fn = '.git'                │                                          │
│ │ no_fail = False                 │                                          │
│ ╰─────────────────────────────────╯                                          │
│                                                                              │
│ /home/runner/work/devapps/devapps/src/devapp/app.py:171 in die               │
│                                                                              │
│   168 │   │   if silent:                                                     │
│   169 │   │   │   app.fatal(msg, **kw)                                       │
│   170 │   │   │   sys.exit(1)                                                │
│ ❱ 171 │   │   raise DieNow(msg, kw)                                          │
│   172 │                                                                      │
│   173 │   app.die = die                                                      │
│   174 │   app.name = name                                                    │
│                                                                              │
│ ╭──────────────────── locals ─────────────────────╮                          │
│ │     kw = {}                                     │                          │
│ │    msg = 'could not determine project root dir' │                          │
│ │ silent = False                                  │                          │
│ ╰─────────────────────────────────────────────────╯                          │
╰──────────────────────────────────────────────────────────────────────────────╯
DieNow: ('could not determine project root dir', {})

08-11 17:21:06 [error    ] could not determine project root dir [list]

More Tools

devapps based applications usually define more, e.g. databases, more tools or log targets.

Batteries Included - but Replaceable

DevApps' resource management is only meant as a convenience machinery to quickly get up projects or dev setups up and running. In production you'll have more distributed setups anyway, installed e.g. via Ansible and/or Container Orchestrators.

Means: You do not need to have those resources managed as shown below - we install "normal" versions, packaged as Conda packages and use standard config options (see previous chapter why).

Project Init¤

Via the --init_at flag you set up a new project, within the given directory, plus its resources:

$ ops project --init_at . --port_offset 2000 --force
$ tree -L 2

$ ops project --init_at . --port_offset 2000 --force
08-11 17:21:06 [info     ] chdir                          [project] args=/home/runner/myproject store_log=None
08-11 17:21:06 [info     ] Directories                    [project] conda_prefix=/home/runner/micromamba fs_dir=/home/runner/micromamba/fs(default)
08-11 17:21:06 [error    ] Not found                      [project] cmd=/home/runner/micromamba/envs/lc_tools/bin/git
08-11 17:21:06 [error    ] Not found                      [project] cmd=/home/runner/micromamba/envs/lc_tools_kf/bin/rotatelogs
08-11 17:21:06 [warning  ] Installing resources           [project] resources=[{'bin_name': 'lc_tools', 'conda_chan': 'conda-forge', 'conda_pkg': 'git fzf jq ripgrep fd-find httpie htop tmux', 'disabled': False, 'doc': 'The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.\n', 'host_conf_dir': '$PROJECT_ROOT/conf/${host:-$HOSTNAME}/lc_tools', 'installed': False, 'module': 'devapp', 'module_dir': '/home/runner/work/devapps/devapps/src/devapp', 'name': 'lc_tools', 'path': False, 'provides': ['git', 'fzf', 'jq', 'rg', 'fd', 'http', 'htop', 'tmux'], 'run': 'lc_tools', 'verify_present': 'verify_tools'}, {'bin_name': 'lc_tools_kf', 'conda_chan': 'kalefranz', 'conda_pkg': 'httpd', 'disabled': False, 'doc': 'The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.\n', 'host_conf_dir': '$PROJECT_ROOT/conf/${host:-$HOSTNAME}/lc_tools_kf', 'installed': False, 'module': 'devapp', 'module_dir': '/home/runner/work/devapps/devapps/src/devapp', 'name': 'lc_tools_kf', 'path': False, 'provides': ['rotatelogs'], 'verify_present': 'verify_tools'}, {'bin_name': 'redis_server', 'cmd': 'redis-server', 'disabled': False, 'doc': 'The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.\n', 'host_conf_dir': '$PROJECT_ROOT/conf/${host:-$HOSTNAME}/redis_server', 'installed': False, 'module': 'devapp', 'module_dir': '/home/runner/work/devapps/devapps/src/devapp', 'name': 'redis_server', 'path': False, 'pkg': 'redis-server', 'port': 6379, 'provides': ['redis-server', 'redis-cli'], 'run': 'redis_server', 'systemd': 'redis-server'}, {'bin_name': 'slc', 'cmd': 'slc', 'disabled': False, 'doc': 'The base class of the class hierarchy.\n\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any.\n', 'environ': ['lc_hubs'], 'host_conf_dir': '$PROJECT_ROOT/conf/${host:-$HOSTNAME}/slc', 'installed': False, 'module': 'devapp', 'module_dir': '/home/runner/work/devapps/devapps/src/devapp', 'name': 'slc', 'path': False, 'pkg': False}]
08-11 17:21:06 [info     ] create_project_dirs            [project] store_log=None
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/bin
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/data
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/log
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/work
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/conf
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/tmp
08-11 17:21:06 [info     ] creating                       [project] dir=/home/runner/myproject/build
08-11 17:21:06 [info     ] chdir                          [project] args=/home/runner/myproject store_log=None
08-11 17:21:06 [info     ] Install.Conda.conda_env        [project] rsc=      lc_tools ['git', 'fzf', 'jq', 'rg', 'fd', 'http', 'htop', 'tmux'] store_log=None
-ne 
Empty environment created at prefix: /home/runner/micromamba/envs/lc_tools

Transaction

  Prefix: /home/runner/micromamba/envs/lc_tools

  Updating specs:

   - git
   - fzf
   - jq
   - ripgrep
   - fd-find
   - httpie
   - htop
   - tmux

  Package                    Version  Build               Channel          Size
─────────────────────────────────────────────────────────────────────────────────
  Install:
─────────────────────────────────────────────────────────────────────────────────

  + _libgcc_mutex                0.1  conda_forge         conda-forge       3kB
  + _openmp_mutex                4.5  2_gnu               conda-forge      24kB
  + brotli-python              1.1.0  py313h46c70d0_3     conda-forge     350kB
  + bzip2                      1.0.8  h4bc722e_7          conda-forge     253kB
  + c-ares                    1.34.5  hb9d3cd8_0          conda-forge     207kB
  + ca-certificates         2025.8.3  hbd8a1cb_0          conda-forge     154kB
  + certifi                 2025.8.3  pyhd8ed1ab_0        conda-forge     159kB
  + cffi                      1.17.1  py313hfab6e84_0     conda-forge     296kB
  + charset-normalizer         3.4.3  pyhd8ed1ab_0        conda-forge      51kB
  + defusedxml                 0.7.1  pyhd8ed1ab_0        conda-forge      24kB
  + fd-find                   10.2.0  h8fae777_1          conda-forge       1MB
  + fzf                       0.65.1  hf1419bc_0          conda-forge       3MB
  + git                       2.49.0  pl5321hc2ff736_2    conda-forge      11MB
  + h2                         4.2.0  pyhd8ed1ab_0        conda-forge      54kB
  + hpack                      4.1.0  pyhd8ed1ab_0        conda-forge      31kB
  + htop                       3.4.1  haa1a288_0          conda-forge     168kB
  + httpie                     3.2.4  pyh55f8243_1        conda-forge      94kB
  + hyperframe                 6.1.0  pyhd8ed1ab_0        conda-forge      17kB
  + idna                        3.10  pyhd8ed1ab_1        conda-forge      50kB
  + jq                         1.8.1  h73b1eb8_0          conda-forge     313kB
  + keyutils                   1.6.3  hb9d3cd8_0          conda-forge     134kB
  + krb5                      1.21.3  h659f571_0          conda-forge       1MB
  + ld_impl_linux-64            2.44  h1423503_1          conda-forge     676kB
  + libcurl                   8.14.1  h332b0f4_0          conda-forge     450kB
  + libedit             3.1.20250104  pl5321h7949ede_0    conda-forge     135kB
  + libev                       4.33  hd590300_2          conda-forge     113kB
  + libevent                  2.1.12  hf998b51_1          conda-forge     427kB
  + libexpat                   2.7.1  hecca717_0          conda-forge      75kB
  + libffi                     3.4.6  h2dba641_1          conda-forge      57kB
  + libgcc                    15.1.0  h767d61c_4          conda-forge     824kB
  + libgcc-ng                 15.1.0  h69a702a_4          conda-forge      29kB
  + libgomp                   15.1.0  h767d61c_4          conda-forge     447kB
  + libiconv                    1.18  h3b78370_2          conda-forge     790kB
  + liblzma                    5.8.1  hb9d3cd8_2          conda-forge     113kB
  + libmpdec                   4.0.0  hb9d3cd8_0          conda-forge      91kB
  + libnghttp2                1.64.0  h161d5f1_0          conda-forge     648kB
  + libnl                     3.11.0  hb9d3cd8_0          conda-forge     741kB
  + libsqlite                 3.50.4  h0c1763c_0          conda-forge     933kB
  + libssh2                   1.11.1  hcf80075_0          conda-forge     305kB
  + libstdcxx                 15.1.0  h8f9b012_4          conda-forge       4MB
  + libstdcxx-ng              15.1.0  h4852527_4          conda-forge      29kB
  + libuuid                   2.38.1  h0b41bf4_0          conda-forge      34kB
  + libxcrypt                 4.4.36  hd590300_1          conda-forge     100kB
  + libzlib                    1.3.1  hb9d3cd8_2          conda-forge      61kB
  + markdown-it-py             3.0.0  pyhd8ed1ab_1        conda-forge      64kB
  + mdurl                      0.1.2  pyhd8ed1ab_1        conda-forge      14kB
  + multidict                  6.6.3  py313h8060acc_0     conda-forge      97kB
  + ncurses                      6.5  h2d0b736_3          conda-forge     892kB
  + oniguruma                 6.9.10  hb9d3cd8_0          conda-forge     249kB
  + openssl                    3.5.2  h26f9b46_0          conda-forge       3MB
  + pcre2                      10.45  hc749103_0          conda-forge       1MB
  + perl                      5.32.1  7_hd590300_perl5    conda-forge      13MB
  + pip                         25.2  pyh145f28c_0        conda-forge       1MB
  + pycparser                   2.22  pyh29332c3_1        conda-forge     110kB
  + pygments                  2.19.2  pyhd8ed1ab_0        conda-forge     889kB
  + pysocks                    1.7.1  pyha55dd90_7        conda-forge      21kB
  + python                    3.13.5  hec9711d_102_cp313  conda-forge      33MB
  + python_abi                  3.13  8_cp313             conda-forge       7kB
  + readline                     8.2  h8c095d6_2          conda-forge     282kB
  + requests                  2.32.4  pyhd8ed1ab_0        conda-forge      59kB
  + requests-toolbelt          1.0.0  pyhd8ed1ab_1        conda-forge      44kB
  + rich                      14.1.0  pyhe01879c_0        conda-forge     201kB
  + ripgrep                   14.1.1  h8fae777_1          conda-forge       2MB
  + setuptools                80.9.0  pyhff2d567_0        conda-forge     749kB
  + tk                        8.6.13  noxft_hd72426e_102  conda-forge       3MB
  + tmux                         3.5  h4463017_0          conda-forge     469kB
  + typing_extensions         4.14.1  pyhe01879c_0        conda-forge      51kB
  + tzdata                     2025b  h78e105d_0          conda-forge     123kB
  + urllib3                    2.5.0  pyhd8ed1ab_0        conda-forge     102kB
  + zstandard                 0.23.0  py313h536fd9c_2     conda-forge     737kB
  + zstd                       1.5.7  hb8e6e7a_2          conda-forge     568kB

  Summary:

  Install: 71 packages

  Total download: 93MB

─────────────────────────────────────────────────────────────────────────────────

Transaction starting
Linking fzf-0.65.1-hf1419bc_0
Linking ld_impl_linux-64-2.44-h1423503_1
Linking libgomp-15.1.0-h767d61c_4
Linking _libgcc_mutex-0.1-conda_forge
Linking _openmp_mutex-4.5-2_gnu
Linking libgcc-15.1.0-h767d61c_4
Linking libmpdec-4.0.0-hb9d3cd8_0
Linking libiconv-1.18-h3b78370_2
Linking keyutils-1.6.3-hb9d3cd8_0
Linking c-ares-1.34.5-hb9d3cd8_0
Linking libzlib-1.3.1-hb9d3cd8_2
Linking liblzma-5.8.1-hb9d3cd8_2
Linking libffi-3.4.6-h2dba641_1
Linking libexpat-2.7.1-hecca717_0
Linking ncurses-6.5-h2d0b736_3
Linking libstdcxx-15.1.0-h8f9b012_4
Linking libgcc-ng-15.1.0-h69a702a_4
Linking ripgrep-14.1.1-h8fae777_1
Linking oniguruma-6.9.10-hb9d3cd8_0
Linking libnl-3.11.0-hb9d3cd8_0
Linking fd-find-10.2.0-h8fae777_1
Linking libsqlite-3.50.4-h0c1763c_0
Linking tk-8.6.13-noxft_hd72426e_102
Linking libedit-3.1.20250104-pl5321h7949ede_0
Linking readline-8.2-h8c095d6_2
Linking libstdcxx-ng-15.1.0-h4852527_4
Linking zstd-1.5.7-hb8e6e7a_2
Linking libxcrypt-4.4.36-hd590300_1
Linking libev-4.33-hd590300_2
Linking bzip2-1.0.8-h4bc722e_7
Linking libuuid-2.38.1-h0b41bf4_0
Linking jq-1.8.1-h73b1eb8_0
Linking htop-3.4.1-haa1a288_0
Linking perl-5.32.1-7_hd590300_perl5
Linking pcre2-10.45-hc749103_0
Linking python_abi-3.13-8_cp313
Linking tzdata-2025b-h78e105d_0
Linking ca-certificates-2025.8.3-hbd8a1cb_0
Linking openssl-3.5.2-h26f9b46_0
Linking libssh2-1.11.1-hcf80075_0
Linking libevent-2.1.12-hf998b51_1
Linking python-3.13.5-hec9711d_102_cp313
Linking libnghttp2-1.64.0-h161d5f1_0
Linking krb5-1.21.3-h659f571_0
Linking tmux-3.5-h4463017_0
Linking libcurl-8.14.1-h332b0f4_0
Linking git-2.49.0-pl5321hc2ff736_2
Linking pip-25.2-pyh145f28c_0
Linking pycparser-2.22-pyh29332c3_1
Linking pysocks-1.7.1-pyha55dd90_7
Linking hyperframe-6.1.0-pyhd8ed1ab_0
Linking hpack-4.1.0-pyhd8ed1ab_0
Linking mdurl-0.1.2-pyhd8ed1ab_1
Linking idna-3.10-pyhd8ed1ab_1
Linking certifi-2025.8.3-pyhd8ed1ab_0
Linking typing_extensions-4.14.1-pyhe01879c_0
Linking defusedxml-0.7.1-pyhd8ed1ab_0
Linking pygments-2.19.2-pyhd8ed1ab_0
Linking charset-normalizer-3.4.3-pyhd8ed1ab_0
Linking setuptools-80.9.0-pyhff2d567_0
Linking h2-4.2.0-pyhd8ed1ab_0
Linking markdown-it-py-3.0.0-pyhd8ed1ab_1
Linking rich-14.1.0-pyhe01879c_0
Linking brotli-python-1.1.0-py313h46c70d0_3
Linking multidict-6.6.3-py313h8060acc_0
Linking cffi-1.17.1-py313hfab6e84_0
Linking zstandard-0.23.0-py313h536fd9c_2
Linking urllib3-2.5.0-pyhd8ed1ab_0
Linking requests-2.32.4-pyhd8ed1ab_0
Linking requests-toolbelt-1.0.0-pyhd8ed1ab_1
Linking httpie-3.2.4-pyh55f8243_1

Transaction finished

-ne 
08-11 17:21:22 [info     ] Install.Conda.conda_env        [project] rsc=      lc_tools_kf ['rotatelogs'] store_log=None
-ne 
Empty environment created at prefix: /home/runner/micromamba/envs/lc_tools_kf
conda-forge/linux-64                                        Using cache
conda-forge/noarch                                          Using cache

Transaction

  Prefix: /home/runner/micromamba/envs/lc_tools_kf

  Updating specs:

   - httpd

  Package             Version  Build        Channel           Size
────────────────────────────────────────────────────────────────────
  Install:
────────────────────────────────────────────────────────────────────

  + _libgcc_mutex         0.1  conda_forge  conda-forge     Cached
  + _openmp_mutex         4.5  2_gnu        conda-forge     Cached
  + ca-certificates  2025.8.3  hbd8a1cb_0   conda-forge     Cached
  + httpd              2.4.12  6            kalefranz          8MB
  + libgcc             15.1.0  h767d61c_4   conda-forge     Cached
  + libgcc-ng          15.1.0  h69a702a_4   conda-forge     Cached
  + libgomp            15.1.0  h767d61c_4   conda-forge     Cached
  + libstdcxx          15.1.0  h8f9b012_4   conda-forge     Cached
  + libstdcxx-ng       15.1.0  h4852527_4   conda-forge     Cached
  + libzlib             1.3.1  hb9d3cd8_2   conda-forge     Cached
  + openssl             3.5.2  h26f9b46_0   conda-forge     Cached
  + pcre                 8.45  h9c3ff4c_0   conda-forge      259kB
  + zlib                1.3.1  hb9d3cd8_2   conda-forge       92kB

  Summary:

  Install: 13 packages

  Total download: 8MB

────────────────────────────────────────────────────────────────────

Transaction starting
Linking ca-certificates-2025.8.3-hbd8a1cb_0
Linking libgomp-15.1.0-h767d61c_4
Linking _libgcc_mutex-0.1-conda_forge
Linking _openmp_mutex-4.5-2_gnu
Linking libgcc-15.1.0-h767d61c_4
Linking libstdcxx-15.1.0-h8f9b012_4
Linking libgcc-ng-15.1.0-h69a702a_4
Linking libzlib-1.3.1-hb9d3cd8_2
Linking openssl-3.5.2-h26f9b46_0
Linking libstdcxx-ng-15.1.0-h4852527_4
Linking zlib-1.3.1-hb9d3cd8_2
Linking pcre-8.45-h9c3ff4c_0
Linking httpd-2.4.12-6

Transaction finished

-ne 
08-11 17:21:27 [info     ] Install.Conda.conda_env        [project] rsc=s     redis-server ['redis-server', 'redis-cli'] store_log=None
-ne 
Empty environment created at prefix: /home/runner/micromamba/envs/redis_server
conda-forge/linux-64                                        Using cache
conda-forge/noarch                                          Using cache

Transaction

  Prefix: /home/runner/micromamba/envs/redis_server

  Updating specs:

   - redis-server

  Package             Version  Build        Channel           Size
────────────────────────────────────────────────────────────────────
  Install:
────────────────────────────────────────────────────────────────────

  + _libgcc_mutex         0.1  conda_forge  conda-forge     Cached
  + _openmp_mutex         4.5  2_gnu        conda-forge     Cached
  + ca-certificates  2025.8.3  hbd8a1cb_0   conda-forge     Cached
  + libgcc             15.1.0  h767d61c_4   conda-forge     Cached
  + libgcc-ng          15.1.0  h69a702a_4   conda-forge     Cached
  + libgomp            15.1.0  h767d61c_4   conda-forge     Cached
  + openssl             3.5.2  h26f9b46_0   conda-forge     Cached
  + redis-server        7.4.0  h3400bea_0   conda-forge        9MB

  Summary:

  Install: 8 packages

  Total download: 9MB

────────────────────────────────────────────────────────────────────

Transaction starting
Linking ca-certificates-2025.8.3-hbd8a1cb_0
Linking libgomp-15.1.0-h767d61c_4
Linking _libgcc_mutex-0.1-conda_forge
Linking _openmp_mutex-4.5-2_gnu
Linking libgcc-15.1.0-h767d61c_4
Linking openssl-3.5.2-h26f9b46_0
Linking libgcc-ng-15.1.0-h69a702a_4
Linking redis-server-7.4.0-h3400bea_0

Transaction finished

-ne 
08-11 17:21:30 [info     ] Install.no_pkg                 [project] rsc=      slc  store_log=None
08-11 17:21:30 [info     ] No package resource - no install [project]
08-11 17:21:30 [info     ] git_init                       [project] store_log=None
-ne 
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /home/runner/myproject/.git/
-ne
$ tree -L 2
.
├── bin
│   ├── fd
│   ├── fzf
│   ├── git
│   ├── htop
│   ├── http
│   ├── jq
│   ├── redis-cli
│   ├── redis-server
│   ├── rg
│   ├── rotatelogs
│   ├── slc
│   └── tmux
├── build
├── conf
├── data
├── log
├── tmp
│   └── tmux
└── work

9 directories, 12 files

As you can see, we created start files in the bin subdirectory of the project directory, pointing to where the actual binaries had been installed. We did set a global port_offset, which affects any port of listening resources started.

Controlling Resources Installation

There are few options regarding which resources are to be installed, where. See the output of the -h regarding this:

$ ops p -h

$ ops p -h

Creating A Project With Resources
────────────────────────────────────────────────────────────────────────────────

This plugin is helper for creating a project directory, incl. required local
resources. Your system remains unchanged, except <project_dir> and
<conda_prefix>.

It provides an install action (implicitely by providing the
--init_resource_match or --init_at switch)

Default action is: list (show installable resources, -m <match> filters).

At install we will (re-)initialize a "project_dir", at location given with
--init_at (default: '.'), incl:

❖ Installing available resources, like databases and tools within a given
  directory (conda_prefix)

❖ Creating resource start wrappers in <project_dir>/bin

❖ Generating default config when required

❖ Optionally generating systemd unit files (e.g. via: --init_create_all_units)

❖ Instances support: export <name>_instances=x before running and you'll get x
  systemd units created, for startable commands.

  Example: export client_instances=10; ops p -irm client -icau
  (Name of a resource: ops p [-m <match>])

❖ Any other parametrization: Via environ variables Check key environ vars in
  list output and also doc text.

Privilege escalation is not required for any of these steps.

Main command line flags [matching ops_devapp.project]:
appc    add_post_process_cmd     ''
Add this to all commands which have systemd service units. Intended for output redirection. Not applied when stdout is a tty.
Example: -appc='2>&1 | rotatelogs -e -n1 "$logfile" 1M' ($logfile defined in wrapper -> use single quotes).
Tip: Use rotatelogs only on powers of 10 - spotted problems with 200M. Use 100M or 1G in that case. 
cp      conda_prefix             /home/runner/micromamba

    Resources install location, except filesystem based ones. Env vars resolved.

    Aliases:
    - local|l: <project_dir>/conda
    - default|d: $HOME/miniconda3 (default path of conda)
    - current|c: Any current conda_prefix set when running.

    Note: Installing resources outside the project keeps the project relocatable and resources reusable for other products.

csu     create_system_units      False
Instead of user unit files, create system files, in /etc/systemd/system. Implies -icau. 🟥 A sudo password is required! 
damsu   delete_all_matching_service_unit_files ''
This removes all matching unit files calling devapp service wrappers. Say "service" to match all 
di      dev_install              False
Set the project up in developer mode - incl. make and poetry file machinery 
emrf    edit_matching_resource_file ''
Open resource files in $EDITOR, matching given string in their content 
f       force                    False
Assume y on all questions. Required when started w/o a tty 
fr      force_reinstall          False
Do not only install resources detected uninstalled but reinstall all 
fd      fs_dir                   default

    Filesystem based resource location. Env vars resolved.
    Aliases:
    - local|l: <project_dir>/fs
    - default|d: $HOME/miniconda3/fs (default path of conda)
    - conda|c: Within conda_prefix/fs

ia      init_at                  ''
Set up project in given directory. env vars / relative dirs supported. Sets install action implicitly 
icau    init_create_all_units    False
If set we create (user) unit files for service type resources 
icuf    init_create_unit_files   ''
List service unit files you want to have created for systemctl --user.  
Valid: Entries in rsc.provides, rsc.cmd or rsc.exe (i.e. the filename of the wrapper in bin dir). Not: rsc.name.
irm     init_resource_match      ''
Like resource match but implies install action 
i       install                  ACTION
Install 
is      install_state            False
show install state infos 
l       list                     ACTION*
Show available definition files. 
lrf     list_resources_files     ACTION
Alias for list action 
loref   log_resources_fully      False
Always output all settings of resources when logging 
m       resource_match           ''
Provide a match string for actions. Examples: -irm "redis, hub" or -irm '!mysql, !redis' (! negates).

-hf [match string]: List ALL (matching) flags. E.g. -hf or -hf log.

More CLI flags

More control flags are only accessible via --helpfull <match> (-hf): Try ops p -hf log_level, ops p -hf port (...)

Project initialization flags

In the example above, a --port_offset=2000 flag was given, determining the start parameters written into the redis wrapper:

$ bin/redis-server
4483:C 11 Aug 2025 17:21:33.690 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
4483:C 11 Aug 2025 17:21:33.690 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
4483:C 11 Aug 2025 17:21:33.690 * Redis version=7.4.0, bits=64, commit=abcd3ea3, modified=0, pid=4483, just started
4483:C 11 Aug 2025 17:21:33.690 * Configuration loaded
4483:M 11 Aug 2025 17:21:33.690 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis Community Edition      
  .-`` .-```.  ```\/    _.,_ ''-._     7.4.0 (abcd3ea3/0) 64 bit
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 8379
 |    `-._   `._    /     _.-'    |     PID: 4483
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

4483:M 11 Aug 2025 17:21:33.691 * Server initialized
4483:M 11 Aug 2025 17:21:33.691 * Ready to accept connections tcp

Idempotency

If you need to re-parametrize the project (e.g. set different port offsets) then run ops project --init_at again and have new start wrappers created.

Unit Files¤

We do not try to manage the live cycle of services but leave that to systemd (available on all Linux major distributions).

The --init_create_unit_files=<name of daemon resource> will create a unit file after installing the resource itself:

Creating a resource incl. unit file

```bash xlp session=project ops project --init_at=. --init_create_unit_files=redis-server --force

You control the service using `systemctl --user`:

```bash xlp session=project
[
{'cmd': 'systemctl --user --no-pager start  redis-server-myproject'},
{'cmd': 'systemctl --user --no-pager status redis-server-myproject', 'assert': '(running)'},
{'cmd': 'systemctl --user --no-pager stop   redis-server-myproject'}
]

Hint

In order to install unit files for ALL service type resources, you can supply --init_create_all_units, alternatively.


  1. Technically project is implemented as a plugin of the ops tool.