Skip to content

Commit

Permalink
Add support for repository runners (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcocanc committed Jul 17, 2023
1 parent f651d8e commit 449c6a5
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 20 deletions.
14 changes: 11 additions & 3 deletions Cilicon/Config/GitHubProvisionerConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ struct GitHubProvisionerConfig: Decodable {
let appId: Int
/// The organization slug
let organization: String
/// The repository name
let repository: String?
/// Path to the private key `.pem` file downloaded from the Github App page
let privateKeyPath: String
/// Extra labels to add to the runner
Expand All @@ -16,7 +18,7 @@ struct GitHubProvisionerConfig: Decodable {

let runnerGroup: String?

let organizationURL: URL
let url: URL

enum CodingKeys: CodingKey {
case apiURL
Expand All @@ -25,19 +27,25 @@ struct GitHubProvisionerConfig: Decodable {
case privateKeyPath
case extraLabels
case runnerGroup
case organizationURL
case url
case downloadLatest
case repository
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.apiURL = try container.decodeIfPresent(URL.self, forKey: .apiURL)
self.appId = try container.decode(Int.self, forKey: .appId)
self.organization = try container.decode(String.self, forKey: .organization)
self.repository = try container.decodeIfPresent(String.self, forKey: .repository)
self.privateKeyPath = try (container.decode(String.self, forKey: .privateKeyPath) as NSString).standardizingPath
self.extraLabels = try container.decodeIfPresent([String].self, forKey: .extraLabels)
self.runnerGroup = try container.decodeIfPresent(String.self, forKey: .runnerGroup)
self.organizationURL = try container.decodeIfPresent(URL.self, forKey: .organizationURL) ?? URL(string: "https://github.com/\(organization)")!
var fallbackURL = URL(string: "https://github.com/\(organization)")!
if let repo = self.repository {
fallbackURL.appendPathComponent(repo)
}
self.url = try container.decodeIfPresent(URL.self, forKey: .url) ?? fallbackURL
self.downloadLatest = try container.decodeIfPresent(Bool.self, forKey: .downloadLatest) ?? true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@ class GitHubActionsProvisioner: Provisioner {
}

func provision(bundle: VMBundle, sshClient: SSHClient) async throws {
let org = gitHubConfig.organization
let appId = gitHubConfig.appId
await SSHLogger.shared.log(string: "[1;35mFetching Github Runner Token[0m\n")
guard let installation = try await service.getInstallations().first(where: { $0.account.login == gitHubConfig.organization }) else {
throw GitHubActionsProvisionerError.githubAppNotInstalled(appID: appId, org: org)
}
let installation = try await service.getInstallation()
let authToken = try await service.getInstallationToken(installation: installation)
let token = try await service.createRunnerToken(token: authToken.token)

Expand All @@ -47,7 +43,7 @@ class GitHubActionsProvisioner: Provisioner {

var configCommandComponents = [
"~/actions-runner/config.sh",
"--url \(gitHubConfig.organizationURL)",
"--url \(gitHubConfig.url)",
"--name '\(runnerName)'",
"--token \(token.token)",
"--replace",
Expand Down
56 changes: 45 additions & 11 deletions Cilicon/Provisioner/GitHub Actions/GitHubService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,79 @@ class GitHubService {
self.urlSession = URLSession(configuration: config)
}

func installationsURL() -> URL {
private var orgInstallationURL: URL {
baseURL
.appendingPathComponent("app")
.appendingPathComponent("installations")
.appendingPathComponent("orgs")
.appendingPathComponent(config.organization)
.appendingPathComponent("installation")
}

private func repoInstallationURL(repo: String) -> URL {
baseURL
.appendingPathComponent("repos")
.appendingPathComponent(config.organization)
.appendingPathComponent(repo)
.appendingPathComponent("installation")
}

var installationURL: URL {
if let repo = config.repository {
return repoInstallationURL(repo: repo)
}
return orgInstallationURL
}

func installationFetchURL(installationId: Int) -> URL {
installationsURL()
baseURL
.appendingPathComponent("app")
.appendingPathComponent("installations")
.appendingPathComponent(String(installationId))
.appendingPathComponent("access_tokens")
}

func actionsURL() -> URL {
var actionsURL: URL {
if let repo = config.repository {
return repoActionsURL(repo: repo)
}
return orgActionsURL
}

private var orgActionsURL: URL {
baseURL
.appendingPathComponent("orgs")
.appendingPathComponent(config.organization)
.appendingPathComponent("actions")
.appendingPathComponent("runners")
}

private func repoActionsURL(repo: String) -> URL {
baseURL
.appendingPathComponent("repos")
.appendingPathComponent(config.organization)
.appendingPathComponent(repo)
.appendingPathComponent("actions")
.appendingPathComponent("runners")
}

func runnerTokenURL() -> URL {
actionsURL()
actionsURL
.appendingPathComponent("registration-token")
}

func runnerDownloadsURL() -> URL {
actionsURL()
actionsURL
.appendingPathComponent("downloads")
}

func getInstallations() async throws -> [Installation] {
func getInstallation() async throws -> Installation {
let jwtToken = try GitHubAppAuthHelper.generateJWTToken(pemPath: config.privateKeyPath, appId: config.appId)
let (data, _) = try await authenticatedRequest(
url: installationsURL(),
url: installationURL,
method: "GET",
token: jwtToken
)
let installations = try decoder.decode([Installation].self, from: data)
return installations
let installation = try decoder.decode(Installation.self, from: data)
return installation
}

func getInstallationToken(installation: Installation) async throws -> AccessToken {
Expand Down

0 comments on commit 449c6a5

Please sign in to comment.