Commit aecaaa67 authored by Phil Hughes's avatar Phil Hughes

Merge branch '30696-long-build-log-improvement' into 'master'

Improves support for long build traces:

Closes #30696

See merge request !10660
parents e30fe47e b67bb566
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
consistent-return, prefer-rest-params */ consistent-return, prefer-rest-params */
/* global Breakpoints */ /* global Breakpoints */
import { bytesToKiB } from './lib/utils/number_utils';
const bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; }; const bind = function (fn, me) { return function () { return fn.apply(me, arguments); }; };
const AUTO_SCROLL_OFFSET = 75; const AUTO_SCROLL_OFFSET = 75;
const DOWN_BUILD_TRACE = '#down-build-trace'; const DOWN_BUILD_TRACE = '#down-build-trace';
...@@ -20,6 +22,7 @@ window.Build = (function () { ...@@ -20,6 +22,7 @@ window.Build = (function () {
this.state = this.options.logState; this.state = this.options.logState;
this.buildStage = this.options.buildStage; this.buildStage = this.options.buildStage;
this.$document = $(document); this.$document = $(document);
this.logBytes = 0;
this.updateDropdown = bind(this.updateDropdown, this); this.updateDropdown = bind(this.updateDropdown, this);
...@@ -98,16 +101,23 @@ window.Build = (function () { ...@@ -98,16 +101,23 @@ window.Build = (function () {
if (log.append) { if (log.append) {
$buildContainer.append(log.html); $buildContainer.append(log.html);
this.logBytes += log.size;
} else { } else {
$buildContainer.html(log.html); $buildContainer.html(log.html);
if (log.truncated) { this.logBytes = log.size;
$('.js-truncated-info-size').html(` ${log.size} `); }
// if the incremental sum of logBytes we received is less than the total
// we need to show a message warning the user about that.
if (this.logBytes < log.total) {
// size is in bytes, we need to calculate KiB
const size = bytesToKiB(this.logBytes);
$('.js-truncated-info-size').html(`${size}`);
this.$truncatedInfo.removeClass('hidden'); this.$truncatedInfo.removeClass('hidden');
this.initAffixTruncatedInfo(); this.initAffixTruncatedInfo();
} else { } else {
this.$truncatedInfo.addClass('hidden'); this.$truncatedInfo.addClass('hidden');
} }
}
this.checkAutoscroll(); this.checkAutoscroll();
......
/* eslint-disable import/prefer-default-export */
export const BYTES_IN_KIB = 1024;
/* eslint-disable import/prefer-default-export */ import { BYTES_IN_KIB } from './constants';
/** /**
* Function that allows a number with an X amount of decimals * Function that allows a number with an X amount of decimals
...@@ -32,3 +32,13 @@ export function formatRelevantDigits(number) { ...@@ -32,3 +32,13 @@ export function formatRelevantDigits(number) {
} }
return formattedNumber; return formattedNumber;
} }
/**
* Utility function that calculates KiB of the given bytes.
*
* @param {Number} number bytes
* @return {Number} KiB
*/
export function bytesToKiB(number) {
return number / BYTES_IN_KIB;
}
...@@ -61,8 +61,9 @@ ...@@ -61,8 +61,9 @@
.truncated-info { .truncated-info {
text-align: center; text-align: center;
border-bottom: 1px solid; border-bottom: 1px solid;
background-color: $black-transparent; background-color: $black;
height: 45px; height: 45px;
padding: 15px;
&.affix { &.affix {
top: 0; top: 0;
...@@ -87,6 +88,16 @@ ...@@ -87,6 +88,16 @@
right: 5px; right: 5px;
left: 5px; left: 5px;
} }
.truncated-info-size {
margin: 0 5px;
}
.raw-link {
color: inherit;
margin-left: 5px;
text-decoration: underline;
}
} }
} }
......
...@@ -71,11 +71,11 @@ ...@@ -71,11 +71,11 @@
= custom_icon('scroll_down_hover_active') = custom_icon('scroll_down_hover_active')
#up-build-trace #up-build-trace
%pre.build-trace#build-trace %pre.build-trace#build-trace
.js-truncated-info.truncated-info.hidden .js-truncated-info.truncated-info.hidden<
%span<
Showing last Showing last
%span.js-truncated-info-size>< %span.js-truncated-info-size.truncated-info-size><
KiB of log KiB of log -
%a.js-raw-link.raw-link{ :href => raw_namespace_project_build_path(@project.namespace, @project, @build) }>< Complete Raw
%code.bash.js-build-output %code.bash.js-build-output
.build-loader-animation.js-build-refresh .build-loader-animation.js-build-refresh
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
# This was inspired from: http://stackoverflow.com/a/10219411/1520132 # This was inspired from: http://stackoverflow.com/a/10219411/1520132
class Stream class Stream
BUFFER_SIZE = 4096 BUFFER_SIZE = 4096
LIMIT_SIZE = 50.kilobytes LIMIT_SIZE = 500.kilobytes
attr_reader :stream attr_reader :stream
......
/* eslint-disable no-new */ /* eslint-disable no-new */
/* global Build */ /* global Build */
import { bytesToKiB } from '~/lib/utils/number_utils';
require('~/lib/utils/datetime_utility'); import '~/lib/utils/datetime_utility';
require('~/lib/utils/url_utility'); import '~/lib/utils/url_utility';
require('~/build'); import '~/build';
require('~/breakpoints'); import '~/breakpoints';
require('vendor/jquery.nicescroll'); import 'vendor/jquery.nicescroll';
describe('Build', () => { describe('Build', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1`; const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/builds/1`;
...@@ -144,6 +144,23 @@ describe('Build', () => { ...@@ -144,6 +144,23 @@ describe('Build', () => {
expect($('#build-trace .js-build-output').text()).toMatch(/Different/); expect($('#build-trace .js-build-output').text()).toMatch(/Different/);
}); });
it('reloads the page when the build is done', () => {
spyOn(gl.utils, 'visitUrl');
jasmine.clock().tick(4001);
const [{ success }] = $.ajax.calls.argsFor(0);
success.call($, {
html: '<span>Final</span>',
status: 'passed',
append: true,
complete: true,
});
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BUILD_URL);
});
describe('truncated information', () => {
describe('when size is less than total', () => {
it('shows information about truncated log', () => { it('shows information about truncated log', () => {
jasmine.clock().tick(4001); jasmine.clock().tick(4001);
const [{ success }] = $.ajax.calls.argsFor(0); const [{ success }] = $.ajax.calls.argsFor(0);
...@@ -152,29 +169,95 @@ describe('Build', () => { ...@@ -152,29 +169,95 @@ describe('Build', () => {
html: '<span>Update</span>', html: '<span>Update</span>',
status: 'success', status: 'success',
append: false, append: false,
truncated: true, size: 50,
size: '50', total: 100,
});
expect(document.querySelector('.js-truncated-info').classList).not.toContain('hidden');
});
it('shows the size in KiB', () => {
jasmine.clock().tick(4001);
const [{ success }] = $.ajax.calls.argsFor(0);
const size = 50;
success.call($, {
html: '<span>Update</span>',
status: 'success',
append: false,
size,
total: 100,
}); });
expect( expect(
$('#build-trace .js-truncated-info').text().trim(), document.querySelector('.js-truncated-info-size').textContent.trim(),
).toContain('Showing last 50 KiB of log'); ).toEqual(`${bytesToKiB(size)}`);
expect($('#build-trace .js-truncated-info-size').text()).toMatch('50');
}); });
it('reloads the page when the build is done', () => { it('shows incremented size', () => {
spyOn(gl.utils, 'visitUrl'); jasmine.clock().tick(4001);
let args = $.ajax.calls.argsFor(0)[0];
args.success.call($, {
html: '<span>Update</span>',
status: 'success',
append: false,
size: 50,
total: 100,
});
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
).toEqual(`${bytesToKiB(50)}`);
jasmine.clock().tick(4001);
args = $.ajax.calls.argsFor(2)[0];
args.success.call($, {
html: '<span>Update</span>',
status: 'success',
append: true,
size: 10,
total: 100,
});
expect(
document.querySelector('.js-truncated-info-size').textContent.trim(),
).toEqual(`${bytesToKiB(60)}`);
});
it('renders the raw link', () => {
jasmine.clock().tick(4001); jasmine.clock().tick(4001);
const [{ success }] = $.ajax.calls.argsFor(0); const [{ success }] = $.ajax.calls.argsFor(0);
success.call($, { success.call($, {
html: '<span>Final</span>', html: '<span>Update</span>',
status: 'passed', status: 'success',
append: true, append: false,
complete: true, size: 50,
total: 100,
}); });
expect(gl.utils.visitUrl).toHaveBeenCalledWith(BUILD_URL); expect(
document.querySelector('.js-raw-link').textContent.trim(),
).toContain('Complete Raw');
});
});
describe('when size is equal than total', () => {
it('does not show the trunctated information', () => {
jasmine.clock().tick(4001);
const [{ success }] = $.ajax.calls.argsFor(0);
success.call($, {
html: '<span>Update</span>',
status: 'success',
append: false,
size: 100,
total: 100,
});
expect(document.querySelector('.js-truncated-info').classList).toContain('hidden');
});
});
}); });
}); });
}); });
......
import { formatRelevantDigits } from '~/lib/utils/number_utils'; import { formatRelevantDigits, bytesToKiB } from '~/lib/utils/number_utils';
describe('Number Utils', () => { describe('Number Utils', () => {
describe('formatRelevantDigits', () => { describe('formatRelevantDigits', () => {
...@@ -38,4 +38,11 @@ describe('Number Utils', () => { ...@@ -38,4 +38,11 @@ describe('Number Utils', () => {
expect(leftFromDecimal.length).toBe(3); expect(leftFromDecimal.length).toBe(3);
}); });
}); });
describe('bytesToKiB', () => {
it('calculates KiB for the given bytes', () => {
expect(bytesToKiB(1024)).toEqual(1);
expect(bytesToKiB(1000)).toEqual(0.9765625);
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment