Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-30 12:10:51 +00:00
parent afa3d4e066
commit a10d237d37
75 changed files with 653 additions and 344 deletions

View file

@ -189,22 +189,6 @@ Rails/SaveBang:
- 'spec/controllers/omniauth_callbacks_controller_spec.rb'
- 'spec/controllers/profiles/emails_controller_spec.rb'
- 'spec/controllers/profiles/notifications_controller_spec.rb'
- 'spec/controllers/projects/artifacts_controller_spec.rb'
- 'spec/controllers/projects/cycle_analytics/events_controller_spec.rb'
- 'spec/controllers/projects/cycle_analytics_controller_spec.rb'
- 'spec/controllers/projects/discussions_controller_spec.rb'
- 'spec/controllers/projects/forks_controller_spec.rb'
- 'spec/controllers/projects/group_links_controller_spec.rb'
- 'spec/controllers/projects/imports_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- 'spec/controllers/projects/labels_controller_spec.rb'
- 'spec/controllers/projects/milestones_controller_spec.rb'
- 'spec/controllers/projects/notes_controller_spec.rb'
- 'spec/controllers/projects/pipelines_controller_spec.rb'
- 'spec/controllers/projects/releases/evidences_controller_spec.rb'
- 'spec/controllers/projects/runners_controller_spec.rb'
- 'spec/controllers/projects/starrers_controller_spec.rb'
- 'spec/controllers/projects/uploads_controller_spec.rb'
- 'spec/controllers/projects_controller_spec.rb'
- 'spec/controllers/sent_notifications_controller_spec.rb'
- 'spec/controllers/sessions_controller_spec.rb'

View file

@ -1 +1 @@
cc879cfb5db4ed342c4f0ea744dbbfdc9649b35f
79003389aa5098a6ef37e73298cafbad4d9e6b79

View file

@ -0,0 +1,105 @@
<script>
import {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { __ } from '~/locale';
import { DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY, DEBOUNCE_DELAY } from '../constants';
import { stripQuotes } from '../filtered_search_utils';
export default {
components: {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlDropdownDivider,
GlLoadingIcon,
},
props: {
config: {
type: Object,
required: true,
},
value: {
type: Object,
required: true,
},
},
data() {
return {
emojis: this.config.initialEmojis || [],
defaultEmojis: this.config.defaultEmojis || [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY],
loading: true,
};
},
computed: {
currentValue() {
return this.value.data.toLowerCase();
},
activeEmoji() {
return this.emojis.find(
(emoji) => emoji.name.toLowerCase() === stripQuotes(this.currentValue),
);
},
},
methods: {
fetchEmojiBySearchTerm(searchTerm) {
this.loading = true;
this.config
.fetchEmojis(searchTerm)
.then((res) => {
this.emojis = Array.isArray(res) ? res : res.data;
})
.catch(() => createFlash(__('There was a problem fetching emojis.')))
.finally(() => {
this.loading = false;
});
},
searchEmojis: debounce(function debouncedSearch({ data }) {
this.fetchEmojiBySearchTerm(data);
}, DEBOUNCE_DELAY),
},
};
</script>
<template>
<gl-filtered-search-token
:config="config"
v-bind="{ ...$props, ...$attrs }"
v-on="$listeners"
@input="searchEmojis"
>
<template #view="{ inputValue }">
<gl-emoji v-if="activeEmoji" :data-name="activeEmoji.name" />
<span v-else>{{ inputValue }}</span>
</template>
<template #suggestions>
<gl-filtered-search-suggestion
v-for="emoji in defaultEmojis"
:key="emoji.value"
:value="emoji.value"
>
{{ emoji.value }}
</gl-filtered-search-suggestion>
<gl-dropdown-divider v-if="defaultEmojis.length" />
<gl-loading-icon v-if="loading" />
<template v-else>
<gl-filtered-search-suggestion
v-for="emoji in emojis"
:key="emoji.name"
:value="emoji.name"
>
<div class="gl-display-flex">
<gl-emoji :data-name="emoji.name" />
<span class="gl-ml-3">{{ emoji.name }}</span>
</div>
</gl-filtered-search-suggestion>
</template>
</template>
</gl-filtered-search-token>
</template>

View file

@ -260,7 +260,7 @@ export default {
:line-content="lineContent"
:can-suggest="canSuggest"
:show-suggest-popover="showSuggestPopover"
:suggestion-start-index="lines.length"
:suggestion-start-index="lines.length - 1"
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"

View file

@ -14,6 +14,9 @@ module Types
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the milestone.'
field :iid, GraphQL::ID_TYPE, null: false,
description: "Internal ID of the milestone."
field :title, GraphQL::STRING_TYPE, null: false,
description: 'Title of the milestone.'

View file

@ -5,6 +5,8 @@ class Packages::Package < ApplicationRecord
include UsageStatistics
include Gitlab::Utils::StrongMemoize
DISPLAYABLE_STATUSES = [:default, :error].freeze
belongs_to :project
belongs_to :creator, class_name: 'User'
@ -70,7 +72,7 @@ class Packages::Package < ApplicationRecord
composer: 6, generic: 7, golang: 8, debian: 9,
rubygems: 10 }
enum status: { default: 0, hidden: 1, processing: 2 }
enum status: { default: 0, hidden: 1, processing: 2, error: 3 }
scope :with_name, ->(name) { where(name: name) }
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }
@ -80,7 +82,7 @@ class Packages::Package < ApplicationRecord
scope :without_version_like, -> (version) { where.not(arel_table[:version].matches(version)) }
scope :with_package_type, ->(package_type) { where(package_type: package_type) }
scope :with_status, ->(status) { where(status: status) }
scope :displayable, -> { with_status(:default) }
scope :displayable, -> { with_status(DISPLAYABLE_STATUSES) }
scope :including_build_info, -> { includes(pipelines: :user) }
scope :including_project_route, -> { includes(project: { namespace: :route }) }
scope :including_tags, -> { includes(:tags) }

View file

@ -1370,9 +1370,9 @@ class Project < ApplicationRecord
end
def disabled_services
return %w(datadog) unless Feature.enabled?(:datadog_ci_integration, self)
return %w[datadog hipchat] unless Feature.enabled?(:datadog_ci_integration, self)
[]
%w[hipchat]
end
def find_or_initialize_service(name)

View file

@ -12,7 +12,7 @@ class Service < ApplicationRecord
SERVICE_NAMES = %w[
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat hipchat irker jira
drone_ci emails_on_push ewm external_wiki flowdock hangouts_chat irker jira
mattermost mattermost_slash_commands microsoft_teams packagist pipelines_email
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
].freeze

View file

@ -3,15 +3,15 @@
.table-mobile-header{ role: 'rowheader' }= _('Type')
.table-mobile-content
- if runner.instance_type?
%span.badge.badge-success shared
%span.badge.badge-pill.gl-badge.sm.badge-success shared
- elsif runner.group_type?
%span.badge.badge-success group
%span.badge.badge-pill.gl-badge.sm.badge-success group
- else
%span.badge.badge-info specific
%span.badge.badge-pill.gl-badge.sm.badge-info specific
- if runner.locked?
%span.badge.badge-warning locked
%span.badge.badge-pill.gl-badge.sm.badge-warning locked
- unless runner.active?
%span.badge.badge-danger paused
%span.badge.badge-pill.gl-badge.sm.badge-danger paused
.table-section.section-10
.table-mobile-header{ role: 'rowheader' }= _('Runner token')

