diff options
29 files changed, 373 insertions, 27 deletions
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 3c40359d3dc..a13e7b9c87e 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -9.4.2 +10.0.0 diff --git a/app/assets/javascripts/jobs/components/log/line.vue b/app/assets/javascripts/jobs/components/log/line.vue new file mode 100644 index 00000000000..86d0fcc3b74 --- /dev/null +++ b/app/assets/javascripts/jobs/components/log/line.vue @@ -0,0 +1,28 @@ +<script> +import LineNumber from './line_number.vue'; + +export default { + components: { + LineNumber, + }, + props: { + line: { + type: Object, + required: true, + }, + path: { + type: String, + required: true, + }, + }, +}; +</script> + +<template> + <div class="line"> + <line-number :line-number="line.lineNumber" :path="path" /> + <span v-for="(content, i) in line.content" :key="i" class="line-text" :class="content.style">{{ + content.text + }}</span> + </div> +</template> diff --git a/app/assets/javascripts/jobs/components/log/line_header.vue b/app/assets/javascripts/jobs/components/log/line_header.vue new file mode 100644 index 00000000000..4ec212d2333 --- /dev/null +++ b/app/assets/javascripts/jobs/components/log/line_header.vue @@ -0,0 +1,45 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; +import LineNumber from './line_number.vue'; + +export default { + components: { + Icon, + LineNumber, + }, + props: { + line: { + type: Object, + required: true, + }, + isClosed: { + type: Boolean, + required: true, + }, + path: { + type: String, + required: true, + }, + }, + computed: { + iconName() { + return this.isClosed ? 'angle-right' : 'angle-down'; + }, + }, + methods: { + handleOnClick() { + this.$emit('toggleLine'); + }, + }, +}; +</script> + +<template> + <div class="line collapsible-line" role="button" @click="handleOnClick"> + <icon :name="iconName" class="arrow" /> + <line-number :line-number="line.lineNumber" :path="path" /> + <span v-for="(content, i) in line.content" :key="i" class="line-text" :class="content.style">{{ + content.text + }}</span> + </div> +</template> diff --git a/app/assets/javascripts/jobs/components/log/line_number.vue b/app/assets/javascripts/jobs/components/log/line_number.vue new file mode 100644 index 00000000000..e06836e2e97 --- /dev/null +++ b/app/assets/javascripts/jobs/components/log/line_number.vue @@ -0,0 +1,52 @@ +<script> +import { GlLink } from '@gitlab/ui'; + +export default { + components: { + GlLink, + }, + props: { + lineNumber: { + type: Number, + required: true, + }, + path: { + type: String, + required: true, + }, + }, + computed: { + /** + * Builds the url for each line number + * + * @returns {String} + */ + buildLineNumber() { + return `${this.path}#${this.lineNumberId}`; + }, + /** + * Array indexes start with 0, so we add 1 + * to create the line number + * + * @returns {Number} the line number + */ + parsedLineNumber() { + return this.lineNumber + 1; + }, + + /** + * Creates the anchor for each link + * + * @returns {String} + */ + lineNumberId() { + return `L${this.parsedLineNumber}`; + }, + }, +}; +</script> +<template> + <gl-link :id="lineNumberId" class="line-number" :href="buildLineNumber">{{ + parsedLineNumber + }}</gl-link> +</template> diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb index 9e7319c1d9b..9c210f3a1f5 100644 --- a/app/services/auth/container_registry_authentication_service.rb +++ b/app/services/auth/container_registry_authentication_service.rb @@ -2,7 +2,7 @@ module Auth class ContainerRegistryAuthenticationService < BaseService - AUDIENCE = 'container_registry'.freeze + AUDIENCE = 'container_registry' def execute(authentication_abilities:) @authentication_abilities = authentication_abilities diff --git a/app/services/auto_merge_service.rb b/app/services/auto_merge_service.rb index 95bf2db2018..053c6d71fbb 100644 --- a/app/services/auto_merge_service.rb +++ b/app/services/auto_merge_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class AutoMergeService < BaseService - STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS = 'merge_when_pipeline_succeeds'.freeze + STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS = 'merge_when_pipeline_succeeds' STRATEGIES = [STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS].freeze class << self diff --git a/app/services/merge_requests/rebase_service.rb b/app/services/merge_requests/rebase_service.rb index 27c16ba1777..4d36dd4feae 100644 --- a/app/services/merge_requests/rebase_service.rb +++ b/app/services/merge_requests/rebase_service.rb @@ -2,7 +2,7 @@ module MergeRequests class RebaseService < MergeRequests::WorkingCopyBaseService - REBASE_ERROR = 'Rebase failed. Please rebase locally'.freeze + REBASE_ERROR = 'Rebase failed. Please rebase locally' def execute(merge_request) @merge_request = merge_request diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb index 5893b8eedff..108c4a79cde 100644 --- a/app/services/projects/destroy_service.rb +++ b/app/services/projects/destroy_service.rb @@ -6,7 +6,7 @@ module Projects DestroyError = Class.new(StandardError) - DELETED_FLAG = '+deleted'.freeze + DELETED_FLAG = '+deleted' REPO_REMOVAL_DELAY = 5.minutes.to_i def async_execute diff --git a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb index 9b72480d18b..5ef7e03ea02 100644 --- a/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb +++ b/app/services/projects/lfs_pointers/lfs_download_link_list_service.rb @@ -5,7 +5,7 @@ module Projects module LfsPointers class LfsDownloadLinkListService < BaseService - DOWNLOAD_ACTION = 'download'.freeze + DOWNLOAD_ACTION = 'download' DownloadLinksError = Class.new(StandardError) DownloadLinkNotFound = Class.new(StandardError) diff --git a/app/services/projects/lfs_pointers/lfs_list_service.rb b/app/services/projects/lfs_pointers/lfs_list_service.rb index 22160017f4f..a07fa93a279 100644 --- a/app/services/projects/lfs_pointers/lfs_list_service.rb +++ b/app/services/projects/lfs_pointers/lfs_list_service.rb @@ -4,7 +4,7 @@ module Projects module LfsPointers class LfsListService < BaseService - REV = 'HEAD'.freeze + REV = 'HEAD' # Retrieve all lfs blob pointers and returns a hash # with the structure { lfs_file_oid => lfs_file_size } diff --git a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb index 5ba0f50f2ff..4224b56c42c 100644 --- a/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb +++ b/app/services/projects/lfs_pointers/lfs_object_download_list_service.rb @@ -8,9 +8,9 @@ module Projects class LfsObjectDownloadListService < BaseService include Gitlab::Utils::StrongMemoize - HEAD_REV = 'HEAD'.freeze + HEAD_REV = 'HEAD' LFS_ENDPOINT_PATTERN = /^\t?url\s*=\s*(.+)$/.freeze - LFS_BATCH_API_ENDPOINT = '/info/lfs/objects/batch'.freeze + LFS_BATCH_API_ENDPOINT = '/info/lfs/objects/batch' LfsObjectDownloadListError = Class.new(StandardError) diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb index ee9884e9042..bc8f7f342f7 100644 --- a/app/services/projects/open_issues_count_service.rb +++ b/app/services/projects/open_issues_count_service.rb @@ -7,8 +7,8 @@ module Projects include Gitlab::Utils::StrongMemoize # Cache keys used to store issues count - PUBLIC_COUNT_KEY = 'public_open_issues_count'.freeze - TOTAL_COUNT_KEY = 'total_open_issues_count'.freeze + PUBLIC_COUNT_KEY = 'public_open_issues_count' + TOTAL_COUNT_KEY = 'total_open_issues_count' def initialize(project, user = nil) @user = user diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index 5caeb4cfa5f..fa7a4f0ed82 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -7,11 +7,11 @@ module Projects BLOCK_SIZE = 32.kilobytes MAX_SIZE = 1.terabyte - PUBLIC_DIR = 'public'.freeze + PUBLIC_DIR = 'public' # this has to be invalid group name, # as it shares the namespace with groups - TMP_EXTRACT_PATH = '@pages.tmp'.freeze + TMP_EXTRACT_PATH = '@pages.tmp' attr_reader :build diff --git a/app/services/submit_usage_ping_service.rb b/app/services/submit_usage_ping_service.rb index 4f10f220298..415a02ab337 100644 --- a/app/services/submit_usage_ping_service.rb +++ b/app/services/submit_usage_ping_service.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class SubmitUsagePingService - URL = 'https://version.gitlab.com/usage_data'.freeze + URL = 'https://version.gitlab.com/usage_data' METRICS = %w[leader_issues instance_issues percentage_issues leader_notes instance_notes percentage_notes leader_milestones instance_milestones percentage_milestones diff --git a/app/services/wikis/create_attachment_service.rb b/app/services/wikis/create_attachment_service.rb index df31ad7c8ea..6ef6cbc3c12 100644 --- a/app/services/wikis/create_attachment_service.rb +++ b/app/services/wikis/create_attachment_service.rb @@ -2,7 +2,7 @@ module Wikis class CreateAttachmentService < Files::CreateService - ATTACHMENT_PATH = 'uploads'.freeze + ATTACHMENT_PATH = 'uploads' MAX_FILENAME_LENGTH = 255 delegate :wiki, to: :project diff --git a/changelogs/unreleased/66454-base-components.yml b/changelogs/unreleased/66454-base-components.yml new file mode 100644 index 00000000000..7a40a66f122 --- /dev/null +++ b/changelogs/unreleased/66454-base-components.yml @@ -0,0 +1,5 @@ +--- +title: Creates base components for the new job log +merge_request: +author: +type: added diff --git a/changelogs/unreleased/persist-needs-error.yml b/changelogs/unreleased/persist-needs-error.yml new file mode 100644 index 00000000000..96aaa4d11a3 --- /dev/null +++ b/changelogs/unreleased/persist-needs-error.yml @@ -0,0 +1,5 @@ +--- +title: Persist `needs:` validation as config error +merge_request: +author: +type: fixed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 6dcaefc05d5..e3693f612e3 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -158,6 +158,7 @@ production: &base # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to. # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`). + # Please be aware that a placeholder is required for the Service Desk feature to work. address: "gitlab-incoming+%{key}@gmail.com" # Email account username diff --git a/doc/administration/index.md b/doc/administration/index.md index 650cb10a64a..711eebcd61e 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -139,6 +139,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Repository checks](repository_checks.md): Periodic Git repository checks. - [Repository storage paths](repository_storage_paths.md): Manage the paths used to store repositories. +- [Repository storage types](repository_storage_types.md): Information about the different repository storage types. - [Repository storage rake tasks](raketasks/storage.md): A collection of rake tasks to list and migrate existing projects and attachments associated with it from Legacy storage to Hashed storage. - [Limit repository size](../user/admin_area/settings/account_and_limit_settings.md): Set a hard limit for your repositories' size. **(STARTER ONLY)** diff --git a/doc/api/issues.md b/doc/api/issues.md index 7498d2d840b..a89a6e7c5cc 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -573,6 +573,18 @@ the `weight` parameter: } ``` +Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) will additionally see +the `epic_iid` property: + +```json +{ + "project_id" : 4, + "description" : "Omnis vero earum sunt corporis dolor et placeat.", + "epic_iid" : 42, + ... +} +``` + **Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API. **Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. diff --git a/lib/api/entities.rb b/lib/api/entities.rb index ba58e125568..f7cd6d35854 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -978,7 +978,9 @@ module API expose :created_at def todo_target_class(target_type) - ::API::Entities.const_get(target_type) + # false as second argument prevents looking up in module hierarchy + # see also https://gitlab.com/gitlab-org/gitlab-ce/issues/59719 + ::API::Entities.const_get(target_type, false) end end @@ -1731,6 +1733,7 @@ API::Entities.prepend_if_ee('EE::API::Entities::Entities') ::API::Entities::Group.prepend_if_ee('EE::API::Entities::Group', with_descendants: true) ::API::Entities::GroupDetail.prepend_if_ee('EE::API::Entities::GroupDetail') ::API::Entities::IssueBasic.prepend_if_ee('EE::API::Entities::IssueBasic', with_descendants: true) +::API::Entities::Issue.prepend_if_ee('EE::API::Entities::Issue') ::API::Entities::List.prepend_if_ee('EE::API::Entities::List') ::API::Entities::MergeRequestBasic.prepend_if_ee('EE::API::Entities::MergeRequestBasic', with_descendants: true) ::API::Entities::Namespace.prepend_if_ee('EE::API::Entities::Namespace') diff --git a/lib/gitlab/ci/pipeline/chain/helpers.rb b/lib/gitlab/ci/pipeline/chain/helpers.rb index 6bb3a75291b..8ccb1066575 100644 --- a/lib/gitlab/ci/pipeline/chain/helpers.rb +++ b/lib/gitlab/ci/pipeline/chain/helpers.rb @@ -5,7 +5,12 @@ module Gitlab module Pipeline module Chain module Helpers - def error(message) + def error(message, config_error: false) + if config_error && command.save_incompleted + pipeline.yaml_errors = message + pipeline.drop!(:config_error) + end + pipeline.errors.add(:base, message) end end diff --git a/lib/gitlab/ci/pipeline/chain/populate.rb b/lib/gitlab/ci/pipeline/chain/populate.rb index 65029f5ce7f..13eca5a9d28 100644 --- a/lib/gitlab/ci/pipeline/chain/populate.rb +++ b/lib/gitlab/ci/pipeline/chain/populate.rb @@ -26,7 +26,7 @@ module Gitlab # Gather all runtime build/stage errors # if seeds_errors = pipeline.stage_seeds.flat_map(&:errors).compact.presence - return error(seeds_errors.join("\n")) + return error(seeds_errors.join("\n"), config_error: true) end ## diff --git a/package.json b/package.json index 782bb3a96a7..247993626a6 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@babel/plugin-syntax-import-meta": "^7.2.0", "@babel/preset-env": "^7.5.5", "@gitlab/svgs": "^1.71.0", - "@gitlab/ui": "5.20.2", + "@gitlab/ui": "5.21.0", "@gitlab/visual-review-tools": "1.0.1", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", diff --git a/spec/javascripts/jobs/components/log/line_header_spec.js b/spec/javascripts/jobs/components/log/line_header_spec.js new file mode 100644 index 00000000000..4efd412a6cd --- /dev/null +++ b/spec/javascripts/jobs/components/log/line_header_spec.js @@ -0,0 +1,84 @@ +import { mount } from '@vue/test-utils'; +import LineHeader from '~/jobs/components/log/line_header.vue'; +import LineNumber from '~/jobs/components/log/line_number.vue'; + +describe('Job Log Header Line', () => { + let wrapper; + + const data = { + line: { + content: [ + { + text: 'Running with gitlab-runner 12.1.0 (de7731dd)', + style: 'term-fg-l-green', + }, + ], + lineNumber: 0, + }, + isClosed: true, + path: '/jashkenas/underscore/-/jobs/335', + }; + + const createComponent = (props = {}) => { + wrapper = mount(LineHeader, { + sync: false, + propsData: { + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + }); + + describe('line', () => { + beforeEach(() => { + createComponent(data); + }); + + it('renders the line number component', () => { + expect(wrapper.contains(LineNumber)).toBe(true); + }); + + it('renders a span the provided text', () => { + expect(wrapper.find('span').text()).toBe(data.line.content[0].text); + }); + + it('renders the provided style as a class attribute', () => { + expect(wrapper.find('span').classes()).toContain(data.line.content[0].style); + }); + }); + + describe('when isCloses is true', () => { + beforeEach(() => { + createComponent({ ...data, isClosed: true }); + }); + + it('sets icon name to be angle-right', () => { + expect(wrapper.vm.iconName).toEqual('angle-right'); + }); + }); + + describe('when isCloses is false', () => { + beforeEach(() => { + createComponent({ ...data, isClosed: false }); + }); + + it('sets icon name to be angle-down', () => { + expect(wrapper.vm.iconName).toEqual('angle-down'); + }); + }); + + describe('on click', () => { + beforeEach(() => { + createComponent(data); + }); + + it('emits toggleLine event', () => { + wrapper.trigger('click'); + + expect(wrapper.emitted().toggleLine.length).toBe(1); + }); + }); +}); diff --git a/spec/javascripts/jobs/components/log/line_number_spec.js b/spec/javascripts/jobs/components/log/line_number_spec.js new file mode 100644 index 00000000000..fcf2edf9159 --- /dev/null +++ b/spec/javascripts/jobs/components/log/line_number_spec.js @@ -0,0 +1,40 @@ +import { shallowMount } from '@vue/test-utils'; +import LineNumber from '~/jobs/components/log/line_number.vue'; + +describe('Job Log Line Number', () => { + let wrapper; + + const data = { + lineNumber: 0, + path: '/jashkenas/underscore/-/jobs/335', + }; + + const createComponent = (props = {}) => { + wrapper = shallowMount(LineNumber, { + sync: false, + propsData: { + ...props, + }, + }); + }; + + beforeEach(() => { + createComponent(data); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders incremented lineNunber by 1', () => { + expect(wrapper.text()).toBe('1'); + }); + + it('renders link with lineNumber as an ID', () => { + expect(wrapper.attributes().id).toBe('L1'); + }); + + it('links to the provided path with line number as anchor', () => { + expect(wrapper.attributes().href).toBe(`${data.path}#L1`); + }); +}); diff --git a/spec/javascripts/jobs/components/log/line_spec.js b/spec/javascripts/jobs/components/log/line_spec.js new file mode 100644 index 00000000000..ea593e3c39a --- /dev/null +++ b/spec/javascripts/jobs/components/log/line_spec.js @@ -0,0 +1,49 @@ +import { shallowMount } from '@vue/test-utils'; +import Line from '~/jobs/components/log/line.vue'; +import LineNumber from '~/jobs/components/log/line_number.vue'; + +describe('Job Log Line', () => { + let wrapper; + + const data = { + line: { + content: [ + { + text: 'Running with gitlab-runner 12.1.0 (de7731dd)', + style: 'term-fg-l-green', + }, + ], + lineNumber: 0, + }, + path: '/jashkenas/underscore/-/jobs/335', + }; + + const createComponent = (props = {}) => { + wrapper = shallowMount(Line, { + sync: false, + propsData: { + ...props, + }, + }); + }; + + beforeEach(() => { + createComponent(data); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the line number component', () => { + expect(wrapper.contains(LineNumber)).toBe(true); + }); + + it('renders a span the provided text', () => { + expect(wrapper.find('span').text()).toBe(data.line.content[0].text); + }); + + it('renders the provided style as a class attribute', () => { + expect(wrapper.find('span').classes()).toContain(data.line.content[0].style); + }); +}); diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index deb68899309..fad865a4811 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1140,10 +1140,26 @@ describe Ci::CreatePipelineService do context 'when pipeline on feature is created' do let(:ref_name) { 'refs/heads/feature' } - it 'does not create a pipeline as test_a depends on build_a' do - expect(pipeline).not_to be_persisted - expect(pipeline.builds).to be_empty - expect(pipeline.errors[:base]).to contain_exactly("test_a: needs 'build_a'") + context 'when save_on_errors is enabled' do + let(:pipeline) { execute_service(save_on_errors: true) } + + it 'does create a pipeline as test_a depends on build_a' do + expect(pipeline).to be_persisted + expect(pipeline.builds).to be_empty + expect(pipeline.yaml_errors).to eq("test_a: needs 'build_a'") + expect(pipeline.errors[:base]).to contain_exactly("test_a: needs 'build_a'") + end + end + + context 'when save_on_errors is disabled' do + let(:pipeline) { execute_service(save_on_errors: false) } + + it 'does not create a pipeline as test_a depends on build_a' do + expect(pipeline).not_to be_persisted + expect(pipeline.builds).to be_empty + expect(pipeline.yaml_errors).to be_nil + expect(pipeline.errors[:base]).to contain_exactly("test_a: needs 'build_a'") + end end end diff --git a/yarn.lock b/yarn.lock index bd8f9014131..d17dae8f907 100644 --- a/yarn.lock +++ b/yarn.lock @@ -996,10 +996,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.71.0.tgz#c8e6e8f500ea91e5cbba4ac08df533fb2e622a00" integrity sha512-kkeNic/FFwaqKnzwio4NE7whBOZ/toRJ8cS0587DBotajAzSYhph5ij4TCY2GTjPa33zIJ5OUr/k90C0Kr71hQ== -"@gitlab/ui@5.20.2": - version "5.20.2" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.20.2.tgz#a51270d5a521e71059c5fd05f86cfc835f5e28ae" - integrity sha512-TSaD5Cz0YXBTsRtQwsa7LbS2O5h0CL3YkdYmBKrMZkphL76xQaN08ZImkQ5Xl8cD1ZiWN2CsTvoUbF19UP2V1w== +"@gitlab/ui@5.21.0": + version "5.21.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.21.0.tgz#975cf0bca3d16dd080d67ed392b9d24cd64695ac" + integrity sha512-8TMVM+pJXf7omHgKMMZ1FiltuyMOTwfQ3iFgorQzcuhio9u35DJpWi45S2TF7m6CrlpJi7dMX3BsXLbF7ViSUw== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.2.1" |