Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-03-01 15:22:06 +00:00
parent fbc7e1f503
commit 2ccb5c91c8
86 changed files with 466 additions and 443 deletions

View File

@ -121,6 +121,11 @@ yarn-audit-dependency_scanning:
- cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/ - cd .. && tar -I "gzip --best" -cf gitlab.tgz gitlab/
script: script:
- DEBUG=* node /usr/src/app/cli.js analyze --format gitlab --manager ${PACKAGE_MANAGER} gitlab.tgz | tee ${CI_PROJECT_DIR}/gl-dependency-scanning-report.json - DEBUG=* node /usr/src/app/cli.js analyze --format gitlab --manager ${PACKAGE_MANAGER} gitlab.tgz | tee ${CI_PROJECT_DIR}/gl-dependency-scanning-report.json
after_script:
- mkdir ~/.aws
- '[[ -z "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ]] || mv "${AWS_SIEM_REPORT_INGESTION_CREDENTIALS_FILE}" ~/.aws/credentials'
- npm install --no-save --ignore-scripts @aws-sdk/client-s3@3.49.0
- scripts/ingest-reports-to-siem
artifacts: artifacts:
paths: paths:
- gl-dependency-scanning-report.json - gl-dependency-scanning-report.json

View File

@ -17,9 +17,6 @@ export default {
revokePath: { revokePath: {
default: '', default: '',
}, },
buttonClass: {
default: '',
},
}, },
computed: { computed: {
modalId() { modalId() {
@ -38,10 +35,9 @@ export default {
<div> <div>
<gl-button <gl-button
v-gl-modal="modalId" v-gl-modal="modalId"
:class="buttonClass"
category="primary" category="primary"
variant="danger" variant="danger"
class="float-right" class="gl-float-right"
data-testid="revoke-button" data-testid="revoke-button"
>{{ s__('DeployTokens|Revoke') }}</gl-button >{{ s__('DeployTokens|Revoke') }}</gl-button
> >

View File

@ -9,14 +9,13 @@ export default () => {
} }
return containers.forEach((el) => { return containers.forEach((el) => {
const { token, revokePath, buttonClass } = el.dataset; const { token, revokePath } = el.dataset;
return new Vue({ return new Vue({
el, el,
provide: { provide: {
token: JSON.parse(token), token: JSON.parse(token),
revokePath, revokePath,
buttonClass,
}, },
render(h) { render(h) {
return h(RevokeButton); return h(RevokeButton);

View File

@ -83,17 +83,17 @@ export default {
i18n: { i18n: {
sectionTitle: s__('JiraService|View Jira issues in GitLab'), sectionTitle: s__('JiraService|View Jira issues in GitLab'),
sectionDescription: s__( sectionDescription: s__(
'JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only.', 'JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues.',
), ),
enableCheckboxLabel: s__('JiraService|Enable Jira issues'), enableCheckboxLabel: s__('JiraService|Enable Jira issues'),
enableCheckboxHelp: s__( enableCheckboxHelp: s__(
'JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below.', 'JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select.',
), ),
projectKeyLabel: s__('JiraService|Jira project key'), projectKeyLabel: s__('JiraService|Jira project key'),
projectKeyPlaceholder: s__('JiraService|For example, AB'), projectKeyPlaceholder: s__('JiraService|For example, AB'),
requiredFieldFeedback: __('This field is required.'), requiredFieldFeedback: __('This field is required.'),
issueTrackerConflictWarning: s__( issueTrackerConflictWarning: s__(
'JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used.', 'JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used.',
), ),
}, },
}; };

View File

@ -138,7 +138,7 @@ export default {
label-for="service[trigger]" label-for="service[trigger]"
:description=" :description="
s__( s__(
'Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled).', 'JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created.',
) )
" "
> >

View File

@ -264,7 +264,7 @@ export default {
<gl-button <gl-button
:disabled="submitDisabled" :disabled="submitDisabled"
:loading="isLoading" :loading="isLoading"
variant="success" variant="confirm"
data-qa-selector="invite_button" data-qa-selector="invite_button"
data-testid="invite-button" data-testid="invite-button"
@click="submit" @click="submit"

View File

@ -135,6 +135,14 @@ export default {
const canReopen = this.isClosed && this.canReopenIssue; const canReopen = this.isClosed && this.canReopenIssue;
return canClose || canReopen; return canClose || canReopen;
}, },
hasDesktopDropdown() {
return (
this.canCreateIssue || this.canPromoteToEpic || !this.isIssueAuthor || this.canReportSpam
);
},
hasMobileDropdown() {
return this.hasDesktopDropdown || this.showToggleIssueStateButton;
},
}, },
created() { created() {
eventHub.$on('toggle.issuable.state', this.toggleIssueState); eventHub.$on('toggle.issuable.state', this.toggleIssueState);
@ -223,10 +231,12 @@ export default {
<template> <template>
<div class="detail-page-header-actions gl-display-flex"> <div class="detail-page-header-actions gl-display-flex">
<gl-dropdown <gl-dropdown
v-if="hasMobileDropdown"
class="gl-sm-display-none! w-100" class="gl-sm-display-none! w-100"
block block
:text="dropdownText" :text="dropdownText"
data-qa-selector="issue_actions_dropdown" data-qa-selector="issue_actions_dropdown"
data-testid="mobile-dropdown"
:loading="isToggleStateButtonLoading" :loading="isToggleStateButtonLoading"
> >
<gl-dropdown-item <gl-dropdown-item
@ -276,11 +286,13 @@ export default {
</gl-button> </gl-button>
<gl-dropdown <gl-dropdown
v-if="hasDesktopDropdown"
class="gl-display-none gl-sm-display-inline-flex! gl-ml-3" class="gl-display-none gl-sm-display-inline-flex! gl-ml-3"
icon="ellipsis_v" icon="ellipsis_v"
category="tertiary" category="tertiary"
:text="dropdownText" :text="dropdownText"
:text-sr-only="true" :text-sr-only="true"
data-testid="desktop-dropdown"
no-caret no-caret
right right
> >

View File

@ -1,5 +1,5 @@
<script> <script>
import { GlSprintf, GlButton, GlButtonGroup } from '@gitlab/ui'; import { GlSprintf, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mapGetters, mapState, mapActions } from 'vuex'; import { mapGetters, mapState, mapActions } from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import FileIcon from '~/vue_shared/components/file_icon.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue';
@ -23,6 +23,7 @@ export default {
GlButton, GlButton,
GlButtonGroup, GlButtonGroup,
GlSprintf, GlSprintf,
GlLoadingIcon,
FileIcon, FileIcon,
DiffFileEditor, DiffFileEditor,
InlineConflictLines, InlineConflictLines,
@ -72,9 +73,7 @@ export default {
</script> </script>
<template> <template>
<div id="conflicts"> <div id="conflicts">
<div v-if="isLoading" class="loading"> <gl-loading-icon v-if="isLoading" size="md" data-testid="loading-spinner" />
<div class="spinner spinner-md"></div>
</div>
<div v-if="hasError" class="nothing-here-block"> <div v-if="hasError" class="nothing-here-block">
{{ conflictsData.errorMessage }} {{ conflictsData.errorMessage }}
</div> </div>

View File

@ -69,9 +69,10 @@ export default {
// If it's a brand new file, we don't want to fetch the content. // If it's a brand new file, we don't want to fetch the content.
// Then when the user commits the first time, the query would run // Then when the user commits the first time, the query would run
// to get the initial file content, but we already have it in `lastCommitedContent` // to get the initial file content, but we already have it in `lastCommitedContent`
// so we skip the loading altogether. // so we skip the loading altogether. We also wait for the currentBranch
skip({ isNewCiConfigFile, lastCommittedContent }) { // to have been fetched
return isNewCiConfigFile || lastCommittedContent; skip() {
return this.shouldSkipBlobContentQuery;
}, },
variables() { variables() {
return { return {
@ -128,8 +129,8 @@ export default {
}, },
ciConfigData: { ciConfigData: {
query: getCiConfigData, query: getCiConfigData,
skip({ currentCiFileContent }) { skip() {
return !currentCiFileContent; return this.shouldSkipCiConfigQuery;
}, },
variables() { variables() {
return { return {
@ -174,6 +175,9 @@ export default {
}, },
commitSha: { commitSha: {
query: getLatestCommitShaQuery, query: getLatestCommitShaQuery,
skip({ currentBranch }) {
return !currentBranch;
},
variables() { variables() {
return { return {
projectPath: this.projectFullPath, projectPath: this.projectFullPath,
@ -181,7 +185,7 @@ export default {
}; };
}, },
update(data) { update(data) {
const latestCommitSha = data.project?.repository?.tree?.lastCommit?.sha; const latestCommitSha = data?.project?.repository?.tree?.lastCommit?.sha;
if (this.isFetchingCommitSha && latestCommitSha === this.commitSha) { if (this.isFetchingCommitSha && latestCommitSha === this.commitSha) {
this.$apollo.queries.commitSha.startPolling(COMMIT_SHA_POLL_INTERVAL); this.$apollo.queries.commitSha.startPolling(COMMIT_SHA_POLL_INTERVAL);
@ -192,6 +196,9 @@ export default {
this.$apollo.queries.commitSha.stopPolling(); this.$apollo.queries.commitSha.stopPolling();
return latestCommitSha; return latestCommitSha;
}, },
error() {
this.reportFailure(LOAD_FAILURE_UNKNOWN);
},
}, },
currentBranch: { currentBranch: {
query: getCurrentBranch, query: getCurrentBranch,
@ -234,6 +241,12 @@ export default {
isEmpty() { isEmpty() {
return this.currentCiFileContent === ''; return this.currentCiFileContent === '';
}, },
shouldSkipBlobContentQuery() {
return this.isNewCiConfigFile || this.lastCommittedContent || !this.currentBranch;
},
shouldSkipCiConfigQuery() {
return !this.currentCiFileContent || !this.commitSha;
},
}, },
i18n: { i18n: {
resetModal: { resetModal: {

View File

@ -5,7 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '../components/runner_header.vue'; import RunnerHeader from '../components/runner_header.vue';
import RunnerUpdateForm from '../components/runner_update_form.vue'; import RunnerUpdateForm from '../components/runner_update_form.vue';
import { I18N_FETCH_ERROR } from '../constants'; import { I18N_FETCH_ERROR } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql'; import runnerQuery from '../graphql/details/runner.query.graphql';
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
export default { export default {
@ -27,7 +27,7 @@ export default {
}, },
apollo: { apollo: {
runner: { runner: {
query: getRunnerQuery, query: runnerQuery,
variables() { variables() {
return { return {
id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId), id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),

View File

@ -8,7 +8,7 @@ import RunnerPauseButton from '../components/runner_pause_button.vue';
import RunnerHeader from '../components/runner_header.vue'; import RunnerHeader from '../components/runner_header.vue';
import RunnerDetails from '../components/runner_details.vue'; import RunnerDetails from '../components/runner_details.vue';
import { I18N_FETCH_ERROR } from '../constants'; import { I18N_FETCH_ERROR } from '../constants';
import getRunnerQuery from '../graphql/get_runner.query.graphql'; import runnerQuery from '../graphql/details/runner.query.graphql';
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
export default { export default {
@ -35,7 +35,7 @@ export default {
}, },
apollo: { apollo: {
runner: { runner: {
query: getRunnerQuery, query: runnerQuery,
variables() { variables() {
return { return {
id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId), id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),

View File

@ -26,8 +26,8 @@ import {
STATUS_STALE, STATUS_STALE,
I18N_FETCH_ERROR, I18N_FETCH_ERROR,
} from '../constants'; } from '../constants';
import getRunnersQuery from '../graphql/get_runners.query.graphql'; import runnersAdminQuery from '../graphql/list/admin_runners.query.graphql';
import getRunnersCountQuery from '../graphql/get_runners_count.query.graphql'; import runnersAdminCountQuery from '../graphql/list/admin_runners_count.query.graphql';
import { import {
fromUrlQueryToSearch, fromUrlQueryToSearch,
fromSearchToUrl, fromSearchToUrl,
@ -36,7 +36,7 @@ import {
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
const runnersCountSmartQuery = { const runnersCountSmartQuery = {
query: getRunnersCountQuery, query: runnersAdminCountQuery,
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
update(data) { update(data) {
return data?.runners?.count; return data?.runners?.count;
@ -77,7 +77,7 @@ export default {
}, },
apollo: { apollo: {
runners: { runners: {
query: getRunnersQuery, query: runnersAdminQuery,
// Runners can be updated by users directly in this list. // Runners can be updated by users directly in this list.
// A "cache and network" policy prevents outdated filtered // A "cache and network" policy prevents outdated filtered
// results. // results.

View File

@ -4,7 +4,7 @@ import { createAlert } from '~/flash';
import { TYPE_GROUP, TYPE_PROJECT } from '~/graphql_shared/constants'; import { TYPE_GROUP, TYPE_PROJECT } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql'; import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../../constants'; import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../../constants';

View File

@ -1,6 +1,6 @@
<script> <script>
import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui'; import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql'; import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';

View File

@ -1,7 +1,7 @@
<script> <script>
import { GlSkeletonLoading } from '@gitlab/ui'; import { GlSkeletonLoading } from '@gitlab/ui';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';
import getRunnerJobsQuery from '../graphql/get_runner_jobs.query.graphql'; import runnerJobsQuery from '../graphql/details/runner_jobs.query.graphql';
import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants'; import { I18N_FETCH_ERROR, I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '../constants';
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
import { getPaginationVariables } from '../utils'; import { getPaginationVariables } from '../utils';
@ -34,7 +34,7 @@ export default {
}, },
apollo: { apollo: {
jobs: { jobs: {
query: getRunnerJobsQuery, query: runnerJobsQuery,
variables() { variables() {
return this.variables; return this.variables;
}, },

View File

@ -1,6 +1,6 @@
<script> <script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql'; import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { I18N_PAUSE, I18N_RESUME } from '../constants'; import { I18N_PAUSE, I18N_RESUME } from '../constants';

View File

@ -2,7 +2,7 @@
import { GlSkeletonLoading } from '@gitlab/ui'; import { GlSkeletonLoading } from '@gitlab/ui';
import { sprintf, formatNumber } from '~/locale'; import { sprintf, formatNumber } from '~/locale';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';
import getRunnerProjectsQuery from '../graphql/get_runner_projects.query.graphql'; import runnerProjectsQuery from '../graphql/details/runner_projects.query.graphql';
import { import {
I18N_ASSIGNED_PROJECTS, I18N_ASSIGNED_PROJECTS,
I18N_NONE, I18N_NONE,
@ -41,7 +41,7 @@ export default {
}, },
apollo: { apollo: {
projects: { projects: {
query: getRunnerProjectsQuery, query: runnerProjectsQuery,
variables() { variables() {
return this.variables; return this.variables;
}, },

View File

@ -15,7 +15,7 @@ import { createAlert, VARIANT_SUCCESS } from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants'; import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants';
import runnerUpdateMutation from '../graphql/runner_update.mutation.graphql'; import runnerUpdateMutation from '../graphql/details/runner_update.mutation.graphql';
export default { export default {
name: 'RunnerUpdateForm', name: 'RunnerUpdateForm',

View File

@ -1,10 +1,9 @@
#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql" #import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql"
query getRunner($id: CiRunnerID!) { query getRunner($id: CiRunnerID!) {
# We have an id in deeply nested fragment # We have an id in deeply nested fragment
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-id-when-available
runner(id: $id) { runner(id: $id) {
__typename
...RunnerDetails ...RunnerDetails
} }
} }

View File

@ -1,4 +1,5 @@
fragment RunnerDetailsShared on CiRunner { fragment RunnerDetailsShared on CiRunner {
__typename
id id
runnerType runnerType
active active
@ -22,7 +23,7 @@ fragment RunnerDetailsShared on CiRunner {
groups { groups {
# Only a single group can be loaded here, while projects # Only a single group can be loaded here, while projects
# are loaded separately using the query with pagination # are loaded separately using the query with pagination
# parameters `get_runner_projects.query.graphql`. # parameters `runner_projects.query.graphql`.
nodes { nodes {
id id
avatarUrl avatarUrl

View File

@ -1,4 +1,4 @@
#import "ee_else_ce/runner/graphql/runner_details.fragment.graphql" #import "ee_else_ce/runner/graphql/details/runner_details.fragment.graphql"
# Mutation for updates from the runner form, loads # Mutation for updates from the runner form, loads
# attributes shown in the runner details. # attributes shown in the runner details.

View File

@ -1,4 +1,4 @@
#import "~/runner/graphql/runner_node.fragment.graphql" #import "~/runner/graphql/list/list_item.fragment.graphql"
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" #import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query getRunners( query getRunners(
@ -24,7 +24,7 @@ query getRunners(
sort: $sort sort: $sort
) { ) {
nodes { nodes {
...RunnerNode ...ListItem
adminUrl adminUrl
editAdminUrl editAdminUrl
} }

View File

@ -1,4 +1,4 @@
#import "~/runner/graphql/runner_node.fragment.graphql" #import "~/runner/graphql/list/list_item.fragment.graphql"
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" #import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query getGroupRunners( query getGroupRunners(
@ -29,8 +29,7 @@ query getGroupRunners(
webUrl webUrl
editUrl editUrl
node { node {
__typename ...ListItem
...RunnerNode
} }
} }
pageInfo { pageInfo {

View File

@ -1,4 +1,4 @@
fragment RunnerNode on CiRunner { fragment ListItem on CiRunner {
__typename __typename
id id
description description

View File

@ -24,8 +24,8 @@ import {
STATUS_OFFLINE, STATUS_OFFLINE,
STATUS_STALE, STATUS_STALE,
} from '../constants'; } from '../constants';
import getGroupRunnersQuery from '../graphql/get_group_runners.query.graphql'; import groupRunnersQuery from '../graphql/list/group_runners.query.graphql';
import getGroupRunnersCountQuery from '../graphql/get_group_runners_count.query.graphql'; import groupRunnersCountQuery from '../graphql/list/group_runners_count.query.graphql';
import { import {
fromUrlQueryToSearch, fromUrlQueryToSearch,
fromSearchToUrl, fromSearchToUrl,
@ -34,7 +34,7 @@ import {
import { captureException } from '../sentry_utils'; import { captureException } from '../sentry_utils';
const runnersCountSmartQuery = { const runnersCountSmartQuery = {
query: getGroupRunnersCountQuery, query: groupRunnersCountQuery,
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK, fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
update(data) { update(data) {
return data?.group?.runners?.count; return data?.group?.runners?.count;
@ -84,7 +84,7 @@ export default {
}, },
apollo: { apollo: {
runners: { runners: {
query: getGroupRunnersQuery, query: groupRunnersQuery,
// Runners can be updated by users directly in this list. // Runners can be updated by users directly in this list.
// A "cache and network" policy prevents outdated filtered // A "cache and network" policy prevents outdated filtered
// results. // results.

View File

@ -3,10 +3,6 @@
class Projects::ClusterAgentsController < Projects::ApplicationController class Projects::ClusterAgentsController < Projects::ApplicationController
before_action :authorize_can_read_cluster_agent! before_action :authorize_can_read_cluster_agent!
before_action do
push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml)
end
feature_category :kubernetes_management feature_category :kubernetes_management
def show def show

View File

@ -3,20 +3,6 @@
class ApplicationExperiment < Gitlab::Experiment class ApplicationExperiment < Gitlab::Experiment
control { nil } # provide a default control for anonymous experiments control { nil } # provide a default control for anonymous experiments
def publish(_result = nil)
super
publish_to_client
end
def publish_to_client
return unless should_track?
Gon.push({ experiment: { name => signature } }, true)
rescue NoMethodError
# means we're not in the request cycle, and can't add to Gon. Log a warning maybe?
end
def publish_to_database def publish_to_database
ActiveSupport::Deprecation.warn('publish_to_database is deprecated and should not be used for reporting anymore') ActiveSupport::Deprecation.warn('publish_to_database is deprecated and should not be used for reporting anymore')

View File

@ -16,4 +16,11 @@ module DeployTokensHelper
Gitlab.config.packages.enabled && Gitlab.config.packages.enabled &&
can?(current_user, :read_package, group_or_project) can?(current_user, :read_package, group_or_project)
end end
def deploy_token_revoke_button_data(token:, group_or_project:)
{
token: token.to_json(only: [:id, :name]),
revoke_path: revoke_deploy_token_path(group_or_project, token)
}
end
end end

View File

@ -111,8 +111,8 @@ module Integrations
end end
def help def help
jira_doc_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_url('integration/jira/index.html') } jira_doc_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('integration/jira/index.html') }
s_("JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}.") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe } s_("JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}") % { jira_doc_link_start: jira_doc_link_start, link_end: '</a>'.html_safe }
end end
def title def title

View File

@ -40,12 +40,7 @@ class Namespace
SQL SQL
Namespace.transaction do Namespace.transaction do
if Feature.enabled?(:for_no_key_update_lock, default_enabled: :yaml) @root.lock!("FOR NO KEY UPDATE")
@root.lock!("FOR NO KEY UPDATE")
else
@root.lock!
end
Namespace.connection.exec_query(sql) Namespace.connection.exec_query(sql)
end end
rescue ActiveRecord::Deadlocked rescue ActiveRecord::Deadlocked

View File

@ -44,22 +44,15 @@ module Namespaces
included do included do
before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? } before_update :lock_both_roots, if: -> { sync_traversal_ids? && parent_id_changed? }
after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? } after_update :sync_traversal_ids, if: -> { sync_traversal_ids? && saved_change_to_parent_id? }
# sync traversal_ids on namespace create, which can happen quite early within a transaction, thus keeping the lock on root namespace record
# for a relatively long time, e.g. creating the project namespace when a project is being created.
after_create :sync_traversal_ids, if: -> { sync_traversal_ids? && !sync_traversal_ids_before_commit? }
# This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed. # This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed.
# This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid # This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid
before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? && sync_traversal_ids_before_commit? } before_commit :sync_traversal_ids, on: [:create], if: -> { sync_traversal_ids? }
end end
def sync_traversal_ids? def sync_traversal_ids?
Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml) Feature.enabled?(:sync_traversal_ids, root_ancestor, default_enabled: :yaml)
end end
def sync_traversal_ids_before_commit?
Feature.enabled?(:sync_traversal_ids_before_commit, root_ancestor, default_enabled: :yaml)
end
def use_traversal_ids? def use_traversal_ids?
return false unless Feature.enabled?(:use_traversal_ids, default_enabled: :yaml) return false unless Feature.enabled?(:use_traversal_ids, default_enabled: :yaml)

View File

@ -22,15 +22,15 @@
= f.label :eks_account_id, _('Account ID'), class: 'label-bold' = f.label :eks_account_id, _('Account ID'), class: 'label-bold'
= f.text_field :eks_account_id, class: 'form-control gl-form-input' = f.text_field :eks_account_id, class: 'form-control gl-form-input'
.form-group .form-group
= f.label :eks_access_key_id, _('Access key ID'), class: 'label-bold' = f.label :eks_access_key_id, _('AWS access key ID (Optional)'), class: 'label-bold'
= f.text_field :eks_access_key_id, class: 'form-control gl-form-input' = f.text_field :eks_access_key_id, class: 'form-control gl-form-input'
.form-text.text-muted .form-text.text-muted
= _('AWS Access Key. Only required if not using role instance credentials') = _('Only required if not using role instance credentials.')
.form-group .form-group
= f.label :eks_secret_access_key, _('Secret access key'), class: 'label-bold' = f.label :eks_secret_access_key, _('AWS secret access key (Optional)'), class: 'label-bold'
= f.password_field :eks_secret_access_key, autocomplete: 'off', class: 'form-control gl-form-input' = f.password_field :eks_secret_access_key, autocomplete: 'off', class: 'form-control gl-form-input'
.form-text.text-muted .form-text.text-muted
= _('AWS Secret Access Key. Only required if not using role instance credentials') = _('Only required if not using role instance credentials.')
= f.submit _('Save changes'), class: "gl-button btn btn-confirm" = f.submit _('Save changes'), class: "gl-button btn btn-confirm"

View File

@ -12,7 +12,7 @@
- link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe - link_end = "#{sprite_icon('external-link', size: 12, css_class: 'ml-1 vertical-align-center')}</a>".html_safe
= s_('SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance\'s code views and merge requests.').html_safe % { link_start: link_start, link_end: link_end } = s_('SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance\'s code views and merge requests.').html_safe % { link_start: link_start, link_end: link_end }
%span %span
= link_to s_('SourcegraphAdmin|More information'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer' = link_to s_('SourcegraphAdmin|Learn more.'), help_page_path('integration/sourcegraph.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content .settings-content
@ -29,10 +29,10 @@
= f.check_box :sourcegraph_public_only, class: 'form-check-input' = f.check_box :sourcegraph_public_only, class: 'form-check-input'
= f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label' = f.label :sourcegraph_public_only, s_('SourcegraphAdmin|Block on private and internal projects'), class: 'form-check-label'
.form-text.text-muted .form-text.text-muted
= s_('SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph.') = s_('SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph.')
.form-group .form-group
= f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold' = f.label :sourcegraph_url, s_('SourcegraphAdmin|Sourcegraph URL'), class: 'label-bold'
= f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|e.g. https://sourcegraph.example.com') = f.text_field :sourcegraph_url, class: 'form-control gl-form-input', placeholder: s_('SourcegraphAdmin|https://sourcegraph.example.com')
.form-text.text-muted .form-text.text-muted
= s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.') = s_('SourcegraphAdmin|Configure the URL to a Sourcegraph instance which can read your GitLab projects.')
= f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-confirm' = f.submit s_('SourcegraphAdmin|Save changes'), class: 'gl-button btn btn-confirm'

View File

@ -15,7 +15,7 @@
.gl-w-half.gl-xs-w-full .gl-w-half.gl-xs-w-full
.gl-display-flex.gl-flex-wrap.gl-justify-content-end.gl-mb-3 .gl-display-flex.gl-flex-wrap.gl-justify-content-end.gl-mb-3
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full', display_text: _('Invite a group') } } .js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full', display_text: _('Invite a group') } }
.js-invite-members-trigger{ data: { variant: 'success', .js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
trigger_source: 'group-members-page', trigger_source: 'group-members-page',
display_text: _('Invite members') } } display_text: _('Invite members') } }

View File

@ -23,7 +23,7 @@
.js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } } .js-invite-group-trigger{ data: { classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', display_text: _('Invite a group') } }
= render 'projects/invite_groups_modal', project: @project = render 'projects/invite_groups_modal', project: @project
- if can_admin_project_member?(@project) - if can_admin_project_member?(@project)
.js-invite-members-trigger{ data: { variant: 'success', .js-invite-members-trigger{ data: { variant: 'confirm',
classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3', classes: 'gl-mt-3 gl-sm-w-auto gl-w-full gl-sm-ml-3',
trigger_source: 'project-members-page', trigger_source: 'project-members-page',
display_text: _('Invite members') } } display_text: _('Invite members') } }

View File

@ -25,7 +25,7 @@
%span.token-never-expires-label= _('Never') %span.token-never-expires-label= _('Never')
%td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected') %td= token.scopes.present? ? token.scopes.join(', ') : _('no scopes selected')
%td %td
.js-deploy-token-revoke-button{ data: { button_class: 'float-right', token: token.to_json, revoke_path: revoke_deploy_token_path(group_or_project, token) } } .js-deploy-token-revoke-button{ data: deploy_token_revoke_button_data(token: token, group_or_project: group_or_project) }
- else - else
.settings-message.text-center .settings-message.text-center

View File

@ -1,8 +0,0 @@
---
name: cluster_vulnerabilities
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73321
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343917
milestone: '14.5'
type: development
group: group::container security
default_enabled: true

View File

@ -1,8 +0,0 @@
---
name: for_no_key_update_lock
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81239
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353619
milestone: '14.9'
type: development
group: group::workspaces
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: sync_traversal_ids_before_commit
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79964
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352499
group: group::workspace
type: development
default_enabled: false
milestone: '14.8'

View File

@ -317,7 +317,7 @@ of tracking an event in Ruby would be:
experiment(:pill_color, actor: current_user).track(:clicked) experiment(:pill_color, actor: current_user).track(:clicked)
``` ```
When you run an experiment with any of the examples so far, an `:assigned` event When you run an experiment with any of the examples so far, an `:assignment` event
is tracked automatically by default. All events that are tracked from an is tracked automatically by default. All events that are tracked from an
experiment have a special experiment have a special
[experiment context](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_experiment/jsonschema/1-0-3) [experiment context](https://gitlab.com/gitlab-org/iglu/-/blob/master/public/schemas/com.gitlab/gitlab_experiment/jsonschema/1-0-3)

View File

@ -50,7 +50,7 @@ To configure your project:
If you enable Jira issues with this setting, all users with access to this GitLab project If you enable Jira issues with this setting, all users with access to this GitLab project
can view all issues from the specified Jira project. can view all issues from the specified Jira project.
1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issues creation from vulnerabilities**. 1. To enable [issue creation for vulnerabilities](../../user/application_security/vulnerabilities/index.md#create-a-jira-issue-for-a-vulnerability), select **Enable Jira issue creation from vulnerabilities**.
1. Select the **Jira issue type**. If the dropdown is empty, select refresh (**{retry}**) and try again. 1. Select the **Jira issue type**. If the dropdown is empty, select refresh (**{retry}**) and try again.
1. To verify the Jira connection is working, select **Test settings**. 1. To verify the Jira connection is working, select **Test settings**.
1. Select **Save changes**. 1. Select **Save changes**.

View File

@ -80,7 +80,7 @@ The issue is then opened so you can take further action.
Prerequisites: Prerequisites:
- [Enable Jira integration](../../../integration/jira/index.md). - [Enable Jira integration](../../../integration/jira/index.md).
The **Enable Jira issues creation from vulnerabilities** option must be selected as part of the configuration. The **Enable Jira issue creation from vulnerabilities** option must be selected as part of the configuration.
- Each user must have a personal Jira user account with permission to create issues in the target project. - Each user must have a personal Jira user account with permission to create issues in the target project.
To create a Jira issue for a vulnerability: To create a Jira issue for a vulnerability:

View File

@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Container vulnerability scanning **(ULTIMATE)** # Container vulnerability scanning **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8 [with a flag](../../../administration/feature_flags.md) named `cluster_vulnerabilities`. Enabled by default. > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md). To view cluster vulnerabilities, you can view the [vulnerability report](../../application_security/vulnerabilities/index.md).
You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab. You can also configure your agent so the vulnerabilities are displayed with other agent information in GitLab.

View File

@ -67,15 +67,15 @@ Features not found in standard Markdown:
Features [extended from standard Markdown](#features-extended-from-standard-markdown): Features [extended from standard Markdown](#features-extended-from-standard-markdown):
| Standard Markdown | Extended Markdown in GitLab | | Standard Markdown | Extended Markdown in GitLab |
| ------------------------------------- | ------------------------- | |---------------------------------------|---------------------------------------------------------------------------------------|
| [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) | | [blockquotes](#blockquotes) | [multi-line blockquotes](#multiline-blockquote) |
| [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) | | [code blocks](#code-spans-and-blocks) | [colored code and syntax highlighting](#colored-code-and-syntax-highlighting) |
| [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) | [emphasis](#emphasis) | [multiple underscores in words](#multiple-underscores-in-words-and-mid-word-emphasis) |
| [headers](#headers) | [linkable Header IDs](#header-ids-and-links) | | [headers](#headers) | [linkable Header IDs](#header-ids-and-links) |
| [images](#images) | [embedded videos](#videos) and [audio](#audio) | | [images](#images) | [embedded videos](#videos) and [audio](#audio) |
| [line breaks](#line-breaks) | [more line break control](#newlines) | | [line breaks](#line-breaks) | [more line break control](#newlines) |
| [links](#links) | [automatically linking URLs](#url-auto-linking) | | [links](#links) | [automatically linking URLs](#url-auto-linking) |
## Features not found in standard Markdown ## Features not found in standard Markdown
@ -262,7 +262,7 @@ The following delimiters are supported:
--- ---
title: About Front Matter title: About Front Matter
example: example:
language: yaml language: yaml
--- ---
``` ```
@ -515,31 +515,31 @@ version to reference other projects from the same namespace.
GitLab Flavored Markdown recognizes the following: GitLab Flavored Markdown recognizes the following:
| references | input | cross-project reference | shortcut inside same namespace | | references | input | cross-project reference | shortcut inside same namespace |
| :--------------------------------------------------- | :---------------------------- | :----------------------------------------- | :------------------------------- | |:----------------------------------------------------------------------------|:------------------------------|:----------------------------------------|:-------------------------------|
| specific user | `@user_name` | | | | specific user | `@user_name` | | |
| specific group | `@group_name` | | | | specific group | `@group_name` | | |
| entire team | `@all` | | | | entire team | `@all` | | |
| project | `namespace/project>` | | | | project | `namespace/project>` | | |
| issue | ``#123`` | `namespace/project#123` | `project#123` | | issue | ``#123`` | `namespace/project#123` | `project#123` |
| merge request | `!123` | `namespace/project!123` | `project!123` | | merge request | `!123` | `namespace/project!123` | `project!123` |
| snippet | `$123` | `namespace/project$123` | `project$123` | | snippet | `$123` | `namespace/project$123` | `project$123` |
| [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | | | [epic](group/epics/index.md) | `&123` | `group1/subgroup&123` | |
| vulnerability **(ULTIMATE)** <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` | | [vulnerability](application_security/vulnerabilities/index.md) <sup>1</sup> | `[vulnerability:123]` | `[vulnerability:namespace/project/123]` | `[vulnerability:project/123]` |
| feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` | | feature flag | `[feature_flag:123]` | `[feature_flag:namespace/project/123]` | `[feature_flag:project/123]` |
| label by ID | `~123` | `namespace/project~123` | `project~123` | | label by ID | `~123` | `namespace/project~123` | `project~123` |
| one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` | | one-word label by name | `~bug` | `namespace/project~bug` | `project~bug` |
| multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` | | multi-word label by name | `~"feature request"` | `namespace/project~"feature request"` | `project~"feature request"` |
| scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` | | scoped label by name | `~"priority::high"` | `namespace/project~"priority::high"` | `project~"priority::high"` |
| project milestone by ID | `%123` | `namespace/project%123` | `project%123` | | project milestone by ID | `%123` | `namespace/project%123` | `project%123` |
| one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` | | one-word milestone by name | `%v1.23` | `namespace/project%v1.23` | `project%v1.23` |
| multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` | | multi-word milestone by name | `%"release candidate"` | `namespace/project%"release candidate"` | `project%"release candidate"` |
| specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` | | specific commit | `9ba12248` | `namespace/project@9ba12248` | `project@9ba12248` |
| commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` | | commit range comparison | `9ba12248...b19a04f5` | `namespace/project@9ba12248...b19a04f5` | `project@9ba12248...b19a04f5` |
| repository file references | `[README](doc/README.md)` | | | | repository file references | `[README](doc/README.md)` | | |
| repository file line references | `[README](doc/README.md#L13)` | | | | repository file line references | `[README](doc/README.md#L13)` | | |
| [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` | | [alert](../operations/incident_management/alerts.md) | `^alert#123` | `namespace/project^alert#123` | `project^alert#123` |
| contact | `[contact:test@example.com]` | | | | contact | `[contact:test@example.com]` | | |
1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222483) in GitLab 13.7. 1. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222483) in GitLab 13.7.
@ -1486,7 +1486,7 @@ but they do not render properly on `docs.gitlab.com`:
#### Copy from spreadsheet and paste in Markdown #### Copy from spreadsheet and paste in Markdown
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7.
If you're working in spreadsheet software (for example, Microsoft Excel, Google If you're working in spreadsheet software (for example, Microsoft Excel, Google
Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy-and-paste Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy-and-paste

View File

@ -34,7 +34,8 @@ in the search field in the upper right corner:
> - Filtering by epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in GitLab 12.9. > - Filtering by epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/195704) in GitLab 12.9.
> - Filtering by child epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in GitLab 13.0. > - Filtering by child epics was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9029) in GitLab 13.0.
> - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6. Moved from GitLab Ultimate to Premium in 13.9. > - Filtering by iterations was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6.
> - Filtering by iterations was moved from GitLab Ultimate to GitLab Premium in 13.9.
> - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default. > - Filtering by type was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322755) in GitLab 13.10 [with a flag](../../administration/feature_flags.md) named `vue_issues_list`. Disabled by default.
Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and Follow these steps to filter the **Issues** and **Merge requests** list pages in projects and
@ -127,14 +128,14 @@ the dropdown list) **Approved-By** and select the user.
![Filter MRs by approved by](img/filter_approved_by_merge_requests_v14_6.png) ![Filter MRs by approved by](img/filter_approved_by_merge_requests_v14_6.png)
### Filtering merge requests by reviewer **(FREE)** ### Filtering merge requests by reviewer
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47605) in GitLab 13.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47605) in GitLab 13.7.
To filter review requested merge requests for a specific individual, you can type (or select from To filter review requested merge requests for a specific individual, you can type (or select from
the dropdown list) **Reviewer** and select the user. the dropdown list) **Reviewer** and select the user.
### Filtering merge requests by environment or deployment date **(FREE)** ### Filtering merge requests by environment or deployment date
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44041) in GitLab 13.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44041) in GitLab 13.6.
@ -307,7 +308,7 @@ GitLab instance.
## Search settings ## Search settings
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292941) in GitLab 13.8 [with a flag](../../administration/feature_flags.md) named `search_settings_in_page`. Disabled by default. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292941) in GitLab 13.8 [with a flag](../../administration/feature_flags.md) named `search_settings_in_page`. Disabled by default.
> - [Added to Group, Administrator, and User settings](https://gitlab.com/groups/gitlab-org/-/epics/4842) in GitLab 13.9. > - [Added](https://gitlab.com/groups/gitlab-org/-/epics/4842) to Group, Administrator, and User settings in GitLab 13.9.
> - [Feature flag `search_settings_in_page` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11. > - [Feature flag `search_settings_in_page` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11. > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/294025) in GitLab 13.11.

View File

@ -37,8 +37,7 @@ You can create snippets in multiple ways, depending on whether you want to creat
**New snippet**, or: **New snippet**, or:
- *If you're on a project's page,* select the plus icon (**{plus-square-o}**) - *If you're on a project's page,* select the plus icon (**{plus-square-o}**)
in the top navigation bar, and then select **New snippet** from the in the top navigation bar, and then select **New snippet** from the
**GitLab** (GitLab SaaS) or **Your Instance** (self-managed) section **GitLab** section of the same dropdown list.
of the same dropdown list.
- *For all other pages,* select the plus icon (**{plus-square-o}**) - *For all other pages,* select the plus icon (**{plus-square-o}**)
in the top navigation bar, then select **New snippet** from the dropdown list. in the top navigation bar, then select **New snippet** from the dropdown list.
- If you installed the [GitLab Workflow VS Code extension](project/repository/vscode.md), - If you installed the [GitLab Workflow VS Code extension](project/repository/vscode.md),
@ -153,8 +152,7 @@ To delete a file from your snippet through the GitLab UI:
1. Go to your snippet in the GitLab UI. 1. Go to your snippet in the GitLab UI.
1. Select **Edit** in the top right corner. 1. Select **Edit** in the top right corner.
1. Select **Delete file** alongside the filename of each file 1. Select **Delete file** alongside the filename of each file you wish to delete.
you wish to delete.
1. Select **Save changes**. 1. Select **Save changes**.
## Clone snippets ## Clone snippets

View File

@ -1773,13 +1773,13 @@ msgstr ""
msgid "AWS Access Key" msgid "AWS Access Key"
msgstr "" msgstr ""
msgid "AWS Access Key. Only required if not using role instance credentials"
msgstr ""
msgid "AWS Secret Access Key" msgid "AWS Secret Access Key"
msgstr "" msgstr ""
msgid "AWS Secret Access Key. Only required if not using role instance credentials" msgid "AWS access key ID (Optional)"
msgstr ""
msgid "AWS secret access key (Optional)"
msgstr "" msgstr ""
msgid "AWS service error: %{error}" msgid "AWS service error: %{error}"
@ -1839,9 +1839,6 @@ msgstr ""
msgid "Access granted" msgid "Access granted"
msgstr "" msgstr ""
msgid "Access key ID"
msgstr ""
msgid "Access requests" msgid "Access requests"
msgstr "" msgstr ""
@ -19864,9 +19861,6 @@ msgstr ""
msgid "Integrations|Use default settings" msgid "Integrations|Use default settings"
msgstr "" msgstr ""
msgid "Integrations|When you mention a Jira issue in a commit or merge request, GitLab creates a remote link and comment (if enabled)."
msgstr ""
msgid "Integrations|You can now close this window and return to the GitLab for Jira application." msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
msgstr "" msgstr ""
@ -20830,15 +20824,15 @@ msgstr ""
msgid "JiraService|Define the type of Jira issue to create from a vulnerability." msgid "JiraService|Define the type of Jira issue to create from a vulnerability."
msgstr "" msgstr ""
msgid "JiraService|Displaying Jira issues while leaving the GitLab issue functionality enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used." msgid "JiraService|Displaying Jira issues while leaving GitLab issues also enabled might be confusing. Consider %{linkStart}disabling GitLab issues%{linkEnd} if they wont otherwise be used."
msgstr ""
msgid "JiraService|Enable Jira issue creation from vulnerabilities"
msgstr "" msgstr ""
msgid "JiraService|Enable Jira issues" msgid "JiraService|Enable Jira issues"
msgstr "" msgstr ""
msgid "JiraService|Enable Jira issues creation from vulnerabilities"
msgstr ""
msgid "JiraService|Enable Jira transitions" msgid "JiraService|Enable Jira transitions"
msgstr "" msgstr ""
@ -20953,16 +20947,19 @@ msgstr ""
msgid "JiraService|View Jira issues in GitLab" msgid "JiraService|View Jira issues in GitLab"
msgstr "" msgstr ""
msgid "JiraService|Warning: All GitLab users that have access to this GitLab project are able to view all issues from the Jira project specified below." msgid "JiraService|Warning: All GitLab users with access to this GitLab project can view all issues from the Jira project you select."
msgstr "" msgstr ""
msgid "JiraService|Web URL" msgid "JiraService|Web URL"
msgstr "" msgstr ""
msgid "JiraService|Work on Jira issues without leaving GitLab. Adds a Jira menu to access your list of Jira issues and view any issue as read-only." msgid "JiraService|When a Jira issue is mentioned in a commit or merge request, a remote link and comment (if enabled) will be created."
msgstr "" msgstr ""
msgid "JiraService|You must configure Jira before enabling this integration. For more details, read the %{jira_doc_link_start}Jira integration documentation%{link_end}." msgid "JiraService|Work on Jira issues without leaving GitLab. Add a Jira menu to access a read-only list of your Jira issues."
msgstr ""
msgid "JiraService|You must configure Jira before enabling this integration. %{jira_doc_link_start}Learn more.%{link_end}"
msgstr "" msgstr ""
msgid "Job" msgid "Job"
@ -25611,6 +25608,9 @@ msgstr ""
msgid "Only reCAPTCHA v2 is supported:" msgid "Only reCAPTCHA v2 is supported:"
msgstr "" msgstr ""
msgid "Only required if not using role instance credentials."
msgstr ""
msgid "Only use lowercase letters, numbers, and underscores." msgid "Only use lowercase letters, numbers, and underscores."
msgstr "" msgstr ""
@ -32290,9 +32290,6 @@ msgstr ""
msgid "Secret Detection" msgid "Secret Detection"
msgstr "" msgstr ""
msgid "Secret access key"
msgstr ""
msgid "Secret token" msgid "Secret token"
msgstr "" msgstr ""
@ -32491,9 +32488,6 @@ msgstr ""
msgid "SecurityOrchestration|.yaml preview" msgid "SecurityOrchestration|.yaml preview"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Action"
msgstr ""
msgid "SecurityOrchestration|Actions" msgid "SecurityOrchestration|Actions"
msgstr "" msgstr ""
@ -32536,9 +32530,6 @@ msgstr ""
msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}" msgid "SecurityOrchestration|Enforce security for this project. %{linkStart}More information.%{linkEnd}"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Executes a %{scanType} scan"
msgstr ""
msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}." msgid "SecurityOrchestration|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}."
msgstr "" msgstr ""
@ -32554,6 +32545,9 @@ msgstr ""
msgid "SecurityOrchestration|New policy" msgid "SecurityOrchestration|New policy"
msgstr "" msgstr ""
msgid "SecurityOrchestration|No actions defined - policy will not run."
msgstr ""
msgid "SecurityOrchestration|No description" msgid "SecurityOrchestration|No description"
msgstr "" msgstr ""
@ -32596,10 +32590,13 @@ msgstr ""
msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:" msgid "SecurityOrchestration|Require %{approvals} %{plural} from %{approvers} if any of the following occur:"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Rule" msgid "SecurityOrchestration|Rules"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Rules" msgid "SecurityOrchestration|Runs %{actions} and %{lastAction} scans"
msgstr ""
msgid "SecurityOrchestration|Runs a %{action} scan"
msgstr "" msgstr ""
msgid "SecurityOrchestration|Scan Execution" msgid "SecurityOrchestration|Scan Execution"
@ -34688,10 +34685,10 @@ msgstr ""
msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance's code views and merge requests." msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourcegraph%{link_end} on your GitLab instance's code views and merge requests."
msgstr "" msgstr ""
msgid "SourcegraphAdmin|If checked, only public projects will have code intelligence and communicate with Sourcegraph." msgid "SourcegraphAdmin|Learn more."
msgstr "" msgstr ""
msgid "SourcegraphAdmin|More information" msgid "SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph."
msgstr "" msgstr ""
msgid "SourcegraphAdmin|Save changes" msgid "SourcegraphAdmin|Save changes"
@ -34700,7 +34697,7 @@ msgstr ""
msgid "SourcegraphAdmin|Sourcegraph URL" msgid "SourcegraphAdmin|Sourcegraph URL"
msgstr "" msgstr ""
msgid "SourcegraphAdmin|e.g. https://sourcegraph.example.com" msgid "SourcegraphAdmin|https://sourcegraph.example.com"
msgstr "" msgstr ""
msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects." msgid "SourcegraphPreferences|This feature is experimental and currently limited to certain projects."

38
scripts/ingest-reports-to-siem Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env node
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3')
const { fromIni } = require('@aws-sdk/credential-provider-ini')
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
function getMD5HashFromFile(data) {
const hash = crypto.createHash('md5').update(data).digest('base64')
return hash
}
(async function () {
const s3Client = new S3Client({
region: 'us-east-2',
credentials: fromIni({ profile: 'gl-logs-for-panther' }),
})
try {
const file = 'gl-dependency-scanning-report.json'
const data = fs.readFileSync(file)
const responseData = await s3Client.send(
new PutObjectCommand({
Bucket: 'gl-logs-for-panther-test',
Key: path.join('package_hunter_test', path.basename(file)),
Body: data,
ContentMD5: getMD5HashFromFile(data),
}),
)
console.log('Successfully uploaded %s', file)
} catch (err) {
if (err.name === 'CredentialsProviderError' || err.name === 'AuthorizationHeaderMalformed')
console.log('Could not upload the report. Are AWS credentials configured in ~/.aws/credentials?')
else
console.log('Unexpected error during upload.')
process.exit(1)
}
})()

View File

@ -28,63 +28,25 @@ RSpec.describe ApplicationExperiment, :experiment do
end end
describe "#publish" do describe "#publish" do
let(:should_track) { true }
before do
allow(application_experiment).to receive(:should_track?).and_return(should_track)
end
it "tracks the assignment", :snowplow do it "tracks the assignment", :snowplow do
expect(application_experiment).to receive(:track).with(:assignment)
application_experiment.publish
end
it "adds to the published experiments" do
# These are surfaced in the client layer by rendering them in the
# _published_experiments.html.haml partial.
application_experiment.publish application_experiment.publish
expect_snowplow_event( expect(ApplicationExperiment.published_experiments['namespaced/stub']).to include(
category: 'namespaced/stub', experiment: 'namespaced/stub',
action: 'assignment', excluded: false,
context: [{ schema: anything, data: anything }] key: anything,
variant: 'control'
) )
end end
it "publishes to the client" do
expect(application_experiment).to receive(:publish_to_client)
application_experiment.publish
end
context 'when we should not track' do
let(:should_track) { false }
it 'does not track an event to Snowplow', :snowplow do
application_experiment.publish
expect_no_snowplow_event
end
end
describe "#publish_to_client" do
it "adds the data into Gon" do
signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' }
expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => hash_including(signature) } }, true)
application_experiment.publish_to_client
end
it "handles when Gon raises exceptions (like when it can't be pushed into)" do
expect(Gon).to receive(:push).and_raise(NoMethodError)
expect { application_experiment.publish_to_client }.not_to raise_error
end
context 'when we should not track' do
let(:should_track) { false }
it 'returns early' do
expect(Gon).not_to receive(:push)
application_experiment.publish_to_client
end
end
end
describe '#publish_to_database' do describe '#publish_to_database' do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax

View File

@ -40,10 +40,8 @@ RSpec.describe "User views incident" do
visit(project_issues_incident_path(project, incident)) visit(project_issues_incident_path(project, incident))
end end
it 'does not show the incident action', :js, :aggregate_failures do it 'does not show the incident actions', :js, :aggregate_failures do
click_button 'Incident actions' expect(page).not_to have_button('Incident actions')
expect(page).not_to have_link('New incident')
end end
end end
end end

View File

@ -70,11 +70,6 @@ describe('RevokeButton', () => {
expect(findRevokeButton().exists()).toBe(true); expect(findRevokeButton().exists()).toBe(true);
}); });
it('passes the buttonClass to the button', () => {
wrapper = createComponent({ buttonClass: 'my-revoke-button' });
expect(findRevokeButton().classes()).toContain('my-revoke-button');
});
it('opens the modal', () => { it('opens the modal', () => {
findRevokeButton().trigger('click'); findRevokeButton().trigger('click');
expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.modalId); expect(glModalDirective).toHaveBeenCalledWith(wrapper.vm.modalId);

View File

@ -33,19 +33,19 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_runners_query_name = 'get_runners.query.graphql' admin_runners_query = 'list/admin_runners.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runners_query_name}") get_graphql_query_as_string("#{query_path}#{admin_runners_query}")
end end
it "#{fixtures_path}#{get_runners_query_name}.json" do it "#{fixtures_path}#{admin_runners_query}.json" do
post_graphql(query, current_user: admin, variables: {}) post_graphql(query, current_user: admin, variables: {})
expect_graphql_errors_to_be_empty expect_graphql_errors_to_be_empty
end end
it "#{fixtures_path}#{get_runners_query_name}.paginated.json" do it "#{fixtures_path}#{admin_runners_query}.paginated.json" do
post_graphql(query, current_user: admin, variables: { first: 2 }) post_graphql(query, current_user: admin, variables: { first: 2 })
expect_graphql_errors_to_be_empty expect_graphql_errors_to_be_empty
@ -53,13 +53,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_runners_count_query_name = 'get_runners_count.query.graphql' admin_runners_count_query = 'list/admin_runners_count.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runners_count_query_name}") get_graphql_query_as_string("#{query_path}#{admin_runners_count_query}")
end end
it "#{fixtures_path}#{get_runners_count_query_name}.json" do it "#{fixtures_path}#{admin_runners_count_query}.json" do
post_graphql(query, current_user: admin, variables: {}) post_graphql(query, current_user: admin, variables: {})
expect_graphql_errors_to_be_empty expect_graphql_errors_to_be_empty
@ -67,13 +67,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_runner_query_name = 'get_runner.query.graphql' runner_query = 'details/runner.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_query_name}") get_graphql_query_as_string("#{query_path}#{runner_query}")
end end
it "#{fixtures_path}#{get_runner_query_name}.json" do it "#{fixtures_path}#{runner_query}.json" do
post_graphql(query, current_user: admin, variables: { post_graphql(query, current_user: admin, variables: {
id: instance_runner.to_global_id.to_s id: instance_runner.to_global_id.to_s
}) })
@ -81,7 +81,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty expect_graphql_errors_to_be_empty
end end
it "#{fixtures_path}#{get_runner_query_name}.with_group.json" do it "#{fixtures_path}#{runner_query}.with_group.json" do
post_graphql(query, current_user: admin, variables: { post_graphql(query, current_user: admin, variables: {
id: group_runner.to_global_id.to_s id: group_runner.to_global_id.to_s
}) })
@ -91,13 +91,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_runner_projects_query_name = 'get_runner_projects.query.graphql' runner_projects_query = 'details/runner_projects.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_projects_query_name}") get_graphql_query_as_string("#{query_path}#{runner_projects_query}")
end end
it "#{fixtures_path}#{get_runner_projects_query_name}.json" do it "#{fixtures_path}#{runner_projects_query}.json" do
post_graphql(query, current_user: admin, variables: { post_graphql(query, current_user: admin, variables: {
id: project_runner.to_global_id.to_s id: project_runner.to_global_id.to_s
}) })
@ -107,13 +107,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_runner_jobs_query_name = 'get_runner_jobs.query.graphql' runner_jobs_query = 'details/runner_jobs.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_runner_jobs_query_name}") get_graphql_query_as_string("#{query_path}#{runner_jobs_query}")
end end
it "#{fixtures_path}#{get_runner_jobs_query_name}.json" do it "#{fixtures_path}#{runner_jobs_query}.json" do
post_graphql(query, current_user: admin, variables: { post_graphql(query, current_user: admin, variables: {
id: instance_runner.to_global_id.to_s id: instance_runner.to_global_id.to_s
}) })
@ -131,13 +131,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_group_runners_query_name = 'get_group_runners.query.graphql' group_runners_query = 'list/group_runners.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_group_runners_query_name}") get_graphql_query_as_string("#{query_path}#{group_runners_query}")
end end
it "#{fixtures_path}#{get_group_runners_query_name}.json" do it "#{fixtures_path}#{group_runners_query}.json" do
post_graphql(query, current_user: group_owner, variables: { post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path groupFullPath: group.full_path
}) })
@ -145,7 +145,7 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
expect_graphql_errors_to_be_empty expect_graphql_errors_to_be_empty
end end
it "#{fixtures_path}#{get_group_runners_query_name}.paginated.json" do it "#{fixtures_path}#{group_runners_query}.paginated.json" do
post_graphql(query, current_user: group_owner, variables: { post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path, groupFullPath: group.full_path,
first: 1 first: 1
@ -156,13 +156,13 @@ RSpec.describe 'Runner (JavaScript fixtures)' do
end end
describe GraphQL::Query, type: :request do describe GraphQL::Query, type: :request do
get_group_runners_count_query_name = 'get_group_runners_count.query.graphql' group_runners_count_query = 'list/group_runners_count.query.graphql'
let_it_be(:query) do let_it_be(:query) do
get_graphql_query_as_string("#{query_path}#{get_group_runners_count_query_name}") get_graphql_query_as_string("#{query_path}#{group_runners_count_query}")
end end
it "#{fixtures_path}#{get_group_runners_count_query_name}.json" do it "#{fixtures_path}#{group_runners_count_query}.json" do
post_graphql(query, current_user: group_owner, variables: { post_graphql(query, current_user: group_owner, variables: {
groupFullPath: group.full_path groupFullPath: group.full_path
}) })

View File

@ -1,5 +1,5 @@
import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import Vue, { nextTick } from 'vue'; import Vue, { nextTick } from 'vue';
import { GlButton, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { mockTracking } from 'helpers/tracking_helper'; import { mockTracking } from 'helpers/tracking_helper';
@ -65,12 +65,17 @@ describe('HeaderActions component', () => {
}, },
}; };
const findToggleIssueStateButton = () => wrapper.findComponent(GlButton); const findToggleIssueStateButton = () => wrapper.find(GlButton);
const findDropdownAt = (index) => wrapper.findAllComponents(GlDropdown).at(index);
const findMobileDropdownItems = () => findDropdownAt(0).findAllComponents(GlDropdownItem); const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`);
const findDesktopDropdownItems = () => findDropdownAt(1).findAllComponents(GlDropdownItem); const findMobileDropdown = () => findDropdownBy('mobile-dropdown');
const findModal = () => wrapper.findComponent(GlModal); const findDesktopDropdown = () => findDropdownBy('desktop-dropdown');
const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index); const findMobileDropdownItems = () => findMobileDropdown().findAll(GlDropdownItem);
const findDesktopDropdownItems = () => findDesktopDropdown().findAll(GlDropdownItem);
const findModal = () => wrapper.find(GlModal);
const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index);
const mountComponent = ({ const mountComponent = ({
props = {}, props = {},
@ -161,10 +166,10 @@ describe('HeaderActions component', () => {
}); });
describe.each` describe.each`
description | isCloseIssueItemVisible | findDropdownItems description | isCloseIssueItemVisible | findDropdownItems | findDropdown
${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} ${'mobile dropdown'} | ${true} | ${findMobileDropdownItems} | ${findMobileDropdown}
${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} ${'desktop dropdown'} | ${false} | ${findDesktopDropdownItems} | ${findDesktopDropdown}
`('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => { `('$description', ({ isCloseIssueItemVisible, findDropdownItems, findDropdown }) => {
describe.each` describe.each`
description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam | canPromoteToEpic | canDestroyIssue
${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true} ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} | ${true} | ${true}
@ -214,6 +219,24 @@ describe('HeaderActions component', () => {
}); });
}, },
); );
describe(`when user can update but not create ${issueType}`, () => {
beforeEach(() => {
wrapper = mountComponent({
props: {
canUpdateIssue: true,
canCreateIssue: false,
isIssueAuthor: true,
issueType,
canReportSpam: false,
canPromoteToEpic: false,
},
});
});
it(`${isCloseIssueItemVisible ? 'shows' : 'hides'} the dropdown button`, () => {
expect(findDropdown().exists()).toBe(isCloseIssueItemVisible);
});
});
}); });
}); });

View File

@ -1,7 +1,7 @@
import { GlSprintf } from '@gitlab/ui'; import { GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue'; import Vue, { nextTick } from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
import InlineConflictLines from '~/merge_conflicts/components/inline_conflict_lines.vue'; import InlineConflictLines from '~/merge_conflicts/components/inline_conflict_lines.vue';
import ParallelConflictLines from '~/merge_conflicts/components/parallel_conflict_lines.vue'; import ParallelConflictLines from '~/merge_conflicts/components/parallel_conflict_lines.vue';
import component from '~/merge_conflicts/merge_conflict_resolver_app.vue'; import component from '~/merge_conflicts/merge_conflict_resolver_app.vue';
@ -18,7 +18,7 @@ describe('Merge Conflict Resolver App', () => {
const decoratedMockFiles = decorateFiles(conflictsMock.files); const decoratedMockFiles = decorateFiles(conflictsMock.files);
const mountComponent = () => { const mountComponent = () => {
wrapper = shallowMount(component, { wrapper = shallowMountExtended(component, {
store, store,
stubs: { GlSprintf }, stubs: { GlSprintf },
provide() { provide() {
@ -41,15 +41,17 @@ describe('Merge Conflict Resolver App', () => {
wrapper.destroy(); wrapper.destroy();
}); });
const findConflictsCount = () => wrapper.find('[data-testid="conflicts-count"]'); const findLoadingSpinner = () => wrapper.findByTestId('loading-spinner');
const findFiles = () => wrapper.findAll('[data-testid="files"]'); const findConflictsCount = () => wrapper.findByTestId('conflicts-count');
const findFileHeader = (w = wrapper) => w.find('[data-testid="file-name"]'); const findFiles = () => wrapper.findAllByTestId('files');
const findFileInteractiveButton = (w = wrapper) => w.find('[data-testid="interactive-button"]'); const findFileHeader = (w = wrapper) => extendedWrapper(w).findByTestId('file-name');
const findFileInlineButton = (w = wrapper) => w.find('[data-testid="inline-button"]'); const findFileInteractiveButton = (w = wrapper) =>
const findSideBySideButton = () => wrapper.find('[data-testid="side-by-side"]'); extendedWrapper(w).findByTestId('interactive-button');
const findFileInlineButton = (w = wrapper) => extendedWrapper(w).findByTestId('inline-button');
const findSideBySideButton = () => wrapper.findByTestId('side-by-side');
const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines); const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines);
const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines); const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines);
const findCommitMessageTextarea = () => wrapper.find('[data-testid="commit-message"]'); const findCommitMessageTextarea = () => wrapper.findByTestId('commit-message');
it('shows the amount of conflicts', () => { it('shows the amount of conflicts', () => {
mountComponent(); mountComponent();
@ -60,6 +62,19 @@ describe('Merge Conflict Resolver App', () => {
expect(title.text().trim()).toBe('Showing 3 conflicts between test-conflicts and main'); expect(title.text().trim()).toBe('Showing 3 conflicts between test-conflicts and main');
}); });
it('shows a loading spinner while loading', () => {
store.commit('SET_LOADING_STATE', true);
mountComponent();
expect(findLoadingSpinner().exists()).toBe(true);
});
it('does not show a loading spinner once loaded', () => {
mountComponent();
expect(findLoadingSpinner().exists()).toBe(false);
});
describe('files', () => { describe('files', () => {
it('shows one file area for each file', () => { it('shows one file area for each file', () => {
mountComponent(); mountComponent();

View File

@ -18,12 +18,15 @@ import {
COMMIT_SUCCESS, COMMIT_SUCCESS,
COMMIT_SUCCESS_WITH_REDIRECT, COMMIT_SUCCESS_WITH_REDIRECT,
COMMIT_FAILURE, COMMIT_FAILURE,
EDITOR_APP_STATUS_LOADING,
} from '~/pipeline_editor/constants'; } from '~/pipeline_editor/constants';
import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql'; import getBlobContent from '~/pipeline_editor/graphql/queries/blob_content.query.graphql';
import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql'; import getCiConfigData from '~/pipeline_editor/graphql/queries/ci_config.query.graphql';
import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql'; import getTemplate from '~/pipeline_editor/graphql/queries/get_starter_template.query.graphql';
import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql'; import getLatestCommitShaQuery from '~/pipeline_editor/graphql/queries/latest_commit_sha.query.graphql';
import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql'; import getPipelineQuery from '~/pipeline_editor/graphql/queries/pipeline.query.graphql';
import getCurrentBranch from '~/pipeline_editor/graphql/queries/client/current_branch.query.graphql';
import getAppStatus from '~/pipeline_editor/graphql/queries/client/app_status.query.graphql';
import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue'; import PipelineEditorApp from '~/pipeline_editor/pipeline_editor_app.vue';
import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue'; import PipelineEditorHome from '~/pipeline_editor/pipeline_editor_home.vue';
@ -84,9 +87,6 @@ describe('Pipeline editor app component', () => {
initialCiFileContent: { initialCiFileContent: {
loading: blobLoading, loading: blobLoading,
}, },
ciConfigData: {
loading: false,
},
}, },
}, },
}, },
@ -94,7 +94,11 @@ describe('Pipeline editor app component', () => {
}); });
}; };
const createComponentWithApollo = async ({ provide = {}, stubs = {} } = {}) => { const createComponentWithApollo = async ({
provide = {},
stubs = {},
withUndefinedBranch = false,
} = {}) => {
const handlers = [ const handlers = [
[getBlobContent, mockBlobContentData], [getBlobContent, mockBlobContentData],
[getCiConfigData, mockCiConfigData], [getCiConfigData, mockCiConfigData],
@ -105,6 +109,31 @@ describe('Pipeline editor app component', () => {
mockApollo = createMockApollo(handlers, resolvers); mockApollo = createMockApollo(handlers, resolvers);
if (!withUndefinedBranch) {
mockApollo.clients.defaultClient.cache.writeQuery({
query: getCurrentBranch,
data: {
workBranches: {
__typename: 'BranchList',
current: {
__typename: 'WorkBranch',
name: mockDefaultBranch,
},
},
},
});
}
mockApollo.clients.defaultClient.cache.writeQuery({
query: getAppStatus,
data: {
app: {
__typename: 'AppData',
status: EDITOR_APP_STATUS_LOADING,
},
},
});
const options = { const options = {
localVue, localVue,
mocks: {}, mocks: {},
@ -145,6 +174,55 @@ describe('Pipeline editor app component', () => {
}); });
}); });
describe('skipping queries', () => {
describe('when branchName is undefined', () => {
beforeEach(async () => {
await createComponentWithApollo({ withUndefinedBranch: true });
});
it('does not calls getBlobContent', () => {
expect(mockBlobContentData).not.toHaveBeenCalled();
});
});
describe('when branchName is defined', () => {
beforeEach(async () => {
await createComponentWithApollo();
});
it('calls getBlobContent', () => {
expect(mockBlobContentData).toHaveBeenCalled();
});
});
describe('when commit sha is undefined', () => {
beforeEach(async () => {
mockLatestCommitShaQuery.mockResolvedValue(undefined);
await createComponentWithApollo();
});
it('calls getBlobContent', () => {
expect(mockBlobContentData).toHaveBeenCalled();
});
it('does not call ciConfigData', () => {
expect(mockCiConfigData).not.toHaveBeenCalled();
});
});
describe('when commit sha is defined', () => {
beforeEach(async () => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);
mockLatestCommitShaQuery.mockResolvedValue(mockCommitShaResults);
await createComponentWithApollo();
});
it('calls ciConfigData', () => {
expect(mockCiConfigData).toHaveBeenCalled();
});
});
});
describe('when queries are called', () => { describe('when queries are called', () => {
beforeEach(() => { beforeEach(() => {
mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse); mockBlobContentData.mockResolvedValue(mockBlobContentQueryResponse);

View File

@ -7,7 +7,7 @@ import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue'; import RunnerHeader from '~/runner/components/runner_header.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql'; import runnerQuery from '~/runner/graphql/details/runner.query.graphql';
import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue'; import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
@ -29,7 +29,7 @@ describe('AdminRunnerEditApp', () => {
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => { const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(AdminRunnerEditApp, { wrapper = mountFn(AdminRunnerEditApp, {
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]), apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]),
propsData: { propsData: {
runnerId: mockRunnerId, runnerId: mockRunnerId,
...props, ...props,

View File

@ -9,7 +9,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerHeader from '~/runner/components/runner_header.vue'; import RunnerHeader from '~/runner/components/runner_header.vue';
import RunnerPauseButton from '~/runner/components/runner_pause_button.vue'; import RunnerPauseButton from '~/runner/components/runner_pause_button.vue';
import RunnerEditButton from '~/runner/components/runner_edit_button.vue'; import RunnerEditButton from '~/runner/components/runner_edit_button.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql'; import runnerQuery from '~/runner/graphql/details/runner.query.graphql';
import AdminRunnerShowApp from '~/runner/admin_runner_show/admin_runner_show_app.vue'; import AdminRunnerShowApp from '~/runner/admin_runner_show/admin_runner_show_app.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
@ -42,7 +42,7 @@ describe('AdminRunnerShowApp', () => {
const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => { const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
wrapper = mountFn(AdminRunnerShowApp, { wrapper = mountFn(AdminRunnerShowApp, {
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]), apolloProvider: createMockApollo([[runnerQuery, mockRunnerQuery]]),
propsData: { propsData: {
runnerId: mockRunnerId, runnerId: mockRunnerId,
...props, ...props,

View File

@ -35,8 +35,8 @@ import {
STATUS_ACTIVE, STATUS_ACTIVE,
RUNNER_PAGE_SIZE, RUNNER_PAGE_SIZE,
} from '~/runner/constants'; } from '~/runner/constants';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql'; import adminRunnersQuery from '~/runner/graphql/list/admin_runners.query.graphql';
import getRunnersCountQuery from '~/runner/graphql/get_runners_count.query.graphql'; import adminRunnersCountQuery from '~/runner/graphql/list/admin_runners_count.query.graphql';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
@ -71,8 +71,8 @@ describe('AdminRunnersApp', () => {
const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => { const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
const handlers = [ const handlers = [
[getRunnersQuery, mockRunnersQuery], [adminRunnersQuery, mockRunnersQuery],
[getRunnersCountQuery, mockRunnersCountQuery], [adminRunnersCountQuery, mockRunnersCountQuery],
]; ];
wrapper = mountFn(AdminRunnersApp, { wrapper = mountFn(AdminRunnersApp, {

View File

@ -8,7 +8,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';
import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue'; import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants'; import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
import runnersRegistrationTokenResetMutation from '~/runner/graphql/runners_registration_token_reset.mutation.graphql'; import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';

View File

@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import runnerDeleteMutation from '~/runner/graphql/runner_delete.mutation.graphql'; import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';

View File

@ -11,7 +11,7 @@ import RunnerPagination from '~/runner/components/runner_pagination.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '~/runner/constants'; import { I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '~/runner/constants';
import getRunnerJobsQuery from '~/runner/graphql/get_runner_jobs.query.graphql'; import runnerJobsQuery from '~/runner/graphql/details/runner_jobs.query.graphql';
import { runnerData, runnerJobsData } from '../mock_data'; import { runnerData, runnerJobsData } from '../mock_data';
@ -34,7 +34,7 @@ describe('RunnerJobs', () => {
const createComponent = ({ mountFn = shallowMountExtended } = {}) => { const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
wrapper = mountFn(RunnerJobs, { wrapper = mountFn(RunnerJobs, {
apolloProvider: createMockApollo([[getRunnerJobsQuery, mockRunnerJobsQuery]]), apolloProvider: createMockApollo([[runnerJobsQuery, mockRunnerJobsQuery]]),
propsData: { propsData: {
runner: mockRunner, runner: mockRunner,
}, },

View File

@ -4,7 +4,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import runnerToggleActiveMutation from '~/runner/graphql/runner_toggle_active.mutation.graphql'; import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { createAlert } from '~/flash'; import { createAlert } from '~/flash';

View File

@ -16,7 +16,7 @@ import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue';
import RunnerPagination from '~/runner/components/runner_pagination.vue'; import RunnerPagination from '~/runner/components/runner_pagination.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import getRunnerProjectsQuery from '~/runner/graphql/get_runner_projects.query.graphql'; import runnerProjectsQuery from '~/runner/graphql/details/runner_projects.query.graphql';
import { runnerData, runnerProjectsData } from '../mock_data'; import { runnerData, runnerProjectsData } from '../mock_data';
@ -40,7 +40,7 @@ describe('RunnerProjects', () => {
const createComponent = ({ mountFn = shallowMountExtended } = {}) => { const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
wrapper = mountFn(RunnerProjects, { wrapper = mountFn(RunnerProjects, {
apolloProvider: createMockApollo([[getRunnerProjectsQuery, mockRunnerProjectsQuery]]), apolloProvider: createMockApollo([[runnerProjectsQuery, mockRunnerProjectsQuery]]),
propsData: { propsData: {
runner: mockRunner, runner: mockRunner,
}, },

View File

@ -13,7 +13,7 @@ import {
ACCESS_LEVEL_REF_PROTECTED, ACCESS_LEVEL_REF_PROTECTED,
ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_NOT_PROTECTED,
} from '~/runner/constants'; } from '~/runner/constants';
import runnerUpdateMutation from '~/runner/graphql/runner_update.mutation.graphql'; import runnerUpdateMutation from '~/runner/graphql/details/runner_update.mutation.graphql';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import { runnerData } from '../mock_data'; import { runnerData } from '../mock_data';

View File

@ -32,8 +32,8 @@ import {
RUNNER_PAGE_SIZE, RUNNER_PAGE_SIZE,
I18N_EDIT, I18N_EDIT,
} from '~/runner/constants'; } from '~/runner/constants';
import getGroupRunnersQuery from '~/runner/graphql/get_group_runners.query.graphql'; import getGroupRunnersQuery from '~/runner/graphql/list/group_runners.query.graphql';
import getGroupRunnersCountQuery from '~/runner/graphql/get_group_runners_count.query.graphql'; import getGroupRunnersCountQuery from '~/runner/graphql/list/group_runners_count.query.graphql';
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue'; import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
import { captureException } from '~/runner/sentry_utils'; import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';

View File

@ -1,18 +1,18 @@
// Fixtures generated by: spec/frontend/fixtures/runner.rb // Fixtures generated by: spec/frontend/fixtures/runner.rb
// Admin queries // List queries
import runnersData from 'test_fixtures/graphql/runner/get_runners.query.graphql.json'; import runnersData from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.json';
import runnersCountData from 'test_fixtures/graphql/runner/get_runners_count.query.graphql.json'; import runnersDataPaginated from 'test_fixtures/graphql/runner/list/admin_runners.query.graphql.paginated.json';
import runnersDataPaginated from 'test_fixtures/graphql/runner/get_runners.query.graphql.paginated.json'; import runnersCountData from 'test_fixtures/graphql/runner/list/admin_runners_count.query.graphql.json';
import runnerData from 'test_fixtures/graphql/runner/get_runner.query.graphql.json'; import groupRunnersData from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.json';
import runnerWithGroupData from 'test_fixtures/graphql/runner/get_runner.query.graphql.with_group.json'; import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.paginated.json';
import runnerProjectsData from 'test_fixtures/graphql/runner/get_runner_projects.query.graphql.json'; import groupRunnersCountData from 'test_fixtures/graphql/runner/list/group_runners_count.query.graphql.json';
import runnerJobsData from 'test_fixtures/graphql/runner/get_runner_jobs.query.graphql.json';
// Group queries // Details queries
import groupRunnersData from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.json'; import runnerData from 'test_fixtures/graphql/runner/details/runner.query.graphql.json';
import groupRunnersCountData from 'test_fixtures/graphql/runner/get_group_runners_count.query.graphql.json'; import runnerWithGroupData from 'test_fixtures/graphql/runner/details/runner.query.graphql.with_group.json';
import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.paginated.json'; import runnerProjectsData from 'test_fixtures/graphql/runner/details/runner_projects.query.graphql.json';
import runnerJobsData from 'test_fixtures/graphql/runner/details/runner_jobs.query.graphql.json';
export { export {
runnersData, runnersData,

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DeployTokensHelper do
describe '#deploy_token_revoke_button_data' do
let_it_be(:token) { build(:deploy_token) }
let_it_be(:project) { build(:project) }
let_it_be(:revoke_deploy_token_path) { '/foobar/baz/-/deploy_tokens/1/revoke' }
it 'returns expected hash' do
expect(helper).to receive(:revoke_deploy_token_path).with(project, token).and_return(revoke_deploy_token_path)
expect(helper.deploy_token_revoke_button_data(token: token, group_or_project: project)).to match({
token: token.to_json(only: [:id, :name]),
revoke_path: revoke_deploy_token_path
})
end
end
end

View File

@ -49,7 +49,7 @@ RSpec.describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProject
it 'invalidates the markdown cache of related projects' do it 'invalidates the markdown cache of related projects' do
expect(subject).to receive(:remove_cached_html_for_projects) expect(subject).to receive(:remove_cached_html_for_projects)
.with(projects.map(&:id)) .with(a_collection_containing_exactly(*projects.map(&:id)))
subject.rename_projects subject.rename_projects
end end

View File

@ -390,26 +390,8 @@ RSpec.describe Group do
let!(:old_parent) { create(:group, parent: root) } let!(:old_parent) { create(:group, parent: root) }
let!(:new_parent) { create(:group, parent: root) } let!(:new_parent) { create(:group, parent: root) }
context 'with FOR UPDATE lock' do
before do
stub_feature_flags(for_no_key_update_lock: false)
subject
reload_models(old_parent, new_parent, group)
end
it 'updates traversal_ids' do
expect(group.traversal_ids).to eq [root.id, new_parent.id, group.id]
end
it_behaves_like 'hierarchy with traversal_ids'
it_behaves_like 'locked row', 'FOR UPDATE' do
let(:row) { root }
end
end
context 'with FOR NO KEY UPDATE lock' do context 'with FOR NO KEY UPDATE lock' do
before do before do
stub_feature_flags(for_no_key_update_lock: true)
subject subject
reload_models(old_parent, new_parent, group) reload_models(old_parent, new_parent, group)
end end
@ -419,7 +401,7 @@ RSpec.describe Group do
end end
it_behaves_like 'hierarchy with traversal_ids' it_behaves_like 'hierarchy with traversal_ids'
it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do it_behaves_like 'locked row' do
let(:row) { root } let(:row) { root }
end end
end end

View File

@ -68,24 +68,11 @@ RSpec.describe Namespace::TraversalHierarchy, type: :model do
end end
end end
it_behaves_like 'locked row', 'FOR UPDATE' do it_behaves_like 'locked row' do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new } let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
let(:row) { root } let(:row) { root }
before do before do
stub_feature_flags(for_no_key_update_lock: false)
recorded_queries.record { subject }
end
end
it_behaves_like 'locked row', 'FOR NO KEY UPDATE' do
let(:recorded_queries) { ActiveRecord::QueryRecorder.new }
let(:row) { root }
before do
stub_feature_flags(for_no_key_update_lock: true)
recorded_queries.record { subject } recorded_queries.record { subject }
end end
end end

View File

@ -436,17 +436,7 @@ RSpec.describe Namespace do
it { expect(namespace.traversal_ids).to eq [namespace.id] } it { expect(namespace.traversal_ids).to eq [namespace.id] }
end end
context 'with before_commit callback' do it_behaves_like 'default traversal_ids'
it_behaves_like 'default traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'default traversal_ids'
end
end end
describe "after_commit :expire_child_caches" do describe "after_commit :expire_child_caches" do

View File

@ -262,17 +262,7 @@ RSpec.describe Project, factory_default: :keep do
end end
end end
context 'sync-ing traversal_ids in before_commit callback' do it_behaves_like 'creates project namespace'
it_behaves_like 'creates project namespace'
end
context 'sync-ing traversal_ids in after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'creates project namespace'
end
end end
end end

View File

@ -85,14 +85,6 @@ RSpec.describe Groups::CreateService, '#execute' do
context 'with before_commit callback' do context 'with before_commit callback' do
it_behaves_like 'has sync-ed traversal_ids' it_behaves_like 'has sync-ed traversal_ids'
end end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
end end
context 'when user can not create a group' do context 'when user can not create a group' do
@ -119,17 +111,7 @@ RSpec.describe Groups::CreateService, '#execute' do
expect { subject }.not_to change(OnboardingProgress, :count).from(0) expect { subject }.not_to change(OnboardingProgress, :count).from(0)
end end
context 'with before_commit callback' do it_behaves_like 'has sync-ed traversal_ids'
it_behaves_like 'has sync-ed traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
end end
context 'as guest' do context 'as guest' do

View File

@ -225,17 +225,7 @@ RSpec.describe Projects::CreateService, '#execute' do
expect(project.project_namespace).to be_in_sync_with_project(project) expect(project.project_namespace).to be_in_sync_with_project(project)
end end
context 'with before_commit callback' do it_behaves_like 'has sync-ed traversal_ids'
it_behaves_like 'has sync-ed traversal_ids'
end
context 'with after_create callback' do
before do
stub_feature_flags(sync_traversal_ids_before_commit: false)
end
it_behaves_like 'has sync-ed traversal_ids'
end
end end
context 'group sharing', :sidekiq_inline do context 'group sharing', :sidekiq_inline do

View File

@ -49,6 +49,7 @@ module SimpleCovEnv
def configure_profile def configure_profile
SimpleCov.configure do SimpleCov.configure do
enable_coverage :branch
load_profile 'test_frameworks' load_profile 'test_frameworks'
track_files '{app,config/initializers,config/initializers_before_autoloader,db/post_migrate,haml_lint,lib,rubocop,tooling}/**/*.rb' track_files '{app,config/initializers,config/initializers_before_autoloader,db/post_migrate,haml_lint,lib,rubocop,tooling}/**/*.rb'

View File

@ -4,10 +4,10 @@
# Ensure a transaction also occurred. # Ensure a transaction also occurred.
# Be careful! This form of spec is not foolproof, but better than nothing. # Be careful! This form of spec is not foolproof, but better than nothing.
RSpec.shared_examples 'locked row' do |lock_type| RSpec.shared_examples 'locked row' do
it "has locked row" do it "has locked row" do
table_name = row.class.table_name table_name = row.class.table_name
ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+#{lock_type}/m ids_regex = /SELECT.*FROM.*#{table_name}.*"#{table_name}"."id" = #{row.id}.+FOR NO KEY UPDATE/m
expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT' expect(recorded_queries.log).to include a_string_matching 'SAVEPOINT'
expect(recorded_queries.log).to include a_string_matching ids_regex expect(recorded_queries.log).to include a_string_matching ids_regex

View File

@ -16,8 +16,8 @@ RSpec.describe 'admin/application_settings/_eks' do
shared_examples 'EKS secret access key input' do shared_examples 'EKS secret access key input' do
it 'renders an empty password field' do it 'renders an empty password field' do
render render
expect(rendered).to have_field('Secret access key', type: 'password') expect(rendered).to have_field('AWS secret access key (Optional)', type: 'password')
expect(page.find_field('Secret access key').value).to be_blank expect(page.find_field('AWS secret access key (Optional)').value).to be_blank
end end
end end

View File

@ -4,22 +4,20 @@ require 'spec_helper'
RSpec.describe 'layouts/_published_experiments', :experiment do RSpec.describe 'layouts/_published_experiments', :experiment do
before do before do
stub_const('TestControlExperiment', ApplicationExperiment) # Stub each experiment to be enabled, otherwise tracking does not happen.
stub_const('TestCandidateExperiment', ApplicationExperiment) stub_experiments(
stub_const('TestExcludedExperiment', ApplicationExperiment) test_control: :control,
test_excluded: true,
test_published_only: :control,
test_candidate: :candidate,
test_variant: :variant_name
)
TestControlExperiment.new('test_control').tap do |e| experiment(:test_control) { }
e.variant(:control) experiment(:test_excluded) { |e| e.exclude! }
e.publish experiment(:test_candidate) { |e| e.candidate { } }
end experiment(:test_variant) { |e| e.variant(:variant_name) { } }
TestCandidateExperiment.new('test_candidate').tap do |e| experiment(:test_published_only).publish
e.variant(:candidate)
e.publish
end
TestExcludedExperiment.new('test_excluded').tap do |e|
e.exclude!
e.publish
end
render render
end end
@ -29,7 +27,9 @@ RSpec.describe 'layouts/_published_experiments', :experiment do
expect(output).to include('gl.experiments = {') expect(output).to include('gl.experiments = {')
expect(output).to match(/"test_control":\{[^}]*"variant":"control"/) expect(output).to match(/"test_control":\{[^}]*"variant":"control"/)
expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/)
expect(output).not_to include('"test_excluded"') expect(output).not_to include('"test_excluded"')
expect(output).to match(/"test_candidate":\{[^}]*"variant":"candidate"/)
expect(output).to match(/"test_variant":\{[^}]*"variant":"variant_name"/)
expect(output).to match(/"test_published_only":\{[^}]*"variant":"control"/)
end end
end end