Skip to content

Commit

Permalink
feat/can-i-merge badge (#707)
Browse files Browse the repository at this point in the history
* Implementing the can-i-merge logic and can-i-merge build badge logic

* Add badge resource

* Fix test build failing due to inconsistent data setup
Fixed rubocop warnings

* Minor code cleanup

* Trying to fix github build

* Force installing docker compose, picked the current latest version
But maybe it should be picking up based on latest available at runtime

* Correctly using docker compose command

---------

Co-authored-by: Phanindra Srungavarapu <[email protected]>
  • Loading branch information
pahnin and Phanindra Srungavarapu authored Aug 5, 2024
1 parent d1dc495 commit 2f09ef3
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 7 deletions.
1 change: 1 addition & 0 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_
add ["pacticipants", :pacticipant_name, "latest-version", :tag], Api::Resources::LatestVersion, {resource_name: "latest_tagged_pacticipant_version"}
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to], Api::Resources::CanIDeployPacticipantVersionByTagToTag, { resource_name: "can_i_deploy_latest_tagged_version_to_tag" }
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to, "badge"], Api::Resources::CanIDeployPacticipantVersionByTagToTagBadge, { resource_name: "can_i_deploy_latest_tagged_version_to_tag_badge" }
add ["pacticipants", :pacticipant_name, "main-branch", "can-i-merge", "badge"], Api::Resources::CanIMergeBadge, { resource_name: "can_i_merge_badge" }
add ["pacticipants", :pacticipant_name, "latest-version"], Api::Resources::LatestVersion, {resource_name: "latest_pacticipant_version"}
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"}
add ["pacticipants", :pacticipant_name, "branches"], Api::Resources::PacticipantBranches, {resource_name: "pacticipant_branches"}
Expand Down
3 changes: 2 additions & 1 deletion lib/pact_broker/api/paths.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ module Api
module Paths
PACT_BADGE_PATH = %r{^/pacts/provider/[^/]+/consumer/.*/badge(?:\.[A-Za-z]+)?$}.freeze
MATRIX_BADGE_PATH = %r{^/matrix/provider/[^/]+/latest/[^/]+/consumer/[^/]+/latest/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
CAN_I_MERGE_BADGE_PATH = %r{^/pacticipants/[^/]+/main-branch/can-i-merge/badge(?:\.[A-Za-z]+)?$}.freeze
CAN_I_DEPLOY_TAG_BADGE_PATH = %r{^/pacticipants/[^/]+/latest-version/[^/]+/can-i-deploy/to/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
CAN_I_DEPLOY_BRANCH_ENV_BADGE_PATH = %r{^/pacticipants/[^/]+/branches/[^/]+/latest-version/can-i-deploy/to-environment/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
VERIFICATION_RESULTS = %r{^/pacts/provider/[^/]+/consumer/[^/]+/pact-version/[^/]+/verification-results/[^/]+}

BADGE_PATHS = [PACT_BADGE_PATH, MATRIX_BADGE_PATH, CAN_I_DEPLOY_TAG_BADGE_PATH, CAN_I_DEPLOY_BRANCH_ENV_BADGE_PATH]
BADGE_PATHS = [PACT_BADGE_PATH, MATRIX_BADGE_PATH, CAN_I_DEPLOY_TAG_BADGE_PATH, CAN_I_DEPLOY_BRANCH_ENV_BADGE_PATH, CAN_I_MERGE_BADGE_PATH]

extend self

Expand Down
39 changes: 39 additions & 0 deletions lib/pact_broker/api/resources/can_i_merge_badge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "pact_broker/api/resources/base_resource"
require "pact_broker/api/resources/badge_methods"

module PactBroker
module Api
module Resources
class CanIMergeBadge < BaseResource
include BadgeMethods # This module contains all necessary webmachine methods for badge implementation

def badge_url
if pacticipant.nil?
# if the pacticipant is nil, we return an error badge url
badge_service.error_badge_url("pacticipant", "not found")
elsif version.nil?
# when there is no main branch version, we return an error badge url
badge_service.error_badge_url("main branch version", "not found")
else
# we call badge_service to build the badge url
badge_service.can_i_merge_badge_url(
version_number: version.number,
deployable: results
)
end
end

private

def results
# can_i_merge returns true or false if the main branch version is compatible with all the integrations
@results ||= matrix_service.can_i_merge(pacticipant: pacticipant, latest_main_branch_version: version)
end

def version
@version ||= version_service.find_latest_version_from_main_branch(pacticipant)
end
end
end
end
end
14 changes: 14 additions & 0 deletions lib/pact_broker/badges/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ def can_i_deploy_badge_url(tag, environment_tag, label, deployable)
build_shield_io_uri(title, status, color)
end

def can_i_merge_badge_url(version_number: nil, deployable: nil)
title = "can-i-merge"
status = deployable ? "yes" : "no"
if deployable.nil?
color = "lightgrey"
status = "unknown"
else
color = deployable ? "brightgreen" : "red"
status = version_number
end
# left text is "can-i-merge", right text is the version number
build_shield_io_uri(title, status, color)
end

def error_badge_url(left_text, right_text)
build_shield_io_uri(left_text, right_text, "lightgrey")
end
Expand Down
26 changes: 26 additions & 0 deletions lib/pact_broker/matrix/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,32 @@ def can_i_deploy(selectors, options = {})
QueryResultsWithDeploymentStatusSummary.new(query_results, DeploymentStatusSummary.new(query_results))
end

def can_i_merge(pacticipant_name: nil, pacticipant: nil, latest_main_branch_version: nil)
# first we find the pacticipant by name (or use the one passed in) if pacticipant is nil
if pacticipant.nil?
pacticipant = pacticipant_service.find_pacticipant_by_name(pacticipant_name)
raise PactBroker::Error.new("No pacticipant found with name '#{pacticipant_name}'") unless pacticipant
else
pacticipant_name = pacticipant.name
end

# then we find the latest version from the main branch if not passed in
if latest_main_branch_version.nil?
latest_main_branch_version = version_service.find_latest_version_from_main_branch(pacticipant)
raise PactBroker::Error.new("No main branch version found for pacticipant '#{pacticipant_name}'") unless latest_main_branch_version
end

selectors = PactBroker::Matrix::UnresolvedSelector.from_hash(
pacticipant_name: pacticipant_name,
pacticipant_version_number: latest_main_branch_version.number
)

options = { main_branch: true, latest: true, latestby: "cvp" }
query_results = can_i_deploy([selectors], options)

query_results.deployable?
end

def find selectors, options = {}
logger.info "Querying matrix", selectors: selectors, options: options
matrix_repository.find(selectors, options)
Expand Down
11 changes: 9 additions & 2 deletions lib/pact_broker/test/test_data_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,16 @@ def create_tag_with_hierarchy pacticipant_name, pacticipant_version, tag_name

def create_pacticipant pacticipant_name, params = {}
params.delete(:comment)
version_to_create = params.delete(:version)

repository_url = "https://github.com/#{params[:repository_namespace] || "example-organization"}/#{params[:repository_name] || pacticipant_name}"
merged_params = { name: pacticipant_name, repository_url: repository_url }.merge(params)
@pacticipant = PactBroker::Domain::Pacticipant.create(merged_params)

version = create_pacticipant_version(version_to_create, @pacticipant) if version_to_create
main_branch = params[:main_branch]
PactBroker::Versions::BranchVersionRepository.new.add_branch(version, main_branch) if version && main_branch

self
end

Expand Down Expand Up @@ -639,8 +646,6 @@ def fixed_json_content(consumer_name, provider_name, differentiator)
}.to_json
end

private

def create_pacticipant_version(version_number, pacticipant, params = {})
params.delete(:comment)
tag_names = [params.delete(:tag_names), params.delete(:tag_name)].flatten.compact
Expand All @@ -665,6 +670,8 @@ def create_pacticipant_version(version_number, pacticipant, params = {})
version
end

private

def create_deployed_version(uuid: , currently_deployed: , version:, environment_name: , target: nil, created_at: nil)
env = find_environment(environment_name)
@deployed_version = PactBroker::Deployments::DeployedVersionService.find_or_create(uuid, version, env, target)
Expand Down
4 changes: 2 additions & 2 deletions script/test/run-rake-on-docker-compose-mysql.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#/bin/sh

cleanup() {
docker-compose -f docker-compose-ci-mysql.yml down
docker compose -f docker-compose-ci-mysql.yml down
}

trap cleanup EXIT
docker-compose -f docker-compose-ci-mysql.yml up --exit-code-from tests --abort-on-container-exit --remove-orphans
docker compose -f docker-compose-ci-mysql.yml up --exit-code-from tests --abort-on-container-exit --remove-orphans
4 changes: 2 additions & 2 deletions script/test/run-rake-on-docker-compose-postgres.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#/bin/sh

cleanup() {
docker-compose -f docker-compose-ci-postgres.yml down
docker compose -f docker-compose-ci-postgres.yml down
}

trap cleanup EXIT
docker-compose -f docker-compose-ci-postgres.yml up --exit-code-from tests --abort-on-container-exit --remove-orphans
docker compose -f docker-compose-ci-postgres.yml up --exit-code-from tests --abort-on-container-exit --remove-orphans
61 changes: 61 additions & 0 deletions spec/lib/pact_broker/api/resources/can_i_merge_badge_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
require "pact_broker/api/resources/can_i_merge_badge"
require "pact_broker/api/resources/base_resource"

module PactBroker
module Api
module Resources
describe CanIMergeBadge do
before do
allow_any_instance_of(described_class).to receive(:badge_service).and_return(badge_service)

allow(badge_service). to receive(:can_i_merge_badge_url).and_return("http://badge_url")
allow(badge_service). to receive(:error_badge_url).and_return("http://error_badge_url")

allow_any_instance_of(CanIMergeBadge).to receive(:pacticipant).and_return(pacticipant)
allow_any_instance_of(CanIMergeBadge).to receive(:version).and_return(version)
allow_any_instance_of(CanIMergeBadge).to receive(:results).and_return(results)
end

let(:branch_service) { class_double("PactBroker::Versions::BranchService").as_stubbed_const }
let(:badge_service) { class_double("PactBroker::Badges::Service").as_stubbed_const }

let(:pacticipant) { double("pacticipant") }
let(:version) { double("version", number: "1") }
let(:results) { true }

let(:path) { "/pacticipants/Foo/main-branch/can-i-merge/badge" }

subject { get(path) }

context "when everything is found" do
it "returns a 307" do
expect(subject.status).to eq 307
end

it "return the badge URL" do
expect(badge_service). to receive(:can_i_merge_badge_url).with(version_number: "1", deployable: true)
expect(subject.headers["Location"]).to eq "http://badge_url"
end
end

context "when the pacticipant is not found" do
let(:pacticipant) { nil }

it "returns an error badge URL" do
expect(badge_service).to receive(:error_badge_url).with("pacticipant", "not found")
expect(subject.headers["Location"]).to eq "http://error_badge_url"
end
end

context "when the version is not found" do
let(:version) { nil }

it "returns an error badge URL" do
expect(badge_service).to receive(:error_badge_url).with("main branch version", "not found")
expect(subject.headers["Location"]).to eq "http://error_badge_url"
end
end
end
end
end
end
15 changes: 15 additions & 0 deletions spec/lib/pact_broker/badges/service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ module Badges
allow(Service).to receive(:logger).and_return(logger)
end

describe "can_i_merge_badge_url" do
let(:version_number) { "abcd1234" }
let(:deployable) { true }

subject { Service.can_i_merge_badge_url(version_number: version_number, deployable: deployable) }
context "when deployable is true" do
it { is_expected.to eq URI("https://img.shields.io/badge/can--i--merge-abcd1234-brightgreen.svg") }
end

context "when deployable is false" do
let(:deployable) { false }
it { is_expected.to eq URI("https://img.shields.io/badge/can--i--merge-abcd1234-red.svg") }
end
end

describe "can_i_deploy_badge_url" do
subject { Service.can_i_deploy_badge_url("main", "prod", nil, true) }

Expand Down
40 changes: 40 additions & 0 deletions spec/lib/pact_broker/matrix/service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,46 @@
module PactBroker
module Matrix
describe Service do
describe "can-i-merge" do
before do
td.create_consumer("A", main_branch: "main_branch", version: "1")
.create_provider("B", main_branch: "main_branch", version: "1")
.create_pact_with_hierarchy("A", "1", "B")
.create_verification(provider_version: "1", number: 1, success: false, branch: "main_branch")
.create_verification(provider_version: "1", number: 2, success: true, branch: "main_branch")
.create_verification(provider_version: "2", number: 3, success: true, branch: "dev")
end

let(:pacticipant_name_param) { "B" }

subject { Service.can_i_merge(pacticipant_name: pacticipant_name_param) }

context "for pacticipant that has verification on it's main branch" do
let(:options) {
{
latest: true,
main_branch: true,
latestby: "cvp"
}
}

let(:unresolved_selectors) {
[
PactBroker::Matrix::UnresolvedSelector.new(pacticipant_name: "B", pacticipant_version_number: "1")
]
}

it "returns true because the mergeble status is true" do
expect(subject).to be_truthy
end

it "calls the can_i_deploy method" do
expect(Service).to receive(:can_i_deploy).with(unresolved_selectors, options).and_call_original
subject
end
end
end

describe "validate_selectors" do
before do
allow(PactBroker::Deployments::EnvironmentService).to receive(:find_by_name).and_return(environment)
Expand Down

0 comments on commit 2f09ef3

Please sign in to comment.