Commit 45a4bc30 authored by GitLab Release Tools Bot's avatar GitLab Release Tools Bot

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

parents c7e6c8ef 4891e8a5
...@@ -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/**/*'
...@@ -184,3 +185,8 @@ Cop/InjectEnterpriseEditionModule: ...@@ -184,3 +185,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: {
...@@ -41,7 +42,8 @@ export default { ...@@ -41,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" />
...@@ -52,6 +54,11 @@ export default { ...@@ -52,6 +54,11 @@ export default {
:environments="state.environments" :environments="state.environments"
:pagination="state.paginationInformation" :pagination="state.paginationInformation"
:can-read-environment="canReadEnvironment" :can-read-environment="canReadEnvironment"
:canary-deployment-feature-id="canaryDeploymentFeatureId"
:show-canary-deployment-callout="showCanaryDeploymentCallout"
:user-callouts-path="userCalloutsPath"
:lock-promotion-svg-path="lockPromotionSvgPath"
:help-canary-deployments-path="helpCanaryDeploymentsPath"
@onChangePage="onChangePage" @onChangePage="onChangePage"
/> />
</div> </div>
......
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';
...@@ -11,6 +12,7 @@ export default () => ...@@ -11,6 +12,7 @@ export default () =>
components: { components: {
environmentsComponent, environmentsComponent,
}, },
mixins: [canaryCalloutMixin],
data() { data() {
const environmentsData = document.querySelector(this.$options.el).dataset; const environmentsData = document.querySelector(this.$options.el).dataset;
...@@ -32,6 +34,7 @@ export default () => ...@@ -32,6 +34,7 @@ export default () =>
cssContainerClass: this.cssContainerClass, cssContainerClass: this.cssContainerClass,
canCreateEnvironment: this.canCreateEnvironment, canCreateEnvironment: this.canCreateEnvironment,
canReadEnvironment: this.canReadEnvironment, canReadEnvironment: this.canReadEnvironment,
...this.canaryCalloutProps,
}, },
}); });
}, },
......
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';
/** /**
* Environments Store. * Environments Store.
* *
...@@ -31,6 +33,14 @@ export default class EnvironmentsStore { ...@@ -31,6 +33,14 @@ export default class EnvironmentsStore {
* If the `size` is bigger than 1, it means it should be rendered as a folder. * If the `size` is bigger than 1, it means it should be rendered as a folder.
* In those cases we add `isFolder` key in order to render it properly. * In those cases we add `isFolder` key in order to render it properly.
* *
* Top level environments - when the size is 1 - with `rollout_status`
* can render a deploy board. We add `isDeployBoardVisible` and `deployBoardData`
* keys to those environments.
* The first key will let's us know if we should or not render the deploy board.
* It will be toggled when the user clicks to seee the deploy board.
*
* The second key will allow us to update the environment with the received deploy board data.
*
* @param {Array} environments * @param {Array} environments
* @returns {Array} * @returns {Array}
*/ */
...@@ -63,6 +73,7 @@ export default class EnvironmentsStore { ...@@ -63,6 +73,7 @@ export default class EnvironmentsStore {
filtered = Object.assign(filtered, env); filtered = Object.assign(filtered, env);
} }
filtered = setDeployBoard(oldEnvironmentState, filtered);
return filtered; return filtered;
}); });
...@@ -71,6 +82,20 @@ export default class EnvironmentsStore { ...@@ -71,6 +82,20 @@ export default class EnvironmentsStore {
return filteredEnvironments; return filteredEnvironments;
} }
/**
* Stores the pagination information needed to render the pagination for the
* table.
*
* Normalizes the headers to uppercase since they can be provided either
* in uppercase or lowercase.
*
* Parses to an integer the normalized ones needed for the pagination component.
*
* Stores the normalized and parsed information.
*
* @param {Object} pagination = {}
* @return {Object}
*/
setPagination(pagination = {}) { setPagination(pagination = {}) {
const normalizedHeaders = normalizeHeaders(pagination); const normalizedHeaders = normalizeHeaders(pagination);
const paginationInformation = parseIntPagination(normalizedHeaders); const paginationInformation = parseIntPagination(normalizedHeaders);
......
/**
* 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,
...@@ -32,9 +34,6 @@ export default { ...@@ -32,9 +34,6 @@ export default {
groupId(group) { groupId(group) {
return `ci-badge-${_.escape(group.name)}`; return `ci-badge-${_.escape(group.name)}`;
}, },
buildConnnectorClass(index) {
return index === 0 && !this.isFirstColumn ? '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,8 +2,9 @@ import Vue from 'vue'; ...@@ -2,8 +2,9 @@ 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_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 pipelineGraph from './components/graph/graph_component.vue';
import pipelineHeader from './components/header_component.vue'; import pipelineHeader from './components/header_component.vue';
import eventHub from './event_hub'; import eventHub from './event_hub';
...@@ -22,6 +23,7 @@ export default () => { ...@@ -22,6 +23,7 @@ export default () => {
components: { components: {
pipelineGraph, pipelineGraph,
}, },
mixins: [GraphEEMixin],
data() { data() {
return { return {
mediator, mediator,
...@@ -44,6 +46,10 @@ export default () => { ...@@ -44,6 +46,10 @@ export default () => {
}, },
on: { on: {
refreshPipelineGraph: this.requestRefreshPipelineGraph, refreshPipelineGraph: this.requestRefreshPipelineGraph,
onClickTriggeredBy: (parentPipeline, pipeline) =>
this.clickTriggeredByPipeline(parentPipeline, pipeline),
onClickTriggered: (parentPipeline, pipeline) =>
this.clickTriggeredPipeline(parentPipeline, pipeline),
}, },
}); });
}, },
......
...@@ -376,18 +376,21 @@ img.emoji { ...@@ -376,18 +376,21 @@ 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; }
.prepend-left-10 { margin-left: 10px; } .prepend-left-10 { margin-left: 10px; }
.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; }
.append-right-10 { margin-right: 10px; } .append-right-10 { margin-right: 10px; }
.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; }
...@@ -396,6 +399,7 @@ img.emoji { ...@@ -396,6 +399,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; }
.center { text-align: center; } .center { text-align: center; }
.vertical-align-middle { vertical-align: middle; } .vertical-align-middle { vertical-align: middle; }
......
...@@ -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
......
...@@ -5,4 +5,5 @@ ...@@ -5,4 +5,5 @@
- if current_user && can?(current_user, :download_code, project) - if current_user && can?(current_user, :download_code, project)
= render 'shared/no_ssh' = render 'shared/no_ssh'
= render 'shared/no_password' = render 'shared/no_password'
= render 'shared/auto_devops_implicitly_enabled_banner', project: project - unless project.empty_repo?
= render 'shared/auto_devops_implicitly_enabled_banner', project: project
...@@ -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
.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
...@@ -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 = (
......
...@@ -6,6 +6,7 @@ const argumentsParser = require('commander'); ...@@ -6,6 +6,7 @@ const argumentsParser = require('commander');
const webpackConfig = require('./webpack.config.js'); const webpackConfig = require('./webpack.config.js');
const ROOT_PATH = path.resolve(__dirname, '..'); const ROOT_PATH = path.resolve(__dirname, '..');
const SPECS_PATH = /^(?:\.[\\\/])?(ee[\\\/])?spec[\\\/]javascripts[\\\/]/;
function fatalError(message) { function fatalError(message) {
console.error(chalk.red(`\nError: ${message}\n`)); console.error(chalk.red(`\nError: ${message}\n`));
...@@ -41,9 +42,19 @@ const specFilters = argumentsParser ...@@ -41,9 +42,19 @@ const specFilters = argumentsParser
) )
.parse(process.argv).filterSpec; .parse(process.argv).filterSpec;
if (specFilters.length) { const createContext = (specFiles, regex, suffix) => {
const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/; const newContext = specFiles.reduce((context, file) => {
const relativePath = file.replace(SPECS_PATH, '');
context[file] = `./${relativePath}`;
return context;
}, {});
webpackConfig.plugins.push(
new webpack.ContextReplacementPlugin(regex, path.join(ROOT_PATH, suffix), newContext),
);
};
if (specFilters.length) {
// resolve filters // resolve filters
let filteredSpecFiles = specFilters.map(filter => let filteredSpecFiles = specFilters.map(filter =>
glob glob
...@@ -64,23 +75,15 @@ if (specFilters.length) { ...@@ -64,23 +75,15 @@ if (specFilters.length) {
fatalError('Your filter did not match any test files.'); fatalError('Your filter did not match any test files.');
} }
if (!filteredSpecFiles.every(file => specsPath.test(file))) { if (!filteredSpecFiles.every(file => SPECS_PATH.test(file))) {
fatalError('Test files must be located within /spec/javascripts.'); fatalError('Test files must be located within /spec/javascripts.');
} }
const newContext = filteredSpecFiles.reduce((context, file) => { const CE_FILES = filteredSpecFiles.filter(file => !file.startsWith('ee'));
const relativePath = file.replace(specsPath, ''); createContext(CE_FILES, /[^e]{2}[\\\/]spec[\\\/]javascripts$/, 'spec/javascripts');
context[file] = `./${relativePath}`;
return context;
}, {});
webpackConfig.plugins.push( const EE_FILES = filteredSpecFiles.filter(file => file.startsWith('ee'));
new webpack.ContextReplacementPlugin( createContext(EE_FILES, /ee[\\\/]spec[\\\/]javascripts$/, 'ee/spec/javascripts');
/spec[\\\/]javascripts$/,
path.join(ROOT_PATH, 'spec/javascripts'),
newContext,
),
);
} }
// Karma configuration // Karma configuration
...@@ -111,10 +114,20 @@ module.exports = function(config) { ...@@ -111,10 +114,20 @@ module.exports = function(config) {
], ],
preprocessors: { preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'], 'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
'ee/spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
}, },
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) {
...@@ -123,6 +136,19 @@ module.exports = function(config) { ...@@ -123,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
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
......
...@@ -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
......
...@@ -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
...@@ -119,6 +119,7 @@ This [documentation](merge_request_workflow.md) outlines the current merge reque ...@@ -119,6 +119,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. |
......
...@@ -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:
......
...@@ -96,7 +96,7 @@ all matching branches: ...@@ -96,7 +96,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),
......
...@@ -103,17 +103,15 @@ module API ...@@ -103,17 +103,15 @@ module API
detail 'This feature was introduced in GitLab 11.9' detail 'This feature was introduced in GitLab 11.9'
end end
post ':id/milestones/:milestone_id/promote' do post ':id/milestones/:milestone_id/promote' do
begin authorize! :admin_milestone, user_project
authorize! :admin_milestone, user_project authorize! :admin_milestone, user_project.group
authorize! :admin_milestone, user_project.group
milestone = user_project.milestones.find(params[:milestone_id]) milestone = user_project.milestones.find(params[:milestone_id])
Milestones::PromoteService.new(user_project, current_user).execute(milestone) Milestones::PromoteService.new(user_project, current_user).execute(milestone)
status(200) status(200)
rescue Milestones::PromoteService::PromoteMilestoneError => error rescue Milestones::PromoteService::PromoteMilestoneError => error
render_api_error!(error.message, 400) render_api_error!(error.message, 400)
end
end end
end end
end end
......
...@@ -89,11 +89,9 @@ module API ...@@ -89,11 +89,9 @@ module API
optional :format, type: String, desc: 'The archive format' optional :format, type: String, desc: 'The archive format'
end end
get ':id/repository/archive', requirements: { format: Gitlab::PathRegex.archive_formats_regex } do get ':id/repository/archive', requirements: { format: Gitlab::PathRegex.archive_formats_regex } do
begin send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true
send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true rescue
rescue not_found!('File')
not_found!('File')
end
end end
desc 'Compare two branches, tags, or commits' do desc 'Compare two branches, tags, or commits' do
...@@ -118,12 +116,10 @@ module API ...@@ -118,12 +116,10 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
end end
get ':id/repository/contributors' do get ':id/repository/contributors' do
begin contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort]))
contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort])) present paginate(contributors), with: Entities::Contributor
present paginate(contributors), with: Entities::Contributor rescue
rescue not_found!
not_found!
end
end end
desc 'Get the common ancestor between commits' do desc 'Get the common ancestor between commits' do
......
...@@ -11,11 +11,10 @@ module Gitlab ...@@ -11,11 +11,10 @@ module Gitlab
# So we chose a way to use ::Ci::Build directly and we don't change the `archive!` method until 11.1 # So we chose a way to use ::Ci::Build directly and we don't change the `archive!` method until 11.1
::Ci::Build.finished.without_archived_trace ::Ci::Build.finished.without_archived_trace
.where(id: start_id..stop_id).find_each do |build| .where(id: start_id..stop_id).find_each do |build|
begin
build.trace.archive! build.trace.archive!
rescue => e rescue => e
Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}" Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}"
end
end end
end end
end end
......
...@@ -302,14 +302,12 @@ module Gitlab ...@@ -302,14 +302,12 @@ module Gitlab
ldap_identities = Identity.where("provider like 'ldap%'").where(id: start_id..end_id) ldap_identities = Identity.where("provider like 'ldap%'").where(id: start_id..end_id)
ldap_identities.each do |identity| ldap_identities.each do |identity|
begin identity.extern_uid = Gitlab::Auth::LDAP::DN.new(identity.extern_uid).to_normalized_s
identity.extern_uid = Gitlab::Auth::LDAP::DN.new(identity.extern_uid).to_normalized_s unless identity.save
unless identity.save Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\". Skipping."
Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\". Skipping."
end
rescue Gitlab::Auth::LDAP::DN::FormatError => e
Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\" due to \"#{e.message}\". Skipping."
end end
rescue Gitlab::Auth::LDAP::DN::FormatError => e
Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\" due to \"#{e.message}\". Skipping."
end end
end end
......
...@@ -34,18 +34,16 @@ module Gitlab ...@@ -34,18 +34,16 @@ module Gitlab
def filter_error_files(files) def filter_error_files(files)
files.partition do |file| files.partition do |file|
begin file.to_h
file.to_h true
true rescue => e
rescue => e msg = <<~MSG
msg = <<~MSG
Error parsing path "#{file.path}": Error parsing path "#{file.path}":
#{e.message} #{e.message}
#{e.backtrace.join("\n ")} #{e.backtrace.join("\n ")}
MSG MSG
Rails.logger.error(msg) Rails.logger.error(msg)
false false
end
end end
end end
......
...@@ -79,31 +79,29 @@ module Gitlab ...@@ -79,31 +79,29 @@ module Gitlab
create_labels create_labels
client.issues(repo).each do |issue| client.issues(repo).each do |issue|
begin description = ''
description = '' description += @formatter.author_line(issue.author) unless find_user_id(issue.author)
description += @formatter.author_line(issue.author) unless find_user_id(issue.author) description += issue.description
description += issue.description
label_name = issue.kind
label_name = issue.kind milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil
milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil
gitlab_issue = project.issues.create!(
gitlab_issue = project.issues.create!( iid: issue.iid,
iid: issue.iid, title: issue.title,
title: issue.title, description: description,
description: description, state: issue.state,
state: issue.state, author_id: gitlab_user_id(project, issue.author),
author_id: gitlab_user_id(project, issue.author), milestone: milestone,
milestone: milestone, created_at: issue.created_at,
created_at: issue.created_at, updated_at: issue.updated_at
updated_at: issue.updated_at )
)
gitlab_issue.labels << @labels[label_name]
gitlab_issue.labels << @labels[label_name]
import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted?
import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? rescue StandardError => e
rescue StandardError => e errors << { type: :issue, iid: issue.iid, errors: e.message }
errors << { type: :issue, iid: issue.iid, errors: e.message }
end
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -150,37 +148,35 @@ module Gitlab ...@@ -150,37 +148,35 @@ module Gitlab
pull_requests = client.pull_requests(repo) pull_requests = client.pull_requests(repo)
pull_requests.each do |pull_request| pull_requests.each do |pull_request|
begin description = ''
description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author)
description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) description += pull_request.description
description += pull_request.description
source_branch_sha = pull_request.source_branch_sha
source_branch_sha = pull_request.source_branch_sha target_branch_sha = pull_request.target_branch_sha
target_branch_sha = pull_request.target_branch_sha source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha
source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha
target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha
merge_request = project.merge_requests.create!(
merge_request = project.merge_requests.create!( iid: pull_request.iid,
iid: pull_request.iid, title: pull_request.title,
title: pull_request.title, description: description,
description: description, source_project: project,
source_project: project, source_branch: pull_request.source_branch_name,
source_branch: pull_request.source_branch_name, source_branch_sha: source_branch_sha,
source_branch_sha: source_branch_sha, target_project: project,
target_project: project, target_branch: pull_request.target_branch_name,
target_branch: pull_request.target_branch_name, target_branch_sha: target_branch_sha,
target_branch_sha: target_branch_sha, state: pull_request.state,
state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author),
author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil,
assignee_id: nil, created_at: pull_request.created_at,
created_at: pull_request.created_at, updated_at: pull_request.updated_at
updated_at: pull_request.updated_at )
)
import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue StandardError => e
rescue StandardError => e errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw }
errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw }
end
end end
end end
...@@ -211,23 +207,21 @@ module Gitlab ...@@ -211,23 +207,21 @@ module Gitlab
end end
inline_comments.each do |comment| inline_comments.each do |comment|
begin attributes = pull_request_comment_attributes(comment)
attributes = pull_request_comment_attributes(comment) attributes[:discussion_id] = discussion_map[comment.parent_id] if comment.has_parent?
attributes[:discussion_id] = discussion_map[comment.parent_id] if comment.has_parent?
attributes.merge!( attributes.merge!(
position: position_map[comment.iid], position: position_map[comment.iid],
type: 'DiffNote') type: 'DiffNote')
note = merge_request.notes.create!(attributes) note = merge_request.notes.create!(attributes)
# We can't store a discussion ID until a note is created, so if # We can't store a discussion ID until a note is created, so if
# replies are created before the parent the discussion ID won't be # replies are created before the parent the discussion ID won't be
# linked properly. # linked properly.
discussion_map[comment.iid] = note.discussion_id discussion_map[comment.iid] = note.discussion_id
rescue StandardError => e rescue StandardError => e
errors << { type: :pull_request, iid: comment.iid, errors: e.message } errors << { type: :pull_request, iid: comment.iid, errors: e.message }
end
end end
end end
...@@ -245,11 +239,9 @@ module Gitlab ...@@ -245,11 +239,9 @@ module Gitlab
def import_standalone_pr_comments(pr_comments, merge_request) def import_standalone_pr_comments(pr_comments, merge_request)
pr_comments.each do |comment| pr_comments.each do |comment|
begin merge_request.notes.create!(pull_request_comment_attributes(comment))
merge_request.notes.create!(pull_request_comment_attributes(comment)) rescue StandardError => e
rescue StandardError => e errors << { type: :pull_request, iid: comment.iid, errors: e.message }
errors << { type: :pull_request, iid: comment.iid, errors: e.message }
end
end end
end end
......
...@@ -162,27 +162,23 @@ module Gitlab ...@@ -162,27 +162,23 @@ module Gitlab
restore_branches(batch) if recover_missing_commits restore_branches(batch) if recover_missing_commits
batch.each do |pull_request| batch.each do |pull_request|
begin import_bitbucket_pull_request(pull_request)
import_bitbucket_pull_request(pull_request) rescue StandardError => e
rescue StandardError => e backtrace = Gitlab::Profiler.clean_backtrace(e.backtrace)
backtrace = Gitlab::Profiler.clean_backtrace(e.backtrace) log_error(stage: 'import_pull_requests', iid: pull_request.iid, error: e.message, backtrace: backtrace)
log_error(stage: 'import_pull_requests', iid: pull_request.iid, error: e.message, backtrace: backtrace)
errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
end
end end
end end
end end
def delete_temp_branches def delete_temp_branches
@temp_branches.each do |branch| @temp_branches.each do |branch|
begin client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
client.delete_branch(project_key, repository_slug, branch.name, branch.sha) project.repository.delete_branch(branch.name)
project.repository.delete_branch(branch.name) rescue BitbucketServer::Connection::ConnectionError => e
rescue BitbucketServer::Connection::ConnectionError => e log_error(stage: 'delete_temp_branches', branch: branch.name, error: e.message)
log_error(stage: 'delete_temp_branches', branch: branch.name, error: e.message) @errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
@errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
end
end end
end end
...@@ -323,16 +319,14 @@ module Gitlab ...@@ -323,16 +319,14 @@ module Gitlab
def import_standalone_pr_comments(pr_comments, merge_request) def import_standalone_pr_comments(pr_comments, merge_request)
pr_comments.each do |comment| pr_comments.each do |comment|
begin merge_request.notes.create!(pull_request_comment_attributes(comment))
merge_request.notes.create!(pull_request_comment_attributes(comment))
comment.comments.each do |replies| comment.comments.each do |replies|
merge_request.notes.create!(pull_request_comment_attributes(replies)) merge_request.notes.create!(pull_request_comment_attributes(replies))
end
rescue StandardError => e
log_error(stage: 'import_standalone_pr_comments', merge_request_id: merge_request.id, comment_id: comment.id, error: e.message)
errors << { type: :pull_request, comment_id: comment.id, errors: e.message }
end end
rescue StandardError => e
log_error(stage: 'import_standalone_pr_comments', merge_request_id: merge_request.id, comment_id: comment.id, error: e.message)
errors << { type: :pull_request, comment_id: comment.id, errors: e.message }
end end
end end
......
...@@ -98,7 +98,7 @@ module Gitlab ...@@ -98,7 +98,7 @@ module Gitlab
def read_uint32(gz) def read_uint32(gz)
binary = gz.read(4) binary = gz.read(4)
binary.unpack('L>')[0] if binary binary.unpack1('L>') if binary
end end
def read_string(gz) def read_string(gz)
......
...@@ -76,7 +76,7 @@ module Gitlab ...@@ -76,7 +76,7 @@ module Gitlab
postgresql? && version.to_f >= 9.4 postgresql? && version.to_f >= 9.4
end end
def self.pg_stat_wal_receiver_supported? def self.postgresql_minimum_supported_version?
postgresql? && version.to_f >= 9.6 postgresql? && version.to_f >= 9.6
end end
...@@ -98,6 +98,10 @@ module Gitlab ...@@ -98,6 +98,10 @@ module Gitlab
Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn' Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn'
end end
def self.pg_last_xact_replay_timestamp
'pg_last_xact_replay_timestamp'
end
def self.nulls_last_order(field, direction = 'ASC') def self.nulls_last_order(field, direction = 'ASC')
order = "#{field} #{direction}" order = "#{field} #{direction}"
......
...@@ -35,12 +35,10 @@ module Gitlab ...@@ -35,12 +35,10 @@ module Gitlab
threads = Array.new(thread_count) do threads = Array.new(thread_count) do
Thread.new do Thread.new do
pool.with_connection do |connection| pool.with_connection do |connection|
begin Thread.current[MULTI_THREAD_AR_CONNECTION] = connection
Thread.current[MULTI_THREAD_AR_CONNECTION] = connection yield
yield ensure
ensure Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
end
end end
end end
end end
......
...@@ -22,7 +22,7 @@ module Gitlab ...@@ -22,7 +22,7 @@ module Gitlab
# Casts binary data to a SHA1 in hexadecimal. # Casts binary data to a SHA1 in hexadecimal.
def deserialize(value) def deserialize(value)
value = super(value) value = super(value)
value ? value.unpack(PACK_FORMAT)[0] : nil value ? value.unpack1(PACK_FORMAT) : nil
end end
# Casts a SHA1 in hexadecimal to the proper binary format. # Casts a SHA1 in hexadecimal to the proper binary format.
......
...@@ -75,13 +75,11 @@ module Gitlab ...@@ -75,13 +75,11 @@ module Gitlab
@certs = stub_cert_paths.flat_map do |cert_file| @certs = stub_cert_paths.flat_map do |cert_file|
File.read(cert_file).scan(PEM_REGEX).map do |cert| File.read(cert_file).scan(PEM_REGEX).map do |cert|
begin OpenSSL::X509::Certificate.new(cert).to_pem
OpenSSL::X509::Certificate.new(cert).to_pem rescue OpenSSL::OpenSSLError => e
rescue OpenSSL::OpenSSLError => e Rails.logger.error "Could not load certificate #{cert_file} #{e}"
Rails.logger.error "Could not load certificate #{cert_file} #{e}" Gitlab::Sentry.track_exception(e, extra: { cert_file: cert_file })
Gitlab::Sentry.track_exception(e, extra: { cert_file: cert_file }) nil
nil
end
end.compact end.compact
end.uniq.join("\n") end.uniq.join("\n")
end end
......
...@@ -13,17 +13,15 @@ module Gitlab ...@@ -13,17 +13,15 @@ module Gitlab
current_blob_data = nil current_blob_data = nil
@rpc_response.each do |msg| @rpc_response.each do |msg|
begin if msg.oid.blank? && msg.data.blank?
if msg.oid.blank? && msg.data.blank? next
next elsif msg.oid.present?
elsif msg.oid.present? yield new_blob(current_blob_data) if current_blob_data
yield new_blob(current_blob_data) if current_blob_data
current_blob_data = msg.to_h.slice(:oid, :path, :size, :revision, :mode)
current_blob_data = msg.to_h.slice(:oid, :path, :size, :revision, :mode) current_blob_data[:data] = msg.data.dup
current_blob_data[:data] = msg.data.dup else
else current_blob_data[:data] << msg.data
current_blob_data[:data] << msg.data
end
end end
end end
......
...@@ -89,12 +89,10 @@ module Gitlab ...@@ -89,12 +89,10 @@ module Gitlab
def import_labels def import_labels
fetch_resources(:labels, repo, per_page: 100) do |labels| fetch_resources(:labels, repo, per_page: 100) do |labels|
labels.each do |raw| labels.each do |raw|
begin gh_label = LabelFormatter.new(project, raw)
gh_label = LabelFormatter.new(project, raw) gh_label.create!
gh_label.create! rescue => e
rescue => e errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message }
errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message }
end
end end
end end
...@@ -104,12 +102,10 @@ module Gitlab ...@@ -104,12 +102,10 @@ module Gitlab
def import_milestones def import_milestones
fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones|
milestones.each do |raw| milestones.each do |raw|
begin gh_milestone = MilestoneFormatter.new(project, raw)
gh_milestone = MilestoneFormatter.new(project, raw) gh_milestone.create!
gh_milestone.create! rescue => e
rescue => e errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message }
errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message }
end
end end
end end
end end
...@@ -223,24 +219,22 @@ module Gitlab ...@@ -223,24 +219,22 @@ module Gitlab
def create_comments(comments) def create_comments(comments)
ActiveRecord::Base.no_touching do ActiveRecord::Base.no_touching do
comments.each do |raw| comments.each do |raw|
begin comment = CommentFormatter.new(project, raw, client)
comment = CommentFormatter.new(project, raw, client)
# GH does not return info about comment's parent, so we guess it by checking its URL! # GH does not return info about comment's parent, so we guess it by checking its URL!
*_, parent, iid = URI(raw.html_url).path.split('/') *_, parent, iid = URI(raw.html_url).path.split('/')
issuable = if parent == 'issues' issuable = if parent == 'issues'
Issue.find_by(project_id: project.id, iid: iid) Issue.find_by(project_id: project.id, iid: iid)
else else
MergeRequest.find_by(target_project_id: project.id, iid: iid) MergeRequest.find_by(target_project_id: project.id, iid: iid)
end end
next unless issuable next unless issuable
issuable.notes.create!(comment.attributes) issuable.notes.create!(comment.attributes)
rescue => e rescue => e
errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end end
end end
end end
...@@ -281,12 +275,10 @@ module Gitlab ...@@ -281,12 +275,10 @@ module Gitlab
def import_releases def import_releases
fetch_resources(:releases, repo, per_page: 100) do |releases| fetch_resources(:releases, repo, per_page: 100) do |releases|
releases.each do |raw| releases.each do |raw|
begin gh_release = ReleaseFormatter.new(project, raw)
gh_release = ReleaseFormatter.new(project, raw) gh_release.create! if gh_release.valid?
gh_release.create! if gh_release.valid? rescue => e
rescue => e errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message }
errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message }
end
end end
end end
end end
......
...@@ -52,10 +52,8 @@ module Gitlab ...@@ -52,10 +52,8 @@ module Gitlab
pool&.with do |connection| pool&.with do |connection|
prepared.each_slice(settings[:packet_size]) do |slice| prepared.each_slice(settings[:packet_size]) do |slice|
begin connection.write_points(slice)
connection.write_points(slice) rescue StandardError
rescue StandardError
end
end end
end end
rescue Errno::EADDRNOTAVAIL, SocketError => ex rescue Errno::EADDRNOTAVAIL, SocketError => ex
......
...@@ -10,6 +10,7 @@ module GoogleApi ...@@ -10,6 +10,7 @@ module GoogleApi
class Client < GoogleApi::Auth class Client < GoogleApi::Auth
SCOPE = 'https://www.googleapis.com/auth/cloud-platform'.freeze SCOPE = 'https://www.googleapis.com/auth/cloud-platform'.freeze
LEAST_TOKEN_LIFE_TIME = 10.minutes LEAST_TOKEN_LIFE_TIME = 10.minutes
CLUSTER_MASTER_AUTH_USERNAME = 'admin'.freeze
class << self class << self
def session_key_for_token def session_key_for_token
...@@ -64,6 +65,12 @@ module GoogleApi ...@@ -64,6 +65,12 @@ module GoogleApi
"node_config": { "node_config": {
"machine_type": machine_type "machine_type": machine_type
}, },
"master_auth": {
"username": CLUSTER_MASTER_AUTH_USERNAME,
"client_certificate_config": {
issue_client_certificate: true
}
},
"legacy_abac": { "legacy_abac": {
"enabled": legacy_abac "enabled": legacy_abac
} }
......
...@@ -11,14 +11,13 @@ namespace :gitlab do ...@@ -11,14 +11,13 @@ namespace :gitlab do
Ci::Build.joins(:project) Ci::Build.joins(:project)
.with_artifacts_stored_locally .with_artifacts_stored_locally
.find_each(batch_size: 10) do |build| .find_each(batch_size: 10) do |build|
begin
build.artifacts_file.migrate!(ObjectStorage::Store::REMOTE)
build.artifacts_metadata.migrate!(ObjectStorage::Store::REMOTE)
logger.info("Transferred artifact ID #{build.id} with size #{build.artifacts_size} to object storage") build.artifacts_file.migrate!(ObjectStorage::Store::REMOTE)
rescue => e build.artifacts_metadata.migrate!(ObjectStorage::Store::REMOTE)
logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end logger.info("Transferred artifact ID #{build.id} with size #{build.artifacts_size} to object storage")
rescue => e
logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end end
end end
end end
......
...@@ -9,13 +9,12 @@ namespace :gitlab do ...@@ -9,13 +9,12 @@ namespace :gitlab do
LfsObject.with_files_stored_locally LfsObject.with_files_stored_locally
.find_each(batch_size: 10) do |lfs_object| .find_each(batch_size: 10) do |lfs_object|
begin
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to object storage") lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
rescue => e
logger.error("Failed to transfer LFS object #{lfs_object.oid} with error: #{e.message}") logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to object storage")
end rescue => e
logger.error("Failed to transfer LFS object #{lfs_object.oid} with error: #{e.message}")
end end
end end
end end
......
...@@ -26,13 +26,12 @@ namespace :gitlab do ...@@ -26,13 +26,12 @@ namespace :gitlab do
Ci::Build.joins(:project) Ci::Build.joins(:project)
.with_archived_trace_stored_locally .with_archived_trace_stored_locally
.find_each(batch_size: 10) do |build| .find_each(batch_size: 10) do |build|
begin
build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
logger.info("Transferred job trace of #{build.id} to object storage") build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
rescue => e
logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}") logger.info("Transferred job trace of #{build.id} to object storage")
end rescue => e
logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end end
end end
end end
......
...@@ -19,11 +19,9 @@ unless Rails.env.production? ...@@ -19,11 +19,9 @@ unless Rails.env.production?
desc "GitLab | lint | Lint HAML files" desc "GitLab | lint | Lint HAML files"
task :haml do task :haml do
begin Rake::Task['haml_lint'].invoke
Rake::Task['haml_lint'].invoke rescue RuntimeError # The haml_lint tasks raise a RuntimeError
rescue RuntimeError # The haml_lint tasks raise a RuntimeError exit(1)
exit(1)
end
end end
desc "GitLab | lint | Run several lint checks" desc "GitLab | lint | Run several lint checks"
......
...@@ -2,49 +2,43 @@ desc "GitLab | Build internal ids for issues and merge requests" ...@@ -2,49 +2,43 @@ desc "GitLab | Build internal ids for issues and merge requests"
task migrate_iids: :environment do task migrate_iids: :environment do
puts 'Issues'.color(:yellow) puts 'Issues'.color(:yellow)
Issue.where(iid: nil).find_each(batch_size: 100) do |issue| Issue.where(iid: nil).find_each(batch_size: 100) do |issue|
begin issue.set_iid
issue.set_iid
if issue.update_attribute(:iid, issue.iid) if issue.update_attribute(:iid, issue.iid)
print '.' print '.'
else else
print 'F'
end
rescue
print 'F' print 'F'
end end
rescue
print 'F'
end end
puts 'done' puts 'done'
puts 'Merge Requests'.color(:yellow) puts 'Merge Requests'.color(:yellow)
MergeRequest.where(iid: nil).find_each(batch_size: 100) do |mr| MergeRequest.where(iid: nil).find_each(batch_size: 100) do |mr|
begin mr.set_iid
mr.set_iid
if mr.update_attribute(:iid, mr.iid) if mr.update_attribute(:iid, mr.iid)
print '.' print '.'
else else
print 'F'
end
rescue
print 'F' print 'F'
end end
rescue
print 'F'
end end
puts 'done' puts 'done'
puts 'Milestones'.color(:yellow) puts 'Milestones'.color(:yellow)
Milestone.where(iid: nil).find_each(batch_size: 100) do |m| Milestone.where(iid: nil).find_each(batch_size: 100) do |m|
begin m.set_iid
m.set_iid
if m.update_attribute(:iid, m.iid) if m.update_attribute(:iid, m.iid)
print '.' print '.'
else else
print 'F'
end
rescue
print 'F' print 'F'
end end
rescue
print 'F'
end end
puts 'done' puts 'done'
......
...@@ -966,15 +966,6 @@ msgstr "" ...@@ -966,15 +966,6 @@ msgstr ""
msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}" msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
msgstr "" msgstr ""
msgid "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}."
msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
msgstr ""
msgid "AutoDevOps|enable Auto DevOps"
msgstr ""
msgid "Automatically marked as default internal user" msgid "Automatically marked as default internal user"
msgstr "" msgstr ""
...@@ -2315,6 +2306,9 @@ msgstr "" ...@@ -2315,6 +2306,9 @@ msgstr ""
msgid "Contribution" msgid "Contribution"
msgstr "" msgstr ""
msgid "Contribution Analytics"
msgstr ""
msgid "Contribution Charts" msgid "Contribution Charts"
msgstr "" msgstr ""
...@@ -3373,12 +3367,6 @@ msgstr "" ...@@ -3373,12 +3367,6 @@ msgstr ""
msgid "Except policy:" msgid "Except policy:"
msgstr "" msgstr ""
msgid "Existing Git repository"
msgstr ""
msgid "Existing folder"
msgstr ""
msgid "Existing members and groups" msgid "Existing members and groups"
msgstr "" msgstr ""
...@@ -4073,9 +4061,6 @@ msgstr "" ...@@ -4073,9 +4061,6 @@ msgstr ""
msgid "If enabled" msgid "If enabled"
msgstr "" msgstr ""
msgid "If you already have files you can push them using the %{link_to_cli} below."
msgstr ""
msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>." msgid "If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgstr "" msgstr ""
...@@ -4540,9 +4525,6 @@ msgstr "" ...@@ -4540,9 +4525,6 @@ msgstr ""
msgid "Learn more about Kubernetes" msgid "Learn more about Kubernetes"
msgstr "" msgstr ""
msgid "Learn more about protected branches"
msgstr ""
msgid "Learn more about signing commits" msgid "Learn more about signing commits"
msgstr "" msgstr ""
...@@ -5210,9 +5192,6 @@ msgstr "" ...@@ -5210,9 +5192,6 @@ msgstr ""
msgid "Not started" msgid "Not started"
msgstr "" msgstr ""
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}." msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
msgstr "" msgstr ""
...@@ -5404,9 +5383,6 @@ msgstr "" ...@@ -5404,9 +5383,6 @@ msgstr ""
msgid "Other Labels" msgid "Other Labels"
msgstr "" msgstr ""
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
msgid "Outbound requests" msgid "Outbound requests"
msgstr "" msgstr ""
...@@ -6265,6 +6241,12 @@ msgstr "" ...@@ -6265,6 +6241,12 @@ msgstr ""
msgid "Push" msgid "Push"
msgstr "" msgstr ""
msgid "Push an existing Git repository"
msgstr ""
msgid "Push an existing folder"
msgstr ""
msgid "Push events" msgid "Push events"
msgstr "" msgstr ""
...@@ -8768,6 +8750,12 @@ msgstr "" ...@@ -8768,6 +8750,12 @@ msgstr ""
msgid "You can also star a label to make it a priority label." msgid "You can also star a label to make it a priority label."
msgstr "" msgstr ""
msgid "You can also upload existing files from your computer using the instructions below."
msgstr ""
msgid "You can create files directly in GitLab using one of the following options."
msgstr ""
msgid "You can easily contribute to them by requesting to join these groups." msgid "You can easily contribute to them by requesting to join these groups."
msgstr "" msgstr ""
...@@ -8972,9 +8960,6 @@ msgstr "" ...@@ -8972,9 +8960,6 @@ msgstr ""
msgid "branch name" msgid "branch name"
msgstr "" msgstr ""
msgid "command line instructions"
msgstr ""
msgid "commented on %{link_to_project}" msgid "commented on %{link_to_project}"
msgstr "" msgstr ""
......
This diff is collapsed.
...@@ -61,6 +61,7 @@ module QA ...@@ -61,6 +61,7 @@ module QA
autoload :File, 'qa/resource/file' autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork' autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key' autoload :SSHKey, 'qa/resource/ssh_key'
autoload :Snippet, 'qa/resource/snippet'
module Events module Events
autoload :Base, 'qa/resource/events/base' autoload :Base, 'qa/resource/events/base'
...@@ -142,6 +143,12 @@ module QA ...@@ -142,6 +143,12 @@ module QA
module Dashboard module Dashboard
autoload :Projects, 'qa/page/dashboard/projects' autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups' autoload :Groups, 'qa/page/dashboard/groups'
module Snippet
autoload :New, 'qa/page/dashboard/snippet/new'
autoload :Index, 'qa/page/dashboard/snippet/index'
autoload :Show, 'qa/page/dashboard/snippet/show'
end
end end
module Group module Group
......
...@@ -2,8 +2,6 @@ module QA ...@@ -2,8 +2,6 @@ module QA
module Page module Page
module Dashboard module Dashboard
class Projects < Page::Base class Projects < Page::Base
view 'app/views/dashboard/projects/index.html.haml'
view 'app/views/shared/projects/_search_form.html.haml' do view 'app/views/shared/projects/_search_form.html.haml' do
element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ # rubocop:disable QA/ElementWithPattern element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ # rubocop:disable QA/ElementWithPattern
end end
......
# frozen_string_literal: true
module QA
module Page
module Dashboard
module Snippet
class Index < Page::Base
view 'app/views/layouts/header/_new_dropdown.haml' do
element :new_menu_toggle
element :global_new_snippet_link
end
def go_to_new_snippet_page
click_element :new_menu_toggle
click_element :global_new_snippet_link
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Dashboard
module Snippet
class New < Page::Base
view 'app/views/shared/form_elements/_description.html.haml' do
element :issuable_form_description
end
view 'app/views/shared/snippets/_form.html.haml' do
element :snippet_title
element :snippet_file_name
element :create_snippet_button
end
def fill_title(title)
fill_element :snippet_title, title
end
def fill_description(description)
fill_element :issuable_form_description, description
end
def set_visibility(visibility)
choose visibility
end
def fill_file_name(name)
finished_loading?
fill_element :snippet_file_name, name
end
def fill_file_content(content)
finished_loading?
text_area.set content
end
def create_snippet
click_element :create_snippet_button
end
private
def text_area
find('#editor>textarea', visible: false)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Dashboard
module Snippet
class Show < Page::Base
view 'app/views/shared/snippets/_header.html.haml' do
element :snippet_title
element :snippet_description
element :embed_type
element :snippet_box
end
view 'app/views/projects/blob/_header_content.html.haml' do
element :file_title_name
end
view 'app/views/shared/_file_highlight.html.haml' do
element :file_content
end
def has_snippet_title?(snippet_title)
within_element(:snippet_title) do
has_text?(snippet_title)
end
end
def has_snippet_description?(snippet_description)
within_element(:snippet_description) do
has_text?(snippet_description)
end
end
def has_embed_type?(embed_type)
within_element(:embed_type) do
has_text?(embed_type)
end
end
def has_visibility_type?(visibility_type)
within_element(:snippet_box) do
has_text?(visibility_type)
end
end
def has_file_name?(file_name)
within_element(:file_title_name) do
has_text?(file_name)
end
end
def has_file_content?(file_content)
finished_loading?
within_element(:file_content) do
has_text?(file_content)
end
end
end
end
end
end
end
...@@ -19,6 +19,7 @@ module QA ...@@ -19,6 +19,7 @@ module QA
element :admin_area_link element :admin_area_link
element :projects_dropdown element :projects_dropdown
element :groups_dropdown element :groups_dropdown
element :snippets_link
end end
view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
...@@ -66,6 +67,10 @@ module QA ...@@ -66,6 +67,10 @@ module QA
end end
end end
def go_to_snippets
click_element :snippets_link
end
def has_personal_area?(wait: Capybara.default_max_wait_time) def has_personal_area?(wait: Capybara.default_max_wait_time)
has_element?(:user_avatar, wait: wait) has_element?(:user_avatar, wait: wait)
end end
......
...@@ -43,11 +43,9 @@ module QA ...@@ -43,11 +43,9 @@ module QA
def create_new_file_from_template(file_name, template) def create_new_file_from_template(file_name, template)
click_element :new_file click_element :new_file
within_element(:template_list) do within_element(:template_list) do
begin click_on file_name
click_on file_name rescue Capybara::ElementNotFound
rescue Capybara::ElementNotFound raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
end
end end
wait(reload: false) do wait(reload: false) do
......
# frozen_string_literal: true
module QA
module Resource
class Snippet < Base
attr_accessor :title, :description, :file_content, :visibility, :file_name
def initialize
@title = 'New snippet title'
@description = 'The snippet description'
@visibility = 'Public'
@file_content = 'The snippet content'
@file_name = 'New snippet file name'
end
def fabricate!
Page::Dashboard::Snippet::Index.perform(&:go_to_new_snippet_page)
Page::Dashboard::Snippet::New.perform do |page|
page.fill_title(@title)
page.fill_description(@description)
page.set_visibility(@visibility)
page.fill_file_name(@file_name)
page.fill_file_content(@file_content)
page.create_snippet
end
end
end
end
end
# frozen_string_literal: true
module QA
context 'Create', :smoke do
describe 'Snippet creation' do
it 'User creates a snippet' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
Page::Main::Menu.perform(&:go_to_snippets)
Resource::Snippet.fabricate_via_browser_ui! do |snippet|
snippet.title = 'Snippet title'
snippet.description = 'Snippet description'
snippet.visibility = 'Public'
snippet.file_name = 'New snippet file name'
snippet.file_content = 'Snippet file text'
end
Page::Dashboard::Snippet::Show.perform do |snippet|
expect(snippet).to have_snippet_title('Snippet title')
expect(snippet).to have_snippet_description('Snippet description')
expect(snippet).to have_embed_type('Embed')
expect(snippet).to have_visibility_type('Public')
expect(snippet).to have_file_name('New snippet file name')
expect(snippet).to have_file_content('Snippet file text')
end
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module QA module QA
context 'Create' do # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/46
context 'Create', :quarantine do
describe 'Web IDE file templates' do describe 'Web IDE file templates' do
include Runtime::Fixtures include Runtime::Fixtures
......
...@@ -79,15 +79,13 @@ module GitalyTest ...@@ -79,15 +79,13 @@ module GitalyTest
socket = read_socket_path socket = read_socket_path
Integer(timeout / delay).times do Integer(timeout / delay).times do
begin UNIXSocket.new(socket)
UNIXSocket.new(socket) puts ' OK'
puts ' OK'
return
return rescue Errno::ENOENT, Errno::ECONNREFUSED
rescue Errno::ENOENT, Errno::ECONNREFUSED print '.'
print '.' sleep delay
sleep delay
end
end end
puts ' FAILED' puts ' FAILED'
......
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