diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 922f8b8f..cc1f92b8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -21,12 +21,11 @@ jobs: name: Test uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main with: - # linux_os_versions: "[\"jammy\", \"noble\", \"focal\", \"amazonlinux2\", \"rhel-ubi9\", \"bookworm\", \"fedora39\"]" - # Not working: noble (compile error in TSC FileSystem), bookworm (missing memory.h), fedora39 (missing memory.h) - linux_os_versions: "[\"jammy\", \"focal\", \"rhel-ubi9\"]" + # Amazon Linux 2 won't work with GH infrastructure + linux_os_versions: "[\"jammy\", \"focal\", \"rhel-ubi9\", \"noble\", \"bookworm\", \"fedora39\"]" # We only care about the current stable release, because that's where we make our swiftly releases linux_exclude_swift_versions: "[{\"swift_version\": \"nightly-main\"},{\"swift_version\": \"nightly-6.0\"},{\"swift_version\": \"5.8\"},{\"swift_version\": \"5.9\"},{\"swift_version\": \"5.10\"}]" - linux_pre_build_command: ((apt-get update && apt-get -y install curl make) || ((curl --help || yum -y install curl) && yum -y install make)) && ./scripts/install-libarchive.sh + linux_pre_build_command: ./scripts/prep-gh-action.sh && ./scripts/install-libarchive.sh enable_windows_checks: false releasebuild: @@ -36,7 +35,7 @@ jobs: linux_os_versions: "[\"rhel-ubi9\"]" # We only care about the current stable release, because that's where we make our swiftly releases linux_exclude_swift_versions: "[{\"swift_version\": \"nightly-main\"},{\"swift_version\": \"nightly-6.0\"},{\"swift_version\": \"5.8\"},{\"swift_version\": \"5.9\"},{\"swift_version\": \"5.10\"}]" - linux_pre_build_command: echo "" + linux_pre_build_command: ./scripts/prep-gh-action.sh linux_build_command: swift run build-swiftly-release --skip 0.4.0 enable_windows_checks: false @@ -47,6 +46,6 @@ jobs: # We only need to run this with one swift release and on one of the linux distributions linux_os_versions: "[\"jammy\"]" linux_exclude_swift_versions: "[{\"swift_version\": \"nightly-main\"},{\"swift_version\": \"nightly-6.0\"},{\"swift_version\": \"5.8\"},{\"swift_version\": \"5.9\"},{\"swift_version\": \"5.10\"}]" - linux_pre_build_command: echo "" + linux_pre_build_command: ./scripts/prep-gh-action.sh linux_build_command: swift run swiftformat --lint --dryrun . enable_windows_checks: false diff --git a/Package.resolved b/Package.resolved index 10355fa9..a0e1f8e1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "942a1612e8d4b18479d16ed7518859be9ac852972e43afb2a08c65d1037a641f", + "originHash" : "9b0d92b6fbb59080a05ce00f87dc9c6277b32d78e56905abba4c40947edf6d7d", "pins" : [ { "identity" : "async-http-client", @@ -150,8 +150,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-tools-support-core.git", "state" : { - "revision" : "3b13e439a341bbbfe0f710c7d1be37221745ef1a", - "version" : "0.6.1" + "revision" : "5b130e04cc939373c4713b91704b0c47ceb36170", + "version" : "0.7.1" } }, { diff --git a/Package.swift b/Package.swift index ade1f5ed..795d4c7a 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"), .package(url: "https://github.com/swift-server/async-http-client", from: "1.21.2"), .package(url: "https://github.com/apple/swift-nio.git", from: "2.64.0"), - .package(url: "https://github.com/apple/swift-tools-support-core.git", from: "0.6.1"), + .package(url: "https://github.com/apple/swift-tools-support-core.git", from: "0.7.1"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.3.0"), // This dependency provides the correct version of the formatter so that you can run `swift run swiftformat Package.swift Plugins/ Sources/ Tests/` .package(url: "https://github.com/nicklockwood/SwiftFormat", exact: "0.49.18"), diff --git a/README.md b/README.md index 9e736365..d7541392 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,6 @@ Target: x86_64-unknown-linux-gnu ## Platform support - Linux-based platforms listed on https://swift.org/download - - CentOS 7 will not be supported due to some dependencies of swiftly not supporting it, however. Right now, swiftly is in early stages of development and is supported on Linux and macOS. For more detailed information about swiftly's intended features and implementation, check out the [design document](DESIGN.md). diff --git a/Sources/LinuxPlatform/Linux.swift b/Sources/LinuxPlatform/Linux.swift index 9dc3c603..7a08917c 100644 --- a/Sources/LinuxPlatform/Linux.swift +++ b/Sources/LinuxPlatform/Linux.swift @@ -7,6 +7,17 @@ var swiftGPGKeysRefreshed = false /// This implementation can be reused for any supported Linux platform. /// TODO: replace dummy implementations public struct Linux: Platform { + let linuxPlatforms = [ + PlatformDefinition.ubuntu2404, + PlatformDefinition.ubuntu2204, + PlatformDefinition.ubuntu2004, + PlatformDefinition.ubuntu1804, + PlatformDefinition.fedora39, + PlatformDefinition.rhel9, + PlatformDefinition.amazonlinux2, + PlatformDefinition.debian12, + ] + public init() {} public var appDataDirectory: URL { @@ -125,6 +136,26 @@ public struct Linux: Platform { "tzdata", "zlib1g-dev", ] + case "ubuntu2404": + [ + "binutils", + "git", + "unzip", + "gnupg2", + "libc6-dev", + "libcurl4-openssl-dev", + "libedit2", + "libgcc-13-dev", + "libpython3-dev", + "libsqlite3-0", + "libstdc++-13-dev", + "libxml2-dev", + "libncurses-dev", + "libz3-dev", + "pkg-config", + "tzdata", + "zlib1g-dev", + ] case "amazonlinux2": [ "binutils", @@ -158,6 +189,39 @@ public struct Linux: Platform { "unzip", "zip", ] + case "fedora39": + [ + "binutils", + "gcc", + "git", + "unzip", + "libcurl-devel", + "libedit-devel", + "libicu-devel", + "sqlite-devel", + "libuuid-devel", + "libxml2-devel", + "python3-devel", + "libstdc++-devel", + "libstdc++-static", + ] + case "debian12": + [ + "binutils-gold", + "libicu-dev", + "libcurl4-openssl-dev", + "libedit-dev", + "libsqlite3-dev", + "libncurses-dev", + "libpython3-dev", + "libxml2-dev", + "pkg-config", + "uuid-dev", + "tzdata", + "git", + "gcc", + "libstdc++-12-dev", + ] default: [] } @@ -169,10 +233,16 @@ public struct Linux: Platform { "apt-get" case "ubuntu2204": "apt-get" + case "ubuntu2404": + "apt-get" case "amazonlinux2": "yum" case "ubi9": "yum" + case "fedora39": + "yum" + case "debian12": + "apt-get" default: nil } @@ -196,7 +266,7 @@ public struct Linux: Platform { // Import the latest swift keys, but only once per session, which will help with the performance in tests if !swiftGPGKeysRefreshed { let tmpFile = self.getTempFilePath() - FileManager.default.createFile(atPath: tmpFile.path, contents: nil, attributes: [.posixPermissions: 0o600]) + let _ = FileManager.default.createFile(atPath: tmpFile.path, contents: nil, attributes: [.posixPermissions: 0o600]) defer { try? FileManager.default.removeItem(at: tmpFile) } @@ -407,7 +477,7 @@ public struct Linux: Platform { public func verifySignature(httpClient: SwiftlyHTTPClient, archiveDownloadURL: URL, archive: URL) async throws { SwiftlyCore.print("Downloading toolchain signature...") let sigFile = self.getTempFilePath() - FileManager.default.createFile(atPath: sigFile.path, contents: nil) + let _ = FileManager.default.createFile(atPath: sigFile.path, contents: nil) defer { try? FileManager.default.removeItem(at: sigFile) } @@ -425,59 +495,43 @@ public struct Linux: Platform { } } - private func manualSelectPlatform(_ platformPretty: String?) -> PlatformDefinition { + private func manualSelectPlatform(_ platformPretty: String?) async -> PlatformDefinition { if let platformPretty = platformPretty { print("\(platformPretty) is not an officially supported platform, but the toolchains for another platform may still work on it.") } else { print("This platform could not be detected, but a toolchain for one of the supported platforms may work on it.") } + let selections = self.linuxPlatforms.enumerated().map { "\($0 + 1)) \($1.namePretty)" }.joined(separator: "\n") + print(""" Please select the platform to use for toolchain downloads: 0) Cancel - 1) Ubuntu 22.04 - 2) Ubuntu 20.04 - 3) Ubuntu 18.04 - 4) RHEL 9 - 5) Amazon Linux 2 + \(selections) """) - let choice = SwiftlyCore.readLine(prompt: "> ") ?? "0" + let choice = SwiftlyCore.readLine(prompt: "Pick one of the available selections [0-\(self.linuxPlatforms.count)] ") ?? "0" - switch choice { - case "1": - return PlatformDefinition.ubuntu2204 - case "2": - return PlatformDefinition.ubuntu2004 - case "3": - return PlatformDefinition.ubuntu1804 - case "4": - return PlatformDefinition.rhel9 - case "5": - return PlatformDefinition.amazonlinux2 - default: + guard let choiceNum = Int(choice) else { + fatalError("Installation canceled") + } + + guard choiceNum > 0 && choiceNum <= self.linuxPlatforms.count else { fatalError("Installation canceled") } + + return self.linuxPlatforms[choiceNum - 1] } public func detectPlatform(disableConfirmation: Bool, platform: String?) async throws -> PlatformDefinition { // We've been given a hint to use - if let platform = platform { - switch platform { - case "ubuntu22.04": - return PlatformDefinition.ubuntu2204 - case "ubuntu20.04": - return PlatformDefinition.ubuntu2004 - case "ubuntu18.04": - return PlatformDefinition.ubuntu1804 - case "amazonlinux2": - return PlatformDefinition.amazonlinux2 - case "rhel9": - return PlatformDefinition.rhel9 - default: - fatalError("Unrecognized platform \(platform)") + if let platform { + guard let pd = linuxPlatforms.first(where: { $0.nameFull == platform }) else { + fatalError("Unrecognized platform \(platform). Recognized values: \(self.linuxPlatforms.map(\.nameFull).joined(separator: ", ")).") } + + return pd } let osReleaseFiles = ["/etc/os-release", "/usr/lib/os-release"] @@ -498,98 +552,62 @@ public struct Linux: Platform { } else { print(message) } - return self.manualSelectPlatform(platformPretty) - } - - let data = FileManager.default.contents(atPath: releaseFile) - guard let data = data else { - let message = "Unable to read OS release information from file \(releaseFile)" - if disableConfirmation { - throw Error(message: message) - } else { - print(message) - } - return self.manualSelectPlatform(platformPretty) + return await self.manualSelectPlatform(platformPretty) } - guard let releaseInfo = String(data: data, encoding: .utf8) else { - let message = "Unable to read OS release information from file \(releaseFile)" - if disableConfirmation { - throw Error(message: message) - } else { - print(message) - } - return self.manualSelectPlatform(platformPretty) - } + let releaseInfo = try String(contentsOfFile: releaseFile, encoding: .utf8) var id: String? var idlike: String? var versionID: String? - var ubuntuCodeName: String? for info in releaseInfo.split(separator: "\n").map(String.init) { if info.hasPrefix("ID=") { id = String(info.dropFirst("ID=".count)).replacingOccurrences(of: "\"", with: "") } else if info.hasPrefix("ID_LIKE=") { idlike = String(info.dropFirst("ID_LIKE=".count)).replacingOccurrences(of: "\"", with: "") } else if info.hasPrefix("VERSION_ID=") { - versionID = String(info.dropFirst("VERSION_ID=".count)).replacingOccurrences(of: "\"", with: "") - } else if info.hasPrefix("UBUNTU_CODENAME=") { - ubuntuCodeName = String(info.dropFirst("UBUNTU_CODENAME=".count)).replacingOccurrences(of: "\"", with: "") + versionID = String(info.dropFirst("VERSION_ID=".count)).replacingOccurrences(of: "\"", with: "").replacingOccurrences(of: ".", with: "") } else if info.hasPrefix("PRETTY_NAME=") { platformPretty = String(info.dropFirst("PRETTY_NAME=".count)).replacingOccurrences(of: "\"", with: "") } } - guard let id = id, let idlike = idlike else { + guard let id, let versionID else { let message = "Unable to find release information from file \(releaseFile)" if disableConfirmation { throw Error(message: message) } else { print(message) } - return self.manualSelectPlatform(platformPretty) + return await self.manualSelectPlatform(platformPretty) } - if (id + idlike).contains("amzn") { - guard let versionID = versionID, versionID == "2" else { + if (id + (idlike ?? "")).contains("amzn") { + guard versionID == "2" else { let message = "Unsupported version of Amazon Linux" if disableConfirmation { throw Error(message: message) } else { print(message) } - return self.manualSelectPlatform(platformPretty) + return await self.manualSelectPlatform(platformPretty) } - return PlatformDefinition(name: "amazonlinux2", nameFull: "amazonlinux2", namePretty: "Amazon Linux 2") - } else if (id + idlike).contains("ubuntu") { - if ubuntuCodeName == "jammy" { - return PlatformDefinition(name: "ubuntu2204", nameFull: "ubuntu22.04", namePretty: "Ubuntu 22.04") - } else if ubuntuCodeName == "focal" { - return PlatformDefinition(name: "ubuntu2004", nameFull: "ubuntu20.04", namePretty: "Ubuntu 20.04") - } else if ubuntuCodeName == "bionic" { - return PlatformDefinition(name: "ubuntu1804", nameFull: "ubuntu18.04", namePretty: "Ubuntu 18.04") - } else { - let message = "Unsupported version of Ubuntu Linux" - if disableConfirmation { - throw Error(message: message) - } else { - print(message) - } - return self.manualSelectPlatform(platformPretty) - } - } else if (id + idlike).contains("rhel") { - guard let versionID = versionID, versionID.hasPrefix("9") else { + return PlatformDefinition.amazonlinux2 + } else if (id + (idlike ?? "")).contains("rhel") { + guard versionID.hasPrefix("9") else { let message = "Unsupported version of RHEL" if disableConfirmation { throw Error(message: message) } else { print(message) } - return self.manualSelectPlatform(platformPretty) + return await self.manualSelectPlatform(platformPretty) } - return PlatformDefinition(name: "ubi9", nameFull: "ubi9", namePretty: "RHEL 9") + return PlatformDefinition.rhel9 + } else if let pd = [PlatformDefinition.ubuntu1804, .ubuntu2004, .ubuntu2204, .ubuntu2404, .debian12, .fedora39].first(where: { $0.name == id + versionID }) { + return pd } let message = "Unsupported Linux platform" @@ -598,7 +616,7 @@ public struct Linux: Platform { } else { print(message) } - return self.manualSelectPlatform(platformPretty) + return await self.manualSelectPlatform(platformPretty) } public func getShell() async throws -> String { diff --git a/Sources/SwiftlyCore/HTTPClient.swift b/Sources/SwiftlyCore/HTTPClient.swift index 0e04ce0f..b34d0211 100644 --- a/Sources/SwiftlyCore/HTTPClient.swift +++ b/Sources/SwiftlyCore/HTTPClient.swift @@ -72,8 +72,6 @@ struct SwiftOrgPlatform: Codable { PlatformDefinition.ubuntu2204 case "Red Hat Universal Base Image 9": PlatformDefinition.rhel9 - case "Ubuntu 23.10": - PlatformDefinition(name: "ubuntu2310", nameFull: "ubuntu23.10", namePretty: "Ubuntu 23.10") case "Ubuntu 24.04": PlatformDefinition(name: "ubuntu2404", nameFull: "ubuntu24.04", namePretty: "Ubuntu 24.04") case "Debian 12": diff --git a/Sources/SwiftlyCore/Platform.swift b/Sources/SwiftlyCore/Platform.swift index b4a64459..591aa971 100644 --- a/Sources/SwiftlyCore/Platform.swift +++ b/Sources/SwiftlyCore/Platform.swift @@ -22,11 +22,15 @@ public struct PlatformDefinition: Codable, Equatable { } public static let macOS = PlatformDefinition(name: "xcode", nameFull: "osx", namePretty: "macOS") + + public static let ubuntu2404 = PlatformDefinition(name: "ubuntu2404", nameFull: "ubuntu24.04", namePretty: "Ubuntu 24.04") public static let ubuntu2204 = PlatformDefinition(name: "ubuntu2204", nameFull: "ubuntu22.04", namePretty: "Ubuntu 22.04") public static let ubuntu2004 = PlatformDefinition(name: "ubuntu2004", nameFull: "ubuntu20.04", namePretty: "Ubuntu 20.04") public static let ubuntu1804 = PlatformDefinition(name: "ubuntu1804", nameFull: "ubuntu18.04", namePretty: "Ubuntu 18.04") public static let rhel9 = PlatformDefinition(name: "ubi9", nameFull: "ubi9", namePretty: "RHEL 9") + public static let fedora39 = PlatformDefinition(name: "fedora39", nameFull: "fedora39", namePretty: "Fedora Linux 39") public static let amazonlinux2 = PlatformDefinition(name: "amazonlinux2", nameFull: "amazonlinux2", namePretty: "Amazon Linux 2") + public static let debian12 = PlatformDefinition(name: "debian12", nameFull: "debian12", namePretty: "Debian GNU/Linux 12") } public protocol Platform { diff --git a/Tests/SwiftlyTests/HTTPClientTests.swift b/Tests/SwiftlyTests/HTTPClientTests.swift index 90bdfb0c..7f1f2d4d 100644 --- a/Tests/SwiftlyTests/HTTPClientTests.swift +++ b/Tests/SwiftlyTests/HTTPClientTests.swift @@ -48,11 +48,20 @@ final class HTTPClientTests: SwiftlyTests { func testGetMetdataFromSwiftOrg() async throws { let supportedPlatforms = [ PlatformDefinition.macOS, + PlatformDefinition.ubuntu2404, PlatformDefinition.ubuntu2204, PlatformDefinition.ubuntu2004, // PlatformDefinition.ubuntu1804, // There are no releases for Ubuntu 18.04 in the branches being tested below PlatformDefinition.rhel9, + PlatformDefinition.fedora39, PlatformDefinition.amazonlinux2, + PlatformDefinition.debian12, + ] + + let newPlatforms = [ + PlatformDefinition.ubuntu2404, + PlatformDefinition.fedora39, + PlatformDefinition.debian12, ] let branches = [ @@ -65,15 +74,17 @@ final class HTTPClientTests: SwiftlyTests { // GIVEN: we have a swiftly http client with swift.org metadata capability // WHEN: we ask for the first five releases of a supported platform in a supported arch let releases = try await SwiftlyCore.httpClient.getReleaseToolchains(platform: platform, arch: arch, limit: 5) - // THEN: we get five releases - XCTAssertEqual(5, releases.count) + // THEN: we get at least 1 release + XCTAssertTrue(1 <= releases.count) + + if newPlatforms.contains(platform) { continue } // Newer distros don't have main snapshots yet for branch in branches { // GIVEN: we have a swiftly http client with swift.org metadata capability // WHEN: we ask for the first five snapshots on a branch for a supported platform and arch let snapshots = try await SwiftlyCore.httpClient.getSnapshotToolchains(platform: platform, arch: arch, branch: branch, limit: 5) - // THEN: we get five snapshots - XCTAssertEqual(5, snapshots.count) + // THEN: we get at least 3 releases + XCTAssertTrue(3 <= snapshots.count) } } } diff --git a/Tests/SwiftlyTests/InstallTests.swift b/Tests/SwiftlyTests/InstallTests.swift index 2d0b30d4..b73bf40e 100644 --- a/Tests/SwiftlyTests/InstallTests.swift +++ b/Tests/SwiftlyTests/InstallTests.swift @@ -39,6 +39,9 @@ final class InstallTests: SwiftlyTests { /// Tests that `swiftly install a.b` installs the latest patch version of Swift a.b. func testInstallLatestPatchVersion() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + guard try await self.baseTestConfig().platform.name != "ubi9" else { print("Skipping test due to insufficient download availability for ubi9") return @@ -133,6 +136,9 @@ final class InstallTests: SwiftlyTests { /// Tests that `swiftly install main-snapshot` installs the latest available main snapshot. func testInstallLatestMainSnapshot() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + try await self.withTestHome { try await self.withMockedToolchain { var cmd = try self.parseCommand(Install.self, ["install", "main-snapshot", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"]) @@ -165,6 +171,9 @@ final class InstallTests: SwiftlyTests { /// Tests that `swiftly install a.b-snapshot` installs the latest available a.b release snapshot. func testInstallLatestReleaseSnapshot() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + try await self.withTestHome { try await self.withMockedToolchain { var cmd = try self.parseCommand(Install.self, ["install", "6.0-snapshot", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"]) @@ -249,12 +258,18 @@ final class InstallTests: SwiftlyTests { /// Tests that attempting to install main snapshots that are already installed doesn't result in an error. func testInstallDuplicateMainSnapshots() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + try await self.duplicateTest("main-snapshot-2023-04-01") try await self.duplicateTest("main-snapshot") } /// Tests that attempting to install release snapshots that are already installed doesn't result in an error. func testInstallDuplicateReleaseSnapshots() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + try await self.duplicateTest("6.0-snapshot-2024-06-18") try await self.duplicateTest("6.0-snapshot") } diff --git a/Tests/SwiftlyTests/SwiftlyTests.swift b/Tests/SwiftlyTests/SwiftlyTests.swift index e9315f51..10f44e9d 100644 --- a/Tests/SwiftlyTests/SwiftlyTests.swift +++ b/Tests/SwiftlyTests/SwiftlyTests.swift @@ -99,25 +99,8 @@ class SwiftlyTests: XCTestCase { ] func baseTestConfig() async throws -> Config { - let getEnv = { varName in - guard let v = ProcessInfo.processInfo.environment[varName] else { - throw SwiftlyTestError(message: "environment variable \(varName) must be set in order to run tests") - } - return v - } - - let name = try? getEnv("SWIFTLY_PLATFORM_NAME") - let nameFull = try? getEnv("SWIFTLY_PLATFORM_NAME_FULL") - let namePretty = try? getEnv("SWIFTLY_PLATFORM_NAME_PRETTY") - - let pd = if let name = name, let nameFull = nameFull, let namePretty = namePretty { - PlatformDefinition(name: name, nameFull: nameFull, namePretty: namePretty) - } else { - try? await Swiftly.currentPlatform.detectPlatform(disableConfirmation: true, platform: nil) - } - - guard let pd = pd else { - throw SwiftlyTestError(message: "unable to detect platform. please set SWIFTLY_PLATFORM_NAME, SWIFTLY_PLATFORM_NAME_FULL, and SWIFTLY_PLATFORM_NAME_PRETTY to run the tests") + guard let pd = try? await Swiftly.currentPlatform.detectPlatform(disableConfirmation: true, platform: nil) else { + throw SwiftlyTestError(message: "Unable to detect the current platform.") } return Config( @@ -146,9 +129,6 @@ class SwiftlyTests: XCTestCase { /// Create a fresh swiftly home directory, populate it with a base config, and run the provided closure. /// Any swiftly commands executed in the closure will use this new home directory. /// - /// This method requires the SWIFTLY_PLATFORM_NAME, SWIFTLY_PLATFORM_NAME_FULL, and SWIFTLY_PLATFORM_NAME_PRETTY - /// environment variables to be set. - /// /// The home directory will be deleted after the provided closure has been executed. func withTestHome( name: String = "testHome", @@ -393,6 +373,20 @@ class SwiftlyTests: XCTestCase { let toolchainVersion = String(decoding: outputData, as: UTF8.self).trimmingCharacters(in: .newlines) return try ToolchainVersion(parsing: toolchainVersion) } + + func snapshotsAvailable() async throws -> Bool { + let pd = try await Swiftly.currentPlatform.detectPlatform(disableConfirmation: true, platform: nil) + + // Snapshots are currently unavailable for these platforms on swift.org + // TODO: remove these once snapshots are available for them + let snapshotsUnavailable = [ + PlatformDefinition.ubuntu2404, + PlatformDefinition.fedora39, + PlatformDefinition.debian12, + ] + + return !snapshotsUnavailable.contains(pd) + } } public class TestOutputHandler: SwiftlyCore.OutputHandler { diff --git a/Tests/SwiftlyTests/UpdateTests.swift b/Tests/SwiftlyTests/UpdateTests.swift index c8eb555a..d02527ef 100644 --- a/Tests/SwiftlyTests/UpdateTests.swift +++ b/Tests/SwiftlyTests/UpdateTests.swift @@ -84,6 +84,9 @@ final class UpdateTests: SwiftlyTests { /// Verify that a toolchain can be updated to the latest patch version of that toolchain's minor version. func testUpdateToLatestPatch() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + try await self.withTestHome { try await self.withMockedToolchain { try await self.installMockedToolchain(selector: "5.9.0") @@ -111,16 +114,16 @@ final class UpdateTests: SwiftlyTests { func testUpdateInUse() async throws { try await self.withTestHome { try await self.withMockedToolchain { - try await self.installMockedToolchain(selector: "5.9.0") + try await self.installMockedToolchain(selector: "6.0.0") var update = try self.parseCommand(Update.self, ["update", "-y", "--no-verify", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"]) try await update.run() let config = try Config.load() let inUse = config.inUse!.asStableRelease! - XCTAssertGreaterThan(inUse, .init(major: 5, minor: 9, patch: 0)) - XCTAssertEqual(inUse.major, 5) - XCTAssertEqual(inUse.minor, 9) + XCTAssertGreaterThan(inUse, .init(major: 6, minor: 0, patch: 0)) + XCTAssertEqual(inUse.major, 6) + XCTAssertEqual(inUse.minor, 0) XCTAssertGreaterThan(inUse.patch, 0) try await self.validateInstalledToolchains( @@ -135,6 +138,9 @@ final class UpdateTests: SwiftlyTests { /// Verifies that snapshots, both from the main branch and from development branches, can be updated. func testUpdateSnapshot() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + let branches: [ToolchainVersion.Snapshot.Branch] = [ .main, .release(major: 6, minor: 0), @@ -170,20 +176,20 @@ final class UpdateTests: SwiftlyTests { func testUpdateSelectsLatestMatchingStableRelease() async throws { try await self.withTestHome { try await self.withMockedToolchain { - try await self.installMockedToolchain(selector: "5.9.1") - try await self.installMockedToolchain(selector: "5.9.0") + try await self.installMockedToolchain(selector: "6.0.1") + try await self.installMockedToolchain(selector: "6.0.0") - var update = try self.parseCommand(Update.self, ["update", "-y", "5.9", "--no-verify", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"]) + var update = try self.parseCommand(Update.self, ["update", "-y", "6.0", "--no-verify", "--post-install-file=\(Swiftly.currentPlatform.getTempFilePath().path)"]) try await update.run() let config = try Config.load() let inUse = config.inUse!.asStableRelease! - XCTAssertEqual(inUse.major, 5) - XCTAssertEqual(inUse.minor, 9) + XCTAssertEqual(inUse.major, 6) + XCTAssertEqual(inUse.minor, 0) XCTAssertGreaterThan(inUse.patch, 1) try await self.validateInstalledToolchains( - [config.inUse!, .init(major: 5, minor: 9, patch: 0)], + [config.inUse!, .init(major: 6, minor: 0, patch: 0)], description: "update with ambiguous selector should update the latest matching toolchain" ) } @@ -192,6 +198,9 @@ final class UpdateTests: SwiftlyTests { /// Verify that the latest of all the matching snapshot toolchains is updated. func testUpdateSelectsLatestMatchingSnapshotRelease() async throws { + let snapshotsAvailable = try await self.snapshotsAvailable() + try XCTSkipIf(!snapshotsAvailable) + let branches: [ToolchainVersion.Snapshot.Branch] = [ .main, .release(major: 6, minor: 0), diff --git a/install/run-tests.sh b/install/run-tests.sh deleted file mode 100755 index f8eea39c..00000000 --- a/install/run-tests.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Script used to execute all the swiftly-install tests found in install/tests. -# This should be run from the install directory of the repository. -# WARNING: these tests make changes to the local filesystem and are intended to be run in a containerized environment. - -source ./test-util.sh - -line_print () { - echo "==============================================" -} - -if ! has_command "curl"; then - echo "curl must be installed in order to run the tests" - exit 1 -fi - -tests_failed=0 -tests_passed=0 -failed_tests=() - -for t in tests/*.sh; do - test_name=$(basename "$t") - line_print - echo "Running test $test_name" - echo "" - if bash "$t"; then - ((tests_passed++)) - else - ((tests_failed++)) - failed_tests+=("$test_name") - fi -done - -line_print - -if [[ "$tests_failed" -gt 0 ]]; then - echo "" - echo "$tests_failed test(s) FAILED, $tests_passed test(s) PASSED" - echo "Failed tests:" - for failed_test in "${failed_tests[@]}"; do - echo "- $failed_test" - done - exit 1 -else - echo "All tests PASSED" - exit 0 -fi diff --git a/install/swiftly-install.sh b/install/swiftly-install.sh deleted file mode 100755 index 780475cc..00000000 --- a/install/swiftly-install.sh +++ /dev/null @@ -1,716 +0,0 @@ -#!/usr/bin/env bash - -# swiftly-install -# Script used to install and configure swiftly. -# -# This script will download the latest released swiftly executable and install it -# to $SWIFTLY_BIN_DIR, or ~/.local/bin (Linux) and ~/Library/Application Support/swiftly/bin (macOS) -# if that variable isn't specified. -# -# This script will also create a directory at $SWIFTLY_HOME_DIR, or -# $XDG_DATA_HOME/swiftly if that variable isn't specified. If XDG_DATA_HOME is also unset, -# ~/.local/share/swiftly (Linux) or ~/Library/Application Support/swift (macOS) will be used as a default -# instead. swiftly will use this directory to store platform information, downloaded toolchains, and other -# state required to manage the toolchains. -# -# After installation, the script will create $SWIFTLY_HOME_DIR/env.{sh,fish}, which can be sourced -# to properly set up the environment variables required to run swiftly. Unless --no-modify-profile -# was specified, the script will also update ~/.profile, ~/.bash_profile, ~/.bash_login, ~/.zprofile, -# $XDG_CONFIG_HOME/fish/conf.d/swiftly.fish, or ~/.config/fish/conf.d/swiftly.fish depending on -# the value of $SHELL and the existence of the files, to source the env.{sh,fish} file on login. -# This will ensure that future logins will automatically configure SWIFTLY_HOME_DIR, SWIFTLY_BIN_DIR, -# and PATH. -# -# Unless the --disable-confirmation flag is set, this script will allow the runner to -# configure either of those two directory paths. -# -# Unless the --no-install-system-deps flag is set, this script will attempt to install Swift's -# system dependencies using the system package manager. -# -# curl and getopt (from the util-linux package) are required to run this script. - -has_command () { - command -v "$1" > /dev/null -} - -read_input_with_default () { - echo -n "> " - # The installer script is usually run by "curl ... | bash", which means that - # stdin is not a tty but the script content itself. In that case, "read" builtin - # command receives EOF immediately. To avoid that, we use /dev/tty as stdin explicitly. - # SWIFTLY_READ_FROM_STDIN is used for testing interactive input - if [[ -t 0 ]] || [[ ${SWIFTLY_READ_FROM_STDIN+set} == "set" ]]; then - read READ_INPUT_RETURN - else - read READ_INPUT_RETURN < /dev/tty - fi - - if [ -z "$READ_INPUT_RETURN" ]; then - READ_INPUT_RETURN="$1" - fi -} - -yn_prompt () { - if [[ "$1" == "true" ]]; then - echo "(Y/n)" - else - echo "(y/N)" - fi -} - -# Read a y/n input. -# First argument is the default value (must be "true" or "false"). -# -# Sets READ_INPUT_RETURN to "true" for an input of "y" or "Y", "false" for an input -# of "n" or "N", or the default value for a blank input -# -# For all other inputs, a message is printed and the user is prompted again. -read_yn_input () { - while [[ true ]]; do - read_input_with_default "$1" - - case "$READ_INPUT_RETURN" in - "y" | "Y") - READ_INPUT_RETURN="true" - return - ;; - - "n" | "N") - READ_INPUT_RETURN="false" - return - ;; - - "$1") - return - ;; - - *) - echo "Please input either \"y\" or \"n\", or press ENTER to use the default." - ;; - esac - done -} - -# Replaces the actual path to $HOME at the beginning of the provided string argument with -# the string "$HOME". This is used when printing to stdout. -# e.g. "home/user/.local/bin" => "$HOME/.local/bin" -replace_home_path () { - if [[ "$1" =~ ^"$HOME"(/|$) ]]; then - echo "\$HOME${1#$HOME}" - else - echo "$1" - fi -} - -# Replaces the string "$HOME" or "~" in the argument with the actual value of $HOME. -# e.g. "$HOME/.local/bin" => "/home/user/.local/bin" -# e.g. "~/.local/bin" => "/home/user/.local/bin" -expand_home_path () { - echo "${1/#@(~|\$HOME)/$HOME}" -} - -# Prints the provided argument using the terminal's bold text effect. -bold () { - echo "$(tput bold)$1$(tput sgr0)" -} - -# Fetch the list of required system dependencies from the apple/swift-docker -# repository and attempt to install them using the system's package manager. -# -# $docker_platform_name, $docker_platform_version, and $package manager need -# to be set before calling this function. -install_system_deps () { - if [[ "$(id --user)" != "0" ]] && ! has_command sudo ; then - echo "Warning: sudo not installed and current user is not root, skipping system dependency installation." - return - elif ! has_command "$package_manager" ; then - echo "Warning: package manager \"$package_manager\" not found, skipping system dependency installation." - return - fi - - dockerfile_url="https://raw.githubusercontent.com/apple/swift-docker/main/nightly-main/$docker_platform_name/$docker_platform_version/Dockerfile" - dockerfile="$(curl --silent --retry 3 --location --fail $dockerfile_url)" - if [[ "$?" -ne 0 ]]; then - echo "Error enumerating system dependencies, skipping installation of system dependencies." - fi - - # Find the line number of the RUN command associated with installing system dependencies. - beg_line_num=$(printf "$dockerfile" | grep -n --max-count=1 "$package_manager.*install" | cut -d ":" -f1) - - # Starting from there, find the first line that starts with an & or doesn't end in a backslash. - relative_end_line_num=$(printf "$dockerfile" | - tail --lines=+"$((beg_line_num + 1))" | - grep -n --max-count=1 --invert-match '[[:space:]]*[^&].*\\$' | cut -d ":" -f1) - end_line_num=$((beg_line_num + relative_end_line_num)) - - # Read the lines between those two, deleting any spaces and backslashes. - readarray -t package_list < <(printf "$dockerfile" | sed -n "$((beg_line_num + 1)),${end_line_num}p" | sed -r 's/[\ ]//g') - - # If the installation command from the Dockerfile included some cleanup as part of a second command, drop that. - if [[ "${package_list[-1]}" =~ ^\&\& ]]; then - unset 'package_list[-1]' - fi - - # Always install gpg, since swiftly itself needs it for signature verification. - package_list+=("gpg") - - install_args=(--quiet -y) - - # Disable errexit since failing to install system dependencies is not swiftly installation-fatal. - set +o errexit - if [[ "$(id --user)" == "0" ]]; then - "$package_manager" install "${install_args[@]}" "${package_list[@]}" - else - sudo "$package_manager" install "${install_args[@]}" "${package_list[@]}" - fi - if [[ "$?" -ne 0 ]]; then - echo "System dependency installation failed." - if [[ "$package_manager" == "apt-get" ]]; then - echo "You may need to run apt-get update before installing system dependencies." - fi - fi - set -o errexit -} - -set_platform_ubuntu () { - docker_platform_name="ubuntu" - package_manager="apt-get" - export DEBIAN_FRONTEND=noninteractive - - PLATFORM_NAME="ubuntu$1$2" - PLATFORM_NAME_FULL="ubuntu$1.$2" - docker_platform_version="$1.$2" - - if [[ -z "$PLATFORM_NAME_PRETTY" ]]; then - PLATFORM_NAME_PRETTY="Ubuntu $1.$2" - fi -} - -set_platform_amazonlinux () { - PLATFORM_NAME="amazonlinux$1" - PLATFORM_NAME_FULL="amazonlinux$1" - docker_platform_name="amazonlinux" - docker_platform_version="$1" - package_manager="yum" - - if [[ -z "$PLATFORM_NAME_PRETTY" ]]; then - PLATFORM_NAME_PRETTY="Amazon Linux $1" - fi -} - -set_platform_rhel () { - PLATFORM_NAME="ubi$1" - PLATFORM_NAME_FULL="ubi$1" - docker_platform_name="rhel-ubi" - docker_platform_version="$1" - package_manager="yum" - - if [[ -z "$PLATFORM_NAME_PRETTY" ]]; then - PLATFORM_NAME_PRETTY="RHEL 9" - fi -} - -detect_platform () { - if [[ -f "/etc/os-release" ]]; then - OS_RELEASE="/etc/os-release" - elif [[ -f "/usr/lib/os-release" ]]; then - OS_RELEASE="/usr/lib/os-release" - else - manually_select_platform - fi - - source "$OS_RELEASE" - PLATFORM_NAME_PRETTY="$PRETTY_NAME" - - case "$ID$ID_LIKE" in - *"amzn"*) - if [[ "$VERSION_ID" != "2" ]]; then - manually_select_platform - else - set_platform_amazonlinux "2" - fi - ;; - - *"ubuntu"*) - case "$UBUNTU_CODENAME" in - "jammy") - set_platform_ubuntu "22" "04" - ;; - - "focal") - set_platform_ubuntu "20" "04" - ;; - - "bionic") - set_platform_ubuntu "18" "04" - ;; - - *) - manually_select_platform - ;; - esac - ;; - - *"rhel"*) - if [[ "$VERSION_ID" != 9* ]]; then - manually_select_platform - else - set_platform_rhel "9" - fi - ;; - - *) - manually_select_platform - ;; - esac -} - -manually_select_platform () { - if [[ "$DISABLE_CONFIRMATION" == "true" ]]; then - echo "Error: Unsupported platform: $PRETTY_NAME" - exit 1 - fi - echo "$PLATFORM_NAME_PRETTY is not an officially supported platform, but the toolchains for another platform may still work on it." - echo "" - echo "Please select the platform to use for toolchain downloads:" - - echo "0) Cancel" - echo "1) Ubuntu 22.04" - echo "2) Ubuntu 20.04" - echo "3) Ubuntu 18.04" - echo "4) RHEL 9" - echo "5) Amazon Linux 2" - - read_input_with_default "0" - case "$READ_INPUT_RETURN" in - "1" | "1)") - set_platform_ubuntu "22" "04" - ;; - - "2" | "2)") - set_platform_ubuntu "20" "04" - ;; - - "3" | "3)") - set_platform_ubuntu "18" "04" - ;; - - "4" | "4)") - set_platform_rhel "9" - ;; - - "5" | "5)") - set_platform_amazonlinux "2" - ;; - - *) - echo "Cancelling installation." - exit 0 - ;; - esac -} - -verify_getopt_install () { - if ! has_command "getopt" ; then - return 1 - fi - - getopt --test - # getopt --test exiting with status code 4 implies getopt from util-linux is being used, which we need. - [[ "$?" -eq 4 ]] - return "$?" -} - -SWIFTLY_INSTALL_VERSION="0.4.0" - -MODIFY_PROFILE="true" -SWIFTLY_INSTALL_SYSTEM_DEPS="true" - -if ! has_command "curl" ; then - echo "Error: curl must be installed to download swiftly" - exit 1 -fi - -IS_MACOS="false" -if [ "$(uname -s)" == "Darwin" ] ; then - IS_MACOS="true" -fi - -if [ "$IS_MACOS" == "false" ]; then - if ! verify_getopt_install ; then - echo "Error: getopt must be installed from the util-linux package to run swiftly-install" - exit 1 - fi -fi - -set -o errexit -shopt -s extglob - -if [ "$IS_MACOS" == "true" ]; then - args=$(getopt ynohvp: $*) -else - args=$(getopt --options ynohvp: --longoptions disable-confirmation,no-modify-profile,no-install-system-deps,help,version,platform:,overwrite --name swiftly-install -- "${@}") -fi - -eval "set -- ${args}" - -while [ true ]; do - case "$1" in - "--help" | "-h") - cat < Specifies which platform's toolchains swiftly will download. If - unspecified, the platform will be automatically detected. Available - options are "ubuntu22.04", "ubuntu20.04", "ubuntu18.04", "rhel9", and - "amazonlinux2". (LINUX ONLY) - -o, --overwrite Overwrite the existing swiftly installation found at the configured - SWIFTLY_HOME, if any. If this option is unspecified and an existing - installation is found, the swiftly executable will be updated, but - the rest of the installation will not be modified. - -h, --help Prints help information. - -v, --version Prints version information. - -NOTES: - macOS only works with the short options (e.g. -v and not --version). - -EOF - exit 0 - ;; - - "--disable-confirmation" | "-y") - DISABLE_CONFIRMATION="true" - shift - ;; - - "--no-modify-profile" | "-n") - MODIFY_PROFILE="false" - shift - ;; - - "--no-install-system-deps") - SWIFTLY_INSTALL_SYSTEM_DEPS="false" - shift - ;; - - "--no-import-pgp-keys") - swiftly_import_pgp_keys="false" - shift - ;; - - "--version" | "-v") - echo "$SWIFTLY_INSTALL_VERSION" - exit 0 - ;; - - "--platform" | "-p") - case "$2" in - "ubuntu22.04") - set_platform_ubuntu "22" "04" - ;; - - "ubuntu20.04") - set_platform_ubuntu "20" "04" - ;; - - "ubuntu18.04") - set_platform_ubuntu "18" "04" - ;; - - "amazonlinux2") - set_platform_amazonlinux "2" - ;; - - "rhel9") - set_platform_rhel "9" - ;; - - *) - echo "Error: unrecognized platform $2" - exit 1 - ;; - esac - shift 2 - ;; - - "--overwrite" | "-o") - overwrite_existing_intallation="true" - shift - ;; - - --) - shift - break - ;; - *) - echo "Error: unrecognized option \"$1\"" - if [ "$IS_MACOS" == "true" ]; then - echo "Note that on macOS you must use the short options (e.g. -v, not --version)." - fi - exit 1 - ;; - esac -done - -if [ "$IS_MACOS" == "false" ]; then - if [[ -z "$PLATFORM_NAME" ]]; then - detect_platform - fi -fi - -RAW_ARCH="$(uname -m)" -case "$RAW_ARCH" in - "x86_64") - ARCH="x86_64" - PLATFORM_ARCH="null" - ;; - - "aarch64" | "arm64") - ARCH="aarch64" - PLATFORM_ARCH='"aarch64"' - ;; - - *) - echo "Error: Unsupported CPU architecture: $RAW_ARCH" - exit 1 - ;; -esac - -if [ "$IS_MACOS" == "false" ]; then - JSON_OUT=$(cat < "$HOME_DIR/config.json" - fi - - # Verify the downloaded executable works. The script will exit if this fails due to errexit. - SWIFTLY_HOME_DIR="$HOME_DIR" SWIFTLY_BIN_DIR="$BIN_DIR" "$BIN_DIR/swiftly" --version > /dev/null - - echo "$ENV_OUT" > "$HOME_DIR/$ENV_FILE" - - if [[ "$MODIFY_PROFILE" == "true" ]]; then - # Only append the line if it isn't in .profile already. - if [[ ! -f "$PROFILE_FILE" ]] || [[ ! "$(cat $PROFILE_FILE)" =~ "$SOURCE_LINE" ]]; then - echo "$SOURCE_LINE" >> "$PROFILE_FILE" - fi - fi -fi - -if [ "$IS_MACOS" == "false" ]; then - if [[ "$SWIFTLY_INSTALL_SYSTEM_DEPS" != "false" ]]; then - echo "" - echo "Installing Swift's system dependencies via $package_manager (note: this may require root access)..." - install_system_deps - fi -fi - -if [ "$IS_MACOS" == "false" ]; then - if [[ "$swiftly_import_pgp_keys" != "false" ]]; then - if has_command gpg ; then - echo "" - echo "Importing Swift's PGP keys..." - curl --silent --retry 3 --location --fail https://swift.org/keys/all-keys.asc | gpg --import - - else - echo "gpg not installed, skipping import of Swift's PGP keys." - fi - fi -fi - -echo "" -echo "swiftly has been successfully installed!" -echo "" - -if ! has_command "swiftly" || [[ "$HOME_DIR" != "$DEFAULT_HOME_DIR" || "$BIN_DIR" != "$DEFAULT_BIN_DIR" ]] ; then - if [[ "$MODIFY_PROFILE" == "true" ]]; then - echo "Once you log in again, swiftly should be accessible from your PATH." - fi - echo "To begin using swiftly from your current shell, first run the following command:" - echo "" - echo " $SOURCE_LINE" - echo "" - echo "Then to install the latest version of Swift, run 'swiftly install latest'" -else - echo "To install the latest version of Swift, run 'swiftly install latest'" -fi - -if has_command "swift" ; then - echo "" - echo "Warning: existing installation of Swift detected at $(command -v swift)" - echo "To ensure swiftly-installed toolchains can be found by the shell, uninstall any existing Swift installation(s)." - echo "To ensure the current shell can find swiftly-installed toolchains, you may also need to run 'hash -r'." -fi diff --git a/install/test-util.sh b/install/test-util.sh deleted file mode 100644 index f2c2ffad..00000000 --- a/install/test-util.sh +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash - -# Common utility functionality used in the various bash tests for swiftly-install.sh. - -export SWIFTLY_READ_FROM_STDIN=1 - -test_log () { - echo "===========================" - echo "$1" - echo "===========================" -} - -has_command () { - command -v "$1" > /dev/null -} - -test_name () { - basename "$0" -} - -test_fail () { - if [ ! -z "$1" ]; then - printf "$1\n" - fi - - if [ ! -z "$3" ]; then - printf "actual: $2\n" - printf "expected: $3\n" - fi - echo "" - echo "$(test_name) FAILED" - exit 1 -} - -test_pass () { - echo "" - echo "$(test_name) PASSED" - exit 0 -} - -get_os () { - if [[ -f "/etc/os-release" ]]; then - OS_RELEASE="/etc/os-release" - elif [[ -f "/usr/lib/os-release" ]]; then - OS_RELEASE="/usr/lib/os-release" - else - echo "Error: could not detect OS information" - exit 1 - fi - - source "$OS_RELEASE" - - case "$ID" in - "amzn") - echo "amazonlinux2" - ;; - - "ubuntu") - case "$UBUNTU_CODENAME" in - "jammy") - echo "ubuntu2204" - ;; - - "focal") - echo "ubuntu2004" - ;; - - "bionic") - echo "ubuntu1804" - ;; - - *) - echo "Unsupported Ubuntu version: $PRETTY_NAME" - exit 1 - ;; - esac - ;; - - "rhel") - echo "rhel-ubi9" - ;; - - *) - echo "Unsupported platform: $PRETTY_NAME" - exit 1 - ;; - esac -} diff --git a/install/tests/cancel-install.sh b/install/tests/cancel-install.sh deleted file mode 100755 index f8ab2d43..00000000 --- a/install/tests/cancel-install.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# Tests that installation can be cancelled. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -echo "3" | ./swiftly-install.sh - -if has_command "swiftly" ; then - test_fail "swiftly executable should not have been installed" -fi - -if [[ -d "$HOME/.local/share/swiftly" ]]; then - test_fail "SWIFTLY_HOME_DIR should not have been created" -fi - -test_pass diff --git a/install/tests/custom-home-install.sh b/install/tests/custom-home-install.sh deleted file mode 100755 index 3a0ec831..00000000 --- a/install/tests/custom-home-install.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# Tests that custom install paths that include the string "$HOME" are expanded properly. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -export CUSTOM_HOME_DIR_NAME="home-substitution-test-home" -export CUSTOM_HOME_DIR="$HOME/$CUSTOM_HOME_DIR_NAME" -export CUSTOM_BIN_DIR="$CUSTOM_HOME_DIR/bin" - -cp "$HOME/.profile" "$HOME/.profile.bak" - -cleanup () { - rm -r "$CUSTOM_HOME_DIR" - mv "$HOME/.profile.bak" "$HOME/.profile" -} -trap cleanup EXIT - -printf "2\n\$HOME/${CUSTOM_HOME_DIR_NAME}\n\$HOME/${CUSTOM_HOME_DIR_NAME}/bin\ny\nn\n1\n" | ./swiftly-install.sh - -# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR. -bash --login -c "swiftly --version" - -. "$CUSTOM_HOME_DIR/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -if [[ "$SWIFTLY_HOME_DIR" != "$CUSTOM_HOME_DIR" ]]; then - test_fail "SWIFTLY_HOME_DIR ($SWIFTLY_HOME_DIR) did not equal $CUSTOM_HOME_DIR" -fi - -if [[ "$SWIFTLY_BIN_DIR" != "$CUSTOM_BIN_DIR" ]]; then - test_fail "SWIFTLY_BIN_DIR ($SWIFTLY_BIN_DIR) did not equal $CUSTOM_BIN_DIR" -fi - -if [[ ! -d "$CUSTOM_HOME_DIR/toolchains" ]]; then - test_fail "the toolchains directory was not created in SWIFTLY_HOME_DIR" -fi - -test_pass diff --git a/install/tests/custom-install.sh b/install/tests/custom-install.sh deleted file mode 100755 index 3b0b0c1f..00000000 --- a/install/tests/custom-install.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash - -# Tests that an installation using custom paths works properly. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -export CUSTOM_HOME_DIR="$(pwd)custom-test-home" -export CUSTOM_BIN_DIR="$CUSTOM_HOME_DIR/bin" -export PATH="$CUSTOM_BIN_DIR:$PATH" - -cp "$HOME/.profile" "$HOME/.profile.bak" -cleanup () { - set +o errexit - - mv "$HOME/.profile.bak" "$HOME/.profile" - - if has_command "swiftly" ; then - swiftly uninstall -y latest > /dev/null - fi - - rm -r "$CUSTOM_HOME_DIR" -} -trap cleanup EXIT - -# Custom home dir -# Custom bin dir -# Modify login config (yes) -# Install system dependencies (no) -printf "2\n$CUSTOM_HOME_DIR\n$CUSTOM_BIN_DIR\ny\nn\n1\n" | ./swiftly-install.sh - -# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR. -bash --login -c "swiftly --version" - -. "$CUSTOM_HOME_DIR/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -if [[ "$SWIFTLY_HOME_DIR" != "$CUSTOM_HOME_DIR" ]]; then - test_fail "SWIFTLY_HOME_DIR ($SWIFTLY_HOME_DIR) did not equal $CUSTOM_HOME_DIR" -fi - -if [[ "$SWIFTLY_BIN_DIR" != "$CUSTOM_BIN_DIR" ]]; then - test_fail "SWIFTLY_BIN_DIR ($SWIFTLY_BIN_DIR) did not equal $CUSTOM_BIN_DIR" -fi - -if [[ ! -d "$CUSTOM_HOME_DIR/toolchains" ]]; then - test_fail "the toolchains directory was not created in SWIFTLY_HOME_DIR" -fi - -if [[ -d "$HOME/.local/share/swiftly" ]]; then - test_fail "expected default home directory to not be created, but it was" -fi - -swiftly install 5.8.0 - -swift --version - -if [[ ! -d "$CUSTOM_HOME_DIR/toolchains/5.8.0" ]]; then - test_fail "the toolchain was not installed to the custom directory" -fi - -if [[ -d "$HOME/.local/share/swiftly" ]]; then - test_fail "expected default home directory to not be created, but it was" -fi - -test_pass diff --git a/install/tests/custom-tilde-install.sh b/install/tests/custom-tilde-install.sh deleted file mode 100755 index 5c0d1de8..00000000 --- a/install/tests/custom-tilde-install.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Tests that custom install paths that include the "~" character are expanded properly. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -export CUSTOM_HOME_DIR_NAME="tilde-substitution-test-home" -export CUSTOM_HOME_DIR="$HOME/$CUSTOM_HOME_DIR_NAME" -export CUSTOM_BIN_DIR="$CUSTOM_HOME_DIR/bin" - -cp "$HOME/.profile" "$HOME/.profile.bak" - -cleanup () { - rm -r "$CUSTOM_HOME_DIR" - mv "$HOME/.profile.bak" "$HOME/.profile" -} -trap cleanup EXIT - -# Make sure that the "~" character is handled properly. -printf "2\n~/${CUSTOM_HOME_DIR_NAME}\n~/${CUSTOM_HOME_DIR_NAME}/bin\ny\nn\n1\n" | ./swiftly-install.sh - -# .profile should be updated to update PATH and SWIFTLY_HOME_DIR/SWIFTLY_BIN_DIR. -bash --login -c "swiftly --version" - -. "$CUSTOM_HOME_DIR/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -if [[ "$SWIFTLY_HOME_DIR" != "$CUSTOM_HOME_DIR" ]]; then - test_fail "SWIFTLY_HOME_DIR ($SWIFTLY_HOME_DIR) did not equal $CUSTOM_HOME_DIR" -fi - -if [[ "$SWIFTLY_BIN_DIR" != "$CUSTOM_BIN_DIR" ]]; then - test_fail "SWIFTLY_BIN_DIR ($SWIFTLY_BIN_DIR) did not equal $CUSTOM_BIN_DIR" -fi - -if [[ ! -d "$CUSTOM_HOME_DIR/toolchains" ]]; then - test_fail "the toolchains directory was not created in SWIFTLY_HOME_DIR" -fi - -test_pass diff --git a/install/tests/default-install.sh b/install/tests/default-install.sh deleted file mode 100755 index df218101..00000000 --- a/install/tests/default-install.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash - -# Tests that an unconfigured installation works properly. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cp "$HOME/.profile" "$HOME/.profile.bak" - -cleanup () { - set +o errexit - - mv "$HOME/.profile.bak" "$HOME/.profile" - - if has_command "swiftly" ; then - swiftly uninstall -y latest > /dev/null - fi - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -case "$(get_os)" in - "ubuntu1804") - system_deps=(binutils - git - libc6-dev - libcurl4-openssl-dev - libedit2 - libgcc-5-dev - libpython3.6 - libstdc++-5-dev - libxml2-dev - pkg-config - tzdata - zip - zlib1g-dev) - ;; - - "ubuntu2004") - system_deps=(binutils - git - gnupg2 - libc6-dev - libcurl4-openssl-dev - libedit2 - libgcc-9-dev - libpython3.8 - libstdc++-9-dev - libxml2-dev - libz3-dev - pkg-config - tzdata - zip - zlib1g-dev) - ;; - - "ubuntu2204") - system_deps=(binutils - git - gnupg2 - libc6-dev - libcurl4-openssl-dev - libedit2 - libgcc-11-dev - libpython3-dev - libstdc++-11-dev - libxml2-dev - libz3-dev - pkg-config - tzdata - zip - zlib1g-dev) - ;; - - "amazonlinux2") - system_deps=(binutils - gcc - git - glibc-static - libcurl-devel - libedit - libicu - libxml2-devel - tar - unzip - zip - zlib-devel) - ;; - - "rhel-ubi9") - system_deps=(git - gcc-c++ - libcurl-devel - libedit-devel - libuuid-devel - libxml2-devel - ncurses-devel - python3-devel - rsync - sqlite-devel - unzip - zip) - ;; - - *) - echo "Unrecognized platform" - exit 1 - ;; -esac - -if has_command apt-get ; then - apt-get update - apt-get remove -y "${system_deps[@]}" -elif has_command yum ; then - yum remove -y "${system_deps[@]}" -fi - -printf "1\n" | ./swiftly-install.sh - -# .profile should be updated to update PATH. -bash --login -c "swiftly --version" - -. "$HOME/.local/share/swiftly/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -if [[ ! -d "$HOME/.local/share/swiftly/toolchains" ]]; then - test_fail "the toolchains directory was not created in SWIFTLY_HOME_DIR" -fi - -echo "Verifying system dependencies were installed..." -for dep in "${system_deps[@]}"; do - if has_command dpkg ; then - if ! dpkg --status "$dep" > /dev/null ; then - test_fail "System dependency $dep was not installed properly" - fi - elif has_command rpm ; then - if ! rpm -q "$dep" > /dev/null ; then - test_fail "System dependency $dep was not installed properly" - fi - fi - echo "System dependency $dep was installed successfully" -done - - -if ! gpg --list-keys Swift ; then - test_fail "Swift PGP keys were not installed by default." -fi - -swiftly install latest - -swift --version - -test_pass diff --git a/install/tests/disable-prompt.sh b/install/tests/disable-prompt.sh deleted file mode 100755 index fbe66807..00000000 --- a/install/tests/disable-prompt.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Tests that the --disable-confirmation argument works. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cp "$HOME/.profile" "$HOME/.profile.bak" - -cleanup () { - mv "$HOME/.profile.bak" "$HOME/.profile" - rm -r "$HOME/.local/share/swiftly" - rm -r "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -if has_command apt-get ; then - apt-get remove -y zlib1g-dev -elif has_command yum ; then - yum remove -y libcurl-devel -fi - -./swiftly-install.sh -y - -# .profile should be updated to update PATH. -bash --login -c "swiftly --version" - -. "$HOME/.local/share/swiftly/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -if has_command dpkg ; then - if ! dpkg --status zlib1g-dev ; then - test_fail "System dependencies were not installed properly" - fi -elif has_command rpm ; then - if ! rpm -q libcurl-devel ; then - test_fail "System dependencies were not installed properly" - fi -fi - -test_pass diff --git a/install/tests/no-install-dependencies.sh b/install/tests/no-install-dependencies.sh deleted file mode 100755 index 352f088c..00000000 --- a/install/tests/no-install-dependencies.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash - -# Tests passing --no-install-system-deps disables installing system dependencies. -# Also verifies that interactive customization also does cancels them. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cleanup () { - set +o errexit - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -verify_dependencies_not_installed () { - if has_command dpkg ; then - if dpkg --status zlib1g-dev ; then - test_fail "System dependencies were installed when they shouldn't have been" - fi - elif has_command rpm ; then - if rpm -q libcurl-devel ; then - test_fail "System dependencies were installed when they shouldn't have been" - fi - fi -} - -if has_command apt-get ; then - apt-get remove -y zlib1g-dev -elif has_command yum ; then - yum remove -y libcurl-devel -fi - -echo "1" | ./swiftly-install.sh --no-install-system-deps - -verify_dependencies_not_installed - -# Use all defaults except "n" for system dependency installation. -printf "2\n\n\n\nn\n1\ny\n" | ./swiftly-install.sh - -verify_dependencies_not_installed - -test_pass diff --git a/install/tests/overwrite.sh b/install/tests/overwrite.sh deleted file mode 100755 index 7a935115..00000000 --- a/install/tests/overwrite.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env bash - -# Tests that swiftly-install properly handles an existing installation of swiftly. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -export SWIFTLY_HOME_DIR="./overwrite-test-home" -export SWIFTLY_BIN_DIR="./overwrite-bin-dir" - -cp "$HOME/.profile" "$HOME/.profile.bak" - -cleanup () { - mv "$HOME/.profile.bak" "$HOME/.profile" - rm -r "$SWIFTLY_HOME_DIR" - rm -r "$SWIFTLY_BIN_DIR" -} -trap cleanup EXIT - -test_log "Performing initial installation" -./swiftly-install.sh -y --no-install-system-deps - -. "$SWIFTLY_HOME_DIR/env.sh" - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -# Modify the home dir to be able to to tell if it is changed with subsequent installs. -DUMMY_CONFIG_CONTENTS="hello world" -PROFILE_CONTENTS="$(cat $HOME/.profile)" -echo "$DUMMY_CONFIG_CONTENTS" > "$SWIFTLY_HOME_DIR/config.json" - -toolchain_dir="$SWIFTLY_HOME_DIR/toolchains/5.7.3" -mkdir -p "$toolchain_dir/usr/bin" -dummy_executable_name="foo" -touch "$toolchain_dir/usr/bin/$dummy_executable_name" - -# Also set up a symlink as if the toolchain were in use. -ln -s -t $SWIFTLY_BIN_DIR "$toolchain_dir/usr/bin/$dummy_executable_name" - -test_log "Attempting the same installation (no --overwrite flag specified)" -./swiftly-install.sh -y --no-install-system-deps - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -NEW_CONFIG_CONTENTS="$(cat $SWIFTLY_HOME_DIR/config.json)" -if [[ "$NEW_CONFIG_CONTENTS" != "$DUMMY_CONFIG_CONTENTS" ]]; then - test_fail "Expected config to remain unchanged" "$NEW_CONFIG_CONTENTS" "$DUMMY_CONFIG_CONTENTS" -fi - -if ! [ -L "$SWIFTLY_BIN_DIR/$dummy_executable_name" ]; then - test_fail "Expected symlink to still exist, but it has been deleted" -fi - -if [[ ! -d "$SWIFTLY_HOME_DIR/toolchains/5.7.3" ]]; then - test_fail "Expected installed toolchain directory to still exist, but it has been deleted" -fi - -test_log "Attempting the same installation (--overwrite flag is specified)" -./swiftly-install.sh -y --overwrite --no-install-system-deps - -if ! has_command "swiftly" ; then - test_fail "Can't find swiftly on the PATH" -fi - -NEW_CONFIG_CONTENTS="$(cat $SWIFTLY_HOME_DIR/config.json)" -if [[ "$NEW_CONFIG_CONTENTS" == "DUMMY_CONFIG_CONTENTS" ]]; then - test_fail "Expected config to be reset but it was not" -fi - -if [[ "$(cat $HOME/.profile)" != "$PROFILE_CONTENTS" ]]; then - test_fail "Expected .profile not to be updated on overwrite install" -fi - -if [[ -d "$SWIFTLY_HOME_DIR/toolchains/5.7.3" ]]; then - test_fail "Expected installed toolchain directory to have been overwritten, but it still exists" -fi - -if [ -L "$SWIFTLY_BIN_DIR/$dummy_executable_name" ]; then - test_fail "Expected symlink to have been deleted, but it still exists" -fi - -swiftly --version - -test_pass diff --git a/install/tests/platform-option.sh b/install/tests/platform-option.sh deleted file mode 100755 index fe776f93..00000000 --- a/install/tests/platform-option.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Tests that a platform can be manually specified via the --platform option. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cleanup () { - set +o errexit - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -platforms=("ubuntu22.04" "ubuntu20.04" "ubuntu18.04" "amazonlinux2" "rhel9") - -for platform in "${platforms[@]}"; do - ./swiftly-install.sh --overwrite --disable-confirmation --no-install-system-deps --platform "$platform" - cat $HOME/.local/share/swiftly/config.json - - if [[ "$platform" == "rhel9" ]]; then - platform="ubi9" - fi - - if ! grep -q "\"nameFull\": \"$platform\"" "$HOME/.local/share/swiftly/config.json" ; then - test_fail "platform option had no effect for platform \"$platform\"" - fi -done - -test_pass diff --git a/install/tests/update-bash-profile.sh b/install/tests/update-bash-profile.sh deleted file mode 100755 index ff89b805..00000000 --- a/install/tests/update-bash-profile.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# Tests that .zprofile is updated properly if the user's shell is zsh. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cleanup () { - set +o errexit - - if [[ -f "$HOME/.bash_profile" ]]; then - rm "$HOME/.bash_profile" - fi - rm "$HOME/.bash_login" - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -touch "$HOME/.bash_profile" -touch "$HOME/.bash_login" -export SHELL="bash" - -echo "1" | ./swiftly-install.sh --no-install-system-deps - -if [[ ! "$(cat $HOME/.bash_profile)" =~ "swiftly/env.sh" ]]; then - test_fail "install did not update .bash_profile" -fi - -if [[ "$(cat $HOME/.bash_login)" != "" ]]; then - test_fail "install updated .bash_login when .bash_profile existed" -fi - -rm "$HOME/.bash_profile" -./swiftly-install.sh -y --overwrite --no-install-system-deps - -if [[ -f "$HOME/.bash_profile" ]]; then - test_fail "install created .bash_profile when it should not have" -fi - -if [[ ! "$(cat $HOME/.bash_login)" =~ "swiftly/env.sh" ]]; then - test_fail "install did not update .bash_login" -fi - -test_pass diff --git a/install/tests/update-fish-config.sh b/install/tests/update-fish-config.sh deleted file mode 100755 index 2f1fc525..00000000 --- a/install/tests/update-fish-config.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# Tests that swiftly.fish is updated properly if the user's shell is fish. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cleanup () { - set +o errexit - - rm "$HOME/.config/fish/conf.d/swiftly.fish" - rm "$HOME/.xdg/config/fish/conf.d/swiftly.fish" - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -export SHELL="fish" - -mkdir -p "$HOME/.config/fish/conf.d" -echo "1" | ./swiftly-install.sh --no-install-system-deps - -if [[ ! "$(cat $HOME/.config/fish/conf.d/swiftly.fish)" =~ "swiftly/env.fish" ]]; then - test_fail "install did not update ~/.config/fish/conf.d/swiftly.fish" -fi - -export XDG_CONFIG_HOME="$HOME/.xdg/config" -mkdir -p "$XDG_CONFIG_HOME/fish/conf.d" -./swiftly-install.sh -y --overwrite --no-install-system-deps - -if [[ ! "$(cat $XDG_CONFIG_HOME/fish/conf.d/swiftly.fish)" =~ "swiftly/env.fish" ]]; then - test_fail "install did not update \$XDG_CONFIG_HOME/fish/conf.d/swiftly.fish" -fi - -test_pass diff --git a/install/tests/update-zprofile.sh b/install/tests/update-zprofile.sh deleted file mode 100755 index 5eb7cc1d..00000000 --- a/install/tests/update-zprofile.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# Tests that .zprofile is updated properly if the user's shell is zsh. -# WARNING: this test makes changes to the local filesystem and is intended to be run in a containerized environment. - -set -o errexit -source ./test-util.sh - -cleanup () { - set +o errexit - - rm "$HOME/.zprofile" - - rm -r "$HOME/.local/share/swiftly" - rm "$HOME/.local/bin/swiftly" -} -trap cleanup EXIT - -touch "$HOME/.zprofile" -export SHELL="zsh" - -echo "1" | ./swiftly-install.sh --no-install-system-deps - -if [[ ! "$(cat $HOME/.zprofile)" =~ "swiftly/env.sh" ]]; then - test_fail "install did not update .zprofile" -fi - -test_pass diff --git a/scripts/prep-gh-action.sh b/scripts/prep-gh-action.sh new file mode 100755 index 00000000..c1306e39 --- /dev/null +++ b/scripts/prep-gh-action.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +apt-get --help && apt-get update && apt-get -y install curl make +yum --help && (curl --help && yum -y install curl) && yum install make + +(cat /etc/os-release | grep bookworm) && apt-get -y install libstdc++-12-dev gnupg2 +(cat /etc/os-release | grep 'Fedora Linux 39') && yum -y install libstdc++-devel libstdc++-static + +exit 0