Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support proper trunk flow #675

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions app/assets/images/tag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions app/components/v2/commit_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
<% end %>
</div>

<% if tag_name.present? %>
<%= render V2::BadgeComponent.new(kind: :badge) do |badge| %>
<% badge.with_icon("tag.svg") %>
<% badge.with_link(tag_name, tag_url) %>
<% end %>
<% end %>

<% if pull_request.present? %>
<%= render V2::BadgeComponent.new(kind: :badge) do |badge| %>
<% badge.with_icon("v2/git_pull_request.svg") %>
Expand Down
3 changes: 2 additions & 1 deletion app/components/v2/commit_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ def initialize(commit:, avatar: true, detailed: true)
end

attr_reader :commit
delegate :message, :author_name, :author_email, :author_login, :author_url, :timestamp, :short_sha, :url, to: :commit
delegate :message, :author_name, :author_email, :author_login,
:author_url, :timestamp, :short_sha, :url, :tag_name, :tag_url, to: :commit

def author_link
author_url || "mailto:#{author_email}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ def changelog_from

def apply_help_text
return if change_queue_commits.blank?
"#{change_queue_commits_count} commit(s) in the queue. These will be automatically applied in #{time_in_words(build_queue&.scheduled_at)} or after #{build_queue&.build_queue_size} commits."
if release.train.trunk?
"#{change_queue_commits_count} commit(s) in the queue."
else
"#{change_queue_commits_count} commit(s) in the queue. These will be automatically applied in #{time_in_words(build_queue&.scheduled_at)} or after #{build_queue&.build_queue_size} commits."
end
end
end
10 changes: 6 additions & 4 deletions app/components/v2/live_release/finalize_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
<div class="flex flex-col gap-2 text-sm text-secondary">
<% if train.tag_releases? %>
<p class="<%= strikethrough %>">
◦ Cut tag at <code><%= release.last_commit&.short_sha %></code> and push to <%= tag_link %> <%= checked %>
◦ Cut a VCS release at <code><%= release.last_commit&.short_sha %></code> and push to <%= tag_link %> <%= checked %>
</p>
<% end %>

<p class="<%= strikethrough %>">
◦ Ensure all unmerged changes are pushed back to <%= release.train.working_branch %> <%= checked %>
</p>
<% unless train.trunk? %>
<p class="<%= strikethrough %>">
◦ Ensure all unmerged changes are pushed back to <%= release.train.working_branch %> <%= checked %>
</p>
<% end %>

<p class="<%= strikethrough %>">
◦ Refresh all the DevOps dashboards and reports <%= checked %>
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/trains_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class TrainsController < SignedInApplicationController

def new
@train = @app.trains.new
@train.build_queue_wait_time_value = 0
@train.build_queue_size = 0
end

def edit
Expand Down
24 changes: 22 additions & 2 deletions app/javascript/controllers/domain/branching_selector_controller.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
import {Controller} from "@hotwired/stimulus";

