# Swift Package Registry

import { Aside } from '@astrojs/starlight/components';

Avrea runs a [Swift package registry](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0292-package-registry-service.md)
that caches public and eligible organization-private Swift packages hosted on
GitHub. SwiftPM can resolve semantic-version dependencies as checksummed source
archives served from colocated storage instead of running a full `git clone`
for each dependency.

Public package source is shared across Avrea customers. Private package source
and metadata remain isolated to the organization that is authorized to access
the corresponding GitHub repository.

The registry caches package source, not compiled output. [Xcode Compilation
Cache](/cache/build-cache/xcode/) reuses compiler results, while [Tuist Module
Cache](/cache/build-cache/tuist/) substitutes complete prebuilt targets.

## Enable the registry

Public registry configuration is **on by default**. SwiftPM and Tuist commands
opt into replacing Git dependencies as described below. Private package
resolution also requires an explicit opt-in. Both require the **Package
Manager Cache** and **Swift Package Registry** settings to remain enabled.

Enable private packages for an organization or repository in the
[console](https://console.avrea.com), or via the CLI:

```bash
avr settings set cache.swift-private-registry.enabled true
```

The setting applies to new jobs. A repository-level override takes precedence
over the organization setting.

## Xcode projects

On macOS runners with Xcode 26 or later, Avrea configures the package registry
before workflow steps run. When Avrea creates the native registry configuration,
it also enables Xcode's source-control-to-registry transformation. Eligible
version-based Git dependencies then resolve through the registry. Branch and
revision dependencies continue to download through Git.

[Xcode Compilation Cache](/cache/build-cache/xcode/) separately reuses compiler
output without requiring registry transformation.

### Local packages and registry compatibility

If resolution fails with `invalid package type registry <identity>`, check the
`swift-tools-version` declaration in every local root package loaded by the
Xcode workspace. SwiftPM selects the lockfile format using the lowest root
tools version. Versions below 5.6 select the legacy format, which cannot store
registry dependencies. Update those local manifests to at least 5.6:

```swift title="Package.swift"
// swift-tools-version:5.6
```

If a branch or revision dependency then reports a missing product, give its
dependency declaration an explicit `name` matching the product's `package`
reference. Registry transformation changes the dependency identity while
retaining Git fetching; the explicit name preserves product lookup:

```swift
.package(
    name: "MetaTextKit",
    url: "https://github.com/mastodon/MetaTextKit.git",
    branch: "2.2.5-xcode16"
)
// The target's product reference stays unchanged:
.product(name: "MetaTextKit", package: "MetaTextKit")
```

The same applies to dependencies declared with `revision:`.

## Tuist and SwiftPM CLI

The SwiftPM CLI does not use Xcode's transformation preference. Commands such
as `tuist install`, `swift package resolve`, and `swift build` need the
`--replace-scm-with-registry` option to replace eligible GitHub URL
dependencies with registry downloads.

Avrea configures its registry as the default before workflow steps run. The
same managed configuration is available to native SwiftPM and embedded
SwiftPM clients such as Tuist. Normal workflows do not need to run
`swift package-registry set` or associate individual public scopes.

### Resolve Tuist dependencies

Commit `Tuist/Package.resolved` and run:

```bash
tuist install \
  --replace-scm-with-registry \
  --force-resolved-versions
```

`--replace-scm-with-registry` asks SwiftPM to replace eligible source-control
dependencies. `--force-resolved-versions` uses the committed versions and fails
if the manifest and lockfile disagree.

To make replacement part of the Tuist project configuration, pass the SwiftPM
option through `installOptions`:

```swift title="Tuist.swift"
import ProjectDescription

let tuist = Tuist(
    fullHandle: "my-organization/my-project",
    project: .tuist(
        installOptions: .options(
            passthroughSwiftPackageManagerArguments: [
                "--replace-scm-with-registry"
            ]
        )
    )
)
```

Alternatively, declare a package by registry identity when it must always use
the registry:

```swift title="Tuist/Package.swift"
dependencies: [
    .package(id: "apple.swift-log", exact: "1.6.4")
]
```

Direct identities do not fall back to Git. Avrea's managed default resolves
eligible public identities. Resolution fails if customer-managed configuration
does not provide a registry for the identity.

### Dependency behavior

The following combinations were validated with `tuist install` on Avrea macOS
runners:

| Package declaration | Additional configuration | Result |
|---|---|---|
| Public identity such as `apple.swift-log` with an exact version | None | Avrea public registry |
| Public GitHub URL with an exact semantic version | Pass `--replace-scm-with-registry` | Avrea public registry |
| Public GitHub URL with an exact semantic version, without the replacement option | None | Git |
| GitHub URL with `branch: "main"` | None | Git |
| GitHub URL with a commit hash in `revision` | None | Git |
| GitHub URL with `revision: "1.6.4"` | None | Git tag or ref, not a registry version |
| Eligible private GitHub URL with an exact semantic version | Enable **Private Swift Packages** and pass `--replace-scm-with-registry` | Organization-isolated private registry |

A dependency graph can mix registry and Git dependencies. Only eligible
semantic-version packages move to the registry.

### Tuist's installed dependency state

`tuist install` stores dependency checkouts, registry downloads, extracted
source, binary artifacts, plugins, macros, and SwiftPM workspace state under
`Tuist/.build`. It can contain complete private dependency source. The [Tuist
Module Cache guide](/cache/build-cache/tuist/#optional-accelerate-package-installation)
shows how to store this state in the repository-scoped GitHub Actions cache and
use it in main-branch warming and pull-request jobs.

## How registry resolution works

Package identities map to GitHub repositories. The package at
`github.com/apple/swift-numerics` has the public registry identity
`apple.swift-numerics`. The first time a version is requested, Avrea fetches
the tagged source from GitHub, creates a stable source archive, and caches it.
Later requests for that version receive the same archive and checksum from
colocated storage.

When **Private Swift Packages** is enabled, eligible private packages can keep
their existing `https://github.com/{owner}/{repository}` declaration. Avrea
selects the organization's private mirror behind the scenes. No GitHub or
Avrea service credential is added to the runner VM.

Branch and commit requirements, private repositories outside the
organization's GitHub App installation, unsupported origins, and packages that
cannot be represented by a registry identity continue to use Git.

## Package.resolved

When a dependency resolves through the registry, `Package.resolved` records a
registry identity and archive checksum instead of a Git URL and commit. If the
checked-out lockfile contains eligible Git-form pins, CI may rewrite them in
the runner's ephemeral working tree while preserving the selected versions.
The committed file is unaffected unless the workflow explicitly persists it.

If workflows diff or hash-key `Package.resolved`, keep the registry behavior
consistent across the repository so pins do not alternate between Git and
registry forms. On a developer machine, local SwiftPM can continue resolving
the original GitHub URL with the developer's own credentials.

If tooling requires `Package.resolved` to remain byte-for-byte unchanged
during CI, disable registry resolution:

```bash
avr settings set cache.swift-registry.enabled false
```

## Faster resolution in CI

For a regular Swift package, commit `Package.resolved` and resolve with:

```bash
swift package resolve \
  --replace-scm-with-registry \
  --force-resolved-versions
```

For Xcode projects, the strict resolved-version equivalent is
`xcodebuild -disableAutomaticPackageResolution`.

The effect is largest on a fresh runner. Resolving Vapor's 28-package
dependency graph produced:

| Setup | Resolve time |
|---|---:|
| Git, no lockfile | 23s |
| Registry, no lockfile | 11s |
| Git, committed lockfile and `--force-resolved-versions` | 12s |
| Registry, committed lockfile and `--force-resolved-versions` | 4s |

## Requirements

- **macOS runner with Xcode 26 or later**: registry resolution relies on
  current SwiftPM and Xcode support. Older Xcode versions continue to use Git.
- **GitHub-hosted dependencies**: public repositories are eligible. Private
  packages additionally require **Private Swift Packages**, an Avrea GitHub App
  installation that covers the organization, and an available private mirror.
- **Semantic-version requirements**: branch and revision dependencies continue
  to use Git and the workflow's existing credentials.

<Aside>
The `cache.avrea.com` registry URL resolves only inside Avrea runners. It is
not reachable from a developer laptop, so local builds use the dependency's
original GitHub location and the developer's own credentials.
</Aside>

## Custom registry configuration

Avrea preserves a `registries.json` supplied by the workflow or runner image.
This customer-owned file takes precedence over Avrea's managed configuration
for the SwiftPM client that reads it. Normal workflows should remove the
custom file and use the managed configuration.

If the custom configuration is intentional, associate its default registry
and the reserved private scope with Avrea:

```bash
swift package-registry set --global \
  https://cache.avrea.com:8443/swift

swift package-registry set --global \
  --scope avrea-com \
  https://cache.avrea.com:8443/swift
```

The `avrea-com` association selects Avrea's private package namespace; it does
not grant access. Private requests remain subject to the organization setting,
GitHub App installation, package mirror, and authorization checks.

Tuist and other embedded SwiftPM clients can read
`~/.swiftpm/configuration/registries.json` instead of native SwiftPM's
`~/Library/org.swift.swiftpm/configuration/registries.json`. Keep both files
aligned when managing them explicitly.

## Troubleshooting

### No registry is configured for a public scope

Avrea's managed default registry handles public scopes without workflow
configuration. Check whether the workflow or runner image creates a custom
`registries.json`. Remove that custom configuration, or associate its default
registry with Avrea as described in [Custom registry
configuration](#custom-registry-configuration).

### A GitHub URL still uses Git

Confirm that the dependency uses a semantic-version requirement and the
SwiftPM or Tuist command includes `--replace-scm-with-registry`.
Branches, commit hashes, and values passed via `revision` intentionally remain
Git dependencies.

### SwiftPM warns that the source archive is not signed

Avrea registry archives currently use a stable checksum but are not signed.
The runner config treats this as a warning so installation continues. A
checksum mismatch remains an error.