Skip to content

Feat/zip installer#1629

Open
fengyca wants to merge 95 commits into
Graphify-Labs:v8from
fengyca:feat/zip-installer
Open

Feat/zip installer#1629
fengyca wants to merge 95 commits into
Graphify-Labs:v8from
fengyca:feat/zip-installer

Conversation

@fengyca

@fengyca fengyca commented Jul 3, 2026

Copy link
Copy Markdown

No description provided.

fengyca added 30 commits June 29, 2026 12:38
…deps

The spec mistakenly used tree-sitter-objc>=0.3,<0.5 in the windows-offline
extra, conflicting with the main dependencies pin of >=3.0,<4.0. Aligned
the extra to >=3.0,<4.0 so 'uv pip install graphifyy[windows-offline]'
can resolve. Plan document updated to match.
Code review follow-ups for path_win:
- Drop the unused 'current' parameter from _build_set_command /
  _build_unset_command (the PS script reads the current PATH itself;
  the Python-side parameter was dead from day one).
- Strengthen test_add_to_user_path_is_idempotent: assert the two PS
  commands are byte-identical AND contain the '-notcontains' dedup
  filter. The previous assertion (call_count == 2) was a smoke test
  that would have stayed green even if the dedup filter were removed.
- Use a raw docstring (r"""..""") to silence the
  DeprecationWarning: invalid escape sequence '\S' from the
  'HKLM\SYSTEM' / 'HKCU\Environment' references.
…rmat)

Code review follow-up for skill_copy: KNOWN_HOSTS has 21 entries, but
_BODY_BY_HOST had only 19. cursor and gemini fell through to the
Claude-bundle fallback, which would write a SKILL.md into directories
the host doesn't recognize (.mdc for cursor, GEMINI.md section for
gemini) — silent failure.

Fix: add _UNSUPPORTED_IN_OFFLINE_INSTALLER = {"cursor", "gemini"}.
copy_skill prints a note to stderr and returns without writing or
mkdir-ing, so the user can run 'graphify install cursor' (or gemini)
after the offline installer to register the real config.

Test added: test_copy_skill_skips_cursor_and_gemini.
…rse)

argparse on Python 3.10 applies %-formatting to help strings; %LOCALAPPDATA%
collapses into 'L' and crashes with 'unsupported format character'.
Python 3.11+ no longer does this, so the fix is %% escape (works on both).
fengyca added 30 commits July 3, 2026 09:52
windows-2022 and windows-2025 runners ship Visual Studio Enterprise
2022 (C:\Program Files\Microsoft Visual Studio\2022\Enterprise), not
BuildTools, so the hard-coded BuildTools path check failed with
'MSVC 2022 Build Tools not found in expected paths'.

Replace the two hard-coded paths with a vswhere.exe lookup filtered by
the C++ x86/x64 workload component. This covers Enterprise / Community
/ BuildTools / Professional uniformly and still fails fast (avoids a
20+ minute Nuitka run that then dies on cl.exe) when no MSVC is
present.
`tools/build_windows_installer.sh` used bash process substitution
(`<($PYTHON ...)`) to feed the parsed windows-offline dependency list
into `pip download --requirement`. Under Git Bash on Windows the
substituted path is `/proc/<pid>/fd/NN`, but the Windows Python
interpreter that pip invokes cannot open that path (no /proc
filesystem in MSYS), so the run died with:

    ERROR: Could not open requirements file: [Errno 2] No such file or
    directory: '/proc/<pid>/fd/NN'

Pipe the dependency list to a real `mktemp` file first and pass that
file to `--requirement`. `trap 'rm -f' EXIT` cleans it up.

Reproduced on the windows-2022 runner used by the
build-windows-installer workflow; fix is local to the build script
and does not affect runtime behaviour.
…eter

`tools/build_windows_installer.sh` hard-coded
`pip download --python-version 3.10` but the offline venv is built
from whatever `$PYTHON` (defaults to `python`) resolves to. On the
current windows-2022 image `python` is 3.12.10, so the wheelhouse
ended up full of cp310 wheels that the cp312 venv's pip refused with

    ERROR: Could not find a version that satisfies the requirement
    numpy>=1.21 (from graphifyy) (from versions: none)
    ERROR: No matching distribution found for numpy>=1.21

because cp312 pip cannot read cp310 wheels under `--no-index
--find-links`.

Detect the interpreter version up front and feed it into both the
wheel download and the wheelhouse cache path
(`wheelhouse-windows-cp312/` vs the old `wheelhouse-windows/`).
Versioning the cache also keeps a stale cp310 tree from poisoning a
later cp312 build.
`tools/build_windows_installer.sh` builds a clean venv from
`--no-index --find-links $WHEELHOUSE`, then runs the three Nuitka
compilations inside it (`python -m nuitka ...`). The wheelhouse is
populated from the `windows-offline` extra plus graphifyy itself, so
it had every runtime wheel — but Nuitka itself lives in the `dev`
group, not in `windows-offline`, and was therefore not pulled into
the wheelhouse. The venv ended up with no Nuitka and the build died
at the first compile with

    .venv-offline-build/Scripts/python.exe: No module named nuitka

