Add vendor directory layout #34

Open
opened 2026-01-03 23:40:29 +00:00 by KofTwentyTwo · 0 comments
KofTwentyTwo commented 2026-01-03 23:40:29 +00:00 (Migrated from github.com)

Part of #11

User Story

As a developer, I want packages downloaded to a consistent vendor directory so I can commit them or inspect them locally.

Design

Directory Structure

project/
├── qbits.lock                      # Lockfile (project root)
└── vendor/
    └── qbits/
        ├── @qrun/
        │   ├── auth-sso/
        │   │   ├── 2.0.1/
        │   │   │   ├── manifest.yaml
        │   │   │   ├── src/
        │   │   │   └── ...
        │   │   └── .current -> 2.0.1   # Symlink to active version
        │   └── reports/
        │       └── 1.3.0/
        │           └── ...
        └── .integrity                  # SHA-512 checksums

Key Design Decisions

Decision Rationale
Scoped directories (@qrun/) Match npm convention, avoid collisions
Version subdirectories Allow multiple versions during transition
.current symlink Quick access to active version
.integrity file Fast verification without parsing lockfile

Vendor Operations

Operation Description
download(pkg, version, url) Fetch and extract to vendor dir
remove(pkg) Delete package directory
exists(pkg, version) Check if already vendored
clean() Remove packages not in lockfile

Files to Create

File Description
qctl-qbit/src/main/java/io/qrun/qctl/qbit/vendor/VendorLayout.java Path calculation and directory management
qctl-qbit/src/main/java/io/qrun/qctl/qbit/vendor/PackageDownloader.java HTTP download and extraction

Download Flow

  1. Calculate target path: vendor/qbits/@scope/name/version/
  2. Create parent directories
  3. Download tarball to temp file
  4. Verify integrity (SHA-512) before extraction
  5. Extract to target path
  6. Update .current symlink
  7. Update .integrity file

Implementation Tasks

  • Create VendorLayout class for path calculations
  • Implement getPackagePath(name, version) method
  • Implement getCurrentPath(name) for symlink resolution
  • Create PackageDownloader for HTTP downloads
  • Implement tarball extraction (tar.gz support)
  • Implement .current symlink management
  • Implement .integrity file updates
  • Add clean() method to remove orphaned packages
  • Handle Windows (no symlinks - use .current file instead)
  • Add unit tests for path calculations
  • Add integration test for download flow

Acceptance Criteria

  • Packages download to correct nested path
  • Scoped packages use @scope/name structure
  • .current symlink points to active version
  • .integrity file contains all checksums
  • Works on macOS, Linux, and Windows
  • Existing packages are not re-downloaded
Part of #11 ## User Story As a developer, I want packages downloaded to a consistent vendor directory so I can commit them or inspect them locally. ## Design ### Directory Structure ``` project/ ├── qbits.lock # Lockfile (project root) └── vendor/ └── qbits/ ├── @qrun/ │ ├── auth-sso/ │ │ ├── 2.0.1/ │ │ │ ├── manifest.yaml │ │ │ ├── src/ │ │ │ └── ... │ │ └── .current -> 2.0.1 # Symlink to active version │ └── reports/ │ └── 1.3.0/ │ └── ... └── .integrity # SHA-512 checksums ``` ### Key Design Decisions | Decision | Rationale | |----------|-----------| | Scoped directories (`@qrun/`) | Match npm convention, avoid collisions | | Version subdirectories | Allow multiple versions during transition | | `.current` symlink | Quick access to active version | | `.integrity` file | Fast verification without parsing lockfile | ### Vendor Operations | Operation | Description | |-----------|-------------| | `download(pkg, version, url)` | Fetch and extract to vendor dir | | `remove(pkg)` | Delete package directory | | `exists(pkg, version)` | Check if already vendored | | `clean()` | Remove packages not in lockfile | ### Files to Create | File | Description | |------|-------------| | `qctl-qbit/src/main/java/io/qrun/qctl/qbit/vendor/VendorLayout.java` | Path calculation and directory management | | `qctl-qbit/src/main/java/io/qrun/qctl/qbit/vendor/PackageDownloader.java` | HTTP download and extraction | ### Download Flow 1. Calculate target path: `vendor/qbits/@scope/name/version/` 2. Create parent directories 3. Download tarball to temp file 4. Verify integrity (SHA-512) before extraction 5. Extract to target path 6. Update `.current` symlink 7. Update `.integrity` file ## Implementation Tasks - [ ] Create `VendorLayout` class for path calculations - [ ] Implement `getPackagePath(name, version)` method - [ ] Implement `getCurrentPath(name)` for symlink resolution - [ ] Create `PackageDownloader` for HTTP downloads - [ ] Implement tarball extraction (tar.gz support) - [ ] Implement `.current` symlink management - [ ] Implement `.integrity` file updates - [ ] Add `clean()` method to remove orphaned packages - [ ] Handle Windows (no symlinks - use .current file instead) - [ ] Add unit tests for path calculations - [ ] Add integration test for download flow ## Acceptance Criteria - [ ] Packages download to correct nested path - [ ] Scoped packages use `@scope/name` structure - [ ] `.current` symlink points to active version - [ ] `.integrity` file contains all checksums - [ ] Works on macOS, Linux, and Windows - [ ] Existing packages are not re-downloaded
Sign in to join this conversation.