Skip to content

Commit

Permalink
Streamline the swiftly init process (swiftlang#177)
Browse files Browse the repository at this point in the history
The init process just installs swiftly itself at the moment. Most
users will immediately install a swift toolchain, most likely the
latest available one. On Linux, there's confusing gpg messages that
most users don't need to be aware.

The init process will provide a summary of things that are going
to happen to the user's system, including the addition of GnuPG keys
on Linux, and the installation of the latest swift toolchain so that
they can agree, or abort the entire process. When the process runs
the user is given line-level and high level processes, not internal
details. Add a verbose mode for more details, such as the messages
that come from GnuPG on Linux.

Add an option to the init subcommand to allow swiftly to be
installed without the latest available swift toolchain so that
advanced users can decide how to install a toolchain themselves
after the swiftly installation.

Update the documentation with the more automated workflow.

Add more detail to the getting started guide.

Rework the getting started guide to have a platform selector for
linux/macOS and hide platform-specific details

Permit bare swiftly command to start the init workflow when not installed.

Quiet the macOS installer messages behind the verbose flag.

Provide more verbose untarring messages in Linux behind the verbose flag.
  • Loading branch information
cmcgee1024 authored Dec 5, 2024
1 parent 4c5911a commit 2794056
Show file tree
Hide file tree
Showing 17 changed files with 313 additions and 121 deletions.
16 changes: 12 additions & 4 deletions Documentation/SwiftlyDocs.docc/automated-install.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ Swiftly can be installed automatically in places like build/CI systems.

This guide will help you to script to the installation of swiftly and toolchains so that it can be unattended. We assume that you have working understanding of your build system. The examples are based on a typical Unix environment.

First, download a swiftly binary from a trusted source, such as your artifact repository, or a well-known website for the operating system (e.g. Linux) and processor architecture (e.g. arm64, or x86_64). Here's an example using the popular curl command.
First, download the swiftly binary from swift.org for your operating system (e.g. Linux) and processor architecture (e.g. arm64, or x86_64). Here's an example using the popular curl command.

```
curl -L <trusted_location_of_swiftly> > swiftly
curl -L <location_of_swiftly_swift_org> > swiftly.tar.gz
tar zxf swiftly.tar.gz
```

On macOS you can download the pkg file and extract it like this from the command-line:

```
curl -L <location_of_swiftly_swift_org> > swiftly.pkg
installer -pkg swiftly.pkg -target CurrentUserHomeDirectory
```

> Tip: If you are using Linux you will need the "ca-certificates" package for the root certificate authorities that will establish the trust that swiftly needs to make API requests that it needs. This package is frequently pre-installed on end-user environments, but may not be present in more minimal installations.
Once swiftly is downloaded you can run the init subcommand to finish the installation. This command will use the default initialization options and proceed without prompting.
Once swiftly is downloaded you can run the init subcommand to finish the installation. This command will print verbose outputs, assume yes for all prompts, and skip the automatic installation of the latest swift toolchain:

```
./swiftly init --assume-yes
./swiftly init --verbose --assume-yes --skip-install # the swiftly binary is extracted to ~/local/bin/swiftly on macOS
```

Swiftly is installed, but the current shell may not yet be updated with the new environment variables, such as the PATH. The init command prints instructions on how to update the current shell environment without opening a new shell. This is an example of the output taken from Linux, but the details might be different for other OSes, username, or shell.
Expand Down
49 changes: 32 additions & 17 deletions Documentation/SwiftlyDocs.docc/getting-started.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,46 @@
# Getting Started with Swiftly

To download swiftly and install Swift, run the following in your terminal, then follow the on-screen instructions:
Start using swiftly and swift.

```
curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
```
To get started with swiftly you can download it from [swift.org](https://swift.org/download), and extract the package.

Alternatively, you can download the swiftly binary and install itself like this:
@TabNavigator {
@Tab("Linux") {
If you are using Linux then you can verify and extract the archive like this:

```
swiftly init
```
```
sha256sum swiftly-x.y.z.tar.gz # Check that the hash matches what's reported on swift.org
tar zxf swiftly-x.y.z.tar.gz
```

Once swiftly is installed you can use it to install the latest available swift toolchain like this:
Now run swiftly init to finish the installation:

```
$ swiftly install latest
```
./swiftly init
```
}

Fetching the latest stable Swift release...
Installing Swift 5.8.1
Downloaded 488.5 MiB of 488.5 MiB
Extracting toolchain...
Swift 5.8.1 installed successfully!
@Tab("macOS") {
On macOS you can either run the pkg installer from the command-line like this or just run the package by double-clicking on it (not recommended):

```
installer -pkg swift-x.y.z.pkg -target CurrentUserHomeDirectory
```

Now run swiftly init to finish the installation:

```
$HOME/usr/local/bin/swiftly init
```
}
}

Swiftly will install itself and download the latest available Swift toolchain. Follow the prompts for any additional steps. Once everything is done you can begin using swift.

```
$ swift --version
Swift version 5.8.1 (swift-5.8.1-RELEASE)
Swift version 6.0.1 (swift-6.0.1-RELEASE)
Target: x86_64-unknown-linux-gnu
$ swift build # Build with the latest (5.8.1) toolchain
Expand Down
54 changes: 47 additions & 7 deletions Documentation/SwiftlyDocs.docc/swiftly-cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ swiftly [--version] [--help]
Install a new toolchain.

```
swiftly install [<version>] [--use] [--verify|no-verify] [--post-install-file=<post-install-file>] [--assume-yes] [--version] [--help]
swiftly install [<version>] [--use] [--verify|no-verify] [--post-install-file=<post-install-file>] [--assume-yes] [--verbose] [--version] [--help]
```

**version:**
Expand Down Expand Up @@ -81,6 +81,11 @@ written to this file as commands that can be run after the installation.
*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**--version:**

*Show the version.*
Expand Down Expand Up @@ -143,7 +148,7 @@ Note that listing available snapshots before the latest release (major and minor
Set the in-use toolchain. If no toolchain is provided, print the currently in-use toolchain, if any.

```
swiftly use [--print-location] [--global-default] [--assume-yes] [<toolchain>] [--version] [--help]
swiftly use [--print-location] [--global-default] [--assume-yes] [--verbose] [<toolchain>] [--version] [--help]
```

**--print-location:**
Expand All @@ -161,6 +166,11 @@ swiftly use [--print-location] [--global-default] [--assume-yes] [<toolchain>] [
*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**toolchain:**

*The toolchain to use.*
Expand Down Expand Up @@ -210,7 +220,7 @@ Likewise, the latest snapshot associated with a given development branch can be
Remove an installed toolchain.

```
swiftly uninstall <toolchain> [--assume-yes] [--version] [--help]
swiftly uninstall <toolchain> [--assume-yes] [--verbose] [--version] [--help]
```

**toolchain:**
Expand Down Expand Up @@ -249,6 +259,11 @@ Finally, all installed toolchains can be uninstalled by specifying 'all':
*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**--version:**

*Show the version.*
Expand Down Expand Up @@ -309,7 +324,7 @@ The installed snapshots for a given devlopment branch can be listed by specifyin
Update an installed toolchain to a newer version.

```
swiftly update [<toolchain>] [--assume-yes] [--verify|no-verify] [--post-install-file=<post-install-file>] [--version] [--help]
swiftly update [<toolchain>] [--assume-yes] [--verbose] [--verify|no-verify] [--post-install-file=<post-install-file>] [--version] [--help]
```

**toolchain:**
Expand Down Expand Up @@ -355,6 +370,11 @@ A specific snapshot toolchain can be updated by including the date:
*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**--verify|no-verify:**

*Verify the toolchain's PGP signature before proceeding with installation.*
Expand Down Expand Up @@ -385,7 +405,7 @@ written to this file as commands that can be run after the installation.
Perform swiftly initialization into your user account.

```
swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--assume-yes] [--version] [--help]
swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--skip-install] [--assume-yes] [--verbose] [--version] [--help]
```

**--no-modify-profile:**
Expand All @@ -400,14 +420,24 @@ swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--assu

**--platform=\<platform\>:**

*Specify the current Linux platform for swiftly.*
*Specify the current Linux platform for swiftly*


**--skip-install:**

*Skip installing the latest toolchain*


**--assume-yes:**

*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**--version:**

*Show the version.*
Expand All @@ -425,9 +455,19 @@ swiftly init [--no-modify-profile] [--overwrite] [--platform=<platform>] [--assu
Update the version of swiftly itself.

```
swiftly self-update [--version] [--help]
swiftly self-update [--assume-yes] [--verbose] [--version] [--help]
```

**--assume-yes:**

*Disable confirmation prompts by assuming 'yes'*


**--verbose:**

*Enable verbose reporting from swiftly*


**--version:**

*Show the version.*
Expand Down
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ Ongoing maintenance and stewardship of this project is led by the [SSWG](https:/

### Installation

To download swiftly and install Swift, run the following in your terminal, then follow the on-screen instructions.
```
curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
```
Download the swiftly package from [swift.org](https://swift.org/download) and it can install itself with init:

Alternatively, you can download the swiftly binary and it can install itself:
```
swiftly init
```
Expand Down
15 changes: 11 additions & 4 deletions Sources/LinuxPlatform/Linux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ public struct Linux: Platform {
}
}

public func install(from tmpFile: URL, version: ToolchainVersion) throws {
public func install(from tmpFile: URL, version: ToolchainVersion, verbose: Bool) throws {
guard tmpFile.fileExists() else {
throw Error(message: "\(tmpFile) doesn't exist")
}
Expand All @@ -348,7 +348,14 @@ public struct Linux: Platform {
let relativePath = name.drop { c in c != "/" }.dropFirst()

// prepend /path/to/swiftlyHomeDir/toolchains/<toolchain> to each file name
return toolchainDir.appendingPathComponent(String(relativePath))
let destination = toolchainDir.appendingPathComponent(String(relativePath))

if verbose {
SwiftlyCore.print("\(destination.path)")
}

// prepend /path/to/swiftlyHomeDir/toolchains/<toolchain> to each file name
return destination
}
}

Expand Down Expand Up @@ -390,7 +397,7 @@ public struct Linux: Platform {
FileManager.default.temporaryDirectory.appendingPathComponent("swiftly-\(UUID())")
}

public func verifySignature(httpClient: SwiftlyHTTPClient, archiveDownloadURL: URL, archive: URL) async throws {
public func verifySignature(httpClient: SwiftlyHTTPClient, archiveDownloadURL: URL, archive: URL, verbose: Bool) async throws {
SwiftlyCore.print("Downloading toolchain signature...")
let sigFile = self.getTempFilePath()
let _ = FileManager.default.createFile(atPath: sigFile.path, contents: nil)
Expand All @@ -405,7 +412,7 @@ public struct Linux: Platform {

SwiftlyCore.print("Verifying toolchain signature...")
do {
try self.runProgram("gpg", "--verify", sigFile.path, archive.path)
try self.runProgram("gpg", "--verify", sigFile.path, archive.path, quiet: !verbose)
} catch {
throw Error(message: "Signature verification failed: \(error).")
}
Expand Down
13 changes: 8 additions & 5 deletions Sources/MacOSPlatform/MacOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public struct MacOS: Platform {
nil
}

public func install(from tmpFile: URL, version: ToolchainVersion) throws {
public func install(from tmpFile: URL, version: ToolchainVersion, verbose: Bool) throws {
guard tmpFile.fileExists() else {
throw Error(message: "\(tmpFile) doesn't exist")
}
Expand All @@ -60,23 +60,26 @@ public struct MacOS: Platform {

if SwiftlyCore.mockedHomeDir == nil {
SwiftlyCore.print("Installing package in user home directory...")
try runProgram("installer", "-pkg", tmpFile.path, "-target", "CurrentUserHomeDirectory")
try runProgram("installer", "-verbose", "-pkg", tmpFile.path, "-target", "CurrentUserHomeDirectory", quiet: !verbose)
} else {
// In the case of a mock for testing purposes we won't use the installer, perferring a manual process because
// the installer will not install to an arbitrary path, only a volume or user home directory.
SwiftlyCore.print("Expanding pkg...")
let tmpDir = self.getTempFilePath()
let toolchainDir = self.swiftlyToolchainsDir.appendingPathComponent("\(version.identifier).xctoolchain", isDirectory: true)
if !toolchainDir.fileExists() {
try FileManager.default.createDirectory(at: toolchainDir, withIntermediateDirectories: false)
}
try runProgram("pkgutil", "--expand", tmpFile.path, tmpDir.path)
try runProgram("pkgutil", "--verbose", "--expand", tmpFile.path, tmpDir.path, quiet: !verbose)
// There's a slight difference in the location of the special Payload file between official swift packages
// and the ones that are mocked here in the test framework.
var payload = tmpDir.appendingPathComponent("Payload")
if !payload.fileExists() {
payload = tmpDir.appendingPathComponent("\(version.identifier)-osx-package.pkg/Payload")
}
try runProgram("tar", "-C", toolchainDir.path, "-xf", payload.path)

SwiftlyCore.print("Untarring pkg Payload...")
try runProgram("tar", "-C", toolchainDir.path, "-xvf", payload.path, quiet: !verbose)
}
}

Expand Down Expand Up @@ -146,7 +149,7 @@ public struct MacOS: Platform {
FileManager.default.temporaryDirectory.appendingPathComponent("swiftly-\(UUID()).pkg")
}

public func verifySignature(httpClient _: SwiftlyHTTPClient, archiveDownloadURL _: URL, archive _: URL) async throws {
public func verifySignature(httpClient _: SwiftlyHTTPClient, archiveDownloadURL _: URL, archive _: URL, verbose _: Bool) async throws {
// No signature verification is required on macOS since the pkg files have their own signing
// mechanism and the swift.org downloadables are trusted by stock macOS installations.
}
Expand Down
Loading

0 comments on commit 2794056

Please sign in to comment.