Convert more fastlane tools to swift commands (#5163)
* Upload dSYMs to sentry using swift and the sentry-cli instead of fastlane. * Replace the fastlane `config_production` lane with a swift tools `ConfigureProduction` command. * Replace Zsh with CI.run in the new ConfigureNightly command * Replace the fastlane `tag_nightly` lane with a swift tools `TagNightly` command. * Print out all the commands going through CI.Run and their arguments * Address PR comments
This commit is contained in:
@@ -11,7 +11,9 @@ struct CI: ParsableCommand {
|
||||
UITests.self,
|
||||
IntegrationTests.self,
|
||||
RunTests.self,
|
||||
ConfigureNightly.self
|
||||
ConfigureNightly.self,
|
||||
ConfigureProduction.self,
|
||||
TagNightly.self
|
||||
])
|
||||
|
||||
static let testOutputDirectory = "test_output"
|
||||
@@ -106,6 +108,8 @@ struct CI: ParsableCommand {
|
||||
environment: Environment = .inherit,
|
||||
output: Output = .standardOutput,
|
||||
error: Error = .standardError) async throws -> CollectedResult<Output, Error> {
|
||||
logger.info("Running \(executable), with arguments: \(arguments)")
|
||||
|
||||
let result = try await Subprocess.run(executable,
|
||||
arguments: arguments,
|
||||
environment: environment,
|
||||
|
||||
@@ -17,8 +17,8 @@ struct ConfigureNightly: AsyncParsableCommand {
|
||||
|
||||
try addNightlyVariant()
|
||||
|
||||
try Zsh.run(command: "swift run pipeline update-foss-secrets")
|
||||
try Zsh.run(command: "xcodegen")
|
||||
try await CI.run(.name("swift"), ["run", "pipeline", "update-foss-secrets"])
|
||||
try await CI.run(.name("xcodegen"))
|
||||
|
||||
let releaseVersion = try CI.readMarketingVersion()
|
||||
try await generateAppIconBanner(version: releaseVersion, buildNumber: buildNumber)
|
||||
|
||||
12
Tools/Sources/Commands/CI/ConfigureProduction.swift
Normal file
12
Tools/Sources/Commands/CI/ConfigureProduction.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
import ArgumentParser
|
||||
import CommandLineTools
|
||||
import Foundation
|
||||
|
||||
struct ConfigureProduction: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Configures the project for a production build.")
|
||||
|
||||
func run() async throws {
|
||||
try await CI.run(.name("swift"), ["run", "pipeline", "update-foss-secrets"])
|
||||
try await CI.run(.name("xcodegen"))
|
||||
}
|
||||
}
|
||||
51
Tools/Sources/Commands/CI/TagNightly.swift
Normal file
51
Tools/Sources/Commands/CI/TagNightly.swift
Normal file
@@ -0,0 +1,51 @@
|
||||
import ArgumentParser
|
||||
import CommandLineTools
|
||||
import Foundation
|
||||
import Yams
|
||||
|
||||
struct TagNightly: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Tags the current commit as a nightly build and pushes the tag.")
|
||||
|
||||
@Option(help: "The build number to include in the tag.")
|
||||
var buildNumber: String
|
||||
|
||||
func run() async throws {
|
||||
guard !buildNumber.isEmpty else {
|
||||
throw ValidationError("Invalid build number.")
|
||||
}
|
||||
|
||||
guard let apiToken = ProcessInfo.processInfo.environment["GITHUB_TOKEN"],
|
||||
!apiToken.isEmpty else {
|
||||
throw ValidationError("Invalid GitHub API token. Please set the GITHUB_TOKEN environment variable.")
|
||||
}
|
||||
|
||||
let repoURL = try getRepoURL()
|
||||
|
||||
try await CI.run(.name("git"), ["config", "--global", "user.name", "Element CI"])
|
||||
try await CI.run(.name("git"), ["config", "--global", "user.email", "ci@element.io"])
|
||||
|
||||
let currentVersion = try CI.readMarketingVersion()
|
||||
let tagName = "nightly/\(currentVersion).\(buildNumber)"
|
||||
try await CI.run(.name("git"), ["tag", tagName])
|
||||
|
||||
try await CI.run(.name("git"), ["push", "https://\(apiToken)@\(repoURL)", tagName])
|
||||
|
||||
logger.info("\n🚀 Successfully tagged nightly: \(tagName)\n")
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func getRepoURL() throws -> String {
|
||||
guard let rawURL = try Zsh.run(command: "git ls-remote --get-url origin") else {
|
||||
throw ValidationError("Could not determine the git remote URL.")
|
||||
}
|
||||
|
||||
return
|
||||
rawURL
|
||||
.replacingOccurrences(of: "http://", with: "")
|
||||
.replacingOccurrences(of: "https://", with: "")
|
||||
.replacingOccurrences(of: "git@", with: "")
|
||||
.replacingOccurrences(of: ".git", with: "")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
}
|
||||
}
|
||||
59
Tools/Sources/Commands/CI/UploadDSYMs.swift
Normal file
59
Tools/Sources/Commands/CI/UploadDSYMs.swift
Normal file
@@ -0,0 +1,59 @@
|
||||
import ArgumentParser
|
||||
import CommandLineTools
|
||||
import Foundation
|
||||
|
||||
struct UploadDSYMs: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(commandName: "upload-dsyms",
|
||||
abstract: "Uploads dSYMs to Sentry using sentry-cli.",
|
||||
discussion: "Requires the SENTRY_AUTH_TOKEN environment variable to be set.")
|
||||
|
||||
@Option(help: "The path to the dSYMs directory or file to upload.")
|
||||
var dsymPath: String
|
||||
|
||||
@Option(help: "The Sentry organization slug.")
|
||||
var orgSlug = "element"
|
||||
|
||||
@Option(help: "The Sentry project slug.")
|
||||
var projectSlug = "element-x-ios"
|
||||
|
||||
@Option(help: "The Sentry server URL.")
|
||||
var url = "https://sentry.tools.element.io/"
|
||||
|
||||
@Option(help: "The maximum number of upload attempts.")
|
||||
var maxRetries = 5
|
||||
|
||||
func run() async throws {
|
||||
guard let authToken = ProcessInfo.processInfo.environment["SENTRY_AUTH_TOKEN"],
|
||||
!authToken.isEmpty else {
|
||||
throw ValidationError("SENTRY_AUTH_TOKEN environment variable is not set.")
|
||||
}
|
||||
|
||||
let command = """
|
||||
sentry-cli dif upload \
|
||||
--auth-token "\(authToken)" \
|
||||
--org "\(orgSlug)" \
|
||||
--project "\(projectSlug)" \
|
||||
--url "\(url)" \
|
||||
--log-level DEBUG \
|
||||
"\(dsymPath)"
|
||||
"""
|
||||
|
||||
var lastError: Swift.Error?
|
||||
|
||||
for attempt in 1...maxRetries {
|
||||
do {
|
||||
logger.info("\n📡 Uploading dSYMs to Sentry (attempt \(attempt)/\(maxRetries))…\n")
|
||||
try await CI.run(.path("/bin/zsh"), ["-cu", command])
|
||||
logger.info("\n✅ Successfully uploaded dSYMs to Sentry.\n")
|
||||
return
|
||||
} catch {
|
||||
lastError = error
|
||||
logger.error("\n❌ Sentry upload attempt \(attempt) failed: \(error.localizedDescription)\n")
|
||||
}
|
||||
}
|
||||
|
||||
if let lastError {
|
||||
throw lastError
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user