Commit graph

5,085 commits

Author SHA1 Message Date
916f56b8d9
Release 1.0.0-259@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m43s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m1s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-09-05 23:11:56 +00:00
fe2219591f
Start version: 1.0.0-259
Signed-off-by: janware DevOps <devops@janware.com>
2026-09-05 23:10:51 +00:00
994c264689
lib.ec.ssh.AsyncSSH: Actually hide password
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m18s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m20s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 3m50s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m20s
CI / Packaging test (push) Successful in 0s
_connect_kwargs(hide_secrets = True) is used to log the connection
parameters when a connection fails, without leaking the password. The
filtered dictionary is built before the password is replaced with
'<hidden>', and the replacement is applied to the local kwargs
dictionary afterwards, after the filtered copy has already been made.
The dictionary that ends up in the log therefore still contains the
real password.

Hide the password before building the filtered dictionary.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 20:53:33 +02:00
f6372d321a
lib.ExecContext: Fix error message in _put()
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m20s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m18s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 3m54s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m4s
CI / Packaging test (push) Successful in 0s
_put() catches a failure of the remote command sequence and logs
"Failed to get <path> from <root>", a phrase copied over from the
get() path. This path, however, pushes content, so the message
describes the wrong operation.

Log a put-oriented message instead.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:36:46 +02:00
5230b44fa8
lib.FileContext: Interpolate path in _is_dir()
_is_dir() logs a DEBUG message when _stat() is not implemented and it
must guess from the trailing slash whether a path is a directory. The
second part of that message is a plain string rather than an f-string,
so '{path}' is logged verbatim instead of the path.

Make it an f-string so the path is interpolated.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:36:46 +02:00
e07c52eda8
lib.ExecContext: Clean up temp file in _put()
_put() writes the content into a temporary file with tee and, when
atomic is set, moves it to the target path with a final mv. The loop
over the command list sets tmp_file to None after each command, on the
assumption stated in the comment that the file has been moved at that
point - which is only true for the last command. When a chown, chmod,
or mv after the tee fails, the finally block finds tmp_file is None
and leaves the temporary file behind.

Reset tmp_file only after all commands have completed, so that the
finally block erases the temporary file whenever a step fails.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:36:46 +02:00
e6295f7d2b
lib.ec.ssh.Paramiko: Avoid recv deadlock
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m13s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m13s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 3m56s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m1s
CI / Packaging test (push) Successful in 0s
_run_ssh() waits for the remote process's exit status before reading
stdout and stderr. When a command produces more output than the
channel's flow-control window can hold, the server stops sending, the
remote process blocks on its write and never exits, and
recv_exit_status() blocks forever.

Drain stdout and stderr to EOF first - the channels close when the
process exits, so reading them also implies completion - and only
then query the exit status.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:00:58 +02:00
4ca370a877
lib.ec.ssh.Paramiko: Close remote stdin
_run_ssh() writes cmd_input to the remote stdin channel but never
closes the write side. The channel stays open until the client
process exits, so a remote command that reads stdin (cat, a login
shell, ...) never sees EOF and blocks forever - including for
cmd_input = None, which is supposed to mean non-interactive with
stdin from /dev/null.

Call shutdown_write() on the channel after writing the input, or
immediately when there is none, so the remote command gets EOF.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:00:58 +02:00
4b55b460ea
lib.ec.ssh.Paramiko: Pass port and password
__client() connects using only the URI's hostname and username. The
URI's port is ignored, so ssh://host:2222/... ends up connecting to
port 22, and a password carried in the URI is never passed to
paramiko, so URI-based password authentication cannot work. The Exec
and AsyncSSH clients both honor the port and the password.