Append `nuitka>=4.1` to the wheel requirement and list `nuitka`
alongside `graphifyy` in the venv install. Drop `--no-deps` from
`pip download` so ordered-set (Nuitka's runtime transitive) is
fetched into the wheelhouse too — with `--no-deps` the offline venv
would install Nuitka but not ordered-set, and Nuitka's first
`import ordered_set` would No-module-name-ordered-set.

No runtime effect: Nuitka is only used to compile the bundled
`graphify.exe`, `graphify-mcp.exe` and `graphify-installer.exe`.
`pip download --python-version X --platform Y` refuses to follow
transitive dependencies: the only way to let it do so under those
constraints is `--only-binary=:all:`, which is stricter than the
offline-installer build needs. So instead of dropping --no-deps (the
previous attempt), keep --no-deps and list the build-tool deps
explicitly in $WHEEL_REQ:

    nuitka>=4.1
    ordered-set>=4.1   # nuitka required transitive
    zstandard>=0.18    # optional in nuitka; dev group lists it for
                       # compressed onefile output

Without ordered-set, `python -m nuitka` dies in the offline venv with
No-module-named-ordered-set. Without zstandard, the resulting
graphify*.exe is ~30% larger (Nuitka falls back to an uncompressed
onefile blob).

The venv install line was already updated to include 'nuitka' in the
previous commit, so no further change there.

Reproduced on the windows-2022 runner used by
build-windows-installer.
When the offline venv installs graphifyy from the wheelhouse, pip
transitively pulls matplotlib. matplotlib 3.11's wheel METADATA
declares `Requires-Dist: setuptools`, and pip under
`--no-index --find-links $WHEELHOUSE` does not fall back to the
setuptools already in the venv's site-packages — that venv is built
from the wheelhouse and only, so pip treats the build as fully
offline. Result:

    Looking in links: .../wheelhouse-windows-cp312
    ERROR: Could not find a version that satisfies the requirement
    setuptools>=42 (from versions: none)
    ERROR: No matching distribution found for setuptools>=42

`wheel` is added for the same reason — PEP 517 build isolation pulls
it in for some wheels even when we are installing prebuilt ones.

Pin matches the project's own dev group: setuptools>=68 (the
`[build-system]` requires floor) and wheel>=0.40. The lower bound
(>=68 for setuptools) lines up with what `pip wheel .` already
needs on the host side, so the venv and the host agree on the floor.
The previous several commits added setuptools, wheel, ordered-set and
zstandard to the wheelhouse one by one, each in response to a new
`Could not find a version that satisfies the requirement X (from
versions: none)` failure during `pip install --no-index --find-links
$WHEELHOUSE`. The pattern is whack-a-mole: any time a wheel in the
tree declares a runtime or build-time Requires-Dist, pip under
--no-index refuses to consider packages already bootstrapped into the
venv's site-packages (packaging, pip itself, etc.) and the build dies.

The wheelhouse is a build-time concern only. It is not bundled
inside the produced .exe — graphify/installer/ only edits the user
PATH and copies SKILL.md to detected host directories, and never
re-runs pip install at runtime. So the offline venv does not need to
model a fully air-gapped end-user install; it just needs to be a
working interpreter for the three `python -m nuitka ...` compiles.

Drop --no-index from the venv install. Keep --find-links so the
wheelhouse is still the *preferred* source — pip checks it first and
only falls through to PyPI / already-installed packages for whatever
is missing. Future transitive dep additions (matplotlib asking for
setuptools, wheel asking for packaging, anything asking for
pep517-style build-isolation helpers, …) no longer break the build.

Also drop the now-unused setuptools / wheel explicit entries in the
venv install list — pip will still find them via --find-links or
PyPI fallback if needed.
The bare `graphifyy` `dependencies` block is the code-only runtime
(networkx, numpy, rapidfuzz, tree-sitter-*). The packages the .exe
actually bundles at runtime — anthropic, mcp, starlette, graspologic,
matplotlib, watchdog, tree-sitter-sql, tree-sitter-hcl, jieba — all
live in optional-dependencies, grouped under the `windows-offline`
extra. The Nuitka invocations below list every one of them via
--include-module=... so they get statically compiled into the .exe,
but Nuitka still imports each module to walk its source, and an
`import anthropic` against a venv where the anthropic wheel was never
installed dies with

    FATAL: Error, failed to locate module 'anthropic' that you asked
    to include.

Switch the venv install from `graphifyy` to
`graphifyy[windows-offline]`. `--find-links $WHEELHOUSE` still
makes the wheelhouse the preferred source so the previously downloaded
cp312 win_amd64 wheels are picked first; only the `[windows-offline]`
extra listing is consulted, not its platform restriction.
…ocal wheel