View file

@ -1,19 +1,11 @@
- add_page_specific_style 'page_bundles/ci_status'
= content_for :title do
%h3.project-title
Runner ##{@runner.id}
.float-right
- if @runner.instance_type?
%span.runner-state.runner-state-shared
Shared
- else
%span.runner-state.runner-state-specific
Specific
- page_title @runner.short_sha
- add_to_breadcrumbs _("Runners"), admin_runners_path
- breadcrumb_title "##{@runner.id}"
- add_to_breadcrumbs _('Runners'), admin_runners_path
- breadcrumb_title page_title
%h2.page-title
= sprintf(s_('Runners|Runner #%{runner_id}'), {runner_id: @runner.id})
- if @runner.instance_type?
.bs-callout.bs-callout-success

View file

@ -0,0 +1,5 @@
---
title: Remove HipChat integration from frontend and docs
merge_request: 57556
author:
type: removed

View file

@ -0,0 +1,5 @@
---
title: Update runner badges look and feel in admin runners table
merge_request: 57566
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Use empty-query by default to check database connection
merge_request: 54366
author: Leandro Gomes @leandrogs
type: performance

View file

@ -0,0 +1,5 @@
---
title: Add TargetProject And SourceBranch Index To MergeRequest
merge_request: 57691
author:
type: performance

View file