Pass the port and the password to connect(). The port argument is
omitted entirely when the URI carries no port, because
getaddrinfo() would interpret a None port as service port 0; without
the argument, paramiko falls back to its default of 22.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 19:00:58 +02:00
e8ade91f21
lib.Uri: Fix path join for empty authority
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m36s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m29s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m8s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m15s
CI / Packaging test (push) Successful in 0s
__new_with_path() joins base and path with exactly one '/', assuming a
trailing '/' in base is a path separator. That assumption breaks for
URIs with empty authority, where scheme_plus_authority ends in '://'
(e.g. 'file:///tmp/x'): new_replace_path('/etc/hosts') drops the
leading slash of the path and produces 'file://etc/hosts', which
parses with hostname 'etc' and path '/hosts' instead of path
'/etc/hosts'.

Treat a base ending in '://' separately and keep a leading slash in
the path, adding one if it is missing.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 14:01:46 +02:00
a1ae17c8cb
lib.Uri: Fix __assemble(): No '://' for relative paths
Assembling a scheme-bearing form always writes '://' after the scheme, even
if the input has no authority part. For a schemeless relative path such as
'../../local/path', .full then becomes 'file://../../local/path', which
re-parses with '..' as the host and '/../local/path' as the path, both
entirely broken.

Fix __assemble() to write a bare ':' instead of '://' when the input has no
host and a relative path, so the assembled form re-parses back to the same
relative path: full of '../../local/path' is now 'file:../../local/path'.
Absolute paths and forms with an authority are unchanged.

The assembled 'file:../../local/path' form is a valid URI under the RFC
3986 generic grammar (scheme with a rootless path), while RFC 8089's file
URI ABNF admits only an empty or an absolute path. Uri thus trades RFC 8089
conformance for RFC 3986 compatibility: a relative path survives the
full-and-reparse cycle unchanged.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 14:01:46 +02:00
23d3c596f6
py-mod.mk: Add TypeAlias to PY_SED_EXTRACT_EXPORT_DEF
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m34s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m41s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m0s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m23s
CI / Packaging test (push) Successful in 0s
Automatically include symbols of the form

  MySymbol: TypeAlias = Something  # export

in generated __init__.py.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 13:39:51 +02:00
29abf14a67
py-ns-dir.mk: Support __init__.py generation
For in-tree tests, the jw package is split across several projects, and
each directory containing a py-ns-dir.mk contributes its subtree. So far,
it's up to that directory how the contribution is handled. Usually an
__init__.py with pkgutil.extend_path() is present in version control to
glue these parts together. This commit makes py-ns-dir.mk handle the
contribution method centrally by default unless PY_UPDATE_INIT_PY is set to
false.

As of this commit, this is the case by default, i.e. PY_UPDATE_INIT_PY is
set to false in py-ns-dir.mk, maintaining the current behaviour.

Downstream projects or modules can decide to have __init__.py centrally
maintained. For this, they need to remove __init__.py from version control
and set PY_UPDATE_INIT_PY to true in the respective Makefile.

PY_UPDATE_INIT_PY = false is also set explicitly in src/python/jw/Makefile,
because that file should never be generated. Bootstrapping the entire
workspace hinges on it.

Pending better testing, most notably of consistent in-tree testing
functionality, PY_UPDATE_INIT_PY = true might become a global default in
the future.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 13:39:51 +02:00
f3f0ef10ca
pgit.sh: Allow running under any projects-dir.mk
There's a random string "some-random-string-to-id-this-makefile" in
projects-dir-minimial.mk intended to check if the Makefile in the
projects-directory is actually the final link target. It's re-used in
pgit.sh to find the projects toplevel directory, but since it doesn't need
to be an actual projects-dir-minimal.mk copy, another case should also be
taken into consideration: Check if projects-dir.mk is included.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 13:39:51 +02:00
9636f1064d
projects-dir.mk: Renovate build-order targets
Renovate the build-order and build-order-% targets as follows:

- Add BUILD_ORDER_DEP_FLAVOURS and default to all currently supported
  build flavours: run,build,test,release. This adds convenience for
  packages declaring differentiated types of dependencies, not only the
  default build dependency, but still want any project it uses somehow
  be be built before them.

- Don't print the recipe's command when running "make build-order", it
  gets in the way if the caller wants to parse the result, so spare him
  filtering it.