A single `pip install --find-links $WHEELHOUSE
'graphifyy[windows-offline]' nuitka` makes pip re-resolve graphifyy
itself; pip then picks the *highest available* version, which on PyPI
is currently 0.9.5. The wheel we just built in $WHEELHOUSE is 0.9.1.
The 0.9.5 release on PyPI does not declare a `windows-offline` extra
in its METADATA, so pip logs

    WARNING: graphifyy 0.9.5 does not provide the extra 'windows-offline'

and silently drops every package the extra would have pulled in. The
venv ends up with no anthropic / mcp / starlette / graspologic /
matplotlib / watchdog / tree-sitter-sql / tree-sitter-hcl / jieba, and
the first Nuitka compile then dies with

    FATAL: Error, failed to locate module 'anthropic' that you asked
    to include.

Split the install into three pip calls so pip never re-resolves
graphifyy from PyPI:

  1. Install the local graphifyy wheel by exact file path
     ($WHEELHOUSE/graphifyy-*.whl, --no-deps). A file path is a hard
     pin: pip cannot upgrade to a different version.
  2. Install the windows-offline extra's *contents* explicitly, as a
     flat list of package names with the same version pins the extra
     uses. This avoids consulting graphifyy's METADATA at all.
  3. Install Nuitka, --no-deps, against the wheelhouse copy.

When [project.optional-dependencies.windows-offline] is updated in
pyproject.toml, mirror the changes here in the explicit list.

graspologic's `python_version < '3.13'` marker is honoured
implicitly: pip only installs it on the cp312 venv (true) and skips it
on a future cp313 venv (false).
Previous commit split venv install into 3 pip calls but kept
`--no-deps` on the first call (`pip install --no-deps
$WHEELHOUSE/graphifyy-*.whl`). That makes pip install the graphifyy
wheel *without* its main `dependencies` block (networkx, numpy,
rapidfuzz, tree-sitter-*). The second pip call (the explicit
[windows-offline] list) installs only the optional-dependency
packages (anthropic, mcp, starlette, graspologic, tree-sitter-sql,
tree-sitter-hcl, jieba, watchdog, matplotlib), not the main deps.

Result: venv has graphifyy, anthropic, mcp, ... but no networkx /
numpy / rapidfuzz / tree-sitter-*. The first Nuitka compile that
hits a `--include-module=rapidfuzz` then dies with

    FATAL: Error, failed to locate module 'rapidfuzz' that you asked
    to include.

Drop --no-deps on the graphifyy install. The file-path pin still
prevents pip from upgrading to PyPI's 0.9.5, and pip will resolve
each main dep against the wheelhouse (the [windows-offline] extra
in pyproject.toml lists every main dep, so the wheels are already
on disk). The --no-deps stays on the nuitka install since we never
want pip to follow its transitive deps.

Reproduced on windows-2022 via build-windows-installer.yml
workflow_dispatch.
`--mode=standalone` / `--mode=onefile` on Windows needs Dependency
Walker (depends.exe) so Nuitka can walk the DLL imports of every
.pyd. On the first run on a clean runner Nuitka prompts:

    Fully automatic, cached. Proceed and download? [Yes]/No :

In a non-interactive CI environment (default-non-interactive) Nuitka
treats the empty answer as 'no' and aborts with

    FATAL: Nuitka does not work in '--mode=standalone' or
    '--mode=onefile' on Windows without dependency walker.

Pipe 'Yes' into stdin via `< <(printf 'Yes\\n')` on each of the
three `python -m nuitka` invocations. The download is cached, so
subsequent runs find the binary in the user cache and do not prompt
again.

Reproduced on windows-2022 via build-windows-installer.yml
workflow_dispatch.
…s & project agent config

install.bat:
- add step 2 pre-cleanup (graphify uninstall claude) for idempotent re-runs
- bump step counter from 1/4 to 1/5
- resolve wheel filename via CMD for-loop glob (pip's own glob is fragile on
  paths with parentheses or other special characters)

uninstall.bat:
- mirror install.bat Python detection (prefer system, fall back to embedded)
- run 'graphify uninstall' (no platform arg) so gf-* and code-pipeline bundled
  skills are removed alongside the platform SKILL.md
- keep installer files in place after uninstall for future re-use

__main__.py:
- introduce _BundledSkill dataclass and _BUNDLED_SKILLS tuple
- new _uninstall_bundled_skills() helper used by _remove_skill_file()

Project agent tooling migration:
- replace AGENTS.md with CLAUDE.md (richer Claude Code instructions)
- add .opencode/ (opencode plugin injecting graph reminder into bash calls)

Tests:
- update test_agents_platform.py and test_install_references.py to match the
  new step counts and uninstall behavior
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant