When launched via pythonw.exe (no console), sys.stdout/stderr are None.
Accessing .encoding on None raised AttributeError, caught by the GUI's
pipeline import guard and shown as 'Failed to load pipeline'. Added
None check before the encoding check in fix_console_encoding(), added
a test, and documented the pitfall in CLAUDE.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
subprocess.run with capture_output=True blocked until process exit,
dumping all output at once. Now uses Popen with line-by-line reading,
-u flag, and PYTHONUNBUFFERED=1 so logs stream in real time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_find_folder() used a plain lowercase compare which failed when the
server canonical folder name differs from the comparison.json mod name
in more than just case (e.g. spaces vs underscores: "NIArms All in One"
vs "@NIArms_All_In_One"). These mods showed ✗ even though the pipeline
found and linked them correctly.
Add a normalized-name fallback (strips non-alphanumeric, same logic as
fetcher._normalize_name) so the lookup matches the same way the fetcher
resolves mods from the server index.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
comparison.json was stale (built from 2 test presets only) while
modlist_html/ now has 4 files. Regenerated comparison.json from all
presets to restore consistency.
Also added _SkipTest exception to the test harness so the
consistency check skips gracefully on a fresh clone (comparison.json
is gitignored) instead of raising AssertionError.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers: common commands, package vs CLI architecture, data flow,
group naming convention, server index structure, junction/symlink
rules (critical: never shutil.rmtree), check_names two-pass
classification, Python 3.9 from __future__ requirement, and
test suite structure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Documents check_names.py (--fix, --fix-ids, status codes table)
- Adds Migrating Existing Mods section for servers with pre-existing mods
- Updates Python requirement from 3.11 to 3.9
- Updates test count from 71 to 85
- Updates check_deps example output to show 3.9.2
- Adds check_names.py to folder structure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ID_COLLISION status (previously NOT_ON_SERVER) now has its own label
and shows the bad steam_id in the report output.
--fix-ids cross-references comparison.json to find the correct
publishedid for each colliding folder and rewrites it in-place in
meta.cpp using regex, preserving all other content.
New helpers: _lookup_detailed (returns server_name + local_sid tuple),
_write_steam_id (regex-replace publishedid in meta.cpp),
_build_comparison_id_map (builds normalized_name -> steam_id map
from comparison.json).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a server folder's meta.cpp publishedid appears in a local folder
that has a completely different name, the steam_id lookup was returning
a wrong MISMATCH. Added a two-pass classification: any proposed rename
target that is already an exact OK match for another folder is
reclassified as NOT_ON_SERVER (steam_id collision) instead of MISMATCH.
_resolve_status moved to module level and takes ok_disk_names as a
parameter so it can be unit-tested independently.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Compares @Mod folder names in downloads/ against server canonical
names. Uses meta.cpp steam_id as primary lookup (most reliable),
falls back to normalized name. Reports OK / MISMATCH / NOT_ON_SERVER
per folder.
--fix mode renames mismatched folders and updates arma_dir junctions
in one pass so the full pipeline can run cleanly afterward.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
parser.py and update_mods.py were missing
'from __future__ import annotations', causing a TypeError on
Python < 3.10 when the X | Y union syntax is evaluated at runtime.
All other modules already had the import.
Also lowers MIN_PYTHON from 3.11 to 3.9 -- the toolchain does not
use any Python 3.10/3.11-specific stdlib features beyond the union
annotation syntax which is now handled by the future import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
str | None union syntax requires Python 3.10+. Adding
from __future__ import annotations makes all annotations
lazy so the script can run on older Python and report the
version requirement instead of crashing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pipeline: parse HTML presets, compare modlists, download from Caddy
file server, create junctions/symlinks to Arma 3 Server directory.
Includes update/sync flows, missing-mod reporting, OS compat layer,
shared config, dep checker, comprehensive test suite (71 tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>