Skip to content

Add setup script and README for Brother HL-L1222-V Android print server (Raspberry Pi / openSUSE)#31

Open
ib-bsb-br wants to merge 1 commit into
mainfrom
codex/create-tutorial-for-brother-hl-l1222-v-setup-on-raspberry-pi-0gaera
Open

Add setup script and README for Brother HL-L1222-V Android print server (Raspberry Pi / openSUSE)#31
ib-bsb-br wants to merge 1 commit into
mainfrom
codex/create-tutorial-for-brother-hl-l1222-v-setup-on-raspberry-pi-0gaera

Conversation

@ib-bsb-br

@ib-bsb-br ib-bsb-br commented Apr 29, 2026

Copy link
Copy Markdown
Owner

Motivation

  • Provide a reproducible, one-step setup to configure a Raspberry Pi 4B+ (openSUSE Tumbleweed) as a CUPS/IP P print server for a USB-connected Brother HL-L1222/HL-L1222-V so Android devices can print via Wi‑Fi (Mopria/IPP).
  • Automate installation and configuration of required services, firewall, and discovery to simplify deployment and avoid manual CUPS edits.
  • Offer convenient fallbacks such as a hotfolder auto-print watcher and an optional LAN upload page for constrained Android workflows.

Description

  • Add scripts/setup_hl_l1222_android_print.sh, a robust Bash script that installs dependencies, enables cups, avahi, firewalld, optional Bluetooth, configures CUPS sharing, opens firewall rules, detects the Brother USB device, picks a suitable PPD/model (prefers brlaser, falls back to everywhere or a generic mono laser), and creates/replaces the CUPS queue with lpadmin and related commands.
  • The script installs and enables a hotfolder auto-print watcher implemented as hotfolder_watcher.py and a systemd unit hll1222-hotfolder.service to auto-print files placed into /var/spool/hll1222-hotfolder.
  • Adds an optional Python-based LAN upload service upload_server.py and systemd unit hll1222-upload.service (enabled when --enable-upload is passed), with safeguards for Python 3.13+ where the cgi module may be missing.
  • Update README.md with purpose, usage (sudo bash scripts/setup_hl_l1222_android_print.sh), post-setup instructions, connection examples (IPP URL), and notes about Bluetooth and model selection.

Testing

  • No automated tests were executed as part of this change.

Codex Task


Open in Devin Review

Summary by Sourcery

Add an automated setup for configuring a Raspberry Pi (openSUSE Tumbleweed) as a CUPS/IPPs print server for a USB-connected Brother HL-L1222/HL-L1222-V printer, including optional hotfolder and upload-based printing workflows for Android devices.

New Features:

  • Introduce a Bash setup script that installs and configures CUPS, Avahi, firewalld, and related services to expose a shared Brother HL-L1222/HL-L1222-V print queue over IPP.
  • Add automatic detection of the connected Brother USB printer and selection of an appropriate CUPS model/PPD with fallbacks for generic compatibility.
  • Provide a systemd-managed hotfolder watcher that auto-prints files dropped into a dedicated spool directory as a workflow fallback.
  • Add an optional Python-based LAN upload web service with a systemd unit to queue uploaded documents into the printer via the hotfolder.

Enhancements:

  • Expand the README with usage instructions, post-setup steps, network printing details, and notes on Bluetooth limitations and model selection.

Documentation:

  • Document how to run the new setup script, connect Android devices via IPP, and utilize optional Bluetooth and model configuration notes in the README.

Summary by cubic

