Fix macOS build, process leaks, extension handling, plug, and add CI #16

Merged
lambadalambda merged 14 commits from fix/homebrew-libmagic-paths into develop 2026-05-13 13:47:53 +00:00

Summary

Started as a fix for macOS Homebrew libmagic build paths, grew into a broader bugfix pass focused on issues that affect Pleroma in production.

Build & CI

  • Use pkg-config to discover libmagic paths (fixes macOS Homebrew build)
  • Remove ei_init() call that crashes on macOS with unlimited fd limit
  • Redirect C port stderr to /dev/null unless MAJIC_DEBUG is set
  • Add Woodpecker CI pipeline (lint + unit tests)

Process Leaks

  • Once.perform/3: clean up server process on error (try/after)
  • Pool.terminate_worker/3: stop server processes on pool shutdown

Extension.fix

  • Preserve directory paths in output (was stripping dir/ prefix)
  • Handle uppercase extensions correctly (.JPEG now matches .jpeg)
  • No trailing dot when no MIME extension candidates exist
  • Reduce cyclomatic complexity below credo threshold

Plug

  • Rewrite with recursive transform_uploads — old collect_uploads/put_in_if_exists broke on nested maps/arrays
  • Fall back to original upload on majic error instead of raising 500
  • Unskip plug test, enable fix_extension option

Server

  • Postpone reload/recycle calls in non-available states (was crashing)
  • Handle late {:data, _} in recycling state (was crashing)
  • Fix :eaccess:eacces typo in errno map
  • Guard terminate/3 against nil port
  • Fix Majic.perform once path ignoring :timeout option

Docs

  • Fix outdated function references in README
  • Update CHANGELOG with all fixes

Tests

  • Use plain text magic file instead of compiled .mgc (cross-version compatible)
  • 33 → 43 tests, all passing on macOS and Linux (Alpine)

Testing

  • mix test --trace passes on macOS (ARM64, Homebrew)
  • mix format --check-formatted passes
  • mix credo passes (0 issues)
  • Docker elixir:1.17-alpine full suite passes (43 tests, 0 failures)
## Summary Started as a fix for macOS Homebrew libmagic build paths, grew into a broader bugfix pass focused on issues that affect Pleroma in production. ### Build & CI - Use `pkg-config` to discover libmagic paths (fixes macOS Homebrew build) - Remove `ei_init()` call that crashes on macOS with unlimited fd limit - Redirect C port stderr to `/dev/null` unless `MAJIC_DEBUG` is set - Add Woodpecker CI pipeline (lint + unit tests) ### Process Leaks - `Once.perform/3`: clean up server process on error (try/after) - `Pool.terminate_worker/3`: stop server processes on pool shutdown ### Extension.fix - Preserve directory paths in output (was stripping `dir/` prefix) - Handle uppercase extensions correctly (`.JPEG` now matches `.jpeg`) - No trailing dot when no MIME extension candidates exist - Reduce cyclomatic complexity below credo threshold ### Plug - Rewrite with recursive `transform_uploads` — old `collect_uploads`/`put_in_if_exists` broke on nested maps/arrays - Fall back to original upload on majic error instead of raising 500 - Unskip plug test, enable `fix_extension` option ### Server - Postpone `reload`/`recycle` calls in non-available states (was crashing) - Handle late `{:data, _}` in recycling state (was crashing) - Fix `:eaccess` → `:eacces` typo in errno map - Guard `terminate/3` against nil port - Fix `Majic.perform` once path ignoring `:timeout` option ### Docs - Fix outdated function references in README - Update CHANGELOG with all fixes ### Tests - Use plain text magic file instead of compiled `.mgc` (cross-version compatible) - 33 → 43 tests, all passing on macOS and Linux (Alpine) ## Testing - `mix test --trace` passes on macOS (ARM64, Homebrew) - `mix format --check-formatted` passes - `mix credo` passes (0 issues) - Docker `elixir:1.17-alpine` full suite passes (43 tests, 0 failures)
- Remove ei_init() call that fails when sysconf(_SC_OPEN_MAX) returns
  LONG_MAX (unlimited fd limit on macOS), causing int overflow in
  Erlang's init_socket_info(). The port only uses ei for
  encoding/decoding, not node connections.
- Redirect port stderr to /dev/null unless MAJIC_DEBUG is set, to
  suppress libmagic warnings in test output.
- Compile test .mgc with matching file binary from Homebrew file-formula
  to avoid version mismatch with the linked libmagic.
