Skip to content

Conversation

Copy link

Copilot AI commented Jan 2, 2026

Extracts package selection logic from the build API into a standalone module, enabling independent package queries without triggering full ImageBuilder container operations.

Changes

New Module: asu/package_selection.py

Extracted functions from inline logic in build.py:

  • get_default_packages() - Parse ImageBuilder default packages
  • get_profile_packages() - Parse profile-specific packages
  • calculate_package_selection() - Apply diff_packages logic and package changes
  • validate_package_manifest() - Verify package versions

New API Endpoints: asu/routers/packages.py

  • POST /api/v1/packages/select - Calculate package selection without building
  • GET /api/v1/packages/defaults/{version}/{target}/{subtarget} - Query default packages
  • GET /api/v1/packages/profile/{version}/{target}/{subtarget}/{profile} - Query profile packages

Refactored: asu/build.py

Replaced 24 lines of inline regex parsing and package selection logic with calls to new module:

# Before: inline parsing and logic
default_packages = set(re.search(r"Default Packages: (.*)\n", stdout).group(1).split())
profile_packages = set(re.search(r"{}:\n.*Packages: (.*?)\n".format(profile), stdout).group(1).split())
apply_package_changes(build_request)
build_cmd_packages = build_request.packages
if build_request.diff_packages:
    build_cmd_packages = diff_packages(packages, default_packages | profile_packages)

# After: delegated to module
default_packages = get_default_packages(job.meta["stdout"])
profile_packages = get_profile_packages(job.meta["stdout"], build_request.profile)
build_cmd_packages = calculate_package_selection(build_request, default_packages, profile_packages)

Backward Compatibility

All existing /api/v1/build behavior unchanged - identical package selection logic, request/response formats, and error handling maintained.

Original prompt

Objective

Refactor the ASU codebase to separate package selection logic from the build API, enabling two independent services:

  1. Package Selection Service: Determines the right package selection for individual devices
  2. Build Service: Requests and builds specific images

The refactoring must maintain 100% backward compatibility with the current API - all existing endpoints, request/response formats, and behaviors must remain unchanged.

Current Architecture Analysis

The current implementation in asu/build.py handles both:

  • Package selection logic (lines 209-232): determining default packages, profile packages, and applying diff_packages
  • Image building: running ImageBuilder, manifest generation, and image creation

The API endpoint /api/v1/build (in asu/api.py or asu/routers/api.py) combines these operations.

Required Changes

1. Create New Package Selection Module (asu/package_selection.py)

Extract package selection logic into a new standalone module with the following functions:

def get_default_packages(version: str, target: str, subtarget: str) -> set[str]:
    """Get default packages for a target/subtarget"""
    
def get_profile_packages(version: str, target: str, subtarget: str, profile: str) -> set[str]:
    """Get profile-specific packages"""
    
def calculate_package_selection(
    build_request: BuildRequest,
    default_packages: set[str],
    profile_packages: set[str]
) -> list[str]:
    """Calculate final package selection based on request parameters
    
    Handles:
    - diff_packages logic
    - package additions/removals
    - package version constraints
    
    Returns list of packages formatted for build command
    """

def validate_package_manifest(
    manifest: dict[str, str],
    requested_versions: dict[str, str]
) -> Optional[str]:
    """Validate that manifest matches requested package versions
    
    Returns error message if validation fails, None if valid
    """

2. Create Package Selection API Router (asu/routers/packages.py)

Create a new API router with endpoints:

POST /api/v1/packages/select
- Input: version, target, profile, packages, diff_packages, packages_versions
- Output: {
    "default_packages": [...],
    "profile_packages": [...],
    "final_packages": [...],
    "packages_to_add": [...],
    "packages_to_remove": [...]
  }

GET /api/v1/packages/defaults/{version}/{target}/{subtarget}
- Output: {"packages": [...]}

GET /api/v1/packages/profile/{version}/{target}/{subtarget}/{profile}
- Output: {"packages": [...]}

3. Refactor Build Module (asu/build.py)

Modify the _build() function to:

  • Import and use the new package_selection module
  • Replace inline package selection logic (lines 209-232) with calls to calculate_package_selection()
  • Maintain identical behavior for backward compatibility
  • Keep all error handling and validation

4. Update API Router

If using newer router structure (asu/routers/api.py):

  • Register the new packages router
  • Ensure /api/v1/build continues to work identically

If using legacy structure (asu/api.py):

  • Add new package selection endpoints
  • Keep existing build endpoints unchanged

5. Add Tests

Create tests/test_package_selection.py:

  • Test package selection logic independently
  • Test new API endpoints
  • Verify backward compatibility with existing /api/v1/build tests

Update tests/test_api.py:

  • Ensure all existing tests pass unchanged
  • Add tests for new package selection endpoints

Constraints

  1. Zero Breaking Changes: All existing API endpoints must work identically
  2. Maintain Request/Response Format: No changes to existing JSON schemas
  3. Preserve Error Handling: Same error messages and status codes
  4. Keep OpenAPI Spec Compatible: Update asu/openapi.yml only to add new endpoints
  5. Service Independence: New package selection service should be callable independently, but integrated seamlessly into existing build flow

Implementation Notes

  • Use the existing BuildRequest class from asu/build_request.py
  • Reuse utility functions from asu/util.py (e.g., get_request_hash, parse_manifest, check_manifest)
  • Consider caching package information in Redis for performance
  • The package selection service should be stateless and not require container operations
  • Build service will continue to use containers and ImageBuilder as before

Success Criteria

  1. All existing tests pass without modification
  2. New package selection endpoints are functional
  3. /api/v1/build endpoint behavior is unchanged
  4. Package selection logic is extracted and reusable
  5. Both services can run independently (architecturally separated)
  6. Documentation updated in README.md explaining the new architecture

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits January 2, 2026 07:20
- Created asu/package_selection.py with extracted functions
- Created asu/routers/packages.py with new API endpoints
- Refactored asu/build.py to use new package_selection module
- Registered packages router in asu/main.py
- Added comprehensive tests in tests/test_package_selection.py
- All existing tests pass (100% backward compatibility)
- Code passes linting and formatting checks

Co-authored-by: aparcar <16000931+aparcar@users.noreply.github.com>
- Created tests/test_packages_api.py with 6 test cases
- Tests cover all new API endpoints (defaults, profile, select)
- Tests validate both success and error cases
- Fixed return type annotation on select endpoint
- All 88 tests pass successfully

Co-authored-by: aparcar <16000931+aparcar@users.noreply.github.com>
- Moved get_default_packages and get_profile_packages to top-level imports
- Removed all local imports inside functions
- All tests still pass (53 tests)
- CodeQL security scan: 0 vulnerabilities found

Co-authored-by: aparcar <16000931+aparcar@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor ASU codebase to separate package selection from build API Refactor package selection logic into independent module Jan 2, 2026
Copilot AI requested a review from aparcar January 2, 2026 07:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants