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
[2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mDirectories [[38;5;5mlist[39m] [1m[38;5;1mconda_prefix=[2m[38;5;5m/home/runner/micromamba [38;5;1mfs_dir=[38;5;5m/home/runner/micromamba/fs(default)
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124merror ] [0mNot found [[38;5;5mlist[39m] [1m[38;5;1mcmd=[2m[38;5;5m/home/runner/micromamba/envs/lc_tools/bin/git
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124merror ] [0mNot found [[38;5;5mlist[39m] [1m[38;5;1mcmd=[2m[38;5;5m/home/runner/micromamba/envs/lc_tools_kf/bin/rotatelogs
[0m[31m╭───────────────────── [1mTraceback [2m(most recent call last)[0m[31m ──────────────────────╮
│[39m [2m/home/runner/work/devapps/devapps/src/devapp/tools/[0;1m__init__.py[0m:1449 in root [31m│
│[39m [31m│
│[39m [2m1446 │ │ # return r[0m [31m│
│[39m [2m1447 │ │ [0mp = project.dir_home [31m│
│[39m [2m1448 │ │ [0m[94mif[39m [95mnot[39m p: [31m│
│[39m [31m❱ [39m1449 [2m│ │ │ [0mp = project.set_project_dir(no_fail=no_fail) [31m│
│[39m [2m1450 │ │ │ [0m[94mif[39m p: [31m│
│[39m [2m1451 │ │ │ │ [0mb = p + [33m'/bin:'[39m [31m│
│[39m [2m1452 │ │ │ │ [0m[94mif[39m b [95mnot[39m [95min[39m envget([33m'PATH'[39m, [33m''[39m): [31m│
│[39m [31m│
│[39m [34m╭──── locals ─────╮[39m [31m│
│[39m [34m│[39m [3m[33mno_fail[0m[31m =[39m [3m[91mFalse[0m [34m│[39m [31m│
│[39m [34m│[39m [3m[33mp[0m[31m =[39m [3m[35mNone[0m [34m│[39m [31m│
│[39m [34m╰─────────────────╯[39m [31m│
│[39m [31m│
│[39m [2m/home/runner/work/devapps/devapps/src/devapp/tools/[0;1m__init__.py[0m:1478 in [31m│
│[39m set_project_dir [31m│
│[39m [31m│
│[39m [2m1475 │ │ │ [0m[94mreturn[39m [31m│
│[39m [2m1476 │ │ [0m[94mfrom[90m [4m[96mdevapp.app[0m[90m [94mimport[39m app [31m│
│[39m [2m1477 │ │ [0m [31m│
│[39m [31m❱ [39m1478 [2m│ │ [0mapp.die([33m'could not determine project root dir'[39m) [31m│
│[39m [2m1479 │ [0m [31m│
│[39m [2m1480 │ [0m[94mdef[90m [92mfn_resources[39m(): [31m│
│[39m [2m1481 │ │ [0m[94mreturn[39m project.root() + [33m'/.resources.json'[39m [31m│
│[39m [31m│
│[39m [34m╭──────────── locals ─────────────╮[39m [31m│
│[39m [34m│[39m [3m[33mapp[0m[31m =[39m [1m{[0m[32m"DevApp"[39m: [32m"project"[1m[39m}[0m [34m│[39m [31m│
│[39m [34m│[39m [3m[33md[0m[31m =[39m [32m'/'[39m [34m│[39m [31m│
│[39m [34m│[39m [3m[33mdir[0m[31m =[39m [3m[35mNone[0m [34m│[39m [31m│
│[39m [34m│[39m [3m[33mfn[0m[31m =[39m [32m'.git'[39m [34m│[39m [31m│
│[39m [34m│[39m [3m[33mno_fail[0m[31m =[39m [3m[91mFalse[0m [34m│[39m [31m│
│[39m [34m╰─────────────────────────────────╯[39m [31m│
│[39m [31m│
│[39m [2m/home/runner/work/devapps/devapps/src/devapp/[0;1mapp.py[0m:171 in die [31m│
│[39m [31m│
│[39m [2m168 │ │ [0m[94mif[39m silent: [31m│
│[39m [2m169 │ │ │ [0mapp.fatal(msg, **kw) [31m│
│[39m [2m170 │ │ │ [0msys.exit([94m1[39m) [31m│
│[39m [31m❱ [39m171 [2m│ │ [0m[94mraise[39m DieNow(msg, kw) [31m│
│[39m [2m172 │ [0m [31m│
│[39m [2m173 │ [0mapp.die = die [31m│
│[39m [2m174 │ [0mapp.name = name [31m│
│[39m [31m│
│[39m [34m╭──────────────────── locals ─────────────────────╮[39m [31m│
│[39m [34m│[39m [3m[33mkw[0m[31m =[39m [1m{}[0m [34m│[39m [31m│
│[39m [34m│[39m [3m[33mmsg[0m[31m =[39m [32m'could not determine project root dir'[39m [34m│[39m [31m│
│[39m [34m│[39m [3m[33msilent[0m[31m =[39m [3m[91mFalse[0m [34m│[39m [31m│
│[39m [34m╰─────────────────────────────────────────────────╯[39m [31m│
╰──────────────────────────────────────────────────────────────────────────────╯
[1m[91mDieNow: [39m([0m[32m'could not determine project root dir'[39m, [1m{})
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124merror ] [0mcould not determine project root dir [[38;5;5mlist[39m]
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
[2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mchdir [[38;5;5mproject[39m] [1m[38;5;1margs=[2m[38;5;5m/home/runner/myproject [38;5;1mstore_log=[38;5;5mNone
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mDirectories [[38;5;5mproject[39m] [1m[38;5;1mconda_prefix=[2m[38;5;5m/home/runner/micromamba [38;5;1mfs_dir=[38;5;5m/home/runner/micromamba/fs(default)
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124merror ] [0mNot found [[38;5;5mproject[39m] [1m[38;5;1mcmd=[2m[38;5;5m/home/runner/micromamba/envs/lc_tools/bin/git
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124merror ] [0mNot found [[38;5;5mproject[39m] [1m[38;5;1mcmd=[2m[38;5;5m/home/runner/micromamba/envs/lc_tools_kf/bin/rotatelogs
[0;2m[38;5;5m08-11 17:21:06 [[1m[38;5;124mwarning ] [0mInstalling resources [[38;5;5mproject[39m] [1m[38;5;1mresources=[2m[38;5;5m[{'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}]
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreate_project_dirs [[38;5;5mproject[39m] [1m[38;5;1mstore_log=[2m[38;5;5mNone
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/bin
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/data
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/log
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/work
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/conf
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/tmp
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mcreating [[38;5;5mproject[39m] [1m[38;5;1mdir=[2m[38;5;5m/home/runner/myproject/build
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mchdir [[38;5;5mproject[39m] [1m[38;5;1margs=[2m[38;5;5m/home/runner/myproject [38;5;1mstore_log=[38;5;5mNone
[0;2m[38;5;5m08-11 17:21:06 [[38;5;4minfo ] [0mInstall.Conda.conda_env [[38;5;5mproject[39m] [1m[38;5;1mrsc=[2m[38;5;5m lc_tools ['git', 'fzf', 'jq', 'rg', 'fd', 'http', 'htop', 'tmux'] [38;5;1mstore_log=[38;5;5mNone
[0m-ne
[38;5;240mEmpty 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:
─────────────────────────────────────────────────────────────────────────────────
[32m+ _libgcc_mutex [39m 0.1 conda_forge conda-forge 3kB
[32m+ _openmp_mutex [39m 4.5 2_gnu conda-forge 24kB
[32m+ brotli-python [39m 1.1.0 py313h46c70d0_3 conda-forge 350kB
[32m+ bzip2 [39m 1.0.8 h4bc722e_7 conda-forge 253kB
[32m+ c-ares [39m 1.34.5 hb9d3cd8_0 conda-forge 207kB
[32m+ ca-certificates [39m 2025.8.3 hbd8a1cb_0 conda-forge 154kB
[32m+ certifi [39m 2025.8.3 pyhd8ed1ab_0 conda-forge 159kB
[32m+ cffi [39m 1.17.1 py313hfab6e84_0 conda-forge 296kB
[32m+ charset-normalizer[39m 3.4.3 pyhd8ed1ab_0 conda-forge 51kB
[32m+ defusedxml [39m 0.7.1 pyhd8ed1ab_0 conda-forge 24kB
[32m+ fd-find [39m 10.2.0 h8fae777_1 conda-forge 1MB
[32m+ fzf [39m 0.65.1 hf1419bc_0 conda-forge 3MB
[32m+ git [39m 2.49.0 pl5321hc2ff736_2 conda-forge 11MB
[32m+ h2 [39m 4.2.0 pyhd8ed1ab_0 conda-forge 54kB
[32m+ hpack [39m 4.1.0 pyhd8ed1ab_0 conda-forge 31kB
[32m+ htop [39m 3.4.1 haa1a288_0 conda-forge 168kB
[32m+ httpie [39m 3.2.4 pyh55f8243_1 conda-forge 94kB
[32m+ hyperframe [39m 6.1.0 pyhd8ed1ab_0 conda-forge 17kB
[32m+ idna [39m 3.10 pyhd8ed1ab_1 conda-forge 50kB
[32m+ jq [39m 1.8.1 h73b1eb8_0 conda-forge 313kB
[32m+ keyutils [39m 1.6.3 hb9d3cd8_0 conda-forge 134kB
[32m+ krb5 [39m 1.21.3 h659f571_0 conda-forge 1MB
[32m+ ld_impl_linux-64 [39m 2.44 h1423503_1 conda-forge 676kB
[32m+ libcurl [39m 8.14.1 h332b0f4_0 conda-forge 450kB
[32m+ libedit [39m 3.1.20250104 pl5321h7949ede_0 conda-forge 135kB
[32m+ libev [39m 4.33 hd590300_2 conda-forge 113kB
[32m+ libevent [39m 2.1.12 hf998b51_1 conda-forge 427kB
[32m+ libexpat [39m 2.7.1 hecca717_0 conda-forge 75kB
[32m+ libffi [39m 3.4.6 h2dba641_1 conda-forge 57kB
[32m+ libgcc [39m 15.1.0 h767d61c_4 conda-forge 824kB
[32m+ libgcc-ng [39m 15.1.0 h69a702a_4 conda-forge 29kB
[32m+ libgomp [39m 15.1.0 h767d61c_4 conda-forge 447kB
[32m+ libiconv [39m 1.18 h3b78370_2 conda-forge 790kB
[32m+ liblzma [39m 5.8.1 hb9d3cd8_2 conda-forge 113kB
[32m+ libmpdec [39m 4.0.0 hb9d3cd8_0 conda-forge 91kB
[32m+ libnghttp2 [39m 1.64.0 h161d5f1_0 conda-forge 648kB
[32m+ libnl [39m 3.11.0 hb9d3cd8_0 conda-forge 741kB
[32m+ libsqlite [39m 3.50.4 h0c1763c_0 conda-forge 933kB
[32m+ libssh2 [39m 1.11.1 hcf80075_0 conda-forge 305kB
[32m+ libstdcxx [39m 15.1.0 h8f9b012_4 conda-forge 4MB
[32m+ libstdcxx-ng [39m 15.1.0 h4852527_4 conda-forge 29kB
[32m+ libuuid [39m 2.38.1 h0b41bf4_0 conda-forge 34kB
[32m+ libxcrypt [39m 4.4.36 hd590300_1 conda-forge 100kB
[32m+ libzlib [39m 1.3.1 hb9d3cd8_2 conda-forge 61kB
[32m+ markdown-it-py [39m 3.0.0 pyhd8ed1ab_1 conda-forge 64kB
[32m+ mdurl [39m 0.1.2 pyhd8ed1ab_1 conda-forge 14kB
[32m+ multidict [39m 6.6.3 py313h8060acc_0 conda-forge 97kB
[32m+ ncurses [39m 6.5 h2d0b736_3 conda-forge 892kB
[32m+ oniguruma [39m 6.9.10 hb9d3cd8_0 conda-forge 249kB
[32m+ openssl [39m 3.5.2 h26f9b46_0 conda-forge 3MB
[32m+ pcre2 [39m 10.45 hc749103_0 conda-forge 1MB
[32m+ perl [39m 5.32.1 7_hd590300_perl5 conda-forge 13MB
[32m+ pip [39m 25.2 pyh145f28c_0 conda-forge 1MB
[32m+ pycparser [39m 2.22 pyh29332c3_1 conda-forge 110kB
[32m+ pygments [39m 2.19.2 pyhd8ed1ab_0 conda-forge 889kB
[32m+ pysocks [39m 1.7.1 pyha55dd90_7 conda-forge 21kB
[32m+ python [39m 3.13.5 hec9711d_102_cp313 conda-forge 33MB
[32m+ python_abi [39m 3.13 8_cp313 conda-forge 7kB
[32m+ readline [39m 8.2 h8c095d6_2 conda-forge 282kB
[32m+ requests [39m 2.32.4 pyhd8ed1ab_0 conda-forge 59kB
[32m+ requests-toolbelt [39m 1.0.0 pyhd8ed1ab_1 conda-forge 44kB
[32m+ rich [39m 14.1.0 pyhe01879c_0 conda-forge 201kB
[32m+ ripgrep [39m 14.1.1 h8fae777_1 conda-forge 2MB
[32m+ setuptools [39m 80.9.0 pyhff2d567_0 conda-forge 749kB
[32m+ tk [39m 8.6.13 noxft_hd72426e_102 conda-forge 3MB
[32m+ tmux [39m 3.5 h4463017_0 conda-forge 469kB
[32m+ typing_extensions [39m 4.14.1 pyhe01879c_0 conda-forge 51kB
[32m+ tzdata [39m 2025b h78e105d_0 conda-forge 123kB
[32m+ urllib3 [39m 2.5.0 pyhd8ed1ab_0 conda-forge 102kB
[32m+ zstandard [39m 0.23.0 py313h536fd9c_2 conda-forge 737kB
[32m+ zstd [39m 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
[2m[38;5;5m08-11 17:21:22 [[38;5;4minfo ] [0mInstall.Conda.conda_env [[38;5;5mproject[39m] [1m[38;5;1mrsc=[2m[38;5;5m lc_tools_kf ['rotatelogs'] [38;5;1mstore_log=[38;5;5mNone
[0m-ne
[38;5;240mEmpty 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:
────────────────────────────────────────────────────────────────────
[32m+ _libgcc_mutex [39m 0.1 conda_forge conda-forge[32m Cached
[39m [32m+ _openmp_mutex [39m 4.5 2_gnu conda-forge[32m Cached
[39m [32m+ ca-certificates[39m 2025.8.3 hbd8a1cb_0 conda-forge[32m Cached
[39m [32m+ httpd [39m 2.4.12 6 kalefranz 8MB
[32m+ libgcc [39m 15.1.0 h767d61c_4 conda-forge[32m Cached
[39m [32m+ libgcc-ng [39m 15.1.0 h69a702a_4 conda-forge[32m Cached
[39m [32m+ libgomp [39m 15.1.0 h767d61c_4 conda-forge[32m Cached
[39m [32m+ libstdcxx [39m 15.1.0 h8f9b012_4 conda-forge[32m Cached
[39m [32m+ libstdcxx-ng [39m 15.1.0 h4852527_4 conda-forge[32m Cached
[39m [32m+ libzlib [39m 1.3.1 hb9d3cd8_2 conda-forge[32m Cached
[39m [32m+ openssl [39m 3.5.2 h26f9b46_0 conda-forge[32m Cached
[39m [32m+ pcre [39m 8.45 h9c3ff4c_0 conda-forge 259kB
[32m+ zlib [39m 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
[2m[38;5;5m08-11 17:21:27 [[38;5;4minfo ] [0mInstall.Conda.conda_env [[38;5;5mproject[39m] [1m[38;5;1mrsc=[2m[38;5;5ms redis-server ['redis-server', 'redis-cli'] [38;5;1mstore_log=[38;5;5mNone
[0m-ne
[38;5;240mEmpty 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:
────────────────────────────────────────────────────────────────────
[32m+ _libgcc_mutex [39m 0.1 conda_forge conda-forge[32m Cached
[39m [32m+ _openmp_mutex [39m 4.5 2_gnu conda-forge[32m Cached
[39m [32m+ ca-certificates[39m 2025.8.3 hbd8a1cb_0 conda-forge[32m Cached
[39m [32m+ libgcc [39m 15.1.0 h767d61c_4 conda-forge[32m Cached
[39m [32m+ libgcc-ng [39m 15.1.0 h69a702a_4 conda-forge[32m Cached
[39m [32m+ libgomp [39m 15.1.0 h767d61c_4 conda-forge[32m Cached
[39m [32m+ openssl [39m 3.5.2 h26f9b46_0 conda-forge[32m Cached
[39m [32m+ redis-server [39m 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
[2m[38;5;5m08-11 17:21:30 [[38;5;4minfo ] [0mInstall.no_pkg [[38;5;5mproject[39m] [1m[38;5;1mrsc=[2m[38;5;5m slc [38;5;1mstore_log=[38;5;5mNone
[0;2m[38;5;5m08-11 17:21:30 [[38;5;4minfo ] [0mNo package resource - no install [[38;5;5mproject[39m]
[2m[38;5;5m08-11 17:21:30 [[38;5;4minfo ] [0mgit_init [[38;5;5mproject[39m] [1m[38;5;1mstore_log=[2m[38;5;5mNone
[0m-ne
[33mhint: 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: [39m [33mgit 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: [39m [33mgit branch -m <name>
[39mInitialized 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
[1m[38;5;11mCreating A Project With Resources
────────────────────────────────────────────────────────────────────────────────
[0mThis 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 [38;5;5minstall[39m action (implicitely by providing the
--init_resource_match or --init_at switch)
Default action is: [38;5;5mlist[39m (show installable resources, -m <match> filters).
At install we will (re-)initialize a "project_dir", at location given with
--init_at (default: '.'), incl:
[38;5;5m❖ [39mInstalling available resources, like databases and tools within a given
directory (conda_prefix)
[38;5;5m❖ [39mCreating resource start wrappers in <project_dir>/bin
[38;5;5m❖ [39mGenerating default config when required
[38;5;5m❖ [39mOptionally generating systemd unit files (e.g. via: --init_create_all_units)
[38;5;5m❖ [39mInstances support: export <name>_instances=x before running and you'll get x
systemd units created, for startable commands.
[38;5;5m Example: export client_instances=10; ops p -irm client -icau
(Name of a resource: ops p [-m <match>])
❖ [39mAny 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 [1m[32mops_devapp.project[0m]:
[32mappc [1m[33madd_post_process_cmd [0m[31m''
[38;5;245mAdd 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.
[32mcp [1m[33mconda_prefix [0m[31m/home/runner/micromamba
[38;5;245m 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.
[32mcsu [1m[33mcreate_system_units [0m[31mFalse
[38;5;245mInstead of user unit files, create system files, in /etc/systemd/system. Implies -icau. 🟥 A sudo password is required!
[32mdamsu [1m[33mdelete_all_matching_service_unit_files [0m[31m''
[38;5;245mThis removes all matching unit files calling devapp service wrappers. Say "service" to match all
[32mdi [1m[33mdev_install [0m[31mFalse
[38;5;245mSet the project up in developer mode - incl. make and poetry file machinery
[32memrf [1m[33medit_matching_resource_file [0m[31m''
[38;5;245mOpen resource files in $EDITOR, matching given string in their content
[32mf [1m[33mforce [0m[31mFalse
[38;5;245mAssume y on all questions. Required when started w/o a tty
[32mfr [1m[33mforce_reinstall [0m[31mFalse
[38;5;245mDo not only install resources detected uninstalled but reinstall all
[32mfd [1m[33mfs_dir [0m[31mdefault
[38;5;245m 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
[32mia [1m[33minit_at [0m[31m''
[38;5;245mSet up project in given directory. env vars / relative dirs supported. Sets install action implicitly
[32micau [1m[33minit_create_all_units [0m[31mFalse
[38;5;245mIf set we create (user) unit files for service type resources
[32micuf [1m[33minit_create_unit_files [0m[31m''
[38;5;245mList service unit files you want to have created for systemctl --user.
[38;5;241mValid: Entries in rsc.provides, rsc.cmd or rsc.exe (i.e. the filename of the wrapper in bin dir). Not: rsc.name.
[32mirm [1m[33minit_resource_match [0m[31m''
[38;5;245mLike resource match but implies install action
[32mi [1m[33minstall [0m[33mACTION
[38;5;245mInstall
[32mis [1m[33minstall_state [0m[31mFalse
[38;5;245mshow install state infos
[32ml [1m[33mlist ACTION*
[0m[38;5;245mShow available definition files.
[32mlrf [1m[33mlist_resources_files [0m[33mACTION
[38;5;245mAlias for list action
[32mloref [1m[33mlog_resources_fully [0m[31mFalse
[38;5;245mAlways output all settings of resources when logging
[32mm [1m[33mresource_match [0m[31m''
[38;5;245mProvide a match string for actions. Examples: -irm "redis, hub" or -irm '!mysql, !redis' (! negates).
[36m-hf [match string][39m: List [1m[36mALL[0m (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.