- Only log messages with priority warning or higher. Messages go to stderr
  anyway to keep them from throwing wrenches into consuming parsers, but
  they are likely confusingly visible without context if they leak to the
  calling program's console.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 13:39:51 +02:00
51c42f7dda
defs-dirs.mk: Filter out non-existing ordered subdirs
SUBDIRS combines $(ORDERED_SUBDIRS) with the subdirectories found on disk.
An $(ORDERED_SUBDIRS) entry that does not exist in the tree is still passed
to the recursive make loop, which then errors out over the missing
directory.

Filter the ordered subdirectories through the wildcard of their Makefile
paths, keeping only the ones that are actually there.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-09-05 13:39:51 +02:00
dc43171368
Release 1.0.0-258@suse-tumbleweed/x86_64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m50s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m15s
CI / Packaging test (push) Successful in 0s
2026-08-25 23:30:53 +00:00
d6341c5577
Release 1.0.0-258@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m50s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m10s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-25 23:11:33 +00:00
e468b01598
Start version: 1.0.0-258
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-25 23:10:29 +00:00
c20bc72efb
defs.mk: Add quadlet unit types to LOCAL_SYSTEMD
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m28s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m20s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 3m49s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m1s
CI / Packaging test (push) Successful in 0s
LOCAL_SYSTEMD in defs.mk only collects the classic unit types, not
quadlet types - .container, .pod, .network, .volume, .image, .kube.
They're all systemd units that are installed under the system's systemd
directory and read by the podman-systemd-generator at boot.

Add all six quadlet extensions to the wildcard.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-25 18:44:30 +02:00
35033f08ca
Release 1.0.0-257@suse-tumbleweed/x86_64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 4m9s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m4s
CI / Packaging test (push) Successful in 0s
2026-08-24 19:25:16 +00:00
300bcfcc4b
Release 1.0.0-257@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 4m45s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m59s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-24 18:57:08 +00:00
a64e84954f
Start version: 1.0.0-257
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-24 18:56:05 +00:00
e6e96d64c2
py-defs.mk, py-mod.mk: Add PY_LOCAL_PY(C)
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m17s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m22s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m11s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m57s
CI / Packaging test (push) Successful in 0s
The meaning of the many PY_XXX variables isn't easy to tell apart, so
start renaming some of them.

PY_ALL_PY and PY_PYC consistently are now named PY_LOCAL|INSTALL_PY and
PY_LOCAL|INSTALL_PYC, depending on whether or not they are intended to
be installed.

The old PY_INSTALL_PY was renamed to PY_DO_INSTALLL_PY, a move that
would be better applied to all boolean variables. I won't try with this
commit, however, because a lot of downstream packages depend on
PY_UPDATE_INIT_PY and PY_INSTALL_INIT_PY, i.e. without "_DO".

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-24 18:14:16 +02:00
5a3355b936
py-rules.mk: py.clean: Honour PY_UPDATE_INIT_PY
py.clean unconditionally runs scm.sh clean -f __init__.py, which
deletes untracked __init__.py files in the source directory. In a
package with PY_UPDATE_INIT_PY=false the build never generates
__init__.py, as the generation rule in py-mod.mk is gated on the
same variable.

The __init__.py files in question are hand-written sources which are
always tracked in SCM, and for those, "scm.sh clean" becomes a no-op, so
with the current code, no real problem occurs, but explicitly protecting
__init__.py from deletion if PY_UPDATE_INIT_PY is false is certainly
more obvious.

Move the __init__.py cleanup inside the PY_UPDATE_INIT_PY=true
conditional so that py.clean only runs removal commands on files the
build actually generates.

Signed-off-by: Jan Lindemann <jan@janware.com>
Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
2026-08-24 18:14:16 +02:00
0407215530
init.detect_modules(): Add base_types filter
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m10s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m29s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m5s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m3s
CI / Packaging test (push) Successful in 0s
Add a parameter "base_types" to detect_modules(), defaulting to None. If
it is not None, a module is only exported if its same-named object
inherits from one of the given types. Modules without a same-named class
are skipped instead of raising AttributeError, which covers helper
modules.

