Commit graph

219 commits

Author SHA1 Message Date
bb13aea694 lib.ExecContext: Fix ignored --interactive

Whether or not the CallContext.interactive property should be True or False, and hence, a call should be processed interactively, depends on multiple factors, constituting matrix of options with multiple preferences.

--interactive is the application default and can be true, false, or auto
- A call can be explicitly invoked as interactive, non-interactive or auto via the cmd_input parameter to ExecContext.run()

This commit adds more "mode:" options to make the latter more explicit. It takes preference over the global --interactive parameter: Global --interactive is only given a chance to decide if cmd_input is None (default) or mode:opt-interactive.

This commit also fixes a bug: --interactive is ignored because the interactive argument passed to ExecContext's constructor is ignored later on in calls to the wrapped _run() and _sudo() methods.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-25 07:32:45 +01:00
3bda9bc826 lib.ExecContext.sudo(): Default None mod_env to {}

mod_env can be None. Make it an empty dict in that case to take a little burden off the implementations in the derived classes.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-25 07:32:45 +01:00
21e67291b5 Fix: Decode run_cmd() result

Since commit 02697af5, ExecContext.run() returns bytes for stdout and stderr and fixes that in calling code. The thing it did not fix was the code calling run_cmd(), which also made return bytes. This commit catches up on that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-25 07:32:45 +01:00
fd35fa0871 lib.Distro: Add missing async

Distro's sudo() and run() wrappers are not flagged async. It still works, because throughout jw-pkg all callers expect a coroutine return value, but flagging them as async makes the return value obvious.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-23 11:33:35 +01:00
f4c76ebab9 lib.ec.SSHClientInternal|SSHClientCmd: Own .py

Move the code of SSHClientInternal and SSCClientCmd into lib.ec.ssh, as "Paramiko" and "Exec", respectively. This makes the class layout a little more modular, and along the way fixes a bug where SSHClientInternal could be instantiated but was unusable (if the Paramiko is not installed).

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 13:35:50 +01:00
f37f025b17 lib.SSHClient: Move to lib.ec

SSHClient in an ExecContext, hence it's better off in lib.ec, move it there and adapt the references.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 13:35:11 +01:00
5078c27682 cmds.projects.CmdListRepos.run(): Fix trailing newline

projects list-repos prints a traling newline, remove that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
02697af568 lib.ExecContext: Align .sudo() prototype to .run()

ExecContext's .sudo() omits many of run()'s parameters, and this commit adds them. To avoid redundancy around repeating and massaging the long parameter list of both functions and their return values, it also adds some deeper changes:

- Make run(), _run(), sudo() and _sudo() always return instances of Result. Before it was allowed to return a triplet of stdout, stderr, and exit status.
- Have ExecContext stay out of the business of decoding the result entirely. Result provides a convenience method .decode() operating on stdout and stderr and leaves the decision to the caller.
This entails miniscule adaptations in calling code, namely in App.os_release, util.get_profile_env() and CmdListRepos._run().
- Wrap the _run() and _sudo() callbacks in a context manager object of type CallContext to avoid code duplication.
- Consistently name the first argument to run(), _run(), sudo() and _sudo() "cmd", not "args". The latter suggests that the caller is omitting the executable, which is not the case.
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
37af0a05e9 lib.SSHClient: Implement verbose logging
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
e924f34441 pkg.App.os_release: Call cat /etc/os-release

Don't open and parse /etc/os-release with Python built-in functions. Spawn "cat /etc/os-release" as a subprocess and capture the output for parsing instead. The obvious advantage is that this also works with a remote shell.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
df40af9fc3 lib.App.call_async(): Add method

Use the AsyncRunner class introduced in the previous commit to add a call_async() method, allowing to run async functions from sync functions by spawning an extra event loop.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
67e51cf07c lib.AsyncRunner: Add class

Add class AsyncRunner. This is a wrapper around the ceremony needed to spawn an extra event loop in a synchronous function which wants to call an async function.

Guido van Rossum considers it bad design that such a function exists in the first place. While that may be true in the long run also for jw-pkg, at this point I'm unwilling to flag every lazyly initialized App property as async. It's not clear, yet, which will be async and which not, and I dread the churn. So I will accept this as a minimally invasive helper for now. When the API has stabilized, a design without it may be better.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
95a384bfff lib.App.eloop: Add property

