Commit e20a1cde authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@12-10-stable-ee

parent 5fc725de
...@@ -39,7 +39,11 @@ export const diffCompareDropdownTargetVersions = (state, getters) => { ...@@ -39,7 +39,11 @@ export const diffCompareDropdownTargetVersions = (state, getters) => {
...v, ...v,
}; };
}; };
return [...state.mergeRequestDiffs.slice(1).map(formatVersion), baseVersion, headVersion];
if (gon.features?.diffCompareWithHead) {
return [...state.mergeRequestDiffs.slice(1).map(formatVersion), baseVersion, headVersion];
}
return [...state.mergeRequestDiffs.slice(1).map(formatVersion), baseVersion];
}; };
export const diffCompareDropdownSourceVersions = (state, getters) => { export const diffCompareDropdownSourceVersions = (state, getters) => {
......
...@@ -216,7 +216,12 @@ export default { ...@@ -216,7 +216,12 @@ export default {
if (entry.type === 'blob') { if (entry.type === 'blob') {
if (tempFile) { if (tempFile) {
// Since we only support one list of file changes, it's safe to just remove from both
// changed and staged. Otherwise, we'd need to somehow evaluate the difference between
// changed and HEAD.
// https://gitlab.com/gitlab-org/create-stage/-/issues/12669
state.changedFiles = state.changedFiles.filter(f => f.path !== path); state.changedFiles = state.changedFiles.filter(f => f.path !== path);
state.stagedFiles = state.stagedFiles.filter(f => f.path !== path);
} else { } else {
state.changedFiles = state.changedFiles.concat(entry); state.changedFiles = state.changedFiles.concat(entry);
} }
......
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import initAlertsSettings from '~/alerts_service_settings';
document.addEventListener('DOMContentLoaded', () => {
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
integrationSettingsForm.init();
initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
});
import initSettingsPanels from '~/settings_panels'; import initSettingsPanels from '~/settings_panels';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
import initVariableList from '~/ci_variable_list'; import initVariableList from '~/ci_variable_list';
import DueDateSelectors from '~/due_date_select';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
// eslint-disable-next-line no-new
new DueDateSelectors();
if (gon.features.newVariablesUi) { if (gon.features.newVariablesUi) {
initVariableList(); initVariableList();
......
import initSettingsPanels from '~/settings_panels';
import DueDateSelectors from '~/due_date_select';
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
new DueDateSelectors(); // eslint-disable-line no-new
});
...@@ -3,7 +3,6 @@ import SecretValues from '~/behaviors/secret_values'; ...@@ -3,7 +3,6 @@ import SecretValues from '~/behaviors/secret_values';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
import registrySettingsApp from '~/registry/settings/registry_settings_bundle'; import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
import initVariableList from '~/ci_variable_list'; import initVariableList from '~/ci_variable_list';
import DueDateSelectors from '~/due_date_select';
import initDeployKeys from '~/deploy_keys'; import initDeployKeys from '~/deploy_keys';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
...@@ -41,9 +40,6 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -41,9 +40,6 @@ document.addEventListener('DOMContentLoaded', () => {
autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked); autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked);
}); });
// eslint-disable-next-line no-new
new DueDateSelectors();
registrySettingsApp(); registrySettingsApp();
initDeployKeys(); initDeployKeys();
}); });
...@@ -7,6 +7,6 @@ class Groups::DeployTokensController < Groups::ApplicationController ...@@ -7,6 +7,6 @@ class Groups::DeployTokensController < Groups::ApplicationController
@token = @group.deploy_tokens.find(params[:id]) @token = @group.deploy_tokens.find(params[:id])
@token.revoke! @token.revoke!
redirect_to group_settings_ci_cd_path(@group, anchor: 'js-deploy-tokens') redirect_to group_settings_repository_path(@group, anchor: 'js-deploy-tokens')
end end
end end
...@@ -8,9 +8,8 @@ module Groups ...@@ -8,9 +8,8 @@ module Groups
before_action :authorize_update_max_artifacts_size!, only: [:update] before_action :authorize_update_max_artifacts_size!, only: [:update]
before_action do before_action do
push_frontend_feature_flag(:new_variables_ui, @group, default_enabled: true) push_frontend_feature_flag(:new_variables_ui, @group, default_enabled: true)
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
end end
before_action :define_variables, only: [:show, :create_deploy_token] before_action :define_variables, only: [:show]
def show def show
end end
...@@ -42,38 +41,10 @@ module Groups ...@@ -42,38 +41,10 @@ module Groups
redirect_to group_settings_ci_cd_path redirect_to group_settings_ci_cd_path
end end
def create_deploy_token
result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute
@new_deploy_token = result[:deploy_token]
if result[:status] == :success
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
flash.now[:notice] = s_('DeployTokens|Your new group deploy token has been created.')
render :show
end
end
else
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
flash.now[:alert] = result[:message]
render :show
end
end
end
end
private private
def define_variables def define_variables
define_ci_variables define_ci_variables
define_deploy_token_variables
end end
def define_ci_variables def define_ci_variables
...@@ -83,12 +54,6 @@ module Groups ...@@ -83,12 +54,6 @@ module Groups
.map { |variable| variable.present(current_user: current_user) } .map { |variable| variable.present(current_user: current_user) }
end end
def define_deploy_token_variables
@deploy_tokens = @group.deploy_tokens.active
@new_deploy_token = DeployToken.new
end
def authorize_admin_group! def authorize_admin_group!
return render_404 unless can?(current_user, :admin_group, group) return render_404 unless can?(current_user, :admin_group, group)
end end
...@@ -112,10 +77,6 @@ module Groups ...@@ -112,10 +77,6 @@ module Groups
def update_group_params def update_group_params
params.require(:group).permit(:max_artifacts_size) params.require(:group).permit(:max_artifacts_size)
end end
def deploy_token_params
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
end end
end end
end end
# frozen_string_literal: true
module Groups
module Settings
class RepositoryController < Groups::ApplicationController
skip_cross_project_access_check :show
before_action :authorize_admin_group!
before_action :define_deploy_token_variables
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
end
def create_deploy_token
result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute
@new_deploy_token = result[:deploy_token]
if result[:status] == :success
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
flash.now[:notice] = s_('DeployTokens|Your new group deploy token has been created.')
render :show
end
end
else
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
flash.now[:alert] = result[:message]
render :show
end
end
end
end
private
def define_deploy_token_variables
@deploy_tokens = @group.deploy_tokens.active
@new_deploy_token = DeployToken.new
end
def deploy_token_params
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
end
end
end
...@@ -7,6 +7,6 @@ class Projects::DeployTokensController < Projects::ApplicationController ...@@ -7,6 +7,6 @@ class Projects::DeployTokensController < Projects::ApplicationController
@token = @project.deploy_tokens.find(params[:id]) @token = @project.deploy_tokens.find(params[:id])
@token.revoke! @token.revoke!
redirect_to project_settings_ci_cd_path(project, anchor: 'js-deploy-tokens') redirect_to project_settings_repository_path(project, anchor: 'js-deploy-tokens')
end end
end end
...@@ -26,6 +26,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -26,6 +26,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:code_navigation, @project) push_frontend_feature_flag(:code_navigation, @project)
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true) push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
push_frontend_feature_flag(:merge_ref_head_comments, @project) push_frontend_feature_flag(:merge_ref_head_comments, @project)
push_frontend_feature_flag(:diff_compare_with_head, @project)
end end
before_action do before_action do
......
...@@ -48,33 +48,6 @@ module Projects ...@@ -48,33 +48,6 @@ module Projects
redirect_to namespace_project_settings_ci_cd_path redirect_to namespace_project_settings_ci_cd_path
end end
def create_deploy_token
result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute
@new_deploy_token = result[:deploy_token]
if result[:status] == :success
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.')
render :show
end
end
else
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
flash.now[:alert] = result[:message]
render :show
end
end
end
end
private private
def update_params def update_params
...@@ -93,10 +66,6 @@ module Projects ...@@ -93,10 +66,6 @@ module Projects
end end
end end
def deploy_token_params
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
def run_autodevops_pipeline(service) def run_autodevops_pipeline(service)
return unless service.run_auto_devops_pipeline? return unless service.run_auto_devops_pipeline?
...@@ -116,7 +85,6 @@ module Projects ...@@ -116,7 +85,6 @@ module Projects
def define_variables def define_variables
define_runners_variables define_runners_variables
define_ci_variables define_ci_variables
define_deploy_token_variables
define_triggers_variables define_triggers_variables
define_badges_variables define_badges_variables
define_auto_devops_variables define_auto_devops_variables
...@@ -168,12 +136,6 @@ module Projects ...@@ -168,12 +136,6 @@ module Projects
@auto_devops = @project.auto_devops || ProjectAutoDevops.new @auto_devops = @project.auto_devops || ProjectAutoDevops.new
end end
def define_deploy_token_variables
@deploy_tokens = @project.deploy_tokens.active
@new_deploy_token = DeployToken.new
end
def define_deploy_keys def define_deploy_keys
@deploy_keys = DeployKeysPresenter.new(@project, current_user: current_user) @deploy_keys = DeployKeysPresenter.new(@project, current_user: current_user)
end end
......
...@@ -4,7 +4,10 @@ module Projects ...@@ -4,7 +4,10 @@ module Projects
module Settings module Settings
class RepositoryController < Projects::ApplicationController class RepositoryController < Projects::ApplicationController
before_action :authorize_admin_project! before_action :authorize_admin_project!
before_action :remote_mirror, only: [:show] before_action :define_variables, only: [:create_deploy_token]
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @project)
end
def show def show
render_show render_show
...@@ -24,15 +27,47 @@ module Projects ...@@ -24,15 +27,47 @@ module Projects
redirect_to project_settings_repository_path(project) redirect_to project_settings_repository_path(project)
end end
def create_deploy_token
result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute
@new_deploy_token = result[:deploy_token]
if result[:status] == :success
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
flash.now[:notice] = s_('DeployTokens|Your new project deploy token has been created.')
render :show
end
end
else
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
flash.now[:alert] = result[:message]
render :show
end
end
end
end
private private
def render_show def render_show
define_protected_refs define_variables
remote_mirror
render 'show' render 'show'
end end
def define_variables
define_deploy_token_variables
define_protected_refs
remote_mirror
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def define_protected_refs def define_protected_refs
@protected_branches = @project.protected_branches.order(:name).page(params[:page]) @protected_branches = @project.protected_branches.order(:name).page(params[:page])
...@@ -51,6 +86,10 @@ module Projects ...@@ -51,6 +86,10 @@ module Projects
@remote_mirror = project.remote_mirrors.first_or_initialize @remote_mirror = project.remote_mirrors.first_or_initialize
end end
def deploy_token_params
params.require(:deploy_token).permit(:name, :expires_at, :read_repository, :read_registry, :write_registry, :username)
end
def access_levels_options def access_levels_options
{ {
create_access_levels: levels_for_dropdown, create_access_levels: levels_for_dropdown,
...@@ -74,6 +113,12 @@ module Projects ...@@ -74,6 +113,12 @@ module Projects
{ open_branches: ProtectableDropdown.new(@project, :branches).hash } { open_branches: ProtectableDropdown.new(@project, :branches).hash }
end end
def define_deploy_token_variables
@deploy_tokens = @project.deploy_tokens.active
@new_deploy_token ||= DeployToken.new
end
def load_gon_index def load_gon_index
gon.push(protectable_tags_for_dropdown) gon.push(protectable_tags_for_dropdown)
gon.push(protectable_branches_for_dropdown) gon.push(protectable_branches_for_dropdown)
......
# frozen_string_literal: true
module Analytics
module NavbarHelper
class NavbarSubItem
attr_reader :title, :path, :link, :link_to_options
def initialize(title:, path:, link:, link_to_options: {})
@title = title
@path = path
@link = link
@link_to_options = link_to_options.merge(title: title)
end
end
def project_analytics_navbar_links(project, current_user)
[
cycle_analytics_navbar_link(project, current_user),
repository_analytics_navbar_link(project, current_user),
ci_cd_analytics_navbar_link(project, current_user)
].compact
end
def group_analytics_navbar_links(group, current_user)
[]
end
private
def navbar_sub_item(args)
NavbarSubItem.new(args)
end
def cycle_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:cycle_analytics)
navbar_sub_item(
title: _('Value Stream'),
path: 'cycle_analytics#show',
link: project_cycle_analytics_path(project),
link_to_options: { class: 'shortcuts-project-cycle-analytics' }
)
end
def repository_analytics_navbar_link(project, current_user)
return if project.empty_repo?
navbar_sub_item(
title: _('Repository'),
path: 'graphs#charts',
link: charts_project_graph_path(project, current_ref),
link_to_options: { class: 'shortcuts-repository-charts' }
)
end
def ci_cd_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:pipelines)
return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
navbar_sub_item(
title: _('CI / CD'),
path: 'pipelines#charts',
link: charts_project_pipelines_path(project)
)
end
end
end
Analytics::NavbarHelper.prepend_if_ee('EE::Analytics::NavbarHelper')
# frozen_string_literal: true
module AnalyticsNavbarHelper
class NavbarSubItem
attr_reader :title, :path, :link, :link_to_options
def initialize(title:, path:, link:, link_to_options: {})
@title = title
@path = path
@link = link
@link_to_options = link_to_options.merge(title: title)
end
end
def project_analytics_navbar_links(project, current_user)
[
cycle_analytics_navbar_link(project, current_user),
repository_analytics_navbar_link(project, current_user),
ci_cd_analytics_navbar_link(project, current_user)
].compact
end
def group_analytics_navbar_links(group, current_user)
[]
end
private
def navbar_sub_item(args)
NavbarSubItem.new(args)
end
def cycle_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:cycle_analytics)
navbar_sub_item(
title: _('Value Stream'),
path: 'cycle_analytics#show',
link: project_cycle_analytics_path(project),
link_to_options: { class: 'shortcuts-project-cycle-analytics' }
)
end
def repository_analytics_navbar_link(project, current_user)
return if project.empty_repo?
navbar_sub_item(
title: _('Repository'),
path: 'graphs#charts',
link: charts_project_graph_path(project, current_ref),
link_to_options: { class: 'shortcuts-repository-charts' }
)
end
def ci_cd_analytics_navbar_link(project, current_user)
return unless project_nav_tab?(:pipelines)
return unless project.feature_available?(:builds, current_user) || !project.empty_repo?
navbar_sub_item(
title: _('CI / CD'),
path: 'pipelines#charts',
link: charts_project_pipelines_path(project)
)
end
end
AnalyticsNavbarHelper.prepend_if_ee('EE::AnalyticsNavbarHelper')
...@@ -7,7 +7,7 @@ module CiVariablesHelper ...@@ -7,7 +7,7 @@ module CiVariablesHelper
def create_deploy_token_path(entity, opts = {}) def create_deploy_token_path(entity, opts = {})
if entity.is_a?(Group) if entity.is_a?(Group)
create_deploy_token_group_settings_ci_cd_path(entity, opts) create_deploy_token_group_settings_repository_path(entity, opts)
else else
# TODO: change this path to 'create_deploy_token_project_settings_ci_cd_path' # TODO: change this path to 'create_deploy_token_project_settings_ci_cd_path'
# See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356 # See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356
......
...@@ -52,7 +52,7 @@ module ExploreHelper ...@@ -52,7 +52,7 @@ module ExploreHelper
end end
def public_visibility_restricted? def public_visibility_restricted?
Gitlab::CurrentSettings.restricted_visibility_levels.include? Gitlab::VisibilityLevel::PUBLIC Gitlab::CurrentSettings.restricted_visibility_levels&.include? Gitlab::VisibilityLevel::PUBLIC
end end
private private
......
...@@ -15,6 +15,7 @@ module GroupsHelper ...@@ -15,6 +15,7 @@ module GroupsHelper
groups#projects groups#projects
groups#edit groups#edit
badges#index badges#index
repository#show
ci_cd#show ci_cd#show
integrations#index integrations#index
integrations#edit integrations#edit
......
...@@ -163,7 +163,7 @@ class MergeRequest < ApplicationRecord ...@@ -163,7 +163,7 @@ class MergeRequest < ApplicationRecord
state_machine :merge_status, initial: :unchecked do state_machine :merge_status, initial: :unchecked do
event :mark_as_unchecked do event :mark_as_unchecked do
transition [:can_be_merged, :checking, :unchecked] => :unchecked transition [:can_be_merged, :checking, :unchecked] => :unchecked
transition [:cannot_be_merged, :cannot_be_merged_recheck] => :cannot_be_merged_recheck transition [:cannot_be_merged, :cannot_be_merged_rechecking, :cannot_be_merged_recheck] => :cannot_be_merged_recheck
end end
event :mark_as_checking do event :mark_as_checking do
...@@ -200,7 +200,7 @@ class MergeRequest < ApplicationRecord ...@@ -200,7 +200,7 @@ class MergeRequest < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
def check_state?(merge_status) def check_state?(merge_status)
[:unchecked, :cannot_be_merged_recheck, :checking].include?(merge_status.to_sym) [:unchecked, :cannot_be_merged_recheck, :checking, :cannot_be_merged_rechecking].include?(merge_status.to_sym)
end end
end end
......
...@@ -2402,7 +2402,7 @@ class Project < ApplicationRecord ...@@ -2402,7 +2402,7 @@ class Project < ApplicationRecord
end end
def deploy_token_create_url(opts = {}) def deploy_token_create_url(opts = {})
Gitlab::Routing.url_helpers.create_deploy_token_project_settings_ci_cd_path(self, opts) Gitlab::Routing.url_helpers.create_deploy_token_project_settings_repository_path(self, opts)
end end
def deploy_token_revoke_url_for(token) def deploy_token_revoke_url_for(token)
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
- expanded = expanded_by_default? - expanded = expanded_by_default?
- general_expanded = @group.errors.empty? ? expanded : true - general_expanded = @group.errors.empty? ? expanded : true
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow read-only access to the repositories and registry images within the group.')
-# Given we only have one field in this form which is also admin-only, -# Given we only have one field in this form which is also admin-only,
-# we don't want to show an empty section to non-admin users, -# we don't want to show an empty section to non-admin users,
...@@ -25,8 +24,6 @@ ...@@ -25,8 +24,6 @@
.settings-content .settings-content
= render 'ci/variables/index', save_endpoint: group_variables_path = render 'ci/variables/index', save_endpoint: group_variables_path
= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
%section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) } %section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header .settings-header
%h4 %h4
......
- breadcrumb_title _('Repository Settings')
- page_title _('Repository')
- deploy_token_description = s_('DeployTokens|Group deploy tokens allow read-only access to the repositories and registry images within the group.')
= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
...@@ -155,6 +155,11 @@ ...@@ -155,6 +155,11 @@
%span %span
= _('Projects') = _('Projects')
= nav_link(controller: :repository) do
= link_to group_settings_repository_path(@group), title: _('Repository') do
%span
= _('Repository')
= nav_link(controller: :ci_cd) do = nav_link(controller: :ci_cd) do
= link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do = link_to group_settings_ci_cd_path(@group), title: _('CI / CD') do
%span %span
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
- expanded = expanded_by_default? - expanded = expanded_by_default?
- general_expanded = @project.errors.empty? ? expanded : true - general_expanded = @project.errors.empty? ? expanded : true
- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.')
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) } %section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
.settings-header .settings-header
...@@ -52,8 +51,6 @@ ...@@ -52,8 +51,6 @@
.settings-content .settings-content
= render 'ci/variables/index', save_endpoint: project_variables_path(@project) = render 'ci/variables/index', save_endpoint: project_variables_path(@project)
= render "shared/deploy_tokens/index", group_or_project: @project, description: deploy_token_description
= render @deploy_keys = render @deploy_keys
%section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) } %section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) }
......
- breadcrumb_title _("Repository Settings") - breadcrumb_title _("Repository Settings")
- page_title _("Repository") - page_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
- deploy_token_description = s_('DeployTokens|Deploy tokens allow access to your repository and registry images.')
= render "projects/default_branch/show" = render "projects/default_branch/show"
= render_if_exists "projects/push_rules/index" = render_if_exists "projects/push_rules/index"
...@@ -11,6 +12,7 @@ ...@@ -11,6 +12,7 @@
-# Those are used throughout the actual views. These `shared` views are then -# Those are used throughout the actual views. These `shared` views are then
-# reused in EE. -# reused in EE.
= render "projects/settings/repository/protected_branches" = render "projects/settings/repository/protected_branches"
= render "shared/deploy_tokens/index", group_or_project: @project, description: deploy_token_description
= render "projects/cleanup/show" = render "projects/cleanup/show"
= render_if_exists 'shared/promotions/promote_repository_features' = render_if_exists 'shared/promotions/promote_repository_features'
---
title: Move Group Deploy Tokens to new Group-scoped Repository settings
merge_request: 29290
author:
type: changed
---
title: Fix Web IDE handling of deleting newly added files
merge_request: 29783
author:
type: fixed
---
title: Fix null dereference in /import status REST endpoint
merge_request: 29886
author:
type: fixed
---
title: Fix Service Templates missing Active toggle
merge_request: 29936
author:
type: fixed
---
title: Fix bug creating project from git ssh
merge_request: 29771
author:
type: fixed
---
title: Migration of dismissals to vulnerabilities
merge_request: 29711
author:
type: other
---
title: Fix 500 error on accessing restricted levels
merge_request: 30313
author:
type: fixed
...@@ -32,6 +32,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -32,6 +32,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
put :reset_registration_token put :reset_registration_token
patch :update_auto_devops patch :update_auto_devops
post :create_deploy_token, path: 'deploy_token/create', to: 'repository#create_deploy_token'
end
resource :repository, only: [:show], controller: 'repository' do
post :create_deploy_token, path: 'deploy_token/create' post :create_deploy_token, path: 'deploy_token/create'
end end
......
...@@ -73,7 +73,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -73,7 +73,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do resource :ci_cd, only: [:show, :update], controller: 'ci_cd' do
post :reset_cache post :reset_cache
put :reset_registration_token put :reset_registration_token
post :create_deploy_token, path: 'deploy_token/create' post :create_deploy_token, path: 'deploy_token/create', to: 'repository#create_deploy_token'
end end
resource :operations, only: [:show, :update] do resource :operations, only: [:show, :update] do
...@@ -87,7 +87,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -87,7 +87,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :repository, only: [:show], controller: :repository do resource :repository, only: [:show], controller: :repository do
# TODO: Removed this "create_deploy_token" route after change was made in app/helpers/ci_variables_helper.rb:14 # TODO: Removed this "create_deploy_token" route after change was made in app/helpers/ci_variables_helper.rb:14
# See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356 # See MR comment for more detail: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27059#note_311585356
post :create_deploy_token, path: 'deploy_token/create', to: 'ci_cd#create_deploy_token' post :create_deploy_token, path: 'deploy_token/create'
post :cleanup post :cleanup
end end
end end
......
# frozen_string_literal: true
class MigrateVulnerabilityDismissals < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
MIGRATION = 'UpdateVulnerabilitiesToDismissed'.freeze
BATCH_SIZE = 500
DELAY_INTERVAL = 2.minutes.to_i
class Vulnerability < ActiveRecord::Base
self.table_name = 'vulnerabilities'
self.inheritance_column = :_type_disabled
include ::EachBatch
end
def up
return unless Gitlab.ee?
Vulnerability.select('project_id').group(:project_id).each_batch(of: BATCH_SIZE, column: "project_id") do |project_batch, index|
batch_delay = (index - 1) * BATCH_SIZE * DELAY_INTERVAL
project_batch.each_with_index do |project, project_batch_index|
project_delay = project_batch_index * DELAY_INTERVAL
migrate_in(batch_delay + project_delay, MIGRATION, project[:project_id])
end
end
end
def down
# nothing to do
end
end
...@@ -13211,6 +13211,7 @@ COPY "schema_migrations" (version) FROM STDIN; ...@@ -13211,6 +13211,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200415161021 20200415161021
20200415161206 20200415161206
20200415192656 20200415192656
20200416111111
20200416120128 20200416120128
20200416120354 20200416120354
\. \.
......
...@@ -70,6 +70,7 @@ The following table depicts the various user permission levels in a project. ...@@ -70,6 +70,7 @@ The following table depicts the various user permission levels in a project.
| Create confidential issue | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ | | Create confidential issue | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ | | View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ |
| View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ | | View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
| View requirements **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| Assign issues | | ✓ | ✓ | ✓ | ✓ | | Assign issues | | ✓ | ✓ | ✓ | ✓ |
| Label issues | | ✓ | ✓ | ✓ | ✓ | | Label issues | | ✓ | ✓ | ✓ | ✓ |
| Set issue weight | | ✓ | ✓ | ✓ | ✓ | | Set issue weight | | ✓ | ✓ | ✓ | ✓ |
...@@ -85,8 +86,8 @@ The following table depicts the various user permission levels in a project. ...@@ -85,8 +86,8 @@ The following table depicts the various user permission levels in a project.
| View project statistics | | ✓ | ✓ | ✓ | ✓ | | View project statistics | | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ | | View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
| Create new merge request | | ✓ | ✓ | ✓ | ✓ | | Create new merge request | | ✓ | ✓ | ✓ | ✓ |
| View requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ | | View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
| Create/edit requirements **(ULTIMATE)** | | ✓ | ✓ | ✓ | ✓ |
| Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ | | Pull [packages](packages/index.md) | | ✓ | ✓ | ✓ | ✓ |
| Publish [packages](packages/index.md) | | | ✓ | ✓ | ✓ | | Publish [packages](packages/index.md) | | | ✓ | ✓ | ✓ |
| Pull from [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ | | Pull from [Conan repository](packages/conan_repository/index.md), [Maven repository](packages/maven_repository/index.md), or [NPM registry](packages/npm_registry/index.md) **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
...@@ -122,7 +123,6 @@ The following table depicts the various user permission levels in a project. ...@@ -122,7 +123,6 @@ The following table depicts the various user permission levels in a project.
| Create and edit wiki pages | | | ✓ | ✓ | ✓ | | Create and edit wiki pages | | | ✓ | ✓ | ✓ |
| Rewrite/remove Git tags | | | ✓ | ✓ | ✓ | | Rewrite/remove Git tags | | | ✓ | ✓ | ✓ |
| Manage Feature Flags **(PREMIUM)** | | | ✓ | ✓ | ✓ | | Manage Feature Flags **(PREMIUM)** | | | ✓ | ✓ | ✓ |
| Manage requirements **(ULTIMATE)** | | | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ | | Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ | | Use environment terminals | | | | ✓ | ✓ |
| Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ | | Run Web IDE's Interactive Web Terminals **(ULTIMATE ONLY)** | | | | ✓ | ✓ |
......
...@@ -67,6 +67,26 @@ current default comparison. ...@@ -67,6 +67,26 @@ current default comparison.
![Merge request versions compare HEAD](img/versions_compare_head_v12_10.png) ![Merge request versions compare HEAD](img/versions_compare_head_v12_10.png)
### Enable or disable `HEAD` comparison mode **(CORE ONLY)**
`HEAD` comparison mode is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/troubleshooting/navigating_gitlab_via_rails_console.md#starting-a-rails-console-session)
can enable it for your instance. You're welcome to test it, but use it at your
own risk.
To enable it:
```ruby
Feature.enable(:diff_compare_with_head)
```
To disable it:
```ruby
Feature.disable(:diff_compare_with_head)
```
<!-- ## Troubleshooting <!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues Include any troubleshooting steps that you can foresee. If you know beforehand what issues
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
end end
expose :failed_relations, using: Entities::ProjectImportFailedRelation do |project, _options| expose :failed_relations, using: Entities::ProjectImportFailedRelation do |project, _options|
project.import_state.relation_hard_failures(limit: 100) project.import_state&.relation_hard_failures(limit: 100) || []
end end
# TODO: Use `expose_nil` once we upgrade the grape-entity gem # TODO: Use `expose_nil` once we upgrade the grape-entity gem
......
...@@ -43,12 +43,9 @@ module API ...@@ -43,12 +43,9 @@ module API
Gitlab::Git::HookEnv.set(gl_repository, env) if container Gitlab::Git::HookEnv.set(gl_repository, env) if container
actor.update_last_used_at! actor.update_last_used_at!
access_checker = access_checker_for(actor, params[:protocol])
check_result = begin check_result = begin
result = access_checker.check(params[:action], params[:changes]) access_check!(actor, params)
@project ||= access_checker.project
result
rescue Gitlab::GitAccess::ForbiddenError => e rescue Gitlab::GitAccess::ForbiddenError => e
# The return code needs to be 401. If we return 403 # The return code needs to be 401. If we return 403
# the custom message we return won't be shown to the user # the custom message we return won't be shown to the user
...@@ -92,6 +89,17 @@ module API ...@@ -92,6 +89,17 @@ module API
response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR) response_with_status(code: 500, success: false, message: UNKNOWN_CHECK_RESULT_ERROR)
end end
end end
def access_check!(actor, params)
access_checker = access_checker_for(actor, params[:protocol])
access_checker.check(params[:action], params[:changes]).tap do |result|
break result if @project || !repo_type.project?
# If we have created a project directly from a git push
# we have to assign its value to both @project and @container
@project = @container = access_checker.project
end
end
end end
namespace 'internal' do namespace 'internal' do
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# rubocop: disable Style/Documentation
class UpdateVulnerabilitiesToDismissed
def perform(project_id)
end
end
end
end
Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed.prepend_if_ee('EE::Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed')
...@@ -13,20 +13,10 @@ module QA ...@@ -13,20 +13,10 @@ module QA
element :variables_settings_content element :variables_settings_content
end end
view 'app/views/shared/deploy_tokens/_index.html.haml' do
element :deploy_tokens_settings
end
view 'app/views/projects/deploy_keys/_index.html.haml' do view 'app/views/projects/deploy_keys/_index.html.haml' do
element :deploy_keys_settings element :deploy_keys_settings
end end
def expand_deploy_tokens(&block)
expand_section(:deploy_tokens_settings) do
Settings::DeployTokens.perform(&block)
end
end
def expand_deploy_keys(&block) def expand_deploy_keys(&block)
expand_section(:deploy_keys_settings) do expand_section(:deploy_keys_settings) do
Settings::DeployKeys.perform(&block) Settings::DeployKeys.perform(&block)
......
...@@ -15,6 +15,16 @@ module QA ...@@ -15,6 +15,16 @@ module QA
element :mirroring_repositories_settings_section element :mirroring_repositories_settings_section
end end
view 'app/views/shared/deploy_tokens/_index.html.haml' do
element :deploy_tokens_settings
end
def expand_deploy_tokens(&block)
expand_section(:deploy_tokens_settings) do
Settings::DeployTokens.perform(&block)
end
end
def expand_protected_branches(&block) def expand_protected_branches(&block)
expand_section(:protected_branches_settings) do expand_section(:protected_branches_settings) do
ProtectedBranches.perform(&block) ProtectedBranches.perform(&block)
......
...@@ -6,16 +6,16 @@ module QA ...@@ -6,16 +6,16 @@ module QA
attr_accessor :name, :expires_at attr_accessor :name, :expires_at
attribute :username do attribute :username do
Page::Project::Settings::CICD.perform do |cicd_page| Page::Project::Settings::Repository.perform do |repository_page|
cicd_page.expand_deploy_tokens do |token| repository_page.expand_deploy_tokens do |token|
token.token_username token.token_username
end end
end end
end end
attribute :password do attribute :password do
Page::Project::Settings::CICD.perform do |cicd_page| Page::Project::Settings::Repository.perform do |repository_page|
cicd_page.expand_deploy_tokens do |token| repository_page.expand_deploy_tokens do |token|
token.token_password token.token_password
end end
end end
...@@ -31,10 +31,10 @@ module QA ...@@ -31,10 +31,10 @@ module QA
def fabricate! def fabricate!
project.visit! project.visit!
Page::Project::Menu.perform(&:go_to_ci_cd_settings) Page::Project::Menu.perform(&:go_to_repository_settings)
Page::Project::Settings::CICD.perform do |cicd| Page::Project::Settings::Repository.perform do |setting|
cicd.expand_deploy_tokens do |page| setting.expand_deploy_tokens do |page|
page.fill_token_name(name) page.fill_token_name(name)
page.fill_token_expires_at(expires_at) page.fill_token_expires_at(expires_at)
page.fill_scopes(read_repository: true, read_registry: false) page.fill_scopes(read_repository: true, read_registry: false)
......
...@@ -216,88 +216,4 @@ describe Groups::Settings::CiCdController do ...@@ -216,88 +216,4 @@ describe Groups::Settings::CiCdController do
end end
end end
end end
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: group })
entity.add_owner(user)
end
it_behaves_like 'a created deploy token' do
let(:entity) { group }
let(:create_entity_params) { { group_id: group } }
let(:deploy_token_type) { DeployToken.deploy_token_types[:group_type] }
end
end
context 'when ajax_new_deploy_token feature flag is enabled for the project' do
let(:good_deploy_token_params) do
{
name: 'name',
expires_at: 1.day.from_now.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:group_type]
}
end
let(:request_params) do
{
group_id: group.to_param,
deploy_token: deploy_token_params
}
end
before do
group.add_owner(user)
end
subject { post :create_deploy_token, params: request_params, format: :json }
context('a good request') do
let(:deploy_token_params) { good_deploy_token_params }
let(:expected_response) do
{
'id' => be_a(Integer),
'name' => deploy_token_params[:name],
'username' => deploy_token_params[:username],
'expires_at' => Time.parse(deploy_token_params[:expires_at]),
'token' => be_a(String),
'scopes' => deploy_token_params.inject([]) do |scopes, kv|
key, value = kv
key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes
end
}
end
it 'creates the deploy token' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
expect(json_response).to match(expected_response)
end
end
context('a bad request') do
let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) }
let(:expected_response) { { 'message' => "Scopes can't be blank" } }
it 'does not create the deploy token' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to match(expected_response)
end
end
context('an invalid request') do
let(:deploy_token_params) { good_deploy_token_params.except(:name) }
it 'raises a validation error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
describe Groups::Settings::RepositoryController do
include ExternalAuthorizationServiceHelpers
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: group })
entity.add_owner(user)
end
it_behaves_like 'a created deploy token' do
let(:entity) { group }
let(:create_entity_params) { { group_id: group } }
let(:deploy_token_type) { DeployToken.deploy_token_types[:group_type] }
end
end
context 'when ajax_new_deploy_token feature flag is enabled for the project' do
let(:good_deploy_token_params) do
{
name: 'name',
expires_at: 1.day.from_now.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:group_type]
}
end
let(:request_params) do
{
group_id: group.to_param,
deploy_token: deploy_token_params
}
end
before do
group.add_owner(user)
end
subject { post :create_deploy_token, params: request_params, format: :json }
context('a good request') do
let(:deploy_token_params) { good_deploy_token_params }
let(:expected_response) do
{
'id' => be_a(Integer),
'name' => deploy_token_params[:name],
'username' => deploy_token_params[:username],
'expires_at' => Time.parse(deploy_token_params[:expires_at]),
'token' => be_a(String),
'scopes' => deploy_token_params.inject([]) do |scopes, kv|
key, value = kv
key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes
end
}
end
it 'creates the deploy token' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
expect(json_response).to match(expected_response)
end
end
context('a bad request') do
let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) }
let(:expected_response) { { 'message' => "Scopes can't be blank" } }
it 'does not create the deploy token' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to match(expected_response)
end
end
context('an invalid request') do
let(:deploy_token_params) { good_deploy_token_params.except(:name) }
it 'raises a validation error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
end
end
...@@ -266,84 +266,4 @@ describe Projects::Settings::CiCdController do ...@@ -266,84 +266,4 @@ describe Projects::Settings::CiCdController do
end end
end end
end end
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
end
it_behaves_like 'a created deploy token' do
let(:entity) { project }
let(:create_entity_params) { { namespace_id: project.namespace, project_id: project } }
let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] }
end
end
context 'when ajax_new_deploy_token feature flag is enabled for the project' do
let(:good_deploy_token_params) do
{
name: 'name',
expires_at: 1.day.from_now.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:project_type]
}
end
let(:request_params) do
{
namespace_id: project.namespace.to_param,
project_id: project.to_param,
deploy_token: deploy_token_params
}
end
subject { post :create_deploy_token, params: request_params, format: :json }
context('a good request') do
let(:deploy_token_params) { good_deploy_token_params }
let(:expected_response) do
{
'id' => be_a(Integer),
'name' => deploy_token_params[:name],
'username' => deploy_token_params[:username],
'expires_at' => Time.parse(deploy_token_params[:expires_at]),
'token' => be_a(String),
'scopes' => deploy_token_params.inject([]) do |scopes, kv|
key, value = kv
key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes
end
}
end
it 'creates the deploy token' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
expect(json_response).to match(expected_response)
end
end
context('a bad request') do
let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) }
let(:expected_response) { { 'message' => "Scopes can't be blank" } }
it 'does not create the deploy token' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to match(expected_response)
end
end
context('an invalid request') do
let(:deploy_token_params) { good_deploy_token_params.except(:name) }
it 'raises a validation error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
end
end end
...@@ -32,4 +32,84 @@ describe Projects::Settings::RepositoryController do ...@@ -32,4 +32,84 @@ describe Projects::Settings::RepositoryController do
expect(RepositoryCleanupWorker).to have_received(:perform_async).once expect(RepositoryCleanupWorker).to have_received(:perform_async).once
end end
end end
describe 'POST create_deploy_token' do
context 'when ajax_new_deploy_token feature flag is disabled for the project' do
before do
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
end
it_behaves_like 'a created deploy token' do
let(:entity) { project }
let(:create_entity_params) { { namespace_id: project.namespace, project_id: project } }
let(:deploy_token_type) { DeployToken.deploy_token_types[:project_type] }
end
end
context 'when ajax_new_deploy_token feature flag is enabled for the project' do
let(:good_deploy_token_params) do
{
name: 'name',
expires_at: 1.day.from_now.to_s,
username: 'deployer',
read_repository: '1',
deploy_token_type: DeployToken.deploy_token_types[:project_type]
}
end
let(:request_params) do
{
namespace_id: project.namespace.to_param,
project_id: project.to_param,
deploy_token: deploy_token_params
}
end
subject { post :create_deploy_token, params: request_params, format: :json }
context('a good request') do
let(:deploy_token_params) { good_deploy_token_params }
let(:expected_response) do
{
'id' => be_a(Integer),
'name' => deploy_token_params[:name],
'username' => deploy_token_params[:username],
'expires_at' => Time.parse(deploy_token_params[:expires_at]),
'token' => be_a(String),
'scopes' => deploy_token_params.inject([]) do |scopes, kv|
key, value = kv
key.to_s.start_with?('read_') && !value.to_i.zero? ? scopes << key.to_s : scopes
end
}
end
it 'creates the deploy token' do
subject
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/deploy_token')
expect(json_response).to match(expected_response)
end
end
context('a bad request') do
let(:deploy_token_params) { good_deploy_token_params.except(:read_repository) }
let(:expected_response) { { 'message' => "Scopes can't be blank" } }
it 'does not create the deploy token' do
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to match(expected_response)
end
end
context('an invalid request') do
let(:deploy_token_params) { good_deploy_token_params.except(:name) }
it 'raises a validation error' do
expect { subject }.to raise_error(ActiveRecord::StatementInvalid)
end
end
end
end
end end
...@@ -37,19 +37,6 @@ describe 'Group CI/CD settings' do ...@@ -37,19 +37,6 @@ describe 'Group CI/CD settings' do
end end
end end
context 'Deploy tokens' do
let!(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
before do
stub_container_registry_config(enabled: true)
visit group_settings_ci_cd_path(group)
end
it_behaves_like 'a deploy token in ci/cd settings' do
let(:entity_type) { 'group' }
end
end
describe 'Auto DevOps form' do describe 'Auto DevOps form' do
before do before do
stub_application_setting(auto_devops_enabled: true) stub_application_setting(auto_devops_enabled: true)
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Group Repository settings' do
include WaitForRequests
let(:user) { create(:user) }
let(:group) { create(:group) }
before do
group.add_owner(user)
sign_in(user)
end
context 'Deploy tokens' do
let!(:deploy_token) { create(:deploy_token, :group, groups: [group]) }
before do
stub_container_registry_config(enabled: true)
visit group_settings_repository_path(group)
end
it_behaves_like 'a deploy token in settings' do
let(:entity_type) { 'group' }
end
end
end
...@@ -30,4 +30,14 @@ describe 'IDE user commits changes', :js do ...@@ -30,4 +30,14 @@ describe 'IDE user commits changes', :js do
expect(project.repository.blob_at('master', 'foo/bar/.gitkeep')).to be_nil expect(project.repository.blob_at('master', 'foo/bar/.gitkeep')).to be_nil
expect(project.repository.blob_at('master', 'foo/bar/lorem_ipsum.md').data).to eql(content) expect(project.repository.blob_at('master', 'foo/bar/lorem_ipsum.md').data).to eql(content)
end end
it 'user adds then deletes new file' do
ide_create_new_file('foo/bar/lorem_ipsum.md')
expect(page).to have_selector(ide_commit_tab_selector)
ide_delete_file('foo/bar/lorem_ipsum.md')
expect(page).not_to have_selector(ide_commit_tab_selector)
end
end end
...@@ -7,22 +7,6 @@ describe 'Projects > Settings > CI / CD settings' do ...@@ -7,22 +7,6 @@ describe 'Projects > Settings > CI / CD settings' do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let_it_be(:role) { :maintainer } let_it_be(:role) { :maintainer }
context 'Deploy tokens' do
let!(:deploy_token) { create(:deploy_token, projects: [project]) }
before do
project.add_role(user, role)
sign_in(user)
stub_container_registry_config(enabled: true)
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
visit project_settings_ci_cd_path(project)
end
it_behaves_like 'a deploy token in ci/cd settings' do
let(:entity_type) { 'project' }
end
end
context 'Deploy Keys', :js do context 'Deploy Keys', :js do
let_it_be(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) } let_it_be(:private_deploy_key) { create(:deploy_key, title: 'private_deploy_key', public: false) }
let_it_be(:public_deploy_key) { create(:another_deploy_key, title: 'public_deploy_key', public: true) } let_it_be(:public_deploy_key) { create(:another_deploy_key, title: 'public_deploy_key', public: true) }
......
...@@ -25,6 +25,20 @@ describe 'Projects > Settings > Repository settings' do ...@@ -25,6 +25,20 @@ describe 'Projects > Settings > Repository settings' do
context 'for maintainer' do context 'for maintainer' do
let(:role) { :maintainer } let(:role) { :maintainer }
context 'Deploy tokens' do
let!(:deploy_token) { create(:deploy_token, projects: [project]) }
before do
stub_container_registry_config(enabled: true)
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
visit project_settings_repository_path(project)
end
it_behaves_like 'a deploy token in settings' do
let(:entity_type) { 'project' }
end
end
context 'remote mirror settings' do context 'remote mirror settings' do
before do before do
visit project_settings_repository_path(project) visit project_settings_repository_path(project)
......
...@@ -12,7 +12,7 @@ describe 'Repository Settings > User sees revoke deploy token modal', :js do ...@@ -12,7 +12,7 @@ describe 'Repository Settings > User sees revoke deploy token modal', :js do
project.add_role(user, role) project.add_role(user, role)
sign_in(user) sign_in(user)
stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project }) stub_feature_flags(ajax_new_deploy_token: { enabled: false, thing: project })
visit(project_settings_ci_cd_path(project)) visit(project_settings_repository_path(project))
click_link('Revoke') click_link('Revoke')
end end
......
...@@ -18,6 +18,7 @@ describe('Compare diff version dropdowns', () => { ...@@ -18,6 +18,7 @@ describe('Compare diff version dropdowns', () => {
}; };
localState.targetBranchName = 'baseVersion'; localState.targetBranchName = 'baseVersion';
localState.mergeRequestDiffs = diffsMockData; localState.mergeRequestDiffs = diffsMockData;
gon.features = { diffCompareWithHead: true };
}); });
describe('selectedTargetIndex', () => { describe('selectedTargetIndex', () => {
...@@ -128,6 +129,14 @@ describe('Compare diff version dropdowns', () => { ...@@ -128,6 +129,14 @@ describe('Compare diff version dropdowns', () => {
}); });
assertVersions(targetVersions); assertVersions(targetVersions);
}); });
it('does not list head version if feature flag is not enabled', () => {
gon.features = { diffCompareWithHead: false };
setupTest();
const targetVersions = getters.diffCompareDropdownTargetVersions(localState, getters);
expect(targetVersions.find(version => version.isHead)).toBeUndefined();
});
}); });
it('diffCompareDropdownSourceVersions', () => { it('diffCompareDropdownSourceVersions', () => {
......
...@@ -273,7 +273,7 @@ describe('Multi-file store mutations', () => { ...@@ -273,7 +273,7 @@ describe('Multi-file store mutations', () => {
expect(localState.changedFiles).toEqual([]); expect(localState.changedFiles).toEqual([]);
}); });
it('removes tempFile from changedFiles when deleted', () => { it('removes tempFile from changedFiles and stagedFiles when deleted', () => {
localState.entries.filePath = { localState.entries.filePath = {
path: 'filePath', path: 'filePath',
deleted: false, deleted: false,
...@@ -282,10 +282,12 @@ describe('Multi-file store mutations', () => { ...@@ -282,10 +282,12 @@ describe('Multi-file store mutations', () => {
}; };
localState.changedFiles.push({ ...localState.entries.filePath }); localState.changedFiles.push({ ...localState.entries.filePath });
localState.stagedFiles.push({ ...localState.entries.filePath });
mutations.DELETE_ENTRY(localState, 'filePath'); mutations.DELETE_ENTRY(localState, 'filePath');
expect(localState.changedFiles).toEqual([]); expect(localState.changedFiles).toEqual([]);
expect(localState.stagedFiles).toEqual([]);
}); });
it('bursts unused seal', () => { it('bursts unused seal', () => {
......
...@@ -17,4 +17,25 @@ describe ExploreHelper do ...@@ -17,4 +17,25 @@ describe ExploreHelper do
expect(helper.explore_nav_links).to contain_exactly(*menu_items) expect(helper.explore_nav_links).to contain_exactly(*menu_items)
end end
end end
describe '#public_visibility_restricted?' do
using RSpec::Parameterized::TableSyntax
where(:visibility_levels, :expected_status) do
nil | nil
[Gitlab::VisibilityLevel::PRIVATE] | false
[Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL] | false
[Gitlab::VisibilityLevel::PUBLIC] | true
end
with_them do
before do
stub_application_setting(restricted_visibility_levels: visibility_levels)
end
it 'returns the expected status' do
expect(helper.public_visibility_restricted?).to eq(expected_status)
end
end
end
end end
...@@ -8,6 +8,17 @@ describe API::Entities::ProjectImportStatus do ...@@ -8,6 +8,17 @@ describe API::Entities::ProjectImportStatus do
let(:correlation_id) { 'cid' } let(:correlation_id) { 'cid' }
context 'when no import state exists' do
let(:entity) { described_class.new(build(:project)) }
it 'includes basic fields and no failures' do
expect(subject[:import_status]).to eq('none')
expect(subject[:correlation_id]).to be_nil
expect(subject[:import_error]).to be_nil
expect(subject[:failed_relations]).to eq([])
end
end
context 'when import has not finished yet' do context 'when import has not finished yet' do
let(:project) { create(:project, :import_scheduled, import_correlation_id: correlation_id) } let(:project) { create(:project, :import_scheduled, import_correlation_id: correlation_id) }
let(:entity) { described_class.new(project) } let(:entity) { described_class.new(project) }
......
...@@ -3332,7 +3332,7 @@ describe MergeRequest do ...@@ -3332,7 +3332,7 @@ describe MergeRequest do
describe 'check_state?' do describe 'check_state?' do
it 'indicates whether MR is still checking for mergeability' do it 'indicates whether MR is still checking for mergeability' do
state_machine = described_class.state_machines[:merge_status] state_machine = described_class.state_machines[:merge_status]
check_states = [:unchecked, :cannot_be_merged_recheck, :checking] check_states = [:unchecked, :cannot_be_merged_recheck, :cannot_be_merged_rechecking, :checking]
check_states.each do |merge_status| check_states.each do |merge_status|
expect(state_machine.check_state?(merge_status)).to be true expect(state_machine.check_state?(merge_status)).to be true
......
...@@ -766,29 +766,98 @@ describe API::Internal::Base do ...@@ -766,29 +766,98 @@ describe API::Internal::Base do
end end
context 'project does not exist' do context 'project does not exist' do
it 'returns a 200 response with status: false' do context 'git pull' do
project.destroy it 'returns a 200 response with status: false' do
project.destroy
pull(key, project) pull(key, project)
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
expect(json_response["status"]).to be_falsey expect(json_response["status"]).to be_falsey
end
it 'returns a 200 response when using a project path that does not exist' do
post(
api("/internal/allowed"),
params: {
key_id: key.id,
project: 'project/does-not-exist.git',
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh'
}
)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response["status"]).to be_falsey
end
end end
it 'returns a 200 response when using a project path that does not exist' do context 'git push' do
post( before do
api("/internal/allowed"), stub_const('Gitlab::QueryLimiting::Transaction::THRESHOLD', 120)
params: { end
key_id: key.id,
project: 'project/does-not-exist.git',
action: 'git-upload-pack',
secret_token: secret_token,
protocol: 'ssh'
}
)
expect(response).to have_gitlab_http_status(:not_found) subject { push_with_path(key, full_path: path, changes: '_any') }
expect(json_response["status"]).to be_falsey
context 'from a user/group namespace' do
let!(:path) { "#{user.namespace.path}/notexist.git" }
it 'creates the project' do
expect do
subject
end.to change { Project.count }.by(1)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['status']).to be_truthy
end
end
context 'from the personal snippet path' do
let!(:path) { 'snippets/notexist.git' }
it 'does not create snippet' do
expect do
subject
end.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'from a project path' do
context 'from an non existent project path' do
let!(:path) { "#{user.namespace.path}/notexist/snippets/notexist.git" }
it 'does not create project' do
expect do
subject
end.not_to change { Project.count }
expect(response).to have_gitlab_http_status(:not_found)
end
it 'does not create snippet' do
expect do
subject
end.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'from an existent project path' do
let!(:path) { "#{project.full_path}/notexist/snippets/notexist.git" }
it 'does not create snippet' do
expect do
subject
end.not_to change { Snippet.count }
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end end
end end
...@@ -1062,18 +1131,27 @@ describe API::Internal::Base do ...@@ -1062,18 +1131,27 @@ describe API::Internal::Base do
end end
def push(key, container, protocol = 'ssh', env: nil, changes: nil) def push(key, container, protocol = 'ssh', env: nil, changes: nil)
push_with_path(key,
full_path: full_path_for(container),
gl_repository: gl_repository_for(container),
protocol: protocol,
env: env,
changes: changes)
end
def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil)
changes ||= 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master' changes ||= 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master'
params = { params = {
changes: changes, changes: changes,
key_id: key.id, key_id: key.id,
project: full_path_for(container), project: full_path,
gl_repository: gl_repository_for(container),
action: 'git-receive-pack', action: 'git-receive-pack',
secret_token: secret_token, secret_token: secret_token,
protocol: protocol, protocol: protocol,
env: env env: env
} }
params[:gl_repository] = gl_repository if gl_repository
post( post(
api("/internal/allowed"), api("/internal/allowed"),
......
...@@ -800,9 +800,8 @@ describe 'project routing' do ...@@ -800,9 +800,8 @@ describe 'project routing' do
it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/settings/repository", "/gitlab/gitlabhq/-/settings/repository" it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/settings/repository", "/gitlab/gitlabhq/-/settings/repository"
# TODO: remove this test as part of https://gitlab.com/gitlab-org/gitlab/issues/207079 (12.9) it 'to repository#create_deploy_token' do
it 'to ci_cd#create_deploy_token' do expect(post('gitlab/gitlabhq/-/settings/ci_cd/deploy_token/create')).to route_to('projects/settings/repository#create_deploy_token', namespace_id: 'gitlab', project_id: 'gitlabhq')
expect(post('gitlab/gitlabhq/-/settings/ci_cd/deploy_token/create')).to route_to('projects/settings/ci_cd#create_deploy_token', namespace_id: 'gitlab', project_id: 'gitlabhq')
end end
end end
......
...@@ -32,6 +32,10 @@ module WebIdeSpecHelpers ...@@ -32,6 +32,10 @@ module WebIdeSpecHelpers
page.find('.ide-tree-actions') page.find('.ide-tree-actions')
end end
def ide_tab_selector(mode)
".js-ide-#{mode}-mode"
end
def ide_file_row_open?(row) def ide_file_row_open?(row)
row.matches_css?('.is-open') row.matches_css?('.is-open')
end end
...@@ -106,16 +110,16 @@ module WebIdeSpecHelpers ...@@ -106,16 +110,16 @@ module WebIdeSpecHelpers
evaluate_script("monaco.editor.getModel('#{uri}').getValue()") evaluate_script("monaco.editor.getModel('#{uri}').getValue()")
end end
def ide_commit_tab_selector
ide_tab_selector('commit')
end
def ide_commit def ide_commit
ide_switch_mode('commit') find(ide_commit_tab_selector).click
commit_to_current_branch commit_to_current_branch
end end
def ide_switch_mode(mode)
find(".js-ide-#{mode}-mode").click
end
private private
def file_row_container(row) def file_row_container(row)
......
...@@ -111,6 +111,7 @@ RSpec.shared_context 'group navbar structure' do ...@@ -111,6 +111,7 @@ RSpec.shared_context 'group navbar structure' do
nav_sub_items: [ nav_sub_items: [
_('General'), _('General'),
_('Projects'), _('Projects'),
_('Repository'),
_('CI / CD'), _('CI / CD'),
_('Integrations'), _('Integrations'),
_('Webhooks'), _('Webhooks'),
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'a deploy token in ci/cd settings' do RSpec.shared_examples 'a deploy token in settings' do
it 'view deploy tokens' do it 'view deploy tokens' do
within('.deploy-tokens') do within('.deploy-tokens') do
expect(page).to have_content(deploy_token.name) expect(page).to have_content(deploy_token.name)
......
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