488 lines
22 KiB
Swift
488 lines
22 KiB
Swift
//
|
|
// Pipeline.swift
|
|
// Buildkite
|
|
//
|
|
// Created by Aaron Sky on 5/3/20.
|
|
// Copyright © 2020 Aaron Sky. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
#if canImport(FoundationNetworking)
|
|
import FoundationNetworking
|
|
#endif
|
|
|
|
/// A pipeline is a template of the steps you want to run. There are many types of steps, some run scripts,
|
|
/// some define conditional logic, and others wait for user input. When you run a pipeline, a build is created.
|
|
/// Each of the steps in the pipeline end up as jobs in the build, which then get distributed to available agents.
|
|
public struct Pipeline: Codable, Equatable, Hashable, Identifiable, Sendable {
|
|
/// ID of the pipeline.
|
|
public var id: UUID
|
|
/// ID of the pipeline to be used with the GraphQL API.
|
|
public var graphqlId: String
|
|
/// Followable URL to fetch this specific pipeline.
|
|
public var url: Followable<Pipeline.Resources.Get>
|
|
/// Human-readable URL of this pipeline in the Buildkite dashboard.
|
|
public var webURL: URL
|
|
/// Name of the pipeline.
|
|
public var name: String
|
|
/// "Slug" identifier for the pipeline.
|
|
public var slug: String
|
|
/// The repository URL.
|
|
public var repository: String
|
|
/// A [branch filter pattern](https://buildkite.com/docs/pipelines/branch-configuration#pipeline-level-branch-filtering)
|
|
/// that limits which pushed branches trigger builds on this pipeline.
|
|
public var branchConfiguration: String?
|
|
/// The name of the branch to prefill when new builds are created or triggered in Buildkite.
|
|
/// It is also used to filter the builds and metrics shown on the Pipelines page.
|
|
public var defaultBranch: String?
|
|
/// Provider settings.
|
|
public var provider: Provider
|
|
/// Skip intermediate builds. When a new build is created on a branch, any previous builds that
|
|
/// haven't yet started on the same branch will be automatically marked as skipped.
|
|
public var skipQueuedBranchBuilds: Bool
|
|
/// A [branch filter pattern](https://buildkite.com/docs/pipelines/branch-configuration#pipeline-level-branch-filtering)
|
|
/// to limit which branches intermediate build skipping applies to.
|
|
public var skipQueuedBranchBuildsFilter: String?
|
|
/// Cancel intermediate builds. When a new build is created on a branch, any previous builds
|
|
/// that are running on the same branch will be automatically canceled.
|
|
public var cancelRunningBranchBuilds: Bool
|
|
/// A [branch filter pattern](https://buildkite.com/docs/pipelines/branch-configuration#pipeline-level-branch-filtering)
|
|
/// to limit which branches intermediate build cancelling applies to.
|
|
public var cancelRunningBranchBuildsFilter: String?
|
|
/// Followable URL to fetch the latest builds for this pipeline.
|
|
public var buildsURL: Followable<Build.Resources.ListForPipeline>
|
|
/// Human-readable URL to the build status badge for this pipeline.
|
|
public var badgeURL: URL
|
|
/// When the pipeline was created.
|
|
public var createdAt: Date
|
|
/// Number of builds currently scheduled.
|
|
public var scheduledBuildsCount: Int
|
|
/// Number of builds currently running.
|
|
public var runningBuildsCount: Int
|
|
/// Number of jobs currently scheduled.
|
|
public var scheduledJobsCount: Int
|
|
/// Number of jobs currently running.
|
|
public var runningJobsCount: Int
|
|
/// Number of jobs currently waiting.
|
|
public var waitingJobsCount: Int
|
|
/// Whether the pipeline is visible to everyone, including users outside this organization.
|
|
public var visibility: String
|
|
/// Pipeline steps configured in the Buildkite dashbaord.
|
|
public var steps: [Step]
|
|
/// Environment configuration for builds run on this pipeline.
|
|
public var env: JSONValue?
|
|
|
|
public init(
|
|
id: UUID,
|
|
graphqlId: String,
|
|
url: Followable<Pipeline.Resources.Get>,
|
|
webURL: URL,
|
|
name: String,
|
|
slug: String,
|
|
repository: String,
|
|
branchConfiguration: String? = nil,
|
|
defaultBranch: String? = nil,
|
|
provider: Provider,
|
|
skipQueuedBranchBuilds: Bool,
|
|
skipQueuedBranchBuildsFilter: String? = nil,
|
|
cancelRunningBranchBuilds: Bool,
|
|
cancelRunningBranchBuildsFilter: String? = nil,
|
|
buildsURL: Followable<Build.Resources.ListForPipeline>,
|
|
badgeURL: URL,
|
|
createdAt: Date,
|
|
scheduledBuildsCount: Int,
|
|
runningBuildsCount: Int,
|
|
scheduledJobsCount: Int,
|
|
runningJobsCount: Int,
|
|
waitingJobsCount: Int,
|
|
visibility: String,
|
|
steps: [Step],
|
|
env: JSONValue? = nil
|
|
) {
|
|
self.id = id
|
|
self.graphqlId = graphqlId
|
|
self.url = url
|
|
self.webURL = webURL
|
|
self.name = name
|
|
self.slug = slug
|
|
self.repository = repository
|
|
self.branchConfiguration = branchConfiguration
|
|
self.defaultBranch = defaultBranch
|
|
self.provider = provider
|
|
self.skipQueuedBranchBuilds = skipQueuedBranchBuilds
|
|
self.skipQueuedBranchBuildsFilter = skipQueuedBranchBuildsFilter
|
|
self.cancelRunningBranchBuilds = cancelRunningBranchBuilds
|
|
self.cancelRunningBranchBuildsFilter = cancelRunningBranchBuildsFilter
|
|
self.buildsURL = buildsURL
|
|
self.badgeURL = badgeURL
|
|
self.createdAt = createdAt
|
|
self.scheduledBuildsCount = scheduledBuildsCount
|
|
self.runningBuildsCount = runningBuildsCount
|
|
self.scheduledJobsCount = scheduledJobsCount
|
|
self.runningJobsCount = runningJobsCount
|
|
self.waitingJobsCount = waitingJobsCount
|
|
self.visibility = visibility
|
|
self.steps = steps
|
|
self.env = env
|
|
}
|
|
|
|
/// Information about how the pipeline is triggered based on source code provider events.
|
|
public struct Provider: Codable, Equatable, Hashable, Identifiable, Sendable {
|
|
/// ID of the source code provider.
|
|
public var id: String
|
|
/// URL to the source code provider that webhook events are sent to.
|
|
public var webhookURL: URL?
|
|
/// Provider settings.
|
|
public var settings: Settings
|
|
|
|
public init(
|
|
id: String,
|
|
webhookURL: URL? = nil,
|
|
settings: Settings
|
|
) {
|
|
self.id = id
|
|
self.webhookURL = webhookURL
|
|
self.settings = settings
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case webhookURL = "webhook_url"
|
|
case settings
|
|
}
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case graphqlId = "graphql_id"
|
|
case url
|
|
case webURL = "web_url"
|
|
case name
|
|
case slug
|
|
case repository
|
|
case branchConfiguration = "branch_configuration"
|
|
case defaultBranch = "default_branch"
|
|
case provider
|
|
case skipQueuedBranchBuilds = "skip_queued_branch_builds"
|
|
case skipQueuedBranchBuildsFilter = "skip_queued_branch_builds_filter"
|
|
case cancelRunningBranchBuilds = "cancel_running_branch_builds"
|
|
case cancelRunningBranchBuildsFilter = "cancel_running_branch_builds_filter"
|
|
case buildsURL = "builds_url"
|
|
case badgeURL = "badge_url"
|
|
case createdAt = "created_at"
|
|
case scheduledBuildsCount = "scheduled_builds_count"
|
|
case runningBuildsCount = "running_builds_count"
|
|
case scheduledJobsCount = "scheduled_jobs_count"
|
|
case runningJobsCount = "running_jobs_count"
|
|
case waitingJobsCount = "waiting_jobs_count"
|
|
case visibility
|
|
case steps
|
|
case env
|
|
}
|
|
}
|
|
|
|
extension Pipeline.Provider {
|
|
/// Source code provider settings.
|
|
public struct Settings: Codable, Equatable, Hashable, Sendable {
|
|
public var repository: String?
|
|
/// Whether to create builds for commits that are part of a Pull Request.
|
|
public var buildPullRequests: Bool?
|
|
/// Whether to limit the creation of builds to specific branches or patterns.
|
|
public var pullRequestBranchFilterEnabled: Bool?
|
|
/// The branch filtering pattern. Only pull requests on branches matching this pattern will cause builds to be created.
|
|
public var pullRequestBranchFilterConfiguration: String?
|
|
/// Whether to skip creating a new build for a pull request if an existing build for the commit and branch already exists.
|
|
public var skipPullRequestBuildsForExistingCommits: Bool?
|
|
/// Whether to create builds when tags are pushed.
|
|
public var buildTags: Bool?
|
|
/// Whether to update the status of commits in Bitbucket or GitHub.
|
|
public var publishCommitStatus: Bool?
|
|
/// Whether to create a separate status for each job in a build, allowing you to see the status of each job directly in Bitbucket or GitHub.
|
|
public var publishCommitStatusPerStep: Bool?
|
|
/// What type of event to trigger builds on. Code will create builds when code is pushed to GitHub. Deployment will create builds when a deployment is created with the GitHub Deployments API. Fork will create builds when the GitHub repository is forked. None will not create any builds based on GitHub activity.
|
|
public var triggerMode: String?
|
|
/// Whether filter conditions are being used for this step.
|
|
public var filterEnabled: Bool?
|
|
/// The conditions under which this step will run. See the Using Conditionals guide for more information.
|
|
public var filterCondition: String?
|
|
/// Whether to create builds for pull requests from third-party forks.
|
|
public var buildPullRequestForks: Bool?
|
|
/// Prefix branch names for third-party fork builds to ensure they don't trigger branch conditions. For example, the master branch from some-user will become some-user:master.
|
|
public var prefixPullRequestForkBranchNames: Bool?
|
|
/// Whether to create a separate status for pull request builds, allowing you to require a passing pull request build in your required status checks in GitHub.
|
|
public var separatePullRequestStatuses: Bool?
|
|
/// The status to use for blocked builds. Pending can be used with required status checks to prevent merging pull requests with blocked builds.
|
|
public var publishBlockedAsPending: Bool?
|
|
|
|
public init(
|
|
repository: String? = nil,
|
|
buildPullRequests: Bool? = nil,
|
|
pullRequestBranchFilterEnabled: Bool? = nil,
|
|
pullRequestBranchFilterConfiguration: String? = nil,
|
|
skipPullRequestBuildsForExistingCommits: Bool? = nil,
|
|
buildTags: Bool? = nil,
|
|
publishCommitStatus: Bool? = nil,
|
|
publishCommitStatusPerStep: Bool? = nil,
|
|
triggerMode: String? = nil,
|
|
filterEnabled: Bool? = nil,
|
|
filterCondition: String? = nil,
|
|
buildPullRequestForks: Bool? = nil,
|
|
prefixPullRequestForkBranchNames: Bool? = nil,
|
|
separatePullRequestStatuses: Bool? = nil,
|
|
publishBlockedAsPending: Bool? = nil
|
|
) {
|
|
self.repository = repository
|
|
self.buildPullRequests = buildPullRequests
|
|
self.pullRequestBranchFilterEnabled = pullRequestBranchFilterEnabled
|
|
self.pullRequestBranchFilterConfiguration = pullRequestBranchFilterConfiguration
|
|
self.skipPullRequestBuildsForExistingCommits = skipPullRequestBuildsForExistingCommits
|
|
self.buildTags = buildTags
|
|
self.publishCommitStatus = publishCommitStatus
|
|
self.publishCommitStatusPerStep = publishCommitStatusPerStep
|
|
self.triggerMode = triggerMode
|
|
self.filterEnabled = filterEnabled
|
|
self.filterCondition = filterCondition
|
|
self.buildPullRequestForks = buildPullRequestForks
|
|
self.prefixPullRequestForkBranchNames = prefixPullRequestForkBranchNames
|
|
self.separatePullRequestStatuses = separatePullRequestStatuses
|
|
self.publishBlockedAsPending = publishBlockedAsPending
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case repository
|
|
case buildPullRequests = "build_pull_requests"
|
|
case pullRequestBranchFilterEnabled = "pull_request_branch_filter_enabled"
|
|
case pullRequestBranchFilterConfiguration = "pull_request_branch_filter_configuration"
|
|
case skipPullRequestBuildsForExistingCommits = "skip_pull_request_builds_for_existing_commits"
|
|
case buildTags = "build_tags"
|
|
case publishCommitStatus = "publish_commit_status"
|
|
case publishCommitStatusPerStep = "publish_commit_status_per_step"
|
|
case triggerMode = "trigger_mode"
|
|
case filterEnabled = "filter_enabled"
|
|
case filterCondition = "filter_condition"
|
|
case buildPullRequestForks = "build_pull_request_forks"
|
|
case prefixPullRequestForkBranchNames = "prefix_pull_request_fork_branch_names"
|
|
case separatePullRequestStatuses = "separate_pull_request_statuses"
|
|
case publishBlockedAsPending = "publish_blocked_as_pending"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Pipeline {
|
|
/// A pipeline step.
|
|
public enum Step: Codable, Equatable, Hashable, Sendable {
|
|
case script(Command)
|
|
case waiter(Wait)
|
|
case manual(Block)
|
|
case trigger(Trigger)
|
|
|
|
private enum Unassociated: String, Codable {
|
|
case script
|
|
case waiter
|
|
case manual
|
|
case trigger
|
|
}
|
|
|
|
public init(
|
|
from decoder: Decoder
|
|
) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
let type = try container.decode(Unassociated.self, forKey: .type)
|
|
switch type {
|
|
case .script:
|
|
self = .script(try Command(from: decoder))
|
|
case .waiter:
|
|
self = .waiter(try Wait(from: decoder))
|
|
case .manual:
|
|
self = .manual(try Block(from: decoder))
|
|
case .trigger:
|
|
self = .trigger(try Trigger(from: decoder))
|
|
}
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
switch self {
|
|
case .script(let step):
|
|
try step.encode(to: encoder)
|
|
case .waiter(let step):
|
|
try step.encode(to: encoder)
|
|
case .manual(let step):
|
|
try step.encode(to: encoder)
|
|
case .trigger(let step):
|
|
try step.encode(to: encoder)
|
|
}
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
}
|
|
|
|
/// Command step.
|
|
public struct Command: Codable, Equatable, Hashable, Sendable {
|
|
public var type = "script"
|
|
/// Name of the step.
|
|
public var name: String?
|
|
/// The shell command/s to run during this step.
|
|
public var command: String?
|
|
/// The label that will be displayed in the pipeline visualisation in Buildkite. Supports emoji.
|
|
public var label: String?
|
|
/// The [glob path](https://buildkite.com/docs/agent/v3/cli-artifact#uploading-artifacts)
|
|
/// or paths of [artifacts](https://buildkite.com/docs/agent/v3/cli-artifact) to upload
|
|
/// from this step.
|
|
public var artifactPaths: String?
|
|
/// The [branch pattern](https://buildkite.com/docs/pipelines/branch-configuration#branch-pattern-examples)
|
|
/// defining which branches will include this step in their builds.
|
|
public var branchConfiguration: String?
|
|
/// A map of [environment variables](https://buildkite.com/docs/pipelines/environment-variables) for this step.
|
|
public var env: JSONValue
|
|
/// The maximum number of minutes a job created from this step is allowed to run. If the
|
|
/// job exceeds this time limit, or if it finishes with a non-zero exit status, the job is
|
|
/// automatically canceled and the build fails. Jobs that time out with an exit status
|
|
/// of 0 are marked as "passed".
|
|
///
|
|
/// Note that command steps on the Buildkite [Free plan](https://buildkite.com/pricing) have a maximum job timeout of 240 minutes.
|
|
///
|
|
/// You can also set
|
|
/// [default and maximum timeouts](https://buildkite.com/docs/pipelines/command-step#command-step-attributes-build-timeouts)
|
|
/// in the Buildkite UI.
|
|
public var timeoutInMinutes: Int?
|
|
/// A map of [agent tag](https://buildkite.com/docs/agent/v3/cli-start#setting-tags)
|
|
/// keys to values to [target specific agents](https://buildkite.com/docs/agent/v3/cli-start#agent-targeting)
|
|
/// for this step.
|
|
public var agentQueryRules: [String]
|
|
/// If set to `true` the step will immediately continue, regardless of the success of
|
|
/// the triggered build. If set to `false` the step will wait for the triggered build to
|
|
/// complete and continue only if the triggered build passed.
|
|
public var async: Bool?
|
|
/// The [maximum number of jobs](https://buildkite.com/docs/pipelines/controlling-concurrency#concurrency-limits)
|
|
/// created from this step that are allowed to run at the same time.
|
|
public var concurrency: Int?
|
|
/// The number of [parallel jobs](https://buildkite.com/docs/tutorials/parallel-builds#parallel-jobs)
|
|
/// that will be created based on this step.
|
|
public var parallelism: Int?
|
|
|
|
public init(
|
|
name: String? = nil,
|
|
command: String? = nil,
|
|
label: String? = nil,
|
|
artifactPaths: String? = nil,
|
|
branchConfiguration: String? = nil,
|
|
env: JSONValue,
|
|
timeoutInMinutes: Int? = nil,
|
|
agentQueryRules: [String],
|
|
async: Bool? = nil,
|
|
concurrency: Int? = nil,
|
|
parallelism: Int? = nil
|
|
) {
|
|
self.name = name
|
|
self.command = command
|
|
self.label = label
|
|
self.artifactPaths = artifactPaths
|
|
self.branchConfiguration = branchConfiguration
|
|
self.env = env
|
|
self.timeoutInMinutes = timeoutInMinutes
|
|
self.agentQueryRules = agentQueryRules
|
|
self.async = async
|
|
self.concurrency = concurrency
|
|
self.parallelism = parallelism
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
case name
|
|
case command
|
|
case label
|
|
case artifactPaths = "artifact_paths"
|
|
case branchConfiguration = "branch_configuration"
|
|
case env
|
|
case timeoutInMinutes = "timeout_in_minutes"
|
|
case agentQueryRules = "agent_query_rules"
|
|
case async
|
|
case concurrency
|
|
case parallelism
|
|
}
|
|
}
|
|
|
|
/// Wait step.
|
|
public struct Wait: Codable, Equatable, Hashable, Sendable {
|
|
public var type = "waiter"
|
|
/// Human-readable label.
|
|
public var label: String?
|
|
/// Whether to continue the build if previous steps have failed.
|
|
public var continueAfterFailure: Bool?
|
|
|
|
public init(
|
|
label: String? = nil,
|
|
continueAfterFailure: Bool? = nil
|
|
) {
|
|
self.label = label
|
|
self.continueAfterFailure = continueAfterFailure
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
case label
|
|
case continueAfterFailure = "continue_after_failure"
|
|
}
|
|
}
|
|
|
|
/// Block step.
|
|
public struct Block: Codable, Equatable, Hashable, Sendable {
|
|
public var type = "manual"
|
|
/// Human-readable label.
|
|
public var label: String?
|
|
|
|
public init(
|
|
label: String? = nil
|
|
) {
|
|
self.label = label
|
|
}
|
|
}
|
|
|
|
/// Trigger step.
|
|
public struct Trigger: Codable, Equatable, Hashable, Sendable {
|
|
public var type = "trigger"
|
|
/// Pipeline slug to trigger a build on.
|
|
public var triggerProjectSlug: String?
|
|
/// Human-readable label.
|
|
public var label: String?
|
|
/// Repository commit to trigger the build for.
|
|
public var triggerCommit: String?
|
|
/// Repository branch to trigger the build for.
|
|
public var triggerBranch: String?
|
|
/// If set to `true` the step will immediately continue, regardless of the success of
|
|
/// the triggered build. If set to `false` the step will wait for the triggered build to
|
|
/// complete and continue only if the triggered build passed.
|
|
///
|
|
/// Note that when ``triggerAsync`` is set to `true`, as long as the triggered
|
|
/// build starts, the original pipeline will show that as successful. The original pipeline
|
|
/// does not get updated after subsequent steps or after the triggered build completes.
|
|
public var triggerAsync: Bool?
|
|
|
|
public init(
|
|
triggerProjectSlug: String? = nil,
|
|
label: String? = nil,
|
|
triggerCommit: String? = nil,
|
|
triggerBranch: String? = nil,
|
|
triggerAsync: Bool? = nil
|
|
) {
|
|
self.triggerProjectSlug = triggerProjectSlug
|
|
self.label = label
|
|
self.triggerCommit = triggerCommit
|
|
self.triggerBranch = triggerBranch
|
|
self.triggerAsync = triggerAsync
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case type
|
|
case triggerProjectSlug = "trigger_project_slug"
|
|
case label
|
|
case triggerCommit = "trigger_commit"
|
|
case triggerBranch = "trigger_branch"
|
|
case triggerAsync = "trigger_async"
|
|
}
|
|
}
|
|
}
|
|
}
|