apt-layer/refactor.md

6.8 KiB

apt-layer C Refactor Plan

If you want to turn apt-layer.sh into a C binary, here's a practical, staged approach:


1. Clarify the Scope

  • Current: Build-time tool for adding packages during image creation (not live/transactional)
  • Future: End-user transactional package layering (like rpm-ostree install)
  • Platforms: Linux only initially, Debian/Ubuntu-specific due to apt integration
  • Note: Deep apt integration makes portability to other package managers a major rewrite

2. Current Shell Script Analysis

  • OSTree repo management: Creating commits, managing branches
  • Package installation: Using apt/apt-get in chroot/container
  • Container integration: Podman/Docker for isolated builds
  • Recipe parsing: YAML/JSON layer definitions
  • Logging and error handling: Progress reporting and failure recovery

Break down into:

  • Shell glue (calling external tools)
  • Logic (parsing, validation, error handling)
  • User interaction (progress reporting, prompts)

3. Design the C Program

Phase 1: Build-Time Tool (MVP)

  • CLI interface: getopt or argp for argument parsing
  • Subcommands: create, list, info, export
  • System calls: Use fork/exec (not system()) for better control and security
  • Logging: Simple file logging or syslog
  • Config/Recipes: YAML/JSON parser library

Phase 2: End-User Layering (Future)

  • Transactional commands: install, remove, rollback, status
  • Live system integration: OSTree admin/deploy operations with atomic commits
  • User session handling: Prompts, reboots, error recovery
  • History management: Layer tracking and rollback support
  • Critical: Deep libapt-pkg integration for reliable package management

4. Incremental Porting Strategy

  • MVP: C program that wraps shell logic (calls ostree, apt, etc.)
  • Refactor: Gradually replace shell-outs with native C code
  • Testing: Ensure C binary produces same results as shell script
  • Transition: Keep shell script as fallback during development
  • Phase 2 Prerequisite: libapt-pkg integration before attempting live layering

5. Libraries to Consider

Essential Libraries

  • Argument parsing: argp, getopt_long
  • YAML/JSON parsing: libyaml, jansson
  • Process management: fork, execvp, popen
  • Logging: syslog or custom implementation
  • OSTree integration: libostree (avoid shelling out)

Critical for Phase 2

  • libapt-pkg: ESSENTIAL for client-side layering - not just an enhancement
  • libdpkg: Alternative package management interface
  • Container integration: Shell out to podman/docker initially

6. Project Structure

apt-layer/
├── src/
│   ├── main.c              # Entry point, CLI parsing
│   ├── cli.c / cli.h       # Command line interface
│   ├── layer.c / layer.h   # Layer creation logic
│   ├── ostree.c / ostree.h # OSTree operations
│   ├── apt.c / apt.h       # Package management (shell-out initially)
│   ├── apt_pkg.c / apt_pkg.h # libapt-pkg integration (Phase 2)
│   ├── config.c / config.h # Configuration/recipe parsing
│   ├── log.c / log.h       # Logging system
│   ├── container.c / container.h # Container integration
│   └── transaction.c / transaction.h # Transactional operations (Phase 2)
├── include/
├── tests/
├── Makefile
├── README.md
└── LICENSE

7. Development Steps

Phase 1: Build-Time Tool

  1. Scaffold CLI: Parse arguments, print help/version
  2. Implement shell-out logic: Use fork/exec for external tool calls
  3. Add config/recipe parsing: Use library for YAML/JSON
  4. Implement core logic: Layer creation, listing, info
  5. Replace shell-outs with native code: Use libraries where possible
  6. Testing: Write tests for each feature

Phase 2: End-User Layering (Future)

  1. Integrate libapt-pkg: CRITICAL - implement dependency resolution and package operations
  2. Design transactional data structures: Layer history, rollback info
  3. Implement live system integration: OSTree admin operations with atomic commits
  4. Add user interaction: Prompts, confirmations, progress
  5. Implement rollback system: History tracking and recovery
  6. Add TUI (optional): Curses-based interface

8. Command Structure

Build-Time Commands (Phase 1)

apt-layer create <base-branch> <new-branch> [packages...]
apt-layer list [branch-pattern]
apt-layer info <branch>
apt-layer export <branch> <image-name>
apt-layer --recipe <file>

End-User Commands (Phase 2)

apt-layer install <packages...>
apt-layer remove <packages...>
apt-layer rollback [commit]
apt-layer status
apt-layer history

9. Critical Technical Challenges

Phase 2 Complexity

  • Atomic Operations: Ensuring package installation and OSTree commit happen atomically
  • Dependency Resolution: Using libapt-pkg for reliable dependency handling
  • State Management: Tracking package state across OSTree commits
  • Failure Recovery: Handling failed installations without breaking the system
  • Performance: Making client-side operations fast enough for interactive use

libapt-pkg Integration

  • Not Optional: Shelling out to apt-get for client-side layering will be brittle and slow
  • Complexity: Direct integration with apt's internals is challenging but necessary
  • Benefits: Reliable dependency resolution, state management, and immutability guarantees

10. Long-Term Enhancements

  • Use libostree for direct repo manipulation
  • Use libapt-pkg for package management (Phase 2 requirement)
  • Add TUI with ncurses
  • Add parallelism and better error handling
  • Support for remote repositories
  • Layer signing and verification

11. Transitional Strategy

  • Keep shell script as fallback for unimplemented features
  • Gradually phase out shell script as C code matures
  • Maintain compatibility with existing build systems
  • Test both build-time and user-time scenarios
  • Phase 2 milestone: Must have libapt-pkg integration before attempting live layering

Summary:
Start with build-time tool in C, then incrementally add end-user layering features. The integration with libapt-pkg is critical for Phase 2 success - it's not just an enhancement but a fundamental requirement for achieving true rpm-ostree-like behavior.

Next Steps:

  1. Create basic CLI skeleton
  2. Implement build-time layer creation
  3. Add recipe support
  4. Research and prototype libapt-pkg integration
  5. Prepare for transactional layering

Success Criteria:

  • Phase 1: C binary that replicates current shell script functionality
  • Phase 2: Reliable client-side package layering with atomic operations and rollback support