Sources/PackageManagerDocs/Documentation.docc/PackageCollections.md
@Metadata { @Available("Swift", introduced: "5.5") }
Learn to create, publish and use Swift package collections.
Package collections, introduced by SE-0291, are curated lists of packages and associated metadata that you can import to make discovery of existing packages easier.
Educators and community influencers can publish package collections to go along with course materials or blog posts, making it easier for their readers to use packages for the first time, or choosing which packages to use for a particular task. Enterprises may use collections to provide a trusted set of packages, or a collection of packages consistently used by a team. You can author a package collection as a static JSON document and publish it to the web or distribute it on a local file system.
With the swift package-collection command-line interface, SwiftPM users can subscribe to package collections.
Contents of imported package
collections are accessible to any clients of libSwiftPM.
swift package-collection has the following subcommands:
add: Add a new collectiondescribe: Get metadata for a collection or a package included in an imported collectionlist: List configured collectionsrefresh: Refresh configured collectionsremove: Remove a configured collectionsearch: Search for packages by keywords or module names within imported collectionsA package collection is a JSON document that contains a list of packages and metadata per package.
Package collections can be created and published by anyone. The swift-package-collection-generator project provides tooling intended for package collection publishers:
package-collection-generate: Generate a package collection given a list of package URLspackage-collection-sign: Sign a package collectionpackage-collection-validate: Perform basic validations on a package collectionpackage-collection-diff: Compare two package collections to see if their contents are differentAll package collections must adhere to the collection data format for SwiftPM to be able to consume them.
The recommended way to create package collections is to use package-collection-generate.
For custom implementations, the data models are available through the PackageCollectionsModel module.
To begin, define the top-level metadata about the collection:
name: The name of the package collection, for display purposes only.overview: A description of the package collection. Optional.keywords: An array of keywords that the collection is associated with. Optional.formatVersion: The version of the format to which the collection conforms. Currently, 1.0 is the only allowed value.revision: The revision number of this package collection. Optional.generatedAt: The ISO 8601-formatted datetime string when the package collection was generated.generatedBy: The author of this package collection. Optional.
name: The author name.packages: A non-empty array of package objects.Each item in the packages array is a package object with the following properties:
url: The URL of the package. Currently only Git repository URLs are supported. URL should be HTTPS and may contain .git suffix.identity: The identity of the package if published to a registry. Optional.summary: A description of the package. Optional.keywords: An array of keywords that the package is associated with. Optional.readmeURL: The URL of the package's README. Optional.license: The package's current license information. Optional.
url: The URL of the license file.name: License name. SPDX identifier (e.g., Apache-2.0, MIT, etc.) preferred. Omit if unknown. Optional.versions: An array of version objects representing the most recent and/or relevant releases of the package.A version object has metadata extracted from Package.swift and optionally additional metadata from other sources:
version: The semantic version string.summary: A description of the package version. Optional.manifests: A non-empty map of manifests by Swift tools version. The keys are (semantic) tools version (more on this below), while the values are:
toolsVersion: The Swift tools version specified in the manifest.packageName: The name of the package.targets: An array of the package version's targets.
name: The target name.moduleName: The module name if this target can be imported as a module. Optional.products: An array of the package version's products.
name: The product name.type: The product type. This must have the same JSON representation as SwiftPM's PackageModel.ProductType.target: An array of the product’s targets.minimumPlatformVersions: An array of the package version’s supported platforms specified in Package.swift. Optional.{
"5.2": {
"toolsVersion": "5.2",
"packageName": "MyPackage",
"targets": [
{
"name": "MyTarget",
"moduleName": "MyTarget"
}
],
"products": [
{
"name": "MyProduct",
"type": {
"library": ["automatic"]
},
"targets": ["MyTarget"]
}
],
"minimumPlatformVersions": [
{
"name": "macOS",
"version": "10.15"
}
]
}
}
defaultToolsVersion: The Swift tools version of the default manifest. The manifests map must contain this in its keys.verifiedCompatibility: An array of compatible platforms and Swift versions that has been tested and verified for. Valid platform names include macOS, iOS, tvOS, watchOS, Linux, Android, and Windows. Swift version should be semantic version string and as specific as possible. Optional.{
"platform": {
"name": "macOS"
},
"swiftVersion": "5.3.2"
}
license: The package version's license. Optional.
url: The URL of the license file.name: License name. SPDX identifier (e.g., Apache-2.0, MIT, etc.) preferred. Omit if unknown. Optional.author: The package version's author. Optional.
name: The author of the package version.signer: The signer of the package version. Optional. Refer to the documentation on package signing for details.
type: The signer type. Currently the only valid value is ADP (Apple Developer Program).commonName: The common name of the signing certificate's subject.organizationalUnitName: The organizational unit name of the signing certificate's subject.organizationName: The organization name of the signing certificate's subject.createdAt: The ISO 8601-formatted datetime string when the package version was created. Optional.Package collection generators should include data from the "default" manifest Package.swift as well as version-specific manifest(s)
The keys of the manifests map are Swift tools (semantic) versions:
Package.swift, the tools version specified in Package.swift should be used.[email protected] it would be 4.2. The tools version in the manifest must match that in the filename.Version-specific tags are not supported by package collections.
Configuration that pertains to package collections are stored in the file ~/.swiftpm/config/collections.json.
It keeps track of user's list of configured collections and preferences such as those set by the --trust-unsigned and --skip-signature-check flags in the package-collection add command.
Note: This file is managed through Swift Package Manager commands and users are not expected to edit it by hand.
{
"name": "Sample Package Collection",
"overview": "This is a sample package collection listing made-up packages.",
"keywords": ["sample package collection"],
"formatVersion": "1.0",
"revision": 3,
"generatedAt": "2020-10-22T06:03:52Z",
"packages": [
{
"url": "https://www.example.com/repos/RepoOne.git",
"summary": "Package One",
"readmeURL": "https://www.example.com/repos/RepoOne/README",
"license": {
"name": "Apache-2.0",
"url": "https://www.example.com/repos/RepoOne/LICENSE"
},
"versions": [
{
"version": "0.1.0",
"summary": "Fixed a few bugs",
"manifests": {
"5.1": {
"toolsVersion": "5.1",
"packageName": "PackageOne",
"targets": [
{
"name": "Foo",
"moduleName": "Foo"
}
],
"products": [
{
"name": "Foo",
"type": {
"library": ["automatic"]
},
"targets": ["Foo"]
}
]
}
},
"defaultToolsVersion": "5.1",
"verifiedCompatibility": [
{
"platform": { "name": "macOS" },
"swiftVersion": "5.1"
},
{
"platform": { "name": "iOS" },
"swiftVersion": "5.1"
},
{
"platform": { "name": "Linux" },
"swiftVersion": "5.1"
}
],
"license": {
"name": "Apache-2.0",
"url": "https://www.example.com/repos/RepoOne/LICENSE"
},
"createdAt": "2020-10-21T09:25:36Z"
}
]
},
{
"url": "https://www.example.com/repos/RepoTwo.git",
"summary": "Package Two",
"readmeURL": "https://www.example.com/repos/RepoTwo/README",
"versions": [
{
"version": "2.1.0",
"manifests": {
"5.2": {
"toolsVersion": "5.2",
"packageName": "PackageTwo",
"targets": [
{
"name": "Bar",
"moduleName": "Bar"
}
],
"products": [
{
"name": "Bar",
"type": {
"library": ["automatic"]
},
"targets": ["Bar"]
}
]
}
},
"defaultToolsVersion": "5.2"
},
{
"version": "1.8.3",
"manifests": {
"5.0": {
"toolsVersion": "5.0",
"packageName": "PackageTwo",
"targets": [
{
"name": "Bar",
"moduleName": "Bar"
}
],
"products": [
{
"name": "Bar",
"type": {
"library": ["automatic"]
},
"targets": ["Bar"]
}
]
}
},
"defaultToolsVersion": "5.0"
}
]
}
]
}
Sign package collections to establish authenticity and protect their integrity. Doing this is optional. Users will be prompted for confirmation before they can add an unsigned collection. The signing certificate you use to sign a package collection must meet a list of requirements. If these requirements are not met, the package manager returns an error.
For more details on the security features Package manager implements, see doc:PackageSecurity.
While signing can provide some degree of protection on package collections and reduce the risks of their contents being modified by malicious actors, it doesn't prevent the following attack vectors:
add operation when the "unsigned" warning appears on a supposedly signed collection.To defend against these attacks, package manager has certificate-pinning configuration that allows collection publishers to:
The process for collection publishers to define their certificate-pinning configuration is as follows:
PackageCollectionSourceCertificatePolicy and add an entry to the defaultSourceCertPolicies dictionary:private static let defaultSourceCertPolicies: [String: CertificatePolicyConfig] = [
// The key should be the "host" component of the package collection URL.
// This would require all package collections hosted on this domain to be signed.
"www.example.com": CertificatePolicyConfig(
// The signing certificate must have this subject user ID
certPolicyKey: CertificatePolicyKey.default(subjectUserID: "exampleUserID"),
/*
To compute base64-encoded string of a certificate:
let certificateURL = URL(fileURLWithPath: <path to DER-encoded root certificate file>)
let certificateData = try Data(contentsOf: certificateURL)
let base64EncoodedCertificate = certificateData.base64EncodedString()
*/
base64EncodedRootCerts: ["<base64-encoded root certificate>"]
)
]
dig -t txt <DOMAIN> to verify. This would act as proof of domain ownership.Since certificate-pinning configuration is associated with web domains, it can only be applied to signed collections hosted on the web (i.e., URL begins with https://) and does
not cover those found on local file system (i.e., URL begins with file://).