Adds a one-step setup to turn a Raspberry Pi (openSUSE Tumbleweed) into a CUPS/IPP print server for a USB Brother HL‑L1222/HL‑L1222‑V so Android devices can print over Wi‑Fi. Includes an auto-print hotfolder and an optional LAN upload service.

  • New Features

    • Setup script scripts/setup_hl_l1222_android_print.sh installs and configures cups, avahi, firewalld (opens ipp and mdns), and optional Bluetooth.
    • Auto-creates/replaces CUPS queue Brother_HL_L1222_V using lpadmin; prefers brlaser, falls back to everywhere or a generic mono model.
    • Enables sharing and mDNS discovery; restarts services safely with a backup of cupsd.conf and interactive confirmations for risky changes.
    • Installs hotfolder watcher (hotfolder_watcher.py) with hll1222-hotfolder.service to auto-print files from /var/spool/hll1222-hotfolder.
    • Optional LAN upload server (upload_server.py, hll1222-upload.service) behind a size cap; skipped automatically on Python 3.13+ if cgi is unavailable.
  • Migration

    • Run: sudo bash scripts/setup_hl_l1222_android_print.sh (use --enable-upload to enable the upload page).
    • Connect Android on the same Wi‑Fi and add printer via Mopria using ipp://<raspberrypi-ip>/printers/Brother_HL_L1222_V.
    • Optional upload page (if enabled): http://<raspberrypi-ip>:8088/.

Written for commit b5072d2. Summary will update on new commits. Review in cubic

@sourcery-ai

sourcery-ai Bot commented Apr 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a one-shot Bash setup script and accompanying systemd-backed helpers to turn an openSUSE Tumbleweed Raspberry Pi into a shared CUPS IPP print server for a USB Brother HL-L1222-V, plus README documentation for Android/Mopria usage.

Sequence diagram for upload-to-hotfolder print workflow

sequenceDiagram
  actor User
  participant Browser as Android_browser
  participant Upload as upload_server.py
  participant Hotfolder as Hotfolder_directory
  participant Watcher as hotfolder_watcher.py
  participant CUPS as CUPS_IPP
  participant Printer as Brother_HL_L1222_V

  User->>Browser: Open http://pi:8088/
  Browser->>Upload: GET /
  Upload-->>Browser: HTML_upload_form

  User->>Browser: Select_file_and_submit_form
  Browser->>Upload: POST /upload (multipart_file)
  Upload->>Upload: Validate_size_and_extension
  Upload->>Hotfolder: Write_file_to_hotfolder
  Upload-->>Browser: 200_OK_uploaded_successfully

  loop Periodic_scan
    Watcher->>Hotfolder: List_and_check_files
    Watcher->>Watcher: Wait_for_stable_file_size
    Watcher->>CUPS: lp -d Brother_HL_L1222_V file
    CUPS->>Printer: Send_print_job_via_USB
    Watcher->>Hotfolder: Move_file_to_printed_or_failed
  end
Loading

Flow diagram for setup_hl_l1222_android_print.sh execution

graph TD
  A[start_script]
  A --> B[parse_args_and_flags]
  B --> C[resolve_and_require_sudo_zypper_lpinfo_lpadmin_lpstat]
  C --> D{--status_flag_set?}
  D -- yes --> S[status_report_and_exit]
  D -- no --> E[install_dependencies_via_zypper]
  E --> F[enable_and_start_cups_avahi_firewalld_optional_bluetooth]
  F --> G[configure_firewalld_for_ipp_mdns_optional_upload_port]
  G --> H[backup_and_configure_cupsd.conf_for_sharing_and_mDNS]
  H --> I[detect_usb_device_uri_for_Brother_printer]
  I --> J[select_best_model_brlaser_else_everywhere_else_generic]
  J --> K{queue_already_exists?}
  K -- yes --> K1[ask_user_confirmation_and_delete_existing_queue]
  K1 --> L[create_new_queue_with_lpadmin_and_set_defaults]
  K -- no --> L
  L --> M[enable_accept_queue_and_set_default_printer]
  M --> N[restart_cups]
  N --> O[install_hotfolder_watcher_script_and_systemd_service]
  O --> P{upload_enabled?}
  P -- yes --> Q[install_upload_server_script_and_systemd_service]
  P -- no --> R[skip_upload_service_installation]
  Q --> T[print_summary_with_IPP_URL_and_optional_upload_URL]
  R --> T
  T --> U[end]
Loading

File-Level Changes

