Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a060caf3db
commit
797182cd82
|
|
@ -64,7 +64,7 @@ variables:
|
|||
BUILD_ASSETS_IMAGE: "false"
|
||||
ES_JAVA_OPTS: "-Xms256m -Xmx256m"
|
||||
ELASTIC_URL: "http://elastic:changeme@elasticsearch:9200"
|
||||
DOCKER_VERSION: "19.03.0"
|
||||
DOCKER_VERSION: "20.10.1"
|
||||
CACHE_CLASSES: "true"
|
||||
|
||||
# Preparing custom clone path to reduce space used by all random forks
|
||||
|
|
|
|||
|
|
@ -311,6 +311,14 @@ db:check-schema:
|
|||
script:
|
||||
- source scripts/schema_changed.sh
|
||||
|
||||
db:check-migrations:
|
||||
extends:
|
||||
- .db-job-base
|
||||
- .rails:rules:ee-and-foss-mr-with-migration
|
||||
script:
|
||||
- scripts/validate_migration_schema
|
||||
allow_failure: true
|
||||
|
||||
db:migrate-from-v12.10.0:
|
||||
extends: .db-job-base
|
||||
variables:
|
||||
|
|
|
|||
|
|
@ -507,6 +507,12 @@
|
|||
- <<: *if-merge-request
|
||||
changes: *db-patterns
|
||||
|
||||
.rails:rules:ee-and-foss-mr-with-migration:
|
||||
rules:
|
||||
- <<: *if-merge-request
|
||||
changes: *db-patterns
|
||||
- <<: *if-merge-request-title-run-all-rspec
|
||||
|
||||
.rails:rules:ee-and-foss-unit:
|
||||
rules:
|
||||
- changes: *backend-patterns
|
||||
|
|
|
|||
108
Gemfile.lock
108
Gemfile.lock
|
|
@ -5,59 +5,59 @@ GEM
|
|||
abstract_type (0.0.7)
|
||||
acme-client (2.0.6)
|
||||
faraday (>= 0.17, < 2.0.0)
|
||||
actioncable (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
actioncable (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
activejob (= 6.0.3.3)
|
||||
activerecord (= 6.0.3.3)
|
||||
activestorage (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
actionmailbox (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
activejob (= 6.0.3.4)
|
||||
activerecord (= 6.0.3.4)
|
||||
activestorage (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
actionview (= 6.0.3.3)
|
||||
activejob (= 6.0.3.3)
|
||||
actionmailer (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
actionview (= 6.0.3.4)
|
||||
activejob (= 6.0.3.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.0.3.3)
|
||||
actionview (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
actionpack (6.0.3.4)
|
||||
actionview (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
activerecord (= 6.0.3.3)
|
||||
activestorage (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
actiontext (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
activerecord (= 6.0.3.4)
|
||||
activestorage (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
actionview (6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
activejob (6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
activerecord (6.0.3.3)
|
||||
activemodel (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
activemodel (6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
activerecord (6.0.3.4)
|
||||
activemodel (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
activerecord-explain-analyze (0.1.0)
|
||||
activerecord (>= 4)
|
||||
pg
|
||||
activestorage (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
activejob (= 6.0.3.3)
|
||||
activerecord (= 6.0.3.3)
|
||||
activestorage (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
activejob (= 6.0.3.4)
|
||||
activerecord (= 6.0.3.4)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (6.0.3.3)
|
||||
activesupport (6.0.3.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
|
@ -686,7 +686,7 @@ GEM
|
|||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.7.0)
|
||||
loofah (2.8.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
lru_redux (1.1.0)
|
||||
|
|
@ -895,20 +895,20 @@ GEM
|
|||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-timeout (0.5.2)
|
||||
rails (6.0.3.3)
|
||||
actioncable (= 6.0.3.3)
|
||||
actionmailbox (= 6.0.3.3)
|
||||
actionmailer (= 6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
actiontext (= 6.0.3.3)
|
||||
actionview (= 6.0.3.3)
|
||||
activejob (= 6.0.3.3)
|
||||
activemodel (= 6.0.3.3)
|
||||
activerecord (= 6.0.3.3)
|
||||
activestorage (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
rails (6.0.3.4)
|
||||
actioncable (= 6.0.3.4)
|
||||
actionmailbox (= 6.0.3.4)
|
||||
actionmailer (= 6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
actiontext (= 6.0.3.4)
|
||||
actionview (= 6.0.3.4)
|
||||
activejob (= 6.0.3.4)
|
||||
activemodel (= 6.0.3.4)
|
||||
activerecord (= 6.0.3.4)
|
||||
activestorage (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 6.0.3.3)
|
||||
railties (= 6.0.3.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
|
|
@ -922,15 +922,15 @@ GEM
|
|||
rails-i18n (6.0.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 7)
|
||||
railties (6.0.3.3)
|
||||
actionpack (= 6.0.3.3)
|
||||
activesupport (= 6.0.3.3)
|
||||
railties (6.0.3.4)
|
||||
actionpack (= 6.0.3.4)
|
||||
activesupport (= 6.0.3.4)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.20.3, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
raindrops (0.19.1)
|
||||
rake (13.0.1)
|
||||
rake (13.0.3)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
|
|
@ -1186,7 +1186,7 @@ GEM
|
|||
truncato (0.7.11)
|
||||
htmlentities (~> 4.3.1)
|
||||
nokogiri (>= 1.7.0, <= 2.0)
|
||||
tzinfo (1.2.8)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
u2f (0.2.1)
|
||||
uber (0.1.0)
|
||||
|
|
@ -1261,7 +1261,7 @@ GEM
|
|||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
yajl-ruby (1.4.1)
|
||||
zeitwerk (2.4.1)
|
||||
zeitwerk (2.4.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
* @typedef {String} UUIDv4
|
||||
*/
|
||||
|
||||
import MersenneTwister from 'mersenne-twister';
|
||||
import { MersenneTwister } from 'fast-mersenne-twister';
|
||||
import stringHash from 'string-hash';
|
||||
import { isString } from 'lodash';
|
||||
import { v4 } from 'uuid';
|
||||
|
|
@ -49,7 +49,7 @@ function randomValuesForUuid(prng) {
|
|||
const buffer = new ArrayBuffer(4);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
view.setUint32(0, prng.random_int());
|
||||
view.setUint32(0, prng.randomNumber());
|
||||
|
||||
randomValues.push(view.getUint8(0), view.getUint8(1), view.getUint8(2), view.getUint8(3));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import axios from 'axios';
|
||||
|
||||
const getJwt = async () => {
|
||||
return AP.context.getToken();
|
||||
};
|
||||
|
||||
export const addSubscription = async (addPath, namespace) => {
|
||||
const jwt = await getJwt();
|
||||
|
||||
return axios.post(addPath, {
|
||||
jwt,
|
||||
namespace_path: namespace,
|
||||
});
|
||||
};
|
||||
|
||||
export const removeSubscription = async (removePath) => {
|
||||
const jwt = await getJwt();
|
||||
|
||||
return axios.delete(removePath, {
|
||||
params: {
|
||||
jwt,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
<script>
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
name: 'JiraConnectApp',
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
computed: {
|
||||
state() {
|
||||
return this.$root.$data.state || {};
|
||||
|
|
@ -8,9 +11,17 @@ export default {
|
|||
error() {
|
||||
return this.state.error;
|
||||
},
|
||||
showNewUi() {
|
||||
return this.glFeatures.newJiraConnectUi;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<div v-if="showNewUi">
|
||||
<h3>{{ s__('Integrations|Linked namespaces') }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import Vue from 'vue';
|
||||
import $ from 'jquery';
|
||||
import setConfigs from '@gitlab/ui/dist/config';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import GlFeatureFlagsPlugin from '~/vue_shared/gl_feature_flags_plugin';
|
||||
|
||||
import App from './components/app.vue';
|
||||
import { addSubscription, removeSubscription } from '~/jira_connect/api';
|
||||
|
||||
const store = {
|
||||
state: {
|
||||
|
|
@ -27,46 +32,35 @@ const initJiraFormHandlers = () => {
|
|||
alert(error);
|
||||
};
|
||||
|
||||
AP.getLocation((location) => {
|
||||
$('.js-jira-connect-sign-in').each(function updateSignInLink() {
|
||||
const updatedLink = `${$(this).attr('href')}?return_to=${location}`;
|
||||
$(this).attr('href', updatedLink);
|
||||
if (typeof AP.getLocation === 'function') {
|
||||
AP.getLocation((location) => {
|
||||
$('.js-jira-connect-sign-in').each(function updateSignInLink() {
|
||||
const updatedLink = `${$(this).attr('href')}?return_to=${location}`;
|
||||
$(this).attr('href', updatedLink);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$('#add-subscription-form').on('submit', function onAddSubscriptionForm(e) {
|
||||
const actionUrl = $(this).attr('action');
|
||||
const addPath = $(this).attr('action');
|
||||
const namespace = $('#namespace-input').val();
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
AP.context.getToken((token) => {
|
||||
// eslint-disable-next-line no-jquery/no-ajax
|
||||
$.post(actionUrl, {
|
||||
jwt: token,
|
||||
namespace_path: $('#namespace-input').val(),
|
||||
format: 'json',
|
||||
})
|
||||
.done(reqComplete)
|
||||
.fail((err) => reqFailed(err, 'Failed to add namespace. Please try again.'));
|
||||
});
|
||||
addSubscription(addPath, namespace)
|
||||
.then(reqComplete)
|
||||
.catch((err) => reqFailed(err.response.data, 'Failed to add namespace. Please try again.'));
|
||||
});
|
||||
|
||||
$('.remove-subscription').on('click', function onRemoveSubscriptionClick(e) {
|
||||
const href = $(this).attr('href');
|
||||
const removePath = $(this).attr('href');
|
||||
e.preventDefault();
|
||||
|
||||
AP.context.getToken((token) => {
|
||||
// eslint-disable-next-line no-jquery/no-ajax
|
||||
$.ajax({
|
||||
url: href,
|
||||
method: 'DELETE',
|
||||
data: {
|
||||
jwt: token,
|
||||
format: 'json',
|
||||
},
|
||||
})
|
||||
.done(reqComplete)
|
||||
.fail((err) => reqFailed(err, 'Failed to remove namespace. Please try again.'));
|
||||
});
|
||||
removeSubscription(removePath)
|
||||
.then(reqComplete)
|
||||
.catch((err) =>
|
||||
reqFailed(err.response.data, 'Failed to remove namespace. Please try again.'),
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -75,6 +69,14 @@ function initJiraConnect() {
|
|||
|
||||
initJiraFormHandlers();
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
setConfigs();
|
||||
Vue.use(Translate);
|
||||
Vue.use(GlFeatureFlagsPlugin);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
// We should only import styles that we actually use.
|
||||
// @import '@gitlab/ui/src/scss/gitlab_ui';
|
||||
|
||||
@import '@gitlab/ui/src/scss/bootstrap';
|
||||
@import 'bootstrap-vue/src/index';
|
||||
|
||||
@import '@gitlab/ui/src/scss/utilities';
|
||||
|
||||
$atlaskit-border-color: #dfe1e6;
|
||||
|
||||
.ac-content {
|
||||
|
|
@ -40,14 +45,16 @@ $header-height: 40px;
|
|||
}
|
||||
|
||||
.jira-connect-user {
|
||||
float: right;
|
||||
position: relative;
|
||||
top: -30px;
|
||||
font-size: $gl-font-size;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.jira-connect-app {
|
||||
margin-top: $header-height;
|
||||
max-width: 600px;
|
||||
min-height: 95vh;
|
||||
padding-top: 48px;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
|
|
@ -108,5 +115,6 @@ svg {
|
|||
}
|
||||
|
||||
.browser-limitations-notice {
|
||||
font-size: $gl-font-size;
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@
|
|||
padding: 11px 0;
|
||||
}
|
||||
|
||||
.wiki-page-title {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.wiki-last-edit-by {
|
||||
display: block;
|
||||
color: var(--gray-500, $gray-500);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
|
|||
before_action :allow_rendering_in_iframe, only: :index
|
||||
before_action :verify_qsh_claim!, only: :index
|
||||
before_action :authenticate_user!, only: :create
|
||||
before_action do
|
||||
push_frontend_feature_flag(:new_jira_connect_ui, type: :development, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def index
|
||||
@subscriptions = current_jira_installation.subscriptions.preload_namespace_route
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module JiraConnectHelper
|
||||
def new_jira_connect_ui?
|
||||
Feature.enabled?(:new_jira_connect_ui, type: :development, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
|
@ -196,6 +196,10 @@ module Issuable
|
|||
is_a?(Issue)
|
||||
end
|
||||
|
||||
def supports_assignee?
|
||||
false
|
||||
end
|
||||
|
||||
def severity
|
||||
return IssuableSeverity::DEFAULT unless supports_severity?
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@ module IssueAvailableFeatures
|
|||
class_methods do
|
||||
# EE only features are listed on EE::IssueAvailableFeatures
|
||||
def available_features_for_issue_types
|
||||
{}.with_indifferent_access
|
||||
{
|
||||
assignee: %w(issue incident)
|
||||
}.with_indifferent_access
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -405,6 +405,11 @@ class Environment < ApplicationRecord
|
|||
deployment_platform.patch_ingress(deployment_namespace, ingress, data)
|
||||
end
|
||||
|
||||
def clear_all_caches
|
||||
expire_etag_cache
|
||||
clear_reactive_cache!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rollout_status_available?
|
||||
|
|
|
|||
|
|
@ -434,6 +434,10 @@ class Issue < ApplicationRecord
|
|||
moved_to || duplicated_to
|
||||
end
|
||||
|
||||
def supports_assignee?
|
||||
issue_type_supports?(:assignee)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_metrics
|
||||
|
|
|
|||
|
|
@ -1774,6 +1774,10 @@ class MergeRequest < ApplicationRecord
|
|||
false
|
||||
end
|
||||
|
||||
def supports_assignee?
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_rebase_lock
|
||||
|
|
|
|||
|
|
@ -104,6 +104,16 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity
|
|||
presenter(merge_request).api_unapprove_path
|
||||
end
|
||||
|
||||
expose :blob_path do
|
||||
expose :head_path, if: -> (mr, _) { mr.source_branch_sha } do |merge_request|
|
||||
project_blob_path(merge_request.project, merge_request.source_branch_sha)
|
||||
end
|
||||
|
||||
expose :base_path, if: -> (mr, _) { mr.diff_base_sha } do |merge_request|
|
||||
project_blob_path(merge_request.project, merge_request.diff_base_sha)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
delegate :current_user, to: :request
|
||||
|
|
|
|||
|
|
@ -115,16 +115,6 @@ class MergeRequestWidgetEntity < Grape::Entity
|
|||
end
|
||||
end
|
||||
|
||||
expose :blob_path do
|
||||
expose :head_path, if: -> (mr, _) { mr.source_branch_sha } do |merge_request|
|
||||
project_blob_path(merge_request.project, merge_request.source_branch_sha)
|
||||
end
|
||||
|
||||
expose :base_path, if: -> (mr, _) { mr.diff_base_sha } do |merge_request|
|
||||
project_blob_path(merge_request.project, merge_request.diff_base_sha)
|
||||
end
|
||||
end
|
||||
|
||||
expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do
|
||||
expose :head_path do |merge_request|
|
||||
head_pipeline_downloadable_path_for_report_type(:codequality)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ module Environments
|
|||
end
|
||||
|
||||
if environment.patch_ingress(canary_ingress, patch_data)
|
||||
environment.clear_all_caches
|
||||
success
|
||||
else
|
||||
error(_('Failed to update the Canary Ingress.'), :bad_request)
|
||||
|
|
|
|||
|
|
@ -29,17 +29,32 @@ module Groups
|
|||
|
||||
group.chat_team&.remove_mattermost_team(current_user)
|
||||
|
||||
user_ids_for_project_authorizations_refresh = group.user_ids_for_project_authorizations
|
||||
# If any other groups are shared with the group that is being destroyed,
|
||||
# we should specifically trigger update of all project authorizations
|
||||
# for users that are the members of this group.
|
||||
# If not, the project authorization records of these users to projects within the shared groups
|
||||
# will never be removed, causing inconsistencies with access permissions.
|
||||
if any_other_groups_are_shared_with_this_group?
|
||||
user_ids_for_project_authorizations_refresh = group.user_ids_for_project_authorizations
|
||||
end
|
||||
|
||||
group.destroy
|
||||
|
||||
UserProjectAccessChangedService
|
||||
.new(user_ids_for_project_authorizations_refresh)
|
||||
.execute(blocking: true)
|
||||
if user_ids_for_project_authorizations_refresh.present?
|
||||
UserProjectAccessChangedService
|
||||
.new(user_ids_for_project_authorizations_refresh)
|
||||
.execute(blocking: true)
|
||||
end
|
||||
|
||||
group
|
||||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
private
|
||||
|
||||
def any_other_groups_are_shared_with_this_group?
|
||||
group.shared_group_links.any?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
.container
|
||||
.gl-mt-3
|
||||
- if Gitlab.ee? && Feature.enabled?(:devops_adoption_feature, default_enabled: true) && License.feature_available?(:devops_adoption)
|
||||
- if Gitlab.ee? && License.feature_available?(:devops_adoption)
|
||||
= render_if_exists 'admin/dev_ops_report/devops_tabs'
|
||||
- else
|
||||
= render 'report'
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@
|
|||
- else
|
||||
.js-jira-connect-app
|
||||
|
||||
%form#add-subscription-form.subscription-form{ action: jira_connect_subscriptions_path }
|
||||
.ak-field-group
|
||||
%label
|
||||
GitLab namespace
|
||||
- unless new_jira_connect_ui?
|
||||
%form#add-subscription-form.subscription-form{ action: jira_connect_subscriptions_path }
|
||||
.ak-field-group
|
||||
%label
|
||||
GitLab namespace
|
||||
|
||||
.ak-field-group.field-group-input
|
||||
%input#namespace-input.ak-field-text{ type: 'text', required: true, placeholder: 'e.g. "MyCompany" or "MyCompany/GroupName"' }
|
||||
%button.ak-button.ak-button__appearance-primary{ type: 'submit' }
|
||||
Link namespace to Jira
|
||||
.ak-field-group.field-group-input
|
||||
%input#namespace-input.ak-field-text{ type: 'text', required: true, placeholder: 'e.g. "MyCompany" or "MyCompany/GroupName"' }
|
||||
%button.ak-button.ak-button__appearance-primary{ type: 'submit' }
|
||||
Link namespace to Jira
|
||||
|
||||
- if @subscriptions.present?
|
||||
%table.subscriptions
|
||||
|
|
@ -49,6 +50,7 @@
|
|||
- else
|
||||
%h4.empty-subscriptions
|
||||
No linked namespaces
|
||||
%p= s_('Integrations|Namespaces are your GitLab groups and subgroups that will be linked to this Jira instance.')
|
||||
|
||||
%p.browser-limitations-notice
|
||||
%strong Browser limitations:
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
%meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
|
||||
%title
|
||||
GitLab
|
||||
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/css-reset@3.0.6/dist/bundle.css'
|
||||
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/reduced-ui-pack@10.5.5/dist/bundle.css'
|
||||
- unless new_jira_connect_ui?
|
||||
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/css-reset@3.0.6/dist/bundle.css'
|
||||
= stylesheet_link_tag 'https://unpkg.com/@atlaskit/reduced-ui-pack@10.5.5/dist/bundle.css'
|
||||
= yield :page_specific_styles
|
||||
|
||||
= javascript_include_tag 'https://connect-cdn.atl-paas.net/all.js'
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
%code v*
|
||||
or
|
||||
%code *-release
|
||||
are supported
|
||||
are supported.
|
||||
.form-group.row
|
||||
%label.col-md-2.text-right{ for: 'create_access_levels_attributes' }
|
||||
Allowed to create:
|
||||
|
|
|
|||
|
|
@ -7,16 +7,14 @@
|
|||
%button.btn.js-settings-toggle{ type: 'button' }
|
||||
= expanded ? 'Collapse' : 'Expand'
|
||||
%p
|
||||
Limit access to creating and updating tags.
|
||||
Limit access to creating and updating tags. #{link_to "What are protected tags?", help_page_path("user/project/protected_tags")}
|
||||
.settings-content
|
||||
%p
|
||||
By default, protected tags are designed to:
|
||||
By default, protected tags protect your code and:
|
||||
%ul
|
||||
%li Prevent tag creation by everybody except Maintainers
|
||||
%li Prevent <strong>anyone</strong> from updating the tag
|
||||
%li Prevent <strong>anyone</strong> from deleting the tag
|
||||
|
||||
%p Read more about #{link_to "protected tags", help_page_path("user/project/protected_tags")}.
|
||||
%li Allow only users with Maintainer #{link_to "permissions", help_page_path("user/permissions")} to create tags.
|
||||
%li Prevent <strong>anyone</strong> from updating tags.
|
||||
%li Prevent <strong>anyone</strong> from deleting tags.
|
||||
|
||||
- if can? current_user, :admin_project, @project
|
||||
= yield :create_protected_tag
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
.protected-tags-list.js-protected-tags-list
|
||||
- if @protected_tags.empty?
|
||||
.card-header
|
||||
Protected tag (#{@protected_tags_count})
|
||||
Protected tags (#{@protected_tags_count})
|
||||
%p.settings-message.text-center
|
||||
There are currently no protected tags, protect a tag with the form above.
|
||||
No tags are protected.
|
||||
- else
|
||||
- can_admin_project = can?(current_user, :admin_project, @project)
|
||||
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
%col
|
||||
%thead
|
||||
%tr
|
||||
%th Protected tag (#{@protected_tags_count})
|
||||
%th Protected tags (#{@protected_tags_count})
|
||||
%th Last commit
|
||||
%th Allowed to create
|
||||
- if can_admin_project
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@
|
|||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _('Changes')
|
||||
%h3.page-title.gl-flex-fill-1
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _('Changes')
|
||||
|
||||
.nav-controls.pb-md-3.pb-lg-0
|
||||
= link_to wiki_page_path(@wiki, @page, action: :history), class: 'btn gl-button', role: 'button', data: { qa_selector: 'page_history_button' } do
|
||||
|
|
|
|||
|
|
@ -6,15 +6,14 @@
|
|||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
- if @page.persisted?
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= s_("Wiki|Edit Page")
|
||||
- else
|
||||
= s_("Wiki|Create New Page")
|
||||
%h3.page-title.gl-flex-fill-1
|
||||
- if @page.persisted?
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= s_("Wiki|Edit Page")
|
||||
- else
|
||||
= s_("Wiki|Create New Page")
|
||||
|
||||
.nav-controls.pb-md-3.pb-lg-0
|
||||
- if @page.persisted?
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@
|
|||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
|
||||
.nav-text
|
||||
%h2.wiki-page-title
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _('History')
|
||||
%h3.page-title
|
||||
= link_to_wiki_page @page
|
||||
%span.light
|
||||
·
|
||||
= _('History')
|
||||
|
||||
.prepend-top-default.gl-mb-3
|
||||
.table-holder
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
|
||||
.wiki-page-header.top-area.flex-column.flex-lg-row
|
||||
|
||||
.nav-text.flex-fill
|
||||
%h2.wiki-page-title
|
||||
= s_("Wiki|Wiki Pages")
|
||||
|
||||
%h3.page-title.gl-flex-fill-1
|
||||
= s_("Wiki|Wiki Pages")
|
||||
|
||||
.nav-controls.pb-md-3.pb-lg-0
|
||||
= link_to wiki_path(@wiki, action: :git_access), class: 'btn gl-button' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: During group deletion, only enqueue jobs for project_authorizations refresh
|
||||
if the group being deleted has other groups shared with it
|
||||
merge_request: 50617
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update Docker from 19.03.0 to 20.10.1 on CI/CD
|
||||
merge_request: 50732
|
||||
author: Takuya Noguchi
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Updated UI text to match style guidelines
|
||||
merge_request: 50476
|
||||
author:
|
||||
type: other
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix Canary Ingress weight is not reflected on UI immediately
|
||||
merge_request: 50246
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Switch to 2x faster PRNG
|
||||
merge_request: 50811
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Standardize page title styles on all wiki pages
|
||||
merge_request: 49777
|
||||
author:
|
||||
type: changed
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
---
|
||||
name: devops_adoption_feature
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46005
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/271568
|
||||
milestone: '13.6'
|
||||
name: new_jira_connect_ui
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50692
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/295647
|
||||
milestone: '13.8'
|
||||
type: development
|
||||
group: group::analytics
|
||||
default_enabled: true
|
||||
group: group::ecosystem
|
||||
default_enabled: false
|
||||
|
|
@ -7,12 +7,12 @@ NO_SPECS_LABELS = [
|
|||
'documentation',
|
||||
'QA'
|
||||
].freeze
|
||||
NO_NEW_SPEC_MESSAGE = <<~MSG
|
||||
NO_NEW_SPEC_MESSAGE = <<~MSG.freeze
|
||||
You've made some app changes, but didn't add any tests.
|
||||
That's OK as long as you're refactoring existing code,
|
||||
but please consider adding any of the %<labels>s labels.
|
||||
MSG
|
||||
EE_CHANGE_WITH_FOSS_SPEC_CHANGE_MESSAGE = <<~MSG
|
||||
EE_CHANGE_WITH_FOSS_SPEC_CHANGE_MESSAGE = <<~MSG.freeze
|
||||
You've made some EE-specific changes, but only made changes to FOSS tests.
|
||||
This could be a sign that you're testing an EE-specific behavior in a FOSS test.
|
||||
|
||||
|
|
@ -24,6 +24,14 @@ Please make sure the spec files pass in AS-IF-FOSS mode either:
|
|||
|
||||
MSG
|
||||
|
||||
CONTROLLER_SPEC_DEPRECATION_MESSAGE = <<~MSG.freeze
|
||||
Do not add new controller specs. We are moving from controller specs to
|
||||
request specs (and/or feature specs). Please add request specs under
|
||||
`/spec/requests` and/or `/ee/spec/requests` instead.
|
||||
|
||||
See https://gitlab.com/groups/gitlab-org/-/epics/5076 for information.
|
||||
MSG
|
||||
|
||||
has_app_changes = helper.all_changed_files.grep(%r{\A(app|lib|db/(geo/)?(post_)?migrate)/}).any?
|
||||
has_ee_app_changes = helper.all_changed_files.grep(%r{\Aee/(app|lib|db/(geo/)?(post_)?migrate)/}).any?
|
||||
spec_changes = helper.all_changed_files.grep(%r{\Aspec/})
|
||||
|
|
@ -39,3 +47,8 @@ end
|
|||
if has_ee_app_changes && has_spec_changes && !(has_app_changes || has_ee_spec_changes)
|
||||
warn format(EE_CHANGE_WITH_FOSS_SPEC_CHANGE_MESSAGE, spec_files: spec_changes.join(" "), mr_title: gitlab.mr_json['title']), sticky: false
|
||||
end
|
||||
|
||||
# Forbidding a new file addition under `/spec/controllers` or `/ee/spec/controllers`
|
||||
if git.added_files.grep(%r{^(ee/)?spec/controllers/}).any?
|
||||
warn CONTROLLER_SPEC_DEPRECATION_MESSAGE
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,21 +59,3 @@ DevOps Adoption allows you to:
|
|||
- Find the groups that have adopted certain features and can provide guidance to other groups on how to use those features.
|
||||
|
||||

|
||||
|
||||
### Disable or enable DevOps Adoption
|
||||
|
||||
DevOps Adoption is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:devops_adoption_feature)
|
||||
```
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:devops_adoption_feature)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ module.exports = (path) => {
|
|||
'^.+\\.(md|zip|png)$': 'jest-raw-loader',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor|monaco-yaml)/)',
|
||||
'node_modules/(?!(@gitlab/ui|bootstrap-vue|three|monaco-editor|monaco-yaml|fast-mersenne-twister)/)',
|
||||
],
|
||||
timers: 'fake',
|
||||
testEnvironment: '<rootDir>/spec/frontend/environment.js',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ module Gitlab
|
|||
end
|
||||
types Issue, MergeRequest
|
||||
condition do
|
||||
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
quick_action_target.supports_assignee? && current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
|
||||
end
|
||||
parse_params do |assignee_param|
|
||||
extract_users(assignee_param)
|
||||
|
|
|
|||
|
|
@ -15135,6 +15135,12 @@ msgstr ""
|
|||
msgid "Integrations|Issues created in Jira are shown here once you have created the issues in project setup in Jira."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Linked namespaces"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Namespaces are your GitLab groups and subgroups that will be linked to this Jira instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Projects using custom settings will not be affected."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@
|
|||
"@gitlab/tributejs": "1.0.0",
|
||||
"@gitlab/ui": "25.2.1",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "^6.0.3-3",
|
||||
"@rails/ujs": "^6.0.3-2",
|
||||
"@rails/actioncable": "^6.0.3-4",
|
||||
"@rails/ujs": "^6.0.3-4",
|
||||
"@sourcegraph/code-host-integration": "0.0.52",
|
||||
"@toast-ui/editor": "^2.5.1",
|
||||
"@toast-ui/vue-editor": "^2.5.1",
|
||||
|
|
@ -87,6 +87,7 @@
|
|||
"emoji-regex": "^7.0.3",
|
||||
"emoji-unicode-version": "^0.2.1",
|
||||
"exports-loader": "^0.7.0",
|
||||
"fast-mersenne-twister": "1.0.2",
|
||||
"file-loader": "^5.1.0",
|
||||
"font-awesome": "4.7.0",
|
||||
"fuzzaldrin-plus": "^0.6.0",
|
||||
|
|
@ -110,7 +111,6 @@
|
|||
"marked": "^0.3.12",
|
||||
"mathjax": "3",
|
||||
"mermaid": "^8.5.2",
|
||||
"mersenne-twister": "1.1.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"miragejs": "^0.1.40",
|
||||
"mock-apollo-client": "^0.5.0",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ GEM
|
|||
remote: https://rubygems.org/
|
||||
specs:
|
||||
abstract_type (0.0.7)
|
||||
activesupport (6.0.3.3)
|
||||
activesupport (6.0.3.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
|
@ -127,7 +127,7 @@ GEM
|
|||
rubyzip (>= 1.2.2)
|
||||
thread_safe (0.3.6)
|
||||
timecop (0.9.1)
|
||||
tzinfo (1.2.7)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
|
|
@ -142,7 +142,7 @@ GEM
|
|||
procto (~> 0.0.2)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.4.0)
|
||||
zeitwerk (2.4.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
|
|
|||
|
|
@ -0,0 +1,119 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open3'
|
||||
|
||||
class MigrationSchemaValidator
|
||||
FILENAME = 'db/structure.sql'
|
||||
|
||||
MIGRATION_DIRS = %w[db/migrate db/post_migrate].freeze
|
||||
|
||||
SCHEMA_VERSION_DIR = 'db/schema_migrations'
|
||||
|
||||
VERSION_DIGITS = 14
|
||||
|
||||
def validate!
|
||||
if committed_migrations.empty?
|
||||
puts "\e[32m No migrations found, skipping schema validation\e[0m"
|
||||
return
|
||||
end
|
||||
|
||||
validate_schema_on_rollback!
|
||||
validate_schema_on_migrate!
|
||||
validate_schema_version_files!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_schema_on_rollback!
|
||||
committed_migrations.each do |filename|
|
||||
version = find_migration_version(filename)
|
||||
|
||||
run("bin/rails db:migrate:down VERSION=#{version}")
|
||||
end
|
||||
|
||||
git_command = "git diff #{diff_target} -- #{FILENAME}"
|
||||
base_message = "rollback of added migrations does not revert #{FILENAME} to previous state"
|
||||
|
||||
validate_clean_output!(git_command, base_message)
|
||||
end
|
||||
|
||||
def validate_schema_on_migrate!
|
||||
run('bin/rails db:migrate')
|
||||
|
||||
git_command = "git diff -- #{FILENAME}"
|
||||
base_message = "the committed #{FILENAME} does not match the one generated by running added migrations"
|
||||
|
||||
validate_clean_output!(git_command, base_message)
|
||||
end
|
||||
|
||||
def validate_schema_version_files!
|
||||
git_command = "git add -A -n #{SCHEMA_VERSION_DIR}"
|
||||
base_message = "the committed files in #{SCHEMA_VERSION_DIR} do not match those expected by the added migrations"
|
||||
|
||||
validate_clean_output!(git_command, base_message)
|
||||
end
|
||||
|
||||
def committed_migrations
|
||||
@committed_migrations ||= begin
|
||||
git_command = "git diff --name-only --diff-filter=A #{diff_target} -- #{MIGRATION_DIRS.join(' ')}"
|
||||
|
||||
run(git_command).split("\n")
|
||||
end
|
||||
end
|
||||
|
||||
def diff_target
|
||||
@diff_target ||= pipeline_for_merged_results? ? target_branch : merge_base
|
||||
end
|
||||
|
||||
def merge_base
|
||||
run("git merge-base #{target_branch} #{source_ref}")
|
||||
end
|
||||
|
||||
def target_branch
|
||||
ENV['CI_MERGE_REQUEST_TARGET_BRANCH_NAME'] || ENV['TARGET'] || 'master'
|
||||
end
|
||||
|
||||
def source_ref
|
||||
ENV['CI_COMMIT_SHA'] || 'HEAD'
|
||||
end
|
||||
|
||||
def pipeline_for_merged_results?
|
||||
ENV.key?('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA')
|
||||
end
|
||||
|
||||
def find_migration_version(filename)
|
||||
file_basename = File.basename(filename)
|
||||
version_match = /\A(?<version>\d{#{VERSION_DIGITS}})_/.match(file_basename)
|
||||
|
||||
die "#{filename} has an invalid migration version" if version_match.nil?
|
||||
|
||||
version_match[:version]
|
||||
end
|
||||
|
||||
def validate_clean_output!(command, base_message)
|
||||
command_output = run(command)
|
||||
|
||||
return if command_output.empty?
|
||||
|
||||
die "#{base_message}:\n#{command_output}"
|
||||
end
|
||||
|
||||
def die(message, error_code: 1)
|
||||
puts "\e[31mError: #{message}\e[0m"
|
||||
exit error_code
|
||||
end
|
||||
|
||||
def run(cmd)
|
||||
puts "\e[32m$ #{cmd}\e[37m"
|
||||
stdout_str, stderr_str, status = Open3.capture3(cmd)
|
||||
puts "#{stdout_str}#{stderr_str}\e[0m"
|
||||
|
||||
die "command failed: #{stderr_str}" unless status.success?
|
||||
|
||||
stdout_str.chomp
|
||||
end
|
||||
end
|
||||
|
||||
MigrationSchemaValidator.new.validate!
|
||||
|
|
@ -68,7 +68,7 @@ RSpec.describe 'Protected Tags', :js do
|
|||
click_on "Protect"
|
||||
|
||||
within(".protected-tags-list") do
|
||||
expect(page).to have_content("Protected tag (2)")
|
||||
expect(page).to have_content("Protected tags (2)")
|
||||
expect(page).to have_content("2 matching tags")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
|
||||
import { addSubscription, removeSubscription } from '~/jira_connect/api';
|
||||
|
||||
describe('JiraConnect API', () => {
|
||||
let mock;
|
||||
let response;
|
||||
|
||||
const mockAddPath = 'addPath';
|
||||
const mockRemovePath = 'removePath';
|
||||
const mockNamespace = 'namespace';
|
||||
const mockJwt = 'jwt';
|
||||
const mockResponse = { success: true };
|
||||
|
||||
const tokenSpy = jest.fn().mockReturnValue(mockJwt);
|
||||
|
||||
window.AP = {
|
||||
context: {
|
||||
getToken: tokenSpy,
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
response = null;
|
||||
});
|
||||
|
||||
describe('addSubscription', () => {
|
||||
const makeRequest = () => addSubscription(mockAddPath, mockNamespace);
|
||||
|
||||
it('returns success response', async () => {
|
||||
jest.spyOn(axios, 'post');
|
||||
mock
|
||||
.onPost(mockAddPath, {
|
||||
jwt: mockJwt,
|
||||
namespace_path: mockNamespace,
|
||||
})
|
||||
.replyOnce(httpStatus.OK, mockResponse);
|
||||
|
||||
response = await makeRequest();
|
||||
|
||||
expect(tokenSpy).toHaveBeenCalled();
|
||||
expect(axios.post).toHaveBeenCalledWith(mockAddPath, {
|
||||
jwt: mockJwt,
|
||||
namespace_path: mockNamespace,
|
||||
});
|
||||
expect(response.data).toEqual(mockResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeSubscription', () => {
|
||||
const makeRequest = () => removeSubscription(mockRemovePath);
|
||||
|
||||
it('returns success response', async () => {
|
||||
jest.spyOn(axios, 'delete');
|
||||
mock.onDelete(mockRemovePath).replyOnce(httpStatus.OK, mockResponse);
|
||||
|
||||
response = await makeRequest();
|
||||
|
||||
expect(tokenSpy).toHaveBeenCalled();
|
||||
expect(axios.delete).toHaveBeenCalledWith(mockRemovePath, {
|
||||
params: {
|
||||
jwt: mockJwt,
|
||||
},
|
||||
});
|
||||
expect(response.data).toEqual(mockResponse);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import JiraConnectApp from '~/jira_connect/components/app.vue';
|
||||
|
||||
describe('JiraConnectApp', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (options = {}) => {
|
||||
wrapper = shallowMount(JiraConnectApp, {
|
||||
provide: {
|
||||
glFeatures: { newJiraConnectUi: true },
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
const findHeader = () => wrapper.find('h3');
|
||||
const findHeaderText = () => findHeader().text();
|
||||
|
||||
describe('template', () => {
|
||||
it('renders new UI', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findHeader().exists()).toBe(true);
|
||||
expect(findHeaderText()).toBe('Linked namespaces');
|
||||
});
|
||||
|
||||
describe('newJiraConnectUi is false', () => {
|
||||
it('does not render new UI', () => {
|
||||
createComponent({
|
||||
provide: {
|
||||
glFeatures: { newJiraConnectUi: false },
|
||||
},
|
||||
});
|
||||
|
||||
expect(findHeader().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1547,4 +1547,18 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear_all_caches' do
|
||||
subject { environment.clear_all_caches }
|
||||
|
||||
it 'clears all caches on the environment' do
|
||||
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
|
||||
expect(store).to receive(:touch).with(environment.etag_cache_key)
|
||||
end
|
||||
|
||||
expect(environment).to receive(:clear_reactive_cache!)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
|
|||
let_it_be(:project, refind: true) { create :project, :repository }
|
||||
let_it_be(:resource, refind: true) { create(:merge_request, source_project: project, target_project: project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:pipeline) { create(:ci_empty_pipeline, project: project) }
|
||||
|
||||
let(:request) { double('request', current_user: user, project: project) }
|
||||
|
||||
|
|
@ -25,6 +26,17 @@ RSpec.describe MergeRequestPollCachedWidgetEntity do
|
|||
expect(subject[:merge_status]).to eq 'checking'
|
||||
end
|
||||
|
||||
it 'has blob path data' do
|
||||
allow(resource).to receive_messages(
|
||||
base_pipeline: pipeline,
|
||||
head_pipeline: pipeline
|
||||
)
|
||||
|
||||
expect(subject).to include(:blob_path)
|
||||
expect(subject[:blob_path]).to include(:base_path)
|
||||
expect(subject[:blob_path]).to include(:head_path)
|
||||
end
|
||||
|
||||
describe 'diverged_commits_count' do
|
||||
context 'when MR open and its diverging' do
|
||||
it 'returns diverged commits count' do
|
||||
|
|
|
|||
|
|
@ -76,17 +76,6 @@ RSpec.describe MergeRequestWidgetEntity do
|
|||
.to eq("/#{resource.project.full_path}/-/merge_requests/#{resource.iid}.diff")
|
||||
end
|
||||
|
||||
it 'has blob path data' do
|
||||
allow(resource).to receive_messages(
|
||||
base_pipeline: pipeline,
|
||||
head_pipeline: pipeline
|
||||
)
|
||||
|
||||
expect(subject).to include(:blob_path)
|
||||
expect(subject[:blob_path]).to include(:base_path)
|
||||
expect(subject[:blob_path]).to include(:head_path)
|
||||
end
|
||||
|
||||
describe 'codequality report artifacts', :request_store do
|
||||
let(:merge_base_pipeline) { create(:ci_pipeline, :with_codequality_report, project: project) }
|
||||
|
||||
|
|
|
|||
|
|
@ -117,6 +117,12 @@ RSpec.describe Environments::CanaryIngress::UpdateService, :clean_gitlab_redis_c
|
|||
expect(subject[:status]).to eq(:success)
|
||||
expect(subject[:message]).to be_nil
|
||||
end
|
||||
|
||||
it 'clears all caches' do
|
||||
expect(environment).to receive(:clear_all_caches)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when patch request does not succeed' do
|
||||
|
|
|
|||
|
|
@ -135,51 +135,120 @@ RSpec.describe Groups::DestroyService do
|
|||
end
|
||||
|
||||
describe 'authorization updates', :sidekiq_inline do
|
||||
context 'shared groups' do
|
||||
context 'for solo groups' do
|
||||
context 'group is deleted' do
|
||||
it 'updates project authorization' do
|
||||
expect { destroy_group(group, user, false) }.to(
|
||||
change { user.can?(:read_project, project) }.from(true).to(false))
|
||||
end
|
||||
|
||||
it 'does not make use of a specific service to update project_authorizations records' do
|
||||
expect(UserProjectAccessChangedService)
|
||||
.not_to receive(:new).with(group.user_ids_for_project_authorizations)
|
||||
|
||||
destroy_group(group, user, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for shared groups within different hierarchies' do
|
||||
let(:shared_with_group) { group }
|
||||
let!(:shared_group) { create(:group, :private) }
|
||||
let!(:shared_group_child) { create(:group, :private, parent: shared_group) }
|
||||
let!(:shared_group_user) { create(:user) }
|
||||
|
||||
let!(:project) { create(:project, group: shared_group) }
|
||||
let!(:project_child) { create(:project, group: shared_group_child) }
|
||||
|
||||
before do
|
||||
create(:group_group_link, shared_group: shared_group, shared_with_group: group)
|
||||
group.refresh_members_authorized_projects
|
||||
shared_group.add_user(shared_group_user, Gitlab::Access::OWNER)
|
||||
|
||||
create(:group_group_link, shared_group: shared_group, shared_with_group: shared_with_group)
|
||||
shared_with_group.refresh_members_authorized_projects
|
||||
end
|
||||
|
||||
it 'updates project authorization' do
|
||||
expect(user.can?(:read_project, project)).to eq(true)
|
||||
expect(user.can?(:read_project, project_child)).to eq(true)
|
||||
|
||||
destroy_group(group, user, false)
|
||||
|
||||
expect(user.can?(:read_project, project)).to eq(false)
|
||||
expect(user.can?(:read_project, project_child)).to eq(false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'shared groups in the same group hierarchy' do
|
||||
let!(:subgroup) { create(:group, :private, parent: group) }
|
||||
let!(:subgroup_user) { create(:user) }
|
||||
|
||||
before do
|
||||
subgroup.add_user(subgroup_user, Gitlab::Access::MAINTAINER)
|
||||
|
||||
create(:group_group_link, shared_group: group, shared_with_group: subgroup)
|
||||
subgroup.refresh_members_authorized_projects
|
||||
end
|
||||
|
||||
context 'group is deleted' do
|
||||
context 'the shared group is deleted' do
|
||||
it 'updates project authorization' do
|
||||
expect { destroy_group(group, user, false) }.to(
|
||||
change { subgroup_user.can?(:read_project, project) }.from(true).to(false))
|
||||
expect(shared_group_user.can?(:read_project, project)).to eq(true)
|
||||
expect(shared_group_user.can?(:read_project, project_child)).to eq(true)
|
||||
|
||||
destroy_group(shared_group, shared_group_user, false)
|
||||
|
||||
expect(shared_group_user.can?(:read_project, project)).to eq(false)
|
||||
expect(shared_group_user.can?(:read_project, project_child)).to eq(false)
|
||||
end
|
||||
|
||||
it 'does not make use of specific service to update project_authorizations records' do
|
||||
expect(UserProjectAccessChangedService)
|
||||
.not_to receive(:new).with(shared_group.user_ids_for_project_authorizations).and_call_original
|
||||
|
||||
destroy_group(shared_group, shared_group_user, false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'subgroup is deleted' do
|
||||
context 'the shared_with group is deleted' do
|
||||
it 'updates project authorization' do
|
||||
expect { destroy_group(subgroup, user, false) }.to(
|
||||
change { subgroup_user.can?(:read_project, project) }.from(true).to(false))
|
||||
expect(user.can?(:read_project, project)).to eq(true)
|
||||
expect(user.can?(:read_project, project_child)).to eq(true)
|
||||
|
||||
destroy_group(shared_with_group, user, false)
|
||||
|
||||
expect(user.can?(:read_project, project)).to eq(false)
|
||||
expect(user.can?(:read_project, project_child)).to eq(false)
|
||||
end
|
||||
|
||||
it 'makes use of a specific service to update project_authorizations records' do
|
||||
expect(UserProjectAccessChangedService)
|
||||
.to receive(:new).with(shared_with_group.user_ids_for_project_authorizations).and_call_original
|
||||
|
||||
destroy_group(shared_with_group, user, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for shared groups in the same group hierarchy' do
|
||||
let(:shared_group) { group }
|
||||
let(:shared_with_group) { nested_group }
|
||||
let!(:shared_with_group_user) { create(:user) }
|
||||
|
||||
before do
|
||||
shared_with_group.add_user(shared_with_group_user, Gitlab::Access::MAINTAINER)
|
||||
|
||||
create(:group_group_link, shared_group: shared_group, shared_with_group: shared_with_group)
|
||||
shared_with_group.refresh_members_authorized_projects
|
||||
end
|
||||
|
||||
context 'the shared group is deleted' do
|
||||
it 'updates project authorization' do
|
||||
expect { destroy_group(shared_group, user, false) }.to(
|
||||
change { shared_with_group_user.can?(:read_project, project) }.from(true).to(false))
|
||||
end
|
||||
|
||||
it 'does not make use of a specific service to update project authorizations' do
|
||||
# Due to the recursive nature of `Groups::DestroyService`, `UserProjectAccessChangedService`
|
||||
# will still be executed for the nested group as they fall under the same hierarchy
|
||||
# and hence we need to account for this scenario.
|
||||
expect(UserProjectAccessChangedService)
|
||||
.to receive(:new).with(shared_with_group.user_ids_for_project_authorizations).and_call_original
|
||||
|
||||
expect(UserProjectAccessChangedService)
|
||||
.not_to receive(:new).with(shared_group.user_ids_for_project_authorizations)
|
||||
|
||||
destroy_group(shared_group, user, false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'the shared_with group is deleted' do
|
||||
it 'updates project authorization' do
|
||||
expect { destroy_group(shared_with_group, user, false) }.to(
|
||||
change { shared_with_group_user.can?(:read_project, project) }.from(true).to(false))
|
||||
end
|
||||
|
||||
it 'makes use of a specific service to update project authorizations' do
|
||||
expect(UserProjectAccessChangedService)
|
||||
.to receive(:new).with(shared_with_group.user_ids_for_project_authorizations).and_call_original
|
||||
|
||||
destroy_group(shared_with_group, user, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -777,6 +777,11 @@ RSpec.describe QuickActions::InterpretService do
|
|||
let(:issuable) { issue }
|
||||
end
|
||||
|
||||
it_behaves_like 'assign command' do
|
||||
let(:content) { "/assign @#{developer.username}" }
|
||||
let(:issuable) { create(:incident, project: project) }
|
||||
end
|
||||
|
||||
it_behaves_like 'assign command' do
|
||||
let(:content) { "/assign @#{developer.username}" }
|
||||
let(:issuable) { merge_request }
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ RSpec.shared_examples 'User creates wiki page' do
|
|||
|
||||
expect(current_path).to eq(wiki_page_path(wiki, "test"))
|
||||
|
||||
page.within(:css, ".nav-text") do
|
||||
page.within(:css, ".wiki-page-header") do
|
||||
expect(page).to have_content("Create New Page")
|
||||
end
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ RSpec.shared_examples 'User creates wiki page' do
|
|||
|
||||
expect(current_path).to eq(wiki_page_path(wiki, "api"))
|
||||
|
||||
page.within(:css, ".nav-text") do
|
||||
page.within(:css, ".wiki-page-header") do
|
||||
expect(page).to have_content("Create")
|
||||
end
|
||||
|
||||
|
|
@ -103,7 +103,7 @@ RSpec.shared_examples 'User creates wiki page' do
|
|||
|
||||
expect(current_path).to eq(wiki_page_path(wiki, "raketasks"))
|
||||
|
||||
page.within(:css, ".nav-text") do
|
||||
page.within(:css, ".wiki-page-header") do
|
||||
expect(page).to have_content("Create")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ RSpec.shared_examples 'User uses wiki shortcuts' do
|
|||
it 'visit edit wiki page using "e" keyboard shortcut', :js do
|
||||
find('body').native.send_key('e')
|
||||
|
||||
expect(find('.wiki-page-title')).to have_content('Edit Page')
|
||||
expect(find('.page-title')).to have_content('Edit Page')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ RSpec.shared_examples 'User views a wiki page' do
|
|||
|
||||
expect(current_path).to include('one/two/three-test')
|
||||
|
||||
page.within(:css, '.nav-text') do
|
||||
page.within(:css, '.wiki-page-header') do
|
||||
expect(page).to have_content('History')
|
||||
end
|
||||
end
|
||||
|
|
@ -69,7 +69,7 @@ RSpec.shared_examples 'User views a wiki page' do
|
|||
|
||||
click_on('Page history')
|
||||
|
||||
within('.nav-text') do
|
||||
within('.wiki-page-header') do
|
||||
expect(page).to have_content('History')
|
||||
end
|
||||
|
||||
|
|
|
|||
26
yarn.lock
26
yarn.lock
|
|
@ -1124,15 +1124,15 @@
|
|||
consola "^2.10.1"
|
||||
node-fetch "^2.6.0"
|
||||
|
||||
"@rails/actioncable@^6.0.3-3":
|
||||
version "6.0.3-3"
|
||||
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.0.3-3.tgz#fb1a46d3d353512764d5fa3cea2f492391601b7a"
|
||||
integrity sha512-+DEbtzvD2ITPKOGBAV0lLdUvImCsHtUYfxwW7TDKOrGNEFqgbVJij7aO6ZE8a3aDPQ7NnO/MlyrPwLVeiIZRSw==
|
||||
"@rails/actioncable@^6.0.3-4":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.0.tgz#f336f25450b1bc43b99bc60557a70b6e6bb1d3d2"
|
||||
integrity sha512-eDgy+vcKN9RIzxmMBfSAe77rTj2cp6kJALiVQyKrW2O9EK2MdostOmP+99At/Dit3ur5+77NVnruxD7y14ZYFA==
|
||||
|
||||
"@rails/ujs@^6.0.3-2":
|
||||
version "6.0.3-2"
|
||||
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.0.3-2.tgz#e14c1f29086858215ce7ccd9ad6d8888c458b4a3"
|
||||
integrity sha512-WcpIEftNCfGDEgk6KerOugiet75Mir5q/HT1yt3dDhpBI91BaZ15lfSQIsZwMw2nyeDz9A9QBz8dAFAd4gXIzg==
|
||||
"@rails/ujs@^6.0.3-4":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.0.tgz#9a48df6511cb2b472c9f596c1f37dc0af022e751"
|
||||
integrity sha512-kQNKyM4ePAc4u9eR1c4OqrbAHH+3SJXt++8izIjeaZeg+P7yBtgoF/dogMD/JPPowNC74ACFpM/4op0Ggp/fPw==
|
||||
|
||||
"@sindresorhus/is@^0.14.0":
|
||||
version "0.14.0"
|
||||
|
|
@ -5009,6 +5009,11 @@ fast-levenshtein@~2.0.6:
|
|||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||
|
||||
fast-mersenne-twister@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-mersenne-twister/-/fast-mersenne-twister-1.0.2.tgz#5ead7caf3ace592a5789d11767732bd81cbaaa56"
|
||||
integrity sha512-IaClPxsoBu3MxGpcURyjV8otT5Bj4ARoK0KBCJGnEVnD1A/qclL5eIeYiUuwG/WWJPxL1jlK61HTm2T6SBmvBQ==
|
||||
|
||||
fault@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa"
|
||||
|
|
@ -8190,11 +8195,6 @@ mermaid@^8.5.2:
|
|||
moment-mini "^2.22.1"
|
||||
scope-css "^1.2.1"
|
||||
|
||||
mersenne-twister@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a"
|
||||
integrity sha1-+RZhjuQ9cXnvz2Qb7EUx65Zwl4o=
|
||||
|
||||
methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
|
|
|
|||
Loading…
Reference in New Issue