- Increase port test receive timeout to accommodate slower port startup.
- Broaden .gitignore to cover /priv/ and *.mgc.
- Fix Once.perform/3 server process leak on error (try/after)
- Fix Pool.terminate_worker/3 not stopping worker servers
- Fix server timeouts using bare integers instead of :state_timeout
- Postpone reload/recycle in non-available states
- Guard Server.terminate/3 against nil port
- Validate Server.perform/3 input, return {:error, :badarg}
- Fix :eaccess -> :eacces errno mapping
- Fix C atom buffer too small (128 -> MAXATOMLEN)
- Fix C fdseek mixing read(2) and getchar()
- Fix C header read robustness (read_exact, unsigned char casts)
- Check C ei_get_type return values
- Free C ei_x_buff on reload early return
- Add NULL checks for magic_file/magic_buffer return values
- Fix Extension.fix/3 stripping directory paths
- Fix Extension.fix/3 mishandling uppercase extensions
- Fix Extension.fix/3 trailing dot with no candidates
- Fix Majic.perform once:true ignoring :timeout option
- Rewrite Majic.Plug with recursive transformation
- Enable previously skipped Plug test
- Add new tests for process leaks, invalid input, oversized packets
Path.join('.', x) prepends './' so use dir != '.' guard.
Use Path.basename(name) for append branches to avoid including
directory in the filename portion being appended to.
Processing state now handles :exit_status and :closed port messages,
replying with {:error, :port_exit} / {:error, :port_closed} and
transitioning to :starting. Recycling state now postpones late
{:data, _} messages instead of crashing.
Catch :exit {:timeout, _} from NimblePool.checkout!/3 and return
{:error, :timeout} instead of crashing the caller.
Libmagic handles directories gracefully (returns inode/directory)
but some platforms may still return errno 21, so map it defensively.
magic_error() can return NULL; guard against strlen(NULL) by encoding
'unknown' atom when error message is unavailable. Also fix
process_command_bytes to check ei_get_type return value instead of
using EI_ENSURE which would exit the port process.
Only transform body_params, then merge with query_params to build
params. This avoids calling Majic.perform on the same upload file
twice (once via body_params, once via the merged params map).
lambadalambda force-pushed fix/homebrew-libmagic-paths from be0dd96941 to fc689178ab 2026-05-13 08:33:27 +00:00 Compare
Calling reload or recycle during processing/starting/loading/recycling
previously crashed the server. Now these calls are postponed until the
server returns to :available. Also discard late {:data, _} messages in
recycling state instead of crashing. Fix :eaccess -> :eacces typo and
add is_port guard in terminate/3.
When Majic.perform fails (e.g. temp file deleted, permission denied,
pool timeout), return the original upload unchanged instead of raising
a 500 error.
Lint pipeline: mix format --check-formatted, credo
Unit test pipeline: mix test --trace --preload-modules
Both use elixir:1.17-alpine with file-dev for libmagic headers.
Fix outdated references in README and update CHANGELOG
Some checks failed
ci/woodpecker/pr/lint Pipeline failed
ci/woodpecker/pr/unit-testing unknown status
dc8f9d47dc
README: fix function names (Once.perform/3, start_option/0),
remove references to nonexistent Majic.Helpers.perform_once/2,
clarify Plug fix_extension is opt-in, fix recycle_threshold typo.

CHANGELOG: remove nonexistent Majic.compile/2, add Fixed section
with all bug fixes, add macOS/Homebrew build support and Woodpecker CI.
Fix format and credo issues for CI
Some checks failed
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/unit-testing Pipeline failed
a8cbe3c4a1
- Run mix format on all files
- Reduce Extension.do_fix cyclomatic complexity below 9
  by extracting fix_existing_ext and fix_no_ext helpers
- Remove unused has_candidates? variable
Fix CI: add file package, use plain text magic file instead of .mgc
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/unit-testing Pipeline was successful
aac3cb655f
- Add 'file' to apk packages (needed for C port, not just file-dev)
- Clean stale macOS binary before compile on CI
- Use plain text magic file directly instead of compiled .mgc
  (.mgc format is version-dependent and breaks across different
  libmagic versions)
lambadalambda changed title from Use pkg-config to discover libmagic paths to Fix macOS build, process leaks, extension handling, plug, and add CI 2026-05-13 12:26:00 +00:00
Fix README: reference Majic.perform with once: true, not Once.perform directly
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/unit-testing Pipeline was successful
8742cc7f70
lambadalambda force-pushed fix/homebrew-libmagic-paths from 8742cc7f70
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/unit-testing Pipeline was successful
to dbd654d0e6
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/unit-testing Pipeline was successful
2026-05-13 12:30:10 +00:00
Compare
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pleroma-elixir-libraries/majic!16
No description provided.