The return annotation becomes Sequence[str] instead of list[str] to
remove mutability for easier type checking.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-24 14:23:27 +02:00
1385b1a4ba lib.ExecApp: Add class
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 11m50s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m14s
CI / Packaging test (push) Successful in 0s
ExecApp is a ready-made base class for applications that operate
through an ExecContext: it adds the --interactive, --verbose and
--target options, exposes interactive, verbose and exec_context
properties, and closes the exec context when the async context
manager exits.

The code for that has lived in jw.pkg.App code before, which now
inherits from ExecApp.

A fix along the way: __aexit__() closes the exec context and then chains
to super().__aexit__(), so App.close() runs when the async context
manager exits. Before the change, exiting the async context left the app
unclosed; close() ran only on the run() path. Add a unit test that
builds an ExecApp with a root command and asserts that close() runs on
context exit and that the exec options are registered.

The exec options are now registered before App's own options,
which moves them up in the rendered --help output. Update the
golden file of the help integration test to match.

Signed-off-by: Jan Lindemann <jan@janware.com>
Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
2026-08-22 17:31:57 +02:00
eec9325b46
App.__format_topdir(): Mention relative in error
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 8m15s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m19s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 8m23s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m7s
CI / Packaging test (push) Successful in 0s
__format_topdir() accepts "absolute", "relative", "unaltered", and
"make:<variable-name>", but the error message it raises for anything
else only lists "unaltered", "absolute", and
"make:<variable-name>", leaving out "relative".

Add "relative" to the list of valid formats in the error message.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-22 16:25:28 +02:00
a89eb18d52 App.__find_circular_deps(): Fix cycle path
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 8m56s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m56s
CI / Packaging test (push) Successful in 0s
__find_circular_deps_recursive() builds the cycle path by inserting
each visited dependency at the front of the list on the way back up,
and __find_circular_deps() appends the project where the cycle was
detected at the end. The path therefore starts at the first child of
the DFS root instead of at the project that closes the cycle, and the
closing project appears twice: for a dependency cycle between projects
A and B, find_circular_deps() reports "B -> A -> A" instead of
"A -> B -> A".

Keep the DFS stack as an ordered list, and when a dependency is already
on the stack, return the part of the stack from that dependency to the
top, plus the dependency itself, which starts and ends at the same
project. Reverse the result before returning, because an edge
a -> b in the flipped graph means that b depends on a, so the cycle
is reported in the original dependency direction.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-22 16:11:34 +02:00
c95a3350ba App.__read_dep_graph(): Remove redundant loop
__read_dep_graph() iterates over the given sections (flavours), but
the loop body does not use the loop variable: it passes the entire
sections list to get_project_refs() on every iteration, so the same
lookup is repeated for each section, and the recursion into the
found dependencies is re-triggered (and skipped) for each of them.

Remove the loop and do the lookup once per project.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-22 16:11:34 +02:00
bd78657588
py-defs.mk: Honor PY_INSTALL_INIT_PY = false
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 8m8s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m12s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 7m43s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m58s
CI / Packaging test (push) Successful in 0s
PY_ALL_PY is derived from PY_SRC_PY, which wildcards *.py in the module
directory, so a checked-in __init__.py enters the install list
unconditionally. PY_INSTALL_INIT_PY only gates the later append of
__init__.py, which covers files generated at build time. Setting it to
false thus had no effect on an existing __init__.py, which was still
installed together with its .pyc.