Change Details Files
Introduce a Bash setup script that installs dependencies, configures CUPS sharing and firewall rules, auto-detects the Brother USB printer and model, and creates/replaces a shared CUPS queue suitable for Android IPP/Mopria printing.
  • Add a robust Bash script with argument parsing, logging helpers, and safety prompts for high-risk CUPS changes and queue replacement.
  • Ensure required tools (sudo, zypper, lpinfo, lpadmin, lpstat) are present and resolvable via PATH, installing cups-client if needed.
  • Automate installation of CUPS, avahi, firewalld, usbutils, python3, and optionally printer-driver-brlaser and Bluetooth packages using zypper.
  • Enable and start cups, avahi-daemon, firewalld, and optionally bluetooth via systemd.
  • Configure firewalld to allow IPP and mDNS, and optionally open a custom TCP port for the upload service.
  • Backup cupsd.conf, apply sharing settings via cupsctl, and append WebInterface/Browsing/BrowseLocalProtocols directives if missing, then restart CUPS.
  • Detect the Brother USB device URI via lpinfo -v and heuristics, erroring with diagnostics if detection fails.
  • Select the best available printer model via lpinfo -m preferring brlaser/Brother entries, then everywhere, then generic mono laser.
  • Create or replace the target CUPS queue with lpadmin, set sensible defaults and sharing options, enable and accept jobs, and set it as default, then restart CUPS.
  • Provide a status-only mode that prints system, USB, CUPS devices/models, and CUPS status for diagnostics.
scripts/setup_hl_l1222_android_print.sh
Install and manage a hotfolder-based auto-print mechanism via a Python watcher script and a dedicated systemd service running as lp.
  • Create a dedicated script directory under /usr/local/lib/hll1222-print and a writable hotfolder at /var/spool/hll1222-hotfolder owned by lp:lp.
  • Generate and install hotfolder_watcher.py that polls the hotfolder, waits for stable uploads, filters by allowed extensions, and prints via lp to the configured queue.
  • Move successfully printed files into a printed subdirectory and failures into a failed subdirectory with timestamped filenames for basic auditing.
  • Install a systemd service unit hll1222-hotfolder.service that runs the watcher as user lp, binds it to cups.service, sets environment variables (queue, hotfolder), and restarts on failure.
  • Enable and start the hotfolder watcher service on installation, reloading systemd units as needed.
scripts/setup_hl_l1222_android_print.sh
Add an optional LAN upload web service implemented in Python, with safeguards for missing cgi on newer Python versions, and a corresponding systemd unit gated by a script flag.
  • Conditionally install upload_server.py only when --enable-upload is passed and python3 has a working cgi module, otherwise log warnings and skip setup.
  • Implement a simple ThreadingHTTPServer-based HTTP service that serves an upload form and accepts multipart POSTs to /upload, enforcing max upload size and allowed extensions.
  • Write uploaded files into the same hotfolder, ensuring safe filenames and chmod, so they are later printed by the hotfolder watcher.
  • Create hll1222-upload.service to run the upload server as lp, bind it to cups and the hotfolder service, and set environment variables for queue, hotfolder, port, and max upload size.
  • Open the configured upload TCP port in firewalld when the upload service is enabled and ensure the systemd unit is enabled and started.
scripts/setup_hl_l1222_android_print.sh
Document the print-server setup and Android usage in the README, pointing to the setup script and key connection details.
  • Replace the placeholder README with a description of the Brother HL-L1222-V Android print server use case on Raspberry Pi 4B+ / openSUSE Tumbleweed.
  • Explain what the configuration enables, including Android IPP/Mopria printing over the same Wi‑Fi and LAN printer sharing via CUPS.
  • Add a note about unreliable Android Bluetooth printing while clarifying that Wi‑Fi IPP is the supported path.
  • Describe how to run the setup script with sudo and outline post-setup steps: keep USB attached, ensure Wi‑Fi connectivity, enable Mopria, and configure the IPP URL with the queue name.
  • Mention that the script defaults to CUPS everywhere model and that users can swap to an official Brother PPD if available.