Expose App's __eloop member containing the application's main event loop to allow outside async event loop trickery.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
52dd3b8f21 lib.ExecContext.run(): Push code up into base class

Take implementation burden from the derived classes _run() callback by moving the respective code into the run() wrapper methods of the base class.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
f2ffe85b61 lib.SSHClientInternal: No key_filename in connect()

Remove the key_filename parameter from the call to Paramiko's connect(). It's user-dependent, and the current DevOps implementation relies on having a SSH_AUTH_SOCK in the environment, anyway.

Signed-off-by: janware DevOps <devops@janware.com>
2026-03-20 10:30:25 +01:00
49daa86696 lib.SSHClient: Log more details around exceptions

Add a wrapper around urlparse() and Paramiko's connect() function, in order to log some more info in case an exception is thrown.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
d905f6d555 App: Support --uri <remote-host>

Add a --uri option to App, allowing jw-pkg.py to operate over the wire.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
76b702f5b4 lib.ExecContext.create(): Add method

Add a class method to instantiate an ExecContext by its URI.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
5f6ec5a182 lib.SSHClient: Retire non-EC API

Remove .run_cmd(), forcing future clients to use the ExecContext aligned API.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
888c1e7f16 lib.ExecContext.__init__(): Add parameter uri

Take a positional uri argument to the constructor of ExecContext, forcing SSHClient to follow suit. The latter was instantiated with a hostname as only argument up to now, which still works as a special case of an uri.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
db9bf1826d cmds.projects.CmdListRepos: Use new SSHClient API

Use SSHClient as an ExecContext, i.e. use the .run() method instead of .run_cmd(). Also, let SSHClient decide which implementation to use.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
a76a6c9316 lib.SSHClient: Move public methods down

Code beautification chore: Move the public methods of SSHClients to the bottom of the class to be consistent with other classes.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
284eb30ecf lib.SSHClient: Derive from ExecContext

Make SSHClient an ExecContext by implementing _run() and _sudo().

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
989e2c93e3 lib.SSHClient.run_cmd(): Align prototype with EC

Align the prototype of SSHClient.run_cmd() to ExecContext.run(). This is a push towards making the SSHClient code an ExceContext, too. Some arguments still log a warning or outright raise NotImplementedError.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
666f778e05 cmds.distro.CmdInfo: Beautify exception logging

CmdInfo._expand_macros() raises a custom exception during exception handling. Replace that by logging some details and raising the original exception to keep the stack trace readable.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-20 10:30:25 +01:00
d0776db01f lib.SSHClient.run_cmd(): Accept cmd: list[str]

Make SSHClient accept a list of strings for the cmd argument to align with the other run_cmd() functions in jw-pkg.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-17 16:23:34 +01:00
a6bf4b164a cmds/__init__.py: Make class loading dynamic
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-17 10:44:24 +01:00
58142a1115 lib.Distro.pkg_files(): Fix argument name
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-17 07:35:21 +01:00
824de4eca4 lib.distros.*.Distro: Align PM prototypes

Make all backend package manager prototypes have the same arguments:

yum(self, args: list[str], verbose: bool=True, sudo: bool=True)

This also implies having them behave equally verbose, unless otherwise specified by the caller. This changes the default for Debian.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-16 14:53:58 +01:00
8869a2d3df cmds.projects.BaseCmdPkgRelations: Code beautification

pkg_relations_list() has an intricate case distinction around expand_semver_revision_range, clean that up.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-16 06:05:01 +01:00
277e5166e8 App.get_value(): Log return value

Log what App.get_value() lookups return with priority DEBUG, insightful for debugging.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 19:34:50 +01:00
5b3118319c cmds.projects.CmdRequiredOsPkg|BaseCmdPkgRelations: Align APIs

In a push to eventually merge the classes, somewhat align the command-line API of CmdRequiredOsPkg to the one of BaseCmdPkgRelations by using dependency flavours as mandatory, first argument.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 18:33:51 +01:00
d7e4bd9e33 cmds.projects.BaseCmdPkgRelations: Support --hide-self

To support the pkg-install-testbuild-deps target, a selector is needed listing all prerequisites to be installed except the project under test. --hide-self should be useful for that.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 18:33:51 +01:00
489652a02f cmds.projects.BaseCmdPkgRelations: Support --skip-excluded

