diff --git a/app/github_app.rb b/app/github_app.rb index 2f450a0..4197eff 100755 --- a/app/github_app.rb +++ b/app/github_app.rb @@ -125,15 +125,23 @@ def sinatra_logger_level halt 200, 'OK' unless %w[rerequested].include? payload['action'].downcase - re_run = Github::Retry.new(payload, logger_level: GithubApp.sinatra_logger_level) + re_run = Github::Retry::Command.new(payload, logger_level: GithubApp.sinatra_logger_level) halt re_run.start when 'installation' logger.debug '>>> Received a new installation policy' halt 202, 'Updated' when 'issue_comment' - logger.debug '>>> Received a new issue comment' - - halt Github::ReRun::Comment.new(payload, logger_level: GithubApp.sinatra_logger_level).start + halt 404, 'Action not found' if payload.nil? or payload['comment'].nil? + + case payload.dig('comment', 'body') + when /ci:retry/ + halt Github::Retry::Comment.new(payload, logger_level: GithubApp.sinatra_logger_level).start + when /ci:rerun/ + halt Github::ReRun::Comment.new(payload, logger_level: GithubApp.sinatra_logger_level).start + else + logger.debug '>>> Just a comment' + halt 200, 'Just a comment' + end when 'check_suite' logger.debug '>>> Received a new check_suite command' halt 200, 'OK' unless payload['action'].downcase.match?('rerequested') diff --git a/lib/github/build/retry.rb b/lib/github/build/retry.rb index e08f899..f6b2efc 100644 --- a/lib/github/build/retry.rb +++ b/lib/github/build/retry.rb @@ -33,6 +33,7 @@ def enqueued_stages stage = Stage.find_by(check_suite: @check_suite, name: bamboo_stage.github_check_run_name) + next if stage.nil? next if stage.success? url = "https://ci1.netdef.org/browse/#{stage.check_suite.bamboo_ci_ref}" diff --git a/lib/github/build/summary.rb b/lib/github/build/summary.rb index 15cb9d2..cffabc1 100644 --- a/lib/github/build/summary.rb +++ b/lib/github/build/summary.rb @@ -27,7 +27,7 @@ def initialize(job, logger_level: Logger::INFO, agent: 'Github') @loggers << GithubLogger.instance.create(filename, logger_level) end - @loggers << GithubLogger.instance.create("pr#{@check_suite.pull_request.github_pr_id}.log", logger_level) + @pr_log = GithubLogger.instance.create("pr#{@check_suite.pull_request.github_pr_id}.log", logger_level) end def build_summary @@ -36,6 +36,8 @@ def build_summary check_and_update_github_ref(current_stage) logger(Logger::INFO, "build_summary: #{current_stage.inspect}") + msg = "Github::Build::Summary - #{@job.inspect}, #{current_stage.inspect}, bamboo info: #{bamboo_info}" + @pr_log.info(msg) # Update current stage update_summary(current_stage) @@ -52,6 +54,11 @@ def build_summary private + def bamboo_info + finish = Github::PlanExecution::Finished.new({ 'bamboo_ref' => @check_suite.bamboo_ci_ref }) + finish.fetch_build_status + end + def check_and_update_github_ref(current_stage) current_refs = @github.fetch_check_runs @@ -73,6 +80,8 @@ def must_update_previous_stage(current_stage) return if previous_stage.nil? or !(previous_stage.in_progress? or previous_stage.queued?) + logger(Logger::INFO, "must_update_previous_stage: #{previous_stage.inspect}") + finished_stage_summary(previous_stage) end diff --git a/lib/github/check.rb b/lib/github/check.rb index 3623de3..29081f0 100644 --- a/lib/github/check.rb +++ b/lib/github/check.rb @@ -54,6 +54,10 @@ def comment_reaction_thumb_up(repo, comment_id) @app.create_issue_comment_reaction(repo, comment_id, '+1') end + def comment_reaction_thumb_down(repo, comment_id) + @app.create_issue_comment_reaction(repo, comment_id, '-1') + end + def create(name) @app.create_check_run( @check_suite.pull_request.repository, diff --git a/lib/github/plan_execution/finished.rb b/lib/github/plan_execution/finished.rb index 2f4f0da..3b69de5 100644 --- a/lib/github/plan_execution/finished.rb +++ b/lib/github/plan_execution/finished.rb @@ -40,16 +40,8 @@ def finished [200, 'Finished'] end - private - - # This method will move all tests that no longer exist in BambooCI to the skipped state, - # because there are no executions for them. - def clear_deleted_jobs - github_check = Github::Check.new(@check_suite) - - @check_suite.ci_jobs.where(status: %w[queued in_progress]).each do |ci_job| - ci_job.skipped(github_check) - end + def fetch_build_status + get_request(URI("https://127.0.0.1/rest/api/latest/result/status/#{@check_suite.bamboo_ci_ref}")) end # Checks if CI still running @@ -63,6 +55,18 @@ def in_progress?(build_status) true end + private + + # This method will move all tests that no longer exist in BambooCI to the skipped state, + # because there are no executions for them. + def clear_deleted_jobs + github_check = Github::Check.new(@check_suite) + + @check_suite.ci_jobs.where(status: %w[queued in_progress]).each do |ci_job| + ci_job.skipped(github_check) + end + end + def ci_stopped?(build_status) build_status.key?('message') and !build_status.key?('finished') end @@ -151,10 +155,6 @@ def check_stages def fetch_ci_execution @result = get_status(@check_suite.bamboo_ci_ref) end - - def fetch_build_status - get_request(URI("https://127.0.0.1/rest/api/latest/result/status/#{@check_suite.bamboo_ci_ref}")) - end end end end diff --git a/lib/github/re_run/comment.rb b/lib/github/re_run/comment.rb index b26ebe3..630817f 100644 --- a/lib/github/re_run/comment.rb +++ b/lib/github/re_run/comment.rb @@ -164,9 +164,7 @@ def sha256 end def action? - return false if action.nil? - - action.downcase.match? 'ci:rerun' and @payload['action'] == 'created' + action.to_s.downcase.match? 'ci:rerun' and @payload['action'] == 'created' end end end diff --git a/lib/github/retry.rb b/lib/github/retry.rb deleted file mode 100644 index e75b34b..0000000 --- a/lib/github/retry.rb +++ /dev/null @@ -1,122 +0,0 @@ -# SPDX-License-Identifier: BSD-2-Clause -# -# retry.rb -# Part of NetDEF CI System -# -# Copyright (c) 2023 by -# Network Device Education Foundation, Inc. ("NetDEF") -# -# frozen_string_literal: true - -require 'logger' - -require_relative '../../database_loader' -require_relative '../bamboo_ci/retry' -require_relative '../bamboo_ci/stop_plan' -require_relative '../github/build/retry' - -require_relative 'check' -require_relative 'build/unavailable_jobs' - -module Github - class Retry - def initialize(payload, logger_level: Logger::INFO) - create_logger(logger_level) - - @payload = payload - end - - def start - return [422, 'Payload can not be blank'] if @payload.nil? or @payload.empty? - - stage = Stage.find_by_check_ref(@payload.dig('check_run', 'id')) - - logger(Logger::DEBUG, "Running stage #{stage.inspect}") - - return [404, 'Stage not found'] if stage.nil? - return [406, 'Already enqueued this execution'] if stage.queued? or stage.in_progress? - - check_suite = stage.check_suite - - return enqueued(stage) if check_suite.in_progress? - - normal_flow(check_suite) - end - - private - - def normal_flow(check_suite) - check_suite.update(retry: check_suite.retry + 1) - - create_ci_jobs(check_suite) - - BambooCi::Retry.restart(check_suite.bamboo_ci_ref) - Github::Build::UnavailableJobs.new(check_suite).update - - SlackBot.instance.execution_started_notification(check_suite) - - [200, 'Retrying failure jobs'] - end - - def create_ci_jobs(check_suite) - github_check = Github::Check.new(check_suite) - - audit_retry = - AuditRetry.create(check_suite: check_suite, - github_username: @payload.dig('sender', 'login'), - github_id: @payload.dig('sender', 'id'), - github_type: @payload.dig('sender', 'type'), - retry_type: 'partial') - - Github::UserInfo.new(@payload.dig('sender', 'id'), check_suite: check_suite, audit_retry: audit_retry) - - build_retry = Github::Build::Retry.new(check_suite, github_check, audit_retry) - - build_retry.enqueued_stages - build_retry.enqueued_failure_tests - - BambooCi::StopPlan.build(check_suite.bamboo_ci_ref) - end - - def enqueued(stage) - github_check = Github::Check.new(stage.check_suite) - previous_stage = github_check.get_check_run(stage.check_ref) - - reason = slack_notification(stage) - - output = { title: previous_stage.dig(:output, :title).to_s, summary: previous_stage.dig(:output, :summary).to_s } - - stage.enqueue(github_check) - stage.failure(github_check, output: output) - - [406, reason] - end - - def slack_notification(job) - reason = SlackBot.instance.invalid_rerun_group(job) - - logger(Logger::WARN, ">>> #{job.inspect} #{reason}") - - pull_request = job.check_suite.pull_request - - PullRequestSubscription - .where(target: [pull_request.github_pr_id, pull_request.author], notification: %w[all errors]) - .uniq(&:slack_user_id) - .each { |subscription| SlackBot.instance.invalid_rerun_dm(job, subscription) } - - reason - end - - def create_logger(logger_level) - @logger_manager = [] - @logger_manager << GithubLogger.instance.create('github_app.log', logger_level) - @logger_manager << GithubLogger.instance.create('github_retry.log', logger_level) - end - - def logger(severity, message) - @logger_manager.each do |logger_object| - logger_object.add(severity, message) - end - end - end -end diff --git a/lib/github/retry/base.rb b/lib/github/retry/base.rb new file mode 100644 index 0000000..36a570c --- /dev/null +++ b/lib/github/retry/base.rb @@ -0,0 +1,140 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# base.rb +# Part of NetDEF CI System +# +# Copyright (c) 2024 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# frozen_string_literal: true + +require 'logger' + +require_relative '../../../database_loader' + +require_relative '../../bamboo_ci/retry' +require_relative '../../bamboo_ci/stop_plan' +require_relative '../../github/build/retry' + +require_relative '../check' +require_relative '../build/unavailable_jobs' + +module Github + module Retry + class Base + def initialize(payload) + @payload = payload + end + + def start + return [422, 'Payload can not be blank'] if @payload.nil? or @payload.empty? + + logger(Logger::DEBUG, "Running stage #{@stage.inspect}") + + return [404, 'Stage not found'] if @stage.nil? + return [406, 'Already enqueued this execution'] if @stage.queued? or @stage.in_progress? + + @check_suite = @stage.check_suite + + return enqueued if @check_suite.in_progress? + + normal_flow + end + + private + + def github_reaction_feedback(comment_id) + return if comment_id.nil? + + github_check = Github::Check.new(@stage.check_suite) + github_check.comment_reaction_thumb_up(@stage.check_suite.pull_request.repository, comment_id) + end + + def github_reaction_feedback_down(comment_id) + return if comment_id.nil? or @check_suite.nil? + + github_check = Github::Check.new(@check_suite) + github_check.comment_reaction_thumb_down(@check_suite.pull_request.repository, comment_id) + end + + def normal_flow + @check_suite.update(retry: @check_suite.retry + 1) + + create_ci_jobs(@check_suite) + + BambooCi::Retry.restart(@check_suite.bamboo_ci_ref) + Github::Build::UnavailableJobs.new(@check_suite).update + + SlackBot.instance.execution_started_notification(@check_suite) + + github_reaction_feedback(@comment_id) if @comment_id + + [200, 'Retrying failure jobs'] + end + + def create_ci_jobs(check_suite) + github_check = Github::Check.new(check_suite) + + audit_retry = + AuditRetry.create(check_suite: check_suite, + github_username: @payload.dig('sender', 'login'), + github_id: @payload.dig('sender', 'id'), + github_type: @payload.dig('sender', 'type'), + retry_type: 'partial') + + Github::UserInfo.new(@payload.dig('sender', 'id'), check_suite: check_suite, audit_retry: audit_retry) + + build_retry = Github::Build::Retry.new(check_suite, github_check, audit_retry) + + build_retry.enqueued_stages + build_retry.enqueued_failure_tests + + BambooCi::StopPlan.build(check_suite.bamboo_ci_ref) + end + + def enqueued + github_check = Github::Check.new(@stage.check_suite) + previous_stage = github_check.get_check_run(@stage.check_ref) + + reason = slack_notification + + output = { + title: previous_stage.dig(:output, :title).to_s, + summary: previous_stage.dig(:output, :summary).to_s + } + + @stage.enqueue(github_check) + @stage.failure(github_check, output: output) + + [406, reason] + end + + def slack_notification + reason = SlackBot.instance.invalid_rerun_group(@stage) + + logger(Logger::WARN, ">>> #{@stage.inspect} #{reason}") + + pull_request = @stage.check_suite.pull_request + + PullRequestSubscription + .where(target: [pull_request.github_pr_id, pull_request.author], notification: %w[all errors]) + .uniq(&:slack_user_id) + .each { |subscription| SlackBot.instance.invalid_rerun_dm(@stage, subscription) } + + reason + end + + def create_logger(logger_level) + @logger_manager = [] + @logger_manager << GithubLogger.instance.create('github_app.log', logger_level) + @logger_manager << GithubLogger.instance.create('github_retry.log', logger_level) + end + + def logger(severity, message) + @logger_manager.each do |logger_object| + logger_object.add(severity, message) + end + end + end + end +end diff --git a/lib/github/retry/command.rb b/lib/github/retry/command.rb new file mode 100644 index 0000000..79645c3 --- /dev/null +++ b/lib/github/retry/command.rb @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# command.rb +# Part of NetDEF CI System +# +# Copyright (c) 2024 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# frozen_string_literal: true + +require_relative 'base' + +module Github + module Retry + class Command < Base + def initialize(payload, logger_level: Logger::INFO) + super(payload) + + create_logger(logger_level) + + @payload = payload + + @stage = Stage.find_by_check_ref(@payload.dig('check_run', 'id')) + end + end + end +end diff --git a/lib/github/retry/comment.rb b/lib/github/retry/comment.rb new file mode 100644 index 0000000..f044743 --- /dev/null +++ b/lib/github/retry/comment.rb @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# comment.rb +# Part of NetDEF CI System +# +# Copyright (c) 2024 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# frozen_string_literal: true + +require_relative 'base' + +module Github + module Retry + class Comment < Base + def initialize(payload, logger_level: Logger::INFO) + super(payload) + + create_logger(logger_level) + + @payload = payload + + fetch_stage + @comment_id = comment_id + end + + def start + output = super + + logger(Logger::INFO, "Github::Retry::Comment - response #{output.inspect}") + if [404, 406, 422].include?(output.first.to_i) + github_reaction_feedback_down(@comment_id) + + return output + end + + github_reaction_feedback(@comment_id) + + output + end + + private + + def fetch_stage + pull_request = PullRequest.find_by(github_pr_id: pr_id) + return unless pull_request + + @check_suite = pull_request.check_suites.last + @stage = @check_suite.stages_failure.min_by(&:id) + end + + def pr_id + @payload.dig('issue', 'number') + end + + def comment_id + @payload.dig('comment', 'id') + end + end + end +end diff --git a/lib/github/update_status.rb b/lib/github/update_status.rb index 3c52549..f597b15 100644 --- a/lib/github/update_status.rb +++ b/lib/github/update_status.rb @@ -79,10 +79,7 @@ def update_status return [200, 'Success'] unless @job.check_suite.pull_request.current_execution? @job.check_suite - summary = Github::Build::Summary.new(@job) - summary.build_summary - - finished_execution? + update_build_summary_or_finished [200, 'Success'] rescue StandardError => e @@ -91,14 +88,16 @@ def update_status [500, 'Internal Server Error'] end - def finished_execution? - return false unless current_execution? - return false unless @check_suite.finished? + def update_build_summary_or_finished + summary = Github::Build::Summary.new(@job) + summary.build_summary + + return unless @job.finished? - logger Logger::INFO, ">>> @check_suite#{@check_suite.inspect} -> finished? #{@check_suite.finished?}" - logger Logger::INFO, @check_suite.ci_jobs.last.inspect + logger(Logger::INFO, "Github::PlanExecution::Finished: '#{@job.check_suite.bamboo_ci_ref}'") - SlackBot.instance.execution_finished_notification(@check_suite) + finished = Github::PlanExecution::Finished.new({ 'bamboo_ref' => @job.check_suite.bamboo_ci_ref }) + finished.finished end def current_execution? diff --git a/lib/github_ci_app.rb b/lib/github_ci_app.rb index 80ec374..8579b38 100644 --- a/lib/github_ci_app.rb +++ b/lib/github_ci_app.rb @@ -23,7 +23,8 @@ require_relative 'github/re_run/comment' require_relative 'github/build_plan' require_relative 'github/check' -require_relative 'github/retry' +require_relative 'github/retry/command' +require_relative 'github/retry/comment' require_relative 'github/update_status' require_relative 'github/plan_execution/finished' require_relative 'github/user_info' diff --git a/lib/models/check_suite.rb b/lib/models/check_suite.rb index 8086976..71688ea 100644 --- a/lib/models/check_suite.rb +++ b/lib/models/check_suite.rb @@ -25,6 +25,10 @@ class CheckSuite < ActiveRecord::Base default_scope -> { order(id: :asc) }, all_queries: true + def stages_failure + stages.joins(:jobs).where(jobs: { status: %w[cancelled failure] }).all.uniq + end + def finished? !running? end diff --git a/spec/app/github_app_spec.rb b/spec/app/github_app_spec.rb index 365aed9..9f45632 100644 --- a/spec/app/github_app_spec.rb +++ b/spec/app/github_app_spec.rb @@ -195,7 +195,7 @@ end end - context 'when receive HTTP_X_GITHUB_EVENT issue_comment' do + context 'when receive HTTP_X_GITHUB_EVENT issue_comment - rerun' do let(:headers) do { 'HTTP_ACCEPT' => 'application/json', @@ -204,7 +204,73 @@ } end - let(:payload) { { x: 1, 'action' => 'rerequested' } } + let(:payload) { { x: 1, 'action' => 'rerequested', 'comment' => { 'body' => 'ci:rerun' } } } + + before do + allow(GitHubApp::Configuration.instance).to receive(:debug?).and_return(false) + end + + it 'must returns error' do + post '/', payload.to_json, headers + + expect(last_response.status).to eq 404 + end + end + + context 'when receive HTTP_X_GITHUB_EVENT issue_comment - retry' do + let(:headers) do + { + 'HTTP_ACCEPT' => 'application/json', + 'HTTP_SIGNATURE' => header, + 'HTTP_X_GITHUB_EVENT' => 'issue_comment' + } + end + + let(:payload) { { x: 1, 'action' => 'rerequested', 'comment' => { 'body' => 'ci:retry' } } } + + before do + allow(GitHubApp::Configuration.instance).to receive(:debug?).and_return(false) + end + + it 'must returns error' do + post '/', payload.to_json, headers + + expect(last_response.status).to eq 404 + end + end + + context 'when receive HTTP_X_GITHUB_EVENT issue_comment - potato' do + let(:headers) do + { + 'HTTP_ACCEPT' => 'application/json', + 'HTTP_SIGNATURE' => header, + 'HTTP_X_GITHUB_EVENT' => 'issue_comment' + } + end + + let(:payload) { { x: 1, 'action' => 'rerequested', 'comment' => { 'body' => 'just a potato' } } } + + before do + allow(GitHubApp::Configuration.instance).to receive(:debug?).and_return(false) + end + + it 'must returns success' do + post '/', payload.to_json, headers + + expect(last_response.status).to eq 200 + end + end + + context 'when receive HTTP_X_GITHUB_EVENT issue_comment - comment null' do + let(:headers) do + { + 'HTTP_ACCEPT' => 'application/json', + 'HTTP_SIGNATURE' => header, + 'HTTP_X_GITHUB_EVENT' => 'issue_comment' + } + end + + let(:payload) { { x: 1, 'action' => 'rerequested', 'comment' => nil } } before do allow(GitHubApp::Configuration.instance).to receive(:debug?).and_return(false) @@ -226,7 +292,7 @@ } end - let(:payload) { { x: 1, 'action' => 'rerequested' } } + let(:payload) { { x: 1, 'action' => 'rerequested', 'comment' => nil } } before do allow(GitHubApp::Configuration.instance).to receive(:debug?).and_return(false) diff --git a/spec/lib/github/build/summary_spec.rb b/spec/lib/github/build/summary_spec.rb index ab95dc6..33ffa09 100644 --- a/spec/lib/github/build/summary_spec.rb +++ b/spec/lib/github/build/summary_spec.rb @@ -12,6 +12,7 @@ let(:summary) { described_class.new(ci_job) } let(:fake_client) { Octokit::Client.new } let(:fake_github_check) { Github::Check.new(nil) } + let(:fake_finish_plan) { Github::PlanExecution::Finished.new({ 'bamboo_ref' => 'UBUNTU-1' }) } let(:pull_request) { create(:pull_request) } let(:check_suite) { create(:check_suite, pull_request: pull_request) } let(:position1) { BambooStageTranslation.find_by_position(1) } @@ -24,6 +25,9 @@ allow(fake_client).to receive(:find_app_installations).and_return([{ 'id' => 1 }]) allow(fake_client).to receive(:create_app_installation_access_token).and_return({ 'token' => 1 }) + allow(Github::PlanExecution::Finished).to receive(:new).and_return(fake_finish_plan) + allow(fake_finish_plan).to receive(:fetch_build_status) + allow(File).to receive(:read).and_return('') allow(OpenSSL::PKey::RSA).to receive(:new).and_return(OpenSSL::PKey::RSA.new(2048)) diff --git a/spec/lib/github/retry_spec.rb b/spec/lib/github/retry/command_spec.rb similarity index 99% rename from spec/lib/github/retry_spec.rb rename to spec/lib/github/retry/command_spec.rb index 9b5f7f3..a5a844f 100644 --- a/spec/lib/github/retry_spec.rb +++ b/spec/lib/github/retry/command_spec.rb @@ -8,7 +8,7 @@ # # frozen_string_literal: true -describe Github::Retry do +describe Github::Retry::Command do let(:github_retry) { described_class.new(payload) } let(:fake_unavailable) { Github::Build::UnavailableJobs.new(nil) } diff --git a/spec/lib/github/retry/comment_spec.rb b/spec/lib/github/retry/comment_spec.rb new file mode 100644 index 0000000..2347551 --- /dev/null +++ b/spec/lib/github/retry/comment_spec.rb @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# comment_spec.rb +# Part of NetDEF CI System +# +# Copyright (c) 2024 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# frozen_string_literal: true + +describe Github::Retry::Comment do + let(:github_retry) { described_class.new(payload) } + let(:fake_client) { Octokit::Client.new } + let(:fake_github_check) { Github::Check.new(nil) } + let(:fake_plan_run) { BambooCi::PlanRun.new(nil) } + let(:fake_unavailable) { Github::Build::UnavailableJobs.new(nil) } + + before do + allow(File).to receive(:read).and_return('') + allow(OpenSSL::PKey::RSA).to receive(:new).and_return(OpenSSL::PKey::RSA.new(2048)) + allow(Github::Build::UnavailableJobs).to receive(:new).and_return(fake_unavailable) + end + + describe 'Valid payload' do + let(:fake_client) { Octokit::Client.new } + let(:fake_github_check) { Github::Check.new(nil) } + let(:fake_translation) { create(:stage_configuration) } + + before do + allow(Octokit::Client).to receive(:new).and_return(fake_client) + allow(fake_client).to receive(:find_app_installations).and_return([{ 'id' => 1 }]) + allow(fake_client).to receive(:create_app_installation_access_token).and_return({ 'token' => 1 }) + + allow(Github::Check).to receive(:new).and_return(fake_github_check) + allow(fake_github_check).to receive(:create).and_return(check_suite) + allow(fake_github_check).to receive(:add_comment) + allow(fake_github_check).to receive(:cancelled) + allow(fake_github_check).to receive(:in_progress) + allow(fake_github_check).to receive(:queued) + allow(fake_github_check).to receive(:comment_reaction_thumb_up) + allow(fake_github_check).to receive(:comment_reaction_thumb_down) + allow(fake_github_check).to receive(:fetch_username).and_return({}) + + allow(BambooCi::PlanRun).to receive(:new).and_return(fake_plan_run) + allow(fake_plan_run).to receive(:start_plan).and_return(200) + allow(fake_plan_run).to receive(:bamboo_reference).and_return('UNIT-TEST-1') + allow(fake_plan_run).to receive(:bamboo_reference).and_return('CHK-01') + + allow(BambooCi::Retry).to receive(:restart) + + allow(BambooCi::StopPlan).to receive(:build) + allow(BambooCi::RunningPlan).to receive(:fetch).with(fake_plan_run.bamboo_reference).and_return(ci_jobs) + end + + context 'when receives a valid command' do + let(:check_suite) { create(:check_suite, :with_running_ci_jobs) } + let(:ci_jobs) do + [ + { name: 'First Test', job_ref: 'UNIT-TEST-FIRST-1', stage: fake_translation.bamboo_stage_name }, + { name: 'Checkout', job_ref: 'CHK-01', stage: fake_translation.bamboo_stage_name } + ] + end + let(:payload) do + { + 'action' => 'created', + 'comment' => { 'body' => "ci:retry ##{check_suite.commit_sha_ref}", 'id' => 1 }, + 'repository' => { 'full_name' => check_suite.pull_request.repository }, + 'issue' => { 'number' => check_suite.pull_request.github_pr_id } + } + end + + before do + check_suite.stages.last.update(status: :failure) + check_suite.stages.last.jobs.each { |job| job.update(status: :failure) } + end + + it 'must returns success' do + expect(github_retry.start).to eq([200, 'Retrying failure jobs']) + expect(check_suite.stages.last.reload.status).to eq('queued') + end + end + + context 'when receives a valid command - but still running' do + let(:check_suite) { create(:check_suite, :with_running_ci_jobs) } + let(:ci_jobs) do + [ + { name: 'First Test', job_ref: 'UNIT-TEST-FIRST-1', stage: fake_translation.bamboo_stage_name }, + { name: 'Checkout', job_ref: 'CHK-01', stage: fake_translation.bamboo_stage_name } + ] + end + let(:payload) do + { + 'action' => 'created', + 'comment' => { 'body' => "ci:retry ##{check_suite.commit_sha_ref}", 'id' => 1 }, + 'repository' => { 'full_name' => check_suite.pull_request.repository }, + 'issue' => { 'number' => check_suite.pull_request.github_pr_id } + } + end + + it 'must returns stage not found' do + expect(github_retry.start).to eq([404, 'Stage not found']) + expect(check_suite.stages.last.reload.status).to eq('in_progress') + end + end + end + + describe 'Invalid payload' do + let(:fake_client) { Octokit::Client.new } + let(:fake_github_check) { Github::Check.new(nil) } + let(:fake_translation) { create(:stage_configuration) } + + before do + allow(Octokit::Client).to receive(:new).and_return(fake_client) + allow(fake_client).to receive(:find_app_installations).and_return([{ 'id' => 1 }]) + allow(fake_client).to receive(:create_app_installation_access_token).and_return({ 'token' => 1 }) + end + + context 'when receives an invalid PR' do + let(:payload) do + { + 'action' => 'created', + 'comment' => { 'body' => 'ci:retry', 'id' => 1 }, + 'repository' => { 'full_name' => 'test' }, + 'issue' => { 'number' => 0 } + } + end + + it 'must returns stage not found' do + expect(github_retry.start).to eq([404, 'Stage not found']) + end + end + end +end diff --git a/spec/lib/github/update_status_spec.rb b/spec/lib/github/update_status_spec.rb index 551558c..51e378f 100644 --- a/spec/lib/github/update_status_spec.rb +++ b/spec/lib/github/update_status_spec.rb @@ -11,6 +11,12 @@ describe Github::UpdateStatus do let(:update_status) { described_class.new(payload) } let(:fake_unavailable) { Github::Build::UnavailableJobs.new(nil) } + let(:fake_finish_plan) { Github::PlanExecution::Finished.new({ 'bamboo_ref' => 'UBUNTU-1' }) } + + before do + allow(Github::PlanExecution::Finished).to receive(:new).and_return(fake_finish_plan) + allow(fake_finish_plan).to receive(:fetch_build_status) + end describe 'Validates different Ci Job status' do let(:payload) do