Annotate the one existing bare "# type: ignore" in AsyncRunner.py
with arg-type and var-annotated codes to prepare for
mypy.ignore-without-code.
Signed-off-by: Jan Lindemann <jan@janware.com>
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev 0.81.1
This commit adds @override decorators to approximately 300 methods
across 76 files that inherit from base classes such as AbstractCmd,
FileContext, ExecContext, Distro, SSHClient, and others.
The decorator ensures the type checker can verify that overridden
methods have compatible signatures and prevents accidental shadowing
of inherited methods without intent.
Files modified include command classes, library modules, distro
implementations, and SSH client implementations.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v
Signed-off-by: Jan Lindemann <jan@janware.com>
CmdBuild supports the targets all, clean and pkg-*. It uses builtin
dependency flavours matching those targets to resolve build order. To
make it more flexible in general, and allow it to support more
targets, e.g. "check" and "test", this commit adds a --dep-flavours
option.
Signed-off-by: Jan Lindemann <jan@janware.com>
A couple of "# export" markers after function prototypes have been
pushed along with the closing parenthesis onto the wrong line by the
code formatter, fix that.
Signed-off-by: Jan Lindemann <jan@janware.com>
local/src is a janware-specific path, remove it from App and defs.mk.
It's still in pkg.sh as a safety measure. Will have to go, too, but
is kept in for now until further audit.
Signed-off-by: Jan Lindemann <jan@janware.com>
Tolera$te missing paramiko imports. jw-pkg is designed to
work with what it finds and use plain /usr/bin/ssh if need
be.
Signed-off-by: Jan Lindemann <jan@janware.com>
CmdPythonpath, AKA pythonpath, prints a PYTHONPATH which runs from
root first to dependency leaves last. This works now but doesn't
give local overrides the advantage it should. Fix that.
Signed-off-by: Jan Lindemann <jan@janware.com>
pyrightconfig.json as generated by CmdCreateFile, doesn't contain the
repository's own Python source location in "extra_paths", fix that.
Signed-off-by: Jan Lindemann <jan@janware.com>
An Uri instance constructed from "~/some/path" returns
"file://~/some/path" as the .full property, i.e. with the "~" in the
authority part. That's not accurate and doesn't make the intended
sense in an URL context, see RFC 8089:
Common UNIX shells such as the Bourne-Again SHell (bash) and Z
SHell (zsh) provide a function known as "tilde expansion"
[Bash-Tilde] or "filename expansion" [Zsh-Tilde], where a path
that begins with a tilde character "~" can be expanded out to a
special directory name. No such facility exists using the file
URI scheme; a tilde in a file URI is always just a tilde.
The "fix" introduced by this commit makes .full return the path
without file:// prepended. That's conservative. It could also chose
to expand the tilde, which is arguably cleaner. To be introduced by a
later change after this commit has seen more coverage.
Signed-off-by: Jan Lindemann <jan@janware.com>
In non-interactive mode, "zypper dup" runs with the options
--force-resolution and --auto-agree-with-licenses duplicated, fix
that.
Signed-off-by: Jan Lindemann <jan@janware.com>
Two error messages in Distro.__init__() contain the typo 'instaniate'
instead of 'instantiate'. Fix the spelling.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v0.80.2
Signed-off-by: Jan Lindemann <jan@janware.com>
Remove unused helper functions from install():
_matches_host_prefix(), _crop_host_prefix(), _crop_default_prefix(),
and _matches_default_prefix().
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v0.80.2
Signed-off-by: Jan Lindemann <jan@janware.com>
log() uses a regular string instead of an f-string, fix that.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v0.80.2
Signed-off-by: Jan Lindemann <jan@janware.com>
The scheme property returns 'file://' for local paths instead of
'file', causing scheme_plus_authority to produce 'file://://' with a
triple slash.
Also fix the test to expect 'file' instead of 'file://'.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v0.80.2
Signed-off-by: Jan Lindemann <jan@janware.com>
The CopyContext._run() calls await self._run(), which would create
infinite recursion raising RecursionError at runtime. Make it a pure
virtual.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL with pi.dev v0.80.2
Signed-off-by: Jan Lindemann <jan@janware.com>
get_values() splits comma-separated values and strips whitespace but
does not filter out empty strings. A value like "a, b, " produces
['a', 'b', ''], with an empty string at the end. This empty string
propagates to callers like CmdRequiredOsPkg.py and pollutes output.
Add a filter for non-empty stripped values.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
The refactored __get_project_conf() raises FileNotFoundError when
project.conf does not exist, whereas the original read_value()
returns None. This causes get_value() to crash for projects with
missing or incomplete project.conf files.
Catch FileNotFoundError in __get_project_conf() and return None to
restore original behavior. Remove redundant @cache from
__read_project_conf() since __get_project_conf() already provides
caching.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
The __find_circular_deps() and find_circular_deps() methods now
return list[str] instead of bool. On a cycle, the path builds up the
dependency chain in __find_circular_deps_recursive(), and
__find_circular_deps() appends the closing project to complete the
cycle. An empty list means no cycle found.
CmdDep prints the cycle as 'a -> b -> c -> a' instead of a generic
message.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
The fd.close() call inside the `with open(...) as fd:` block is
redundant because the file handle is managed by the context manager.
Remove it.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
Replace the hardcoded /opt in __proj_dir() with a configurable ___opt_root
member, consistent with how __projs_root is configured.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
Mutable default arguments (list) cause unexpected shared state
between calls. Use None as default and initialise to [] inside the
function body.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
get_project_refs() always returns a list[str], never None. The `if
deps is None: continue` check in __read_dep_graph() is dead code,
remove it.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
The __flip_dep_graph(graph) call sits inside the while loop and
performs redundant graph flipping on every iteration. Hoist it
outside to compute once and reuse the result.
Assisted-by: unsloth/Qwen3.6-35B-A3B-GGUF:IQ4_NL and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
Remove App.get_section() which parses raw file sections by scanning for
section headers and accumulating lines. This method is no longer needed
since ProjectConf now handles all config file parsing.
Signed-off-by: Jan Lindemann <jan@janware.com>
Refactor App to use the new ProjectConf module for parsing
make/project.conf files.
This commit
- removes the inline ad-hoc read_value() method and its nested
helper functions, replacing them with ProjectConf's
get_str_or_none() API.
- introduces two cached helper methods (__read_project_conf() and
__get_project_conf()) to read and cache ProjectConf instances per
project.
- updates get_value() to delegate to ProjectConf and moves the
proj_dir lookup to only run in the version branch where it is
needed.
- updates the topdir init to use __read_project_conf() with a
FileNotFoundError catch for optional config files.
Signed-off-by: Jan Lindemann <jan@janware.com>
Introduce ProjectConf module to cleanly parse make/project.conf ini-like
configuration files. The new class supports:
- ini-style sections with header comments
- Key-value pairs with backslash line continuation
- Quoted values preserving spaces and comment delimiters (#) inside
- Inline comments outside of quotes
- Comma-separated list values with quoted commas
- Cached section parsing to avoid re-parsing the same section
- .get_section() to return an entire section unparsed
Signed-off-by: Jan Lindemann <jan@janware.com>
Replace the @functools.lru_cache(maxsize=None) decorator with
@functools.cache throughout App. functools.cache is a shorthand for
functools.lru_cache(maxsize=None) introduced in Python 3.9 and is
more concise and readable with identical behaviour.
Signed-off-by: Jan Lindemann <jan@janware.com>
Relabel the toplevel command CmdPkg from "pkg" to "packages", because
it rolls off the tounge much more nicely. Keep "pkg" as an alias for
compatibilty.
Signed-off-by: Jan Lindemann <jan@janware.com>
Add a property aliases to AbstractCmd in prepeparation for commands
to bear multiple names / abbreviations / aliases.
The App.add_cmds_to_parser() function uses parse_known_args() to
determine which subcommand was invoked, then conditionally registers
nested subcommands. The lookup dictionary (scs) contains only
canonical names, not aliases, so add them too, otherwise using the
alias instead of the canonical name causes the lookup to fail and
nested subcommands to never be registered.
Fix: Register each alias in scs pointing to the same SubCommand
object, and deduplicate with id(sc) when iterating in all=True mode
to avoid infinite recursion on help output.
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit adds more tweaks to shell command output in order to make
it nicer. The biggest patch is in Result.__summarize(), which makes
it more versatile, and allows removal of some code in SSHClient.
App sees some independent, minor result format beautification.
Signed-off-by: Jan Lindemann <jan@janware.com>
Support a --delimiter option to the ldlibpath and exepath commands.
Notable use case are the JW_PKG_XXX_PATH variables, which should use
spaces instead of colons.
TODO: Merging those two command modules with BaseCmdPkgRelations
would have made introducing this redundancy unnecessary, check if
that's a possibility.
Signed-off-by: Jan Lindemann <jan@janware.com>
Some options to the pkg-xxx commands, like flavour, --subsections and
--ignore understand a comma as delimiter if multiple option values
are specified. The comma character is not very friendly to use in
$(call ...) macros, though, so support spaces and pipe characters as
well.
Signed-off-by: Jan Lindemann <jan@janware.com>
The global --topdir-format make:XXX option to jw-pkg is half-baked at
best, and __find_dir() ignores it entirely. Make __find_dir() return
some Makefile-syntax-formatted output if the option is present. Not
used anywhere, currently, and, hence, badly tested, but still better
than the situation before.
Signed-off-by: Jan Lindemann <jan@janware.com>
Overriding the _run() method entirely in App subclasses is currently
only possible if the application supports a subcommand structure.
Make it possible to use it as an abstraction for a single-command
application.
Signed-off-by: Jan Lindemann <jan@janware.com>
"jw-pkg secrets compile-templates" errors out over checking the
contents of inexistent secrets, fix that.
Signed-off-by: Jan Lindemann <jan@janware.com>
The .strip property of class Result defaults to True, and its name
isn't very clear. Rename it to .strip_output, and default it to False
to avoid surprising contents for unsuspecting callers.
Signed-off-by: Jan Lindemann <jan@janware.com>
The command line jw-pkg.py is run with is logged with level "debug",
and reconstructed with ' '.join(sys.argv). Use pretty_cmd() instead,
this adds quotes around spaces.
Signed-off-by: Jan Lindemann <jan@janware.com>
Commands executed by ExecContext and its derived classes don't
populate the "cmd" parameter of "Result"'s constructor. Fixing that
makes for nicer error messages.
Signed-off-by: Jan Lindemann <jan@janware.com>
Use the (mostly) call-compatible log implementation from jw-python.
This buys us much nicer logs with color and source-code positon
annotations.
Signed-off-by: Jan Lindemann <jan@janware.com>
In LoadTypes' constructor, allow the type_filter parameter to be of
type Sequence[type[Any]] instead of list[type[T]]. a) Sequence is
more generic than list, and b) with T instead of Any, trying to
instantiate with an abstract class has mypy complain:
# E: Only concrete class can be given where "type[MyClass]" is
expected [type-abstract]
- type_filter: list[type[T]] = [],
+ type_filter: Sequence[type[Any]] | None = None,
Not that this makes mypy complain that it needs an annotation at the
places where LoadTypes is used.
Signed-off-by: Jan Lindemann <jan@janware.com>
OpenSUSE leaves installing local packages to the default
implementation in lib.Distro._install_local_files(), which passes the
package path to the package manager, i.e. zypper in OpenSUSE's case.
That has advantages, namely automatic installation of dependencies,
but also disadvantages, namely the attempt to install dependencies
even if the package manager is disfunctional, possibly because an
installed package containing installation sources is broken.
That could lead to a deadlock when trying to install a fixed package.
I see two ways out: Support an additional flag to jw-pkg's install
command which selects whether or not dependencies shall be resolved
along, or just use rpm directly for all local install attempts.
The latter is the less fancy way to handle this, so as a first step
make it the default by overriding suse.Distro._install_local_files().
Signed-off-by: Jan Lindemann <jan@janware.com>
Define default parameter values for Result's constructor, namely None
for exit status, stdout and stderr.
Instantiating a Result object without parameters signifies "this
object doesn't contain data from a real process's exit event". Up to
now, similar meaning has been hand-crafted by ExecContext's run() and
friends by using an error exit status (1) to make sure it wasn't
mistaken for success. This commit formalizes that into the Result
structure itself, but uses None instead for the exit status.
Controlling default values in Result itself also means that the
Result class gets better awareness of what it contains, and its log
messages and stdin / stdout can be more fitting:
- If a real process failed, make stdout return at least b''
- If a real process succeeded, make stdout return at least b''
Returning something from .stdout on success fixes a real bug: An
attempt to access what "rpm -U somepackage.rpm" returns, namely
nothing, raises a bogus exception, because stdout is None.
Signed-off-by: Jan Lindemann <jan@janware.com>
Make the log delimiter look more consistent: Whether a CallContext
was constructed with a title parameter or without, prefix its
.log_delimiter property with a "----".
Signed-off-by: Jan Lindemann <jan@janware.com>