Skip to content

Commit 6c4e314

Browse files
Merge pull request #112 from RodrigoMNardi/feature/github/output/checkout_code_errors
Checkout Code error in GitHub Checks
2 parents 89897c1 + eae13eb commit 6c4e314

File tree

11 files changed

+170
-10
lines changed

11 files changed

+170
-10
lines changed

.github/workflows/ruby.yml

+15-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030

3131
- uses: ruby/setup-ruby@v1
3232
with:
33-
ruby-version: 3.3.1
33+
ruby-version: 3.1.2
3434

3535
- name: rubocop
3636
uses: reviewdog/[email protected]
@@ -63,11 +63,11 @@ jobs:
6363
with:
6464
fetch-depth: 0
6565

66-
- name: Set up Ruby 3.3.1
66+
- name: Set up Ruby 3.1.2
6767
id: ruby
6868
uses: ruby/setup-ruby@v1
6969
with:
70-
ruby-version: 3.3.1
70+
ruby-version: 3.1.2
7171
bundler-cache: true
7272

7373
- name: Install required package
@@ -93,12 +93,22 @@ jobs:
9393
- name: Run specs
9494
run: |
9595
cp config_template.yml config.yml
96-
bundle exec rspec -f j -o tmp/rspec_results.json -f p
96+
bundle exec rspec \
97+
-f j -o tmp/rspec_results.json \
98+
-f h -o tmp/rspec_results.html
99+
100+
- name: Save coverage report
101+
uses: actions/upload-artifact@v4
102+
if: always()
103+
with:
104+
name: coverage-report
105+
path: /home/runner/work/netdef-ci-github-app/netdef-ci-github-app/coverage
106+
if-no-files-found: warn
97107

98108
- name: RSpec Report
99109
uses: SonicGarden/rspec-report-action@v2
100110
with:
101111
token: ${{ secrets.GITHUB_TOKEN }}
102112
title: 'Unit tests'
103113
json-path: tmp/rspec_results.json
104-
if: always()
114+
if: always()

config_template.yml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ auth_signature:
1010
github_apps:
1111
- login:
1212
cert:
13+
repo:
1314

1415
redis:
1516
host:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
#
3+
# 20240617121935_create_delayed_jobs.rb
4+
# Part of NetDEF CI System
5+
#
6+
# Copyright (c) 2024 by
7+
# Network Device Education Foundation, Inc. ("NetDEF")
8+
#
9+
# frozen_string_literal: true
10+
11+
class AddCiJobSummary < ActiveRecord::Migration[6.0]
12+
def change
13+
add_column :ci_jobs, :summary, :string
14+
end
15+
end

db/schema.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema[7.2].define(version: 2024_10_14_134659) do
13+
ActiveRecord::Schema[7.2].define(version: 2025_04_16_153222) do
1414
# These are extensions that must be enabled in order to support this database
1515
enable_extension "plpgsql"
1616

@@ -78,6 +78,7 @@
7878
t.integer "parent_stage_id"
7979
t.bigint "stage_id"
8080
t.integer "execution_time"
81+
t.string "summary"
8182
t.index ["check_suite_id"], name: "index_ci_jobs_on_check_suite_id"
8283
t.index ["stage_id"], name: "index_ci_jobs_on_stage_id"
8384
end

lib/github/build/summary.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,9 @@ def other_message(name, jobs)
223223
end
224224

225225
def generate_message(name, job)
226-
failures = name.downcase.match?('build') ? build_message(job) : tests_message(job)
226+
failures = tests_message(job)
227+
failures = build_message(job) if name.downcase.match?('build')
228+
failures = checkout_message(job) if name.downcase.match?('source')
227229

228230
"- #{job.name} -> https://ci1.netdef.org/browse/#{job.job_ref}\n#{failures}"
229231
end
@@ -236,6 +238,14 @@ def tests_message(job)
236238
"\t :no_entry_sign: #{failure.test_suite} #{failure.test_case}\n ```\n#{failure.message}\n```\n"
237239
end
238240

241+
def checkout_message(job)
242+
failures = job.summary
243+
244+
return '' if failures.nil?
245+
246+
"```\n#{failures}\n```\n"
247+
end
248+
239249
def build_message(job)
240250
output = BambooCi::Result.fetch(job.job_ref, expand: 'testResults.failedTests.testResult.errors,artifacts')
241251
entry = output.dig('artifacts', 'artifact')&.find { |elem| elem['name'] == 'ErrorLog' }

lib/github/check.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ def authenticate_app
279279
def github_app_by_repo
280280
app =
281281
@config['github_apps'].find do |entry|
282-
entry.key? 'repo' and entry['repo'] == @check_suite.pull_request.repository
282+
entry.key? 'repo' and entry['repo'] == @check_suite&.pull_request&.repository
283283
end
284284

285285
@logger.info("github_app_by_repo: #{app.inspect}")

lib/github/update_status.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ def initialize(payload)
3939
@check_suite = @job&.check_suite
4040
@failures = payload['failures'] || []
4141

42+
@summary = ''
43+
@summary = payload['output']['summary'] if payload.key? 'output'
44+
4245
logger_initializer
4346
end
4447

@@ -94,6 +97,8 @@ def update_status
9497
else
9598
failure
9699
@job.update_execution_time
100+
@job.summary = @summary
101+
@job.save
97102
end
98103

99104
return [200, 'Success'] unless @job.check_suite.pull_request.current_execution? @job.check_suite
@@ -143,8 +148,9 @@ def fetch_delayed_job(queue)
143148
# The unable2find string must match the phrase defined in the ci-files repository file
144149
# github_checks/hook_api.py method __topotest_title_summary
145150
def failure
146-
@job.failure(@github_check)
151+
return if @job.nil?
147152

