Skip to content

Support multiple package versions in repository #4

@lex57ukr

Description

@lex57ukr

Summary

Currently, the apt repository only maintains the latest version of each package in the Packages manifest. This means users cannot install older versions via apt install package=version even though the Cloudflare redirect functions can technically serve any version from GitHub Releases.

Current Behavior

  • update-repo.sh tracks only one version per package
  • The Packages file contains a single entry per package
  • Running apt install keystone-cli=0.1.9 fails because apt doesn't know that version exists

Proposed Solution

Add a previous_versions field to packages.yml to configure how many previous versions to maintain per package:

packages:
  - name: keystone-cli
    repo: knight-owl-dev/keystone-cli
    architectures:
      - amd64
      - arm64
    verify: keystone-cli info
    previous_versions: 3  # Keep 3 previous versions (4 total including current)

Configuration semantics:

  • 0 or omitted: Rolling release (current behavior) - only latest version
  • 1: Keep 1 previous version (2 total)
  • 2: Keep 2 previous versions (3 total)
  • 3: Keep 3 previous versions (4 total)
  • etc.

Explicit Version Behavior

When an explicit version is provided via CLI (e.g., ./scripts/update-repo.sh keystone-cli:0.1.9):

  • That version becomes the "current" version for the package
  • Previous versions are fetched from GitHub release history relative to the specified version

Example with previous_versions: 2 and keystone-cli:0.1.9:

  • Current: 0.1.9 (explicitly specified)
  • Previous: 0.1.8, 0.1.7 (releases before 0.1.9 in GitHub history)

This allows pinning to a specific version while still maintaining version history.

Implementation Considerations

1. Fetching Version History

The script will need to query GitHub for release history:

gh release list --repo knight-owl-dev/keystone-cli --limit N

This already works well (tested - 8 releases available for keystone-cli).

2. Packages File Format

Multiple versions would be represented as separate stanzas:

Package: keystone-cli
Version: 0.2.0
Filename: pool/main/k/keystone-cli/keystone-cli_0.2.0_amd64.deb
SHA256: abc123...

Package: keystone-cli
Version: 0.1.9
Filename: pool/main/k/keystone-cli/keystone-cli_0.1.9_amd64.deb
SHA256: def456...

This is standard apt format - multiple entries for the same package with different versions.

3. Checksum Strategy

For the current version: Download the .deb to extract control metadata and compute checksums (current behavior).

For previous versions: Trust the SHA256SUMS file from GitHub releases (required for all releases). This avoids re-downloading large .deb files for older versions.

  • Only include SHA256 in Packages entries for previous versions
  • MD5/SHA1 are deprecated and can be omitted
  • This keeps the implementation fast and bandwidth-efficient

Note: The control metadata (Package, Description, etc.) for previous versions can be extracted from the existing Packages file before regeneration, or we accept that previous versions only have minimal metadata.

4. Version Lifecycle

When a new release is published:

  1. Fetch latest N+1 versions from GitHub (or N+1 versions starting from explicit version)
  2. Download only the current .deb for full metadata extraction
  3. For previous versions, fetch SHA256 from their release checksums files
  4. Generate entries for all N+1 versions
  5. Oldest version naturally "falls off" when outside retention window

5. Edge Cases to Handle

  • Fewer releases than configured: If previous_versions: 5 but only 3 releases exist, include all 3
  • Pre-release versions: Should pre-releases (e.g., 1.0.0-beta.1) count toward the limit?
  • Yanked releases: How to handle if a release is deleted from GitHub?
  • Architecture mismatches: Older versions might not support all current architectures
  • Missing checksums file: Skip that version (checksums file is required)

6. Cloudflare Redirect Compatibility

The existing redirect functions already handle any version - no changes needed there. The semver regex pattern in [[path]].js will continue to work.

7. Storage Impact

  • No binary storage impact (still redirects to GitHub)
  • Packages file grows ~300-500 bytes per additional version entry
  • Packages.gz compression keeps this minimal

Suggested Implementation Steps

  1. Add previous_versions field to packages.yml schema with default of 0
  2. Update update-repo.sh to fetch version history from GitHub
  3. Add function to fetch SHA256 from release checksums file without downloading .deb
  4. Modify Packages file generation to output multiple stanzas
  5. Update lib/packages.sh to handle multi-version lookups
  6. Add tests for multi-version scenarios
  7. Update documentation (CLAUDE.md, packages.yml comments)

Questions to Resolve

  1. Should there be a global default, or is per-package configuration sufficient?
  2. Should pre-release versions be included in the count or handled separately?
  3. Is there a maximum reasonable limit (e.g., cap at 10 versions)?
  4. For previous versions, should we preserve full control metadata from prior Packages files, or is minimal metadata (Package, Version, Filename, SHA256, Architecture) sufficient?

Related Files

  • packages.yml - Configuration
  • scripts/update-repo.sh - Main update logic (lines 28, 250-303)
  • scripts/lib/packages.sh - Packages file parsing
  • dists/stable/main/binary-*/Packages - Generated manifests

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions