Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-21 18:10:22 +00:00
parent 48e30ea32f
commit 2c87a975e3
62 changed files with 1423 additions and 97 deletions

View File

@ -99,7 +99,7 @@ rules:
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.'
overrides:
- files:
- '{,ee/,jh/}spec/frontend*/**/*'
- '{,ee/,jh/}spec/frontend*/**/*'
rules:
'@gitlab/require-i18n-strings': off
'@gitlab/no-runtime-template-compiler': off
@ -139,11 +139,13 @@ overrides:
parser: '@graphql-eslint/eslint-plugin'
operations:
- '{,ee/,jh/}app/**/*.graphql'
# You can run `bundle exec rake gitlab:graphql:schema:dump` and then uncomment this line
# schema: './tmp/tests/graphql/gitlab_schema.graphql'
# You can run `bundle exec rake gitlab:graphql:schema:dump` and then uncomment this line
# schema: './tmp/tests/graphql/gitlab_schema.graphql'
rules:
filenames/match-regex: off
spaced-comment: off
# TODO: We need a way to include this rule + support ee_else_ce fragments
#'@graphql-eslint/unique-fragment-name': error
# TODO: Uncomment these rules when then `schema` is available
#'@graphql-eslint/fragments-on-composite-type': error
#'@graphql-eslint/known-argument-names': error
@ -151,4 +153,3 @@ overrides:
'@graphql-eslint/no-anonymous-operations': error
'@graphql-eslint/unique-operation-name': error
'@graphql-eslint/require-id-when-available': error
'@graphql-eslint/unique-fragment-name': error

View File