In a push to eventually merge class CmdRequiredOsPkg into this class, add the --skip-excluded option required by it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 18:33:51 +01:00
f075d3d0cf cmds.projects.BaseCmdPkgRelations: Support --quote

In a push to eventually merge class CmdRequiredOsPkg into this class, add the --quote option required by it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 18:33:51 +01:00
d643956846 cmds.projects.BaseCmdPkgRelations: Add pkg_relations_list()

Add a function pkg_relations_list(), doing pretty much the same as pkg_relations(), but taking individual arguments instead of an argparse.Namespace args argument, in order to provide the functionality to derived classes.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 18:33:51 +01:00
5f81f8d4da cmds.projects.BaseCmdPkgRelations: Add type hints

Add type hints to the class's methods.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 16:42:04 +01:00
57f204ccaa cmds.projects.BaseCmdPkgRelations: Remove --no-version

Merge --no-version into the --syntax option as "--syntax names-only" to remove redundancy.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 16:41:59 +01:00
cff63786e9 lib.Distro: Allow empty packages list

In commands taking lists of packages, namely install, delete and pkg_files, don't bother asking the backend. Uniformly log a warning and return successfully.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 16:41:46 +01:00
7046983d7c cmds.projects.BaseCmdPkgRelations: --syntax debian

Add support for --syntax to BaseCmdPkgRelations.pkg_relations(), and default to 'semver', i.e. the current state of affairs. If that's changed to 'debian', relations declared in project.conf as

pkg.requires.os.devel = jw-pkg-devel > 1.2.3

will be output as

jw-pkg-devel >> 1.2.3

which is what Debian expects.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 10:43:08 +01:00
509fe1c5e0 cmds.projects.BaseCmdPkgRelations: --expand-semver-revision-range

Add support for --expand-semver-revision-range to cmds.projects.BaseCmdPkgRelations.pkg_relations(). The options turns a dependency in projects.conf

pkg.requires.os.devel = jw-pkg-devel = VERSION

into

jw-pkg-devel >= 1.2.3, jw-pkg-devel < 1.2.4

Note that this will break as soon as a real range is specified in projects.conf. To be fixed later, this commit is at least less breakage than before.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 10:42:51 +01:00
5bd1f3378b App.get_value(): Beautify logging

"getting value xxx for project" is a prominent debug log message but ugly. Beautify to e.g.:

Lookup jw-fail2ban -> jw-pkg / version

Meaning project "jw-fail2ban" looks up the value for key "version" in project "jw-pkg".

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 10:42:35 +01:00
b2847809c1 lib.Types: Make debug logging optional

lib.Types class detection is too chatty. Make that a ctor option.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-15 10:42:24 +01:00
2e1115e722 cmds.projects.CmdRequiredOsPkg: Select devel with release

The output of

jw.pkg.py projects required-os-pkg --flavours release

should include all packages required by flavour devel, because during the release process, -devel and -run packages are both installed, and installing the -devel package is only possible if its dependencies are installed.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-14 12:09:42 +01:00
72bd5e3555 lib.Local.run(): Be less dramatic about exit != 0

Don't mention "error" in log message for exit codes > 0 from spawned processes, because sometimes they don't mean an error.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 20:03:01 +01:00
281e6e2440 cmds.secrets.CompileTemplates: Log missing secrets

Log the number of secrets missing to fully compile all templates.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 20:01:26 +01:00
6df4c86fc5 lib.App: Add property cmdline

Add the property App.cmdline, containing the invoking command line as a string.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 20:00:30 +01:00
f3c4a51b85 cmds.secrets.CmdCompileTemplates: Support -ogm

Add support for the -o (--owner) -g (--group) -m (--mode) options. They allow to specify a default for compiling templates, but _don't_ override what's in the #conf: specification line in .jw-tmpl or .jw-secret files.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 19:58:28 +01:00
1ffac7b365 cmds.secrets.CmdXX: Add option --all

Support option --all to jw-pkg.py secrets list-compilation-output and list-secrets (CmdListCompilationOutput & CmdSecrets). This allows them to also report non-existent files.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 19:58:02 +01:00
699980c32e cmds.secrets.Cmd: Fix errors on Kali 2026.1

Fix errors dug up by testing on Kali Linux 2026.1:

- Nested class Cmds.Attrs is constructed without scope

- "replace" falls back to empty list, not empty dictionary

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-03-09 19:57:56 +01:00