@ -0,0 +1,5 @@
---
title: 'GraphQL: expose milestone iid'
merge_request: 57732
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Add Runner ID as title in Runner details page
merge_request: 57247
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Fix Rails/SaveBang rubocop offenses in spec/controllers/projects/*
merge_request: 57643
author: Abdul Wadood @abdulwd
type: fixed

View file

@ -1,8 +0,0 @@
---
name: cluster_agent_list
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42115
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249596
milestone: '13.5'
type: development
group: group::configure
default_enabled: true

View file

@ -2,6 +2,6 @@
# # frozen_string_literal: true
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: false)
if Gitlab::Utils.to_boolean(ENV['ENABLE_ACTIVERECORD_EMPTY_PING'], default: true)
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Database::PostgresqlAdapter::EmptyQueryPing)
end

View file

@ -1,4 +1,4 @@
# Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/lib/gitlab/usage_data_counters/aggregated_metrics/common.yml
# Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/config/metrics/aggregates/common.yml
# instead of this file.
#- name: unique name of aggregated metric
# operator: aggregation operator. Valid values are:

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
class AddTargetProjectAndSourceBranchIndexToMergeRequest < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_merge_requests_on_target_project_id_and_source_branch'
disable_ddl_transaction!
def up
add_concurrent_index :merge_requests, [:target_project_id, :source_branch], name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :epic_issues, INDEX_NAME
end
end

View file

@ -0,0 +1 @@
412d0cedef5c933c7de3a70ca2365fe0cfaa4087429ca418854092b6c37904f1

View file

@ -23090,6 +23090,8 @@ CREATE INDEX index_merge_requests_on_target_project_id_and_iid_and_state_id ON m
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_jira_title ON merge_requests USING btree (target_project_id, iid) WHERE ((title)::text ~ '[A-Z][A-Z_0-9]+-\d+'::text);
CREATE INDEX index_merge_requests_on_target_project_id_and_source_branch ON merge_requests USING btree (target_project_id, source_branch);
CREATE INDEX index_merge_requests_on_target_project_id_and_squash_commit_sha ON merge_requests USING btree (target_project_id, squash_commit_sha);
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));

View file

@ -245,7 +245,6 @@ Helm
Heroku
Herokuish
Hexo
HipChat
hostname
hostnames
hotfix

View file

@ -3064,6 +3064,7 @@ Autogenerated return type of GitlabSubscriptionActivate.
| `additionalPurchasedStorageSize` | [`Float`](#float) | Additional storage purchased for the root namespace in bytes. |
| `autoDevopsEnabled` | [`Boolean`](#boolean) | Indicates whether Auto DevOps is enabled for all projects within this group. |
| `avatarUrl` | [`String`](#string) | Avatar URL of the group. |
| `billableMembersCount` | [`Int`](#int) | The number of billable users in the group. |
| `board` | [`Board`](#board) | A single board of the group. |
| `boards` | [`BoardConnection`](#boardconnection) | Boards of the group. |
| `codeCoverageActivities` | [`CodeCoverageActivityConnection`](#codecoverageactivityconnection) | Represents the code coverage activity for this group. |
@ -4167,6 +4168,7 @@ Represents a milestone.
| `dueDate` | [`Time`](#time) | Timestamp of the milestone due date. |
| `groupMilestone` | [`Boolean!`](#boolean) | Indicates if milestone is at group level. |
| `id` | [`ID!`](#id) | ID of the milestone. |
| `iid` | [`ID!`](#id) | Internal ID of the milestone. |
| `projectMilestone` | [`Boolean!`](#boolean) | Indicates if milestone is at project level. |
| `report` | [`TimeboxReport`](#timeboxreport) | Historically accurate report about the timebox. |
| `startDate` | [`Time`](#time) | Timestamp of the milestone start date. |
@ -8013,7 +8015,6 @@ State of a Sentry error.
| `FLOWDOCK_SERVICE` | FlowdockService type. |
| `GITHUB_SERVICE` | GithubService type. |
| `HANGOUTS_CHAT_SERVICE` | HangoutsChatService type. |
| `HIPCHAT_SERVICE` | HipchatService type. |
| `IRKER_SERVICE` | IrkerService type. |
| `JENKINS_SERVICE` | JenkinsService type. |
| `JIRA_SERVICE` | JiraService type. |

View file

@ -692,53 +692,6 @@ Get Hangouts Chat service settings for a project.
GET /projects/:id/services/hangouts-chat
```
## HipChat
Private group chat and IM
### Create/Edit HipChat service
Set HipChat service for a project.
```plaintext
PUT /projects/:id/services/hipchat
```
Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | Room token |
| `color` | string | false | The room color |
| `notify` | boolean | false | Enable notifications |
| `room` | string | false |Room name or ID |
| `api_version` | string | false | Leave blank for default (v2) |
| `server` | string | false | Leave blank for default. For example, `https://hipchat.example.com`. |
| `push_events` | boolean | false | Enable notifications for push events |
| `issues_events` | boolean | false | Enable notifications for issue events |
| `confidential_issues_events` | boolean | false | Enable notifications for confidential issue events |
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
| `note_events` | boolean | false | Enable notifications for note events |
| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
### Delete HipChat service
Delete HipChat service for a project.
```plaintext
DELETE /projects/:id/services/hipchat
```
### Get HipChat service settings
Get HipChat service settings for a project.
```plaintext
GET /projects/:id/services/hipchat
```
## Irker (IRC gateway)
Send IRC messages, on update, to a list of recipients through an Irker gateway.

View file

@ -98,3 +98,58 @@ bazel test //internal/module/gitops/server:server_test
- Bazel documentation about [specifying targets to build](https://docs.bazel.build/versions/master/guide.html#specifying-targets-to-build).
- [The Bazel query](https://docs.bazel.build/versions/master/query.html)
- [Bazel query how to](https://docs.bazel.build/versions/master/query-how-to.html)
## KAS QA tests
This section describes how to run KAS tests against different GitLab environments based on the
[GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa).
### Status
The `kas` QA tests currently have some limitations. You can run them manually on GDK, but they don't
run automatically with the nightly jobs against the live environment. See the section below
to learn how to run them against different environments.
### Prepare
Before performing any of these tests, if you have a `k3s` instance running, make sure to
stop it manually before running them. Otherwise, the tests might fail with the message
`failed to remove k3s cluster`.
You might need to specify the correct Agent image version that matches the `kas` image version. You can use the `GITLAB_AGENTK_VERSION` local env for this.
### Against `staging`
1. Go to your local `qa/qa/service/cluster_provider/k3s.rb` and comment out
[this line](https://gitlab.com/gitlab-org/gitlab/-/blob/5b15540ea78298a106150c3a1d6ed26416109b9d/qa/qa/service/cluster_provider/k3s.rb#L8) and
[this line](https://gitlab.com/gitlab-org/gitlab/-/blob/5b15540ea78298a106150c3a1d6ed26416109b9d/qa/qa/service/cluster_provider/k3s.rb#L36).
We don't allow local connections on `staging` as they require an admin user.
1. Ensure you don't have an `EE_LICENSE` env var set as this would force an admin login.
1. Go to your GDK root folder and `cd gitlab/qa`.
1. Login with your user in staging and create a group to be used as sandbox.
Something like: `username-qa-sandbox`.
1. Create an access token for your user with the `api` permission.
1. Replace the values given below with your own and run:
```shell
GITLAB_SANDBOX_NAME="<THE GROUP ID YOU CREATED ON STEP 2>" \
GITLAB_QA_ACCESS_TOKEN="<THE ACCESS TOKEN YOU CREATED ON STEP 3>" \
GITLAB_USERNAME="<YOUR STAGING USERNAME>" \
GITLAB_PASSWORD="<YOUR STAGING PASSWORD>" \
bundle exec bin/qa Test::Instance::All https://staging.gitlab.com -- --tag quarantine qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb
```
### Against GDK
1. Go to your `qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb` and comment out [this line](https://gitlab.com/gitlab-org/gitlab/-/blob/a55b78532cfd29426cf4e5b4edda81407da9d449/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb#L27) and uncomment [this line](https://gitlab.com/gitlab-org/gitlab/-/blob/a55b78532cfd29426cf4e5b4edda81407da9d449/qa/qa/fixtures/kubernetes_agent/agentk-manifest.yaml.erb#L28).
GDK's `kas` listens on `grpc`, not on `wss`.
1. Go to the GDK's root folder and `cd gitlab/qa`.
1. On the contrary to staging, run the QA test in GDK as admin, which is the default choice. To do so, use the default sandbox group and run the command below. Make sure to adjust your credentials if necessary, otherwise, the test might fail:
```shell
GITLAB_USERNAME=root \
GITLAB_PASSWORD="5iveL\!fe" \
GITLAB_ADMIN_USERNAME=root \
GITLAB_ADMIN_PASSWORD="5iveL\!fe" \
bundle exec bin/qa Test::Instance::All http://gdk.test:3000 -- --tag quarantine qa/specs/features/ee/api/7_configure/kubernetes/kubernetes_agent_spec.rb
```

View file

@ -409,7 +409,7 @@ When ready to merge:
- **Start a new merge request pipeline with the `Run Pipeline` button in the merge
request's "Pipelines" tab, and enable "Merge When Pipeline Succeeds" (MWPS).** Note that:
- If **[master is broken](https://about.gitlab.com/handbook/engineering/workflow/#broken-master),
do not merge the merge request**. Follow these specific [handbook instructions](https://about.gitlab.com/handbook/engineering/workflow/#maintaining-throughput-during-broken-master).
For other cases, follow these [handbook instructions](https://about.gitlab.com/handbook/engineering/workflow/#merging-during-broken-master).
- If the **latest [Pipeline for Merged Results](../ci/merge_request_pipelines/pipelines_for_merged_results/#pipelines-for-merged-results)** finished less than 2 hours ago, you
might merge without starting a new pipeline as the merge request is close
enough to `master`.

View file

@ -949,7 +949,7 @@ appear to be associated to any of the services running, because they all appear
WARNING:
This feature is intended solely for internal GitLab use.
To add data for aggregated metrics into Usage Ping payload you should add corresponding definition at [`lib/gitlab/usage_data_counters/aggregated_metrics/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/aggregated_metrics/) for metrics available at Community Edition and at [`ee/lib/gitlab/usage_data_counters/aggregated_metrics/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/gitlab/usage_data_counters/aggregated_metrics/) for Enterprise Edition ones.
To add data for aggregated metrics into Usage Ping payload you should add corresponding definition at [`config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/) for metrics available at Community Edition and at [`ee/config/metrics/aggregates/*.yaml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/aggregates/) for Enterprise Edition ones.
Each aggregate definition includes following parts:
@ -1111,7 +1111,7 @@ end
#### Add new aggregated metric definition
After all metrics are persisted, you can add an aggregated metric definition at
[`aggregated_metrics/`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters/aggregated_metrics/).
[`aggregated_metrics/`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/aggregates/).
To declare the aggregate of metrics collected with [Estimated Batch Counters](#estimated-batch-counters),
you must fulfill the following requirements:

View file

@ -63,7 +63,7 @@ or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc a
## Integrations
Integration with services such as Campfire, Flowdock, HipChat, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
Integration with services such as Campfire, Flowdock, Jira, Pivotal Tracker, and Slack are available as [Integrations](../user/project/integrations/overview.md).
## Troubleshooting

View file

@ -4,7 +4,7 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# GitLab Kubernetes Agent **(PREMIUM SELF)**
# GitLab Kubernetes Agent **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/223061) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.4.
> - [In GitLab 13.10](https://gitlab.com/gitlab-org/gitlab/-/issues/300960), KAS became available on GitLab.com under `wss://kas.gitlab.com` through an Early Adopter Program.

View file

@ -70,7 +70,7 @@ With GitLab Enterprise Edition, you can also:
- Leverage continuous delivery method with [Canary Deployments](project/canary_deployments.md).
- Scan your code for vulnerabilities and [display them in merge requests](application_security/sast/index.md).
You can also [integrate](project/integrations/overview.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, HipChat, Trello, Slack, Bamboo CI, Jira, and a lot more.
You can also [integrate](project/integrations/overview.md) GitLab with numerous third-party applications, such as Mattermost, Microsoft Teams, Trello, Slack, Bamboo CI, Jira, and a lot more.
## User types

View file

@ -1,64 +0,0 @@
---
stage: Create
group: Ecosystem
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Atlassian HipChat **(FREE)**
WARNING:
This feature, and the integration for it, were
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57434) in
GitLab 13.11. The HipChat integration no longer sends any notifications to HipChat.
GitLab provides a way to send HipChat notifications upon a number of events,
such as when a user pushes code, creates a branch or tag, adds a comment, and
creates a merge request.
## Setup
GitLab requires the use of a HipChat v2 API token to work. v1 tokens are
not supported at this time. Note the differences between v1 and v2 tokens:
HipChat v1 API (legacy) supports "API Auth Tokens" in the Group API menu. A v1
token is allowed to send messages to *any* room.
HipChat v2 API has tokens that are can be created using the Integrations tab
in the Group or Room administration page. By design, these are lightweight tokens that
allow GitLab to send messages only to *one* room.
### Complete these steps in HipChat
1. Go to: `https://admin.hipchat.com/admin`
1. Click on "Group Admin" -> "Integrations".
1. Find "Build Your Own!" and click "Create".
1. Select the desired room, name the integration "GitLab", and click "Create".
1. In the "Send messages to this room by posting this URL" column, you should
see a URL in the format:
```plaintext
https://api.hipchat.com/v2/room/<room>/notification?auth_token=<token>
```
HipChat is now ready to accept messages from GitLab. Next, set up the HipChat
service in GitLab.
### Complete these steps in GitLab
1. Navigate to the project you want to configure for notifications.
1. Navigate to the [Integrations page](overview.md#accessing-integrations)
1. Click "HipChat".
1. Ensure that the **Active** toggle is enabled.
1. Insert the `token` field from the URL into the `Token` field on the Web page.
1. Insert the `room` field from the URL into the `Room` field on the Web page.
1. Save or optionally click "Test Settings".
## Troubleshooting
If you do not see notifications, make sure you are using a HipChat v2 API
token, not a v1 token.
Note that the v2 token is tied to a specific room. If you want to be able to
specify arbitrary rooms, you can create an API token for a specific user in
HipChat under "Account settings" and "API access". Use the `XXX` value under
`auth_token=XXX`.

View file

@ -41,7 +41,6 @@ Click on the service links to see further configuration instructions and details
| [Generic alerts](../../../operations/incident_management/integrations.md) **(ULTIMATE)** | Receive alerts on GitLab from any source | No |
| [GitHub](github.md) **(PREMIUM)** | Sends pipeline notifications to GitHub | No |
| [Hangouts Chat](hangouts_chat.md) | Receive events notifications in Google Hangouts Chat | No |
| [HipChat](hipchat.md) | Private group chat and IM | No |
| [Irker (IRC gateway)](irker.md) | Send IRC messages, on update, to a list of recipients through an Irker gateway | No |
| [Jira](jira.md) | Jira issue tracker | No |
| [Jenkins](../../../integration/jenkins.md) **(STARTER)** | An extendable open source continuous integration server | Yes |

View file

@ -91,7 +91,7 @@ Examples:
### Excluding filters
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab Starter 13.3.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/31684) in GitLab 13.3.
Filters can be inverted to **filter out** results from the result set, by prefixing the filter name with a `-` (hyphen) character, such as:

View file

@ -112,7 +112,6 @@ module API
end
def delete_group(group)
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/22226')
destroy_conditionally!(group) do |group|
::Groups::DestroyService.new(group, current_user).async_execute
end

View file

@ -27,7 +27,7 @@ module Gitlab
def included_in_gitlab_com_rollout?(project)
return true unless ::Gitlab.com?
Feature.enabled?(:kubernetes_agent_on_gitlab_com, project)
Feature.enabled?(:kubernetes_agent_on_gitlab_com, project, default_enabled: :yaml)
end
end
end

View file

@ -56,7 +56,7 @@ module Gitlab
class << self
def paths
@paths ||= [Rails.root.join('config', 'metrics', '**', '*.yml')]
@paths ||= [Rails.root.join('config', 'metrics', '[^agg]*', '*.yml')]
end
def definitions(skip_validation: false)

View file

@ -7,7 +7,7 @@ module Gitlab
UNION_OF_AGGREGATED_METRICS = 'OR'
INTERSECTION_OF_AGGREGATED_METRICS = 'AND'
ALLOWED_METRICS_AGGREGATIONS = [UNION_OF_AGGREGATED_METRICS, INTERSECTION_OF_AGGREGATED_METRICS].freeze
AGGREGATED_METRICS_PATH = Rails.root.join('lib/gitlab/usage_data_counters/aggregated_metrics/*.yml')
AGGREGATED_METRICS_PATH = Rails.root.join('config/metrics/aggregates/*.yml')
AggregatedMetricError = Class.new(StandardError)
UnknownAggregationOperator = Class.new(AggregatedMetricError)
UnknownAggregationSource = Class.new(AggregatedMetricError)

View file

@ -6,6 +6,7 @@ module Gitlab
module NamesSuggestions
class Generator < ::Gitlab::UsageData
FREE_TEXT_METRIC_NAME = "<please fill metric name>"
CONSTRAINTS_PROMPT_TEMPLATE = "<adjective describing: '%{constraints}'>"
class << self
def generate(key_path)
@ -76,15 +77,20 @@ module Gitlab
# count_environment_id_from_clusters_with_deployments
actual_source = parse_source(relation, arel_column)
if constraints.include?(actual_source)
parts << "<adjective describing: '#{constraints}'>"
end
append_constraints_prompt(actual_source, [constraints], parts)
parts << actual_source
parts += process_joined_relations(actual_source, arel, relation)
parts += process_joined_relations(actual_source, arel, relation, constraints)
parts.compact.join('_')
end
def append_constraints_prompt(target, constraints, parts)
applicable_constraints = constraints.select { |constraint| constraint.include?(target) }
return unless applicable_constraints.any?
parts << CONSTRAINTS_PROMPT_TEMPLATE % { constraints: applicable_constraints.join(' AND ') }
end
def parse_constraints(relation:, arel:)
connection = relation.connection
::Gitlab::Usage::Metrics::NamesSuggestions::RelationParsers::Constraints
@ -94,7 +100,7 @@ module Gitlab
end
# TODO: joins with `USING` keyword
def process_joined_relations(actual_source, arel, relation)
def process_joined_relations(actual_source, arel, relation, where_constraints)
joins = parse_joins(connection: relation.connection, arel: arel)
return [] unless joins.any?
@ -109,7 +115,7 @@ module Gitlab
build_relations_tree(joins + [{ source: relation.table_name }], actual_source, source_key: :target, target_key: :source)
end
collect_join_parts(relations[actual_source])
collect_join_parts(relations: relations[actual_source], joins: joins, wheres: where_constraints)
end
def parse_joins(connection:, arel:)
@ -128,7 +134,11 @@ module Gitlab
join_cond_regex = /(#{source_regex}\s+=\s+#{target_regex})|(#{target_regex}\s+=\s+#{source_regex})/i
matched = join_cond_regex.match(join[:constraints])
join[:target] = matched[:target] if matched
if matched
join[:target] = matched[:target]
join[:constraints].gsub!(/#{join_cond_regex}(\s+(and|or))*/i, '')
end
join
end
end
@ -147,13 +157,15 @@ module Gitlab
tree
end
def collect_join_parts(joined_relations, parts = [], conjunctions = %w[with having including].cycle)
def collect_join_parts(relations:, joins:, wheres:, parts: [], conjunctions: %w[with having including].cycle)
conjunction = conjunctions.next
joined_relations.each do |subtree|
relations.each do |subtree|
subtree.each do |parent, children|
parts << "<#{conjunction}>"
join_constraints = joins.find { |join| join[:source] == parent }&.dig(:constraints)
append_constraints_prompt(parent, [wheres, join_constraints].compact, parts)
parts << parent
collect_join_parts(children, parts, conjunctions)
collect_join_parts(relations: children, joins: joins, wheres: wheres, parts: parts, conjunctions: conjunctions)
end
end
parts

