Skip to content

fix: stop bundling awscrt in the installer - #1331

Closed
andychoquette wants to merge 1 commit into
aws-deadline:mainlinefrom
andychoquette:fix/installer-env-features
Closed

fix: stop bundling awscrt in the installer#1331
andychoquette wants to merge 1 commit into
aws-deadline:mainlinefrom
andychoquette:fix/installer-env-features

Conversation

@andychoquette

Copy link
Copy Markdown
Contributor

Why

envs.installer in hatch.toml declared no features, so Hatch resolved them from envs.default. When #1323 added the console extra to the default env, the installer build began installing botocore[crt], PyInstaller bundled awscrt, and installer:validate_exe failed against scripts/pyinstaller/allowlist.py:

Found file _internal/_awscrt.abi3.so which was not included in the allowlist
Found file deadline/PYZ.pyz/awscrt.auth which was not included in the allowlist
... (+9 more awscrt submodules)

This broke BuildAndStageInstallers in the 0.60.4 release (run 31435049529) — macOS failed and Linux/Windows were cancelled by fail-fast, so Release, Publish, and PublishToPyPI never ran. 0.60.4 is not on PyPI.

What

Pin features = ["gui"] on envs.installer so an extra added to envs.default can no longer change what ships in the signed artifact. console is omitted deliberately: awscrt is a compiled wheel that isn't allowlisted.

Rejected alternative: adding awscrt to the allowlist so the installer ships console sign-in support. That puts a compiled binary into three signed artifacts and needs per-platform allowlist entries — a supply-chain decision worth making deliberately, not as release triage. The allowlist is a deliberate control, so the conservative fix is to keep the bundle as it was.

No consumer impact. Console sign-in credential refresh stays available via pip install "deadline[console]". The extra is wheel metadata (Provides-Extra: consolebotocore[crt]>=1.42.89), independent of hatch.toml. Verified in a clean venv: adding the extra to an existing install pulls awscrt, botocore.compat.EC becomes non-None, _check_console_login_dependency stops raising, and botocore's login provider appears in the credential resolver chain.

Testing

New test/unit/test_installer_env_features.py asserts the installer env declares features explicitly and omits console. It lives in test/unit/, so it runs in the existing PR CI matrix. Confirmed it fails against the pre-fix config, not just passes against the new one.

Locally on arm64 macOS / Python 3.13 (matching the failing CodeBuild host):

Check Result
awscrt in installer env absent
hatch run installer:make_exe exit 0
hatch run installer:validate_exe Validation passed, 0 allowlist failures
hatch run lint (ruff + mypy) clean, 316 files
hatch run test 3209 passed, 22 skipped

Follow-ups (not in this PR)

  • installer:validate_exe runs only in the release pipeline, never in PR CI — which is why feat: support login and logout for AWS Console sign-in profiles #1323 merged green and broke at release. This test closes the specific hole; catching the general case means build+validate on PRs touching hatch.toml, pyproject.toml extras, requirements-installer.txt, or scripts/pyinstaller/.
  • hatch.toml test_build_installer points at test/build_installer, which doesn't exist.

The installer's PyInstaller build reads its extras from the `installer` Hatch
env, which declared no `features` and so inherited them from `envs.default`.
When the `console` extra was added there it began installing botocore[crt],
PyInstaller bundled awscrt, and `installer:validate_exe` failed against
scripts/pyinstaller/allowlist.py -- breaking the release build on all three
platforms after the change had already merged.

Pin the env's features explicitly so an extra added to `envs.default` can no
longer change what ships in the signed artifact. Console sign-in credential
refresh stays available to consumers via `pip install "deadline[console]"`,
which is unaffected: the extra is wheel metadata and independent of hatch.toml.

