sps

repository·main·Indexed 23 days ago

https://github.com/alexykn/sps

A Rust-powered package manager for macOS ARM architecture, inspired by Homebrew. It manages Formulae (CLI tools, libraries, languages) and Casks (desktop applications). The project includes the sps-common crate, which provides functionality for dependency resolution via DependencyResolver, cache management using the Cache struct, and installed package tracking through the KegRegistry.

Tokens
16.9K
Snippets
40
Records
110
Agent score
83%

What's inside sps

  1. Build sps from source

    main

    To build the sps binary from the source repository, ensure you have a stable Rust toolchain installed. After building, the binary will be located in target/release/sps. You should add this path to your PATH environment variable to use the command globally.

    git clone <repo-url>
    cd sps
    cargo build --release
  2. Manage packages with the sps CLI

    main

    The sps CLI allows you to manage Formulae (command-line tools, libraries, languages) and Casks (macOS desktop applications).

    Note: Currently, sps supports ARM only.

  3. Install sps

    main

    You can install sps using cargo install. Note that the crates.io version may not always reflect the absolute latest changes due to update frequency.

    cargo install sps
  4. Use the latest nightly build

    main

    To use the most recent nightly builds, download the artifacts from the GitHub Actions workflow (actions/workflows/rust.yml). Select a successful build, download the artifact, extract it, and run the binary directly.

    ./sps --help
  5. Configure logging verbosity

    main

    You can control the amount of output and logging detail using the --verbose flag (or its equivalent in the CLI arguments). The verbosity levels affect both the console output and the log files written to the configured log directory.

    • Level 0 (Default): INFO level logging.
    • Level 1: DEBUG level logging.
    • Level 2+: TRACE level logging.

    When verbosity is greater than 0, logs are also written to a rolling daily file named sps.log in the configured logs directory.

  6. Understand the job lifecycle with JobProcessingState

    main

    A job in the sps pipeline moves through several states. Understanding these states is essential for implementing custom runners or monitoring tools:

    1. PendingDownload: Waiting for the download to start.
    2. Downloading: Download is currently in progress.
    3. Downloaded(PathBuf): Download finished; the artifact is at the provided path.
    4. WaitingForDependencies(PathBuf): Artifact is ready, but waiting for required dependencies to succeed.
    5. DispatchedToCore(PathBuf): Job has been sent to the core worker pool.
    6. Installing(PathBuf): Installation/processing is actively being performed by a worker.
    7. Succeeded: The job completed successfully.
    8. Failed(Arc<SpsError>): The job failed with the provided error.
    pub enum JobProcessingState {
        PendingDownload,
        Downloading,
        Downloaded(PathBuf),
        WaitingForDependencies(PathBuf),
        DispatchedToCore(PathBuf),
        Installing(PathBuf),
        Succeeded,
        Failed(Arc<SpsError>),
    }
  7. How dependency success is determined in the pipeline

    main

    The pipeline execution engine determines if a target (Formula or Cask) can proceed by checking its dependencies against two sources: the current job_states and the ResolvedGraph.

    To return true (success), every dependency must meet one of these criteria:

    1. Active Success: The dependency exists in job_states with the state JobProcessingState::Succeeded.
    2. Pre-installed: The dependency is not currently being processed but is found in the ResolvedGraph with a ResolutionStatus::Installed status.

    If a dependency is in a Failed state, or if it is neither succeeded nor pre-installed, the target's dependency check fails.

  8. Understand NodeInstallStrategy and how it affects dependencies

    main

    The NodeInstallStrategy determines the installation method for a formula and influences how its dependencies are resolved.

    Strategies

    • BottlePreferred: Attempts to use a pre-built bottle if available via has_bottle_for_current_platform. If no bottle is available, it falls back to SourceOnly.
    • SourceOnly: Always builds from source.
    • BottleOrFail: Attempts to use a bottle; if no bottle is available, the resolution fails.

    Dependency Cascading Logic

    When a parent formula is resolved with a specific strategy, it affects its children via should_process_dependency_edge:

    • If parent is BottlePreferred or BottleOrFail: Pure BUILD dependencies (those that do not intersect with RUNTIME, RECOMMENDED, or OPTIONAL tags) are skipped. This is because a bottle-installed package typically does not require its build-time dependencies to be present in the runtime environment.
    • If parent is SourceOnly: All relevant dependencies (based on global context filters) are processed.
  9. How dictionary artifacts are installed

    main

    When a Cask contains a dictionary stanza in its artifacts definition, sps installs it by moving the declared .dictionary bundles from the staging area to the standard macOS user dictionary directory: ~/Library/Dictionaries.

    To maintain traceability within the Caskroom, sps also creates a symlink in the cask_version_install_path that points back to the bundle in the user's Library directory. This behavior mimics the Homebrew Ruby definition class Dictionary < Moved; end.

  10. OCI Authentication Configuration

    main

    The OCI utilities determine authentication based on the provided Config object. The priority for authentication is:

    1. Explicit Bearer Token: If config.docker_registry_token is set, it uses Authorization: Bearer <token>.
    2. Basic Auth: If config.docker_registry_basic_auth is set, it uses Authorization: Basic <encoded_string>.
    3. Anonymous Bearer Token: If the registry is ghcr.io (or the default domain) and no explicit credentials are provided, the client attempts to fetch an anonymous token from the registry's token endpoint (e.g., https://ghcr.io/token) for the specific repository scope.
    4. Unauthenticated: If all above fail, it proceeds without an Authorization header.
  11. How app upgrades preserve identity on macOS

    main

    When upgrading a macOS application, sps avoids the standard 'remove and symlink' pattern used for fresh installs. Instead, it employs a Direct Overwrite Strategy (similar to Homebrew):

    1. Why: Removing a symlink and replacing it with a new one breaks the application's established identity in the eyes of macOS. This can trigger Gatekeeper resets, lose quarantine exemptions, and potentially disrupt access to user data in ~/Library.
    2. How:
      • The old app in /Applications is removed via sudo rm -rf.
      • The new app is copied from the private store to /Applications using sudo cp -pR.
    3. Result: The -p flag preserves file attributes, ownership, and timestamps, and the -R flag ensures recursive copying. This maintains the app's security context and Gatekeeper approval status.