View file

@ -34,120 +34,120 @@ module Gitlab
ISSUE_COMMENT_REMOVED = 'g_project_management_issue_comment_removed'
class << self
def track_issue_created_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_CREATED, author, time)
def track_issue_created_action(author:)
track_unique_action(ISSUE_CREATED, author)
end
def track_issue_title_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_TITLE_CHANGED, author, time)
def track_issue_title_changed_action(author:)
track_unique_action(ISSUE_TITLE_CHANGED, author)
end
def track_issue_description_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_DESCRIPTION_CHANGED, author, time)
def track_issue_description_changed_action(author:)
track_unique_action(ISSUE_DESCRIPTION_CHANGED, author)
end
def track_issue_assignee_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_ASSIGNEE_CHANGED, author, time)
def track_issue_assignee_changed_action(author:)
track_unique_action(ISSUE_ASSIGNEE_CHANGED, author)
end
def track_issue_made_confidential_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MADE_CONFIDENTIAL, author, time)
def track_issue_made_confidential_action(author:)
track_unique_action(ISSUE_MADE_CONFIDENTIAL, author)
end
def track_issue_made_visible_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MADE_VISIBLE, author, time)
def track_issue_made_visible_action(author:)
track_unique_action(ISSUE_MADE_VISIBLE, author)
end
def track_issue_closed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_CLOSED, author, time)
def track_issue_closed_action(author:)
track_unique_action(ISSUE_CLOSED, author)
end
def track_issue_reopened_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_REOPENED, author, time)
def track_issue_reopened_action(author:)
track_unique_action(ISSUE_REOPENED, author)
end
def track_issue_label_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_LABEL_CHANGED, author, time)
def track_issue_label_changed_action(author:)
track_unique_action(ISSUE_LABEL_CHANGED, author)
end
def track_issue_milestone_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MILESTONE_CHANGED, author, time)
def track_issue_milestone_changed_action(author:)
track_unique_action(ISSUE_MILESTONE_CHANGED, author)
end
def track_issue_cross_referenced_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_CROSS_REFERENCED, author, time)
def track_issue_cross_referenced_action(author:)
track_unique_action(ISSUE_CROSS_REFERENCED, author)
end
def track_issue_moved_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MOVED, author, time)
def track_issue_moved_action(author:)
track_unique_action(ISSUE_MOVED, author)
end
def track_issue_related_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_RELATED, author, time)
def track_issue_related_action(author:)
track_unique_action(ISSUE_RELATED, author)
end
def track_issue_unrelated_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_UNRELATED, author, time)
def track_issue_unrelated_action(author:)
track_unique_action(ISSUE_UNRELATED, author)
end
def track_issue_marked_as_duplicate_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author, time)
def track_issue_marked_as_duplicate_action(author:)
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author)
end
def track_issue_locked_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_LOCKED, author, time)
def track_issue_locked_action(author:)
track_unique_action(ISSUE_LOCKED, author)
end
def track_issue_unlocked_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_UNLOCKED, author, time)
def track_issue_unlocked_action(author:)
track_unique_action(ISSUE_UNLOCKED, author)
end
def track_issue_designs_added_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_DESIGNS_ADDED, author, time)
def track_issue_designs_added_action(author:)
track_unique_action(ISSUE_DESIGNS_ADDED, author)
end
def track_issue_designs_modified_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_DESIGNS_MODIFIED, author, time)
def track_issue_designs_modified_action(author:)
track_unique_action(ISSUE_DESIGNS_MODIFIED, author)
end
def track_issue_designs_removed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_DESIGNS_REMOVED, author, time)
def track_issue_designs_removed_action(author:)
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
end
def track_issue_due_date_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_DUE_DATE_CHANGED, author, time)
def track_issue_due_date_changed_action(author:)
track_unique_action(ISSUE_DUE_DATE_CHANGED, author)
end
def track_issue_time_estimate_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author, time)
def track_issue_time_estimate_changed_action(author:)
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author)
end
def track_issue_time_spent_changed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author, time)
def track_issue_time_spent_changed_action(author:)
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author)
end
def track_issue_comment_added_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_COMMENT_ADDED, author, time)
def track_issue_comment_added_action(author:)
track_unique_action(ISSUE_COMMENT_ADDED, author)
end
def track_issue_comment_edited_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_COMMENT_EDITED, author, time)
def track_issue_comment_edited_action(author:)
track_unique_action(ISSUE_COMMENT_EDITED, author)
end
def track_issue_comment_removed_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_COMMENT_REMOVED, author, time)
def track_issue_comment_removed_action(author:)
track_unique_action(ISSUE_COMMENT_REMOVED, author)
end
def track_issue_cloned_action(author:, time: Time.zone.now)
track_unique_action(ISSUE_CLONED, author, time)
def track_issue_cloned_action(author:)
track_unique_action(ISSUE_CLONED, author)
end
private
def track_unique_action(action, author, time)
def track_unique_action(action, author)
return unless author
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id, time: time)
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id)
end
end
end

