Skip to content

feat: add PyInstaller CI binaries and Streamlit web UI#50

Open
ambicuity wants to merge 5 commits intoSUPAIDEAS:mainfrom
ambicuity:feat/pyinstaller-and-streamlit
Open

feat: add PyInstaller CI binaries and Streamlit web UI#50
ambicuity wants to merge 5 commits intoSUPAIDEAS:mainfrom
ambicuity:feat/pyinstaller-and-streamlit

Conversation

@ambicuity
Copy link
Copy Markdown
Contributor

Summary

Closes #44 - Cross-platform native binaries via PyInstaller
Closes #45 - Streamlit web UI wrapper

Changes

PyInstaller CI (#44)

  • .github/workflows/build-binaries.yml - GitHub Actions pipeline that builds standalone executables for Linux (x86_64), macOS (arm64) and Windows (x64) on every v* tag push using PyInstaller --onefile. Includes a smoke test step and uploads binaries as artifacts + attaches them to GitHub Releases via softprops/action-gh-release.

Streamlit Web UI (#45)

  • app/streamlit_app.py - A local Streamlit web interface: users upload a PDF, enter (and confirm) a password, and download the encrypted file with one click. No CLI knowledge required.
    • requirements-webui.txt - Separate requirements file (streamlit>=1.35.0) so the core CLI install stays lean.
    • README.md - Added Web UI usage instructions and a pre-built binary download section.

Testing

All 4 tests pass. The Streamlit app can be tested locally with streamlit run app/streamlit_app.py.

…EAS#44, closes SUPAIDEAS#45)

- feat(ci): add .github/workflows/build-binaries.yml — GitHub Actions
  pipeline using PyInstaller to build standalone executables for
  Linux (x86_64), macOS (arm64) and Windows (x64) on every tag push;
  binaries are uploaded as artifacts and attached to GitHub Releases
- feat(ux): add app/streamlit_app.py — a local Streamlit web UI wrapper
  that lets users upload a PDF, set a password with confirmation, and
  download the encrypted file without any CLI knowledge
- feat: add requirements-webui.txt with streamlit dependency (kept
  separate from core dependencies to keep the CLI install lean)
- docs: update README with Web UI usage instructions, pre-built
  binary download section, and fix existing typos
Copilot AI review requested due to automatic review settings February 21, 2026 02:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds distribution and UX improvements by introducing a Streamlit-based local web UI and a GitHub Actions workflow intended to publish PyInstaller-built binaries on tagged releases.

Changes:

  • Adds a Streamlit app for encrypting uploaded PDFs via a browser-based local UI.
  • Adds a new release workflow to build and attach standalone binaries for Linux/macOS/Windows.
  • Updates documentation and adds a separate requirements file for the optional web UI.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
uv.lock Introduces a uv lockfile intended to support uv sync in the binary-build workflow.
requirements-webui.txt Adds a minimal dependency list for running the optional Streamlit UI.
app/streamlit_app.py Implements the Streamlit web UI wrapper around passifypdf.encryptpdf.encrypt_pdf.
README.md Documents Web UI usage and pre-built binary downloads; fixes typos in “Known Issues”/“Note”.
.github/workflows/build-binaries.yml Adds CI for building PyInstaller binaries and attaching them to GitHub Releases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread requirements-webui.txt
Comment on lines +36 to +47
- name: Install uv
uses: astral-sh/setup-uv@v4

- name: Install dependencies
run: uv sync

- name: Install PyInstaller
run: uv pip install pyinstaller

- name: Build binary
run: |
uv run pyinstaller \
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

This workflow uses uv sync, but the repo's dependency management is Poetry-based (see .github/workflows/ci.yml and pyproject.toml). As-is, uv sync depends on uv.lock, which currently doesn't list any dependencies (e.g., pypdf), so the build environment may be missing runtime deps needed by PyInstaller. Consider either switching this workflow to poetry install (matching CI) or generating/committing a complete uv.lock produced by uv lock so uv sync installs the same deps as Poetry.

Suggested change
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync
- name: Install PyInstaller
run: uv pip install pyinstaller
- name: Build binary
run: |
uv run pyinstaller \
- name: Install Poetry
run: |
python -m pip install --upgrade pip
python -m pip install poetry
- name: Install dependencies
run: |
poetry install --no-interaction --no-ansi
- name: Install PyInstaller
run: |
poetry run python -m pip install pyinstaller
- name: Build binary
run: |
poetry run pyinstaller \

Copilot uses AI. Check for mistakes.
--onefile \
--name passifypdf \
--console \
passifypdf/encryptpdf.py
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

PyInstaller is being pointed at passifypdf/encryptpdf.py, but that file uses a relative import (from .cli import get_arg_parser). When executed as a script (which is how the frozen app runs the entry module), that relative import will fail. Use an entry point that imports the package module (e.g., a small top-level main.py that calls passifypdf.encryptpdf.main, or a passifypdf/__main__.py) and build from that instead.

Suggested change
passifypdf/encryptpdf.py
-m passifypdf.encryptpdf

Copilot uses AI. Check for mistakes.
- os: ubuntu-latest
artifact_name: passifypdf-linux-x86_64
binary_path: dist/passifypdf
- os: macos-latest
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The matrix labels the macOS artifact as arm64, but macos-latest isn't guaranteed to stay on Apple Silicon. Pin the runner to an explicit version/arch (e.g., macos-14 for arm64) or rename the artifact to avoid claiming a specific architecture when it's not enforced.

Suggested change
- os: macos-latest
- os: macos-14

Copilot uses AI. Check for mistakes.
Comment thread app/streamlit_app.py
streamlit run app/streamlit_app.py
"""

import io
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

io is imported but never used. Please remove it to avoid lint/quality noise.

Suggested change
import io

Copilot uses AI. Check for mistakes.
Comment thread app/streamlit_app.py
Comment on lines +77 to +110
try:
# Write uploaded bytes to a temp input file
with tempfile.NamedTemporaryFile(
suffix=".pdf", delete=False
) as tmp_in:
tmp_in.write(uploaded_file.read())
tmp_in_path = Path(tmp_in.name)

# Encrypt to a temp output file
with tempfile.NamedTemporaryFile(
suffix=".pdf", delete=False
) as tmp_out:
tmp_out_path = Path(tmp_out.name)

encrypt_pdf(tmp_in_path, tmp_out_path, password)

# Read the encrypted bytes and offer download
encrypted_bytes = tmp_out_path.read_bytes()

st.success("✅ PDF encrypted successfully!")
st.download_button(
label="⬇️ Download encrypted PDF",
data=encrypted_bytes,
file_name=output_name,
mime="application/pdf",
use_container_width=True,
)

except Exception as exc:
st.error(f"❌ Encryption failed: {exc}")
finally:
# Clean up temp files
tmp_in_path.unlink(missing_ok=True)
tmp_out_path.unlink(missing_ok=True)
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

tmp_in_path / tmp_out_path are created inside the try, but the finally always calls tmp_in_path.unlink(...) / tmp_out_path.unlink(...). If an exception occurs before either variable is assigned (e.g., temp file creation fails), the finally will raise UnboundLocalError and mask the original error. Initialize these paths to None before the try and guard the cleanup accordingly.

Copilot uses AI. Check for mistakes.
Comment thread README.md
Added instructions for launching the UI and running commands.
Updated UI-README to clarify PDF file functionality.
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.

UX: Create a Streamlit or Gradio based web UI wrapper Build: Implement cross-platform native binaries using PyInstaller

3 participants