Filter __init__.py out of PY_ALL_PY. When PY_INSTALL_INIT_PY is true,
the existing append adds it back, so generated and checked-in
__init__.py files are installed as before. When it is false, an
existing __init__.py is now excluded from PY_ALL_PY and therefore also
from PY_INSTALLED_PY and PY_PYC.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-22 10:07:55 +02:00
419b3f59f4
Release 1.0.0-256@suse-tumbleweed/x86_64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 4m12s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m2s
CI / Packaging test (push) Successful in 0s
2026-08-20 23:34:24 +00:00
5f4be63b3b
Release 1.0.0-256@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m39s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m48s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-20 23:12:16 +00:00
8ea2538c4e
Start version: 1.0.0-256
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-20 23:11:14 +00:00
9a953d0017
lib.Uri: Fix stale cache in __new_with_path()
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m4s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m6s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m13s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m53s
CI / Packaging test (push) Successful in 0s
__new_with_path() builds the new Uri by deep-copying self and then
replacing __string. A deep copy, however, also carries over any
cached_property values that were already computed on self (e.g. __p,
path, scheme, full), so once __string changes, they stay stale: for a
Uri on which any of those properties had been accessed before,
new_add_path() and new_replace_path() returned objects whose
to_string() showed the new string while path(), hostname(), full() and
friends still described the old one.

Build a fresh instance with object.__new__() and initialize its three
basic attributes instead of copying, so no computed cached state can
be inherited.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M and pi.dev
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-20 07:50:19 +02:00
4ea3c91e94
doc: Add subdir
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m4s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m8s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m5s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m8s
CI / Packaging test (push) Successful in 0s
Add $(TOPDIR)/doc with some subdirectories:

  - jw-pkg: Using the jw-pkg application, mirroring the subcommand
    structure in its subdirectories. At this point only packages,
    projects and secrets are added, all empty To be expanded as needed
  - python: jw-pkg Python API

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-19 20:51:11 +02:00
2b67d43279
defs.mk: Add *.md to the DOC file types
Add  *.md to DOC, the list of file extensions recognized and packaged by
default.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-19 20:51:11 +02:00
1d77122974
doc-rules.mk: Add empty target test
Add empty target "test" to keep the build from erroring out over make
test in documentation directories.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-19 20:51:11 +02:00
d359696c5f
Release 1.0.0-255@suse-tumbleweed/x86_64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m40s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m53s
CI / Packaging test (push) Successful in 0s
2026-08-18 23:32:24 +00:00
3cafef7a1e
Release 1.0.0-255@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m39s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m54s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-18 23:11:40 +00:00
7fa7cdcf2c
Start version: 1.0.0-255
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-18 23:10:38 +00:00
6da6ac2505
conf / scripts: Fix unprivileged "make install"
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 3m33s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 3m46s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 3m36s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m46s
CI / Packaging test (push) Successful in 0s
Unprivileged "make install" currently returns an error. That looks bad.

It actually is not a real problem, because unprivileged "make install"
installs files into locations that are good for nothing, in the sense
that they don't constitute a meaningful or usable hierarchy of installed
files. Debugging / inspecting the install machinery itself is a valid
use case, though, and it's easier to explain to the unsuspecting user
that unprivileged installation is something akin to shooting range
training than why "make install" outright errors out, so fix the error
messages.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 17:27:32 +02:00
db14ef54ff
pkg.sh: log-install: Don't log by default
cmd_log_install() defaults $c_logfile to "install.log", so every
invocation without -l appends to an install.log in the current
directory. That's semantical nonsense.

Default $c_logfile to empty instead. The existing empty-$c_logfile
early exit already skips logging, so no -l now means no log, and
callers that want a log pass -l explicitly, as the packaging flow
does through INSTALL_LOG.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M (pi)
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 17:27:32 +02:00
214e4b995e
defs.mk: Don't use plain /usr/bin/install
In a development tree, $(INSTALL) falls back to plain install(1), which
refuses dead symlinks, so unprivileged "make install" fails outright.
Packaging is unaffected, because the spec exports INSTALL_LOG, which
already routes $(INSTALL) through "pkg.sh log-install"