const STRATEGIES = {
trunk: "trunk",
almost_trunk: "almost_trunk",
release_backmerge: "release_backmerge",
parallel_working: "parallel_working"
}
export default class extends Controller {
static targets = ["branchingStrategy", "almostTrunk", "releaseBackMerge", "parallelBranches", "backmerge"]
static targets = ["branchingStrategy", "almostTrunk", "trunk", "releaseBackMerge", "parallelBranches", "backmerge", "buildQueueToggle"]
static outlets = ["domain--build-queue-help", "domain--release-schedule-help"]

initialize() {
this.showCorrectInputs()
}

change() {
this.showCorrectInputs()
if (this.hasDomainBuildQueueHelpOutlet) {
this.domainBuildQueueHelpOutlet.branchingStrategyValue = this.branchingStrategyTarget.value
}
}

showCorrectInputs() {
this.__resetFields()
const selectedBranchingStrategy = this.branchingStrategyTarget.value

if (selectedBranchingStrategy === STRATEGIES.almost_trunk) {

if (selectedBranchingStrategy === STRATEGIES.trunk) {
this.disableBuildQueueToggle()
this.__hideBackmergeConfig()
} else if (selectedBranchingStrategy === STRATEGIES.almost_trunk) {
this.almostTrunkTarget.hidden = false
this.enableBuildQueueToggle()
this.__showBackmergeConfig()
} else if (selectedBranchingStrategy === STRATEGIES.release_backmerge) {
this.releaseBackMergeTarget.hidden = false
this.enableBuildQueueToggle()
this.__hideBackmergeConfig()
} else if (selectedBranchingStrategy === STRATEGIES.parallel_working) {
this.parallelBranchesTarget.hidden = false
this.enableBuildQueueToggle()
this.__hideBackmergeConfig()
}
}
Expand All @@ -45,4 +57,12 @@ export default class extends Controller {
__showBackmergeConfig() {
this.backmergeTarget.hidden = false
}

disableBuildQueueToggle() {
this.buildQueueToggleTarget.disabled = true
}

enableBuildQueueToggle() {
this.buildQueueToggleTarget.disabled = false;
}
}
23 changes: 21 additions & 2 deletions app/javascript/controllers/domain/build_queue_help_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ const BASE_HELP_TEXT = "Changes will be applied to the release every "
const ERR_HELP_TEXT = "You must set a valid build queue config when it is enabled"

export default class extends Controller {
static targets = ["checkbox", "size", "waitTimeValue", "waitTimeUnit", "output", "errOutput"];
static targets = ["checkbox", "size", "waitTimeValue", "waitTimeUnit", "output", "errOutput"]
static values = {
branchingStrategy: String
}

initialize() {
this.change();
}

branchingStrategyValueChanged() {
this.change();
}

change() {
this.__resetContents()

Expand All @@ -29,7 +36,13 @@ export default class extends Controller {
const waitTimeUnit = this.waitTimeUnitTarget.value
const waitTimeValue = this.waitTimeValueTarget.value

this.outputTarget.textContent = `${BASE_HELP_TEXT}${waitTimeValue} ${waitTimeUnit} OR ${size} commits`
if (this.branchingStrategyValue === "trunk") {
this.outputTarget.textContent = "Changes will be applied manually"
this.__changeInputStates(true)
} else {
this.outputTarget.textContent = `${BASE_HELP_TEXT}${waitTimeValue} ${waitTimeUnit} OR ${size} commits`
this.__changeInputStates(false)
}
}

__resetContents() {
Expand All @@ -40,4 +53,10 @@ export default class extends Controller {
__isEmptyConfig() {
return this.sizeTarget.value === "" || this.waitTimeUnitTarget.value === "" || this.waitTimeValueTarget.value === ""
}

__changeInputStates(enabled) {
this.sizeTarget.disabled = enabled
this.waitTimeValueTarget.disabled = enabled
this.waitTimeUnitTarget.disabled = enabled
}
}
3 changes: 3 additions & 0 deletions app/libs/coordinators/apply_commit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def initialize(release, commit)

def call
return unless commit.applicable?
commit.create_tag! if train.tag_applied_commits?

release.release_platform_runs.each do |run|
next unless run.on_track?

Expand Down Expand Up @@ -44,4 +46,5 @@ def apply_change?(run)
end

attr_reader :release, :commit
delegate :train, to: :release
end
1 change: 1 addition & 0 deletions app/libs/coordinators/finalize_release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def initialize(release, force_finalize = false)
end

HANDLERS = {
"trunk" => Trunk,
"almost_trunk" => AlmostTrunk,
"parallel_working" => ParallelBranches,
"release_backmerge" => ReleaseBackMerge
Expand Down
16 changes: 16 additions & 0 deletions app/libs/coordinators/finalize_release/trunk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Coordinators::FinalizeRelease::Trunk
def self.call(release)
new(release).call
end

def initialize(release)
@release = release
end

def call
GitHub::Result.new { release.create_release_from_tag!(release.applied_commits.last.tag_name) }
end

attr_reader :release
delegate :train, to: :release
end
2 changes: 1 addition & 1 deletion app/libs/coordinators/process_commits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def call
def create_head_commit!
commit = Commit.find_or_create_by!(commit_params(fudge_timestamp(head_commit)))
if release.queue_commit?(commit)
queue_commit!(commit)
queue_commit!(commit, can_apply: !release.train.trunk?)
else
Coordinators::ApplyCommit.call(release, commit)
end
Expand Down
1 change: 1 addition & 0 deletions app/libs/coordinators/start_release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def create_release
end

def release_branch
return train.working_branch if train.trunk?
return new_branch_name(hotfix: true) if hotfix_from_new_branch? && create_branches?
return existing_hotfix_branch if hotfix_from_previous_branch?
return new_branch_name if create_branches?
Expand Down
2 changes: 1 addition & 1 deletion app/libs/coordinators/webhooks/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def initialize(train, payload)
end

def release
@release ||= train.active_runs.for_branch(branch_name)
@release ||= train.active_runs.find_active_for_train(train.id)
end

private
Expand Down
6 changes: 3 additions & 3 deletions app/libs/installations/github/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@ def create_annotated_tag!(repo, name, branch_name, message, tagger_name, tagger_
end

# creates a lightweight tag and a GitHub release simultaneously
def create_release!(repo, tag_name, branch_name, release_notes = nil)
def create_release!(repo, tag_name, branch_name = nil, release_notes = nil)
options = {
target_commitish: branch_name,
name: tag_name,
body: release_notes.presence,
generate_release_notes: release_notes.blank?
}.compact
options[:target_commitish] = branch_name if branch_name.present?
execute do
raise Installations::Error.new("Should not create a tag", reason: :tag_reference_already_exists) if tag_exists?(repo, tag_name)
raise Installations::Error.new("Should not create a tag", reason: :tag_reference_already_exists) if branch_name.present? && tag_exists?(repo, tag_name)
@client.create_release(repo, tag_name, options)
end
end
Expand Down
3 changes: 2 additions & 1 deletion app/libs/triggers/pre_release.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ class Triggers::PreRelease
RELEASE_HANDLERS = {
"almost_trunk" => AlmostTrunk,
"parallel_working" => ParallelBranches,
"release_backmerge" => ReleaseBackMerge
"release_backmerge" => ReleaseBackMerge,
"trunk" => Trunk
}

def self.call(release)
Expand Down
17 changes: 17 additions & 0 deletions app/libs/triggers/pre_release/trunk.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Triggers::PreRelease::Trunk
def self.call(release, release_branch)
new(release, release_branch).call
end

def initialize(release, release_branch)
@release = release
@release_branch = release_branch
end

def call
GitHub::Result.new do
latest_commit = @release.latest_commit_hash(sha_only: false)
Coordinators::Signals.commits_have_landed!(@release, latest_commit, [])
end
end
end
1 change: 1 addition & 0 deletions app/models/build_queue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def add_commit_v2!(commit, can_apply: true)
end

def schedule_kickoff!
return if train.trunk?
BuildQueueApplicationJob.set(wait_until: scheduled_at).perform_later(id)
end

Expand Down
17 changes: 15 additions & 2 deletions app/models/commit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# commit_hash :string not null, indexed => [release_id]
# message :string
# parents :jsonb
# tag_name :string
# timestamp :datetime not null, indexed => [release_id]
# url :string
# created_at :datetime not null
Expand All @@ -23,6 +24,7 @@ class Commit < ApplicationRecord
has_paper_trail
include Passportable
include Commitable
include Taggable

self.implicit_order_column = :timestamp

Expand All @@ -34,13 +36,13 @@ class Commit < ApplicationRecord

scope :sequential, -> { order(timestamp: :desc) }

STAMPABLE_REASONS = ["created"]
STAMPABLE_REASONS = %w[created tag_created]

validates :commit_hash, uniqueness: {scope: :release_id}

after_commit -> { create_stamp!(data: {sha: short_sha}) }, on: :create

delegate :release_platform_runs, :notify!, :train, :platform, to: :release
delegate :release_platform_runs, :notify!, :train, :platform, :base_tag_name, to: :release

def self.commit_messages(first_parent_only = false)
Commit.commit_log(reorder("timestamp DESC"), first_parent_only)&.map(&:message)
Expand Down Expand Up @@ -77,6 +79,17 @@ def self.between_commits(base_commit, head_commit)
end
end

# recursively attempt to create a release tag until a unique one gets created
# it *can* get expensive in the worst-case scenario, so ideally invoke this in a bg job
def create_tag!(input_tag_name = base_tag_name)
train.create_tag!(input_tag_name, commit_hash)
update!(tag_name: input_tag_name)
event_stamp!(reason: :tag_created, kind: :notice, data: {tag: tag_name, commit_sha: short_sha, commit_url: url})
rescue Installations::Error => ex
raise unless ex.reason == :tag_reference_already_exists
create_tag!(unique_tag_name(input_tag_name, short_sha))
end

def team
user&.team_for(release.organization)
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/concerns/commitable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def pull_request = nil

def backmerge_failure? = nil

def tag_name = nil

def eql?(other)
commit_hash == other.commit_hash
end
Expand Down
5 changes: 5 additions & 0 deletions app/models/concerns/taggable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ def unique_tag_name(currently, sha)
return [base_tag_name, "-", sha].join if currently.end_with?(base_tag_name)
[base_tag_name, "-", sha, "-", Time.now.to_i].join
end

def tag_url
return if tag_name.blank?
train.vcs_provider&.tag_url(tag_name)
end
end
Loading
Loading