Signed-off-by: andychoquette <78888816+andychoquette@users.noreply.github.com>
@github-actions github-actions Bot added the waiting-on-maintainers Waiting on the maintainers to review. label Aug 11, 2026
Comment thread hatch.toml
# omitted on purpose -- it pulls in awscrt, a compiled wheel that is not in
# scripts/pyinstaller/allowlist.py. Consumers who need AWS Console sign-in
# credential refresh install the extra themselves.
features = ["gui"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropping console from the installer env means AWS Console sign-in is not just absent from the shipped installer — it is unreachable there, and the error message users hit points at a remedy they cannot apply.

_check_console_login_dependency (src/deadline/client/api/_loginout.py:59-63) raises:

Signing in to the AWS Console sign-in profile <p> requires an additional
dependency. Install it with: pip install "deadline[console]"

The installer ships a PyInstaller-frozen bundle (scripts/pyinstaller/make_exe.pyDeadlineClient.zip), which has no pip and no site-packages a user can extend. So for anyone using the installed CLI/GUI rather than a pip install, deadline auth login on a login_session profile fails with instructions that are impossible to follow. The login_session profile itself is one Deadline Cloud monitor can create, so this is a reachable path for installer users, not a corner case.

I agree with the PR description that adding awscrt to the allowlist is a supply-chain decision not worth making as release triage — so the config change here looks like the right call for unblocking the release. The gap worth tracking separately is the messaging: in a frozen build the guard should say something actionable (e.g. "not supported by the Deadline Cloud installer build; use a pip install of deadline[console]") rather than naming a pip command that cannot run. Detecting it is cheap — getattr(sys, "frozen", False), which the codebase already reasons about in _deadline_web_url.py:128.

# Extras that must never be bundled, mapped to why. An extra listed here brings in
# a distribution that scripts/pyinstaller/allowlist.py does not allow.
_EXTRAS_EXCLUDED_FROM_INSTALLER = {
"console": "pulls in awscrt, a compiled wheel absent from the PyInstaller allowlist",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard is a denylist, but the bug it is guarding against is an unanticipated addition — so it cannot catch the next instance of the same class of failure.

_EXTRAS_EXCLUDED_FROM_INSTALLER only names console. If someone adds mcp (or a future extra) directly to envs.installer.features, both tests here pass and installer:validate_exe still breaks at release time — exactly the failure mode described in the module docstring. mcp is concretely in that position today: scripts/pyinstaller/deadline_cli.spec:21 strips MCP prefixes out of hiddenimports and mcp is absent from allowlist.py's DEPENDENCIES, so enabling it would produce unallowlisted files.

The invariant that actually matches the allowlist is exact equality, not exclusion:

_INSTALLER_FEATURES = ["gui"]  # every extra here must be covered by scripts/pyinstaller/allowlist.py

def test_installer_env_features_are_pinned(hatch_envs: dict) -> None:
    assert hatch_envs["installer"].get("features") == _INSTALLER_FEATURES, (
        "Changing envs.installer features changes what PyInstaller bundles into the "
        "signed installer. Add the new distribution to scripts/pyinstaller/allowlist.py "
        "and update this list together."
    )

That subsumes both current tests (a missing features key fails it too, since .get returns None), makes any change to the bundle a deliberate two-file edit, and removes the need to enumerate reasons per excluded extra.

try:
import tomllib
except ModuleNotFoundError: # Python 3.9/3.10
import tomli as tomllib # type: ignore[no-redef]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tomli is not a declared test dependency, and if it is absent this fallback raises out of module scope rather than skipping — turning a missing optional dep into a collection error for the whole file on Python 3.9/3.10.

The except ModuleNotFoundError: handler catches the failure of import tomllib, but import tomli inside the handler is unguarded. tomli appears nowhere in requirements-testing.txt; the only path by which it reaches a 3.9/3.10 env is transitively through coverage[toml]'s marker-gated tomli requirement. code_quality.yml:24 runs the unit suite on 3.9 and 3.10, so this file's importability there rests on a transitive dep of a coverage extra that nothing in this repo pins or asserts.

Two ways to make it robust:

tomllib = pytest.importorskip("tomllib" if sys.version_info >= (3, 11) else "tomli")

or add tomli; python_version < "3.11" to requirements-testing.txt and keep the current import. The first is preferable if the guard is not considered important enough to warrant a new dependency; the second if it is (a silent skip on 3.9/3.10 still leaves the invariant checked on 3.11+, so either is defensible — an unguarded ImportError is not).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-maintainers Waiting on the maintainers to review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant