Skip to content

Commit

Permalink
Merge pull request #917 from Shopify/ajs/743_set_ref_upon_deploy
Browse files Browse the repository at this point in the history
Set a git ref when a deploy is successful.
  • Loading branch information
ajshepley committed Jul 5, 2019
2 parents 6422110 + 63486cc commit aa6a1e3
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 0 deletions.
41 changes: 41 additions & 0 deletions app/jobs/shipit/update_github_last_deployed_ref_job.rb
@@ -0,0 +1,41 @@
module Shipit
class UpdateGithubLastDeployedRefJob < BackgroundJob
queue_as :default

DEPLOY_PREFIX = 'shipit-deploy'.freeze

def perform(stack)
stack_sha = stack.last_successful_deploy_commit&.sha
return unless stack_sha

environment = stack.environment
stack_ref = create_full_ref(environment)
client = Shipit.github.api

full_repo_name = stack.github_repo_name

update_or_create_ref(client: client, repo_name: full_repo_name, ref: stack_ref, new_sha: stack_sha)
end

private

def create_full_ref(stack_environment)
[DEPLOY_PREFIX, stack_environment].join("/")
end

def create_ref(client:, repo_name:, ref:, sha:)
client.create_ref(repo_name, ref, sha)
end

def update_or_create_ref(client:, repo_name:, ref:, new_sha:)
client.update_ref(repo_name, ref, new_sha)
rescue Octokit::UnprocessableEntity => e
error_msg = e.message
if error_msg.include? "Reference does not exist"
create_ref(client: client, repo_name: repo_name, ref: ref, sha: new_sha)
else
raise
end
end
end
end
5 changes: 5 additions & 0 deletions app/models/shipit/deploy.rb
Expand Up @@ -8,6 +8,7 @@ class Deploy < Task
after_transition to: :success, do: :schedule_continuous_delivery
after_transition to: :success, do: :schedule_merges
after_transition to: :success, do: :update_undeployed_commits_count
after_transition to: :success, do: :update_latest_deployed_ref
after_transition to: :aborted, do: :trigger_revert_if_required
after_transition any => any, do: :update_release_status
after_transition any => any, do: :update_commit_deployments
Expand Down Expand Up @@ -280,5 +281,9 @@ def update_undeployed_commits_count
def update_last_deploy_time
stack.update(last_deployed_at: ended_at)
end

def update_latest_deployed_ref
stack.update_latest_deployed_ref
end
end
end
8 changes: 8 additions & 0 deletions app/models/shipit/stack.rb
Expand Up @@ -288,6 +288,10 @@ def last_completed_deploy
deploys_and_rollbacks.last_completed
end

def last_successful_deploy_commit
deploys_and_rollbacks.last_successful&.until_commit
end

def previous_successful_deploy(deploy_id)
deploys_and_rollbacks.success.where("id < ?", deploy_id).last
end
Expand Down Expand Up @@ -458,6 +462,10 @@ def update_undeployed_commits_count(after_commit = nil)
update(undeployed_commits_count: undeployed_commits)
end

def update_latest_deployed_ref
UpdateGithubLastDeployedRefJob.perform_later(self)
end

def broadcast_update
Pubsubstub.publish(
"stack.#{id}",
Expand Down
4 changes: 4 additions & 0 deletions app/models/shipit/task.rb
Expand Up @@ -47,6 +47,10 @@ def last_completed
completed.last
end

def last_successful
success.last
end

def current
active.exclusive.last
end
Expand Down
88 changes: 88 additions & 0 deletions test/jobs/update_github_last_deployed_ref_job_test.rb
@@ -0,0 +1,88 @@
require 'test_helper'

module Shipit
class UpdateGithubLastDeployedRefJobTest < ActiveSupport::TestCase
setup do
@stack = shipit_stacks(:shipit)
@job = UpdateGithubLastDeployedRefJob.new
@deploy = @stack.deploys.last
@commit = @deploy.until_commit
@api_client = Shipit.github.api
@expected_ref_prefix = "shipit-deploy/#{@stack.environment}"
@expected_name = @stack.github_repo_name
@expected_sha = @commit.sha

expected_ref = ["refs", @expected_ref_prefix].join('/')
ref_url = "http://api.github.test.com/shopify/shipit-engine/git/#{expected_ref}"
commit_url = "https://api.github.test.com/repos/shopify/shipit-engine/git/commits/#{@commit.sha}"
response_inner_obj = OpenStruct.new(sha: @commit.sha, type: "commit", url: commit_url)
@response = OpenStruct.new(ref: expected_ref, node_id: "blah", url: ref_url, object: response_inner_obj)
end

test "#perform will create a ref when one is not present" do
Octokit::UnprocessableEntity.any_instance.stubs(:build_error_message).returns("Reference does not exist")

@api_client.expects(:update_ref).with(@expected_name, @expected_ref_prefix, @expected_sha).raises(Octokit::UnprocessableEntity)
@api_client.expects(:create_ref).with(@expected_name, @expected_ref_prefix, @expected_sha).returns(@response)

result = @job.perform(@stack)

assert_equal @response, result
end

test "#perform will update a ref when one is present" do
prior_response = @response.dup
prior_response.object = prior_response.object.dup
new_sha = "some_new_sha"
@response.object.sha = new_sha
@commit.sha = new_sha
@commit.save

@api_client.expects(:update_ref).with(@expected_name, @expected_ref_prefix, new_sha).returns(@response)

result = @job.perform(@stack)

assert_equal @response, result
end

test '#perform will raise an exception for non ref existence errors' do
Octokit::UnprocessableEntity.any_instance.stubs(:build_error_message).returns("Some other error.")

@api_client.expects(:update_ref).with(@expected_name, @expected_ref_prefix, @expected_sha).raises(Octokit::UnprocessableEntity)
@api_client.expects(:create_ref).with(@expected_name, @expected_ref_prefix, @expected_sha).never

assert_raises Octokit::UnprocessableEntity do
@job.perform(@stack)
end
end

test '#perform skips unsuccessful deploys when finding sha to use' do
prior_response = @response.dup
prior_response.object = prior_response.object.dup
new_sha = "some_new_sha"
@response.object.sha = new_sha
@commit.sha = new_sha
@commit.save

new_deploy = @stack.deploys.last.dup
new_commit = new_deploy.until_commit.dup
new_commit.sha = "some fake sha"
new_deploy.until_commit = new_commit
new_deploy.id = nil
new_deploy.status = "faulty"
new_deploy.save

@api_client.expects(:update_ref).with(@expected_name, @expected_ref_prefix, new_sha).returns(@response)
result = @job.perform(@stack)

assert_equal @response, result

new_deploy.reload
new_deploy.status = "success"
new_deploy.save

@api_client.expects(:update_ref).with(@expected_name, @expected_ref_prefix, new_commit.sha).returns(@response)
@job.perform(@stack)
end
end
end
7 changes: 7 additions & 0 deletions test/models/deploys_test.rb
Expand Up @@ -245,6 +245,13 @@ def setup
end
end

test "transitioning to success enqueues a last-deployed git reference update" do
@deploy = shipit_deploys(:shipit_running)
assert_enqueued_with(job: UpdateGithubLastDeployedRefJob, args: [@deploy.stack]) do
@deploy.complete!
end
end

test "transitions to any state updates last deploy time to stack record" do
@deploy = shipit_deploys(:shipit_running)
@deploy.complete!
Expand Down
33 changes: 33 additions & 0 deletions test/models/stacks_test.rb
Expand Up @@ -573,6 +573,39 @@ def setup
end
end

test "#trigger_continuous_delivery enqueues deployment ref update job" do
@stack = shipit_stacks(:shipit_canaries)
shipit_tasks(:canaries_running).delete

assert_no_enqueued_jobs(only: Shipit::UpdateGithubLastDeployedRefJob) do
assert_no_difference -> { Deploy.count } do
@stack.trigger_continuous_delivery
end
end

assert_enqueued_with(job: Shipit::UpdateGithubLastDeployedRefJob, args: [@stack]) do
@stack.last_active_task.complete!
end
end

test "#trigger_continuous_delivery executes ref update job with correct sha" do
@stack = shipit_stacks(:shipit_canaries)
shipit_tasks(:canaries_running).delete

assert_no_enqueued_jobs(only: Shipit::UpdateGithubLastDeployedRefJob) do
assert_no_difference -> { Deploy.count } do
@stack.trigger_continuous_delivery
end
end

desired_last_commit_sha = @stack.last_active_task.until_commit.sha
Shipit.github.api.expects(:update_ref).with(anything, anything, desired_last_commit_sha).returns("test")

perform_enqueued_jobs(only: Shipit::UpdateGithubLastDeployedRefJob) do
@stack.last_active_task.complete!
end
end

test "#trigger_continuous_delivery trigger a deploy if all conditions are met" do
@stack.tasks.delete_all
assert_predicate @stack, :deployable?
Expand Down

0 comments on commit aa6a1e3

Please sign in to comment.