Skip to content

fix(python-sdk): chmod CLI binary at build time instead of runtime#3179

Open
mattConnHarbour wants to merge 1 commit intomainfrom
matthew/sd-2971-python-sdk-add-post-install-hook-to-chmod-cli-binary
Open

fix(python-sdk): chmod CLI binary at build time instead of runtime#3179
mattConnHarbour wants to merge 1 commit intomainfrom
matthew/sd-2971-python-sdk-add-post-install-hook-to-chmod-cli-binary

Conversation

@mattConnHarbour
Copy link
Copy Markdown
Contributor

Problem

CLI binary shipped without execute permissions (Python wheels strip them). In Docker: root installs, non-root user tries to exec, and is denied. Runtime os.chmod() fails for the same reason—non-root can't chmod a root-owned file.

Fix

Add setup.py build hooks that chmod the binary during wheel creation. Binary ships with execute permissions baked in, no runtime chmod needed.

Follow-up

Platform packages are nearly identical—could be generated from python-embedded-cli-targets.mjs instead of maintained manually.

Fixes Docker issue where root installs but non-root user runs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@mattConnHarbour mattConnHarbour requested a review from a team as a code owner May 5, 2026 22:48
@linear
Copy link
Copy Markdown

linear Bot commented May 5, 2026

Copy link
Copy Markdown
Contributor

@caio-pizzol caio-pizzol left a comment

Choose a reason for hiding this comment

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

hey @mattConnHarbour! good call moving chmod out of runtime, kills two failure modes at once :)

three things, only the middle one has teeth.


packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/setup.py:3

os is imported but never used. same in the other three Unix setup.py files.

-import os
 import stat
 from pathlib import Path

packages/sdk/langs/python/superdoc/embedded_cli.py:106

repro:

PKG=packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64
chmod 0644 $PKG/superdoc_sdk_cli_darwin_arm64/bin/superdoc
pip install -e $PKG
python -c "from superdoc.embedded_cli import resolve_embedded_cli_path; import subprocess; subprocess.run([resolve_embedded_cli_path()])"
# PermissionError: [Errno 13]

editable installs skip the build hook, so they see the source binary as-is. normal flow is fine because stage-python-companion-cli.mjs:106 chmods to 0755 first - but that chmod sits in a try/except marked "runtime will surface execution errors" (lines 107-109). the runtime chmod you just removed was that surfacing. tighten the staging chmod to fail loudly, or update that comment?


packages/sdk/langs/python/platforms/superdoc-sdk-cli-darwin-arm64/setup.py:16

# After files are copied to build dir, chmod the binary just restates the next four lines. drop it, or swap for the why. same in the other three Unix setup.py files.


(posted as one comment because GitHub's review-comment API kept returning 422 "internal error" on inline anchors for this PR.)

Copy link
Copy Markdown
Contributor

@andrii-harbour andrii-harbour left a comment

Choose a reason for hiding this comment

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

This PR is about shipping packages to customers. If it doesn't work - no matter how good our code is - nobody would know that.

2 very important things :

  1. The follow-up item mentioned in the description, imo, should be done here. It is directly related to what we are trying to fix.
  2. This setup must be tested manually on a separate CI/CD pipeline to ensure that the build is correct.

# After files are copied to build dir, chmod the binary
if self.build_lib:
binary_path = Path(self.build_lib) / 'superdoc_sdk_cli_darwin_arm64' / 'bin' / 'superdoc'
if binary_path.exists():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

if the path doesn't exist it will just fail silently.

@@ -10,15 +10,15 @@

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The four Unix setup.py files are byte-identical except for one string (the module name); the Windows one is a stub. Five files, one logical thing.

Duplication like this is fine in a code-only PR — a small surface, easy to revisit. This PR is infrastructure: wheel builds ship binaries to every consumer, and we're literally fixing a wheel-build bug right now. The next permission edge case will be fixed in one file and forgotten in the other three, and we'll ship asymmetric wheels.

Generate from python-embedded-cli-targets.mjs (already the source of truth), or extract a shared _build_helpers.py so each setup.py is a one-line shim. Worth doing in this PR — the duplication exists because of this change, and "we'll unify it later" is how five copies become eight.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants