-
Notifications
You must be signed in to change notification settings - Fork 31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add support for Swift package registries #1318
base: main
Are you sure you want to change the base?
Conversation
a5adfda
to
6739de1
Compare
452f05d
to
ad96158
Compare
671c25d
to
f921aba
Compare
a541b18
to
9838fcd
Compare
3d7452d
to
01abd62
Compare
Im testing in a real registry implementation now and realizing some issues, going to make some updates but the majority of the design is ready for review |
💯 (as a follow-up). |
Can we spin up a local webserver as part of CI instead? |
I like the underscores version. However, it is not a strong preference. |
+1 to @brentleyjones suggestion. Again, I am not too familiar with Swift registry hosting. If it is static, can we use GitHub to host it? I presume the functionality just needs a URL. |
01abd62
to
2a46f9e
Compare
I tried this originally but local host doesn't support https and so the bazel download stuff fails because of insecure connection. |
It is not static, it needs to be a web server which follows the registry spec. |
Done! I fixed my fake registry implementation, the |
@luispadron When you are ready to merge, if you don't want to babysit the merge, you can specify |
I think the answer is here is: no? Thoughts? Doing the replacement would require manipulating the identity in a fair amount of the rules and im not sure it gives us much (plus the label is still different than non registry so we don't gain anything from doing the replacement) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
url = archive_url, | ||
output = output, | ||
sha256 = checksum, | ||
stripPrefix = "{scope}.{name}".format(scope = id.scope, name = id.name), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This prefix isn't guaranteed unfortunately; it's going to be whatever the name of the parent folder happened to be when someone ran the publish command. I worked around this by downloading to a temp directory first and then using shell commands to move the contents back to the repo root.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh rough yeah, I had been using <name>-<version>
for the prefix in my fake registry but then our Artifactory implementation was using scope.name
.
I'll update this to work like you described since it looks like we cant use strip_prefix
here
"{version}.zip".format(version = version), | ||
) | ||
repository_ctx.download_and_extract( | ||
headers = _REGISTRY_ZIP_ACCEPT_HEADER, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the bit that requires Bazel 7.1, which was a concern previously. Not sure if that's still a concern, but may be worth documenting as a requirement for using a registry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can add a comment
@cgrindel whats the minimum supported Bazel version for these rules? We need to set the headers here which is only possible in Bazel 7.1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I think having this specific feature labelled as requiring Bazel 7.1 is fine. If people don't use this feature, it won't break people using less than 7.1, right? If so, it's not breaking anyone and people just need to upgrade if they want to use the new feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @brentleyjones is right. Let's document that it is only available in Bazel 7.1 or later. Go for it.
) | ||
|
||
# Download the `Package.swift` file to the root of the source archive output. | ||
_download_package_swift( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary? I think it's already inside the archive isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah true.. I figured this was the point of this endpoint but we should probably just use the archive content and save ourselves a download. I'll update.
"use_registry_identity_for_scm": attr.bool( | ||
doc = """Look up source control dependencies in the registry and use \ | ||
their registry identity when possible to help deduplicate across the two \ | ||
origins. | ||
|
||
NOTE: `replace_scm_with_registry = True` implies \ | ||
`use_registry_identity_for_scm = True`. | ||
""", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't these exclusive? The docs say you can only choose one of the scm transformation flags.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sorry the note isn't clear, it's an error to pass both flags but thats because --replace-scm-with-registry
already implies --use-registry-identity-for-scm
. I'll update the doc comment here to note that both cannot be set at once though
"config_path": attr.string( | ||
doc = "The relative path within the runfiles tree for the Swift Package Manager configuration directory.", | ||
default = ".swiftpm", | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. By default, swift looks at both a shared config in ~/.swiftpm
and a local project config at .swiftpm
. The --config-path
argument specifies a different shared config path (there is no argument to change the project config path).
Setting this to .swiftpm
(the project config path) effectively means swift will only look at .swiftpm
and ignore the shared path.
Setting it to any other value means swift will look at both that value and .swiftpm
.
For a hermetic build I think the first (default) behavior is maybe the only sane one, which is why I was hesitant to expose this as a configurable option previously.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not quite. By default, swift looks at both a shared config in ~/.swiftpm and a local project config at .swiftpm. The --config-path argument specifies a different shared config path (there is no argument to change the project config path).
Yes thats how I understand this to work as well, the .swiftpm
in this rule isn't in the project tree though its part of the rules runfiles.
Setting this to .swiftpm (the project config path) effectively means swift will only look at .swiftpm and ignore the shared path.
Maybe the name i picked is confusing but this .swiftpm
directory is going to live in the rules runfiles, it does not mean a .swiftpm
directory in the repo.
The goal of the swift_package_tool
is to attempt to control where SPM looks for caches, configs, etc. By setting --config-path
here SPM will use a registries.json
file that exists in the swift_package_tool
rules runfiles tree.
# If registries_json is set, symlink the `.json` file to the `config_path/configuration` directory. | ||
if [ -n "$registries_json" ]; then | ||
mkdir -p "$config_path" && ln -sf "$(readlink -f "$registries_json")" "$config_path/registries.json" | ||
fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this blow away a real registries.json
file if one exists? I think you're also missing the configuration
directory in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this blow away a real registries.json file if one exists?
No, this is symlinking the real file into the rules runfiles so that SPM uses that one from the rules --config-path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're also missing the configuration directory in here.
Seems to work without one but i'll update to follow the normal .swiftpm
layout
let package = Package( | ||
name: "swift_package_registry_example", | ||
dependencies: [ | ||
.package(id: "apple.swift-collections", exact: "1.1.3"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran into problems at first with --replace-scm-with-registry
, because Package.resolved will contain a scoped ID like apple.swift-collections
but every Package.swift will still reference the unscoped swift-collections
.
To support that, I made changes to swiftpkg/internal/deps_indexes.bzl
to remove the scope in _new_product_index_key
. Not sure that's the ideal fix, but it worked.
Anyway, that's definitely something I would suggest testing out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To support that, I made changes to swiftpkg/internal/deps_indexes.bzl to remove the scope in _new_product_index_key. Not sure that's the ideal fix, but it worked.
Ooh could you elaborate a bit here, is this an issue with swift package resolve
, etc or with the way the rules generate something. I dont actually know whats required in a normal SPM project to go from SCM -> Registry, do folks need to delete the Package.resolved and re-resolve to replace the SCM identities?
Yeah, let's not do the replacement. |
Fixes #1016
This adds initial support for consuming packages using a Swift Package Registry.
The majority of the changes are in the new
registry_swift_package
rule which creates a repository to build the targets from a package downloaded via a registry.Notably, because the identity of the package is used to derive the repository name, packages used via a registry have the following label:
@swiftpkg_[org].[name]
, so for example@swiftpkg_swift_collections
becomes@swiftpkg_apple.swift_collections
if using theswift-collections
package from theapple
organization (as registries support multiple orgs).The
registry_swift_package
rule is mostly just an updated version of #1043 (thanks @watt)Open questions
.
with_
in identity? E.g.@swiftpkg_apple.swift_collections
vs.@swiftpkg_apple_swift_collections
Follow-up
rule_swift_package_manager
already has a bootstrapping issue, if there is noPackage.resolved
defined already then the Bazel repositories fail to generate. The addition of registry support adds another bootstrapping issue with theregistries.json
file (users will need to runswift package-registry
commands to create it currently). We should address both of these by making bootstrapping a new project easier.Caveats
Example
The following shows an example of a
swift_binary
target depending on an SPM dependency downloaded from a registry defined inregistries.json
.Package.swift
Package.resolved
registries.json
MODULE.bazel
BUILD.bazel