@ -5,11 +5,11 @@
/milestone %
/assign `@EM/PM` (choose the DRI; remove backticks here, and below)
**Be sure to link this MR to the relevant issue(s).**
**Be sure to link this MR to the relevant issues.**
- Deprecation issue:
- Removal issue:
- MR that removes the feature (optional):
- MR that removed (or _will_ remove) the feature:
If there is no relevant deprecation issue, hit pause and:
@ -45,6 +45,7 @@ Please review the [guidelines for removals](https://about.gitlab.com/handbook/ma
- [ ] Follow the process to [create a removal YAML file](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-removal-entry).
- [ ] Add reviewers by the 10th.
- [ ] When ready to be merged and not later than the 15th, add the ~ready label and @ message the TW for final review and merge.
- Removal notices should not be merged before the code is removed from the product. Do not mark ~ready until the removal is complete, or you are certain it will be completed within the current milestone and released. If PMs are not sure, they should confirm with their Engineering Manager.
## Reviewers
@ -66,6 +67,8 @@ with the same process as regular docs MRs. Add suggestions as needed, @ message
the PM to inform them the first review is complete, and remove
yourself as a reviewer if it's not yet ready for merge.
**Removal notices should not be merged before the code is removed from the product.**
<details>
<summary>Expand for Details</summary>

View File

@ -1 +1 @@
53768dae0bc6395d2b80438286723a912aa50921
1b1b95408d11a2532db5a44ffefd5cbab6e0effd

View File

@ -1,5 +1,3 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment HttpIntegrationPayloadData on AlertManagementHttpIntegration {
id
}

View File

@ -1,5 +1,3 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment BoardFragment on Board {
id
name

View File

@ -1,7 +1,5 @@
#import "./board_list_shared.fragment.graphql"
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment BoardListFragment on BoardList {
...BoardListShared
}

View File

@ -1,5 +1,3 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment BoardScopeFragment on Board {
id
name

View File

@ -1,7 +1,5 @@
#import "~/graphql_shared/fragments/issue.fragment.graphql"
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment Issue on Issue {
id
...IssueNode

View File

@ -1,4 +1,4 @@
fragment IterationShared on Iteration {
fragment Iteration on Iteration {
id
title
}

View File

@ -0,0 +1,4 @@
fragment PageInfo on PageInfo {
startCursor
endCursor
}

View File

@ -1,5 +1,4 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/require-id-when-available, @graphql-eslint/unique-fragment-name
# eslint-disable-next-line @graphql-eslint/require-id-when-available
fragment IncidentFields on Issue {
severity
escalationStatus

View File

@ -0,0 +1,6 @@
fragment Label on Label {
id
color
textColor
title
}

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/label.fragment.graphql"
#import "./label.fragment.graphql"
query searchLabels($fullPath: ID!, $search: String, $isProject: Boolean = false) {
group(fullPath: $fullPath) @skip(if: $isProject) {

View File

@ -1,4 +1,4 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
#import "./user.fragment.graphql"
query searchUsers($fullPath: ID!, $search: String, $isProject: Boolean = false) {
group(fullPath: $fullPath) @skip(if: $isProject) {

View File

@ -0,0 +1,6 @@
fragment User on User {
id
avatarUrl
name
username
}

View File

@ -1,5 +1,3 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment TreeEntryCommit on LogTreeCommit {
sha
message

View File

@ -1,5 +1,3 @@
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment ProjectPathLocksFragment on Project {
id
}

View File

@ -1,7 +1,5 @@
#import "./runner_details_shared.fragment.graphql"
# This fragment is used as ee-else-ce
# eslint-disable-next-line @graphql-eslint/unique-fragment-name
fragment RunnerDetails on CiRunner {
...RunnerDetailsShared
}

View File

@ -12,7 +12,7 @@ query groupMilestones($fullPath: ID!, $title: String, $state: MilestoneStateEnum
includeAncestors: true
) {
nodes {
...SidebarMilestoneFragment
...MilestoneFragment
state
}
}

View File

@ -8,7 +8,7 @@ query mergeRequestMilestone($fullPath: ID!, $iid: String!) {
__typename
id
attribute: milestone {
...SidebarMilestoneFragment
...MilestoneFragment
}
}
}

View File

@ -1,4 +1,4 @@
fragment SidebarMilestoneFragment on Milestone {
fragment MilestoneFragment on Milestone {
id
title
webUrl: webPath

View File

@ -8,7 +8,7 @@ query projectIssueMilestone($fullPath: ID!, $iid: String!) {
__typename
id
attribute: milestone {
...SidebarMilestoneFragment
...MilestoneFragment
}
}
}

View File

@ -12,7 +12,7 @@ query projectMilestones($fullPath: ID!, $title: String, $state: MilestoneStateEn
includeAncestors: true
) {
nodes {
...SidebarMilestoneFragment
...MilestoneFragment
state
}
}

View File

@ -51,7 +51,8 @@ class Admin::TopicsController < Admin::ApplicationController
[
:avatar,
:description,
:name
:name,
:title
]
end
end

View File

@ -1,4 +1,4 @@
fragment LocalPageInfo on PageInfo {
fragment PageInfo on PageInfo {
__typename
hasNextPage
hasPreviousPage
@ -6,7 +6,7 @@ fragment LocalPageInfo on PageInfo {
endCursor
}
fragment LocalTreeEntry on Entry {
fragment TreeEntry on Entry {
__typename
id
sha
@ -34,12 +34,12 @@ query getFiles(
edges {
__typename
node {
...LocalTreeEntry
...TreeEntry
webPath
}
}
pageInfo {
...LocalPageInfo
...PageInfo
}
}
submodules(first: $pageSize, after: $nextPageCursor) {
@ -47,13 +47,13 @@ query getFiles(
edges {
__typename
node {
...LocalTreeEntry
...TreeEntry
webUrl
treeUrl
}
}
pageInfo {
...LocalPageInfo
...PageInfo
}
}
blobs(first: $pageSize, after: $nextPageCursor) {
@ -61,14 +61,14 @@ query getFiles(
edges {
__typename
node {
...LocalTreeEntry
...TreeEntry
mode
webPath
lfsOid
}
}
pageInfo {
...LocalPageInfo
...PageInfo
}
}
}

View File

@ -2903,7 +2903,7 @@ class Project < ApplicationRecord
if @topic_list != self.topic_list
self.topics.delete_all
self.topics = @topic_list.map do |topic|
Projects::Topic.where('lower(name) = ?', topic.downcase).order(total_projects_count: :desc).first_or_create(name: topic)
Projects::Topic.where('lower(name) = ?', topic.downcase).order(total_projects_count: :desc).first_or_create(name: topic, title: topic)
end
end

View File

@ -9,6 +9,7 @@ module Projects
validates :name, presence: true, length: { maximum: 255 }
validates :name, uniqueness: { case_sensitive: false }, if: :name_changed?
validates :title, presence: true, length: { maximum: 255 }, on: :create
validates :description, length: { maximum: 1024 }
has_many :project_topics, class_name: 'Projects::ProjectTopic'

View File

@ -3,12 +3,19 @@
.form-group
= f.label :name do
= _("Topic name")
= f.text_field :name, placeholder: _('My topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_name_field' },
= _("Topic slug (name)")
= f.text_field :name, placeholder: _('my-topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_name_field' },
required: true,
title: _('Please fill in a name for your topic.'),
autofocus: true
.form-group
= f.label :title do
= _("Topic title")
= f.text_field :title, placeholder: _('My topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_title_field' },
required: true,
title: _('Please fill in a title for your topic.')
.form-group
= f.label :description, _("Description")
= render layout: 'shared/md_preview', locals: { url: preview_markdown_admin_topics_path, referenced_users: false } do

View File

@ -1,4 +1,5 @@
- topic = local_assigns.fetch(:topic)
- title = topic.title || topic.name
%li.topic-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'topic_row_content' } }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
@ -6,7 +7,9 @@
.gl-min-w-0.gl-flex-grow-1
.title
= link_to topic.name, topic_explore_projects_path(topic_name: topic.name)
= link_to title, topic_explore_projects_path(topic_name: topic.name)
%div
= topic.name
.stats.gl-text-gray-500.gl-flex-shrink-0.gl-display-none.gl-sm-display-flex
%span.gl-ml-5.has-tooltip{ title: n_('%d project', '%d projects', topic.total_projects_count) % topic.total_projects_count }

View File

@ -6,7 +6,7 @@
breaking_change: true
reporter: NicoleSchwartz
body: |
In GitLab 15.0, for Dependency Scanning, the default version of Java will be updated to 17. This is the same as [the most up-to-date Long Term Support (LTS) version](https://en.wikipedia.org/wiki/Java_version_history). GitLab still [supports the same versions as it does today (8, 11, 13, 14, 15, 16, 17)](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning). Only the enabled default is changing. To change the default, set the `DS_Java_Version` variable.
In GitLab 15.0, for Dependency Scanning, the default version of Java that the scanner expects will be updated from 11 to 17. Java 17 is [the most up-to-date Long Term Support (LTS) version](https://en.wikipedia.org/wiki/Java_version_history). Dependency scanning continues to support the same [range of versions (8, 11, 13, 14, 15, 16, 17)](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#supported-languages-and-package-managers), only the default version is changing. If your project uses the previous default of Java 11, be sure to [set the `DS_Java_Version` variable to match](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning).
# The following items are not published on the docs page, but may be used in the future.
stage: secure # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: ultimate # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]

View File

@ -0,0 +1,9 @@
---
table_name: group_features
classes:
- Groups::FeatureSetting
feature_categories:
- subgroups
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82017
milestone: '14.10'

View File

@ -0,0 +1,9 @@
---
table_name: project_build_artifacts_size_refreshes
classes:
- Projects::BuildArtifactsSizeRefresh
feature_categories:
- build_artifacts
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81306
milestone: '14.9'

View File

@ -0,0 +1,9 @@
---
table_name: protected_environment_approval_rules
classes:
- ProtectedEnvironments::ApprovalRule
feature_categories:
- continuous_delivery
description: TODO
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82800
milestone: '14.10'

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
class AddTitleToTopic < Gitlab::Database::Migration[1.0]
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20220331130726_add_text_limit_to_topics_title.rb
def change
add_column :topics, :title, :text
end
# rubocop:enable Migration/AddLimitToTextColumns
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddTextLimitToTopicsTitle < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
def up
add_text_limit :topics, :title, 255
end
def down
remove_text_limit :topics, :title
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class ScheduleBackfillTopicsTitle < Gitlab::Database::Migration[1.0]
MIGRATION = 'BackfillTopicsTitle'
DELAY_INTERVAL = 2.minutes
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('topics'),
MIGRATION,
DELAY_INTERVAL,
track_jobs: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
a7e5490e9b53cbbed01c03690cbe0bb4668adb17ec4fe14ca96e021f2e313b38

View File

@ -0,0 +1 @@
bcaf6139100dc5658d33292e8e5484d1d6278f022eeb6e3bcd519efdccdf4470

View File

@ -0,0 +1 @@
0e96430b245f6f04447ee50b6e0c0b9d7828cfeaf1f08e303aa04bb40a117a7f

View File

@ -21050,6 +21050,8 @@ CREATE TABLE topics (
description text,
total_projects_count bigint DEFAULT 0 NOT NULL,
non_private_projects_count bigint DEFAULT 0 NOT NULL,
title text,
CONSTRAINT check_223b50f9be CHECK ((char_length(title) <= 255)),
CONSTRAINT check_26753fb43a CHECK ((char_length(avatar) <= 255)),
CONSTRAINT check_5d1a07c8c8 CHECK ((char_length(description) <= 1024)),
CONSTRAINT check_7a90d4c757 CHECK ((char_length(name) <= 255))

View File

@ -1422,7 +1422,7 @@ Supported attributes:
| `emails_disabled` | boolean | **{dotted-circle}** No | Disable email notifications. |
| `external_authorization_classification_label` **(PREMIUM)** | string | **{dotted-circle}** No | The classification label for the project. |
| `forking_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `import_url` | string | **{dotted-circle}** No | URL to import repository from. |
| `import_url` | string | **{dotted-circle}** No | URL the repository was imported from. |
| `issues_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `issues_enabled` | boolean | **{dotted-circle}** No | _(Deprecated)_ Enable issues for this project. Use `issues_access_level` instead. |
| `issues_template` **(PREMIUM)** | string | **{dotted-circle}** No | Default description for Issues. Description is parsed with GitLab Flavored Markdown. See [Templates for issues and merge requests](#templates-for-issues-and-merge-requests). |

View File

@ -38,21 +38,24 @@ Example response:
[
{
"id": 1,
"name": "GitLab",
"name": "gitlab",
"title": "GitLab",
"description": "GitLab is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more.",
"total_projects_count": 1000,
"avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon"
},
{
"id": 3,
"name": "Git",
"name": "git",
"title": "Git",
"description": "Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.",
"total_projects_count": 900,
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon"
},
{
"id": 2,
"name": "Git LFS",
"name": "git-lfs",
"title": "Git LFS",
"description": null,
"total_projects_count": 300,
"avatar_url": null
@ -85,7 +88,8 @@ Example response:
```json
{
"id": 1,
"name": "GitLab",
"name": "gitlab",
"title": "GitLab",
"description": "GitLab is an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more.",
"total_projects_count": 1000,
"avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon"
@ -112,7 +116,8 @@ Supported attributes:
| Attribute | Type | Required | Description |
| ------------- | ------- | ---------------------- | ----------- |
| `name` | string | **{check-circle}** Yes | Name |
| `name` | string | **{check-circle}** Yes | Slug (name) |
| `title` | string | **{check-circle}** Yes | Title |
| `avatar` | file | **{dotted-circle}** No | Avatar |
| `description` | string | **{dotted-circle}** No | Description |
@ -120,7 +125,7 @@ Example request:
```shell
curl --request POST \
--data "name=topic1" \
--data "name=topic1&title=Topic 1" \
--header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/topics"
```
@ -131,6 +136,7 @@ Example response:
{
"id": 1,
"name": "topic1",
"title": "Topic 1",
"description": null,
"total_projects_count": 0,
"avatar_url": null
@ -152,7 +158,8 @@ Supported attributes:
| `id` | integer | **{check-circle}** Yes | ID of project topic |
| `avatar` | file | **{dotted-circle}** No | Avatar |
| `description` | string | **{dotted-circle}** No | Description |
| `name` | string | **{dotted-circle}** No | Name |
| `name` | string | **{dotted-circle}** No | Slug (name) |
| `title` | string | **{dotted-circle}** No | Title |
Example request:
@ -169,6 +176,7 @@ Example response:
{
"id": 1,
"name": "topic1",
"title": "Topic 1",
"description": null,
"total_projects_count": 0,
"avatar_url": null

View File

@ -45,7 +45,7 @@ You can combine entries if they happened in the same release:
## Use a note to describe the state of the feature flag
Information about feature flags should be in a **Note** at the start of the topic (just below the version history).
Information about feature flags should be in a `FLAG` note at the start of the topic (just below the version history).
The note has three parts, and follows this structure:
@ -62,6 +62,7 @@ FLAG:
|--------------------------|---------------|
| Available | `On self-managed GitLab, by default this feature is available. To hide the feature, ask an administrator to [disable the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |
| Unavailable | `On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |
| Available to some users | `On self-managed GitLab, by default this feature is available to a subset of users. To show or hide the feature for all, ask an administrator to [change the status of the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |
| Available, per-group | `On self-managed GitLab, by default this feature is available. To hide the feature per group, ask an administrator to [disable the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |
| Unavailable, per-group | `On self-managed GitLab, by default this feature is not available. To make it available per group, ask an administrator to [enable the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |
| Available, per-project | `On self-managed GitLab, by default this feature is available. To hide the feature per project or for your entire instance, ask an administrator to [disable the feature flag](<path to>/administration/feature_flags.md) named <flag name>.` |

View File

@ -52,7 +52,7 @@ as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#brea
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
In GitLab 15.0, for Dependency Scanning, the default version of Java will be updated to 17. This is the same as [the most up-to-date Long Term Support (LTS) version](https://en.wikipedia.org/wiki/Java_version_history). GitLab still [supports the same versions as it does today (8, 11, 13, 14, 15, 16, 17)](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning). Only the enabled default is changing. To change the default, set the `DS_Java_Version` variable.
In GitLab 15.0, for Dependency Scanning, the default version of Java that the scanner expects will be updated from 11 to 17. Java 17 is [the most up-to-date Long Term Support (LTS) version](https://en.wikipedia.org/wiki/Java_version_history). Dependency scanning continues to support the same [range of versions (8, 11, 13, 14, 15, 16, 17)](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#supported-languages-and-package-managers), only the default version is changing. If your project uses the previous default of Java 11, be sure to [set the `DS_Java_Version` variable to match](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning).
**Planned removal milestone: 15.0 (2021-05-22)**

View File

@ -107,13 +107,9 @@ workflows.
## The GitLab Terraform provider
NOTE:
The GitLab Terraform provider is released separately from GitLab.
We are working on migrating the GitLab Terraform provider to GitLab.com.
The [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab) is a plugin for Terraform to facilitate
managing of GitLab resources such as users, groups, and projects.
Its documentation is available on [Terraform](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs).
The [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab) is a Terraform plugin to facilitate
managing of GitLab resources such as users, groups, and projects. It is released separately from GitLab
and its documentation is available on [Terraform](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs).
## Create a new cluster through IaC

View File

@ -41,7 +41,7 @@ Prerequisites:
To export a project and its data, follow these steps:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Select **Export project**.
1. After the export is generated, you should receive an email with a link to download the file.

View File

@ -6,6 +6,7 @@ module API
class Topic < Grape::Entity
expose :id
expose :name
expose :title
expose :description
expose :total_projects_count
expose :avatar_url do |topic, options|

View File

@ -38,7 +38,8 @@ module API
success Entities::Projects::Topic
end
params do
requires :name, type: String, desc: 'Name'
requires :name, type: String, desc: 'Slug (name)'
requires :title, type: String, desc: 'Title'
optional :description, type: String, desc: 'Description'
optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for topic'
end
@ -60,7 +61,8 @@ module API
end
params do
requires :id, type: Integer, desc: 'ID of project topic'
optional :name, type: String, desc: 'Name'
optional :name, type: String, desc: 'Slug (name)'
optional :title, type: String, desc: 'Title'
optional :description, type: String, desc: 'Description'
optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for topic'
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# The class to backfill the topic title
class BackfillTopicsTitle
# Temporary AR model for topics
class Topic < ActiveRecord::Base
self.table_name = 'topics'
end
def perform(start_id, end_id)
Topic.where(id: start_id..end_id).where(title: nil).update_all('title = name')
mark_job_as_succeeded(start_id, end_id)
end
private
def mark_job_as_succeeded(*arguments)
::Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
self.class.name.demodulize,
arguments
)
end
end
end
end

View File

@ -6,7 +6,7 @@ After=network.target
JoinsNamespaceOf=gitlab-puma.service
[Service]
Type=simple
Type=notify
User=git
WorkingDirectory=/home/git/gitlab
Environment=RAILS_ENV=production
@ -17,6 +17,7 @@ Restart=on-failure
RestartSec=1
SyslogIdentifier=gitlab-sidekiq
Slice=gitlab.slice
WatchdogSec=10
[Install]
WantedBy=gitlab.target

View File

@ -28294,6 +28294,9 @@ msgstr ""
msgid "Please fill in a name for your topic."
msgstr ""
msgid "Please fill in a title for your topic."
msgstr ""
msgid "Please fill out this field."
msgstr ""
@ -39730,7 +39733,10 @@ msgstr ""
msgid "Topic avatar for %{name} will be removed. This cannot be undone."
msgstr ""
msgid "Topic name"
msgid "Topic slug (name)"
msgstr ""
msgid "Topic title"
msgstr ""
msgid "Topic was successfully updated."
@ -45344,6 +45350,9 @@ msgstr ""
msgid "my-channel"
msgstr ""
msgid "my-topic"
msgstr ""
msgid "need attention"
msgstr ""

View File

@ -83,7 +83,7 @@ module Trigger
# Override to trigger and work with pipeline on different GitLab instance
# type: :downstream -> downstream build and pipeline status
# type: :upstream -> this project, e.g. for posting comments
def gitlab_client(type)
def gitlab_client(_type)
# By default, always use the same client
@gitlab_client ||= Gitlab.client(
endpoint: 'https://gitlab.com/api/v4',
@ -97,10 +97,15 @@ module Trigger
end
# Must be overridden
def ref
def ref_param_name
raise NotImplementedError
end
# Can be overridden
def primary_ref
'main'
end
# Can be overridden
def trigger_token
ENV['CI_JOB_TOKEN']
@ -116,6 +121,27 @@ module Trigger
ENV[version_file]&.strip || File.read(version_file).strip
end
# Can be overridden
def trigger_stable_branch_if_detected?
false
end
def stable_branch?
ENV['CI_COMMIT_REF_NAME'] =~ /^[\d-]+-stable(-ee)?$/
end
def fallback_ref
if trigger_stable_branch_if_detected? && stable_branch?
ENV['CI_COMMIT_REF_NAME'].delete_suffix('-ee')
else
primary_ref
end
end
def ref
ENV.fetch(ref_param_name, fallback_ref)
end
def base_variables
# Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results,
# and fallback to CI_COMMIT_SHA for the `detached` pipelines.
@ -146,8 +172,16 @@ module Trigger
ENV.fetch('OMNIBUS_PROJECT_PATH', 'gitlab-org/build/omnibus-gitlab-mirror')
end
def ref
ENV.fetch('OMNIBUS_BRANCH', 'master')
def ref_param_name
'OMNIBUS_BRANCH'
end
def primary_ref
'master'
end
def trigger_stable_branch_if_detected?
true
end
def extra_variables
@ -184,10 +218,16 @@ module Trigger
private
def ref
return ENV['CI_COMMIT_REF_NAME'] if ENV['CI_COMMIT_REF_NAME'] =~ /^[\d-]+-stable(-ee)?$/
def ref_param_name
'CNG_BRANCH'
end
ENV.fetch('CNG_BRANCH', 'master')
def primary_ref
'master'
end
def trigger_stable_branch_if_detected?
true
end
def extra_variables
@ -272,8 +312,8 @@ module Trigger
ENV.fetch('DOCS_PROJECT_PATH', 'gitlab-org/gitlab-docs')
end
def ref
ENV.fetch('DOCS_BRANCH', 'main')
def ref_param_name
'DOCS_BRANCH'
end
# `gitlab-org/gitlab-docs` pipeline trigger "Triggered from gitlab-org/gitlab 'review-docs-deploy' job"
@ -377,8 +417,12 @@ module Trigger
}
end
def ref
ENV['GITLABCOM_DATABASE_TESTING_TRIGGER_REF'] || 'master'
def ref_param_name
'GITLABCOM_DATABASE_TESTING_TRIGGER_REF'
end
def primary_ref
'master'
end
end

View File

@ -77,24 +77,31 @@ RSpec.describe Admin::TopicsController do
describe 'POST #create' do
it 'creates topic' do
expect do
post :create, params: { projects_topic: { name: 'test' } }
post :create, params: { projects_topic: { name: 'test', title: 'Test' } }
end.to change { Projects::Topic.count }.by(1)
end
it 'shows error message for invalid topic' do
post :create, params: { projects_topic: { name: nil } }
it 'shows error message for invalid topic name' do
post :create, params: { projects_topic: { name: nil, title: 'Test' } }
errors = assigns[:topic].errors
expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.blank')))
end
it 'shows error message if topic not unique (case insensitive)' do
post :create, params: { projects_topic: { name: topic.name.upcase } }
it 'shows error message if topic name not unique (case insensitive)' do
post :create, params: { projects_topic: { name: topic.name.upcase, title: topic.title } }
errors = assigns[:topic].errors
expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.taken')))
end
it 'shows error message for invalid topic title' do
post :create, params: { projects_topic: { name: 'test', title: nil } }
errors = assigns[:topic].errors
expect(errors).to contain_exactly(errors.full_message(:title, I18n.t('errors.messages.blank')))
end
context 'as a normal user' do
before do
sign_in(user)

131
spec/db/docs_spec.rb Normal file
View File

@ -0,0 +1,131 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Database Documentation' do
context 'for each table' do
let(:all_tables) do
Gitlab::Database.database_base_models.flat_map { |_, m| m.connection.tables }.sort.uniq
end
let(:metadata_required_fields) do
%i(
feature_categories
table_name
)
end
let(:metadata_allowed_fields) do
metadata_required_fields + %i(
classes
description
introduced_by_url
milestone
)
end
let(:metadata) do
all_tables.each_with_object({}) do |table_name, hash|
next unless File.exist?(table_metadata_file_path(table_name))
hash[table_name] ||= load_table_metadata(table_name)
end
end
let(:tables_without_metadata) do
all_tables.reject { |t| metadata.has_key?(t) }
end
let(:tables_without_valid_metadata) do
metadata.select { |_, t| t.has_key?(:error) }.keys
end
let(:tables_with_disallowed_fields) do
metadata.select { |_, t| t.has_key?(:disallowed_fields) }.keys
end
let(:tables_with_missing_required_fields) do
metadata.select { |_, t| t.has_key?(:missing_required_fields) }.keys
end
it 'has a metadata file' do
expect(tables_without_metadata).to be_empty, multiline_error(
'Missing metadata files',
tables_without_metadata.map { |t| " #{table_metadata_file(t)}" }
)
end
it 'has a valid metadata file' do
expect(tables_without_valid_metadata).to be_empty, table_metadata_errors(
'Table metadata files with errors',
:error,
tables_without_valid_metadata
)
end
it 'has a valid metadata file with allowed fields' do
expect(tables_with_disallowed_fields).to be_empty, table_metadata_errors(
'Table metadata files with disallowed fields',
:disallowed_fields,
tables_with_disallowed_fields
)
end
it 'has a valid metadata file without missing fields' do
expect(tables_with_missing_required_fields).to be_empty, table_metadata_errors(
'Table metadata files with missing fields',
:missing_required_fields,
tables_with_missing_required_fields
)
end
end
private
def table_metadata_file(table_name)
File.join('db', 'docs', "#{table_name}.yml")
end
def table_metadata_file_path(table_name)
Rails.root.join(table_metadata_file(table_name))
end
def load_table_metadata(table_name)
result = {}
begin
result[:metadata] = YAML.safe_load(File.read(table_metadata_file_path(table_name))).deep_symbolize_keys
disallowed_fields = (result[:metadata].keys - metadata_allowed_fields)
unless disallowed_fields.empty?
result[:disallowed_fields] = "fields not allowed: #{disallowed_fields.join(', ')}"
end
missing_required_fields = (metadata_required_fields - result[:metadata].reject { |_, v| v.blank? }.keys)
unless missing_required_fields.empty?
result[:missing_required_fields] = "missing required fields: #{missing_required_fields.join(', ')}"
end
rescue Psych::SyntaxError => ex
result[:error] = ex.message
end
result
end
def table_metadata_errors(title, field, tables)
lines = tables.map do |table_name|
<<~EOM
#{table_metadata_file(table_name)}
#{metadata[table_name][field]}
EOM
end
multiline_error(title, lines)
end
def multiline_error(title, lines)
<<~EOM
#{title}:
#{lines.join("\n")}
EOM
end
end

View File

@ -3,5 +3,6 @@
FactoryBot.define do
factory :topic, class: 'Projects::Topic' do
name { generate(:name) }
title { generate(:title) }
end
end

View File

@ -11,6 +11,7 @@ RSpec.describe API::Entities::Projects::Topic do
expect(subject).to include(
:id,
:name,
:title,
:description,
:total_projects_count,
:avatar_url

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillTopicsTitle, schema: 20220331133802 do
it 'correctly backfills the title of the topics' do
topics = table(:topics)
topic_1 = topics.create!(name: 'topic1')
topic_2 = topics.create!(name: 'topic2', title: 'Topic 2')
topic_3 = topics.create!(name: 'topic3')
topic_4 = topics.create!(name: 'topic4')
subject.perform(topic_1.id, topic_3.id)
expect(topic_1.reload.title).to eq('topic1')
expect(topic_2.reload.title).to eq('Topic 2')
expect(topic_3.reload.title).to eq('topic3')
expect(topic_4.reload.title).to be_nil
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 20220223124428 do
RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 20220331133802 do
def set_avatar(topic_id, avatar)
topic = ::Projects::Topic.find(topic_id)
topic.avatar = avatar
@ -16,49 +16,62 @@ RSpec.describe Gitlab::BackgroundMigration::MergeTopicsWithSameName, schema: 202
topics = table(:topics)
project_topics = table(:project_topics)
group = namespaces.create!(name: 'group', path: 'group')
project_1 = projects.create!(namespace_id: group.id, visibility_level: 20)
project_2 = projects.create!(namespace_id: group.id, visibility_level: 10)
project_3 = projects.create!(namespace_id: group.id, visibility_level: 0)
group_1 = namespaces.create!(name: 'space1', type: 'Group', path: 'space1')
group_2 = namespaces.create!(name: 'space2', type: 'Group', path: 'space2')
group_3 = namespaces.create!(name: 'space3', type: 'Group', path: 'space3')
proj_space_1 = namespaces.create!(name: 'proj1', path: 'proj1', type: 'Project', parent_id: group_1.id)
proj_space_2 = namespaces.create!(name: 'proj2', path: 'proj2', type: 'Project', parent_id: group_2.id)
proj_space_3 = namespaces.create!(name: 'proj3', path: 'proj3', type: 'Project', parent_id: group_3.id)
project_1 = projects.create!(namespace_id: group_1.id, project_namespace_id: proj_space_1.id, visibility_level: 20)
project_2 = projects.create!(namespace_id: group_2.id, project_namespace_id: proj_space_2.id, visibility_level: 10)
project_3 = projects.create!(namespace_id: group_3.id, project_namespace_id: proj_space_3.id, visibility_level: 0)
topic_1_keep = topics.create!(
name: 'topic1',
title: 'Topic 1',
description: 'description 1 to keep',
total_projects_count: 2,
non_private_projects_count: 2
)
topic_1_remove = topics.create!(
name: 'TOPIC1',
title: 'Topic 1',
description: 'description 1 to remove',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_2_remove = topics.create!(
name: 'topic2',
title: 'Topic 2',
total_projects_count: 0
)
topic_2_keep = topics.create!(
name: 'TOPIC2',
title: 'Topic 2',
description: 'description 2 to keep',
total_projects_count: 1
)
topic_3_remove_1 = topics.create!(
name: 'topic3',
title: 'Topic 3',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_3_keep = topics.create!(
name: 'Topic3',
title: 'Topic 3',
total_projects_count: 2,
non_private_projects_count: 2
)
topic_3_remove_2 = topics.create!(
name: 'TOPIC3',
title: 'Topic 3',
description: 'description 3 to keep',
total_projects_count: 2,
non_private_projects_count: 1
)
topic_4_keep = topics.create!(
name: 'topic4'
name: 'topic4',
title: 'Topic 4'
)
project_topics_1 = []

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ScheduleBackfillTopicsTitle do
let(:topics) { table(:topics) }
let!(:topic1) { topics.create!(name: 'topic1') }
let!(:topic2) { topics.create!(name: 'topic2') }
let!(:topic3) { topics.create!(name: 'topic3') }
it 'correctly schedules background migrations', :aggregate_failures do
stub_const("#{Gitlab::Database::Migrations::BackgroundMigrationHelpers}::BATCH_SIZE", 2)
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, topic1.id, topic2.id)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, topic3.id, topic3.id)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
end
end
end

View File

@ -25,6 +25,8 @@ RSpec.describe Projects::Topic do
it { is_expected.to validate_uniqueness_of(:name).case_insensitive }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
it { is_expected.to validate_length_of(:description).is_at_most(1024) }
it { expect(Projects::Topic.new).to validate_presence_of(:title) }
it { expect(Projects::Topic.new).to validate_length_of(:title).is_at_most(255) }
end
describe 'scopes' do

View File

@ -117,7 +117,7 @@ RSpec.describe API::Topics do
describe 'POST /topics', :aggregate_failures do
context 'as administrator' do
it 'creates a topic' do
post api('/topics/', admin), params: { name: 'my-topic' }
post api('/topics/', admin), params: { name: 'my-topic', title: 'My Topic' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['name']).to eq('my-topic')
@ -128,7 +128,7 @@ RSpec.describe API::Topics do
workhorse_form_with_file(
api('/topics/', admin),
file_key: :avatar,
params: { name: 'my-topic', description: 'my description...', avatar: file }
params: { name: 'my-topic', title: 'My Topic', description: 'my description...', avatar: file }
)
expect(response).to have_gitlab_http_status(:created)
@ -137,23 +137,30 @@ RSpec.describe API::Topics do
end
it 'returns 400 if name is missing' do
post api('/topics/', admin)
post api('/topics/', admin), params: { title: 'My Topic' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('name is missing')
end
it 'returns 400 if name is not unique (case insensitive)' do
post api('/topics/', admin), params: { name: topic_1.name.downcase }
post api('/topics/', admin), params: { name: topic_1.name.downcase, title: 'My Topic' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['message']['name']).to eq(['has already been taken'])
end
it 'returns 400 if title is missing' do
post api('/topics/', admin), params: { name: 'my-topic' }
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eql('title is missing')
end
end
context 'as normal user' do
it 'returns 403 Forbidden' do
post api('/topics/', user), params: { name: 'my-topic' }
post api('/topics/', user), params: { name: 'my-topic', title: 'My Topic' }
expect(response).to have_gitlab_http_status(:forbidden)
end
@ -161,7 +168,7 @@ RSpec.describe API::Topics do
context 'as anonymous' do
it 'returns 401 Unauthorized' do
post api('/topics/'), params: { name: 'my-topic' }
post api('/topics/'), params: { name: 'my-topic', title: 'My Topic' }
expect(response).to have_gitlab_http_status(:unauthorized)
end

View File

@ -0,0 +1,936 @@
# frozen_string_literal: true
# rubocop:disable RSpec/VerifiedDoubles
require 'fast_spec_helper'
require 'rspec-parameterized'
require_relative '../../scripts/trigger-build'
RSpec.describe Trigger do
let(:env) do
{
'CI_JOB_URL' => 'ci_job_url',
'CI_PROJECT_PATH' => 'ci_project_path',
'CI_COMMIT_REF_NAME' => 'ci_commit_ref_name',
'CI_COMMIT_REF_SLUG' => 'ci_commit_ref_slug',
'CI_COMMIT_SHA' => 'ci_commit_sha',
'CI_MERGE_REQUEST_PROJECT_ID' => 'ci_merge_request_project_id',
'CI_MERGE_REQUEST_IID' => 'ci_merge_request_iid',
'GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN' => 'bot-token',
'CI_JOB_TOKEN' => 'job-token',
'GITLAB_USER_NAME' => 'gitlab_user_name',
'GITLAB_USER_LOGIN' => 'gitlab_user_login',
'QA_IMAGE' => 'qa_image'
}
end
let(:stubbed_downstream_gitlab_client) { double('downstream_gitlab_api_client') }
let(:gitlab_client_private_token) { env['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN'] }
let(:stubbed_pipeline) { Struct.new(:id, :web_url).new(42, 'pipeline_url') }
let(:trigger_token) { env['CI_JOB_TOKEN'] }
let(:api_endpoint) { 'https://gitlab.com/api/v4' }
before do
stub_env(env)
allow(subject).to receive(:puts)
allow(Gitlab).to receive(:client)
.with(
endpoint: api_endpoint,
private_token: gitlab_client_private_token
)
.and_return(stubbed_downstream_gitlab_client)
end
def expect_run_trigger_with_params(variables = {})
expect(stubbed_downstream_gitlab_client).to receive(:run_trigger)
.with(
downstream_project_path,
trigger_token,
ref,
hash_including(variables)
)
.and_return(stubbed_pipeline)
end
describe Trigger::Base do
let(:ref) { 'main' }
describe '#invoke!' do
context "when required methods aren't defined" do
it 'raises a NotImplementedError' do
expect { described_class.new.invoke! }.to raise_error(NotImplementedError)
end
end
context "when required methods are defined" do
let(:downstream_project_path) { 'foo/bar' }
let(:subclass) do
Class.new(Trigger::Base) do
def downstream_project_path
'foo/bar'
end
# Must be overridden
def ref_param_name
'FOO_BAR_BRANCH'
end
end
end
subject { subclass.new }
context 'when env variable `FOO_BAR_BRANCH` does not exist' do
it 'triggers the pipeline on the correct project and branch' do
expect_run_trigger_with_params
subject.invoke!
end
end
context 'when env variable `FOO_BAR_BRANCH` exists' do
let(:ref) { 'foo_bar_branch' }
before do
stub_env('FOO_BAR_BRANCH', ref)
end
it 'triggers the pipeline on the correct project and branch' do
expect_run_trigger_with_params
subject.invoke!
end
end
it 'waits for downstream pipeline' do
expect_run_trigger_with_params
expect(Trigger::Pipeline).to receive(:new)
.with(downstream_project_path, stubbed_pipeline.id, stubbed_downstream_gitlab_client)
subject.invoke!
end
context 'with post_comment: true' do
before do
stub_env('CI_COMMIT_REF_NAME', "#{ref}-ee")
end
it 'posts a comment' do
expect_run_trigger_with_params
expect(Trigger::CommitComment).to receive(:post!).with(stubbed_pipeline, stubbed_downstream_gitlab_client)
subject.invoke!(post_comment: true)
end
end
context 'with downstream_job_name: "foo"' do
let(:downstream_job) { Struct.new(:id, :name).new(42, 'foo') }
let(:paginated_resources) { Struct.new(:auto_paginate).new([downstream_job]) }
before do
stub_env('CI_COMMIT_REF_NAME', "#{ref}-ee")
end
it 'fetches the downstream job' do
expect_run_trigger_with_params
expect(stubbed_downstream_gitlab_client).to receive(:pipeline_jobs)
.with(downstream_project_path, stubbed_pipeline.id).and_return(paginated_resources)
expect(Trigger::Job).to receive(:new)
.with(downstream_project_path, downstream_job.id, stubbed_downstream_gitlab_client)
subject.invoke!(downstream_job_name: 'foo')
end
end
end
end
describe '#variables' do
let(:simple_forwarded_variables) do
{
'TRIGGER_SOURCE' => env['CI_JOB_URL'],
'TOP_UPSTREAM_SOURCE_PROJECT' => env['CI_PROJECT_PATH'],
'TOP_UPSTREAM_SOURCE_REF' => env['CI_COMMIT_REF_NAME'],
'TOP_UPSTREAM_SOURCE_JOB' => env['CI_JOB_URL'],
'TOP_UPSTREAM_MERGE_REQUEST_PROJECT_ID' => env['CI_MERGE_REQUEST_PROJECT_ID'],
'TOP_UPSTREAM_MERGE_REQUEST_IID' => env['CI_MERGE_REQUEST_IID']
}
end
it 'includes simple forwarded variables' do
expect(subject.variables).to include(simple_forwarded_variables)
end
describe "#base_variables" do
context 'when CI_COMMIT_TAG is set' do
before do
stub_env('CI_COMMIT_TAG', 'v1.0')
end
it 'sets GITLAB_REF_SLUG to CI_COMMIT_REF_NAME' do
expect(subject.variables['GITLAB_REF_SLUG']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
context 'when CI_COMMIT_TAG is nil' do
before do
stub_env('CI_COMMIT_TAG', nil)
end
it 'sets GITLAB_REF_SLUG to CI_COMMIT_REF_SLUG' do
expect(subject.variables['GITLAB_REF_SLUG']).to eq(env['CI_COMMIT_REF_SLUG'])
end
end
context 'when TRIGGERED_USER is set' do
before do
stub_env('TRIGGERED_USER', 'triggered_user')
end
it 'sets TRIGGERED_USER to triggered_user' do
expect(subject.variables['TRIGGERED_USER']).to eq('triggered_user')
end
end
context 'when TRIGGERED_USER is not set' do
before do
stub_env('TRIGGERED_USER', nil)
end
it 'sets TRIGGERED_USER to GITLAB_USER_NAME' do
expect(subject.variables['TRIGGERED_USER']).to eq(env['GITLAB_USER_NAME'])
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
end
it 'sets TOP_UPSTREAM_SOURCE_SHA to ci_merge_request_source_branch_sha' do
expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq('ci_merge_request_source_branch_sha')
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
end
it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
end
end
end
describe "#version_file_variables" do
using RSpec::Parameterized::TableSyntax
where(:version_file, :version) do
'GITALY_SERVER_VERSION' | "1"
'GITLAB_ELASTICSEARCH_INDEXER_VERSION' | "2"
'GITLAB_KAS_VERSION' | "3"
'GITLAB_PAGES_VERSION' | "4"
'GITLAB_SHELL_VERSION' | "5"
'GITLAB_WORKHORSE_VERSION' | "6"
end
with_them do
context "when set in ENV" do
before do
stub_env(version_file, version)
end
it 'includes the version from ENV' do
expect(subject.variables[version_file]).to eq(version)
end
end
context "when set in a file" do
before do
allow(File).to receive(:read).and_call_original
end
it 'includes the version from the file' do
expect(File).to receive(:read).with(version_file).and_return(version)
expect(subject.variables[version_file]).to eq(version)
end
end
end
end
end
end
describe Trigger::Omnibus do
describe '#variables' do
it 'invokes the trigger with expected variables' do
expect(subject.variables).to include(
'QA_IMAGE' => env['QA_IMAGE'],
'SKIP_QA_DOCKER' => 'true',
'ALTERNATIVE_SOURCES' => 'true',
'CACHE_UPDATE' => env['OMNIBUS_GITLAB_CACHE_UPDATE'],
'GITLAB_QA_OPTIONS' => env['GITLAB_QA_OPTIONS'],
'QA_TESTS' => env['QA_TESTS'],
'ALLURE_JOB_NAME' => env['ALLURE_JOB_NAME']
)
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
end
it 'sets GITLAB_VERSION & IMAGE_TAG to ci_merge_request_source_branch_sha' do
expect(subject.variables).to include(
'GITLAB_VERSION' => 'ci_merge_request_source_branch_sha',
'IMAGE_TAG' => 'ci_merge_request_source_branch_sha'
)
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
end
it 'sets GITLAB_VERSION & IMAGE_TAG to CI_COMMIT_SHA' do
expect(subject.variables).to include(
'GITLAB_VERSION' => env['CI_COMMIT_SHA'],
'IMAGE_TAG' => env['CI_COMMIT_SHA']
)
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets GITLAB_VERSION & IMAGE_TAG to CI_COMMIT_SHA' do
expect(subject.variables).to include(
'GITLAB_VERSION' => env['CI_COMMIT_SHA'],
'IMAGE_TAG' => env['CI_COMMIT_SHA']
)
end
end
context 'when Trigger.security? is true' do
before do
allow(Trigger).to receive(:security?).and_return(true)
end
it 'sets SECURITY_SOURCES to true' do
expect(subject.variables['SECURITY_SOURCES']).to eq('true')
end
end
context 'when Trigger.security? is false' do
before do
allow(Trigger).to receive(:security?).and_return(false)
end
it 'sets SECURITY_SOURCES to false' do
expect(subject.variables['SECURITY_SOURCES']).to eq('false')
end
end
context 'when Trigger.ee? is true' do
before do
allow(Trigger).to receive(:ee?).and_return(true)
end
it 'sets ee to true' do
expect(subject.variables['ee']).to eq('true')
end
end
context 'when Trigger.ee? is false' do
before do
allow(Trigger).to receive(:ee?).and_return(false)
end
it 'sets ee to false' do
expect(subject.variables['ee']).to eq('false')
end
end
context 'when QA_BRANCH is set' do
before do
stub_env('QA_BRANCH', 'qa_branch')
end
it 'sets QA_BRANCH to qa_branch' do
expect(subject.variables['QA_BRANCH']).to eq('qa_branch')
end
end
end
describe '.access_token' do
context 'when OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN is set' do
let(:omnibus_gitlab_project_access_token) { 'omnibus_gitlab_project_access_token' }
before do
stub_env('OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN', omnibus_gitlab_project_access_token)
end
it 'returns the omnibus-specific access token' do
expect(described_class.access_token).to eq(omnibus_gitlab_project_access_token)
end
end
context 'when OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN is not set' do
before do
stub_env('OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN', nil)
end
it 'returns the default access token' do
expect(described_class.access_token).to eq(Trigger::Base.access_token)
end
end
end
describe '#invoke!' do
let(:downstream_project_path) { 'gitlab-org/build/omnibus-gitlab-mirror' }
let(:ref) { 'master' }
let(:env) do
super().merge(
'QA_IMAGE' => 'qa_image',
'OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN' => nil,
'OMNIBUS_GITLAB_CACHE_UPDATE' => 'omnibus_gitlab_cache_update',
'GITLAB_QA_OPTIONS' => 'gitlab_qa_options',
'QA_TESTS' => 'qa_tests',
'ALLURE_JOB_NAME' => 'allure_job_name'
)
end
describe '#downstream_project_path' do
context 'when OMNIBUS_PROJECT_PATH is set' do
let(:downstream_project_path) { 'omnibus_project_path' }
before do
stub_env('OMNIBUS_PROJECT_PATH', downstream_project_path)
end
it 'triggers the pipeline on the correct project' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
describe '#ref' do
context 'when OMNIBUS_BRANCH is set' do
let(:ref) { 'omnibus_branch' }
before do
stub_env('OMNIBUS_BRANCH', ref)
end
it 'triggers the pipeline on the correct ref' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
context 'when CI_COMMIT_REF_NAME is a stable branch' do
let(:ref) { '14-10-stable' }
before do
stub_env('CI_COMMIT_REF_NAME', "#{ref}-ee")
end
it 'triggers the pipeline on the correct ref' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
end
describe Trigger::CNG do
describe '#variables' do
it 'does not include redundant variables' do
expect(subject.variables).not_to include('TRIGGER_SOURCE', 'TRIGGERED_USER')
end
it 'invokes the trigger with expected variables' do
expect(subject.variables).to include('FORCE_RAILS_IMAGE_BUILDS' => 'true')
end
describe "TRIGGER_BRANCH" do
context 'when CNG_BRANCH is not set' do
it 'sets TRIGGER_BRANCH to master' do
expect(subject.variables['TRIGGER_BRANCH']).to eq('master')
end
end
context 'when CNG_BRANCH is set' do
let(:ref) { 'cng_branch' }
before do
stub_env('CNG_BRANCH', ref)
end
it 'sets TRIGGER_BRANCH to cng_branch' do
expect(subject.variables['TRIGGER_BRANCH']).to eq(ref)
end
end
context 'when CI_COMMIT_REF_NAME is a stable branch' do
let(:ref) { '14-10-stable' }
before do
stub_env('CI_COMMIT_REF_NAME', "#{ref}-ee")
end
it 'sets TRIGGER_BRANCH to the corresponding stable branch' do
expect(subject.variables['TRIGGER_BRANCH']).to eq(ref)
end
end
end
describe "GITLAB_VERSION" do
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
end
it 'sets GITLAB_VERSION to CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' do
expect(subject.variables['GITLAB_VERSION']).to eq('ci_merge_request_source_branch_sha')
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
end
it 'sets GITLAB_VERSION to CI_COMMIT_SHA' do
expect(subject.variables['GITLAB_VERSION']).to eq(env['CI_COMMIT_SHA'])
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets GITLAB_VERSION to CI_COMMIT_SHA' do
expect(subject.variables['GITLAB_VERSION']).to eq(env['CI_COMMIT_SHA'])
end
end
end
describe "GITLAB_TAG" do
context 'when CI_COMMIT_TAG is set' do
before do
stub_env('CI_COMMIT_TAG', 'v1.0')
end
it 'sets GITLAB_TAG to true' do
expect(subject.variables['GITLAB_TAG']).to eq('v1.0')
end
end
context 'when CI_COMMIT_TAG is nil' do
before do
stub_env('CI_COMMIT_TAG', nil)
end
it 'sets GITLAB_TAG to nil' do
expect(subject.variables['GITLAB_TAG']).to eq(nil)
end
end
end
describe "GITLAB_ASSETS_TAG" do
context 'when CI_COMMIT_TAG is set' do
before do
stub_env('CI_COMMIT_TAG', 'v1.0')
end
it 'sets GITLAB_ASSETS_TAG to CI_COMMIT_REF_NAME' do
expect(subject.variables['GITLAB_ASSETS_TAG']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
context 'when CI_COMMIT_TAG and CI_MERGE_REQUEST_SOURCE_BRANCH_SHA are nil' do
before do
stub_env('CI_COMMIT_TAG', nil)
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets GITLAB_ASSETS_TAG to CI_COMMIT_SHA' do
expect(subject.variables['GITLAB_ASSETS_TAG']).to eq(env['CI_COMMIT_SHA'])
end
end
end
describe "CE_PIPELINE" do
context 'when Trigger.ee? is true' do
before do
allow(Trigger).to receive(:ee?).and_return(true)
end
it 'sets CE_PIPELINE to nil' do
expect(subject.variables['CE_PIPELINE']).to eq(nil)
end
end
context 'when Trigger.ee? is false' do
before do
allow(Trigger).to receive(:ee?).and_return(false)
end
it 'sets CE_PIPELINE to true' do
expect(subject.variables['CE_PIPELINE']).to eq('true')
end
end
end
describe "EE_PIPELINE" do
context 'when Trigger.ee? is true' do
before do
allow(Trigger).to receive(:ee?).and_return(true)
end
it 'sets EE_PIPELINE to true' do
expect(subject.variables['EE_PIPELINE']).to eq('true')
end
end
context 'when Trigger.ee? is false' do
before do
allow(Trigger).to receive(:ee?).and_return(false)
end
it 'sets EE_PIPELINE to nil' do
expect(subject.variables['EE_PIPELINE']).to eq(nil)
end
end
end
describe "#version_param_value" do
using RSpec::Parameterized::TableSyntax
let(:version_file) { 'GITALY_SERVER_VERSION' }
where(:raw_version, :expected_version) do
"1.2.3" | "v1.2.3"
"1.2.3-rc1" | "v1.2.3-rc1"
"1.2.3-ee" | "v1.2.3-ee"
"1.2.3-rc1-ee" | "v1.2.3-rc1-ee"
end
with_them do
context "when set in ENV" do
before do
stub_env(version_file, raw_version)
end
it 'includes the version from ENV' do
expect(subject.variables[version_file]).to eq(expected_version)
end
end
end
end
end
end
describe Trigger::Docs do
describe '#variables' do
describe "BRANCH_CE" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/gitlab-foss')
end
context 'when CI_PROJECT_PATH is gitlab-org/gitlab-foss' do
it 'sets BRANCH_CE to CI_COMMIT_REF_NAME' do
expect(subject.variables['BRANCH_CE']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
end
describe "BRANCH_EE" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/gitlab')
end
context 'when CI_PROJECT_PATH is gitlab-org/gitlab' do
it 'sets BRANCH_EE to CI_COMMIT_REF_NAME' do
expect(subject.variables['BRANCH_EE']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
end
describe "BRANCH_RUNNER" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/gitlab-runner')
end
context 'when CI_PROJECT_PATH is gitlab-org/gitlab-runner' do
it 'sets BRANCH_RUNNER to CI_COMMIT_REF_NAME' do
expect(subject.variables['BRANCH_RUNNER']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
end
describe "BRANCH_OMNIBUS" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/omnibus-gitlab')
end
context 'when CI_PROJECT_PATH is gitlab-org/omnibus-gitlab' do
it 'sets BRANCH_OMNIBUS to CI_COMMIT_REF_NAME' do
expect(subject.variables['BRANCH_OMNIBUS']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
end
describe "BRANCH_CHARTS" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/charts/gitlab')
end
context 'when CI_PROJECT_PATH is gitlab-org/charts/gitlab' do
it 'sets BRANCH_CHARTS to CI_COMMIT_REF_NAME' do
expect(subject.variables['BRANCH_CHARTS']).to eq(env['CI_COMMIT_REF_NAME'])
end
end
end
describe "REVIEW_SLUG" do
before do
stub_env('CI_PROJECT_PATH', 'gitlab-org/gitlab-foss')
end
context 'when CI_MERGE_REQUEST_IID is set' do
it 'sets REVIEW_SLUG' do
expect(subject.variables['REVIEW_SLUG']).to eq("-ce-#{env['CI_MERGE_REQUEST_IID']}")
end
end
context 'when CI_MERGE_REQUEST_IID is not set' do
before do
stub_env('CI_MERGE_REQUEST_IID', nil)
end
it 'sets REVIEW_SLUG' do
expect(subject.variables['REVIEW_SLUG']).to eq("-ce-#{env['CI_COMMIT_REF_SLUG']}")
end
end
end
end
describe '.access_token' do
context 'when DOCS_PROJECT_API_TOKEN is set' do
let(:docs_project_api_token) { 'docs_project_api_token' }
before do
stub_env('DOCS_PROJECT_API_TOKEN', docs_project_api_token)
end
it 'returns the docs-specific access token' do
expect(described_class.access_token).to eq(docs_project_api_token)
end
end
context 'when DOCS_PROJECT_API_TOKEN is not set' do
before do
stub_env('DOCS_PROJECT_API_TOKEN', nil)
end
it 'returns the default access token' do
expect(described_class.access_token).to eq(Trigger::Base.access_token)
end
end
end
describe '#invoke!' do
let(:downstream_project_path) { 'gitlab-org/gitlab-docs' }
let(:trigger_token) { 'docs_trigger_token' }
let(:ref) { 'main' }
let(:env) do
super().merge(
'CI_PROJECT_PATH' => 'gitlab-org/gitlab-foss',
'DOCS_PROJECT_API_TOKEN' => nil,
'DOCS_TRIGGER_TOKEN' => trigger_token
)
end
describe '#downstream_project_path' do
context 'when DOCS_PROJECT_PATH is set' do
let(:downstream_project_path) { 'docs_project_path' }
before do
stub_env('DOCS_PROJECT_PATH', downstream_project_path)
end
it 'triggers the pipeline on the correct project' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
describe '#ref' do
context 'when DOCS_BRANCH is set' do
let(:ref) { 'docs_branch' }
before do
stub_env('DOCS_BRANCH', ref)
end
it 'triggers the pipeline on the correct ref' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
end
end
describe Trigger::DatabaseTesting do
describe '#variables' do
it 'invokes the trigger with expected variables' do
expect(subject.variables).to include('TRIGGERED_USER_LOGIN' => env['GITLAB_USER_LOGIN'])
end
describe "GITLAB_COMMIT_SHA" do
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
end
it 'sets GITLAB_COMMIT_SHA to ci_merge_request_source_branch_sha' do
expect(subject.variables['GITLAB_COMMIT_SHA']).to eq('ci_merge_request_source_branch_sha')
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
end
it 'sets GITLAB_COMMIT_SHA to CI_COMMIT_SHA' do
expect(subject.variables['GITLAB_COMMIT_SHA']).to eq(env['CI_COMMIT_SHA'])
end
end
context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
before do
stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets GITLAB_COMMIT_SHA to CI_COMMIT_SHA' do
expect(subject.variables['GITLAB_COMMIT_SHA']).to eq(env['CI_COMMIT_SHA'])
end
end
end
end
describe '#invoke!' do
let(:downstream_project_path) { 'gitlab-com/database-team/gitlab-com-database-testing' }
let(:trigger_token) { 'gitlabcom_database_testing_access_token' }
let(:api_endpoint) { 'https://ops.gitlab.net/api/v4' }
let(:gitlab_client_private_token) { 'gitlabcom_database_testing_access_token' }
let(:ref) { 'master' }
let(:stubbed_upstream_gitlab_client) { double('upstream_gitlab_api_client') }
let(:mr_notes) { [double(body: described_class::IDENTIFIABLE_NOTE_TAG)] }
let(:env) do
super().merge(
'GITLABCOM_DATABASE_TESTING_ACCESS_TOKEN' => gitlab_client_private_token,
'GITLABCOM_DATABASE_TESTING_TRIGGER_TOKEN' => trigger_token
)
end
before do
allow(Gitlab).to receive(:client)
.with(
endpoint: 'https://gitlab.com/api/v4',
private_token: env['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
)
.and_return(stubbed_upstream_gitlab_client)
allow(stubbed_upstream_gitlab_client).to receive(:merge_request_notes)
.with(
env['CI_PROJECT_PATH'],
env['CI_MERGE_REQUEST_IID']
)
.and_return(double(auto_paginate: mr_notes))
end
it 'invokes the trigger with expected variables' do
expect_run_trigger_with_params
subject.invoke!
end
describe '#downstream_project_path' do
context 'when GITLABCOM_DATABASE_TESTING_PROJECT_PATH is set' do
let(:downstream_project_path) { 'gitlabcom_database_testing_project_path' }
before do
stub_env('GITLABCOM_DATABASE_TESTING_PROJECT_PATH', downstream_project_path)
end
it 'triggers the pipeline on the correct project' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
describe '#ref' do
context 'when GITLABCOM_DATABASE_TESTING_TRIGGER_REF is set' do
let(:ref) { 'gitlabcom_database_testing_trigger_ref' }
before do
stub_env('GITLABCOM_DATABASE_TESTING_TRIGGER_REF', ref)
end
it 'triggers the pipeline on the correct ref' do
expect_run_trigger_with_params
subject.invoke!
end
end
end
context 'when no MR notes with the identifier exist yet' do
let(:mr_notes) { [double(body: 'hello world')] }
it 'posts a new note' do
expect_run_trigger_with_params
expect(stubbed_upstream_gitlab_client).to receive(:create_merge_request_note)
.with(
env['CI_PROJECT_PATH'],
env['CI_MERGE_REQUEST_IID'],
instance_of(String)
)
.and_return(double(id: 42))
subject.invoke!
end
end
end
end
end
# rubocop:enable RSpec/VerifiedDoubles