Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-08-26 12:10:28 +00:00
parent c8a7e4ada1
commit 6a0a4a80f7
27 changed files with 337 additions and 55 deletions

View file

@ -1 +1 @@
6ecbf56693083f1a872a53ad6accfc7d0d806718
d950585cb9763c6014ae2c9b7c4f4923d90c9f81

View file

@ -1 +1 @@
13.19.1
13.21.0

View file

@ -1,6 +1,5 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
import { GlTooltipDirective, GlIcon, GlLink } from '@gitlab/ui';
import { GlTooltipDirective, GlIcon, GlLink, GlSprintf } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __, s__, sprintf } from '~/locale';
@ -32,6 +31,7 @@ export default {
ExternalUrlComponent,
GlIcon,
GlLink,
GlSprintf,
MonitoringButtonComponent,
PinComponent,
DeleteComponent,
@ -647,14 +647,17 @@ export default {
</span>
<span v-if="!isFolder && deploymentHasUser" class="text-break-word">
by
<user-avatar-link
:link-href="deploymentUser.web_url"
:img-src="deploymentUser.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="deploymentUser.username"
class="js-deploy-user-container float-none"
/>
<gl-sprintf :message="s__('Environments|by %{avatar}')">
<template #avatar>
<user-avatar-link
:link-href="deploymentUser.web_url"
:img-src="deploymentUser.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="deploymentUser.username"
class="js-deploy-user-container float-none"
/>
</template>
</gl-sprintf>
</span>
<div v-if="showNoDeployments" class="commit-title table-mobile-content">
@ -743,13 +746,16 @@ export default {
</div>
<div class="gl-display-flex">
<span v-if="upcomingDeployment.user" class="text-break-word">
by
<user-avatar-link
:link-href="upcomingDeployment.user.web_url"
:img-src="upcomingDeployment.user.avatar_url"
:img-alt="upcomingDeploymentUserImageAltDescription"
:tooltip-text="upcomingDeployment.user.username"
/>
<gl-sprintf :message="s__('Environments|by %{avatar}')">
<template #avatar>
<user-avatar-link
:link-href="upcomingDeployment.user.web_url"
:img-src="upcomingDeployment.user.avatar_url"
:img-alt="upcomingDeploymentUserImageAltDescription"
:tooltip-text="upcomingDeployment.user.username"
/>
</template>
</gl-sprintf>
</span>
</div>
</div>

View file

@ -5,6 +5,7 @@ import Group from '~/group';
import { parseBoolean } from '~/lib/utils/common_utils';
import NewGroupCreationApp from './components/app.vue';
import GroupPathValidator from './group_path_validator';
import initToggleInviteMembers from './toggle_invite_members';
new GroupPathValidator(); // eslint-disable-line no-new
@ -31,3 +32,5 @@ function initNewGroupCreation(el) {
const el = document.querySelector('.js-new-group-creation');
initNewGroupCreation(el);
initToggleInviteMembers();

View file

@ -0,0 +1,14 @@
import { parseBoolean } from '~/lib/utils/common_utils';
export default function initToggleInviteMembers() {
const inviteMembersSection = document.querySelector('.js-invite-members-section');
const setupForCompanyRadios = document.querySelectorAll('input[name="group[setup_for_company]"]');
if (inviteMembersSection && setupForCompanyRadios.length) {
setupForCompanyRadios.forEach((el) => {
el.addEventListener('change', (event) => {
inviteMembersSection.classList.toggle('hidden', !parseBoolean(event.target.value));
});
});
}
}

View file

@ -64,6 +64,7 @@ class GroupsController < Groups::ApplicationController
def new
@group = Group.new(params.permit(:parent_id))
@group.build_namespace_settings
end
def create
@ -269,7 +270,9 @@ class GroupsController < Groups::ApplicationController
:default_branch_name,
:allow_mfa_for_subgroups,
:resource_access_token_creation_allowed,
:prevent_sharing_groups_outside_hierarchy
:prevent_sharing_groups_outside_hierarchy,
:setup_for_company,
:jobs_to_be_done
]
end
@ -342,7 +345,15 @@ class GroupsController < Groups::ApplicationController
render action: 'new'
end
def successful_creation_hooks; end
def successful_creation_hooks
update_user_role_and_setup_for_company
end
def update_user_role_and_setup_for_company
user_params = params.fetch(:user, {}).permit(:role)
user_params[:setup_for_company] = @group.setup_for_company if !@group.setup_for_company.nil? && current_user.setup_for_company.nil?
Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute if user_params.present?
end
def groups
if @group.supports_events?

View file

@ -219,6 +219,18 @@ module GroupsHelper
def group_url_error_message
s_('GroupSettings|Please choose a group URL with no special characters or spaces.')
end
# Maps `jobs_to_be_done` values to option texts
def localized_jobs_to_be_done_choices
{
basics: _('I want to learn the basics of Git'),
move_repository: _('I want to move my repository to GitLab from somewhere else'),
code_storage: _('I want to store my code'),
exploring: _('I want to explore GitLab to see if its worth switching to'),
ci: _('I want to use GitLab CI with my existing repository'),
other: _('A different reason')
}.with_indifferent_access.freeze
end
end
GroupsHelper.prepend_mod_with('GroupsHelper')

View file

@ -81,7 +81,7 @@ class Group < Namespace
# debian_distributions and associated component_files must be destroyed by ruby code in order to properly remove carrierwave uploads
has_many :debian_distributions, class_name: 'Packages::Debian::GroupDistribution', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
delegate :prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap, to: :namespace_settings
delegate :prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap, :setup_for_company, :jobs_to_be_done, to: :namespace_settings
accepts_nested_attributes_for :variables, allow_destroy: true

View file

@ -16,9 +16,12 @@ class NamespaceSetting < ApplicationRecord
before_validation :normalize_default_branch_name
enum jobs_to_be_done: { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5 }, _suffix: true
NAMESPACE_SETTINGS_PARAMS = [:default_branch_name, :delayed_project_removal,
:lock_delayed_project_removal, :resource_access_token_creation_allowed,
:prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap].freeze
:prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap,
:setup_for_company, :jobs_to_be_done].freeze
self.primary_key = :namespace_id

View file

@ -13,7 +13,10 @@
- if Gitlab.config.mattermost.enabled
.row
= render 'create_chat_team', f: f
.row
= render 'personalize', f: f
.row.js-invite-members-section
.col-sm-4
= render_if_exists 'shared/groups/invite_members'

View file

@ -0,0 +1,27 @@
.row
.form-group.col-sm-12.gl-mb-0
%label.label-bold
= _('Now, personalize your GitLab experience')
%p
= _("We'll use this to help surface the right features and information to you.")
.row
.form-group.col-sm-4
= label :user, :role, _('Role')
= select :user, :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { selected: @current_user.role }, class: 'form-control'
.row
.form-group.col-sm-4
= f.label :setup_for_company, _('Who will be using this group?')
.gl-display-flex.gl-flex-direction-column.gl-lg-flex-direction-row
.gl-flex-grow-1.gl-display-flex.gl-align-items-center
= f.radio_button :setup_for_company, true, checked: true
= f.label :setup_for_company, _('My company or team'), class: 'gl-font-weight-normal gl-mb-0 gl-ml-2', value: 'true'
.gl-flex-grow-1.gl-display-flex.gl-align-items-center
= f.radio_button :setup_for_company, false
= f.label :setup_for_company, _('Just me'), class: 'gl-font-weight-normal gl-mb-0 gl-ml-2', value: 'false'
.row
.form-group.col-sm-4
= f.label :jobs_to_be_done, _("What will you use this group for?")
= f.select :jobs_to_be_done, ::NamespaceSetting.jobs_to_be_dones.keys.map { |job_to_be_done| [localized_jobs_to_be_done_choices[job_to_be_done], job_to_be_done] }, { include_blank: true }, class: 'form-control'

View file

@ -4,8 +4,9 @@
%fieldset
%h5= _('Permissions')
.form-group
= render 'shared/allow_request_access', form: f
- unless ::Feature.enabled?(:saas_user_caps)
.form-group
= render 'shared/allow_request_access', form: f
- if @group.root?
.form-group.gl-mb-3
@ -43,5 +44,6 @@
= render_if_exists 'groups/settings/prevent_forking', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f, group: @group
= render_if_exists 'groups/personal_access_token_expiration_policy', f: f, group: @group
= render_if_exists 'groups/settings/membership', f: f
= render_if_exists 'groups/member_lock_setting', f: f, group: @group
= f.submit _('Save changes'), class: 'btn gl-button btn-confirm gl-mt-3 js-dirty-submit', data: { qa_selector: 'save_permissions_changes_button' }

View file

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/332963
milestone: '14.0'
type: development
group: group::pipeline authoring
default_enabled: false
default_enabled: true

View file

@ -0,0 +1,21 @@
# frozen_string_literal: true
class AddColumnsToNamespaceSettings < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
with_lock_retries do
add_column :namespace_settings, :setup_for_company, :boolean
add_column :namespace_settings, :jobs_to_be_done, :smallint
end
end
def down
with_lock_retries do
remove_column :namespace_settings, :setup_for_company
remove_column :namespace_settings, :jobs_to_be_done
end
end
end

View file

@ -0,0 +1 @@
5a02c5a24bb4c7cb63da2e5cc53ff89461f328d0092bb4bb6589223dc4bdae8c

View file

@ -15344,6 +15344,8 @@ CREATE TABLE namespace_settings (
lock_delayed_project_removal boolean DEFAULT false NOT NULL,
prevent_sharing_groups_outside_hierarchy boolean DEFAULT false NOT NULL,
new_user_signups_cap integer,
setup_for_company boolean,
jobs_to_be_done smallint,
CONSTRAINT check_0ba93c78c7 CHECK ((char_length(default_branch_name) <= 255))
);

View file

@ -4472,7 +4472,6 @@ Input type: `VulnerabilityCreateInput`
| <a id="mutationvulnerabilitycreateproject"></a>`project` | [`ProjectID!`](#projectid) | ID of the project to attach the vulnerability to. |
| <a id="mutationvulnerabilitycreateresolvedat"></a>`resolvedAt` | [`Time`](#time) | Timestamp of when the vulnerability state changed to resolved (defaults to creation time if status is `resolved`). |
| <a id="mutationvulnerabilitycreatescannername"></a>`scannerName` | [`String!`](#string) | Name of the security scanner used to discover the vulnerability. |
| <a id="mutationvulnerabilitycreatescannertype"></a>`scannerType` | [`SecurityScannerType!`](#securityscannertype) | Type of the security scanner used to discover the vulnerability. |
| <a id="mutationvulnerabilitycreateseverity"></a>`severity` | [`VulnerabilitySeverity`](#vulnerabilityseverity) | Severity of the vulnerability (defaults to `unknown`). |
| <a id="mutationvulnerabilitycreatesolution"></a>`solution` | [`String`](#string) | How to fix this vulnerability. |
| <a id="mutationvulnerabilitycreatestate"></a>`state` | [`VulnerabilityState`](#vulnerabilitystate) | State of the vulnerability (defaults to `detected`). |

View file

@ -82,6 +82,10 @@ To create a group:
- Underscores
- Dashes and dots (it cannot start with dashes or end in a dot)
1. Choose the [visibility level](../../public_access/public_access.md).
1. Personalize your GitLab experience by answering the following questions:
- What is your role?
- Who will be using this group?
- What will you use this group for?
1. Invite GitLab members or other users to join the group.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>

View file

@ -13,18 +13,13 @@ module API
post '/lint' do
unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil?
result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute
result = Gitlab::Ci::Lint.new(project: nil, current_user: current_user)
.validate(params[:content], dry_run: false)
status 200
response = if result.errors.empty?
{ status: 'valid', errors: [], warnings: result.warnings }
else
{ status: 'invalid', errors: result.errors, warnings: result.warnings }
end
response.tap do |response|
response[:merged_yaml] = result.merged_yaml if params[:include_merged_yaml]
Entities::Ci::Lint::Result.represent(result, current_user: current_user).serializable_hash.tap do |presented_result|
presented_result[:status] = presented_result[:valid] ? 'valid' : 'invalid'
presented_result.delete(:merged_yaml) unless params[:include_merged_yaml]
end
end
end

View file

@ -30,5 +30,3 @@ module Gitlab
end
end
end
Gitlab::Checks::BaseSingleChecker.prepend_mod_with('Gitlab::Checks::BaseSingleChecker')

View file

@ -76,23 +76,33 @@ module Gitlab
result
end
def single_change_accesses
@single_changes_accesses ||=
changes.map do |change|
commits =
if change[:newrev].blank? || Gitlab::Git.blank_ref?(change[:newrev])
[]
else
Gitlab::Lazy.new { commits_for(change[:newrev]) }
end
Checks::SingleChangeAccess.new(
change,
user_access: user_access,
project: project,
protocol: protocol,
logger: logger,
commits: commits
)
end
end
protected
def single_access_checks!
# Iterate over all changes to find if user allowed all of them to be applied
changes.each do |change|
commits = Gitlab::Lazy.new { commits_for(change[:newrev]) }
# If user does not have access to make at least one change, cancel all
# push by allowing the exception to bubble up
Checks::SingleChangeAccess.new(
change,
user_access: user_access,
project: project,
protocol: protocol,
logger: logger,
commits: commits
).validate!
single_change_accesses.each do |single_change_access|
single_change_access.validate!
end
end
@ -102,3 +112,5 @@ module Gitlab
end
end
end
Gitlab::Checks::ChangesAccess.prepend_mod_with('Gitlab::Checks::ChangesAccess')

View file

@ -21,7 +21,7 @@ module Gitlab
def initialize(project:, current_user:, sha: nil)
@project = project
@current_user = current_user
@sha = sha || project.repository.commit&.sha
@sha = sha || project&.repository&.commit&.sha
end
def validate(content, dry_run: false)

View file

@ -12911,6 +12911,9 @@ msgstr ""
msgid "Environments|Your feedback helps GitLab make environments better for you and other users. Participate and enter a sweepstake to win a USD 30 gift card."
msgstr ""
msgid "Environments|by %{avatar}"
msgstr ""
msgid "Environments|protected"
msgstr ""
@ -20808,6 +20811,9 @@ msgstr ""
msgid "Members of a group may only view projects they have permission to access"
msgstr ""
msgid "Membership"
msgstr ""
msgid "Members|%{time} by %{user}"
msgstr ""
@ -23071,6 +23077,9 @@ msgstr ""
msgid "Novice"
msgstr ""
msgid "Now, personalize your GitLab experience"
msgstr ""
msgid "Nuget metadatum must have at least license_url, project_url or icon_url set"
msgstr ""
@ -37346,6 +37355,9 @@ msgstr ""
msgid "We'll continuously validate your pipeline configuration. The validation results will appear here."
msgstr ""
msgid "We'll use this to help surface the right features and information to you."
msgstr ""
msgid "We've found no vulnerabilities"
msgstr ""
@ -37601,6 +37613,9 @@ msgstr ""
msgid "What is your job title? (optional)"
msgstr ""
msgid "What will you use this group for?"
msgstr ""
msgid "What's new"
msgstr ""
@ -37660,6 +37675,9 @@ msgstr ""
msgid "Who will be using this GitLab trial?"
msgstr ""
msgid "Who will be using this group?"
msgstr ""
msgid "Why are you signing up? (Optional)"
msgstr ""

View file

@ -370,6 +370,57 @@ RSpec.describe GroupsController, factory_default: :keep do
end
end
end
context 'when creating a group with the `role` attribute present' do
it 'changes the users role' do
sign_in(user)
expect do
post :create, params: { group: { name: 'new_group', path: 'new_group' }, user: { role: 'devops_engineer' } }
end.to change { user.reload.role }.to('devops_engineer')
end
end
context 'when creating a group with the `setup_for_company` attribute present' do
before do
sign_in(user)
end
subject do
post :create, params: { group: { name: 'new_group', path: 'new_group', setup_for_company: 'false' } }
end
it 'sets the groups `setup_for_company` value' do
subject
expect(Group.last.setup_for_company).to be(false)
end
context 'when the user already has a value for `setup_for_company`' do
before do
user.update_attribute(:setup_for_company, true)
end
it 'does not change the users `setup_for_company` value' do
expect(Users::UpdateService).not_to receive(:new)
expect { subject }.not_to change { user.reload.setup_for_company }.from(true)
end
end
context 'when the user has no value for `setup_for_company`' do
it 'changes the users `setup_for_company` value' do
expect(Users::UpdateService).to receive(:new).and_call_original
expect { subject }.to change { user.reload.setup_for_company }.to(false)
end
end
end
context 'when creating a group with the `jobs_to_be_done` attribute present' do
it 'sets the groups `jobs_to_be_done` value' do
sign_in(user)
post :create, params: { group: { name: 'new_group', path: 'new_group', jobs_to_be_done: 'other' } }
expect(Group.last.jobs_to_be_done).to eq('other')
end
end
end
describe 'GET #index' do

View file

@ -486,4 +486,10 @@ RSpec.describe GroupsHelper do
expect(helper.can_admin_group_member?(group)).to be(false)
end
end
describe '#localized_jobs_to_be_done_choices' do
it 'has a translation for all `jobs_to_be_done` values' do
expect(localized_jobs_to_be_done_choices.keys).to match_array(NamespaceSetting.jobs_to_be_dones.keys)
end
end
end

View file

@ -174,6 +174,101 @@ RSpec.describe Gitlab::Checks::ChangesAccess do
end
end
describe '#single_change_accesses' do
let(:commits_for) { {} }
let(:expected_accesses) { [] }
shared_examples '#single_change_access' do
before do
commits_for.each do |id, commits|
expect(subject)
.to receive(:commits_for)
.with(id)
.and_return(commits)
end
end
it 'returns an array of SingleChangeAccess' do
# Commits are wrapped in a Gitlab::Lazy and thus need to be resolved
# first such that we can directly compare types.
actual_accesses = subject.single_change_accesses
.each { |access| access.instance_variable_set(:@commits, access.commits.to_a) }
expect(actual_accesses).to match_array(expected_accesses)
end
end
context 'with no changes' do
let(:changes) { [] }
it_behaves_like '#single_change_access'
end
context 'with a single change and no new commits' do
let(:commits_for) { { 'new' => [] } }
let(:changes) do
[
{ oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch' }
]
end
let(:expected_accesses) do
[
have_attributes(oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch', commits: [])
]
end
it_behaves_like '#single_change_access'
end
context 'with a single change and new commits' do
let(:commits_for) { { 'new' => [create_commit('new', [])] } }
let(:changes) do
[
{ oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch' }
]
end
let(:expected_accesses) do
[
have_attributes(oldrev: 'old', newrev: 'new', ref: 'refs/heads/branch', commits: [create_commit('new', [])])
]
end
it_behaves_like '#single_change_access'
end
context 'with multiple changes' do
let(:commits_for) do
{
'a' => [create_commit('a', [])],
'c' => [create_commit('c', [])],
'd' => []
}
end
let(:changes) do
[
{ newrev: 'a', ref: 'refs/heads/a' },
{ oldrev: 'b', ref: 'refs/heads/b' },
{ oldrev: 'a', newrev: 'c', ref: 'refs/heads/c' },
{ newrev: 'd', ref: 'refs/heads/d' }
]
end
let(:expected_accesses) do
[
have_attributes(newrev: 'a', ref: 'refs/heads/a', commits: [create_commit('a', [])]),
have_attributes(oldrev: 'b', ref: 'refs/heads/b', commits: []),
have_attributes(oldrev: 'a', newrev: 'c', ref: 'refs/heads/c', commits: [create_commit('c', [])]),
have_attributes(newrev: 'd', ref: 'refs/heads/d', commits: [])
]
end
it_behaves_like '#single_change_access'
end
end
def create_commit(id, parent_ids)
Gitlab::Git::Commit.new(project.repository, {
id: id,

View file

@ -113,7 +113,6 @@ RSpec.describe API::Lint do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['status']).to eq('valid')
expect(json_response['warnings']).not_to be_empty
expect(json_response['status']).to eq('valid')
expect(json_response['errors']).to eq([])
end
end