View file

@ -26554,6 +26554,9 @@ msgstr ""
msgid "Runners|Revision"
msgstr ""
msgid "Runners|Runner #%{runner_id}"
msgstr ""
msgid "Runners|Shared"
msgstr ""
@ -30690,6 +30693,9 @@ msgstr ""
msgid "There was a problem fetching branches."
msgstr ""
msgid "There was a problem fetching emojis."
msgstr ""
msgid "There was a problem fetching groups."
msgstr ""
@ -32190,7 +32196,7 @@ msgstr ""
msgid "Trials|You can apply your trial to a new group or an existing group."
msgstr ""
msgid "Trials|You won't get a free trial right now but you can always resume this process by clicking on your avatar and choosing 'Start a free trial'"
msgid "Trials|You won't get a free trial right now but you can always resume this process by selecting your avatar and choosing 'Start an Ultimate trial'"
msgstr ""
msgid "Trials|Your trial ends on %{boldStart}%{trialEndDate}%{boldEnd}. We hope youre enjoying the features of GitLab %{planName}. To keep those features after your trial ends, youll need to buy a subscription. (You can also choose GitLab Premium if it meets your needs.)"

View file

@ -448,7 +448,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with regular branch' do
before do
pipeline.update(ref: 'master',
pipeline.update!(ref: 'master',
sha: project.commit('master').sha)
get :latest_succeeded, params: params_from_ref('master')
@ -459,7 +459,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name containing slash' do
before do
pipeline.update(ref: 'improve/awesome',
pipeline.update!(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome')
@ -470,7 +470,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name and path containing slashes' do
before do
pipeline.update(ref: 'improve/awesome',
pipeline.update!(ref: 'improve/awesome',
sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome', job.name, 'file/README.md')

View file

@ -26,7 +26,7 @@ RSpec.describe Projects::CycleAnalytics::EventsController do
let(:issue) { create(:issue, project: project, created_at: 9.days.ago) }
before do
issue.update(milestone: milestone)
issue.update!(milestone: milestone)
end
it 'is not empty' do

View file

@ -85,7 +85,7 @@ RSpec.describe Projects::DiscussionsController do
context "when the discussion is not resolvable" do
before do
note.update(system: true)
note.update!(system: true)
end
it "returns status 404" do
@ -168,7 +168,7 @@ RSpec.describe Projects::DiscussionsController do
context "when the discussion is not resolvable" do
before do
note.update(system: true)
note.update!(system: true)
end
it "returns status 404" do

View file

@ -71,7 +71,7 @@ RSpec.describe Projects::ForksController do
context 'when fork is internal' do
before do
forked_project.update(visibility_level: Project::INTERNAL, group: group)
forked_project.update!(visibility_level: Project::INTERNAL, group: group)
end
it 'forks counts are correct' do
@ -86,7 +86,7 @@ RSpec.describe Projects::ForksController do
context 'when fork is private' do
before do
forked_project.update(visibility_level: Project::PRIVATE, group: group)
forked_project.update!(visibility_level: Project::PRIVATE, group: group)
end
shared_examples 'forks counts' do

View file

@ -31,7 +31,7 @@ RSpec.describe Projects::GroupLinksController do
context 'when project is not allowed to be shared with a group' do
before do
group.update(share_with_group_lock: false)
group.update!(share_with_group_lock: false)
end
include_context 'link project to group'

View file

@ -47,7 +47,7 @@ RSpec.describe Projects::ImportsController do
context 'when import is in progress' do
before do
import_state.update(status: :started)
import_state.update!(status: :started)
end
it 'renders template' do
@ -65,7 +65,7 @@ RSpec.describe Projects::ImportsController do
context 'when import failed' do
before do
import_state.update(status: :failed)
import_state.update!(status: :failed)
end
it 'redirects to new_namespace_project_import_path' do
@ -77,7 +77,7 @@ RSpec.describe Projects::ImportsController do
context 'when import finished' do
before do
import_state.update(status: :finished)
import_state.update!(status: :finished)
end
context 'when project is a fork' do
@ -126,7 +126,7 @@ RSpec.describe Projects::ImportsController do
context 'when import never happened' do
before do
import_state.update(status: :none)
import_state.update!(status: :none)
end
it 'redirects to namespace_project_path' do

View file

@ -44,7 +44,7 @@ RSpec.describe Projects::IssuesController do
let_it_be(:issue) { create(:issue, project: new_project) }
before do
project.route.destroy
project.route.destroy!
new_project.redirect_routes.create!(path: project.full_path)
new_project.add_developer(user)
end
@ -711,7 +711,7 @@ RSpec.describe Projects::IssuesController do
issue.update!(last_edited_by: deleted_user, last_edited_at: Time.current)
deleted_user.destroy
deleted_user.destroy!
sign_in(user)
end
@ -1064,10 +1064,10 @@ RSpec.describe Projects::IssuesController do
labels = create_list(:label, 10, project: project).map(&:to_reference)
issue = create(:issue, project: project, description: 'Test issue')
control_count = ActiveRecord::QueryRecorder.new { issue.update(description: [issue.description, label].join(' ')) }.count
control_count = ActiveRecord::QueryRecorder.new { issue.update!(description: [issue.description, label].join(' ')) }.count
# Follow-up to get rid of this `2 * label.count` requirement: https://gitlab.com/gitlab-org/gitlab-foss/issues/52230
expect { issue.update(description: [issue.description, labels].join(' ')) }
expect { issue.update!(description: [issue.description, labels].join(' ')) }
.not_to exceed_query_limit(control_count + 2 * labels.count)
end
@ -1923,7 +1923,7 @@ RSpec.describe Projects::IssuesController do
before do
sign_in(user)
project.route.destroy
project.route.destroy!
new_project.redirect_routes.create!(path: project.full_path)
new_project.add_developer(user)
end

View file

@ -65,7 +65,7 @@ RSpec.describe Projects::LabelsController do
end
it 'does not include group labels when project does not belong to a group' do
project.update(namespace: create(:namespace))
project.update!(namespace: create(:namespace))
list_labels
@ -221,7 +221,7 @@ RSpec.describe Projects::LabelsController do
end
context 'when requesting a redirected path' do
let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
let_it_be(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
it 'redirects to the canonical path' do
get :index, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }
@ -267,7 +267,7 @@ RSpec.describe Projects::LabelsController do
end
context 'when requesting a redirected path' do
let_it_be(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
let_it_be(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
it 'returns not found' do
post :generate, params: { namespace_id: project.namespace, project_id: project.to_param + 'old' }

View file

@ -105,7 +105,7 @@ RSpec.describe Projects::MilestonesController do
context 'with a single group ancestor' do
before do
project.update(namespace: group)
project.update!(namespace: group)
get :index, params: { namespace_id: project.namespace.id, project_id: project.id }, format: :json
end
@ -122,7 +122,7 @@ RSpec.describe Projects::MilestonesController do
let!(:subgroup_milestone) { create(:milestone, group: subgroup) }
before do
project.update(namespace: subgroup)
project.update!(namespace: subgroup)
get :index, params: { namespace_id: project.namespace.id, project_id: project.id }, format: :json
end
@ -158,7 +158,7 @@ RSpec.describe Projects::MilestonesController do
let(:group) { create(:group) }
before do
project.update(namespace: group)
project.update!(namespace: group)
end
context 'when user does not have permission to promote milestone' do
@ -234,7 +234,7 @@ RSpec.describe Projects::MilestonesController do
end
it 'renders 404' do
project.update(namespace: user.namespace)
project.update!(namespace: user.namespace)
post :promote, params: { namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid }
@ -253,7 +253,7 @@ RSpec.describe Projects::MilestonesController do
before do
project.add_guest(guest_user)
sign_in(guest_user)
issue.update(assignee_ids: issue_assignee.id)
issue.update!(assignee_ids: issue_assignee.id)
end
context "when issue is not confidential" do
@ -269,7 +269,7 @@ RSpec.describe Projects::MilestonesController do
context "when issue is confidential" do
before do
issue.update(confidential: true)
issue.update!(confidential: true)
end
it 'shows no milestone participants' do

View file

@ -334,7 +334,7 @@ RSpec.describe Projects::NotesController do
before do
project.update_attribute(:visibility_level, project_visibility)
project.project_feature.update(merge_requests_access_level: merge_requests_access_level)
project.project_feature.update!(merge_requests_access_level: merge_requests_access_level)
sign_in(user)
end
@ -917,7 +917,7 @@ RSpec.describe Projects::NotesController do
context "when the note is not resolvable" do
before do
note.update(system: true)
note.update!(system: true)
end
it "returns status 404" do
@ -980,7 +980,7 @@ RSpec.describe Projects::NotesController do
context "when the note is not resolvable" do
before do
note.update(system: true)
note.update!(system: true)
end
it "returns status 404" do

View file

@ -13,7 +13,7 @@ RSpec.describe Projects::PipelinesController do
allow(Sidekiq.logger).to receive(:info)
stub_not_protect_default_branch
project.add_developer(user)
project.project_feature.update(builds_access_level: feature)
project.project_feature.update!(builds_access_level: feature)
sign_in(user)
end
@ -702,7 +702,7 @@ RSpec.describe Projects::PipelinesController do
before do
project.add_developer(user)
project.project_feature.update(builds_access_level: feature)
project.project_feature.update!(builds_access_level: feature)
end
context 'with a valid .gitlab-ci.yml file' do
@ -777,7 +777,7 @@ RSpec.describe Projects::PipelinesController do
before do
project.add_developer(user)
project.project_feature.update(builds_access_level: feature)
project.project_feature.update!(builds_access_level: feature)
end
context 'with a valid .gitlab-ci.yml file' do

View file

@ -62,7 +62,7 @@ RSpec.describe Projects::Releases::EvidencesController do
context 'when the release was created before evidence existed' do
before do
evidence.destroy
evidence.destroy!
end
it_behaves_like 'not found'

View file

@ -46,7 +46,7 @@ RSpec.describe Projects::RunnersController do
describe '#resume' do
it 'marks the runner as active and ticks the queue' do
runner.update(active: false)
runner.update!(active: false)
expect do
post :resume, params: params
@ -61,7 +61,7 @@ RSpec.describe Projects::RunnersController do
describe '#pause' do
it 'marks the runner as inactive and ticks the queue' do
runner.update(active: true)
runner.update!(active: true)
expect do
post :pause, params: params

View file

@ -170,7 +170,7 @@ RSpec.describe Projects::StarrersController do
context 'when project is private' do
before do
project.update(visibility_level: Project::PRIVATE)
project.update!(visibility_level: Project::PRIVATE)
end
it 'starrers are not visible for non logged in users' do

View file

@ -29,7 +29,7 @@ RSpec.describe Projects::UploadsController do
let!(:upload) { create(:upload, :issuable_upload, :with_file, model: model) }
let(:project) { model }
let(:upload_path) { File.basename(upload.path) }
let!(:redirect_route) { project.redirect_routes.create(path: project.full_path + 'old') }
let!(:redirect_route) { project.redirect_routes.create!(path: project.full_path + 'old') }
it 'redirects to a file with the proper extension' do
get :show, params: { namespace_id: project.namespace, project_id: project.to_param + 'old', filename: File.basename(upload.path), secret: upload.secret }

View file

@ -16,6 +16,10 @@ FactoryBot.define do
status { :processing }
end
trait :error do
status { :error }
end
factory :maven_package do
maven_metadatum

View file

@ -285,8 +285,16 @@ RSpec.describe "Admin Runners" do
end
describe 'runner page breadcrumbs' do
it 'contains the current runners short sha' do
expect(page.find('h2')).to have_content(runner.short_sha)
it 'contains the current runner token' do
page.within '[data-testid="breadcrumb-links"]' do
expect(page.find('h2')).to have_content(runner.short_sha)
end
end
end
describe 'runner page title' do
it 'contains the runner id' do
expect(find('.page-title')).to have_content("Runner ##{runner.id}")
end
end

View file

@ -12,10 +12,10 @@ RSpec.describe 'Disable individual triggers', :js do
end
context 'service has multiple supported events' do
let(:service_name) { 'HipChat' }
let(:service_name) { 'Jenkins CI' }
it 'shows trigger checkboxes' do
event_count = HipchatService.supported_events.count
event_count = JenkinsService.supported_events.count
expect(page).to have_content "Trigger"
expect(page).to have_css(checkbox_selector, visible: :all, count: event_count)

View file

@ -1,40 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'User activates HipChat', :js do
include_context 'project service activation'
context 'with standard settings' do
before do
stub_request(:post, /.*api.hipchat.com.*/)
end
it 'activates service' do
visit_project_integration('HipChat')
fill_in('Room', with: 'gitlab')
fill_in('Token', with: 'verySecret')
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('HipChat settings saved and active.')
end
end
context 'with custom settings' do
before do
stub_request(:post, /.*chat.example.com.*/)
end
it 'activates service' do
visit_project_integration('HipChat')
fill_in('Room', with: 'gitlab_custom')
fill_in('Token', with: 'secretCustom')
fill_in('Server', with: 'https://chat.example.com')
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('HipChat settings saved and active.')
end
end
end

View file

@ -10,7 +10,7 @@ RSpec.describe 'User views services' do
expect(page).to have_content('Integrations')
expect(page).to have_content('Campfire')
expect(page).to have_content('HipChat')
expect(page).to have_content('Jira')
expect(page).to have_content('Assembla')
expect(page).to have_content('Pushover')
expect(page).to have_content('Atlassian Bamboo')

View file

@ -29,7 +29,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
>
<markdown-header-stub
linecontent=""
suggestionstartindex="0"
suggestionstartindex="-1"
/>
<div

View file

@ -3,6 +3,7 @@ import { mockLabels } from 'jest/vue_shared/components/sidebar/labels_select_vue
import Api from '~/api';
import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
import BranchToken from '~/vue_shared/components/filtered_search_bar/tokens/branch_token.vue';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
@ -59,6 +60,16 @@ export const mockMilestones = [
mockEscapedMilestone,
];
export const mockEmoji1 = {
name: 'thumbsup',
};
export const mockEmoji2 = {
name: 'star',
};
export const mockEmojis = [mockEmoji1, mockEmoji2];
export const mockBranchToken = {
type: 'source_branch',
icon: 'branch',
@ -103,6 +114,16 @@ export const mockMilestoneToken = {
fetchMilestones: () => Promise.resolve({ data: mockMilestones }),
};
export const mockReactionEmojiToken = {
type: 'my_reaction_emoji',
icon: 'thumb-up',
title: 'My-Reaction',
unique: true,
token: EmojiToken,
operators: [{ value: '=', description: 'is', default: 'true' }],
fetchEmojis: () => Promise.resolve(mockEmojis),
};
export const mockMembershipToken = {
type: 'with_inherited_permissions',
icon: 'group',

View file

@ -0,0 +1,217 @@
import {
GlFilteredSearchToken,
GlFilteredSearchSuggestion,
GlFilteredSearchTokenSegment,
GlDropdownDivider,
} from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
DEFAULT_LABEL_NONE,
DEFAULT_LABEL_ANY,
} from '~/vue_shared/components/filtered_search_bar/constants';
import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
import { mockReactionEmojiToken, mockEmojis } from '../mock_data';
jest.mock('~/flash');
const GlEmoji = { template: '<img/>' };
const defaultStubs = {
Portal: true,
GlFilteredSearchSuggestionList: {
template: '<div></div>',
methods: {
getValue: () => '=',
},
},
GlEmoji,
};
function createComponent(options = {}) {
const {
config = mockReactionEmojiToken,
value = { data: '' },
active = false,
stubs = defaultStubs,
} = options;
return mount(EmojiToken, {
propsData: {
config,
value,
active,
},
provide: {
portalName: 'fake target',
alignSuggestions: function fakeAlignSuggestions() {},
suggestionsListClass: 'custom-class',
},
stubs,
});
}
describe('EmojiToken', () => {
let mock;
let wrapper;
beforeEach(() => {
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
wrapper.destroy();
});
describe('computed', () => {
beforeEach(async () => {
wrapper = createComponent({ value: { data: mockEmojis[0].name } });
wrapper.setData({
emojis: mockEmojis,
});
await wrapper.vm.$nextTick();
});
describe('currentValue', () => {
it('returns lowercase string for `value.data`', () => {
expect(wrapper.vm.currentValue).toBe(mockEmojis[0].name);
});
});
describe('activeEmoji', () => {
it('returns object for currently present `value.data`', () => {
expect(wrapper.vm.activeEmoji).toEqual(mockEmojis[0]);
});
});
});
describe('methods', () => {
beforeEach(() => {
wrapper = createComponent();
});
describe('fetchEmojiBySearchTerm', () => {
it('calls `config.fetchEmojis` with provided searchTerm param', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis');
wrapper.vm.fetchEmojiBySearchTerm('foo');
expect(wrapper.vm.config.fetchEmojis).toHaveBeenCalledWith('foo');
});
it('sets response to `emojis` when request is successful', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockResolvedValue(mockEmojis);
wrapper.vm.fetchEmojiBySearchTerm('foo');
return waitForPromises().then(() => {
expect(wrapper.vm.emojis).toEqual(mockEmojis);
});
});
it('calls `createFlash` with flash error message when request fails', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
wrapper.vm.fetchEmojiBySearchTerm('foo');
return waitForPromises().then(() => {
expect(createFlash).toHaveBeenCalledWith('There was a problem fetching emojis.');
});
});
it('sets `loading` to false when request completes', () => {
jest.spyOn(wrapper.vm.config, 'fetchEmojis').mockRejectedValue({});
wrapper.vm.fetchEmojiBySearchTerm('foo');
return waitForPromises().then(() => {
expect(wrapper.vm.loading).toBe(false);
});
});
});
});
describe('template', () => {
const defaultEmojis = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
beforeEach(async () => {
wrapper = createComponent({
value: { data: `"${mockEmojis[0].name}"` },
});
wrapper.setData({
emojis: mockEmojis,
});
await wrapper.vm.$nextTick();
});
it('renders gl-filtered-search-token component', () => {
expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true);
});
it('renders token item when value is selected', () => {
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
expect(tokenSegments).toHaveLength(3); // My Reaction, =, "thumbsup"
expect(tokenSegments.at(2).find(GlEmoji).attributes('data-name')).toEqual('thumbsup');
});
it('renders provided defaultEmojis as suggestions', async () => {
wrapper = createComponent({
active: true,
config: { ...mockReactionEmojiToken, defaultEmojis },
stubs: { Portal: true, GlEmoji },
});
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
suggestionsSegment.vm.$emit('activate');
await wrapper.vm.$nextTick();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
expect(suggestions).toHaveLength(defaultEmojis.length);
defaultEmojis.forEach((emoji, index) => {
expect(suggestions.at(index).text()).toBe(emoji.text);
});
});
it('does not render divider when no defaultEmojis', async () => {
wrapper = createComponent({
active: true,
config: { ...mockReactionEmojiToken, defaultEmojis: [] },
stubs: { Portal: true, GlEmoji },
});
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
suggestionsSegment.vm.$emit('activate');
await wrapper.vm.$nextTick();
expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false);
expect(wrapper.find(GlDropdownDivider).exists()).toBe(false);
});
it('renders `DEFAULT_LABEL_NONE` and `DEFAULT_LABEL_ANY` as default suggestions', async () => {
wrapper = createComponent({
active: true,
config: { ...mockReactionEmojiToken },
stubs: { Portal: true, GlEmoji },
});
const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment);
const suggestionsSegment = tokenSegments.at(2);
suggestionsSegment.vm.$emit('activate');
await wrapper.vm.$nextTick();
const suggestions = wrapper.findAll(GlFilteredSearchSuggestion);
expect(suggestions).toHaveLength(2);
expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_NONE.text);
expect(suggestions.at(1).text()).toBe(DEFAULT_LABEL_ANY.text);
});
});
});

View file

@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['Milestone'] do
it 'has the expected fields' do
expected_fields = %w[
id title description state web_path
id iid title description state web_path
due_date start_date created_at updated_at
project_milestone group_milestone subgroup_milestone
stats

View file

@ -44,7 +44,10 @@ RSpec.describe Gitlab::Usage::Metrics::NamesSuggestions::Generator do
# ::Deployment.arel_table[:environment_id]
# )
let(:key_path) { 'counts.ingress_modsecurity_logging' }
let(:name_suggestion) { /count_distinct_environment_id_from_<adjective describing\: '\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'>_deployments_<with>_clusters_<having>_clusters_applications_ingress/ }
let(:name_suggestion) do
constrains = /'\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'/
/count_distinct_environment_id_from_<adjective describing\: #{constrains}>_deployments_<with>_<adjective describing\: #{constrains}>_clusters_<having>_<adjective describing\: #{constrains}>_clusters_applications_ingress/
end
end
end

View file

@ -5,7 +5,7 @@ require 'spec_helper'
# If this spec fails, we need to add the new code review event to the correct aggregated metric
RSpec.describe 'Code review events' do
it 'the aggregated metrics contain all the code review metrics' do
path = Rails.root.join('lib/gitlab/usage_data_counters/aggregated_metrics/code_review.yml')
path = Rails.root.join('config/metrics/aggregates/code_review.yml')
aggregated_events = YAML.safe_load(File.read(path), aliases: true)&.map(&:with_indifferent_access)
code_review_aggregated_events = aggregated_events

View file

@ -3,9 +3,10 @@
require 'spec_helper'
RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_gitlab_redis_shared_state do
let(:user1) { build(:user, id: 1) }
let(:user2) { build(:user, id: 2) }
let(:user3) { build(:user, id: 3) }
let_it_be(:user1) { build(:user, id: 1) }
let_it_be(:user2) { build(:user, id: 2) }
let_it_be(:user3) { build(:user, id: 3) }
let(:time) { Time.zone.now }
context 'for Issue title edit actions' do
@ -272,10 +273,13 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
described_class.track_issue_title_changed_action(author: user1)
described_class.track_issue_description_changed_action(author: user1)
described_class.track_issue_assignee_changed_action(author: user1)
described_class.track_issue_title_changed_action(author: user2, time: time - 2.days)
described_class.track_issue_title_changed_action(author: user3, time: time - 3.days)
described_class.track_issue_description_changed_action(author: user3, time: time - 3.days)
described_class.track_issue_assignee_changed_action(author: user3, time: time - 3.days)
travel_to(2.days.ago) do
described_class.track_issue_title_changed_action(author: user2)
described_class.track_issue_title_changed_action(author: user3)
described_class.track_issue_description_changed_action(author: user3)
described_class.track_issue_assignee_changed_action(author: user3)
end
events = Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(described_class::ISSUE_CATEGORY)
today_count = Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: events, start_date: time, end_date: time)

View file

@ -649,10 +649,12 @@ RSpec.describe Packages::Package, type: :model do
describe '.displayable' do
let_it_be(:hidden_package) { create(:maven_package, :hidden) }
let_it_be(:processing_package) { create(:maven_package, :processing) }
let_it_be(:error_package) { create(:maven_package, :error) }
subject { described_class.displayable }
it 'does not include hidden packages', :aggregate_failures do
it 'does not include non-displayable packages', :aggregate_failures do
is_expected.to include(error_package)
is_expected.not_to include(hidden_package)
is_expected.not_to include(processing_package)
end

View file

@ -4,10 +4,10 @@ require 'spec_helper'
RSpec.describe Packages::Rubygems::CreateGemspecService do
include RubygemsHelpers
let_it_be(:package) { create(:rubygems_package) }
let_it_be(:package_file) { create(:package_file, :gem) }
let_it_be(:gem) { gem_from_file(package_file.file) }
let_it_be(:gemspec) { gem.spec }
let_it_be(:package) { package_file.package }
let(:service) { described_class.new(package, gemspec) }

View file

@ -90,7 +90,7 @@ RSpec.describe Submodules::UpdateService do
let(:submodule) { '../six' }
it_behaves_like 'returns error result' do
let(:error_message) { 'Invalid parameters' }
let(:error_message) { 'Invalid submodule path' }
end
end
end

View file

@ -14,10 +14,6 @@ RSpec.shared_examples 'a daily tracked issuable event' do
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user1)).to be_truthy
expect(track_action(author: user2)).to be_truthy
expect(track_action(author: user3, time: time - 3.days)).to be_truthy
expect(count_unique(date_from: time, date_to: time)).to eq(2)
expect(count_unique(date_from: time - 5.days, date_to: 1.day.since(time))).to eq(3)
end
end