README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive setup script and documentation for configuring a Raspberry Pi as a print server for the Brother HL-L1222-V printer on openSUSE Tumbleweed. The script automates dependency installation, CUPS sharing configuration, and the deployment of a Python-based hotfolder service for automated printing. Review feedback focuses on improving the robustness of the CUPS configuration by utilizing cupsctl for all directives, optimizing the file stability check in the watcher service to prevent blocking, and ensuring consistent binary path resolution for all system utilities.

Comment on lines +266 to +280
if ! sudo cupsctl --share-printers --remote-any; then
err "Failed to configure cups sharing with cupsctl."
exit 1
fi

# Ensure WebInterface and mDNS browsing directives exist.
if ! sudo grep -qi '^WebInterface[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'WebInterface Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^Browsing[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'Browsing Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^BrowseLocalProtocols[[:space:]]\+dnssd' "${CUPSD_CONF}"; then
echo 'BrowseLocalProtocols dnssd' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using cupsctl to set all configuration directives at once is more robust and cleaner than manually appending to cupsd.conf. cupsctl handles existing entries correctly and ensures proper syntax, avoiding potential duplicate or conflicting directives in the configuration file.

Suggested change
if ! sudo cupsctl --share-printers --remote-any; then
err "Failed to configure cups sharing with cupsctl."
exit 1
fi
# Ensure WebInterface and mDNS browsing directives exist.
if ! sudo grep -qi '^WebInterface[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'WebInterface Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^Browsing[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'Browsing Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^BrowseLocalProtocols[[:space:]]\+dnssd' "${CUPSD_CONF}"; then
echo 'BrowseLocalProtocols dnssd' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
log "Configuring CUPS sharing using cupsctl..."
if ! sudo "$CUPSCTL_BIN" --share-printers --remote-any WebInterface=Yes Browsing=Yes BrowseLocalProtocols="dnssd"; then
err "Failed to configure cups sharing with cupsctl."
exit 1
fi

exit 1
fi

if ! sudo "$LPADMIN_BIN" -p "$QUEUE_NAME" -o printer-is-shared=true -o media=A4 -o PageSize=A4 -o sides=one-sided; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The paper size is hardcoded to A4. This may cause printing issues (e.g., "wrong paper size" errors) for users in regions where Letter is the standard. It is recommended to add a comment or make this configurable to improve usability across different regions.

Suggested change
if ! sudo "$LPADMIN_BIN" -p "$QUEUE_NAME" -o printer-is-shared=true -o media=A4 -o PageSize=A4 -o sides=one-sided; then
# Set queue defaults (Note: media is set to A4; change to 'Letter' if required)
if ! sudo "$LPADMIN_BIN" -p "$QUEUE_NAME" -o printer-is-shared=true -o media=A4 -o PageSize=A4 -o sides=one-sided; then

Comment on lines +418 to +425
def stable(p: Path) -> bool:
try:
s1 = p.stat().st_size
time.sleep(1)
s2 = p.stat().st_size
return s1 == s2 and s2 > 0
except FileNotFoundError:
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current stable function uses time.sleep(1) inside a loop that iterates over files. This causes a linear delay for every new file in the hotfolder, blocking the entire watcher process. A more efficient approach is to check if the file's modification time is old enough (e.g., > 2 seconds), which indicates the write operation has likely finished without blocking the loop execution.

Suggested change
def stable(p: Path) -> bool:
try:
s1 = p.stat().st_size
time.sleep(1)
s2 = p.stat().st_size
return s1 == s2 and s2 > 0
except FileNotFoundError:
return False
def stable(p: Path) -> bool:
try:
st = p.stat()
# File is stable if it hasn't been modified for at least 2 seconds and has content
return st.st_size > 0 and (time.time() - st.st_mtime) > 2
except FileNotFoundError:
return False

Comment on lines +682 to +693
require_command "lpinfo" "cups-client"
require_command "lpadmin" "cups-client"
require_command "lpstat" "cups-client"

LPINFO_BIN="$(resolve_bin lpinfo || true)"
LPADMIN_BIN="$(resolve_bin lpadmin || true)"
LPSTAT_BIN="$(resolve_bin lpstat || true)"
if [[ -z "$LPINFO_BIN" || -z "$LPADMIN_BIN" || -z "$LPSTAT_BIN" ]]; then
err "lpinfo/lpadmin/lpstat not resolvable in PATH or standard sbin/bin directories."
err "Try: sudo zypper install cups-client && reopen shell, then rerun."
exit 1
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For consistency and robustness, cupsctl should be checked and resolved to its full path like lpadmin and lpinfo. This ensures the script uses the correct binary and provides a clear error message if it's missing.

Suggested change
require_command "lpinfo" "cups-client"
require_command "lpadmin" "cups-client"
require_command "lpstat" "cups-client"
LPINFO_BIN="$(resolve_bin lpinfo || true)"
LPADMIN_BIN="$(resolve_bin lpadmin || true)"
LPSTAT_BIN="$(resolve_bin lpstat || true)"
if [[ -z "$LPINFO_BIN" || -z "$LPADMIN_BIN" || -z "$LPSTAT_BIN" ]]; then
err "lpinfo/lpadmin/lpstat not resolvable in PATH or standard sbin/bin directories."
err "Try: sudo zypper install cups-client && reopen shell, then rerun."
exit 1
fi
require_command "lpinfo" "cups-client"
require_command "lpadmin" "cups-client"
require_command "lpstat" "cups-client"
require_command "cupsctl" "cups"
LPINFO_BIN="$(resolve_bin lpinfo || true)"
LPADMIN_BIN="$(resolve_bin lpadmin || true)"
LPSTAT_BIN="$(resolve_bin lpstat || true)"
CUPSCTL_BIN="$(resolve_bin cupsctl || true)"
if [[ -z "$LPINFO_BIN" || -z "$LPADMIN_BIN" || -z "$LPSTAT_BIN" || -z "$CUPSCTL_BIN" ]]; then
err "lpinfo/lpadmin/lpstat/cupsctl not resolvable in PATH or standard sbin/bin directories."
err "Try: sudo zypper install cups cups-client && reopen shell, then rerun."
exit 1
fi

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b5072d2a47

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +321 to +323
if "$LPINFO_BIN" -m | awk '{print $1}' | grep -qx 'everywhere'; then
MODEL_NAME="everywhere"
warn "Falling back to 'everywhere' model."

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid selecting everywhere for directly attached USB URIs

When brlaser is unavailable, the fallback at this branch sets MODEL_NAME="everywhere", but this script always builds a local USB queue (direct usb://...) in detect_usb_uri. Per lpadmin(8), -m everywhere is intended to query an IPP device URI, so this fallback can cause queue creation to fail on exactly the recovery path meant to keep setup working (e.g., no Brother PPD available).

Useful? React with 👍 / 👎.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The --status flag likely never takes effect because STATUS_ONLY is declared local inside main while parse_args writes to the global variable; remove the local or pass the flag via return value/explicit parameter instead of relying on a global.
  • In print_summary, the line echo " "$LPINFO_BIN" -m | grep -Ei ... breaks quoting and will not print the intended command; wrap the whole command in a single set of quotes or escape the $ so it prints literally (e.g., echo " $LPINFO_BIN -m | grep -Ei '...'").
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `--status` flag likely never takes effect because `STATUS_ONLY` is declared `local` inside `main` while `parse_args` writes to the global variable; remove the `local` or pass the flag via return value/explicit parameter instead of relying on a global.
- In `print_summary`, the line `echo "  "$LPINFO_BIN" -m | grep -Ei ...` breaks quoting and will not print the intended command; wrap the whole command in a single set of quotes or escape the `$` so it prints literally (e.g., `echo "  $LPINFO_BIN -m | grep -Ei '...'"`).

## Individual Comments

### Comment 1
<location path="scripts/setup_hl_l1222_android_print.sh" line_range="677-686" />
<code_context>
+}
+
+main() {
+  local STATUS_ONLY="no"
+  parse_args "$@"
+
+  require_command "sudo" "sudo"
+  require_command "zypper" "zypper"
+  require_command "lpinfo" "cups-client"
+  require_command "lpadmin" "cups-client"
+  require_command "lpstat" "cups-client"
+
+  LPINFO_BIN="$(resolve_bin lpinfo || true)"
+  LPADMIN_BIN="$(resolve_bin lpadmin || true)"
+  LPSTAT_BIN="$(resolve_bin lpstat || true)"
+  if [[ -z "$LPINFO_BIN" || -z "$LPADMIN_BIN" || -z "$LPSTAT_BIN" ]]; then
+    err "lpinfo/lpadmin/lpstat not resolvable in PATH or standard sbin/bin directories."
+    err "Try: sudo zypper install cups-client && reopen shell, then rerun."
+    exit 1
+  fi
+
+  if [[ "${STATUS_ONLY:-no}" == "yes" ]]; then
+    status_report
+    exit 0
</code_context>
<issue_to_address>
**issue (bug_risk):** The --status flag never takes effect because STATUS_ONLY is local to main while parse_args writes a separate global variable.

Because `STATUS_ONLY` is declared `local` in `main`, `parse_args` only updates the global `STATUS_ONLY`, while the condition in `main` still reads the unchanged local value. Consider either making `STATUS_ONLY` global (remove `local` in `main`) or having `parse_args` return or set a different variable that `main` then uses to set its local `STATUS_ONLY`.
</issue_to_address>

### Comment 2
<location path="scripts/setup_hl_l1222_android_print.sh" line_range="662" />
<code_context>
+  echo "Validate:"
+  echo "  $LPSTAT_BIN -t"
+  echo "  lpinfo -v"
+  echo "  "$LPINFO_BIN" -m | grep -Ei 'brother|hl|brlaser|everywhere|generic|laser|mono|pcl'"
+  echo "  echo 'test page from pi' | lp -d ${QUEUE_NAME}"
+  echo
</code_context>
<issue_to_address>
**issue (bug_risk):** The quoting here makes the grep command execute instead of printing the literal command string.

Because the quotes are broken around `$LPINFO_BIN`, the shell interprets this as `echo /path/to/lpinfo -m | grep -Ei ...`, so `grep` actually runs instead of showing a copy/pasteable command. If you intend to display the command, wrap the whole pipeline in a single quoted string and interpolate `${LPINFO_BIN}`, for example:

```bash
echo "  ${LPINFO_BIN} -m | grep -Ei 'brother|hl|brlaser|everywhere|generic|laser|mono|pcl'"
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +677 to +686
local STATUS_ONLY="no"
parse_args "$@"

require_command "sudo" "sudo"
require_command "zypper" "zypper"
require_command "lpinfo" "cups-client"
require_command "lpadmin" "cups-client"
require_command "lpstat" "cups-client"

LPINFO_BIN="$(resolve_bin lpinfo || true)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The --status flag never takes effect because STATUS_ONLY is local to main while parse_args writes a separate global variable.

Because STATUS_ONLY is declared local in main, parse_args only updates the global STATUS_ONLY, while the condition in main still reads the unchanged local value. Consider either making STATUS_ONLY global (remove local in main) or having parse_args return or set a different variable that main then uses to set its local STATUS_ONLY.

echo "Validate:"
echo " $LPSTAT_BIN -t"
echo " lpinfo -v"
echo " "$LPINFO_BIN" -m | grep -Ei 'brother|hl|brlaser|everywhere|generic|laser|mono|pcl'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The quoting here makes the grep command execute instead of printing the literal command string.

Because the quotes are broken around $LPINFO_BIN, the shell interprets this as echo /path/to/lpinfo -m | grep -Ei ..., so grep actually runs instead of showing a copy/pasteable command. If you intend to display the command, wrap the whole pipeline in a single quoted string and interpolate ${LPINFO_BIN}, for example:

echo "  ${LPINFO_BIN} -m | grep -Ei 'brother|hl|brlaser|everywhere|generic|laser|mono|pcl'"

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 9 potential issues.

Open in Devin Review

exit 1
fi

TMP_DIR=""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Temp directories leaked: TMP_DIR="" clears tracking without removing the directory

In install_hotfolder_service (line 494) and install_upload_service_optional (line 643), TMP_DIR is set to "" after the temp directory's contents have been installed. However, the actual temp directory created by mktemp -d (scripts/setup_hl_l1222_android_print.sh:396 and scripts/setup_hl_l1222_android_print.sh:510) is never removed. The cleanup trap at scripts/setup_hl_l1222_android_print.sh:62-66 only acts when TMP_DIR is non-empty, so on successful script completion both temp directories remain as orphaned directories in /tmp.

Suggested change
TMP_DIR=""
rm -rf "$TMP_DIR"
TMP_DIR=""
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +572 to +573
safe = "".join(ch if ch.isalnum() or ch in "._-" else "_" for ch in name)[:160]
dst = hot / safe

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Upload filename truncation to 160 chars can silently drop file extension, causing hotfolder rejection

In the embedded upload server's do_POST, the sanitized filename is truncated to 160 characters at line 572: safe = "...".join(...)[:160]. If the original filename exceeds 160 chars, the extension (e.g. .pdf) is lost from safe. The file is then saved to disk without its extension. The hotfolder watcher (scripts/setup_hl_l1222_android_print.sh:433) checks f.suffix.lower() not in allowed and moves the extension-less file to failed/, even though the upload server reported success to the user at line 585. Note the inconsistency: the collision-resolution path at line 576 appends the original ext, but the initial non-collision path at line 573 uses safe directly with the truncated (extension-less) name.

Suggested change
safe = "".join(ch if ch.isalnum() or ch in "._-" else "_" for ch in name)[:160]
dst = hot / safe
safe = ".".join(ch if ch.isalnum() or ch in "._-" else "_" for ch in Path(name).stem)[:160 - len(ext)] + ext
dst = hot / safe
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

exit 1
fi

TMP_DIR=""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Second temp directory also leaked in install_upload_service_optional

Same issue as the hotfolder function: TMP_DIR="" at line 643 clears the tracking variable without removing the temp directory created at scripts/setup_hl_l1222_android_print.sh:510. This is the second instance of the same pattern — the actual rm -rf is missing before clearing the variable.

Suggested change
TMP_DIR=""
rm -rf "$TMP_DIR"
TMP_DIR=""
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +14 to +16
# Execution context:
# Run as a normal user from home directory (~/). Script uses sudo for
# privileged operations.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚩 Script execution context contradicts README instructions

The script's header comments at lines 15-16 say "Run as a normal user from home directory (~/)" and "Script uses sudo for privileged operations." However, the README instructs sudo bash scripts/setup_hl_l1222_android_print.sh (running the entire script as root). More importantly, several lpinfo calls (scripts/setup_hl_l1222_android_print.sh:294, scripts/setup_hl_l1222_android_print.sh:314, scripts/setup_hl_l1222_android_print.sh:321, scripts/setup_hl_l1222_android_print.sh:328) are invoked without sudo, while lpadmin calls at lines 346, 353, 358 use sudo. Since lpinfo typically requires root/lpadmin group, the script would fail if run as a normal user as the comments suggest. The README's sudo bash approach works, but the inconsistency is confusing and the script's own documentation is misleading.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

if not CGI_AVAILABLE:
self.send_body(503, "Upload disabled: Python cgi module not available (Python 3.13+).")
return
clen = int(self.headers.get("Content-Length", "0"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Upload server int(Content-Length) can raise ValueError on malformed requests

At scripts/setup_hl_l1222_android_print.sh:558, clen = int(self.headers.get("Content-Length", "0")) will raise ValueError if a client sends a non-numeric Content-Length header (e.g., Content-Length: abc). This was considered as a bug but dismissed because ThreadingHTTPServer handles each request in a separate thread, and BaseServer._handle_request_noblock catches unhandled exceptions via handle_error(), so the server continues running. The client receives a dropped connection rather than a proper 4xx error, which is suboptimal but not a crash.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +574 to +577
i = 1
while dst.exists():
dst = hot / f"{Path(safe).stem}_{i}{ext}"
i += 1

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Upload server TOCTOU race in filename collision resolution

At lines 574-577, the upload server checks dst.exists() and increments a counter to find a unique filename. With ThreadingHTTPServer, two concurrent uploads with the same filename could both pass the existence check simultaneously and write to the same path, causing one upload to silently overwrite the other. This was not reported as a bug because the upload service is optional, the race window is narrow, and concurrent uploads of identically-named files to a single-printer hotfolder is an unlikely scenario. However, if robustness is desired, using os.open with O_CREAT | O_EXCL would eliminate the race.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


while True:
for f in sorted(hot.iterdir()):
if f.is_dir() or f.name.startswith('.'):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Upload server dot-prefixed filenames silently accepted but never printed

If a user uploads a file whose sanitized name starts with . (e.g., .config.pdf), the upload server accepts it and reports success (scripts/setup_hl_l1222_android_print.sh:585), but the hotfolder watcher skips files starting with . (scripts/setup_hl_l1222_android_print.sh:430). The file would sit in the hotfolder indefinitely, never printed and never moved to failed/. This is an edge case since most real uploads won't have dot-prefixed names, but it creates a misleading user experience when it does occur.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +272 to +280
if ! sudo grep -qi '^WebInterface[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'WebInterface Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^Browsing[[:space:]]\+Yes' "${CUPSD_CONF}"; then
echo 'Browsing Yes' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi
if ! sudo grep -qi '^BrowseLocalProtocols[[:space:]]\+dnssd' "${CUPSD_CONF}"; then
echo 'BrowseLocalProtocols dnssd' | sudo tee -a "${CUPSD_CONF}" >/dev/null
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: CUPS config grep patterns may cause duplicate directives

At lines 272-279, the script checks for existing CUPS directives using grep -qi before appending. However, cupsctl --share-printers --remote-any (line 266) may set some of these directives with different formatting (e.g., different whitespace). The grep patterns use \+ (one-or-more spaces) which works with GNU grep's BRE, but if cupsctl writes e.g. BrowseLocalProtocols dnssd with two spaces, or the directive already exists with a different value like BrowseLocalProtocols dnssd cups, the grep could match or miss. If there's a mismatch, duplicate/conflicting directives could be appended. On the target platform (openSUSE with GNU grep), this is unlikely but worth noting.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

echo "Validate:"
echo " $LPSTAT_BIN -t"
echo " lpinfo -v"
echo " "$LPINFO_BIN" -m | grep -Ei 'brother|hl|brlaser|everywhere|generic|laser|mono|pcl'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Line 662 has unusual quoting style but works correctly by accident

The echo statement echo " "$LPINFO_BIN" -m | grep ..." at line 662 has the variable $LPINFO_BIN sitting between two double-quoted segments. This is technically unquoted variable expansion, but because the adjacent quoted segments form a single word and the variable contains a simple path without spaces, the output is correct. The intended form is likely echo " $LPINFO_BIN -m | grep ..." with the variable inside a single set of double quotes. Not flagged as a bug since the behavior is identical for standard system paths.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="README.md">

<violation number="1" location="README.md:29">
P3: This line documents model selection incorrectly: the script does not default to `everywhere`; it prefers `brlaser`/Brother models and only falls back to `everywhere`.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread README.md
- `ipp://<raspberrypi-ip>/printers/Brother_HL_L1222_V`

## Notes
- Script uses CUPS `everywhere` model as default for broad compatibility.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: This line documents model selection incorrectly: the script does not default to everywhere; it prefers brlaser/Brother models and only falls back to everywhere.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At README.md, line 29:

<comment>This line documents model selection incorrectly: the script does not default to `everywhere`; it prefers `brlaser`/Brother models and only falls back to `everywhere`.</comment>

<file context>
@@ -1,3 +1,30 @@
+   - `ipp://<raspberrypi-ip>/printers/Brother_HL_L1222_V`
+
+## Notes
+- Script uses CUPS `everywhere` model as default for broad compatibility.
+- If you have an official Brother HL-L1222-V Linux PPD/driver package, install it and adjust queue model if desired.
</file context>
Suggested change
- Script uses CUPS `everywhere` model as default for broad compatibility.
- Script prefers `brlaser`/Brother models when available, then falls back to CUPS `everywhere` for broad compatibility.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant