Skip to content

Commit

Permalink
Working version
Browse files Browse the repository at this point in the history
  • Loading branch information
finnvoor committed Dec 11, 2023
1 parent 480d367 commit be782e3
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 14 deletions.
20 changes: 20 additions & 0 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--enable blankLineAfterImports
--enable blankLinesBetweenImports
--enable blockComments
--enable docComments
--enable isEmpty
--enable markTypes
--enable organizeDeclarations

--disable numberFormatting
--disable redundantNilInit
--disable trailingCommas
--disable wrapMultilineStatementBraces

--ifdef no-indent
--funcattributes same-line
--typeattributes same-line
--varattributes same-line
--ranges no-space
--header strip
--selfrequired log,debug,info,notice,warning,trace,error,critical,fault
50 changes: 50 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"pins" : [
{
"identity" : "appstoreconnect-swift-sdk",
"kind" : "remoteSourceControl",
"location" : "git@github.com:AvdLee/appstoreconnect-swift-sdk.git",
"state" : {
"revision" : "d345a2bcacdaa053ee7c758f05c97f668f24d18a",
"version" : "3.0.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser.git",
"state" : {
"revision" : "c8ed701b513cf5177118a175d85fbbbcd707ab41",
"version" : "1.3.0"
}
},
{
"identity" : "swift-crypto",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "60f13f60c4d093691934dc6cfdf5f508ada1f894",
"version" : "2.6.0"
}
},
{
"identity" : "swifttui",
"kind" : "remoteSourceControl",
"location" : "https://github.com/Finnvoor/SwiftTUI.git",
"state" : {
"revision" : "c4a996bf0d9252ab6d308a0c8b624b7ac21eef79",
"version" : "1.0.0"
}
},
{
"identity" : "urlqueryencoder",
"kind" : "remoteSourceControl",
"location" : "https://github.com/CreateAPI/URLQueryEncoder.git",
"state" : {
"revision" : "4ce950479707ea109f229d7230ec074a133b15d7",
"version" : "0.2.1"
}
}
],
"version" : 2
}
8 changes: 5 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "xcc",
platforms: [.macOS(.v11)],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
.package(url: "git@github.com:AvdLee/appstoreconnect-swift-sdk.git", from: "3.0.1"),
.package(url: "https://github.com/Finnvoor/SwiftTUI.git", from: "1.0.0")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.executableTarget(
name: "xcc",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "AppStoreConnect-Swift-SDK", package: "AppStoreConnect-Swift-SDK"),
.product(name: "SwiftTUI", package: "SwiftTUI")
]
),
]
Expand Down
208 changes: 197 additions & 11 deletions Sources/xcc.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,200 @@
// The Swift Programming Language
// https://docs.swift.org/swift-book
//
// Swift Argument Parser
// https://swiftpackageindex.com/apple/swift-argument-parser/documentation

import AppStoreConnect_Swift_SDK
import ArgumentParser
import Foundation
import SwiftTUI

// MARK: - xcc

@main struct xcc: AsyncParsableCommand {
@Option var issuerID: String?
@Option var privateKeyID: String?
@Option var privateKey: String?

@Option var product: String?
@Option var workflow: String?
@Option var reference: String?

mutating func run() async throws {
Termios.enterRawMode()
defer { Termios.pop() }
signal(SIGINT) { _ in
Cursor.show()
fflush(stdout)
signal(SIGINT, SIG_DFL)
raise(SIGINT)
}

let issuerID = issuerID ?? ProcessInfo.processInfo.environment["XCC_ISSUER_ID"]
let privateKeyID = privateKeyID ?? ProcessInfo.processInfo.environment["XCC_PRIVATE_KEY_ID"]
let privateKey = privateKey ?? ProcessInfo.processInfo.environment["XCC_PRIVATE_KEY"]

guard let issuerID else { throw ValidationError("Missing Issuer ID. Create an API key at https://appstoreconnect.apple.com/access/api") }
guard let privateKeyID else { throw ValidationError("Missing Private Key ID. Create an API key at https://appstoreconnect.apple.com/access/api") }
guard var privateKey else { throw ValidationError("Missing Private Key. Create an API key at https://appstoreconnect.apple.com/access/api") }

privateKey = privateKey.replacingOccurrences(of: "\n", with: "")
if privateKey.hasPrefix("-----BEGIN PRIVATE KEY-----") {
privateKey.removeFirst("-----BEGIN PRIVATE KEY-----".count)
}
if privateKey.hasSuffix("-----END PRIVATE KEY-----") {
privateKey.removeLast("-----END PRIVATE KEY-----".count)
}

let configuration = try APIConfiguration(
issuerID: issuerID,
privateKeyID: privateKeyID,
privateKey: privateKey
)
let provider = APIProvider(configuration: configuration)

let products = try await provider.request(
APIEndpoint.v1.ciProducts.get(parameters: .init(
fieldsCiProducts: [.bundleID, .name],
include: [.bundleID]
))
).data

let selectedProduct = if let product {
products.first(where: { $0.attributes?.name == product })
} else {
chooseFromList(products, prompt: "Select a product")
}
guard let selectedProduct else {
throw Error.couldNotFindProduct(availableProducts: products)
}

let workflows = try await provider.request(
APIEndpoint.v1.ciProducts.id(selectedProduct.id).workflows.get()
).data

let selectedWorkflow = if let workflow {
workflows.first(where: { $0.attributes?.name == workflow })
} else {
chooseFromList(workflows, prompt: "Select a workflow")
}
guard let selectedWorkflow else {
throw Error.couldNotFindWorkflow(availableWorkflows: workflows)
}

let repository = try await provider.request(
APIEndpoint.v1.ciWorkflows.id(selectedWorkflow.id).repository.get()
).data

let gitReferences = try await provider.request(
APIEndpoint.v1.scmRepositories.id(repository.id).gitReferences.get()
).data

let selectedGitReference = if let reference {
gitReferences.first(where: { $0.attributes?.name == reference })
} else {
chooseFromList(gitReferences, prompt: "Select a reference")
}
guard let selectedGitReference else {
throw Error.couldNotFindReference(availableReferences: gitReferences)
}

_ = try? await provider.request(APIEndpoint.v1.ciBuildRuns.post(.init(
data: .init(
type: .ciBuildRuns,
relationships: .init(
workflow: .init(data: .init(
type: .ciWorkflows,
id: selectedWorkflow.id
)),
sourceBranchOrTag: .init(data: .init(
type: .scmGitReferences,
id: selectedGitReference.id
))
)
)
))).data

print("".brightGreen.bold + "Build started")
}
}

