diff options
47 files changed, 565 insertions, 123 deletions
diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue index 6d671845f8e..c660828b30e 100644 --- a/app/assets/javascripts/jobs/components/header.vue +++ b/app/assets/javascripts/jobs/components/header.vue @@ -30,6 +30,9 @@ shouldRenderContent() { return !this.isLoading && Object.keys(this.job).length; }, + jobStarted() { + return this.job.started; + }, }, methods: { getActions() { @@ -63,8 +66,9 @@ :time="job.created_at" :user="job.user" :actions="actions" - :hasSidebarButton="true" - /> + :has-sidebar-button="true" + :should-render-triggered-label="jobStarted" + /> <loading-icon v-if="isLoading" size="2" diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue index 632fc167f2b..f31a91c3403 100644 --- a/app/assets/javascripts/pipelines/components/nav_controls.vue +++ b/app/assets/javascripts/pipelines/components/nav_controls.vue @@ -17,6 +17,11 @@ export default { required: true, }, + resetCachePath: { + type: String, + required: true, + }, + ciLintPath: { type: String, required: true, @@ -46,6 +51,14 @@ export default { </a> <a + data-method="post" + rel="nofollow" + :href="resetCachePath" + class="btn btn-default"> + Clear runner caches + </a> + + <a :href="ciLintPath" class="btn btn-default"> CI Lint diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index fe1f3b4246a..8fa416168e7 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -50,6 +50,7 @@ canCreatePipeline: pipelinesData.canCreatePipeline, hasCi: pipelinesData.hasCi, ciLintPath: pipelinesData.ciLintPath, + resetCachePath: pipelinesData.resetCachePath, state: this.store.state, scope: getParameterByName('scope') || 'all', page: getParameterByName('page') || '1', @@ -220,6 +221,7 @@ :new-pipeline-path="newPipelinePath" :has-ci-enabled="hasCiEnabled" :help-page-path="helpPagePath" + :resetCachePath="resetCachePath" :ci-lint-path="ciLintPath" :can-create-pipeline="canCreatePipelineParsed " /> diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue index d305bd6acdc..2209bc0f9cf 100644 --- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue +++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue @@ -45,6 +45,11 @@ export default { required: false, default: false, }, + shouldRenderTriggeredLabel: { + type: Boolean, + required: false, + default: true, + }, }, directives: { @@ -82,7 +87,12 @@ export default { {{itemName}} #{{itemId}} </strong> - triggered + <template v-if="shouldRenderTriggeredLabel"> + triggered + </template> + <template v-else> + created + </template> <timeago-tooltip :time="time" /> diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index aa2d30a3cef..fd5c3c81a53 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -20,10 +20,13 @@ width: 100%; } - &.svg-250 { - img, - svg { - width: 250px; + $image-widths: 250 306 394; + @each $width in $image-widths { + &.svg-#{$width} { + img, + svg { + width: #{$width + 'px'}; + } } } } diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index b029b31f9af..86717bb7242 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -11,6 +11,16 @@ module Projects define_auto_devops_variables end + def reset_cache + if ResetProjectCacheService.new(@project, current_user).execute + flash[:notice] = _("Project cache successfully reset.") + else + flash[:error] = _("Unable to reset project cache.") + end + + redirect_to project_pipelines_path(@project) + end + private def define_runners_variables diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index fb5ae83ac7b..6012dbba1b9 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -461,7 +461,14 @@ module Ci end def cache - [options[:cache]] + cache = options[:cache] + + if cache && project.jobs_cache_index + cache = cache.merge( + key: "#{cache[:key]}:#{project.jobs_cache_index}") + end + + [cache] end def credentials diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 72e56a2c77f..523b522d449 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -4,6 +4,8 @@ class JobEntity < Grape::Entity expose :id expose :name + expose :started?, as: :started + expose :build_path do |build| build.target_url || path_to(:namespace_project_job, build) end diff --git a/app/services/reset_project_cache_service.rb b/app/services/reset_project_cache_service.rb new file mode 100644 index 00000000000..a162a6eedb9 --- /dev/null +++ b/app/services/reset_project_cache_service.rb @@ -0,0 +1,5 @@ +class ResetProjectCacheService < BaseService + def execute + @project.increment!(:jobs_cache_index) + end +end diff --git a/app/views/projects/jobs/_empty_state.html.haml b/app/views/projects/jobs/_empty_state.html.haml new file mode 100644 index 00000000000..c66313bdbf3 --- /dev/null +++ b/app/views/projects/jobs/_empty_state.html.haml @@ -0,0 +1,17 @@ +- illustration = local_assigns.fetch(:illustration) +- illustration_size = local_assigns.fetch(:illustration_size) +- title = local_assigns.fetch(:title) +- content = local_assigns.fetch(:content) +- action = local_assigns.fetch(:action, nil) + +.row.empty-state + .col-xs-12 + .svg-content{ class: illustration_size } + = image_tag illustration + .col-xs-12 + .text-content + %h4.text-center= title + %p= content + - if action + .text-center + = action diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml index fd24bbbb9ba..8b05440fc78 100644 --- a/app/views/projects/jobs/show.html.haml +++ b/app/views/projects/jobs/show.html.haml @@ -54,41 +54,53 @@ Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)} - else Job has been erased #{time_ago_with_tooltip(@build.erased_at)} + - if @build.started? + .build-trace-container.prepend-top-default + .top-bar.js-top-bar + .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden< + Showing last + %span.js-truncated-info-size.truncated-info-size>< + of log - + %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw - .build-trace-container.prepend-top-default - .top-bar.js-top-bar - .js-truncated-info.truncated-info.hidden-xs.pull-left.hidden< - Showing last - %span.js-truncated-info-size.truncated-info-size>< - of log - - %a.js-raw-link.raw-link{ href: raw_project_job_path(@project, @build) }>< Complete Raw + .controllers.pull-right + - if @build.has_trace? + = link_to raw_project_job_path(@project, @build), + title: 'Show complete raw', + data: { placement: 'top', container: 'body' }, + class: 'js-raw-link-controller has-tooltip controllers-buttons' do + = icon('file-text-o') - .controllers.pull-right - - if @build.has_trace? - = link_to raw_project_job_path(@project, @build), - title: 'Show complete raw', - data: { placement: 'top', container: 'body' }, - class: 'js-raw-link-controller has-tooltip controllers-buttons' do - = icon('file-text-o') - - - if @build.erasable? && can?(current_user, :erase_build, @build) - = link_to erase_project_job_path(@project, @build), - method: :post, - data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' }, - title: 'Erase job log', - class: 'has-tooltip js-erase-link controllers-buttons' do - = icon('trash') - .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} } - %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } - = custom_icon('scroll_up') - .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } - %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } - = custom_icon('scroll_down') - - %pre.build-trace#build-trace - %code.bash.js-build-output - .build-loader-animation.js-build-refresh + - if @build.erasable? && can?(current_user, :erase_build, @build) + = link_to erase_project_job_path(@project, @build), + method: :post, + data: { confirm: 'Are you sure you want to erase this build?', placement: 'top', container: 'body' }, + title: 'Erase job log', + class: 'has-tooltip js-erase-link controllers-buttons' do + = icon('trash') + .has-tooltip.controllers-buttons{ title: 'Scroll to top', data: { placement: 'top', container: 'body'} } + %button.js-scroll-up.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } + = custom_icon('scroll_up') + .has-tooltip.controllers-buttons{ title: 'Scroll to bottom', data: { placement: 'top', container: 'body'} } + %button.js-scroll-down.btn-scroll.btn-transparent.btn-blank{ type: 'button', disabled: true } + = custom_icon('scroll_down') + %pre.build-trace#build-trace + %code.bash.js-build-output + .build-loader-animation.js-build-refresh + - elsif @build.playable? + = render 'empty_state', + illustration: 'illustrations/manual_action.svg', + illustration_size: 'svg-394', + title: _('This job requires a manual action'), + content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.'), + action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), class: 'btn btn-primary', title: _('Trigger this manual action') ) + - else + = render 'empty_state', + illustration: 'illustrations/job_not_triggered.svg', + illustration_size: 'svg-306', + title: _('This job has not been triggered yet'), + content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered.') = render "sidebar" diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index b2e71cff6ce..f8555f11aab 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -10,7 +10,8 @@ "new-pipeline-path" => new_project_pipeline_path(@project), "can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s, "has-ci" => @repository.gitlab_ci_yml, - "ci-lint-path" => ci_lint_path } } + "ci-lint-path" => ci_lint_path, + "reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } } = page_specific_javascript_bundle_tag('common_vue') = page_specific_javascript_bundle_tag('pipelines') diff --git a/changelogs/unreleased/41249-clearing-the-cache.yml b/changelogs/unreleased/41249-clearing-the-cache.yml new file mode 100644 index 00000000000..221589a1239 --- /dev/null +++ b/changelogs/unreleased/41249-clearing-the-cache.yml @@ -0,0 +1,5 @@ +--- +title: Implement project jobs cache reset +merge_request: 16067 +author: +type: added diff --git a/config/routes/project.rb b/config/routes/project.rb index 1354c4c5537..bdf4b199c0a 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -408,7 +408,9 @@ constraints(ProjectUrlConstrainer.new) do end namespace :settings do get :members, to: redirect("%{namespace_id}/%{project_id}/project_members") - resource :ci_cd, only: [:show], controller: 'ci_cd' + resource :ci_cd, only: [:show], controller: 'ci_cd' do + post :reset_cache + end resource :integrations, only: [:show] resource :repository, only: [:show], controller: :repository end diff --git a/db/migrate/20171222183504_add_jobs_cache_index_to_project.rb b/db/migrate/20171222183504_add_jobs_cache_index_to_project.rb new file mode 100644 index 00000000000..607e9d027d7 --- /dev/null +++ b/db/migrate/20171222183504_add_jobs_cache_index_to_project.rb @@ -0,0 +1,13 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddJobsCacheIndexToProject < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + def change + add_column :projects, :jobs_cache_index, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 740e80ccfd4..e6a2ea4c862 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1451,6 +1451,7 @@ ActiveRecord::Schema.define(version: 20171230123729) do t.boolean "repository_read_only" t.boolean "merge_requests_ff_only_enabled", default: false t.boolean "merge_requests_rebase_enabled", default: false, null: false + t.integer "jobs_cache_index" end add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb index c267195f0e8..a3e4459f169 100644 --- a/features/steps/shared/builds.rb +++ b/features/steps/shared/builds.rb @@ -11,7 +11,7 @@ module SharedBuilds step 'project has a recent build' do @pipeline = create(:ci_empty_pipeline, project: @project, sha: @project.commit.sha, ref: 'master') - @build = create(:ci_build, :coverage, pipeline: @pipeline) + @build = create(:ci_build, :running, :coverage, pipeline: @pipeline) end step 'recent build is successful' do diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 84105501d1e..d7605b74cf1 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1187,7 +1187,7 @@ module Gitlab end # Items should be of format [[commit_id, path], [commit_id1, path1]] - def batch_blobs(items, blob_size_limit: nil) + def batch_blobs(items, blob_size_limit: Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) Gitlab::Git::Blob.batch(self, items, blob_size_limit: blob_size_limit) end diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb index 77a47f0ad13..0202149f335 100644 --- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb +++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb @@ -17,4 +17,51 @@ describe Projects::Settings::CiCdController do expect(response).to render_template(:show) end end + + describe '#reset_cache' do + before do + sign_in(user) + + project.add_master(user) + + allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(true) + end + + subject { post :reset_cache, namespace_id: project.namespace, project_id: project } + + it 'calls reset project cache service' do + expect(ResetProjectCacheService).to receive_message_chain(:new, :execute) + + subject + end + + it 'redirects to project pipelines path' do + subject + + expect(response).to have_gitlab_http_status(:redirect) + expect(response).to redirect_to(project_pipelines_path(project)) + end + + context 'when service returns successfully' do + it 'sets the flash notice variable' do + subject + + expect(controller).to set_flash[:notice] + expect(controller).not_to set_flash[:error] + end + end + + context 'when service does not return successfully' do + before do + allow(ResetProjectCacheService).to receive_message_chain(:new, :execute).and_return(false) + end + + it 'sets the flash error variable' do + subject + + expect(controller).not_to set_flash[:notice] + expect(controller).to set_flash[:error] + end + end + end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index dc1d88c92dc..6f66468570f 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -7,12 +7,10 @@ FactoryBot.define do stage_idx 0 ref 'master' tag false - status 'pending' - created_at 'Di 29. Okt 09:50:00 CET 2013' - started_at 'Di 29. Okt 09:51:28 CET 2013' - finished_at 'Di 29. Okt 09:53:28 CET 2013' commands 'ls -a' protected false + created_at 'Di 29. Okt 09:50:00 CET 2013' + pending options do { @@ -29,23 +27,37 @@ FactoryBot.define do pipeline factory: :ci_pipeline + trait :started do + started_at 'Di 29. Okt 09:51:28 CET 2013' + end + + trait :finished do + started + finished_at 'Di 29. Okt 09:53:28 CET 2013' + end + trait :success do + finished status 'success' end trait :failed do + finished status 'failed' end trait :canceled do + finished status 'canceled' end trait :skipped do + started status 'skipped' end trait :running do + started status 'running' end @@ -114,11 +126,6 @@ FactoryBot.define do build.project ||= build.pipeline.project end - factory :ci_not_started_build do - started_at nil - finished_at nil - end - trait :tag do tag true end diff --git a/spec/features/projects/jobs/user_browses_job_spec.rb b/spec/features/projects/jobs/user_browses_job_spec.rb index 5d9208ebadd..4c49cff30d4 100644 --- a/spec/features/projects/jobs/user_browses_job_spec.rb +++ b/spec/features/projects/jobs/user_browses_job_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'User browses a job', :js do - let!(:build) { create(:ci_build, :coverage, pipeline: pipeline) } + let!(:build) { create(:ci_build, :running, :coverage, pipeline: pipeline) } let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') } let(:project) { create(:project, :repository, namespace: user.namespace) } let(:user) { create(:user) } diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index f8ea1a52656..9a6b27c00f8 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -369,6 +369,34 @@ feature 'Jobs' do end end end + + context 'Playable manual action' do + let(:job) { create(:ci_build, :playable, pipeline: pipeline) } + + before do + project.add_developer(user) + visit project_job_path(project, job) + end + + it 'shows manual action empty state' do + expect(page).to have_content('This job requires a manual action') + expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.') + expect(page).to have_link('Trigger this manual action') + end + end + + context 'Non triggered job' do + let(:job) { create(:ci_build, :created, pipeline: pipeline) } + + before do + visit project_job_path(project, job) + end + + it 'shows manual action empty state' do + expect(page).to have_content('This job has not been triggered yet') + expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered.') + end + end end describe "POST /:project/jobs/:id/cancel", :js do diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index df261c246f7..592c99fc64a 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -545,6 +545,40 @@ describe 'Pipelines', :js do end end end + + describe 'Reset runner caches' do + let(:project) { create(:project, :repository) } + + before do + create(:ci_empty_pipeline, status: 'success', project: project, sha: project.commit.id, ref: 'master') + project.add_master(user) + visit project_pipelines_path(project) + end + + it 'has a clear caches button' do + expect(page).to have_link 'Clear runner caches' + end + + describe 'user clicks the button' do + context 'when project already has jobs_cache_index' do + before do + project.update_attributes(jobs_cache_index: 1) + end + + it 'increments jobs_cache_index' do + click_link 'Clear runner caches' + expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.' + end + end + + context 'when project does not have jobs_cache_index' do + it 'sets jobs_cache_index to 1' do + click_link 'Clear runner caches' + expect(page.find('.flash-notice')).to have_content 'Project cache successfully reset.' + end + end + end + end end context 'when user is not logged in' do diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml index 85ee61f0b54..0161c0550d1 100644 --- a/spec/javascripts/fixtures/pipelines.html.haml +++ b/spec/javascripts/fixtures/pipelines.html.haml @@ -7,4 +7,6 @@ "new-pipeline-path" => 'foo', "can-create-pipeline" => 'true', "has-ci" => 'foo', - "ci-lint-path" => 'foo' } } + "ci-lint-path" => 'foo', + "reset-cache-path" => 'foo' } } + diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js index 4a210faa017..83395ea451e 100644 --- a/spec/javascripts/jobs/header_spec.js +++ b/spec/javascripts/jobs/header_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import headerComponent from '~/jobs/components/header.vue'; +import mountComponent from '../helpers/vue_mount_component_helper'; describe('Job details header', () => { let HeaderComponent; @@ -35,7 +36,7 @@ describe('Job details header', () => { isLoading: false, }; - vm = new HeaderComponent({ propsData: props }).$mount(); + vm = mountComponent(HeaderComponent, props); }); afterEach(() => { diff --git a/spec/javascripts/pipelines/nav_controls_spec.js b/spec/javascripts/pipelines/nav_controls_spec.js index f1697840fcd..09a0c14d96c 100644 --- a/spec/javascripts/pipelines/nav_controls_spec.js +++ b/spec/javascripts/pipelines/nav_controls_spec.js @@ -14,6 +14,7 @@ describe('Pipelines Nav Controls', () => { hasCiEnabled: true, helpPagePath: 'foo', ciLintPath: 'foo', + resetCachePath: 'foo', canCreatePipeline: true, }; @@ -31,6 +32,7 @@ describe('Pipelines Nav Controls', () => { hasCiEnabled: true, helpPagePath: 'foo', ciLintPath: 'foo', + resetCachePath: 'foo', canCreatePipeline: false, }; @@ -41,12 +43,31 @@ describe('Pipelines Nav Controls', () => { expect(component.$el.querySelector('.btn-create')).toEqual(null); }); + it('should render link for resetting runner caches', () => { + const mockData = { + newPipelinePath: 'foo', + hasCiEnabled: true, + helpPagePath: 'foo', + ciLintPath: 'foo', + resetCachePath: 'foo', + canCreatePipeline: false, + }; + + const component = new NavControlsComponent({ + propsData: mockData, + }).$mount(); + + expect(component.$el.querySelectorAll('.btn-default')[0].textContent).toContain('Clear runner caches'); + expect(component.$el.querySelectorAll('.btn-default')[0].getAttribute('href')).toEqual(mockData.resetCachePath); + }); + it('should render link for CI lint', () => { const mockData = { newPipelinePath: 'foo', hasCiEnabled: true, helpPagePath: 'foo', ciLintPath: 'foo', + resetCachePath: 'foo', canCreatePipeline: true, }; @@ -54,8 +75,8 @@ describe('Pipelines Nav Controls', () => { propsData: mockData, }).$mount(); - expect(component.$el.querySelector('.btn-default').textContent).toContain('CI Lint'); - expect(component.$el.querySelector('.btn-default').getAttribute('href')).toEqual(mockData.ciLintPath); + expect(component.$el.querySelectorAll('.btn-default')[1].textContent).toContain('CI Lint'); + expect(component.$el.querySelectorAll('.btn-default')[1].getAttribute('href')).toEqual(mockData.ciLintPath); }); it('should render link to help page when CI is not enabled', () => { @@ -64,6 +85,7 @@ describe('Pipelines Nav Controls', () => { hasCiEnabled: false, helpPagePath: 'foo', ciLintPath: 'foo', + resetCachePath: 'foo', canCreatePipeline: true, }; @@ -81,6 +103,7 @@ describe('Pipelines Nav Controls', () => { hasCiEnabled: true, helpPagePath: 'foo', ciLintPath: 'foo', + resetCachePath: 'foo', canCreatePipeline: true, }; diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js index b4553acb341..b378a0bd896 100644 --- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js +++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import headerCi from '~/vue_shared/components/header_ci_component.vue'; +import mountComponent from '../../helpers/vue_mount_component_helper'; describe('Header CI Component', () => { let HeaderCi; @@ -8,7 +9,6 @@ describe('Header CI Component', () => { beforeEach(() => { HeaderCi = Vue.extend(headerCi); - props = { status: { group: 'failed', @@ -45,54 +45,65 @@ describe('Header CI Component', () => { ], hasSidebarButton: true, }; - - vm = new HeaderCi({ - propsData: props, - }).$mount(); }); afterEach(() => { vm.$destroy(); }); - it('should render status badge', () => { - expect(vm.$el.querySelector('.ci-failed')).toBeDefined(); - expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined(); - expect( - vm.$el.querySelector('.ci-failed').getAttribute('href'), - ).toEqual(props.status.details_path); - }); + describe('render', () => { + beforeEach(() => { + vm = mountComponent(HeaderCi, props); + }); - it('should render item name and id', () => { - expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123'); - }); + it('should render status badge', () => { + expect(vm.$el.querySelector('.ci-failed')).toBeDefined(); + expect(vm.$el.querySelector('.ci-status-icon-failed svg')).toBeDefined(); + expect( + vm.$el.querySelector('.ci-failed').getAttribute('href'), + ).toEqual(props.status.details_path); + }); - it('should render timeago date', () => { - expect(vm.$el.querySelector('time')).toBeDefined(); - }); + it('should render item name and id', () => { + expect(vm.$el.querySelector('strong').textContent.trim()).toEqual('job #123'); + }); - it('should render user icon and name', () => { - expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name); - }); + it('should render timeago date', () => { + expect(vm.$el.querySelector('time')).toBeDefined(); + }); - it('should render provided actions', () => { - expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON'); - expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label); - expect(vm.$el.querySelector('.link').tagName).toEqual('A'); - expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label); - expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path); - }); + it('should render user icon and name', () => { + expect(vm.$el.querySelector('.js-user-link').textContent.trim()).toEqual(props.user.name); + }); + + it('should render provided actions', () => { + expect(vm.$el.querySelector('.btn').tagName).toEqual('BUTTON'); + expect(vm.$el.querySelector('.btn').textContent.trim()).toEqual(props.actions[0].label); + expect(vm.$el.querySelector('.link').tagName).toEqual('A'); + expect(vm.$el.querySelector('.link').textContent.trim()).toEqual(props.actions[1].label); + expect(vm.$el.querySelector('.link').getAttribute('href')).toEqual(props.actions[0].path); + }); - it('should show loading icon', (done) => { - vm.actions[0].isLoading = true; + it('should show loading icon', (done) => { + vm.actions[0].isLoading = true; - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy(); - done(); + Vue.nextTick(() => { + expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy(); + done(); + }); + }); + + it('should render sidebar toggle button', () => { + expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined(); }); }); - it('should render sidebar toggle button', () => { - expect(vm.$el.querySelector('.js-sidebar-build-toggle')).toBeDefined(); + describe('shouldRenderTriggeredLabel', () => { + it('should rendered created keyword when the shouldRenderTriggeredLabel is false', () => { + vm = mountComponent(HeaderCi, { ...props, shouldRenderTriggeredLabel: false }); + + expect(vm.$el.textContent).toContain('created'); + expect(vm.$el.textContent).not.toContain('triggered'); + }); }); }); diff --git a/spec/lib/gitlab/cycle_analytics/events_spec.rb b/spec/lib/gitlab/cycle_analytics/events_spec.rb index 28ea7d4c303..38a47a159e1 100644 --- a/spec/lib/gitlab/cycle_analytics/events_spec.rb +++ b/spec/lib/gitlab/cycle_analytics/events_spec.rb @@ -122,17 +122,18 @@ describe 'cycle analytics events' do let(:stage) { :test } let(:merge_request) { MergeRequest.first } + let!(:pipeline) do create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, - project: context.project, + project: project, head_pipeline_of: merge_request) end before do - create(:ci_build, pipeline: pipeline, status: :success, author: user) - create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) pipeline.run! pipeline.succeed! @@ -219,17 +220,18 @@ describe 'cycle analytics events' do describe '#staging_events' do let(:stage) { :staging } let(:merge_request) { MergeRequest.first } + let!(:pipeline) do create(:ci_pipeline, ref: merge_request.source_branch, sha: merge_request.diff_head_sha, - project: context.project, + project: project, head_pipeline_of: merge_request) end before do - create(:ci_build, pipeline: pipeline, status: :success, author: user) - create(:ci_build, pipeline: pipeline, status: :success, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) + create(:ci_build, :success, pipeline: pipeline, author: user) pipeline.run! pipeline.succeed! diff --git a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb index d81774c8b8f..a067c42b75b 100644 --- a/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb +++ b/spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb @@ -19,4 +19,18 @@ describe Gitlab::Diff::FileCollection::MergeRequestDiff do diff_files end + + shared_examples 'initializes a DiffCollection' do + it 'returns a valid instance of a DiffCollection' do + expect(diff_files).to be_a(Gitlab::Git::DiffCollection) + end + end + + context 'with Gitaly disabled', :disable_gitaly do + it_behaves_like 'initializes a DiffCollection' + end + + context 'with Gitaly enabled' do + it_behaves_like 'initializes a DiffCollection' + end end diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index ec8fa99e0da..ec577903eb5 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -459,6 +459,7 @@ Project: - delete_error - merge_requests_ff_only_enabled - merge_requests_rebase_enabled +- jobs_cache_index Author: - name ProjectFeature: diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 871e8b47650..3eaeeebf97d 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -255,6 +255,42 @@ describe Ci::Build do end end + describe '#cache' do + let(:options) { { cache: { key: "key", paths: ["public"], policy: "pull-push" } } } + + subject { build.cache } + + context 'when build has cache' do + before do + allow(build).to receive(:options).and_return(options) + end + + context 'when project has jobs_cache_index' do + before do + allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1) + end + + it { is_expected.to be_an(Array).and all(include(key: "key:1")) } + end + + context 'when project does not have jobs_cache_index' do + before do + allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(nil) + end + + it { is_expected.to eq([options[:cache]]) } + end + end + + context 'when build does not have cache' do + before do + allow(build).to receive(:options).and_return({}) + end + + it { is_expected.to eq([nil]) } + end + end + describe '#depends_on_builds' do let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') } let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') } diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb index e77745acbb7..805496e4a54 100644 --- a/spec/requests/api/jobs_spec.rb +++ b/spec/requests/api/jobs_spec.rb @@ -11,7 +11,7 @@ describe API::Jobs do ref: project.default_branch) end - let!(:job) { create(:ci_build, pipeline: pipeline) } + let!(:job) { create(:ci_build, :success, pipeline: pipeline) } let(:user) { create(:user) } let(:api_user) { user } @@ -443,7 +443,7 @@ describe API::Jobs do context 'user with :update_build persmission' do it 'cancels running or pending job' do expect(response).to have_gitlab_http_status(201) - expect(project.builds.first.status).to eq('canceled') + expect(project.builds.first.status).to eq('success') end end diff --git a/spec/services/reset_project_cache_service_spec.rb b/spec/services/reset_project_cache_service_spec.rb new file mode 100644 index 00000000000..de475d16586 --- /dev/null +++ b/spec/services/reset_project_cache_service_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe ResetProjectCacheService do + let(:project) { create(:project) } + let(:user) { create(:user) } + + subject { described_class.new(project, user).execute } + + context 'when project cache_index is nil' do + before do + project.jobs_cache_index = nil + end + + it 'sets project cache_index to one' do + expect { subject }.to change { project.reload.jobs_cache_index }.from(nil).to(1) + end + end + + context 'when project cache_index is a numeric value' do + before do + project.update_attributes(jobs_cache_index: 1) + end + + it 'increments project cache index' do + expect { subject }.to change { project.reload.jobs_cache_index }.by(1) + end + end +end diff --git a/vendor/gitignore/Eagle.gitignore b/vendor/gitignore/Eagle.gitignore index 9ced1260266..9afc324d6ae 100644 --- a/vendor/gitignore/Eagle.gitignore +++ b/vendor/gitignore/Eagle.gitignore @@ -4,6 +4,9 @@ *.s#? *.b#? *.l#? +*.b$? +*.s$? +*.l$? # Eagle project file # It contains a serial number and references to the file structure @@ -31,14 +34,19 @@ eagle.epf *.drl *.gpi *.pls +*.ger +*.gpi +*.xln *.drd *.drd.* +*.s#* +*.b#* + *.info *.eps # file locks introduced since 7.x *.lck - diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore index ce1c12cdb7a..0eb8a5e8571 100644 --- a/vendor/gitignore/Global/Eclipse.gitignore +++ b/vendor/gitignore/Global/Eclipse.gitignore @@ -23,6 +23,9 @@ local.properties # CDT-specific (C/C++ Development Tooling) .cproject +# CDT- autotools +.autotools + # Java annotation processor (APT) .factorypath diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore index 345e61ae3f2..a30eacf1d98 100644 --- a/vendor/gitignore/Global/JetBrains.gitignore +++ b/vendor/gitignore/Global/JetBrains.gitignore @@ -21,6 +21,7 @@ # CMake cmake-build-debug/ +cmake-build-release/ # Mongo Explorer plugin: .idea/**/mongoSettings.xml diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore index 7996ad5058e..d87a6bdbeeb 100644 --- a/vendor/gitignore/Global/Matlab.gitignore +++ b/vendor/gitignore/Global/Matlab.gitignore @@ -1,8 +1,3 @@ -##--------------------------------------------------- -## Remove autosaves generated by the MATLAB editor -## We have git for backups! -##--------------------------------------------------- - # Windows default autosave extension *.asv @@ -12,12 +7,19 @@ # Compiled MEX binaries (all platforms) *.mex* -# Simulink Code Generation +# Packaged app and toolbox files +*.mlappinstall +*.mltbx + +# Generated helpsearch folders +helpsearch*/ + +# Simulink code generation folders slprj/ sccprj/ -# Session info -octave-workspace - # Simulink autosave extension *.autosave + +# Octave session info +octave-workspace diff --git a/vendor/gitignore/Go.gitignore b/vendor/gitignore/Go.gitignore index ea58090bd21..f1c181ec9c5 100644 --- a/vendor/gitignore/Go.gitignore +++ b/vendor/gitignore/Go.gitignore @@ -1,5 +1,6 @@ # Binaries for programs and plugins *.exe +*.exe~ *.dll *.so *.dylib diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore index 97e28736892..d1bed128fa8 100644 --- a/vendor/gitignore/Node.gitignore +++ b/vendor/gitignore/Node.gitignore @@ -57,3 +57,5 @@ typings/ # dotenv environment variables file .env +# next.js build output +.next diff --git a/vendor/gitignore/Rails.gitignore b/vendor/gitignore/Rails.gitignore index 42aeb55000a..828ab1d556a 100644 --- a/vendor/gitignore/Rails.gitignore +++ b/vendor/gitignore/Rails.gitignore @@ -42,3 +42,7 @@ bower.json # Ignore Byebug command history file. .byebug_history + +# Ignore node_modules +node_modules/ + diff --git a/vendor/gitignore/Umbraco.gitignore b/vendor/gitignore/Umbraco.gitignore index b6b0743f62a..10fc2b4d825 100644 --- a/vendor/gitignore/Umbraco.gitignore +++ b/vendor/gitignore/Umbraco.gitignore @@ -16,8 +16,11 @@ # Don't ignore Umbraco packages (VisualStudio.gitignore mistakes this for a NuGet packages folder) # Make sure to include details from VisualStudio.gitignore BEFORE this -!**/App_Data/[Pp]ackages/ -!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages +!**/App_Data/[Pp]ackages/* +!**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/* # ImageProcessor DiskCache **/App_Data/cache/ + +# Ignore the Models Builder models out of date flag +**/App_Data/Models/ood.flag diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore index 6217e6c48e9..d3d5371b415 100644 --- a/vendor/gitignore/VisualStudio.gitignore +++ b/vendor/gitignore/VisualStudio.gitignore @@ -219,6 +219,10 @@ ClientBin/ *.publishsettings orleans.codegen.cs +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -313,3 +317,7 @@ OpenCover/ # Azure Stream Analytics local run output ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + diff --git a/vendor/gitignore/WordPress.gitignore b/vendor/gitignore/WordPress.gitignore index 97923503c4c..3b181ec0cf2 100644 --- a/vendor/gitignore/WordPress.gitignore +++ b/vendor/gitignore/WordPress.gitignore @@ -7,6 +7,7 @@ wp-content/blogs.dir/ wp-content/cache/ wp-content/upgrade/ wp-content/uploads/ +wp-content/mu-plugins/ wp-content/wp-cache-config.php wp-content/plugins/hello.php diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml index 06473fba8e1..75de266369d 100644 --- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml @@ -112,6 +112,19 @@ sast: - sast . artifacts: paths: [gl-sast-report.json] + +sast:container: + image: docker:latest + variables: + DOCKER_DRIVER: overlay2 + allow_failure: true + services: + - docker:dind + script: + - setup_docker + - sast_container + artifacts: + paths: [gl-sast-container-report.json] review: stage: review @@ -247,6 +260,18 @@ production: export CI_APPLICATION_TAG=$CI_COMMIT_SHA export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID} export TILLER_NAMESPACE=$KUBE_NAMESPACE + + function sast_container() { + docker run -d --name db arminc/clair-db:latest + docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1 + apk add -U wget ca-certificates + docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} + wget https://github.com/arminc/clair-scanner/releases/download/v6/clair-scanner_linux_386 + mv clair-scanner_linux_386 clair-scanner + chmod +x clair-scanner + touch clair-whitelist.yml + ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-sast-container-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true + } function codeclimate() { cc_opts="--env CODECLIMATE_CODE="$PWD" \ diff --git a/vendor/gitlab-ci-yml/Mono.gitlab-ci.yml b/vendor/gitlab-ci-yml/Mono.gitlab-ci.yml new file mode 100644 index 00000000000..3585f99760f --- /dev/null +++ b/vendor/gitlab-ci-yml/Mono.gitlab-ci.yml @@ -0,0 +1,42 @@ +# This is a simple gitlab continuous integration template (compatible with the shared runner provided on gitlab.com) +# using the official mono docker image to build a visual studio project. +# +# MyProject.sln +# MyProject\ +# MyProject\ +# MyProject.csproj (console application) +# MyProject.Test\ +# MyProject.Test.csproj (test library using nuget packages "NUnit" and "NUnit.ConsoleRunner") +# +# Please find the full example project here: +# https://gitlab.com/tobiaskoch/gitlab-ci-example-mono + +# see https://hub.docker.com/_/mono/ +image: mono:latest + +stages: + - test + - deploy + +before_script: + - nuget restore -NonInteractive + +release: + stage: deploy + only: + - master + artifacts: + paths: + - build/release/MyProject.exe + script: + # The output path is relative to the position of the csproj-file + - msbuild /p:Configuration="Release" /p:Platform="Any CPU" + /p:OutputPath="./../../build/release/" "MyProject.sln" + +debug: + stage: test + script: + # The output path is relative to the position of the csproj-file + - msbuild /p:Configuration="Debug" /p:Platform="Any CPU" + /p:OutputPath="./../../build/debug/" "MyProject.sln" + - mono packages/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe build/debug/MyProject.Test.dll
\ No newline at end of file diff --git a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml index 1463161a04b..cab087c48c7 100644 --- a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml @@ -20,4 +20,4 @@ image: "rust:latest" test:cargo: script: - rustc --version && cargo --version # Print version info for debugging - - cargo test --verbose --jobs 1 --release # Don't parallelise to make errors more readable + - cargo test --all --verbose diff --git a/vendor/licenses.csv b/vendor/licenses.csv index b6a5c2f81a0..e3ccf080f74 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -23,7 +23,7 @@ autoprefixer-rails,6.2.3,MIT axiom-types,0.1.1,MIT babosa,1.0.2,MIT base32,0.3.2,MIT -batch-loader,1.1.1,MIT +batch-loader,1.2.1,MIT bcrypt,3.1.11,MIT bcrypt_pbkdf,1.0.0,MIT bindata,2.4.1,ruby @@ -73,8 +73,9 @@ faraday_middleware,0.11.0.1,MIT faraday_middleware-multi_json,0.0.6,MIT fast_gettext,1.4.0,"MIT,ruby" ffi,1.9.18,New BSD -flipper,0.10.2,MIT -flipper-active_record,0.10.2,MIT +flipper,0.11.0,MIT +flipper-active_record,0.11.0,MIT +flipper-active_support_cache_store,0.11.0,MIT flowdock,0.7.1,MIT fog-aliyun,0.2.0,MIT fog-aws,1.4.0,MIT @@ -92,7 +93,7 @@ gemojione,3.3.0,MIT get_process_mem,0.2.0,MIT gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails_js,1.2.0,MIT -gitaly-proto,0.59.0,MIT +gitaly-proto,0.64.0,MIT github-linguist,4.7.6,MIT github-markup,1.6.1,MIT gitlab-flowdock-git-hook,1.0.1,MIT @@ -164,7 +165,7 @@ multi_xml,0.6.0,MIT multipart-post,2.0.0,MIT mustermann,1.0.0,MIT mustermann-grape,1.0.0,MIT -mysql2,0.4.5,MIT +mysql2,0.4.10,MIT net-ldap,0.16.0,MIT net-ssh,4.1.0,MIT netrc,0.11.0,MIT @@ -210,7 +211,7 @@ po_to_json,1.0.1,MIT posix-spawn,0.3.13,MIT premailer,1.10.4,New BSD premailer-rails,1.9.7,MIT -prometheus-client-mmap,0.7.0.beta43,Apache 2.0 +prometheus-client-mmap,0.7.0.beta44,Apache 2.0 public_suffix,3.0.0,MIT pyu-ruby-sasl,0.0.3.3,MIT rack,1.6.8,MIT @@ -237,11 +238,11 @@ re2,1.1.1,New BSD recaptcha,3.0.0,MIT recursive-open-struct,1.0.0,MIT redcarpet,3.4.0,MIT -redis,3.3.3,MIT +redis,3.3.5,MIT redis-actionpack,5.0.2,MIT redis-activesupport,5.0.4,MIT redis-namespace,1.5.2,MIT -redis-rack,2.0.3,MIT +redis-rack,2.0.4,MIT redis-rails,5.0.2,MIT redis-store,1.4.1,MIT representable,3.0.4,MIT @@ -273,7 +274,7 @@ select2-rails,3.5.9.3,MIT sentry-raven,2.5.3,Apache 2.0 settingslogic,2.0.9,MIT sexp_processor,4.9.0,MIT -sidekiq,5.0.4,LGPL +sidekiq,5.0.5,LGPL sidekiq-cron,0.6.0,MIT sidekiq-limit_fetch,3.4.0,MIT signet,0.7.3,Apache 2.0 |