Commit 5d1ec53b authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ee

parents 99af4d0f 962c0ce3
...@@ -8,6 +8,7 @@ require: ...@@ -8,6 +8,7 @@ require:
- rubocop-rspec - rubocop-rspec
AllCops: AllCops:
TargetRubyVersion: 2.5
TargetRailsVersion: 5.0 TargetRailsVersion: 5.0
Exclude: Exclude:
- 'vendor/**/*' - 'vendor/**/*'
...@@ -187,3 +188,8 @@ Cop/InjectEnterpriseEditionModule: ...@@ -187,3 +188,8 @@ Cop/InjectEnterpriseEditionModule:
Style/ReturnNil: Style/ReturnNil:
Enabled: true Enabled: true
# It isn't always safe to replace `=~` with `.match?`, especially when there are
# nil values on the left hand side
Performance/RegexpMatch:
Enabled: false
<script> <script>
import folderMixin from 'ee_else_ce/environments/mixins/environments_folder_view_mixin';
import environmentsMixin from '../mixins/environments_mixin'; import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin'; import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import StopEnvironmentModal from '../components/stop_environment_modal.vue'; import StopEnvironmentModal from '../components/stop_environment_modal.vue';
...@@ -8,7 +9,7 @@ export default { ...@@ -8,7 +9,7 @@ export default {
StopEnvironmentModal, StopEnvironmentModal,
}, },
mixins: [environmentsMixin, CIPaginationMixin], mixins: [environmentsMixin, CIPaginationMixin, folderMixin],
props: { props: {
endpoint: { endpoint: {
...@@ -27,28 +28,6 @@ export default { ...@@ -27,28 +28,6 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
// ee-only start
canaryDeploymentFeatureId: {
type: String,
required: true,
},
showCanaryDeploymentCallout: {
type: Boolean,
required: true,
},
userCalloutsPath: {
type: String,
required: true,
},
lockPromotionSvgPath: {
type: String,
required: true,
},
helpCanaryDeploymentsPath: {
type: String,
required: true,
},
// ee-only end
}, },
methods: { methods: {
successCallback(resp) { successCallback(resp) {
...@@ -63,7 +42,8 @@ export default { ...@@ -63,7 +42,8 @@ export default {
<div v-if="!isLoading" class="top-area"> <div v-if="!isLoading" class="top-area">
<h4 class="js-folder-name environments-folder-name"> <h4 class="js-folder-name environments-folder-name">
{{ s__('Environments|Environments') }} / <b>{{ folderName }}</b> {{ s__('Environments|Environments') }} /
<b>{{ folderName }}</b>
</h4> </h4>
<tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" /> <tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" />
......
import Vue from 'vue'; import Vue from 'vue';
import canaryCalloutMixin from 'ee_else_ce/environments/mixins/canary_callout_mixin';
import environmentsComponent from './components/environments_app.vue'; import environmentsComponent from './components/environments_app.vue';
import { parseBoolean } from '../lib/utils/common_utils'; import { parseBoolean } from '../lib/utils/common_utils';
import Translate from '../vue_shared/translate'; import Translate from '../vue_shared/translate';
// ee-only start
import CanaryCalloutMixin from 'ee/environments/mixins/canary_callout_mixin'; // eslint-disable-line import/order
// ee-only end
Vue.use(Translate); Vue.use(Translate);
export default () => export default () =>
...@@ -15,9 +12,7 @@ export default () => ...@@ -15,9 +12,7 @@ export default () =>
components: { components: {
environmentsComponent, environmentsComponent,
}, },
// ee-only start mixins: [canaryCalloutMixin],
mixins: [CanaryCalloutMixin],
// ee-only end
data() { data() {
const environmentsData = document.querySelector(this.$options.el).dataset; const environmentsData = document.querySelector(this.$options.el).dataset;
...@@ -39,13 +34,7 @@ export default () => ...@@ -39,13 +34,7 @@ export default () =>
cssContainerClass: this.cssContainerClass, cssContainerClass: this.cssContainerClass,
canCreateEnvironment: this.canCreateEnvironment, canCreateEnvironment: this.canCreateEnvironment,
canReadEnvironment: this.canReadEnvironment, canReadEnvironment: this.canReadEnvironment,
// ee-only start ...this.canaryCalloutProps,
canaryDeploymentFeatureId: this.canaryDeploymentFeatureId,
showCanaryDeploymentCallout: this.showCanaryDeploymentCallout,
userCalloutsPath: this.userCalloutsPath,
lockPromotionSvgPath: this.lockPromotionSvgPath,
helpCanaryDeploymentsPath: this.helpCanaryDeploymentsPath,
// ee-only end
}, },
}); });
}, },
......
export default {
computed: {
canaryCalloutProps() {},
},
};
export default {
props: {
canaryDeploymentFeatureId: {
type: String,
required: false,
default: '',
},
showCanaryDeploymentCallout: {
type: Boolean,
required: false,
default: false,
},
userCalloutsPath: {
type: String,
required: false,
default: '',
},
lockPromotionSvgPath: {
type: String,
required: false,
default: '',
},
helpCanaryDeploymentsPath: {
type: String,
required: false,
default: '',
},
},
};
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
*/ */
import _ from 'underscore'; import _ from 'underscore';
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import EnvironmentsStore from 'ee_else_ce/environments/stores/environments_store';
import Poll from '../../lib/utils/poll'; import Poll from '../../lib/utils/poll';
import { getParameterByName } from '../../lib/utils/common_utils'; import { getParameterByName } from '../../lib/utils/common_utils';
import { s__ } from '../../locale'; import { s__ } from '../../locale';
import Flash from '../../flash'; import Flash from '../../flash';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import EnvironmentsStore from '../stores/environments_store';
import EnvironmentsService from '../services/environments_service'; import EnvironmentsService from '../services/environments_service';
import tablePagination from '../../vue_shared/components/table_pagination.vue'; import tablePagination from '../../vue_shared/components/table_pagination.vue';
import environmentTable from '../components/environments_table.vue'; import environmentTable from '../components/environments_table.vue';
......
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { setDeployBoard } from 'ee_else_ce/environments/stores/helpers';
// ee-only
import { CLUSTER_TYPE } from '~/clusters/constants';
/** /**
* Environments Store. * Environments Store.
* *
...@@ -74,41 +73,12 @@ export default class EnvironmentsStore { ...@@ -74,41 +73,12 @@ export default class EnvironmentsStore {
filtered = Object.assign(filtered, env); filtered = Object.assign(filtered, env);
} }
if ( filtered = setDeployBoard(oldEnvironmentState, filtered);
filtered.size === 1 &&
filtered.rollout_status &&
filtered.cluster_type !== CLUSTER_TYPE.GROUP
) {
filtered = Object.assign({}, filtered, {
hasDeployBoard: true,
isDeployBoardVisible:
oldEnvironmentState.isDeployBoardVisible === false
? oldEnvironmentState.isDeployBoardVisible
: true,
deployBoardData:
filtered.rollout_status.status === 'found' ? filtered.rollout_status : {},
isLoadingDeployBoard: filtered.rollout_status.status === 'loading',
isEmptyDeployBoard: filtered.rollout_status.status === 'not_found',
});
}
return filtered; return filtered;
}); });
this.state.environments = filteredEnvironments; this.state.environments = filteredEnvironments;
// ee-only start
/**
* Add the canary callout banner underneath the second environment listed.
*
* If there is only one environment, then add to it underneath the first.
*/
if (this.state.environments.length >= 2) {
this.state.environments[1].showCanaryCallout = true;
} else if (this.state.environments.length === 1) {
this.state.environments[0].showCanaryCallout = true;
}
// ee-only end
return filteredEnvironments; return filteredEnvironments;
} }
...@@ -221,27 +191,4 @@ export default class EnvironmentsStore { ...@@ -221,27 +191,4 @@ export default class EnvironmentsStore {
return environments.filter(env => env.isFolder && env.isOpen); return environments.filter(env => env.isFolder && env.isOpen);
} }
/**
* Toggles deploy board visibility for the provided environment ID.
*
* @param {Object} environment
* @return {Array}
*/
toggleDeployBoard(environmentID) {
const environments = this.state.environments.slice();
this.state.environments = environments.map(env => {
let updated = Object.assign({}, env);
if (env.id === environmentID) {
updated = Object.assign({}, updated, {
isDeployBoardVisible: !env.isDeployBoardVisible,
});
}
return updated;
});
return this.state.environments;
}
} }
/**
* Deploy boards are EE only.
*
* @param {Object} environment
* @returns {Object}
*/
// eslint-disable-next-line import/prefer-default-export
export const setDeployBoard = (oldEnvironmentState, environment) => environment;
import initGroupDetails from '../shared/group_details';
document.addEventListener('DOMContentLoaded', () => {
initGroupDetails('details');
});
/* eslint-disable no-new */
import { getPagePath } from '~/lib/utils/common_utils';
import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
export default function initGroupDetails(actionName = 'show') {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
const paths = window.location.pathname.split('/');
const subpath = paths[paths.length - 1];
let action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
if (actionName && action === actionName) {
action = 'show'; // 'show' resets GroupTabs to default action through base class
}
new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
new NotificationsForm();
notificationsDropdown();
new ProjectsList();
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
}
/* eslint-disable no-new */ import initGroupDetails from '../shared/group_details';
import { getPagePath } from '~/lib/utils/common_utils';
import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); initGroupDetails();
const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
const paths = window.location.pathname.split('/');
const subpath = paths[paths.length - 1];
const action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
new NotificationsForm();
notificationsDropdown();
new ProjectsList();
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
}); });
...@@ -91,6 +91,7 @@ export default class UserTabs { ...@@ -91,6 +91,7 @@ export default class UserTabs {
this.actions = Object.keys(this.loaded); this.actions = Object.keys(this.loaded);
this.bindEvents(); this.bindEvents();
// TODO: refactor to make this configurable via constructor params with a default value of 'show'
if (this.action === 'show') { if (this.action === 'show') {
this.action = this.defaultAction; this.action = this.defaultAction;
} }
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import stageColumnMixin from 'ee_else_ce/pipelines/mixins/stage_column_mixin';
import JobItem from './job_item.vue'; import JobItem from './job_item.vue';
import JobGroupDropdown from './job_group_dropdown.vue'; import JobGroupDropdown from './job_group_dropdown.vue';
...@@ -8,6 +9,7 @@ export default { ...@@ -8,6 +9,7 @@ export default {
JobItem, JobItem,
JobGroupDropdown, JobGroupDropdown,
}, },
mixins: [stageColumnMixin],
props: { props: {
title: { title: {
type: String, type: String,
...@@ -27,19 +29,11 @@ export default { ...@@ -27,19 +29,11 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
hasTriggeredBy: {
type: Boolean,
required: false,
default: false,
},
}, },
methods: { methods: {
groupId(group) { groupId(group) {
return `ci-badge-${_.escape(group.name)}`; return `ci-badge-${_.escape(group.name)}`;
}, },
buildConnnectorClass(index) {
return index === 0 && (!this.isFirstColumn || this.hasTriggeredBy) ? 'left-connector' : '';
},
pipelineActionRequestComplete() { pipelineActionRequestComplete() {
this.$emit('refreshPipelineGraph'); this.$emit('refreshPipelineGraph');
}, },
......
export default {
methods: {
clickTriggeredByPipeline() {},
clickTriggeredPipeline() {},
},
};
export default {
methods: {
buildConnnectorClass(index) {
return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
},
},
};
...@@ -2,11 +2,11 @@ import Vue from 'vue'; ...@@ -2,11 +2,11 @@ import Vue from 'vue';
import Flash from '~/flash'; import Flash from '~/flash';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import { __ } from '~/locale'; import { __ } from '~/locale';
import pipelineGraph from 'ee/pipelines/components/graph/graph_component.vue'; import pipelineGraph from 'ee_else_ce/pipelines/components/graph/graph_component.vue';
import GraphEEMixin from 'ee_else_ce/pipelines/mixins/graph_pipeline_bundle_mixin';
import PipelinesMediator from './pipeline_details_mediator'; import PipelinesMediator from './pipeline_details_mediator';
import pipelineHeader from './components/header_component.vue'; import pipelineHeader from './components/header_component.vue';
import eventHub from './event_hub'; import eventHub from './event_hub';
import GraphEEMixin from 'ee/pipelines/mixins/graph_pipeline_bundle_mixin'; // eslint-disable-line import/order
Vue.use(Translate); Vue.use(Translate);
......
...@@ -376,6 +376,7 @@ img.emoji { ...@@ -376,6 +376,7 @@ img.emoji {
.prepend-top-default { margin-top: $gl-padding !important; } .prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-16 { margin-top: 16px; } .prepend-top-16 { margin-top: 16px; }
.prepend-top-20 { margin-top: 20px; } .prepend-top-20 { margin-top: 20px; }
.prepend-top-32 { margin-top: 32px; }
.prepend-left-4 { margin-left: 4px; } .prepend-left-4 { margin-left: 4px; }
.prepend-left-5 { margin-left: 5px; } .prepend-left-5 { margin-left: 5px; }
.prepend-left-8 { margin-left: 8px; } .prepend-left-8 { margin-left: 8px; }
...@@ -383,6 +384,7 @@ img.emoji { ...@@ -383,6 +384,7 @@ img.emoji {
.prepend-left-15 { margin-left: 15px; } .prepend-left-15 { margin-left: 15px; }
.prepend-left-default { margin-left: $gl-padding; } .prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; } .prepend-left-20 { margin-left: 20px; }
.prepend-left-32 { margin-left: 32px; }
.append-right-4 { margin-right: 4px; } .append-right-4 { margin-right: 4px; }
.append-right-5 { margin-right: 5px; } .append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; } .append-right-8 { margin-right: 8px; }
...@@ -390,6 +392,7 @@ img.emoji { ...@@ -390,6 +392,7 @@ img.emoji {
.append-right-15 { margin-right: 15px; } .append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; } .append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; } .append-right-20 { margin-right: 20px; }
.prepend-right-32 { margin-right: 32px; }
.append-bottom-0 { margin-bottom: 0; } .append-bottom-0 { margin-bottom: 0; }
.append-bottom-4 { margin-bottom: $gl-padding-4; } .append-bottom-4 { margin-bottom: $gl-padding-4; }
.append-bottom-5 { margin-bottom: 5px; } .append-bottom-5 { margin-bottom: 5px; }
...@@ -398,6 +401,7 @@ img.emoji { ...@@ -398,6 +401,7 @@ img.emoji {
.append-bottom-15 { margin-bottom: 15px; } .append-bottom-15 { margin-bottom: 15px; }
.append-bottom-20 { margin-bottom: 20px; } .append-bottom-20 { margin-bottom: 20px; }
.append-bottom-default { margin-bottom: $gl-padding; } .append-bottom-default { margin-bottom: $gl-padding; }
.prepend-bottom-32 { margin-bottom: 32px; }
.inline { display: inline-block; } .inline { display: inline-block; }
.block { display: block; } .block { display: block; }
.flex { display: flex; } .flex { display: flex; }
......
...@@ -693,10 +693,6 @@ ...@@ -693,10 +693,6 @@
} }
} }
.project-empty-note-panel {
border-bottom: 1px solid $border-color;
}
.project-stats, .project-stats,
.project-buttons { .project-buttons {
.scrolling-tabs-container { .scrolling-tabs-container {
......
...@@ -58,11 +58,24 @@ class GroupsController < Groups::ApplicationController ...@@ -58,11 +58,24 @@ class GroupsController < Groups::ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html format.html do
render_show_html
end
format.atom do format.atom do
load_events render_details_view_atom
render layout: 'xml.atom' end
end
end
def details
respond_to do |format|
format.html do
render_details_html
end
format.atom do
render_details_view_atom
end end
end end
end end
...@@ -119,6 +132,19 @@ class GroupsController < Groups::ApplicationController ...@@ -119,6 +132,19 @@ class GroupsController < Groups::ApplicationController
protected protected
def render_show_html
render 'groups/show'
end
def render_details_html
render 'groups/show'
end
def render_details_view_atom
load_events
render layout: 'xml.atom', template: 'groups/show'
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def authorize_create_group! def authorize_create_group!
allowed = if params[:parent_id].present? allowed = if params[:parent_id].present?
...@@ -178,8 +204,8 @@ class GroupsController < Groups::ApplicationController ...@@ -178,8 +204,8 @@ class GroupsController < Groups::ApplicationController
.includes(:namespace) .includes(:namespace)
@events = EventCollection @events = EventCollection
.new(@projects, offset: params[:offset].to_i, filter: event_filter) .new(@projects, offset: params[:offset].to_i, filter: event_filter)
.to_a .to_a
Events::RenderService Events::RenderService
.new(current_user) .new(current_user)
......
...@@ -4,6 +4,7 @@ module GroupsHelper ...@@ -4,6 +4,7 @@ module GroupsHelper
def group_overview_nav_link_paths def group_overview_nav_link_paths
%w[ %w[
groups#show groups#show
groups#details
groups#activity groups#activity
groups#subgroups groups#subgroups
analytics#show analytics#show
......
...@@ -286,13 +286,11 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -286,13 +286,11 @@ class MergeRequestDiff < ActiveRecord::Base
return yield(@external_diff_file) if @external_diff_file return yield(@external_diff_file) if @external_diff_file
external_diff.open do |file| external_diff.open do |file|
begin @external_diff_file = file
@external_diff_file = file
yield(@external_diff_file) yield(@external_diff_file)
ensure ensure
@external_diff_file = nil @external_diff_file = nil
end
end end
end end
......
...@@ -23,6 +23,6 @@ class MergeRequestDiffFile < ActiveRecord::Base ...@@ -23,6 +23,6 @@ class MergeRequestDiffFile < ActiveRecord::Base
super super
end end
binary? ? content.unpack('m0').first : content binary? ? content.unpack1('m0') : content
end end
end end
...@@ -1209,11 +1209,9 @@ class Project < ActiveRecord::Base ...@@ -1209,11 +1209,9 @@ class Project < ActiveRecord::Base
def repo_exists? def repo_exists?
strong_memoize(:repo_exists) do strong_memoize(:repo_exists) do
begin repository.exists?
repository.exists? rescue
rescue false
false
end
end end
end end
......
...@@ -205,12 +205,10 @@ class JiraService < IssueTrackerService ...@@ -205,12 +205,10 @@ class JiraService < IssueTrackerService
# if any transition fails it will log the error message and stop the transition sequence # if any transition fails it will log the error message and stop the transition sequence
def transition_issue(issue) def transition_issue(issue)
jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).each do |transition_id| jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).each do |transition_id|
begin issue.transitions.build.save!(transition: { id: transition_id })
issue.transitions.build.save!(transition: { id: transition_id }) rescue => error
rescue => error log_error("Issue transition failed", error: error.message, client_url: client_url)
log_error("Issue transition failed", error: error.message, client_url: client_url) return false
return false
end
end end
end end
......
...@@ -265,16 +265,14 @@ class Repository ...@@ -265,16 +265,14 @@ class Repository
# to avoid unnecessary syncing. # to avoid unnecessary syncing.
def keep_around(*shas) def keep_around(*shas)
shas.each do |sha| shas.each do |sha|
begin next unless sha.present? && commit_by(oid: sha)
next unless sha.present? && commit_by(oid: sha)
next if kept_around?(sha) next if kept_around?(sha)
# This will still fail if the file is corrupted (e.g. 0 bytes) # This will still fail if the file is corrupted (e.g. 0 bytes)
raw_repository.write_ref(keep_around_ref_name(sha), sha) raw_repository.write_ref(keep_around_ref_name(sha), sha)
rescue Gitlab::Git::CommandError => ex rescue Gitlab::Git::CommandError => ex
Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}" Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
end
end end
end end
......
...@@ -26,16 +26,14 @@ class UserInteractedProject < ActiveRecord::Base ...@@ -26,16 +26,14 @@ class UserInteractedProject < ActiveRecord::Base
cached_exists?(attributes) do cached_exists?(attributes) do
transaction(requires_new: true) do transaction(requires_new: true) do
begin where(attributes).select(1).first || create!(attributes)
where(attributes).select(1).first || create!(attributes) true # not caching the whole record here for now
true # not caching the whole record here for now rescue ActiveRecord::RecordNotUnique
rescue ActiveRecord::RecordNotUnique # Note, above queries are not atomic and prone
# Note, above queries are not atomic and prone # to race conditions (similar like #find_or_create!).
# to race conditions (similar like #find_or_create!). # In the case where we hit this, the record we want
# In the case where we hit this, the record we want # already exists - shortcut and return.
# already exists - shortcut and return. true
true
end
end end
end end
end end
......
...@@ -42,11 +42,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -42,11 +42,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def empty_repo_statistics_anchors def empty_repo_statistics_anchors
[ [
license_anchor_data, license_anchor_data
commits_anchor_data,
branches_anchor_data,
tags_anchor_data,
files_anchor_data
].compact.select { |item| item.is_link } ].compact.select { |item| item.is_link }
end end
...@@ -55,9 +51,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated ...@@ -55,9 +51,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
new_file_anchor_data, new_file_anchor_data,
readme_anchor_data, readme_anchor_data,
changelog_anchor_data, changelog_anchor_data,
contribution_guide_anchor_data, contribution_guide_anchor_data
autodevops_anchor_data,
kubernetes_cluster_anchor_data
].compact.reject { |item| item.is_link } ].compact.reject { |item| item.is_link }
end end
......
...@@ -9,16 +9,14 @@ class DetailedStatusEntity < Grape::Entity ...@@ -9,16 +9,14 @@ class DetailedStatusEntity < Grape::Entity
expose :details_path expose :details_path
expose :illustration do |status| expose :illustration do |status|
begin illustration = {
illustration = { image: ActionController::Base.helpers.image_path(status.illustration[:image])
image: ActionController::Base.helpers.image_path(status.illustration[:image]) }
} illustration = status.illustration.merge(illustration)
illustration = status.illustration.merge(illustration)
illustration illustration
rescue NotImplementedError rescue NotImplementedError
# ignored # ignored
end
end end
expose :favicon do |status| expose :favicon do |status|
......
...@@ -42,17 +42,15 @@ module Projects ...@@ -42,17 +42,15 @@ module Projects
def parse_response_links(objects_response) def parse_response_links(objects_response)
objects_response.each_with_object([]) do |entry, link_list| objects_response.each_with_object([]) do |entry, link_list|
begin link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
raise DownloadLinkNotFound unless link raise DownloadLinkNotFound unless link
link_list << LfsDownloadObject.new(oid: entry['oid'], link_list << LfsDownloadObject.new(oid: entry['oid'],
size: entry['size'], size: entry['size'],
link: add_credentials(link)) link: add_credentials(link))
rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError
log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.") log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.")
end
end end
end end
......
...@@ -75,17 +75,15 @@ module Projects ...@@ -75,17 +75,15 @@ module Projects
create_tmp_storage_dir create_tmp_storage_dir
File.open(tmp_filename, 'wb') do |file| File.open(tmp_filename, 'wb') do |file|
begin yield file
yield file rescue StandardError => e
rescue StandardError => e # If the lfs file is successfully downloaded it will be removed
# If the lfs file is successfully downloaded it will be removed # when it is added to the project's lfs files.
# when it is added to the project's lfs files. # Nevertheless if any excetion raises the file would remain
# Nevertheless if any excetion raises the file would remain # in the file system. Here we ensure to remove it
# in the file system. Here we ensure to remove it File.unlink(file) if File.exist?(file)
File.unlink(file) if File.exist?(file)
raise e
raise e
end
end end
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
class ShaValidator < ActiveModel::EachValidator class ShaValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
return if value.blank? || value.match(/\A\h{40}\z/) return if value.blank? || Commit.valid_hash?(value)
record.errors.add(attribute, 'is not a valid SHA') record.errors.add(attribute, 'is not a valid SHA')
end end
......
...@@ -38,4 +38,4 @@ ...@@ -38,4 +38,4 @@
%li= link_to _('New project'), new_project_path, class: 'qa-global-new-project-link' %li= link_to _('New project'), new_project_path, class: 'qa-global-new-project-link'
- if current_user.can_create_group? - if current_user.can_create_group?
%li= link_to _('New group'), new_group_path %li= link_to _('New group'), new_group_path
%li= link_to _('New snippet'), new_snippet_path %li= link_to _('New snippet'), new_snippet_path, class: 'qa-global-new-snippet-link'
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
- if dashboard_nav_link?(:snippets) - if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do = nav_link(controller: 'dashboard/snippets', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do
= link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets qa-snippets-link', title: _('Snippets') do
= _('Snippets') = _('Snippets')
- if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets]) - if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
......
...@@ -20,13 +20,14 @@ ...@@ -20,13 +20,14 @@
= _('Overview') = _('Overview')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do = nav_link(path: ['groups#show', 'groups#details', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
= link_to group_path(@group) do = link_to group_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
= _('Overview') = _('Overview')
%li.divider.fly-out-top-item %li.divider.fly-out-top-item
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: _('Group details') do = nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to details_group_path(@group), title: _('Group details') do
%span %span
= _('Details') = _('Details')
...@@ -40,9 +41,9 @@ ...@@ -40,9 +41,9 @@
- if group_sidebar_link?(:contribution_analytics) - if group_sidebar_link?(:contribution_analytics)
= nav_link(path: 'analytics#show') do = nav_link(path: 'analytics#show') do
= link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do = link_to group_analytics_path(@group), title: _('Contribution Analytics'), data: { placement: 'right' } do
%span %span
Contribution Analytics = _('Contribution Analytics')
= render_if_exists "layouts/nav/ee/epic_link", group: @group = render_if_exists "layouts/nav/ee/epic_link", group: @group
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
= render 'shared/no_ssh' = render 'shared/no_ssh'
= render 'shared/no_password' = render 'shared/no_password'
= render 'shared/shared_runners_minutes_limit', project: project = render 'shared/shared_runners_minutes_limit', project: project
= render 'shared/auto_devops_implicitly_enabled_banner', project: project - unless project.empty_repo?
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
- if project.above_size_limit? - if project.above_size_limit?
= render 'above_size_limit_warning' = render 'above_size_limit_warning'
...@@ -57,7 +57,10 @@ ...@@ -57,7 +57,10 @@
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
%nav.project-stats %nav.project-stats
.nav-links.quick-links .nav-links.quick-links
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout) - if @project.empty_repo?
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
- else
= render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
.home-panel-home-desc.mt-1 .home-panel-home-desc.mt-1
- if @project.description.present? - if @project.description.present?
......
.file-header-content .file-header-content
= blob_icon blob.mode, blob.name = blob_icon blob.mode, blob.name
%strong.file-title-name %strong.file-title-name.qa-file-title-name
= blob.name = blob.name
= copy_file_path_button(blob.path) = copy_file_path_button(blob.path)
......
...@@ -7,89 +7,64 @@ ...@@ -7,89 +7,64 @@
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] } %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "home_panel" = render "home_panel"
.project-empty-note-panel %h4.prepend-top-0.append-bottom-8
%h4.append-bottom-20 = _('The repository for this project is empty')
= _('The repository for this project is empty')
- if @project.can_current_user_push_code? - if @project.can_current_user_push_code?
%p %p.append-bottom-0
- link_to_cli = link_to _('command line instructions'), '#repo-command-line-instructions' = _('You can create files directly in GitLab using one of the following options.')
= _('If you already have files you can push them using the %{link_to_cli} below.').html_safe % { link_to_cli: link_to_cli }
%p
%em
- link_to_protected_branches = link_to _('Learn more about protected branches'), help_page_path('user/project/protected_branches')
= _('Note that the master branch is automatically protected. %{link_to_protected_branches}').html_safe % { link_to_protected_branches: link_to_protected_branches }
%hr
%p
- link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'))
- link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), new_project_cluster_path(@project))
= s_('AutoDevOps|You can automatically build and test your application if you %{link_to_auto_devops_settings} for this project. You can automatically deploy it as well, if you %{link_to_add_kubernetes_cluster}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
%hr .project-buttons.qa-quick-actions
%p = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
= _('Otherwise it is recommended you start with one of the options below.')
.prepend-top-20
%nav.project-buttons
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller.qa-quick-actions
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
.nav-links.scrolling-tabs.quick-links
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
%div .empty-wrapper.prepend-top-32
.prepend-top-20 %h3#repo-command-line-instructions.page-title-empty
.empty_wrapper = _('Command line instructions')
%h3#repo-command-line-instructions.page-title-empty %p
= _('Command line instructions') = _('You can also upload existing files from your computer using the instructions below.')
.git-empty.js-git-empty .git-empty.js-git-empty
%fieldset %fieldset
%h5= _('Git global setup') %h5= _('Git global setup')
%pre.bg-light %pre.bg-light
:preserve :preserve
git config --global user.name "#{h git_user_name}" git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}" git config --global user.email "#{h git_user_email}"
%fieldset
%h5= _('Create a new repository')
%pre.bg-light
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
cd #{h @project.path}
touch README.md
git add README.md
git commit -m "add README"
- if @project.can_current_user_push_to_default_branch?
%span><
git push -u origin master
%fieldset %fieldset
%h5= _('Existing folder') %h5= _('Create a new repository')
%pre.bg-light %pre.bg-light
:preserve :preserve
cd existing_folder git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
git init cd #{h @project.path}
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} touch README.md
git add . git add README.md
git commit -m "Initial commit" git commit -m "add README"
- if @project.can_current_user_push_to_default_branch? - if @project.can_current_user_push_to_default_branch?
%span>< %span><
git push -u origin master git push -u origin master
%fieldset %fieldset
%h5= _('Existing Git repository') %h5= _('Push an existing folder')
%pre.bg-light %pre.bg-light
:preserve :preserve
cd existing_repo cd existing_folder
git remote rename origin old-origin git init
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- if @project.can_current_user_push_to_default_branch? git add .
%span>< git commit -m "Initial commit"
git push -u origin --all - if @project.can_current_user_push_to_default_branch?
git push -u origin --tags %span><
git push -u origin master
- if can? current_user, :remove_project, @project %fieldset
.prepend-top-20 %h5= _('Push an existing Git repository')
= link_to _('Remove project'), [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right" %pre.bg-light
:preserve
cd existing_repo
git remote rename origin old-origin
git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- if @project.can_current_user_push_to_default_branch?
%span><
git push -u origin --all
git push -u origin --tags
...@@ -24,6 +24,6 @@ ...@@ -24,6 +24,6 @@
.input-group-append .input-group-append
= clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard") = clipboard_button(target: '#project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= geo_button(modal_target: '#modal-geo-info') = geo_button(modal_target: '#modal-geo-info') if Gitlab::Geo.secondary?
= render 'shared/geo_info_modal', project: project if Gitlab::Geo.secondary? = render 'shared/geo_info_modal', project: project if Gitlab::Geo.secondary?
.file-content.code.js-syntax-highlight .file-content.code.js-syntax-highlight.qa-file-content
.line-numbers .line-numbers
- if blob.data.present? - if blob.data.present?
- link_icon = icon('link') - link_icon = icon('link')
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.form-group.row .form-group.row
= f.label :title, class: 'col-form-label col-sm-2' = f.label :title, class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
= f.text_field :title, class: 'form-control', required: true, autofocus: true = f.text_field :title, class: 'form-control qa-snippet-title', required: true, autofocus: true
= render 'shared/form_elements/description', model: @snippet, project: @project, form: f = render 'shared/form_elements/description', model: @snippet, project: @project, form: f
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.col-sm-10 .col-sm-10
.file-holder.snippet .file-holder.snippet
.js-file-title.file-title .js-file-title.file-title
= f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name' = f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name qa-snippet-file-name'
.file-content.code .file-content.code
%pre#editor= @snippet.content %pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content' = f.hidden_field :content, class: 'snippet-file-content'
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
.form-actions .form-actions
- if @snippet.new_record? - if @snippet.new_record?
= f.submit 'Create snippet', class: "btn-success btn" = f.submit 'Create snippet', class: "btn-success btn qa-create-snippet-button"
- else - else
= f.submit 'Save changes', class: "btn-success btn" = f.submit 'Save changes', class: "btn-success btn"
......
.detail-page-header .detail-page-header
.detail-page-header-body .detail-page-header-body
.snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } } .snippet-box.qa-snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } }
%span.sr-only %span.sr-only
= visibility_level_label(@snippet.visibility_level) = visibility_level_label(@snippet.visibility_level)
= visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_icon(@snippet.visibility_level, fw: false)
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
= render "snippets/actions" = render "snippets/actions"
.snippet-header.limited-header-width .snippet-header.limited-header-width
%h2.snippet-title.prepend-top-0.append-bottom-0 %h2.snippet-title.prepend-top-0.append-bottom-0.qa-snippet-title
= markdown_field(@snippet, :title) = markdown_field(@snippet, :title)
- if @snippet.description.present? - if @snippet.description.present?
.description .description.qa-snippet-description
.wiki .wiki
= markdown_field(@snippet, :description) = markdown_field(@snippet, :description)
%textarea.hidden.js-task-list-field %textarea.hidden.js-task-list-field
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
.embed-snippet .embed-snippet
.input-group .input-group
.input-group-prepend .input-group-prepend
%button.btn.btn-svg.embed-toggle.input-group-text{ 'data-toggle': 'dropdown', type: 'button' } %button.btn.btn-svg.embed-toggle.input-group-text.qa-embed-type{ 'data-toggle': 'dropdown', type: 'button' }
%span.js-embed-action= _("Embed") %span.js-embed-action= _("Embed")
= sprite_icon('angle-down', size: 12, css_class: 'caret-down') = sprite_icon('angle-down', size: 12, css_class: 'caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list %ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list
......
...@@ -25,11 +25,9 @@ module WaitableWorker ...@@ -25,11 +25,9 @@ module WaitableWorker
failed = [] failed = []
args_list.each do |args| args_list.each do |args|
begin new.perform(*args)
new.perform(*args) rescue
rescue failed << args
failed << args
end
end end
bulk_perform_async(failed) if failed.present? bulk_perform_async(failed) if failed.present?
......
...@@ -20,11 +20,9 @@ class CreateGpgSignatureWorker ...@@ -20,11 +20,9 @@ class CreateGpgSignatureWorker
# This calculates and caches the signature in the database # This calculates and caches the signature in the database
commits.each do |commit| commits.each do |commit|
begin Gitlab::Gpg::Commit.new(commit).signature
Gitlab::Gpg::Commit.new(commit).signature rescue => e
rescue => e Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}")
Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}")
end
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -52,24 +52,22 @@ class EmailsOnPushWorker ...@@ -52,24 +52,22 @@ class EmailsOnPushWorker
end end
valid_recipients(recipients).each do |recipient| valid_recipients(recipients).each do |recipient|
begin send_email(
send_email( recipient,
recipient, project_id,
project_id, author_id: author_id,
author_id: author_id, ref: ref,
ref: ref, action: action,
action: action, compare: compare,
compare: compare, reverse_compare: reverse_compare,
reverse_compare: reverse_compare, diff_refs: diff_refs,
diff_refs: diff_refs, send_from_committer_email: send_from_committer_email,
send_from_committer_email: send_from_committer_email, disable_diffs: disable_diffs
disable_diffs: disable_diffs )
)
# These are input errors and won't be corrected even if Sidekiq retries
# These are input errors and won't be corrected even if Sidekiq retries rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e logger.info("Failed to send e-mail for project '#{project.full_name}' to #{recipient}: #{e}")
logger.info("Failed to send e-mail for project '#{project.full_name}' to #{recipient}: #{e}")
end
end end
ensure ensure
@email = nil @email = nil
......
...@@ -126,11 +126,9 @@ module ObjectStorage ...@@ -126,11 +126,9 @@ module ObjectStorage
def process_uploader(uploader) def process_uploader(uploader)
MigrationResult.new(uploader.upload).tap do |result| MigrationResult.new(uploader.upload).tap do |result|
begin uploader.migrate!(@to_store)
uploader.migrate!(@to_store) rescue => e
rescue => e result.error = e
result.error = e
end
end end
end end
end end
......
...@@ -8,16 +8,15 @@ class PipelineScheduleWorker ...@@ -8,16 +8,15 @@ class PipelineScheduleWorker
def perform def perform
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now) Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule| .preload(:owner, :project).find_each do |schedule|
begin
Ci::CreatePipelineService.new(schedule.project, Ci::CreatePipelineService.new(schedule.project,
schedule.owner, schedule.owner,
ref: schedule.ref) ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule) .execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule)
rescue => e rescue => e
error(schedule, e) error(schedule, e)
ensure ensure
schedule.schedule_next_run! schedule.schedule_next_run!
end
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -6,11 +6,9 @@ class RemoveExpiredMembersWorker ...@@ -6,11 +6,9 @@ class RemoveExpiredMembersWorker
def perform def perform
Member.expired.find_each do |member| Member.expired.find_each do |member|
begin Members::DestroyService.new.execute(member, skip_authorization: true)
Members::DestroyService.new.execute(member, skip_authorization: true) rescue => ex
rescue => ex logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
end
end end
end end
end end
---
title: 'Project: Improve empty repository state UI'
merge_request: 26024
author:
type: other
---
title: Explicitly set master_auth setting to enable basic auth and client certificate
for new GKE clusters
merge_request: 26018
author:
type: other
---
title: Fix bug in BitBucket imports with SHA shorter than 40 chars
merge_request: 26050
author:
type: fixed
...@@ -3,6 +3,6 @@ HealthCheck.setup do |config| ...@@ -3,6 +3,6 @@ HealthCheck.setup do |config|
config.full_checks = %w(database migrations cache) config.full_checks = %w(database migrations cache)
config.add_custom_check('geo') do config.add_custom_check('geo') do
Gitlab::Geo::HealthCheck.perform_checks Gitlab::Geo::HealthCheck.new.perform_checks
end end
end end
...@@ -14,10 +14,8 @@ module Rack ...@@ -14,10 +14,8 @@ module Rack
end end
gitlab_trusted_proxies = Array(Gitlab.config.gitlab.trusted_proxies).map do |proxy| gitlab_trusted_proxies = Array(Gitlab.config.gitlab.trusted_proxies).map do |proxy|
begin IPAddr.new(proxy)
IPAddr.new(proxy) rescue IPAddr::InvalidAddressError
rescue IPAddr::InvalidAddressError
end
end.compact end.compact
Rails.application.config.action_dispatch.trusted_proxies = ( Rails.application.config.action_dispatch.trusted_proxies = (
......
...@@ -119,6 +119,15 @@ module.exports = function(config) { ...@@ -119,6 +119,15 @@ module.exports = function(config) {
reporters: ['mocha'], reporters: ['mocha'],
webpack: webpackConfig, webpack: webpackConfig,
webpackMiddleware: { stats: 'errors-only' }, webpackMiddleware: { stats: 'errors-only' },
plugins: [
'karma-chrome-launcher',
'karma-coverage-istanbul-reporter',
'karma-jasmine',
'karma-junit-reporter',
'karma-mocha-reporter',
'karma-sourcemap-loader',
'karma-webpack',
],
}; };
if (process.env.CI) { if (process.env.CI) {
...@@ -127,6 +136,19 @@ module.exports = function(config) { ...@@ -127,6 +136,19 @@ module.exports = function(config) {
outputFile: 'junit_karma.xml', outputFile: 'junit_karma.xml',
useBrowserName: false, useBrowserName: false,
}; };
} else {
// ignore 404s in local environment because we are not fixing them and they bloat the log
function ignore404() {
return (request, response /* next */) => {
response.writeHead(404);
return response.end('NOT FOUND');
};
}
karmaConfig.middleware = ['ignore-404'];
karmaConfig.plugins.push({
'middleware:ignore-404': ['factory', ignore404],
});
} }
if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') { if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
......
...@@ -14,6 +14,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -14,6 +14,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :issues, as: :issues_group get :issues, as: :issues_group
get :merge_requests, as: :merge_requests_group get :merge_requests, as: :merge_requests_group
get :projects, as: :projects_group get :projects, as: :projects_group
get :details, as: :details_group # needed for EE but left here to provide helpers for CE partials
get :activity, as: :activity_group get :activity, as: :activity_group
put :transfer, as: :transfer_group put :transfer, as: :transfer_group
# TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693 # TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693
......
...@@ -324,6 +324,10 @@ module.exports = { ...@@ -324,6 +324,10 @@ module.exports = {
reportFilename: path.join(ROOT_PATH, 'webpack-report/index.html'), reportFilename: path.join(ROOT_PATH, 'webpack-report/index.html'),
statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'), statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'),
}), }),
new webpack.DefinePlugin({
'process.env.EE': JSON.stringify(IS_EE),
}),
].filter(Boolean), ].filter(Boolean),
devServer: { devServer: {
......
...@@ -126,11 +126,10 @@ class ProjectForeignKeysWithCascadingDeletes < ActiveRecord::Migration[4.2] ...@@ -126,11 +126,10 @@ class ProjectForeignKeysWithCascadingDeletes < ActiveRecord::Migration[4.2]
queues.each do |queue| queues.each do |queue|
# Stealing is racy so it's possible a pop might be called on an # Stealing is racy so it's possible a pop might be called on an
# already-empty queue. # already-empty queue.
begin
remove_orphans(*queue.pop(true)) remove_orphans(*queue.pop(true))
stolen = true stolen = true
rescue ThreadError rescue ThreadError
end
end end
break unless stolen break unless stolen
......
# Container Scanning with GitLab CI/CD # Container Scanning with GitLab CI/CD **[ULTIMATE]**
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
You can check your Docker images (or more precisely the containers) for known You can check your Docker images (or more precisely the containers) for known
vulnerabilities by using [Clair](https://github.com/coreos/clair) and vulnerabilities by using [Clair](https://github.com/coreos/clair) and
[clair-scanner](https://github.com/arminc/clair-scanner), two open source tools [clair-scanner](https://github.com/arminc/clair-scanner), two open source tools
for Vulnerability Static Analysis for containers. for Vulnerability Static Analysis for containers.
First, you need GitLab Runner with These examples show how to run Container Scanning on your Docker image by using GitLab CI/CD.
[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
CAUTION: **Caution:**
Starting with GitLab 11.5, Container Scanning feature is licensed under the name `container_scanning`.
While the old name `sast_container` is still maintained, it has been deprecated with GitLab 11.5 and
may be removed in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change if you are using the `$GITLAB_FEATURES` environment variable.
## Prerequisites
To run a Container Scanning job, you need:
- a GitLab Runner with
[docker-in-docker executor](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode).
- to [build and push](../../ci/docker/using_docker_build.md#container-registry-examples) your Docker image
using the [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html) running within your GitLab installation.
## Configuring with templates
Since GitLab 11.9, a CI/CD template with the default Container Scanning job definition is provided as a part of your GitLab installation.
This section describes how to use it and customize its execution.
### Using job definition template
CAUTION: **Caution:**
The CI/CD template for job definition is supported on GitLab 11.9 and later versions.
For earlier versions, use the [manual job definition](#manual-job-definition).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` using [the CI/CD template](../../ci/yaml/README.md#includetemplate) for Container Scanning:
```yaml
include:
template: Container-Scanning.gitlab-ci.yml
```
If you want to whitelist some specific vulnerabilities, you can do so by defining
them in a [YAML file](https://github.com/arminc/clair-scanner/blob/master/README.md#example-whitelist-yaml-file),
in our case its named `clair-whitelist.yml`.
### Scanning results
The above example will create a `container_scanning` job in your CI/CD pipeline, pull
the image from the [Container Registry](../../user/project/container_registry.md)
(whose name is defined from the two `CI_APPLICATION_` variables) and scan it
for possible vulnerabilities. The report will be saved as a
[Container Scanning report artifact](../yaml/README.md#artifactsreportscontainer_scanning-ultimate)
that you can later download and analyze.
Due to implementation limitations we always take the latest Container Scanning artifact available.
TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget.
[Learn more on Container Scanning in merge requests](../../user/project/merge_requests/container_scanning.html).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` that ## Manual job definition
generates the expected report:
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions _(although it's preferred to use
[the job definition template](#using-job-definition-template) since 11.9)_.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
If you are using GitLab prior to 11.9, you can define it manually using the following snippet:
```yaml ```yaml
container_scanning: container_scanning:
...@@ -47,29 +100,6 @@ container_scanning: ...@@ -47,29 +100,6 @@ container_scanning:
container_scanning: gl-container-scanning-report.json container_scanning: gl-container-scanning-report.json
``` ```
The above example will create a `container_scanning` job in your CI/CD pipeline, pull
the image from the [Container Registry](../../user/project/container_registry.md)
(whose name is defined from the two `CI_APPLICATION_` variables) and scan it
for possible vulnerabilities. The report will be saved as a
[Container Scanning report artifact](../yaml/README.md#artifactsreportscontainer_scanning-ultimate)
that you can later download and analyze.
Due to implementation limitations we always take the latest Container Scanning artifact available.
If you want to whitelist some specific vulnerabilities, you can do so by defining
them in a [YAML file](https://github.com/arminc/clair-scanner/blob/master/README.md#example-whitelist-yaml-file),
in our case its named `clair-whitelist.yml`.
TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget.
[Learn more on Container Scanning in merge requests](../../user/project/merge_requests/container_scanning.html).
CAUTION: **Caution:**
Starting with GitLab 11.5, Container Scanning feature is licensed under the name `container_scanning`.
While the old name `sast_container` is still maintained, it has been deprecated with GitLab 11.5 and
may be removed in next major release, GitLab 12.0. You are advised to update your current `.gitlab-ci.yml`
configuration to reflect that change if you are using the `$GITLAB_FEATURES` environment variable.
## Previous job definitions ## Previous job definitions
CAUTION: **Caution:** CAUTION: **Caution:**
...@@ -111,7 +141,7 @@ container_scanning: ...@@ -111,7 +141,7 @@ container_scanning:
paths: [gl-container-scanning-report.json] paths: [gl-container-scanning-report.json]
``` ```
Alternatively the job name could be `sast:container` Alternatively, the job name could be `sast:container`
and the artifact name could be `gl-sast-container-report.json`. and the artifact name could be `gl-sast-container-report.json`.
These names have been deprecated with GitLab 11.0 These names have been deprecated with GitLab 11.0
and may be removed in next major release, GitLab 12.0. and may be removed in next major release, GitLab 12.0.
......
# Dynamic Application Security Testing with GitLab CI/CD # Dynamic Application Security Testing with GitLab CI/CD
CAUTION: **Caution:** [Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_Application_Security_Testing)
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
[Dynamic Application Security Testing (DAST)](https://en.wikipedia.org/wiki/Dynamic_program_analysis)
is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy) is using the popular open source tool [OWASP ZAProxy](https://github.com/zaproxy/zaproxy)
to perform an analysis on your running web application. to perform an analysis on your running web application.
Since it is based on [ZAP Baseline](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan) Since it is based on [ZAP Baseline](https://github.com/zaproxy/zaproxy/wiki/ZAP-Baseline-Scan)
...@@ -14,13 +9,114 @@ it will not actively attack your application. ...@@ -14,13 +9,114 @@ it will not actively attack your application.
It can be very useful combined with [Review Apps](../review_apps/index.md). It can be very useful combined with [Review Apps](../review_apps/index.md).
## Example These examples show how to run DAST on your running web application by using GitLab CI/CD.
## Prerequisites
First, you need GitLab Runner with To run a DAST job, you need GitLab Runner with
[docker executor](https://docs.gitlab.com/runner/executors/docker.html). [docker executor](https://docs.gitlab.com/runner/executors/docker.html).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` that ## Configuring with templates
generates the expected report:
Since GitLab 11.9, a CI/CD template with the default DAST job definition is provided as a part of your GitLab installation.
This section describes how to use it and customize its execution.
### Using job definition template
CAUTION: **Caution:**
The CI/CD template for job definition is supported on GitLab 11.9 and later versions.
For earlier versions, use the [manual job definition](#manual-job-definition).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` using [the CI/CD template](../../ci/yaml/README.md#includetemplate) for DAST:
```yaml
include:
template: DAST.gitlab-ci.yml
```
The above example will create a `dast` job in your CI/CD pipeline which will run
the tests on the URL defined in the `DAST_WEBSITE` variable (change it to use your
own) and scan it for possible vulnerabilities.
It's also possible to authenticate the user before performing DAST checks:
```yaml
include:
template: DAST.gitlab-ci.yml
variables:
DAST_AUTH_URL: https://example.com/sign-in
DAST_USERNAME: john.doe@example.com
DAST_PASSWORD: john-doe-password
DAST_USERNAME_FIELD: session[user] # the name of username field at the sign-in HTML form
DAST_PASSWORD_FIELD: session[password] # the name of password field at the sign-in HTML form
```
### Scanning results
The report will be saved as a
[DAST report artifact](../yaml/README.md#artifactsreportsdast-ultimate)
that you can later download and analyze.
Due to implementation limitations we always take the latest DAST artifact available.
TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget.
[Learn more on DAST in merge requests](../../user/project/merge_requests/dast.md).
### Customizing the template
You can customize DAST job execution in various ways of different granularity.
#### Scanning tool settings
DAST tool settings can be changed through environment variables. These variables are documented in the:
- Job definition [template](#using-job-definition-template).
- DAST [README](https://gitlab.com/gitlab-org/security-products/dast#settings).
The customization itself is performed by using the [`variables`](https://docs.gitlab.com/ee/ci/yaml/#variables)
parameter in the project's pipeline configuration file (`.gitlab-ci.yml`):
```yaml
include:
template: DAST.gitlab-ci.yml
variables:
DAST_TARGET_AVAILABILITY_TIMEOUT: 120
```
Because template is evaluated [before](../yaml/README.md#include) the pipeline configuration,
the last mention of the variable will take precedence.
#### Overriding job definition
If you want to override the job definition (for example, change properties like `variables` or `dependencies`), you need to declare
its definition after the template inclusion and specify any additional keys under it. For example:
```yaml
include:
template: DAST.gitlab-ci.yml
dast:
stage: dast # IMPORTANT: don't forget to add this
variables:
CI_DEBUG_TRACE: "true"
```
CAUTION: **Caution:**
As DAST job belongs to a separate `"dast"` stage that runs after all [default stages](../yaml/README.md#stages),
don't forget to add `stage: dast` entry when you override the template job definition.
## Manual job definition
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions _(although it's preferred to use
[the job definition template](#using-job-definition-template) since 11.9)_.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
If you are using GitLab prior to 11.9, you can define it manually using the following snippet:
```yaml ```yaml
dast: dast:
...@@ -37,14 +133,9 @@ dast: ...@@ -37,14 +133,9 @@ dast:
dast: gl-dast-report.json dast: gl-dast-report.json
``` ```
The above example will create a `dast` job in your CI/CD pipeline which will run where the `website` variable is supposed to hold the URL to run the tests against.
the tests on the URL defined in the `website` variable (change it to use your
own) and scan it for possible vulnerabilities. The report will be saved as a
[DAST report artifact](../yaml/README.md#artifactsreportsdast-ultimate)
that you can later download and analyze.
Due to implementation limitations we always take the latest DAST artifact available.
It's also possible to authenticate the user before performing DAST checks: For an authenticated scan, use the following definition:
```yaml ```yaml
dast: dast:
...@@ -66,14 +157,10 @@ dast: ...@@ -66,14 +157,10 @@ dast:
reports: reports:
dast: gl-dast-report.json dast: gl-dast-report.json
``` ```
See [zaproxy documentation](https://gitlab.com/gitlab-org/security-products/zaproxy) See [zaproxy documentation](https://gitlab.com/gitlab-org/security-products/zaproxy)
to learn more about authentication settings. to learn more about authentication settings.
TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget.
[Learn more on DAST in merge requests](../../user/project/merge_requests/dast.md).
## Previous job definitions ## Previous job definitions
CAUTION: **Caution:** CAUTION: **Caution:**
...@@ -97,6 +184,6 @@ dast: ...@@ -97,6 +184,6 @@ dast:
- cp /zap/wrk/gl-dast-report.json . - cp /zap/wrk/gl-dast-report.json .
artifacts: artifacts:
paths: [gl-dast-report.json] paths: [gl-dast-report.json]
``` ```
[ee]: https://about.gitlab.com/pricing/ [ee]: https://about.gitlab.com/pricing/
# Dependency Scanning with GitLab CI/CD **[ULTIMATE]** # Dependency Scanning with GitLab CI/CD **[ULTIMATE]**
CAUTION: **Caution:** These examples show how to run Dependency Scanning on your project's dependencies by using GitLab CI/CD.
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later. ## Prerequisites
For earlier versions, use the [previous job definitions](#previous-job-definitions).
This example shows how to run Dependency Scanning on your To run a Dependency Scanning job, you need GitLab Runner with
project's dependencies by using GitLab CI/CD. [docker-in-docker executor](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode).
## Configuring with templates
First, you need GitLab Runner with Since GitLab 11.9, a CI/CD template with the default Dependency Scanning job definition is provided as a part of your GitLab installation.
[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor). This section describes how to use it and customize its execution.
Once you set up the Runner, add a new job to `.gitlab-ci.yml` that ### Using job definition template
generates the expected report:
CAUTION: **Caution:**
The CI/CD template for job definition is supported on GitLab 11.9 and later versions.
For earlier versions, use the [manual job definition](#manual-job-definition).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` using [the CI/CD template](../../ci/yaml/README.md#includetemplate) for Dependency Scanning:
```yaml ```yaml
dependency_scanning: include:
image: docker:stable template: Dependency-Scanning.gitlab-ci.yml
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
``` ```
### Scanning results
The above example will create a `dependency_scanning` job in your CI/CD pipeline The above example will create a `dependency_scanning` job in your CI/CD pipeline
and scan your dependencies for possible vulnerabilities. The report will be saved as a and scan your dependencies for possible vulnerabilities. The report will be saved as a
[Dependency Scanning report artifact](../../ci/yaml/README.md#artifactsreportsdependency_scanning-ultimate) [Dependency Scanning report artifact](../../ci/yaml/README.md#artifactsreportsdependency_scanning-ultimate)
...@@ -54,16 +46,94 @@ is used to detect the languages/package managers and in turn runs the matching s ...@@ -54,16 +46,94 @@ is used to detect the languages/package managers and in turn runs the matching s
Some security scanners require to send a list of project dependencies to GitLab Some security scanners require to send a list of project dependencies to GitLab
central servers to check for vulnerabilities. To learn more about this or to central servers to check for vulnerabilities. To learn more about this or to
disable it, check the [GitLab Dependency Scanning documentation](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks). disable it, check the [GitLab Dependency Scanning documentation](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks)
and the [customization guide](#job-execution-customization).
TIP: **Tip:** TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget. be automatically extracted and shown right in the merge request widget.
[Learn more on Dependency Scanning in merge requests](../../user/project/merge_requests/dependency_scanning.md). [Learn more on Dependency Scanning in merge requests](../../user/project/merge_requests/dependency_scanning.md).
## Supported languages and package managers ### Customizing the template
See [the full list of supported languages and package managers](../../user/project/merge_requests/dependency_scanning.md#supported-languages-and-dependency-managers). You can customize Dependency Scanning job execution in various ways of different granularity.
#### Scanning tool settings
Dependency Scanning tool settings can be changed through environment variables. These variables are documented in the:
- Job definition [template](#using-job-definition-template).
- Dependency Scanning [README](https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings).
The customization itself is performed by using the [`variables`](https://docs.gitlab.com/ee/ci/yaml/#variables)
parameter in the project's pipeline configuration file (`.gitlab-ci.yml`):
```yaml
include:
template: Dependency-Scanning.gitlab-ci.yml
variables:
DEP_SCAN_DISABLE_REMOTE_CHECKS: true
```
Because template is evaluated [before](../yaml/README.md#include) the pipeline configuration,
the last mention of the variable will take precedence.
#### Overriding job definition
If you want to override the job definition (for example, change properties like `variables` or `dependencies`), you need to declare
its definition after the template inclusion and specify any additional keys under it. For example:
```yaml
include:
template: Dependency-Scanning.gitlab-ci.yml
dependency_scanning:
variables:
CI_DEBUG_TRACE: "true"
```
## Manual job definition
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions _(although it's preferred to use
[the job definition template](#using-job-definition-template) since 11.9)_.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
If you are using GitLab prior to 11.9, you can define it manually using the following snippet:
```yaml
dependency_scanning:
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export DS_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- |
docker run \
--env DS_ANALYZER_IMAGES \
--env DS_ANALYZER_IMAGE_PREFIX \
--env DS_ANALYZER_IMAGE_TAG \
--env DS_DEFAULT_ANALYZERS \
--env DEP_SCAN_DISABLE_REMOTE_CHECKS \
--env DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
--env DS_PULL_ANALYZER_IMAGE_TIMEOUT \
--env DS_RUN_ANALYZER_TIMEOUT \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$DS_VERSION" /code
dependencies: []
artifacts:
reports:
dependency_scanning: gl-dependency-scanning-report.json
```
You can supply many other [settings variables](https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings)
via `docker run --env` to customize your job execution.
## Previous job definitions ## Previous job definitions
...@@ -85,14 +155,26 @@ dependency_scanning: ...@@ -85,14 +155,26 @@ dependency_scanning:
services: services:
- docker:stable-dind - docker:stable-dind
script: script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - export DS_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- docker run - |
--env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}" docker run \
--volume "$PWD:/code" --env DS_ANALYZER_IMAGES \
--volume /var/run/docker.sock:/var/run/docker.sock --env DS_ANALYZER_IMAGE_PREFIX \
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code --env DS_ANALYZER_IMAGE_TAG \
--env DS_DEFAULT_ANALYZERS \
--env DEP_SCAN_DISABLE_REMOTE_CHECKS \
--env DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
--env DS_PULL_ANALYZER_IMAGE_TIMEOUT \
--env DS_RUN_ANALYZER_TIMEOUT \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$DS_VERSION" /code
artifacts: artifacts:
paths: [gl-dependency-scanning-report.json] paths: [gl-dependency-scanning-report.json]
``` ```
## Supported languages and package managers
See [the full list of supported languages and package managers](../../user/project/merge_requests/dependency_scanning.md#supported-languages-and-dependency-managers).
[ee]: https://about.gitlab.com/pricing/ [ee]: https://about.gitlab.com/pricing/
...@@ -269,7 +269,7 @@ The `releases` directory will hold all our deployments: ...@@ -269,7 +269,7 @@ The `releases` directory will hold all our deployments:
echo 'Cloning repository' echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }} [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }} git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }} cd {{ $new_release_dir }}
git reset --hard {{ $commit }} git reset --hard {{ $commit }}
@endtask @endtask
...@@ -347,7 +347,7 @@ At the end, our `Envoy.blade.php` file will look like this: ...@@ -347,7 +347,7 @@ At the end, our `Envoy.blade.php` file will look like this:
echo 'Cloning repository' echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }} [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }} git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }} cd {{ $new_release_dir }}
git reset --hard {{ $commit }} git reset --hard {{ $commit }}
@endtask @endtask
......
# Dependencies license management with GitLab CI/CD **[ULTIMATE]** # Dependencies license management with GitLab CI/CD **[ULTIMATE]**
CAUTION: **Caution:** These examples show how to run License Management scanning on your project's dependencies by using GitLab CI/CD.
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later. ## Prerequisites
For earlier versions, use the [previous job definitions](#previous-job-definitions).
To run a License Management scanning job, you need GitLab Runner with
[docker executor](https://docs.gitlab.com/runner/executors/docker.html).
This example shows how to run the License Management tool on your ## Configuring with templates
project's dependencies by using GitLab CI/CD.
First, you need GitLab Runner with Since GitLab 11.9, a CI/CD template with the default License Management scanning job definition is provided as a part of your GitLab installation.
[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor). This section describes how to use it and customize its execution.
Once you set up the Runner, add a new job to `.gitlab-ci.yml` that ### Using job definition template
generates the expected report:
CAUTION: **Caution:**
The CI/CD template for job definition is supported on GitLab 11.9 and later versions.
For earlier versions, use the [manual job definition](#manual-job-definition).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` using [the CI/CD template](../../ci/yaml/README.md#includetemplate) for License Management:
```yaml ```yaml
license_management: include:
image: template: License-Management.gitlab-ci.yml
name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
entrypoint: [""]
stage: test
allow_failure: true
script:
- /run.sh analyze .
artifacts:
reports:
license_management: gl-license-management-report.json
``` ```
### Scanning results
The above example will create a `license_management` job in your CI/CD pipeline The above example will create a `license_management` job in your CI/CD pipeline
and scan your dependencies to find their licenses. The report will be saved as a and scan your dependencies to find their licenses. The report will be saved as a
[License Management report artifact](../../ci/yaml/README.md#artifactsreportslicense_management-ultimate) [License Management report artifact](../../ci/yaml/README.md#artifactsreportslicense_management-ultimate)
that you can later download and analyze. that you can later download and analyze.
Due to implementation limitations we always take the latest License Management artifact available. Due to implementation limitations we always take the latest License Management artifact available.
## Install custom project dependencies TIP: **Tip:**
For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget.
[Learn more on License Management in merge requests](../../user/project/merge_requests/license_management.md).
### Customizing the template
#### Install custom project dependencies
> Introduced in GitLab Ultimate 11.4. > Introduced in GitLab Ultimate 11.4.
...@@ -50,14 +56,45 @@ of your application (ex: for a project with a `Gemfile`, the setup step will be ...@@ -50,14 +56,45 @@ of your application (ex: for a project with a `Gemfile`, the setup step will be
Example: Example:
```yaml
include:
template: License-Management.gitlab-ci.yml
variables:
LICENSE_MANAGEMENT_SETUP_CMD: ./my-custom-install-script.sh
```
In this example, `my-custom-install-script.sh` is a shell script at the root of the project.
#### Overriding job definition
If you want to override the job definition (for example, change properties like `variables` or `dependencies`), you need to declare
its definition after the template inclusion and specify any additional keys under it. For example:
```yaml
include:
template: License-Management.gitlab-ci.yml
license_management:
stage: my-custom-stage
```
## Manual job definition
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions _(although it's preferred to use
[the job definition template](#using-job-definition-template) since 11.9)_.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
If you are using GitLab prior to 11.9, you can define it manually using the following snippet:
```yaml ```yaml
license_management: license_management:
image: image:
name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable" name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
entrypoint: [""] entrypoint: [""]
stage: test stage: test
variables:
SETUP_CMD: ./my-custom-install-script.sh
allow_failure: true allow_failure: true
script: script:
- /run.sh analyze . - /run.sh analyze .
...@@ -66,12 +103,23 @@ license_management: ...@@ -66,12 +103,23 @@ license_management:
license_management: gl-license-management-report.json license_management: gl-license-management-report.json
``` ```
In this example, `my-custom-install-script.sh` is a shell script at the root of the project. Install custom project dependencies via `SETUP_CMD` variable:
TIP: **Tip:** ```yaml
For [GitLab Ultimate][ee] users, this information will license_management:
be automatically extracted and shown right in the merge request widget. image:
[Learn more on License Management in merge requests](../../user/project/merge_requests/license_management.md). name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
entrypoint: [""]
stage: test
variables:
SETUP_CMD: ./my-custom-install-script.sh
allow_failure: true
script:
- /run.sh analyze .
artifacts:
reports:
license_management: gl-license-management-report.json
```
## Previous job definitions ## Previous job definitions
......
# Static Application Security Testing with GitLab CI/CD **[ULTIMATE]** # Static Application Security Testing with GitLab CI/CD **[ULTIMATE]**
CAUTION: **Caution:** These examples show how to run [Static Application Security Testing (SAST)](https://en.wikipedia.org/wiki/Static_program_analysis)
The job definition shown below is supported on GitLab 11.5 and later versions.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
This example shows how to run
[Static Application Security Testing (SAST)](https://en.wikipedia.org/wiki/Static_program_analysis)
on your project's source code by using GitLab CI/CD. on your project's source code by using GitLab CI/CD.
First, you need GitLab Runner with ## Prerequisites
[docker-in-docker executor](../docker/using_docker_build.md#use-docker-in-docker-executor).
To run a SAST job, you need GitLab Runner with
[docker-in-docker executor](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode).
## Configuring with templates
Since GitLab 11.9, a CI/CD template with the default SAST job definition is provided as a part of your GitLab installation.
This section describes how to use it and customize its execution.
### Using job definition template
Once you set up the Runner, add a new job to `.gitlab-ci.yml` that CAUTION: **Caution:**
generates the expected report: The CI/CD template for job definition is supported on GitLab 11.9 and later versions.
For earlier versions, use the [manual job definition](#manual-job-definition).
Once you set up the Runner, add a new job to `.gitlab-ci.yml` using [the CI/CD template](../../ci/yaml/README.md#includetemplate) for SAST:
```yaml ```yaml
sast: include:
image: docker:stable template: SAST.gitlab-ci.yml
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- docker run
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
--volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code
artifacts:
reports:
sast: gl-sast-report.json
``` ```
### Scanning results
The above example will create a `sast` job in your CI/CD pipeline The above example will create a `sast` job in your CI/CD pipeline
and scan your dependencies for possible vulnerabilities. The report will be saved as a and scan your project's source code for possible vulnerabilities. The report will be saved as a
[SAST report artifact](../../ci/yaml/README.md#artifactsreportssast-ultimate) [SAST report artifact](../../ci/yaml/README.md#artifactsreportssast-ultimate)
that you can later download and analyze. that you can later download and analyze.
Due to implementation limitations we always take the latest SAST artifact available. Due to implementation limitations we always take the latest SAST artifact available.
...@@ -58,9 +51,89 @@ For [GitLab Ultimate][ee] users, this information will ...@@ -58,9 +51,89 @@ For [GitLab Ultimate][ee] users, this information will
be automatically extracted and shown right in the merge request widget. be automatically extracted and shown right in the merge request widget.
[Learn more on SAST in merge requests](../../user/project/merge_requests/sast.md). [Learn more on SAST in merge requests](../../user/project/merge_requests/sast.md).
## Supported languages and frameworks ### Customizing the template
You can customize SAST job execution in various ways of different granularity.
#### Scanning tool settings
SAST tool settings can be changed through environment variables. These variables are documented in the:
- Job definition [template](#using-job-definition-template).
- SAST [README](https://gitlab.com/gitlab-org/security-products/sast#settings).
The customization itself is performed by using the [`variables`](https://docs.gitlab.com/ee/ci/yaml/#variables)
parameter in the project's pipeline configuration file (`.gitlab-ci.yml`):
```yaml
include:
template: SAST.gitlab-ci.yml
variables:
SAST_GOSEC_LEVEL: 2
```
Because template is evaluated [before](../yaml/README.md#include) the pipeline configuration,
the last mention of the variable will take precedence.
#### Overriding job definition
If you want to override the job definition (for example, change properties like `variables` or `dependencies`), you need to declare
its definition after the template inclusion and specify any additional keys under it. For example:
```yaml
include:
template: SAST.gitlab-ci.yml
sast:
variables:
CI_DEBUG_TRACE: "true"
```
## Manual job definition
CAUTION: **Caution:**
The job definition shown below is supported on GitLab 11.5 and later versions _(although it's preferred to use
[the job definition template](#using-job-definition-template) since 11.9)_.
It also requires the GitLab Runner 11.5 or later.
For earlier versions, use the [previous job definitions](#previous-job-definitions).
If you are using GitLab prior to 11.9, you can define it manually using the following snippet:
```yaml
sast:
stage: test
image: docker:stable
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:stable-dind
script:
- export SAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- |
docker run \
--env SAST_ANALYZER_IMAGES \
--env SAST_ANALYZER_IMAGE_PREFIX \
--env SAST_ANALYZER_IMAGE_TAG \
--env SAST_DEFAULT_ANALYZERS \
--env SAST_BRAKEMAN_LEVEL \
--env SAST_GOSEC_LEVEL \
--env SAST_FLAWFINDER_LEVEL \
--env SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
--env SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
--env SAST_RUN_ANALYZER_TIMEOUT \
--volume "$PWD:/code" \
--volume /var/run/docker.sock:/var/run/docker.sock \
"registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
dependencies: []
artifacts:
reports:
sast: gl-sast-report.json
```
See [the full list of supported languages and frameworks](../../user/project/merge_requests/sast.md#supported-languages-and-frameworks). You can supply many other [settings variables](https://gitlab.com/gitlab-org/security-products/sast#settings)
via `docker run --env` to customize your job execution.
## Previous job definitions ## Previous job definitions
...@@ -82,12 +155,12 @@ sast: ...@@ -82,12 +155,12 @@ sast:
services: services:
- docker:stable-dind - docker:stable-dind
script: script:
- export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') - export SAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
- docker run - docker run
--env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}"
--volume "$PWD:/code" --volume "$PWD:/code"
--volume /var/run/docker.sock:/var/run/docker.sock --volume /var/run/docker.sock:/var/run/docker.sock
"registry.gitlab.com/gitlab-org/security-products/sast:$SP_VERSION" /app/bin/run /code "registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
artifacts: artifacts:
paths: [gl-sast-report.json] paths: [gl-sast-report.json]
``` ```
......
...@@ -2561,4 +2561,4 @@ git push -o ci.skip ...@@ -2561,4 +2561,4 @@ git push -o ci.skip
[environment]: ../environments.md "CI/CD environments" [environment]: ../environments.md "CI/CD environments"
[schedules]: ../../user/project/pipelines/schedules.md "Pipelines schedules" [schedules]: ../../user/project/pipelines/schedules.md "Pipelines schedules"
[variables]: ../variables/README.md "CI/CD variables" [variables]: ../variables/README.md "CI/CD variables"
[push-option]: https://git-scm.com/docs/git-push#git-push--oltoptiongt [push-option]: https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt
...@@ -52,9 +52,12 @@ for audiences of all ages. ...@@ -52,9 +52,12 @@ for audiences of all ages.
If a contributor is no longer actively working on a submitted merge request If a contributor is no longer actively working on a submitted merge request
we can decide that the merge request will be finished by one of our we can decide that the merge request will be finished by one of our
[Merge request coaches][team] or close the merge request. We make this decision [Merge request coaches][team] or close the merge request. We make this decision
based on how important the change is for our product vision. If a Merge request based on how important the change is for our product vision. If a merge request
coach is going to finish the merge request we assign the coach is going to finish the merge request we assign the
~"coach will finish" label. ~"coach will finish" label. When a team member picks up a community contribution,
we credit the original author by adding a changelog entry crediting the author
and optionally include the original author on at least one of the commits
within the MR.
## Helping others ## Helping others
...@@ -119,6 +122,7 @@ This [documentation](merge_request_workflow.md) outlines the current merge reque ...@@ -119,6 +122,7 @@ This [documentation](merge_request_workflow.md) outlines the current merge reque
- [Merge request guidelines](merge_request_workflow.md#merge-request-guidelines) - [Merge request guidelines](merge_request_workflow.md#merge-request-guidelines)
- [Contribution acceptance criteria](merge_request_workflow.md#contribution-acceptance-criteria) - [Contribution acceptance criteria](merge_request_workflow.md#contribution-acceptance-criteria)
- [Definition of done](merge_request_workflow.md#definition-of-done) - [Definition of done](merge_request_workflow.md#definition-of-done)
- [Dependencies](merge_request_workflow.md#dependencies)
## Style guides ## Style guides
......
...@@ -80,11 +80,10 @@ yield a useful result, and ensuring content is helpful and easy to consume. ...@@ -80,11 +80,10 @@ yield a useful result, and ensuring content is helpful and easy to consume.
## Text ## Text
- Split up long lines (wrap text), this makes it much easier to review and edit. Only - Splitting long lines (preferably up to 100 characters) can make it easier to provide feedback on small chunks of text.
double line breaks are shown as a full line break by creating new paragraphs. - Insert an empty line for new paragraphs.
80-100 characters is the recommended line length.
- Use sentence case for titles, headings, labels, menu items, and buttons. - Use sentence case for titles, headings, labels, menu items, and buttons.
- Jump a line between different markups (e.g., after every paragraph, header, list, etc). Example: - Insert an empty line between different markups (e.g., after every paragraph, header, list, etc). Example:
```md ```md
## Header ## Header
......
...@@ -15,7 +15,7 @@ information on general testing practices at GitLab. ...@@ -15,7 +15,7 @@ information on general testing practices at GitLab.
## Jest ## Jest
GitLab has started to migrate tests to the (Jest)[https://jestjs.io] GitLab has started to migrate tests to the [Jest](https://jestjs.io)
testing framework. You can read a [detailed evaluation](https://gitlab.com/gitlab-org/gitlab-ce/issues/49171) testing framework. You can read a [detailed evaluation](https://gitlab.com/gitlab-org/gitlab-ce/issues/49171)
of Jest compared to our use of Karma and Jasmine. In summary, it will allow us of Jest compared to our use of Karma and Jasmine. In summary, it will allow us
to improve the performance and consistency of our frontend tests. to improve the performance and consistency of our frontend tests.
......
...@@ -704,6 +704,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac ...@@ -704,6 +704,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> | | `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | | `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | | `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | | `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | | `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. | | `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
......
...@@ -30,7 +30,9 @@ each one separately. ...@@ -30,7 +30,9 @@ each one separately.
## Supported features ## Supported features
The group security dashboard supports [SAST](../../project/merge_requests/sast.md), and [Dependency Scanning](../../project/merge_requests/dependency_scanning.md) reports. The group security dashboard supports [SAST](../../project/merge_requests/sast.md),
[Dependency Scanning](../../project/merge_requests/dependency_scanning.md),
and [Container Scanning](../../project/merge_requests/container_scanning.md).
## Requirements ## Requirements
......
...@@ -63,6 +63,12 @@ are available: ...@@ -63,6 +63,12 @@ are available:
- `%{commit_sha}`: ID of the most recent commit to the default branch of a - `%{commit_sha}`: ID of the most recent commit to the default branch of a
project's repository project's repository
NOTE: **NOTE**
Placeholders allow badges to expose otherwise-private information, such as the
default branch or commit SHA when the project is configured to have a private
repository. This is by design, as badges are intended to be used publicly. Avoid
using these placeholders if the information is sensitive.
## API ## API
You can also configure badges via the GitLab API. As in the settings, there is You can also configure badges via the GitLab API. As in the settings, there is
......
...@@ -75,6 +75,14 @@ new Kubernetes cluster to your project: ...@@ -75,6 +75,14 @@ new Kubernetes cluster to your project:
After a couple of minutes, your cluster will be ready to go. You can now proceed After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](#installing-applications). to install some [pre-defined applications](#installing-applications).
NOTE: **Note:**
GitLab requires basic authentication enabled and a client certificate issued for
the cluster in order to setup an [initial service
account](#access-controls). Starting from [GitLab
11.10](https://gitlab.com/gitlab-org/gitlab-ce/issues/58208), the cluster
creation process will explicitly request that basic authentication and
client certificate is enabled.
## Adding an existing Kubernetes cluster ## Adding an existing Kubernetes cluster
To add an existing Kubernetes cluster to your project: To add an existing Kubernetes cluster to your project:
......
...@@ -293,6 +293,8 @@ Approvers are suggested for merge requests based on the previous authors of the ...@@ -293,6 +293,8 @@ Approvers are suggested for merge requests based on the previous authors of the
## Filtering merge requests by approvers ## Filtering merge requests by approvers
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9468) in [GitLab Starter](https://about.gitlab.com/pricing/) 11.9.
To filter merge requests by an individual approver, you can type (or select from To filter merge requests by an individual approver, you can type (or select from
the dropdown) `approver` and select the user. the dropdown) `approver` and select the user.
......
...@@ -35,7 +35,6 @@ The following languages and frameworks are supported. ...@@ -35,7 +35,6 @@ The following languages and frameworks are supported.
| Language / framework | Scan tool | | Language / framework | Scan tool |
|-------------------------|----------------------------------------------------------------------------------------| |-------------------------|----------------------------------------------------------------------------------------|
| .NET | [Security Code Scan](https://security-code-scan.github.io) | | .NET | [Security Code Scan](https://security-code-scan.github.io) |
| Any | [Gitleaks](https://github.com/zricethezav/gitleaks), [TruffleHog](https://github.com/dxa4481/truffleHog) and [Diffence](https://github.com/techjacker/diffence) (secret detectors) |
| C/C++ | [Flawfinder](https://www.dwheeler.com/flawfinder/) | | C/C++ | [Flawfinder](https://www.dwheeler.com/flawfinder/) |
| Go | [Gosec](https://github.com/securego/gosec) | | Go | [Gosec](https://github.com/securego/gosec) |
| Groovy (Ant, Gradle, Maven and SBT) | [find-sec-bugs](https://find-sec-bugs.github.io/) | | Groovy (Ant, Gradle, Maven and SBT) | [find-sec-bugs](https://find-sec-bugs.github.io/) |
...@@ -48,6 +47,17 @@ The following languages and frameworks are supported. ...@@ -48,6 +47,17 @@ The following languages and frameworks are supported.
| Scala (Ant, Gradle, Maven and SBT) | [find-sec-bugs](https://find-sec-bugs.github.io/) | | Scala (Ant, Gradle, Maven and SBT) | [find-sec-bugs](https://find-sec-bugs.github.io/) |
| Typescript | [TSLint Config Security](https://github.com/webschik/tslint-config-security/) | | Typescript | [TSLint Config Security](https://github.com/webschik/tslint-config-security/) |
## Secret Detection
GitLab is also able to detect secrets and credentials that have been unintentionally pushed to the repository.
For example, an API key that allows write access to third-party deployment environments.
This check is performed by a specific analyzer during the `sast` job. It runs regardless of the programming
language of your app, and you don't need to change anything to your
CI/CD configuration file to turn it on. Results are available in the SAST report.
GitLab currently includes [Gitleaks](https://github.com/zricethezav/gitleaks), [TruffleHog](https://github.com/dxa4481/truffleHog), and [Diffence](https://github.com/techjacker/diffence).
## How it works ## How it works
First of all, you need to define a job in your `.gitlab-ci.yml` file that generates the First of all, you need to define a job in your `.gitlab-ci.yml` file that generates the
......
...@@ -109,7 +109,7 @@ all matching branches: ...@@ -109,7 +109,7 @@ all matching branches:
## Creating a protected branch ## Creating a protected branch
> [Introduced][https://gitlab.com/gitlab-org/gitlab-ce/issues/53361] in GitLab 11.9. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53361) in GitLab 11.9.
When a protected branch or wildcard protected branches are set to When a protected branch or wildcard protected branches are set to
[**No one** is **Allowed to push**](#using-the-allowed-to-merge-and-allowed-to-push-settings), [**No one** is **Allowed to push**](#using-the-allowed-to-merge-and-allowed-to-push-settings),
......
...@@ -12,4 +12,15 @@ export default { ...@@ -12,4 +12,15 @@ export default {
helpCanaryDeploymentsPath: data.environmentsDataHelpCanaryDeploymentsPath, helpCanaryDeploymentsPath: data.environmentsDataHelpCanaryDeploymentsPath,
}; };
}, },
computed: {
canaryCalloutProps() {
return {
canaryDeploymentFeatureId: this.canaryDeploymentFeatureId,
showCanaryDeploymentCallout: this.showCanaryDeploymentCallout,
userCalloutsPath: this.userCalloutsPath,
lockPromotionSvgPath: this.lockPromotionSvgPath,
helpCanaryDeploymentsPath: this.helpCanaryDeploymentsPath,
};
},
},
}; };
export default {
props: {
canaryDeploymentFeatureId: {
type: String,
required: true,
},
showCanaryDeploymentCallout: {
type: Boolean,
required: true,
},
userCalloutsPath: {
type: String,
required: true,
},
lockPromotionSvgPath: {
type: String,
required: true,
},
helpCanaryDeploymentsPath: {
type: String,
required: true,
},
},
};
import CeEnvironmentsStore from '~/environments/stores/environments_store';
export default class EnvironmentsStore extends CeEnvironmentsStore {
storeEnvironments(environments = []) {
super.storeEnvironments(environments);
/**
* Add the canary callout banner underneath the second environment listed.
*
* If there is only one environment, then add to it underneath the first.
*/
if (this.state.environments.length >= 2) {
this.state.environments[1].showCanaryCallout = true;
} else if (this.state.environments.length === 1) {
this.state.environments[0].showCanaryCallout = true;
}
}
/**
* Toggles deploy board visibility for the provided environment ID.
*
* @param {Object} environment
* @return {Array}
*/
toggleDeployBoard(environmentID) {
const environments = this.state.environments.slice();
this.state.environments = environments.map(env => {
let updated = Object.assign({}, env);
if (env.id === environmentID) {
updated = Object.assign({}, updated, {
isDeployBoardVisible: !env.isDeployBoardVisible,
});
}
return updated;
});
return this.state.environments;
}
}
import { CLUSTER_TYPE } from '~/clusters/constants';
/**
* Deploy boards are EE only.
*
* @param {Object} environment
* @returns {Object}
*/
// eslint-disable-next-line import/prefer-default-export
export const setDeployBoard = (oldEnvironmentState, environment) => {
let parsedEnvironment = environment;
if (
environment.size === 1 &&
environment.rollout_status &&
environment.cluster_type !== CLUSTER_TYPE.GROUP
) {
parsedEnvironment = Object.assign({}, environment, {
hasDeployBoard: true,
isDeployBoardVisible:
oldEnvironmentState.isDeployBoardVisible === false
? oldEnvironmentState.isDeployBoardVisible
: true,
deployBoardData:
environment.rollout_status.status === 'found' ? environment.rollout_status : {},
isLoadingDeployBoard: environment.rollout_status.status === 'loading',
isEmptyDeployBoard: environment.rollout_status.status === 'not_found',
});
}
return parsedEnvironment;
};
import initGroupDetails from '~/pages/groups/shared/group_details';
import initSecurityDashboard from 'ee/security_dashboard/index';
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('#js-group-security-dashboard')) {
initSecurityDashboard();
} else {
initGroupDetails();
}
});
export default {
props: {
hasTriggeredBy: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
buildConnnectorClass(index) {
return index === 0 && (!this.isFirstColumn || this.hasTriggeredBy) ? 'left-connector' : '';
},
},
};
...@@ -15,7 +15,7 @@ class Admin::Geo::NodesController < Admin::ApplicationController ...@@ -15,7 +15,7 @@ class Admin::Geo::NodesController < Admin::ApplicationController
flash.now[:alert] = _('You need a different license to enable Geo replication.') flash.now[:alert] = _('You need a different license to enable Geo replication.')
end end
unless Gitlab::Database.pg_stat_wal_receiver_supported? unless Gitlab::Database.postgresql_minimum_supported_version?
flash.now[:warning] = _('Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version.') flash.now[:warning] = _('Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version.')
end end
end end
......
...@@ -3,6 +3,16 @@ ...@@ -3,6 +3,16 @@
module EE module EE
module GroupsController module GroupsController
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
override :render_show_html
def render_show_html
if redirect_show_path
redirect_to redirect_show_path, status: :temporary_redirect
else
super
end
end
def group_params_attributes def group_params_attributes
super + group_params_ee super + group_params_ee
...@@ -25,5 +35,24 @@ module EE ...@@ -25,5 +35,24 @@ module EE
def current_group def current_group
@group @group
end end
def redirect_show_path
strong_memoize(:redirect_show_path) do
case group_view
when 'security_dashboard'
helpers.group_security_dashboard_path(group) if ::Feature.enabled?(:group_overview_security_dashboard)
else
nil
end
end
end
def group_view
current_user&.group_view || default_group_view
end
def default_group_view
EE::User::DEFAULT_GROUP_VIEW
end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
class Groups::Security::ApplicationController < Groups::ApplicationController class Groups::Security::ApplicationController < Groups::ApplicationController
before_action :ensure_security_dashboard_feature_enabled before_action :ensure_security_dashboard_feature_enabled!
before_action :authorize_read_group_security_dashboard! before_action :authorize_read_group_security_dashboard!
private protected
def ensure_security_dashboard_feature_enabled def ensure_security_dashboard_feature_enabled!
render_404 unless @group.feature_available?(:security_dashboard) render_404 unless group.feature_available?(:security_dashboard)
end end
def authorize_read_group_security_dashboard! def authorize_read_group_security_dashboard!
render_403 unless can?(current_user, :read_group_security_dashboard, group) render_403 unless helpers.can_read_group_security_dashboard?(group)
end end
end end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module EE module EE
module PreferencesHelper module PreferencesHelper
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include ::Groups::Security::DashboardHelper
override :excluded_dashboard_choices override :excluded_dashboard_choices
def excluded_dashboard_choices def excluded_dashboard_choices
...@@ -13,9 +14,10 @@ module EE ...@@ -13,9 +14,10 @@ module EE
def group_view_choices def group_view_choices
strong_memoize(:group_view_choices) do strong_memoize(:group_view_choices) do
[[_('Details (default)'), :details]].tap do |choices| choices = []
choices << [_('Security dashboard'), :security_dashboard] if group_view_security_dashboard_enabled? choices << [_('Details (default)'), :details]
end choices << [_('Security dashboard'), :security_dashboard] if group_view_security_dashboard_enabled?
choices
end end
end end
......
# frozen_string_literal: true
module Groups
module Security
module DashboardHelper
def can_read_group_security_dashboard?(group)
can?(current_user, :read_group_security_dashboard, group)
end
end
end
end
...@@ -68,13 +68,11 @@ module EE ...@@ -68,13 +68,11 @@ module EE
::Feature.disabled?(:parse_dast_reports, default_enabled: false) ::Feature.disabled?(:parse_dast_reports, default_enabled: false)
security_reports.get_report(file_type).tap do |security_report| security_reports.get_report(file_type).tap do |security_report|
begin next unless project.feature_available?(LICENSED_PARSER_FEATURES.fetch(file_type))
next unless project.feature_available?(LICENSED_PARSER_FEATURES.fetch(file_type))
::Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, security_report) ::Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, security_report)
rescue => e rescue => e
security_report.error = e security_report.error = e
end
end end
end end
end end
......
...@@ -5,11 +5,11 @@ module EE ...@@ -5,11 +5,11 @@ module EE
def rollout_status(environment) def rollout_status(environment)
case environment.name case environment.name
when 'staging' when 'staging'
Gitlab::Kubernetes::RolloutStatus.new([], status: :not_found) ::Gitlab::Kubernetes::RolloutStatus.new([], status: :not_found)
when 'test' when 'test'
Gitlab::Kubernetes::RolloutStatus.new([], status: :loading) ::Gitlab::Kubernetes::RolloutStatus.new([], status: :loading)
else else
Gitlab::Kubernetes::RolloutStatus.new(rollout_status_deployments) ::Gitlab::Kubernetes::RolloutStatus.new(rollout_status_deployments)
end end
end end
......
...@@ -209,7 +209,7 @@ class GeoNodeStatus < ActiveRecord::Base ...@@ -209,7 +209,7 @@ class GeoNodeStatus < ActiveRecord::Base
def load_secondary_data def load_secondary_data
if Gitlab::Geo.secondary? if Gitlab::Geo.secondary?
self.db_replication_lag_seconds = Gitlab::Geo::HealthCheck.db_replication_lag_seconds self.db_replication_lag_seconds = Gitlab::Geo::HealthCheck.new.db_replication_lag_seconds
self.cursor_last_event_id = current_cursor_last_event_id self.cursor_last_event_id = current_cursor_last_event_id
self.cursor_last_event_date = Geo::EventLog.find_by(id: self.cursor_last_event_id)&.created_at self.cursor_last_event_date = Geo::EventLog.find_by(id: self.cursor_last_event_id)&.created_at
self.repositories_synced_count = projects_finder.count_synced_repositories self.repositories_synced_count = projects_finder.count_synced_repositories
......
...@@ -144,11 +144,9 @@ module Vulnerabilities ...@@ -144,11 +144,9 @@ module Vulnerabilities
def metadata def metadata
strong_memoize(:metadata) do strong_memoize(:metadata) do
begin JSON.parse(raw_metadata)
JSON.parse(raw_metadata) rescue JSON::ParserError
rescue JSON::ParserError {}
{}
end
end end
end end
......
...@@ -16,13 +16,11 @@ module SoftwareLicensePolicies ...@@ -16,13 +16,11 @@ module SoftwareLicensePolicies
name = params.delete(:name) name = params.delete(:name)
software_license = SoftwareLicense.transaction do software_license = SoftwareLicense.transaction do
begin SoftwareLicense.transaction(requires_new: true) do
SoftwareLicense.transaction(requires_new: true) do SoftwareLicense.find_or_create_by(name: name)
SoftwareLicense.find_or_create_by(name: name)
end
rescue ActiveRecord::RecordNotUnique
retry
end end
rescue ActiveRecord::RecordNotUnique
retry
end end
# Add the software license to params # Add the software license to params
......
- if can?(current_user, :read_group_security_dashboard, @group) - if @group.feature_available?(:security_dashboard)
= nav_link(path: 'groups/security/dashboard#show') do = nav_link(path: 'groups/security/dashboard#show') do
= link_to group_security_dashboard_path(@group), title: _('Security Dashboard') do = link_to group_security_dashboard_path(@group), title: _('Security Dashboard') do
%span= _('Security Dashboard') %span= _('Security Dashboard')
---
title: 'Geo: Only display Geo-specific clone instructions button on a Geo Secondary
node'
merge_request: 10007
author:
type: fixed
---
title: 'Geo: Support archive recovery or streaming replication types in health check'
merge_request: 9935
author:
type: fixed
---
title: Enabled setting the Security Dashboard as a default view for groups
merge_request: 7889
author:
type: added
...@@ -46,18 +46,16 @@ class FixImportDataAuthMethodForMirrors < ActiveRecord::Migration[5.0] ...@@ -46,18 +46,16 @@ class FixImportDataAuthMethodForMirrors < ActiveRecord::Migration[5.0]
# Only 129 had this issue, so most of the time will be spent decrypting secrets. # Only 129 had this issue, so most of the time will be spent decrypting secrets.
# It took about 3 minutes to complete. # It took about 3 minutes to complete.
Project.where(mirror: true).where("import_url LIKE 'http%'").preload(:import_data).find_each do |project| Project.where(mirror: true).where("import_url LIKE 'http%'").preload(:import_data).find_each do |project|
begin import_data = project.import_data
import_data = project.import_data
next unless import_data next unless import_data
if import_data.auth_method == 'ssh_public_key' if import_data.auth_method == 'ssh_public_key'
import_data.auth_method = 'password' import_data.auth_method = 'password'
import_data.save import_data.save
end
rescue OpenSSL::Cipher::CipherError
Rails.logger.warn "Error decrypting credentials in import data #{import_data&.id}"
end end
rescue OpenSSL::Cipher::CipherError
Rails.logger.warn "Error decrypting credentials in import data #{import_data&.id}"
end end
end end
end end
...@@ -164,11 +164,9 @@ module EE ...@@ -164,11 +164,9 @@ module EE
end end
members.select do |dn| members.select do |dn|
begin ::Gitlab::Auth::LDAP::DN.new(dn).to_a.last(base.length) == base
::Gitlab::Auth::LDAP::DN.new(dn).to_a.last(base.length) == base rescue ::Gitlab::Auth::LDAP::DN::FormatError => e
rescue ::Gitlab::Auth::LDAP::DN::FormatError => e Rails.logger.warn "Received invalid member DN from LDAP group '#{cn}': '#{dn}'. Error: \"#{e.message}\". Skipping"
Rails.logger.warn "Received invalid member DN from LDAP group '#{cn}': '#{dn}'. Error: \"#{e.message}\". Skipping"
end
end end
end end
......
...@@ -45,15 +45,13 @@ module EE ...@@ -45,15 +45,13 @@ module EE
def file_name_validation def file_name_validation
lambda do |diff| lambda do |diff|
begin if (diff.renamed_file || diff.new_file) && blacklisted_regex = push_rule.filename_blacklisted?(diff.new_path)
if (diff.renamed_file || diff.new_file) && blacklisted_regex = push_rule.filename_blacklisted?(diff.new_path) return unless blacklisted_regex.present?
return unless blacklisted_regex.present?
"File name #{diff.new_path} was blacklisted by the pattern #{blacklisted_regex}."
"File name #{diff.new_path} was blacklisted by the pattern #{blacklisted_regex}."
end
rescue ::PushRule::MatchError => e
raise ::Gitlab::GitAccess::UnauthorizedError, e.message
end end
rescue ::PushRule::MatchError => e
raise ::Gitlab::GitAccess::UnauthorizedError, e.message
end end
end end
end end
......
...@@ -126,14 +126,12 @@ module Gitlab ...@@ -126,14 +126,12 @@ module Gitlab
def load def load
Gitlab::Geo::DatabaseTasks.with_geo_db do Gitlab::Geo::DatabaseTasks.with_geo_db do
begin should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
should_reconnect = ActiveRecord::Base.connection_pool.active_connection? ActiveRecord::Schema.verbose = false
ActiveRecord::Schema.verbose = false ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA'] ensure
ensure if should_reconnect
if should_reconnect ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
end
end end
end end
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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