// MARK: xcc.Error

extension xcc {
enum Error: LocalizedError {
case missingIssuerID
case missingPrivateKeyID
case missingPrivateKey

case couldNotFindProduct(availableProducts: [CiProduct])
case couldNotFindWorkflow(availableWorkflows: [CiWorkflow])
case couldNotFindReference(availableReferences: [ScmGitReference])

// MARK: Internal

var errorDescription: String? {
switch self {
case .missingIssuerID: """
Missing Issuer ID. Create an API key at https://appstoreconnect.apple.com/access/api and specify an Issuer ID using one of the following:
- Pass it to xcc as a flag (--issuer-id <issuer-id>)
- Set an environment variable (XCC_ISSUER_ID=<issuer-id>)
"""

case .missingPrivateKeyID: """
Missing Private Key ID. Create an API key at https://appstoreconnect.apple.com/access/api and specify a Private Key ID using one of the following:
- Pass it to xcc as a flag (--private-key-id <private-key-id>)
- Set an environment variable (XCC_PRIVATE_KEY_ID=<private-key-id>)
"""

case .missingPrivateKey: """
Missing Private Key. Create an API key at https://appstoreconnect.apple.com/access/api and specify a Private Key using one of the following:
- Pass it to xcc as a flag (--private-key <private-key>)
- Set an environment variable (XCC_PRIVATE_KEY=<private-key>)
"""

case let .couldNotFindProduct(products): """
Could not find product with specified name. Available products:
\(products.compactMap(\.attributes?.name).map { "- \($0)" }.joined(separator: "\n"))
"""

case let .couldNotFindWorkflow(workflows): """
Could not find workflow with specified name. Available workflows:
\(workflows.compactMap(\.attributes?.name).map { "- \($0)" }.joined(separator: "\n"))
"""

case let .couldNotFindReference(references): """
Could not find reference with specified name. Available references:
\(references.compactMap(\.attributes?.name).map { "- \($0)" }.joined(separator: "\n"))
"""
}
}
}
}

// MARK: - CiProduct + CustomStringConvertible

extension CiProduct: CustomStringConvertible {
public var description: String {
let name = attributes?.name ?? "Unknown"
let bundleID = relationships?.bundleID?.data
return "\(name) \((bundleID.map { "(\($0.id))" } ?? "").faint)"
}
}

// MARK: - CiWorkflow + CustomStringConvertible

extension CiWorkflow: CustomStringConvertible {
public var description: String {
attributes?.name ?? "Unknown"
}
}

// MARK: - ScmGitReference + CustomStringConvertible

@main
struct xcc: ParsableCommand {
mutating func run() throws {
print("Hello, world!")
extension ScmGitReference: CustomStringConvertible {
public var description: String {
switch attributes?.kind {
case .branch:
"(branch) ".faint + (attributes?.name ?? "")
case .tag:
" (tag) ".faint + (attributes?.name ?? "")
case nil:
attributes?.name ?? ""
}
}
}
}

0 comments on commit be782e3

Please sign in to comment.