Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m

## Unreleased

- fix(app-restart): mask the value of any `Name=value` whose name holds password/secret/token/key (e.g. `--constant Module.XPassword=v`), not only values after secret-named flags; the printed `start:` line leaked them (TK-003) — five-app lab project (Mendix 11.15, mxcli v0.26.0)
- learn(bug-logs): **six BUG-DRAFT entries from a five-app lab project (mxcli v0.26.0, Mendix 11.15)**: `check` expression gaps (formatDecimal, Decimal toString, raw backslash in replaceAll), declare/retrieve partial checks, microflow splice and alter variable scope, create without commit, Data grid 2 write defects, describe round-trip and DateTime localization. Each gives the workaround; none is filed upstream yet, upstream issue numbers named where they exist (#580, #1489, #1111, #891). Read on demand through `bin/bug-lookup.sh`. — MendixMau, five-app lab report TK-027 to TK-036
- new(mxpatch, exec): **`project-bin/mxpatch.py`, the shared patch core, and a `mxcli fix hashes` check around every `exec.sh --patch` run: the two shipped workflow patchers no longer leave a stale ContentsHash.** mxpatch finds a unit by type + name (+ module) with a byte pre-filter, finds an element by Name, gets/sets a value by key, and writes the unit and its `.mpr` ContentsHash (base64 SHA-256 of the file) in one step; dry run by default, `already applied` on a re-run, and a refusal (exit 3) when the current value is not the expected old one. `wf-set-call-captions.py` and `wf-add-path-terminators.py` now write through it, refuse drift (a caption is replaced only while it is still the stamped microflow name or the `--expect-caption` value; `--expect-paths N` for terminators), and also take the `exec.sh --patch` form (`<model.mpr> Module.Workflow`). The index is committed before the file swap, so a kill mid-write leaves a MISMATCH that `fix hashes` reports and a re-run repairs. `mxunit_bson.py` reads BSON 0x09 (UTC datetime). `exec.sh --patch` runs `mxcli fix hashes` (v0.25.0+, probed via `fix --help`) before and after the script and restores the snapshot on any issue the baseline did not have, before mxbuild, which cannot see it. Field evidence: captured mxcli v0.25.0/v0.26.0 runs on a public model (`tests/wave2/fixtures/mxpatch/CAPTURE.txt`): master's patchers leave a MISMATCH, the ported ones verify clean; the hash routine agrees with mxcli on 67 of 67 mismatched and 302 matching units of a second model. Widget-shape copying and pluggable-widget properties wait on a captured page unit. Field run ([#390](https://github.com/MendixMau/mxcli-project-toolkit/issues/390), mxcli v0.26.0, macOS): PASS, and the patch repaired a ContentsHash MISMATCH an old-style `--patch` had left on the unit it rewrote. Fixes from it: the backup of a rewritten unit is kept outside `mprcontents/` (in `$MXPATCH_BACKUP_DIR`, which `exec.sh --patch` points into the snapshot it just took, else a 0700 `<tempdir>/mxpatch-bak-<uid>/`; refused when it resolves inside `mprcontents/`, through a symlink or another case spelling; named with pid and counter so two writes in one second keep both), no longer as an untracked `<unit>.mxunit.bak` that `git add mprcontents` would commit; the BUILD-LOG row carries the hash bracket (`hashes: baseline 1 mismatch (pre-existing), verify clean (1 pre-existing repaired by this patch's rewrite)`); and the baseline message says that a unit the patch rewrites gets a fresh hash, and to repair the others with `fix hashes --repair`. A baseline MISMATCH that is still reported on a unit the patch rewrote counts as new (restore, exit 1), not as pre-existing. — jopterhorst ([#294](https://github.com/MendixMau/mxcli-project-toolkit/issues/294), [#295](https://github.com/MendixMau/mxcli-project-toolkit/issues/295))
- learn(pipelines/mendix-mpr/samples): **three minimal mxcli repros from the native-module bug hunt, the files the upstream draft cites** (split from [#293](https://github.com/MendixMau/mxcli-project-toolkit/pull/293), 6/6). `fsm-template-9.24.2/native-bughunt/` holds CAPTURE.txt and D1–D3, which are written from scratch in a `Repro` module: `describe entity` printing `index ( desc);`, the native Repeater `content` slot, and no flow writable to a 9.24.2 project. They are the repros for U3, U2 and U14 in `bug-logs/pending-github-issues/2026-10-12-mxcli-0.26-round-trip-report.md`. The other 472 sample files of #293 had no citing reader, because the skills and Rehost splits state their field results inline, so none of them ships. — MendixMau, Decompose/Rehost lab runs 2026-10-12
Expand Down
9 changes: 7 additions & 2 deletions project-bin/app-restart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
# Git Bash, so there --cmd is needed the first time. Every successful restart remembers the
# command in .claude/loop/app-restart/start.cmd (self-gitignored, machine-local), so the next one
# — or one after the app died — needs no --cmd. It must be an `mxcli run --local` command; this
# script starts nothing else. Values after password/secret/token/key flags are masked on screen.
# script starts nothing else. Values after password/secret/token/key flags, and of any Name=value whose Name holds one, are masked on screen.
#
# READY means: a new record with a live pid, a Mendix answer on its port (the login/index page
# carries Mendix markers — any 4xx-answering process is NOT enough, see test-stack-up.sh), and the
Expand Down Expand Up @@ -134,8 +134,13 @@ bundle_ok() { # $1 port — the script bundle index.html loads must answer 2xx
mask() { # print argv joined, with the value after any password/secret/token/key flag masked
local a out=""
for a in "$@"; do case "$a" in *" "*) a="'$a'" ;; esac; out="$out $a"; done
# Two passes (TK-003): (1) a secret-named FLAG masks its value, as `--db-password v` or
# `--db-password=v`; (2) ANY `Name=value` whose Name holds a secret word masks the value, which
# is how `--constant Module.HubApiPassword=v` (flag name `--constant`) used to print in clear.
local _w='([Pp][Aa][Ss][Ss]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Tt][Oo][Kk][Ee][Nn]|[Kk][Ee][Yy])'
printf '%s\n' "${out# }" | sed -E \
"s/(-[-A-Za-z_]*([Pp][Aa][Ss][Ss]|[Ss][Ee][Cc][Rr][Ee][Tt]|[Tt][Oo][Kk][Ee][Nn]|[Kk][Ee][Yy])[-A-Za-z_]*)(=| +)[^ ']+/\1\3****/g"
-e "s/(-[-A-Za-z_]*${_w}[-A-Za-z_]*)(=| +)[^ ']+/\1\3****/g" \
-e "s/([A-Za-z0-9_.-]*${_w}[A-Za-z0-9_.-]*)=[^ ']+/\1=****/g"
}

# Port and process helpers (mxtk_port_holder, mxtk_holder_text, mxtk_pid_in_tree) live in
Expand Down
6 changes: 6 additions & 0 deletions tests/wave2/test-app-restart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,12 @@ has "points at --cmd" "$OUT" "--cmd"
echo "-- secrets masked"
OUT="$(restart "$P" --dry-run --cmd './mxcli run --local --db-password hunter2 -p Fixture.mpr')"
hasnt "a password value never reaches the screen" "$OUT" "hunter2"
OUT="$(restart "$P" --dry-run --cmd './mxcli run --local --constant Mod.HubApiPassword=s3cretA --constant Mod.Api_Token=tokB --db-password hunter3 --db-password=hunter4 --constant Mod.Port=8080 -p Fixture.mpr')"
hasnt "--constant Name=value with a Password name is masked" "$OUT" "s3cretA"
hasnt "a Token-named constant is masked" "$OUT" "tokB"
hasnt "--db-password <value> and --db-password=<value> are masked" "$OUT" "hunter3"
hasnt "the = form is masked" "$OUT" "hunter4"
has "a non-secret constant stays readable" "$OUT" "Mod.Port=8080"
alive "$DECOY" && ok "decoy survived the whole run" || bad "decoy killed"

echo "-- #160: a bin/_common.sh older than the moved helpers → exit 2 naming sync, nothing stopped"
Expand Down
Loading