153+
@job.failure(@github_check)
148154
return failures_stats if @failures.is_a? Array and !@failures.empty?
149155

150156
CiJobFetchTopotestFailures

lib/models/ci_job.rb

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class CiJob < ActiveRecord::Base
3232

3333
default_scope -> { order(id: :asc) }, all_queries: true
3434

35+
def checkout_code?
36+
name.match(/checkout/i)
37+
end
38+
3539
def finished?
3640
!%w[queued in_progress].include?(status)
3741
end

spec/factories/ci_job.rb

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
status { 0 }
1515
job_ref { Faker::Alphanumeric.alphanumeric(number: 18, min_alpha: 3, min_numeric: 3) }
1616
check_ref { Faker::Alphanumeric.alphanumeric(number: 18, min_alpha: 3, min_numeric: 3) }
17+
summary { Faker::Lorem.sentence }
1718

1819
check_suite
1920
stage { create(:stage, status: status, check_suite: check_suite) }

spec/lib/github/build/action_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
end
168168

169169
it 'must not change' do
170-
expect(check_suite_new.reload.stages.first.status).to eq('queued')
170+
expect(check_suite_new.reload.stages.order(id: :asc).first.status).to eq('queued')
171171
end
172172
end
173173

spec/lib/github/build/summary_spec.rb

+112
Original file line numberDiff line numberDiff line change
@@ -312,4 +312,116 @@
312312
expect(ci_job.reload.stage).not_to be_nil
313313
end
314314
end
315+
316+
context 'when the current stage is cancelled' do
317+
let(:stage) { create(:stage, :cancelled, check_suite: check_suite) }
318+
let(:ci_job) { create(:ci_job, stage: stage, check_suite: check_suite) }
319+
320+
before do
321+
ci_job
322+
end
323+
324+
it 'does not update the stage' do
325+
expect { summary.build_summary }.not_to(change { stage.reload.status })
326+
end
327+
end
328+
329+
context 'when stage does not exists' do
330+
let(:ci_job) { create(:ci_job, stage: nil, check_suite: check_suite) }
331+
let(:stage_configuration) { create(:stage_configuration, bamboo_stage_name: 'D', position: 1) }
332+
333+
let(:current_stage) do
334+
create(:stage, name: 'B', check_suite: check_suite, configuration: create(:stage_configuration, position: 1))
335+
end
336+
337+
let(:job_info) do
338+
[
339+
{
340+
name: ci_job.name,
341+
stage: 'D'
342+
}
343+
]
344+
end
345+
346+
before do
347+
allow(BambooCi::RunningPlan).to receive(:fetch).and_return(job_info)
348+
ci_job
349+
stage_configuration
350+
end
351+
352+
it 'must create a new stage' do
353+
summary.build_summary
354+
expect(Stage.all.pluck(:name)).to include('D')
355+
end
356+
end
357+
358+
context 'when the current stage is not mandatory and fails' do
359+
let(:stage1) { create(:stage, :build, check_suite: check_suite) }
360+
let(:stage2) { create(:stage, check_suite: check_suite) }
361+
let(:ci_job) { create(:ci_job, :failure, stage: stage1, check_suite: check_suite) }
362+
let(:ci_job2) { create(:ci_job, stage: stage2, check_suite: check_suite) }
363+
364+
before do
365+
stage1.configuration.update(mandatory: false, position: 1)
366+
stage2.configuration.update(position: 2)
367+
368+
ci_job
369+
ci_job2
370+
summary.build_summary
371+
end
372+
373+
it 'does not cancel the next stage' do
374+
expect(stage1.reload.status).to eq('failure')
375+
expect(stage2.reload.status).to eq('queued')
376+
end
377+
end
378+
379+
context 'when the current stage is mandatory and succeeds' do
380+
let(:stage1) { create(:stage, :build, check_suite: check_suite) }
381+
let(:stage2) { create(:stage, check_suite: check_suite) }
382+
let(:ci_job) { create(:ci_job, :success, stage: stage1, check_suite: check_suite) }
383+
let(:ci_job2) { create(:ci_job, stage: stage2, check_suite: check_suite) }
384+
385+
before do
386+
stage1.configuration.update(mandatory: true, position: 1)
387+
stage2.configuration.update(position: 2)
388+
389+
ci_job
390+
ci_job2
391+
summary.build_summary
392+
end
393+
394+
it 'marks the next stage as in_progress' do
395+
expect(stage1.reload.status).to eq('success')
396+
expect(stage2.reload.status).to eq('in_progress')
397+
end
398+
end
399+
400+
context 'when has a checkout message' do
401+
let(:stage) { create(:stage, :build, check_suite: check_suite) }
402+
let(:ci_job) do
403+
create(:ci_job, :success, stage: stage, check_suite: check_suite, name: 'Sourcecode', summary: 'HI')
404+
end
405+
let(:message) do
406+
"Sourcecode -> https://ci1.netdef.org/browse/#{ci_job.job_ref}\n```\nHI\n```"
407+
end
408+
409+
it 'must update stage' do
410+
expect(summary.send(:generate_message, 'source', ci_job)).to include(message)
411+
end
412+
end
413+
414+
context 'when has not a checkout message' do
415+
let(:stage) { create(:stage, :build, check_suite: check_suite) }
416+
let(:ci_job) do
417+
create(:ci_job, :success, stage: stage, check_suite: check_suite, name: 'Sourcecode')
418+
end
419+
let(:message) do
420+
"Sourcecode -> https://ci1.netdef.org/browse/#{ci_job.job_ref}\n```\nHI\n```"
421+
end
422+
423+
it 'must update stage' do
424+
expect(summary.send(:generate_message, 'source', ci_job)).not_to include(message)
425+
end
426+
end
315427
end

0 commit comments

Comments
 (0)