mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Run SwiftFormat.
This commit is contained in:
+2
-2
@@ -23,7 +23,7 @@ final class TemplateScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: TemplateScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<TemplateScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<TemplateScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -47,7 +47,7 @@ final class TemplateScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(TemplateScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class TemplateScreenViewModel: TemplateScreenViewModelType, TemplateScreenViewMo
|
||||
var actionsPublisher: AnyPublisher<TemplateScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
super.init(initialViewState: TemplateScreenViewState(title: "Template title",
|
||||
placeholder: "Enter something here",
|
||||
|
||||
@@ -6,10 +6,10 @@ struct AppIconBanner: AsyncParsableCommand {
|
||||
|
||||
@Argument(help: "Path to the input image.")
|
||||
var path: String
|
||||
|
||||
|
||||
@Option(help: "Text for the banner.")
|
||||
var bannerText: String
|
||||
|
||||
|
||||
@MainActor
|
||||
func run() async throws {
|
||||
let currentDirectoryURL = URL(filePath: FileManager.default.currentDirectoryPath)
|
||||
|
||||
@@ -33,7 +33,7 @@ struct BuildSDK: AsyncParsableCommand {
|
||||
|
||||
@Option(help: "The profile to use when building the SDK. Omit this option to build in debug mode.")
|
||||
var profile: Profile = .reldbg
|
||||
|
||||
|
||||
enum Error: LocalizedError {
|
||||
case rustupOutputFailure
|
||||
case missingRustTargets([String])
|
||||
@@ -133,7 +133,7 @@ struct BuildSDK: AsyncParsableCommand {
|
||||
let updatedYAMLString = try Yams.serialize(node: projectConfig)
|
||||
try updatedYAMLString.write(to: yamlURL, atomically: true, encoding: .utf8)
|
||||
}
|
||||
|
||||
|
||||
func generateSDKMocks() async throws {
|
||||
var command = GenerateSDKMocks()
|
||||
command.version = "local"
|
||||
|
||||
@@ -5,7 +5,7 @@ import Foundation
|
||||
struct BumpCalendarVersion: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "A tool that bumps the CalVer every month (if needed), setting the patch back to 0.",
|
||||
discussion: "The tool assumes the release will be published in 6-days so bumps early.")
|
||||
|
||||
|
||||
func run() throws {
|
||||
try updateProjectYAML()
|
||||
try Zsh.run(command: "xcodegen")
|
||||
|
||||
@@ -4,7 +4,7 @@ import Foundation
|
||||
struct AccessibilityTests: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(commandName: "accessibility-tests",
|
||||
abstract: "Runs the accessibility test CI workflow.")
|
||||
|
||||
|
||||
@Option(help: "Device name for tests.")
|
||||
var device = "iPhone 17"
|
||||
|
||||
@@ -25,15 +25,15 @@ struct AccessibilityTests: AsyncParsableCommand {
|
||||
testsFailed = true
|
||||
logger.error("\n❌ Accessibility tests failed.\n")
|
||||
}
|
||||
|
||||
|
||||
// Zip results (best-effort, useful for CI artifact uploads)
|
||||
await CI.zipResults(bundles: ["AccessibilityTests.xcresult"],
|
||||
outputName: "AccessibilityTests.xcresult.zip")
|
||||
|
||||
|
||||
if testsFailed {
|
||||
throw ExitCode.failure
|
||||
}
|
||||
|
||||
|
||||
logger.info("\n✅ Accessibility tests passed.\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ struct CI: ParsableCommand {
|
||||
else {
|
||||
throw ValidationError("GITHUB_TOKEN environment variable is not set.")
|
||||
}
|
||||
|
||||
|
||||
let repoURL = try await CI.gitRepositoryURL()
|
||||
|
||||
if let tagName {
|
||||
|
||||
@@ -5,24 +5,24 @@ import Yams
|
||||
struct ConfigureNightly: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "Configures the project for a Nightly build.",
|
||||
discussion: "Adds the Nightly variant to project.yml, updates secrets, runs xcodegen, and generates the app icon banner.")
|
||||
|
||||
|
||||
@Option(help: "The build number to display on the app icon banner.")
|
||||
var buildNumber: String
|
||||
|
||||
|
||||
func run() async throws {
|
||||
guard !buildNumber.isEmpty else {
|
||||
throw ValidationError("Invalid build number.")
|
||||
}
|
||||
|
||||
|
||||
try addNightlyVariant()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
/// Adds the Nightly variant include path to `project.yml` if it isn't already present.
|
||||
private func addNightlyVariant() throws {
|
||||
let projectURL = URL.projectDirectory.appending(component: "project.yml")
|
||||
@@ -35,11 +35,11 @@ struct ConfigureNightly: AsyncParsableCommand {
|
||||
if projectConfig["include"]?.sequence?.contains(where: { $0.mapping?["path"] == "Variants/Nightly/nightly.yml" }) == false {
|
||||
projectConfig["include"]?.sequence?.append(["path": "Variants/Nightly/nightly.yml"])
|
||||
}
|
||||
|
||||
|
||||
let updatedYAMLString = try Yams.serialize(node: projectConfig)
|
||||
try updatedYAMLString.write(to: projectURL, atomically: true, encoding: .utf8)
|
||||
}
|
||||
|
||||
|
||||
/// Generates the app icon banner with version and build number.
|
||||
private func generateAppIconBanner(version: String, buildNumber: String) async throws {
|
||||
let bannerText = "\(version) (\(buildNumber))"
|
||||
|
||||
@@ -8,7 +8,7 @@ struct IntegrationTests: AsyncParsableCommand {
|
||||
Deletes old log files, runs integration tests, validates that logs are set
|
||||
to the trace level and don't contain private messages, then collects results.
|
||||
""")
|
||||
|
||||
|
||||
@Option(help: "Device name for tests.")
|
||||
var device = "iPhone 17"
|
||||
|
||||
|
||||
@@ -4,21 +4,21 @@ import Foundation
|
||||
struct PreviewTests: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(commandName: "preview-tests",
|
||||
abstract: "Runs the preview test CI workflow, with optional snapshot recording.")
|
||||
|
||||
|
||||
@Flag(help: "Re-record snapshots for tests that fail or are missing a reference image.")
|
||||
var record = false
|
||||
|
||||
|
||||
private static let scheme = "PreviewTests"
|
||||
private static let device = "iPhone SE (3rd generation)"
|
||||
private static let osVersion = "26.4.1"
|
||||
private static let simulatorType = "com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation"
|
||||
private static let testPlanPath = "PreviewTests/SupportingFiles/PreviewTests.xctestplan"
|
||||
|
||||
|
||||
func run() async throws {
|
||||
if record {
|
||||
try setRecordFailures(enabled: true)
|
||||
}
|
||||
|
||||
|
||||
var testsFailed = false
|
||||
do {
|
||||
logger.info("\n🧪 Running preview tests…\n")
|
||||
@@ -47,41 +47,41 @@ struct PreviewTests: AsyncParsableCommand {
|
||||
testsFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Collect coverage and test results regardless of test outcome (best-effort).
|
||||
await CI.collectCoverage(resultBundle: "\(Self.scheme).xcresult", outputName: "preview-cobertura.xml")
|
||||
await CI.collectTestResults(resultBundle: "\(Self.scheme).xcresult", outputName: "preview-junit.xml")
|
||||
|
||||
|
||||
if testsFailed {
|
||||
throw ExitCode.failure
|
||||
}
|
||||
|
||||
|
||||
if !record {
|
||||
logger.info("\n✅ Preview tests passed.\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Test Plan
|
||||
|
||||
|
||||
/// Enables or disables the `RECORD_FAILURES` environment variable entry in the test plan.
|
||||
private func setRecordFailures(enabled: Bool) throws {
|
||||
let url = URL.projectDirectory.appendingPathComponent(Self.testPlanPath)
|
||||
let data = try Data(contentsOf: url)
|
||||
|
||||
|
||||
guard var plan = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
var defaultOptions = plan["defaultOptions"] as? [String: Any],
|
||||
var envVars = defaultOptions["environmentVariableEntries"] as? [[String: Any]] else {
|
||||
throw ValidationError("Could not parse test plan at \(Self.testPlanPath).")
|
||||
}
|
||||
|
||||
|
||||
for index in envVars.indices where envVars[index]["key"] as? String == "RECORD_FAILURES" {
|
||||
envVars[index]["enabled"] = enabled
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
defaultOptions["environmentVariableEntries"] = envVars
|
||||
plan["defaultOptions"] = defaultOptions
|
||||
|
||||
|
||||
let jsonData = try JSONSerialization.data(withJSONObject: plan, options: [.prettyPrinted, .sortedKeys])
|
||||
try jsonData.write(to: url)
|
||||
}
|
||||
|
||||
@@ -5,14 +5,14 @@ import Yams
|
||||
struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(commandName: "release-to-github",
|
||||
abstract: "Creates a GitHub release and updates CHANGES.md with generated release notes.")
|
||||
|
||||
|
||||
enum ReleaseError: LocalizedError {
|
||||
case missingGitHubToken
|
||||
case failedToCreateRelease(String)
|
||||
case failedToParseResponse
|
||||
case missingReleaseNotes
|
||||
case failedToReadVersion
|
||||
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .missingGitHubToken:
|
||||
@@ -28,45 +28,45 @@ struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func run() async throws {
|
||||
let currentVersion = try CI.readMarketingVersion()
|
||||
logger.info("Creating GitHub release for version \(currentVersion)…")
|
||||
|
||||
|
||||
let releaseBody = try await createGitHubRelease(version: currentVersion)
|
||||
|
||||
|
||||
try updateChangelog(version: currentVersion, generatedNotes: releaseBody)
|
||||
|
||||
|
||||
let changesFilePath = URL.projectDirectory.appendingPathComponent("CHANGES.md").path
|
||||
try await CI.run(.name("git"), ["add", changesFilePath])
|
||||
|
||||
|
||||
logger.info("Successfully created GitHub release \(currentVersion) and updated CHANGES.md.")
|
||||
|
||||
let targetFilePath = "project.yml"
|
||||
let xcodeProjPath = "ElementX.xcodeproj"
|
||||
|
||||
|
||||
guard let newVersion = bumpPatchVersion(currentVersion) else {
|
||||
throw ValidationError("Invalid version format: \(currentVersion)")
|
||||
}
|
||||
|
||||
|
||||
// Bump the patch version using sed (preserves file formatting)
|
||||
try await CI.run(.name("sed"), ["-i", "", "s/MARKETING_VERSION: \(currentVersion)/MARKETING_VERSION: \(newVersion)/g", targetFilePath])
|
||||
logger.info("Version updated from \(currentVersion) to \(newVersion)")
|
||||
|
||||
|
||||
try await CI.run(.name("xcodegen"))
|
||||
|
||||
|
||||
try await CI.gitConfigureGlobals()
|
||||
|
||||
|
||||
try await CI.run(.name("git"), ["add", targetFilePath, xcodeProjPath])
|
||||
try await CI.run(.name("git"), ["commit", "-m", "Prepare next release"])
|
||||
|
||||
try await CI.gitPush()
|
||||
|
||||
|
||||
try await rebaseMainOntoCurrentBranch()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private func createGitHubRelease(version: String) async throws -> String {
|
||||
guard let apiToken = ProcessInfo.processInfo.environment["GITHUB_TOKEN"], !apiToken.isEmpty
|
||||
else {
|
||||
@@ -79,23 +79,23 @@ struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
request.setValue("Bearer \(apiToken)", forHTTPHeaderField: "Authorization")
|
||||
request.setValue("application/vnd.github+json", forHTTPHeaderField: "Accept")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
|
||||
let body: [String: Any] = ["tag_name": "release/\(version)",
|
||||
"name": version,
|
||||
"generate_release_notes": true]
|
||||
request.httpBody = try JSONSerialization.data(withJSONObject: body)
|
||||
|
||||
let (data, response) = try await URLSession.shared.data(for: request)
|
||||
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw ReleaseError.failedToParseResponse
|
||||
}
|
||||
|
||||
|
||||
guard (200...299).contains(httpResponse.statusCode) else {
|
||||
let errorBody = String(data: data, encoding: .utf8) ?? "Unknown error"
|
||||
throw ReleaseError.failedToCreateRelease("HTTP \(httpResponse.statusCode): \(errorBody)")
|
||||
}
|
||||
|
||||
|
||||
guard let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||
let releaseBody = json["body"] as? String else {
|
||||
throw ReleaseError.failedToParseResponse
|
||||
@@ -103,25 +103,25 @@ struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
|
||||
return releaseBody
|
||||
}
|
||||
|
||||
|
||||
private func updateChangelog(version: String, generatedNotes: String) throws {
|
||||
let changesURL = URL.projectDirectory.appending(component: "CHANGES.md")
|
||||
|
||||
|
||||
// Clean up the generated notes: remove HTML comments and adjust header levels
|
||||
let cleanedNotes = generatedNotes
|
||||
.replacingOccurrences(of: "<!-- .*? -->", with: "", options: .regularExpression)
|
||||
.replacingOccurrences(of: "### ", with: "\n")
|
||||
.replacingOccurrences(of: "## ", with: "### ")
|
||||
|
||||
|
||||
guard !cleanedNotes.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||||
throw ReleaseError.missingReleaseNotes
|
||||
}
|
||||
|
||||
|
||||
let releaseDate = Date().formatted(.iso8601.year().month().day())
|
||||
|
||||
|
||||
let existingContent = try String(contentsOf: changesURL)
|
||||
let newContent = "## Changes in \(version) (\(releaseDate))\(cleanedNotes)\n\n\(existingContent)"
|
||||
|
||||
|
||||
try newContent.write(to: changesURL, atomically: true, encoding: .utf8)
|
||||
logger.info("Updated CHANGES.md with release notes.")
|
||||
}
|
||||
@@ -131,7 +131,7 @@ struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
guard let match = version.firstMatch(of: regex), var patch = Int(match.3) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let year = String(match.1)
|
||||
let month = String(match.2)
|
||||
patch = patch + 1
|
||||
@@ -146,14 +146,14 @@ struct ReleaseToGitHub: AsyncParsableCommand {
|
||||
}
|
||||
|
||||
logger.info("Current branch: \(currentBranch)")
|
||||
|
||||
|
||||
try await CI.run(.name("git"), ["reset", "--hard"])
|
||||
try await CI.run(.name("git"), ["checkout", "main"])
|
||||
try await CI.run(.name("git"), ["pull", "origin", "main"])
|
||||
try await CI.run(.name("git"), ["rebase", currentBranch])
|
||||
|
||||
|
||||
try await CI.gitPush()
|
||||
|
||||
|
||||
logger.info("Successfully rebased main onto \(currentBranch)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,9 +143,9 @@ struct RunTests: AsyncParsableCommand {
|
||||
if let testName {
|
||||
command += " -only-testing:\(scheme)/\(testName)"
|
||||
}
|
||||
|
||||
|
||||
command += " | \(formatter)"
|
||||
|
||||
|
||||
try await CI.run(.path("/bin/zsh"), ["-cu", command])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,22 @@ 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.")
|
||||
}
|
||||
|
||||
try await CI.gitConfigureGlobals()
|
||||
|
||||
|
||||
let currentVersion = try CI.readMarketingVersion()
|
||||
let tagName = "nightly/\(currentVersion).\(buildNumber)"
|
||||
|
||||
try await CI.gitPush(tagName: tagName)
|
||||
|
||||
|
||||
logger.info("\n🚀 Successfully tagged nightly: \(tagName)\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,35 +10,35 @@ struct UITests: AsyncParsableCommand {
|
||||
swift run tools ci ui-tests --device-type iPad
|
||||
swift run tools ci ui-tests --device-type iPhone --test-name "ClassName/testName"
|
||||
""")
|
||||
|
||||
|
||||
enum DeviceType: String, CaseIterable, ExpressibleByArgument {
|
||||
case iPhone
|
||||
case iPad
|
||||
}
|
||||
|
||||
|
||||
@Option(help: "The device type to test (iPhone or iPad).")
|
||||
var deviceType: DeviceType
|
||||
|
||||
|
||||
@Option(help: "iOS version for the simulator.")
|
||||
var osVersion = "26.4.1"
|
||||
|
||||
|
||||
@Option(help: "Run only a specific test (format: 'ClassName/testName').")
|
||||
var testName: String?
|
||||
|
||||
|
||||
private var simulatorName: String {
|
||||
switch deviceType {
|
||||
case .iPhone: "iPhone-\(osVersion)"
|
||||
case .iPad: "iPad-\(osVersion)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var simulatorType: String {
|
||||
switch deviceType {
|
||||
case .iPhone: "com.apple.CoreSimulator.SimDeviceType.iPhone-17"
|
||||
case .iPad: "com.apple.CoreSimulator.SimDeviceType.iPad-A16"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// We used to run these simultaneously on iPhone and iPad but it is *really* slow on GitHub runners.
|
||||
/// Presumably because launching 2 simulators uses more memory than the runner has available.
|
||||
func run() async throws {
|
||||
@@ -49,11 +49,11 @@ struct UITests: AsyncParsableCommand {
|
||||
"--create-simulator-name", simulatorName,
|
||||
"--create-simulator-type", simulatorType
|
||||
]
|
||||
|
||||
|
||||
if let testName {
|
||||
args += ["--test-name", testName]
|
||||
}
|
||||
|
||||
|
||||
var testsFailed = false
|
||||
do {
|
||||
print("\n🧪 Running UI tests (\(deviceType.rawValue))…\n")
|
||||
@@ -68,11 +68,11 @@ struct UITests: AsyncParsableCommand {
|
||||
|
||||
await CI.collectCoverage(resultBundle: "UITests.xcresult", outputName: "ui-cobertura.xml")
|
||||
await CI.collectTestResults(resultBundle: "UITests.xcresult", outputName: "ui-junit.xml")
|
||||
|
||||
|
||||
if testsFailed {
|
||||
throw ExitCode.failure
|
||||
}
|
||||
|
||||
|
||||
print("\n✅ UI tests (\(deviceType.rawValue)) passed.\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Foundation
|
||||
struct UnitTests: AsyncParsableCommand {
|
||||
static let configuration = CommandConfiguration(commandName: "unit-tests",
|
||||
abstract: "Runs the unit test CI workflow: lint, unit tests, preview tests, and result collection.")
|
||||
|
||||
|
||||
@Flag(help: "Skip preview tests")
|
||||
var skipPreviews = false
|
||||
|
||||
|
||||
@@ -5,28 +5,28 @@ 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 --url "\(url)" dif upload \
|
||||
--auth-token "\(authToken)" \
|
||||
@@ -35,9 +35,9 @@ struct UploadDSYMs: AsyncParsableCommand {
|
||||
--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")
|
||||
@@ -49,7 +49,7 @@ struct UploadDSYMs: AsyncParsableCommand {
|
||||
logger.error("\n❌ Sentry upload attempt \(attempt) failed: \(error.localizedDescription)\n")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if let lastError {
|
||||
throw lastError
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@ import Foundation
|
||||
|
||||
struct DownloadStrings: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "A tool to download localizable strings from localazy")
|
||||
|
||||
|
||||
@Flag(help: "Use to download translation keys for all languages")
|
||||
var allLanguages = false
|
||||
|
||||
|
||||
func run() throws {
|
||||
try localazyDownload()
|
||||
try sortStringsFiles()
|
||||
try swiftgen()
|
||||
}
|
||||
|
||||
|
||||
private func localazyDownload() throws {
|
||||
let arguments = allLanguages ? " all" : ""
|
||||
try Zsh.run(command: "localazy download\(arguments)")
|
||||
@@ -75,7 +75,7 @@ struct DownloadStrings: ParsableCommand {
|
||||
guard let closeQuote = line[afterOpen...].firstIndex(of: "\"") else { return nil }
|
||||
return String(line[afterOpen..<closeQuote])
|
||||
}
|
||||
|
||||
|
||||
private func swiftgen() throws {
|
||||
try Zsh.run(command: "swiftgen config run --config Tools/SwiftGen/swiftgen-config.yml")
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ struct GenerateSDKMocks: AsyncParsableCommand {
|
||||
enum GenerateSDKMocksError: Error {
|
||||
case invalidFileUrl
|
||||
}
|
||||
|
||||
|
||||
static let configuration = CommandConfiguration(abstract: "A tool to setup the mocks for the Matrix Rust SDK")
|
||||
|
||||
|
||||
@Argument(help: "The argument to specify a branch of the SDK. Use `local` to use your local version")
|
||||
var version: String
|
||||
|
||||
|
||||
private var fileURLFormat = "https://raw.githubusercontent.com/element-hq/matrix-rust-components-swift/%@/Sources/MatrixRustSDK/matrix_sdk_ffi.swift"
|
||||
|
||||
|
||||
func run() async throws {
|
||||
if version == "local" {
|
||||
try generateSDKMocks(ffiPath: "\(URL.sdkDirectory.path)/bindings/apple/generated/swift")
|
||||
@@ -23,12 +23,12 @@ struct GenerateSDKMocks: AsyncParsableCommand {
|
||||
try FileManager.default.removeItem(atPath: path)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Generates the SDK mocks using Sourcery.
|
||||
func generateSDKMocks(ffiPath: String) throws {
|
||||
try Zsh.run(command: "sourcery --sources \(ffiPath) --templates Tools/Sourcery/SDKAutoMockable.stencil --output SDKMocks/Sources/Generated/SDKGeneratedMocks.swift")
|
||||
}
|
||||
|
||||
|
||||
/// Downloads the specified version of the `matrix_sdk_ffi.swift` file and returns the path to the downloaded file.
|
||||
func downloadSDK(version: String) async throws -> String {
|
||||
let fileURLString = String(format: fileURLFormat, version)
|
||||
|
||||
@@ -6,7 +6,7 @@ struct Locheck: ParsableCommand {
|
||||
enum LocheckError: LocalizedError {
|
||||
case missingMint
|
||||
case outputError
|
||||
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .missingMint:
|
||||
@@ -16,28 +16,28 @@ struct Locheck: ParsableCommand {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static let configuration = CommandConfiguration(abstract: "A tool that verifies bad strings contained in localization files")
|
||||
|
||||
|
||||
private var stringsDirectoryURL: URL {
|
||||
.projectDirectory.appendingPathComponent("ElementX/Resources/Localizations")
|
||||
}
|
||||
|
||||
|
||||
func run() throws {
|
||||
try checkMint()
|
||||
try checkStrings()
|
||||
}
|
||||
|
||||
|
||||
func checkStrings() throws {
|
||||
guard let output = try Zsh.run(command: "mint run locheck discoverlproj --ignore-missing --ignore lproj_file_missing_from_translation --treat-warnings-as-errors \(stringsDirectoryURL.path)") else {
|
||||
throw LocheckError.missingMint
|
||||
}
|
||||
print(output)
|
||||
}
|
||||
|
||||
|
||||
private func checkMint() throws {
|
||||
let result = try Zsh.run(command: "which mint")
|
||||
|
||||
|
||||
if result?.contains("not found") == true {
|
||||
throw LocheckError.missingMint
|
||||
}
|
||||
|
||||
@@ -4,21 +4,21 @@ import Foundation
|
||||
|
||||
struct OutdatedPackages: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "A tool to check outdated package dependencies. Please make sure you have already run setup-project before using this tool.")
|
||||
|
||||
|
||||
private var projectSwiftPMDirectoryURL: URL {
|
||||
.projectDirectory.appendingPathComponent("ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm")
|
||||
}
|
||||
|
||||
|
||||
func run() throws {
|
||||
try checkToolsDependencies()
|
||||
try checkProjectDependencies()
|
||||
}
|
||||
|
||||
|
||||
func checkToolsDependencies() throws {
|
||||
guard let output = try Zsh.run(command: "swift outdated"), !output.isEmpty else { return }
|
||||
print("outdated tools Swift packages:\n\(output)")
|
||||
}
|
||||
|
||||
|
||||
func checkProjectDependencies() throws {
|
||||
guard let output = try Zsh.run(command: "swift outdated", directory: projectSwiftPMDirectoryURL), !output.isEmpty else { return }
|
||||
print("outdated project Swift packages:\n\(output)")
|
||||
|
||||
@@ -4,29 +4,29 @@ import Foundation
|
||||
|
||||
struct SetupProject: ParsableCommand {
|
||||
static let configuration = CommandConfiguration(abstract: "A tool to setup the required components to efficiently run and contribute to Element X iOS")
|
||||
|
||||
|
||||
func run() throws {
|
||||
try setupGitHooks()
|
||||
try brewInstall()
|
||||
try mintPackagesInstall()
|
||||
try xcodegen()
|
||||
}
|
||||
|
||||
|
||||
func setupGitHooks() throws {
|
||||
try Zsh.run(command: "git config core.hooksPath .githooks")
|
||||
}
|
||||
|
||||
|
||||
func brewInstall() throws {
|
||||
try Zsh.run(command: "brew install xcodegen swiftgen git-lfs sourcery mint pkl kiliankoe/formulae/swift-outdated localazy/tools/localazy peripheryapp/periphery/periphery")
|
||||
|
||||
// Install swiftformat from develop (for now), making sure to avoid conflicts with an existing versioned installation.
|
||||
// Install swiftformat from develop (for now), making sure to avoid conflicts with an existing versioned installation.
|
||||
try Zsh.run(command: "source ci_scripts/ci_common.sh && install_swiftformat_head")
|
||||
}
|
||||
|
||||
|
||||
func mintPackagesInstall() throws {
|
||||
try Zsh.run(command: "mint install Asana/locheck")
|
||||
}
|
||||
|
||||
|
||||
func xcodegen() throws {
|
||||
try Zsh.run(command: "xcodegen")
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ struct UnusedStrings: ParsableCommand {
|
||||
|
||||
@Flag(help: "Save the results to disk instead of printing them.")
|
||||
var saveToFile = false
|
||||
|
||||
|
||||
func run() throws {
|
||||
try peripheryScan()
|
||||
}
|
||||
|
||||
|
||||
func peripheryScan() throws {
|
||||
print("Analysing project, this may take a while…")
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ extension URL {
|
||||
static var projectDirectory: URL {
|
||||
URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
|
||||
}
|
||||
|
||||
|
||||
static var parentDirectory: URL {
|
||||
.projectDirectory.deletingLastPathComponent()
|
||||
}
|
||||
|
||||
|
||||
static var sdkDirectory: URL {
|
||||
.parentDirectory.appendingPathComponent("matrix-rust-sdk")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user