Route the remaining $(INSTALL) defaults through $(LOG_INSTALL_SH) as
well to address the failures: $(SUDO) jw.pkg log-install -p for
DEVELOPMENT=false, plain jw.pkg log-install otherwise. Move -f $(PKG_FORMAT)
out of $(LOG_INSTALL_SH) and into the two logging call sites, so the
no-log paths don't carry a package format they never use.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 17:27:32 +02:00
c42f5f98ab
test-guard.mk: Add file
All checks were successful
CI / Packaging - Kali Linux (pull_request) Successful in 4m3s
CI / Packaging - OpenSUSE Tumbleweed (pull_request) Successful in 4m2s
CI / Packaging test (pull_request) Successful in 0s
CI / Packaging - Kali Linux (push) Successful in 4m16s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m51s
CI / Packaging test (push) Successful in 0s
Running "make clean" in some directories can break "make test" in
others. That notably happens by running clean somewhere in or below
$(TOPDIR)/src/python, which removes __init__.py files potentially needed
by unit and integration tests in $(TOPDIR)/test.

This commit makes the failure easily visible by introducing the notion
of "testabiltity" - if the tree is not testable, "make test" logs
exactly that in a clear error message and gives up.

Makefiles which need to check "testability" can use it as a
prerequisite. This commit does that with py-run.mk and jw-py-test.mk.

Testability is asserted by running "make", "make all" or "make test"
from $(TOPDIR). A successful toplevel build coincides with testability,
and leaves a $(TOPDIR)/dirs-all.done behind, which is why that file is
the perfect testability marker.

It is automatically created by a toplevel build and cleared by using the
"invalidate-testability" prerequisite. This commit makes target "clean"
depend on it in py-mod.mk. To be extended to other use cases / makefile
snippets as needed.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 14:05:30 +02:00
c471388fba App: Remove ResultCache
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 6m16s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m0s
CI / Packaging test (push) Successful in 0s
App.ResultCache is a horrible piece of software, now superseded by
functools.cache, with no measurable performance benefit as of now.
Remove it.

Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 13:02:04 +02:00
d4fa311771
Release 1.0.0-254@suse-tumbleweed/x86_64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 5m0s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 4m45s
CI / Packaging test (push) Successful in 0s
2026-08-17 23:33:40 +00:00
e3cff8104f lib.App: Find invoked path by walking argv
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 3m36s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m47s
CI / Packaging test (push) Successful in 0s
The discovery re-parse re-parses the full command line at each level
against the subparsers registered so far. A level's subcommand parsers
are not registered until the re-parse descends into them, so the tokens
after the subcommand name are parsed against the current level's
options. An option meant for a deeper level can then collide with a
same-named option of a shallower level, or fail on a missing value.

Replace the re-parse with a single argv walk. The subcommand names at
each level are known once the commands are materialized, so the invoked
path is found by matching tokens against those names and skipping the
options (and the values they consume) that precede them. Walking the
tokens never parses the tail against a half-built parser, so the
collision cannot occur.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 01:14:40 +02:00
9f7e1b6cb2 lib.Cmd: Build the command tree lazily
App() construction builds the entire command tree: every
load_subcommands() call in a command's __init__() constructs its whole
subtree eagerly, so running a single leaf command pays to instantiate
every unrelated command object as well (jw-pkg builds about 50 command
objects for any invocation).

Defer the construction instead. load_subcommands() now records only the
module search path and name filter, and the subcommands are materialized
on first access to the children or child_classes property. The parser
reads children only down the invoked branch on the non-help path, so a
simple run instantiates just that path (jw-pkg builds 5-7 objects),
while the help and completion path expands every node and leaves
rendered help unchanged.

Assisted-by: unsloth/Qwen3.8-27B-GGUF:Q4_K_M with pi.dev v0.84.2
Signed-off-by: Jan Lindemann <jan@janware.com>
2026-08-18 01:14:40 +02:00
3ae8738b36
Release 1.0.0-254@kali-rolling/amd64
All checks were successful
CI / Packaging - Kali Linux (push) Successful in 4m31s
CI / Packaging - OpenSUSE Tumbleweed (push) Successful in 3m49s
CI / Packaging test (push) Successful in 0s
Signed-off-by: janware DevOps <devops@janware.com>
2026-08-17 23:12:48 +00:00