Skip to content

Merge installation logic into swiftly as an init subcommand #127

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

Merged
merged 26 commits into from
Aug 8, 2024

Conversation

cmcgee1024
Copy link
Member

@cmcgee1024 cmcgee1024 commented Jun 19, 2024

Instead of relying on a separate shell script with certain issues swiftly can perform its own user initialization with many of the features of the script, including prompts, flags, and options. It also prepares swiftly for possible delivery through system package managers.

Provide a design for the new installation system in the DESIGN.md.

Add an implementation, tests and documentation of an init subcommand that is capable of finishing the installation of swiftly (after the download step) into the user's environment, including swiftly home, toolchains directory, the swiftly binary (if it's user downloaded), and symbolic links.

Contribute documentation describing how to install swiftly and toolchains automatically in the context of a build/CI system.

Instead of relying on a separate shell script with certain issues
swiftly can perform its own user initialization with many of the
features of the script, including prompts, flags, and options. It
also prepares swiftly for possible delivery through system package
managers.

Provide a design for the new installation system in the DESIGN.md.

Change SwiftlyVersion to make it more conformant to the semantic
version specification (https://semver.org) with respect to pre
releases. Add test cases that cover typical scenarios. Update the
swiftly version to a "dev" pre-release.
DESIGN.md Outdated
- Amazon Linux 2
We'll need an initialization mode which, detects information about the OS and distribution in the case of Linux. The initialization mode is also responsible for setting up the directory structure for the toolchains (if necessary), setting up the shell environment for the user, and determining any operating system level dependencies that are required, but missing. Swiftly can perform these tasks itself with the capabilities provided by the Swift language and libraries, such as rich argument parsing, and launching system processes provided that the binary is somehow delivered to the user.

The delivery of the binary onto the user's system can be accomplished in a number of ways, such as direct download from a trusted website with some guidance for the user to pick the right one for their OS and architecture. There might also be a copy/paste of a simple shell command-line for some more automation. Swiftly might be provided as a system-level package (e.g. homebrew, apt, yum, macOS pkg, etc). In some cases delivery might come from manual compilation from this git repository and the "swift build" command, useful for swiftly development and testing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delivery of the binary is where this falls down to some degree (at least on Linux). Previously this was all done automatically by the script. macOS has pkg which avoids this issue, but unless we are using some package management system it will up to the user to copy the binary into a folder in their path which isn't an ideal situation. Or are you expecting swiftly-init to do this for the user?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the path/environment management, creating the swiftly configuration, detection of missing required system packages, and most of what the shell script does today would be incorporated into swiftly's new init subcommand in this design. It's up to the delivery mechanisms to help the user to get to that entry point. I think that with some of the conventions described here those mechanisms have some mechanisms to provide optimal user experiences. For example, I think that swift.org can provide a copy-able command-line that would be equivalent to what is there today, but a little more complex.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to breakdown the assumed different responsibilities of the different delivery methods to ensure that we're covering all bases and don't have any missing features or regressions in behaviours. This would help us hash out what needs to be done for different platforms, what needs to be be done for security etc

…r entirely

Describe in more detail the anticipated delivery methods
@cmcgee1024
Copy link
Member Author

@adam-fowler @patrickfreed how does this new revision of the design look? I've enumerated the expected delivery methods, simplified things, and fixed up some of the wording.

Copy link
Contributor

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm pretty happy with this

DESIGN.md Outdated
- CentOS 7
- CentOS 8
- Amazon Linux 2
Initialization also covers creating symbolic links for various toolchain commands (e.g. swift, swiftc, clang, clang++, etc.) so that swiftly can configure the targets. These need to be installed into a user controlled directory so that swiftly can operate on the without super user permissions. Also, the swiftly binary itself is copied into this location if it is not run from a system location, managed by a system package manager.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the binary is copied we should message the user that they can delete the version they have in their current directory. Or at least message that it has been copied so the user understands they can delete that copy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've reworked this paragraph to notify the user about the case where swiftly is copied into the swiftly bin directory so that they are aware.

Copy link
Contributor

@patrickfreed patrickfreed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the late review, mostly looks good just have a few minor suggestions

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

Automate the installing of the swift gpg signatures into the user keyring
@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024 cmcgee1024 marked this pull request as ready for review July 26, 2024 17:49
Fix error message if swiftly is run outside of an installation
@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024 cmcgee1024 changed the title DRAFT: Merge installation logic into swiftly as an init subcommand Merge installation logic into swiftly as an init subcommand Jul 29, 2024
@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@adam-fowler @patrickfreed this should be ready for review now. The macOS tests all pass, but there's some kind of CI system problem that throws errors at the end. Thank you.

Copy link
Contributor

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor change requests, but in general looks good.

@@ -45,8 +53,22 @@ public struct Swiftly: SwiftlyCommand {

public protocol SwiftlyCommand: AsyncParsableCommand {}

extension Data {
func append(file: URL) throws {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't very clear what this function is doing from its name

Suggested change
func append(file: URL) throws {
func append(to file: URL) throws {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also where is this used, might be better to move this to nearer its usage

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this change to the signature.


if let config = config, !overwrite && config.version != SwiftlyCore.version {
// We don't support downgrades, and we don't yet support upgrades
throw Error(message: "An existing swiftly installation was detected. You can try overwriting it.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is expected of the user here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I understand, you need to add --overwrite. Maybe make the error more explicit. Also when running with --overwrite on Linux it returns a different error

Error: The operation could not be completed. A file with the same name already exists.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the message more explicit about the --overwrite flag since it's in the init subcommand and the error can directly reference the subcommand's flag.

} catch {
let msg = """
Could not load swiftly's configuration file at \(Swiftly.currentPlatform.swiftlyConfigFile.path) due to
error: \"\(error)\".
To use swiftly, modify the configuration file to fix the issue or perform a clean installation.
To begin using swiftly you can install it by running 'swiftly init'.
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error messages you get when a config is not there includes a lot of extraneous info.

error: "Error Domain=NSCocoaErrorDomain Code=260 "The file “config.json” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/Users/adamfowler/Library/Application Support/swiftly/config.json, NSUnderlyingError=0x133207dc0 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}"

Can we translate some of these into more user friendly strings

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pulled out this inner error since it's likely to cause more confusion. The focus should be on the instruction to run the init.

}

guard let id = id, let idlike = idlike else {
let message = "Unable to find release information from file \(releaseFile)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let message = "Unable to find release information from file \(releaseFile)"
let message = "Unable to find OS release information from file \(releaseFile)"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is much clearer.


let data = FileManager.default.contents(atPath: releaseFile)
guard let data = data else {
let message = "Unable to read release information from file \(releaseFile)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let message = "Unable to read release information from file \(releaseFile)"
let message = "Unable to read OS release information from file \(releaseFile)"

Note that the locations can be changed with SWIFTLY_HOME and SWIFTLY_BIN environment variables and run
this again.

Proceed with the installation?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this to a [Y/n]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the prompt to a yes or no instead of numbered options.

@@ -11,6 +11,11 @@ To download swiftly and install Swift, run the following in your terminal, then
curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need to edit this script to download the correct version of swiftly (Linux or mac) and run swiftly init or get rid of this line in the docs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tricky since this README is one of the places where people see how to install swiftly, but it's a few commits newer than the last release. Also, it's unclear if we are going to cut a release or not that still makes use of the shell script, which will still technically work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stick to one installation method. If we add swiftly init then the install script should become history, or at least be reduced to copy the correct version of swiftly off the internet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduced to copy the correct version of swiftly off the internet.

Yeah that's probably a good idea ... i think the maximum installation complexity i'd expect is "choose macOS or linux". Even that can probably be avoided in the script. Hopefully no manual architecture detection "are you on arm64 or amd64?!". manually choosing the arch is not the end of the world, but it can be harder than it looks, to beginners.

```
To begin using installed swiftly from your current shell, first run the following command:

. "/root/.local/share/swiftly/env.sh"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to make it clear this is for Linux, maybe provide macOS version as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env.sh is not accurate, for fish, it'll be env.fish.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

env.sh is not accurate, for fish, it'll be env.fish.

Right, I'm hoping to make this an illustrative example of something that an integrator would look for from the swiftly output so that they can automate it. I'll see if I can make the description above highlight that this is just an example and can be different depending on the environment (e.g. Windows or Fish)

@@ -6,6 +6,12 @@ To download swiftly and install Swift, run the following in your terminal, then
curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We either need to edit this script to download the correct version of swiftly (Linux or mac) and run swiftly init or get rid of this line in the docs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same kind of problem with the readme. People might be reading this from the main branch of swiftpackageindex.com and want the latest release, which is using the shell script installer. We'll need to sweep all of the documentation when we release to switch over to the new method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok we should create a separate PR with documentation changes

Copy link
Contributor

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a suggested fix for an issue on Linux when downloading gpg keys


try process.run()
// Attach this process to our process group so that Ctrl-C and other signals work
let pgid = tcgetpgrp(STDOUT_FILENO)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add the same process group support to runProgram

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this functionality to the runProgram too.


if swiftlyBin.fileExists() {
if !overwrite {
throw Error(message: "Swiftly binary already exists. You can try again with overwrite to replace it.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw Error(message: "Swiftly binary already exists. You can try again with overwrite to replace it.")
throw Error(message: "Swiftly binary already exists. You can try again with `--overwrite` to replace it.")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've fixed the wording here. Thank you.

@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

1 similar comment
@cmcgee1024
Copy link
Member Author

@swift-ci test macOS

@cmcgee1024
Copy link
Member Author

@adam-fowler @patrickfreed although the macOS platform check is failing, that is happening after the tests due to some kind of problem with the CI system itself. The tests are all reporting as successful there.

@cmcgee1024 cmcgee1024 merged commit eba9bd9 into swiftlang:main Aug 8, 2024
5 of 6 checks passed
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.

5 participants