Commit f92a53a2 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent e3764d34
......@@ -42,7 +42,6 @@ rules:
lines-between-class-members: off
# Disabled for now, to make the plugin-vue 4.5 -> 5.0 update smoother
vue/no-confusing-v-for-v-if: error
vue/no-unused-components: off
vue/no-use-v-if-with-v-for: off
vue/no-v-html: off
vue/use-v-on-exact: off
......
......@@ -23,6 +23,7 @@ module Ci
belongs_to :runner
belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
belongs_to :resource_group, class_name: 'Ci::ResourceGroup', inverse_of: :builds
RUNNER_FEATURES = {
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
......@@ -34,6 +35,7 @@ module Ci
}.freeze
has_one :deployment, as: :deployable, class_name: 'Deployment'
has_one :resource, class_name: 'Ci::Resource', inverse_of: :build
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id
......
# frozen_string_literal: true
module Ci
class Resource < ApplicationRecord
extend Gitlab::Ci::Model
belongs_to :resource_group, class_name: 'Ci::ResourceGroup', inverse_of: :resources
belongs_to :build, class_name: 'Ci::Build', inverse_of: :resource
scope :free, -> { where(build: nil) }
scope :retained_by, -> (build) { where(build: build) }
end
end
# frozen_string_literal: true
module Ci
class ResourceGroup < ApplicationRecord
extend Gitlab::Ci::Model
belongs_to :project, inverse_of: :resource_groups
has_many :resources, class_name: 'Ci::Resource', inverse_of: :resource_group
has_many :builds, class_name: 'Ci::Build', inverse_of: :resource_group
validates :key,
length: { maximum: 255 },
format: { with: Gitlab::Regex.environment_name_regex,
message: Gitlab::Regex.environment_name_regex_message }
before_create :ensure_resource
##
# NOTE: This is concurrency-safe method that the subquery in the `UPDATE`
# works as explicit locking.
def assign_resource_to(build)
resources.free.limit(1).update_all(build_id: build.id) > 0
end
def release_resource_from(build)
resources.retained_by(build).update_all(build_id: nil) > 0
end
private
def ensure_resource
# Currently we only support one resource per group, which means
# maximum one build can be set to the resource group, thus builds
# belong to the same resource group are executed once at time.
self.resources.build if self.resources.empty?
end
end
end
......@@ -285,6 +285,7 @@ class Project < ApplicationRecord
has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule'
has_many :project_deploy_tokens
has_many :deploy_tokens, through: :project_deploy_tokens
has_many :resource_groups, class_name: 'Ci::ResourceGroup', inverse_of: :project
has_one :auto_devops, class_name: 'ProjectAutoDevops', inverse_of: :project, autosave: true
has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
......
......@@ -5,7 +5,7 @@ module Ci
CLONE_ACCESSORS = %i[pipeline project ref tag options name
allow_failure stage stage_id stage_idx trigger_request
yaml_variables when environment coverage_regex
description tag_list protected needs].freeze
description tag_list protected needs resource_group].freeze
def execute(build)
reprocess!(build).tap do |new_build|
......
---
title: Add Ci Resource Group models
merge_request: 20950
author:
type: other
---
title: Fix bug in Container Scanning report remediations
merge_request: 21980
author:
type: fixed
# frozen_string_literal: true
class AddCiResourceGroups < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
create_table :ci_resource_groups do |t|
t.timestamps_with_timezone
t.bigint :project_id, null: false
t.string :key, null: false, limit: 255
t.index %i[project_id key], unique: true
end
create_table :ci_resources do |t|
t.timestamps_with_timezone
t.references :resource_group, null: false, index: false, foreign_key: { to_table: :ci_resource_groups, on_delete: :cascade }
t.bigint :build_id, null: true
t.index %i[build_id]
t.index %i[resource_group_id build_id], unique: true
end
end
end
# frozen_string_literal: true
class AddFkToCiResourcesBuildId < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key :ci_resources, :ci_builds, column: :build_id, on_delete: :nullify
end
def down
remove_foreign_key_if_exists :ci_resources, column: :build_id
end
end
# frozen_string_literal: true
class AddFkToCiResourceGroupsProjectId < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_foreign_key :ci_resource_groups, :projects, column: :project_id, on_delete: :cascade
end
def down
remove_foreign_key_if_exists :ci_resource_groups, column: :project_id
end
end
# frozen_string_literal: true
class AddResourceGroupIdToCiBuilds < ActiveRecord::Migration[5.2]
DOWNTIME = false
def up
unless column_exists?(:ci_builds, :resource_group_id)
add_column :ci_builds, :resource_group_id, :bigint
end
unless column_exists?(:ci_builds, :waiting_for_resource_at)
add_column :ci_builds, :waiting_for_resource_at, :datetime_with_timezone
end
end
def down
if column_exists?(:ci_builds, :resource_group_id)
remove_column :ci_builds, :resource_group_id, :bigint
end
if column_exists?(:ci_builds, :waiting_for_resource_at)
remove_column :ci_builds, :waiting_for_resource_at, :datetime_with_timezone
end
end
end
# frozen_string_literal: true
class AddIndexToResourceGroupId < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_for_resource_group'.freeze
disable_ddl_transaction!
def up
add_concurrent_index :ci_builds, %i[resource_group_id id], where: 'resource_group_id IS NOT NULL', name: INDEX_NAME
add_concurrent_foreign_key :ci_builds, :ci_resource_groups, column: :resource_group_id, on_delete: :nullify
end
def down
remove_foreign_key_if_exists :ci_builds, column: :resource_group_id
remove_concurrent_index_by_name :ci_builds, INDEX_NAME
end
end
......@@ -684,6 +684,8 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
t.datetime_with_timezone "scheduled_at"
t.string "token_encrypted"
t.integer "upstream_pipeline_id"
t.bigint "resource_group_id"
t.datetime_with_timezone "waiting_for_resource_at"
t.index ["artifacts_expire_at"], name: "index_ci_builds_on_artifacts_expire_at", where: "(artifacts_file <> ''::text)"
t.index ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id"
t.index ["commit_id", "artifacts_expire_at", "id"], name: "index_ci_builds_on_commit_id_and_artifacts_expireatandidpartial", where: "(((type)::text = 'Ci::Build'::text) AND ((retried = false) OR (retried IS NULL)) AND ((name)::text = ANY (ARRAY[('sast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('sast:container'::character varying)::text, ('container_scanning'::character varying)::text, ('dast'::character varying)::text])))"
......@@ -698,6 +700,7 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
t.index ["project_id"], name: "index_ci_builds_on_project_id_for_successfull_pages_deploy", where: "(((type)::text = 'GenericCommitStatus'::text) AND ((stage)::text = 'deploy'::text) AND ((name)::text = 'pages:deploy'::text) AND ((status)::text = 'success'::text))"
t.index ["protected"], name: "index_ci_builds_on_protected"
t.index ["queued_at"], name: "index_ci_builds_on_queued_at"
t.index ["resource_group_id", "id"], name: "index_for_resource_group", where: "(resource_group_id IS NOT NULL)"
t.index ["runner_id"], name: "index_ci_builds_on_runner_id"
t.index ["scheduled_at"], name: "partial_index_ci_builds_on_scheduled_at_with_scheduled_jobs", where: "((scheduled_at IS NOT NULL) AND ((type)::text = 'Ci::Build'::text) AND ((status)::text = 'scheduled'::text))"
t.index ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)"
......@@ -872,6 +875,23 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
t.index ["user_id"], name: "index_ci_pipelines_on_user_id"
end
create_table "ci_resource_groups", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.bigint "project_id", null: false
t.string "key", limit: 255, null: false
t.index ["project_id", "key"], name: "index_ci_resource_groups_on_project_id_and_key", unique: true
end
create_table "ci_resources", force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.bigint "resource_group_id", null: false
t.bigint "build_id"
t.index ["build_id"], name: "index_ci_resources_on_build_id"
t.index ["resource_group_id", "build_id"], name: "index_ci_resources_on_resource_group_id_and_build_id", unique: true
end
create_table "ci_runner_namespaces", id: :serial, force: :cascade do |t|
t.integer "runner_id"
t.integer "namespace_id"
......@@ -4395,6 +4415,7 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify
add_foreign_key "ci_builds", "ci_pipelines", column: "commit_id", name: "fk_d3130c9a7f", on_delete: :cascade
add_foreign_key "ci_builds", "ci_pipelines", column: "upstream_pipeline_id", name: "fk_87f4cefcda", on_delete: :cascade
add_foreign_key "ci_builds", "ci_resource_groups", column: "resource_group_id", name: "fk_6661f4f0e8", on_delete: :nullify
add_foreign_key "ci_builds", "ci_stages", column: "stage_id", name: "fk_3a9eaa254d", on_delete: :cascade
add_foreign_key "ci_builds", "projects", name: "fk_befce0568a", on_delete: :cascade
add_foreign_key "ci_builds_metadata", "ci_builds", column: "build_id", on_delete: :cascade
......@@ -4415,6 +4436,9 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
add_foreign_key "ci_pipelines", "external_pull_requests", name: "fk_190998ef09", on_delete: :nullify
add_foreign_key "ci_pipelines", "merge_requests", name: "fk_a23be95014", on_delete: :cascade
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
add_foreign_key "ci_resource_groups", "projects", name: "fk_774722d144", on_delete: :cascade
add_foreign_key "ci_resources", "ci_builds", column: "build_id", name: "fk_e169a8e3d5", on_delete: :nullify
add_foreign_key "ci_resources", "ci_resource_groups", column: "resource_group_id", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "ci_runners", column: "runner_id", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "namespaces", on_delete: :cascade
add_foreign_key "ci_runner_projects", "projects", name: "fk_4478a6f1e4", on_delete: :cascade
......
......@@ -7,7 +7,7 @@ GitLab has several features based on receiving incoming emails:
- [New issue by email](../user/project/issues/managing_issues.md#new-issue-via-email):
allow GitLab users to create a new issue by sending an email to a
user-specific email address.
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#create-new-merge-requests-by-email):
- [New merge request by email](../user/project/merge_requests/creating_merge_requests.md#new-merge-request-by-email-core-only):
allow GitLab users to create a new merge request by sending an email to a
user-specific email address.
- [Service Desk](../user/project/service_desk.md): provide e-mail support to
......@@ -79,7 +79,7 @@ email address in order to sign up.
If you also host a public-facing GitLab instance at `hooli.com` and set your
incoming email domain to `hooli.com`, an attacker could abuse the "Create new
issue by email" or
"[Create new merge request by email](../user/project/merge_requests/creating_merge_requests.md#create-new-merge-requests-by-email)"
"[Create new merge request by email](../user/project/merge_requests/creating_merge_requests.md#new-merge-request-by-email-core-only)"
features by using a project's unique address as the email when signing up for
Slack, which would send a confirmation email, which would create a new issue or
merge request on the project owned by the attacker, allowing them to click the
......
......@@ -119,7 +119,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Auditor users](auditor_users.md): Users with read-only access to all projects, groups, and other resources on the GitLab instance. **(PREMIUM ONLY)**
- [Incoming email](incoming_email.md): Configure incoming emails to allow
users to [reply by email](reply_by_email.md), create [issues by email](../user/project/issues/managing_issues.md#new-issue-via-email) and
[merge requests by email](../user/project/merge_requests/creating_merge_requests.md#create-new-merge-requests-by-email), and to enable [Service Desk](../user/project/service_desk.md).
[merge requests by email](../user/project/merge_requests/creating_merge_requests.md#new-merge-request-by-email-core-only), and to enable [Service Desk](../user/project/service_desk.md).
- [Postfix for incoming email](reply_by_email_postfix_setup.md): Set up a
basic Postfix mail server with IMAP authentication on Ubuntu for incoming
emails.
......
......@@ -297,8 +297,7 @@ To request help:
1. Locate the the Technical Writer for the relevant
[DevOps stage group](https://about.gitlab.com/handbook/product/technical-writing/index.html#assignments).
1. Either:
- If urgent help is required, directly assign the Technical Writer in the issue or
[in the merge request](../../user/project/merge_requests/creating_merge_requests.md#multiple-assignees-starter).
- If urgent help is required, directly assign the Technical Writer in the issue or in the merge request.
- If non-urgent help is required, ping the Technical Writer in the issue or merge request.
If you are a member of GitLab's Slack workspace, you can request help in `#docs`.
......
---
type: howto
redirect_to: '../user/project/merge_requests/creating_merge_requests.md'
---
# How to create a merge request
Merge requests are how you integrate separate changes that you've made in a
[branch](create-branch.md) to a [project](create-project.md).
This is a brief guide on how to create a merge request. For more detailed information,
check the [merge requests documentation](../user/project/merge_requests/index.md), or
you can watch our [GitLab Flow video](https://www.youtube.com/watch?v=InKNIvky2KE) for
a quick overview of working with merge requests.
1. Before you start, you should have already [created a branch](create-branch.md)
and [pushed your changes](start-using-git.md#send-changes-to-gitlabcom) to GitLab.
1. Go to the project where you'd like to merge your changes and click on the
**Merge requests** tab.
1. Click on **New merge request** on the right side of the screen.
1. From there, you have the option to select the source branch and the target
branch you'd like to compare to. The default target project is the upstream
repository, but you can choose to compare across any of its forks.
![Select a branch](img/merge_request_select_branch.png)
1. When ready, click on the **Compare branches and continue** button.
1. At a minimum, add a title and a description to your merge request. Optionally,
select a user to review your merge request. You may also select a milestone and
labels.
![New merge request page](img/merge_request_page.png)
1. When ready, click on the **Submit merge request** button.
Your merge request will be ready to be reviewed, approved, and merged.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
This document was moved to [another location](../user/project/merge_requests/creating_merge_requests.md).
This diff is collapsed.
......@@ -10,7 +10,7 @@ The Packages feature allows GitLab to act as a repository for the following:
| ------------------- | ----------- | --------------------------- |
| [Container Registry](container_registry/index.md) | The GitLab Container Registry enables every project in GitLab to have its own space to store [Docker](https://www.docker.com/) images. | 8.8+ |
| [Dependency Proxy](dependency_proxy/index.md) **(PREMIUM)** | The GitLab Dependency Proxy sets up a local proxy for frequently used upstream images/packages. | 11.11+ |
| [Conan Repository](conan_repository/index.md) **(PREMIUM)** | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.4+ |
| [Conan Repository](conan_repository/index.md) **(PREMIUM)** | The GitLab Conan Repository enables every project in GitLab to have its own space to store [Conan](https://conan.io/) packages. | 12.6+ |
| [Maven Repository](maven_repository/index.md) **(PREMIUM)** | The GitLab Maven Repository enables every project in GitLab to have its own space to store [Maven](https://maven.apache.org/) packages. | 11.3+ |
| [NPM Registry](npm_registry/index.md) **(PREMIUM)** | The GitLab NPM Registry enables every project in GitLab to have its own space to store [NPM](https://www.npmjs.com/) packages. | 11.7+ |
| [NuGet Repository](https://gitlab.com/gitlab-org/gitlab/issues/20050) **(PREMIUM)** | *COMING SOON* The GitLab NuGet Repository will enable every project in GitLab to have its own space to store [NuGet](https://www.nuget.org/) packages. | 12.7 (planned) |
......
---
type: index, reference
description: "Getting started with Merge Requests."
---
# Getting started with Merge Requests
A Merge Request (**MR**) is the basis of GitLab as a code
collaboration and version control.
When working in a Git-based platform, you can use branching
strategies to collaborate on code.
A repository is composed by its _default branch_, which contains
the major version of the codebase, from which you create minor
branches, also called _feature branches_, to propose changes to
the codebase without introducing them directly into the major
version of the codebase.
Branching is specially important when collaborating with others,
avoiding changes to be pushed directly to the default branch
without prior reviews, tests, and approvals.
When you create a new feature branch, change the files, and push
it to GitLab, you have the option to create a **Merge Request**,
which is essentially a _request_ to merge one branch into another.
The branch you added your changes into is called _source branch_
while the branch you'll request to merge your changes into is
called _target branch_.
The target branch can be the default or any other branch, depending
on the branching strategies you choose.
In a merge request, beyond visualizing the differences between the
original content and your proposed changes, you can execute a
[significant number of tasks](#what-you-can-do-with-merge-requests)
before concluding your work and merging the merge request.
You can watch our [GitLab Flow video](https://www.youtube.com/watch?v=InKNIvky2KE) for
a quick overview of working with merge requests.
## How to create a merge request
Learn the various ways to [create a merge request](creating_merge_requests.md).
## What you can do with merge requests
When you start a new merge request, you'll have the following
options to include straightaway (you can also add them later by
clicking the **Edit** button on the merge request's page at the
top-right side):
- [Assign](#assignee) the merge request to a colleage for review.With GitLab Starter and higher tiers, you can [assign it to more than one person at a time](#multiple-assignees-starter).
- Set a [milestone](../milestones/index.md) to track time-sensitive changes.
- Add [labels](../labels.md) to help contextualize and filter your merge requests over time.
- Require [approval](merge_request_approvals.md) from your team. **(STARTER)**
- [Close issues automatically](#merge-requests-to-close-issues) when it's merged.
- Enable the [delete source branch when merge request is accepted](#deleting-the-source-branch) option to keep your repository clean.
- Enable the [squash commits when merge request is accepted](squash_and_merge.md) option to combine all the commits into one before merging, thus keep a clean commit history in your repository.
- Set the merge request as a [Work In Progress (WIP)](work_in_progress_merge_requests.md) to avoid accidental merges before it's ready.
Once you have created the merge request, you can also:
- [Discuss](../../discussions/index.md) your implementation with your team in the merge request thread.
- [Perform inline code reviews](reviewing_and_managing_merge_requests.md#perform-inline-code-reviews).
- Add [merge request dependencies](merge_request_dependencies.md) to restrict it to be merged only when other merge requests have been merged. **(PREMIUM)**
- Preview continuous integration [pipelines on the merge request widget](reviewing_and_managing_merge_requests.md#pipeline-status-in-merge-requests-widgets).
- Preview how your changes look directly on your deployed application with [Review Apps](reviewing_and_managing_merge_requests.md#live-preview-with-review-apps).
- [Allow collaboration on merge requests across forks](allow_collaboration.md).
- Perform a [Review](../../discussions/index.md#merge-request-reviews-premium) in order to create multiple comments on a diff and publish them once you're ready. **(PREMIUM)**
- Add [code suggestions](../../discussions/index.md#suggest-changes) to change the content of merge requests directly into merge request threads, and easily apply them to the codebase directly from the UI.
- Add a time estimation and the time spent with that merge request with [Time Tracking](../time_tracking.md#time-tracking).
Many of these can be set when pushing changes from the command line,
with [Git push options](../push_options.md).
See also other [features associated to merge requests](reviewing_and_managing_merge_requests.md#associated-features).
### Assignee
Choose an assignee to designate someone as the person responsible
for the first [review of the merge request](reviewing_and_managing_merge_requests.md).
Open the drop down box to search for the user you wish to assign,
and the merge request will be added to their
[assigned merge request list](../../search/index.md#issues-and-merge-requests).
#### Multiple assignees **(STARTER)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/2004) in [GitLab Starter 11.11](https://about.gitlab.com/pricing/).
Multiple people often review merge requests at the same time.
GitLab allows you to have multiple assignees for merge requests
to indicate everyone that is reviewing or accountable for it.
![multiple assignees for merge requests sidebar](img/multiple_assignees_for_merge_requests_sidebar.png)
To assign multiple assignees to a merge request:
1. From a merge request, expand the right sidebar and locate the **Assignees** section.
1. Click on **Edit** and from the dropdown menu, select as many users as you want to assign the merge request to.
Similarly, assignees are removed by deselecting them from the same
dropdown menu.
It's also possible to manage multiple assignees:
- When creating a merge request.
- Using [quick actions](../quick_actions.md#quick-actions-for-issues-merge-requests-and-epics).
### Merge requests to close issues
If the merge request is being created to resolve an issue, you can
add a note in the description which will set it to
[automatically close the issue](../issues/managing_issues.md#closing-issues-automatically)
when merged.
If the issue is [confidential](../issues/confidential_issues.md),
you may want to use a different workflow for
[merge requests for confidential issues](../issues/confidential_issues.md#merge-requests-for-confidential-issues)
to prevent confidential information from being exposed.
### Deleting the source branch
When creating a merge request, select the "Delete source branch
when merge request accepted" option and the source branch will be
deleted when the merge request is merged. To make this option
enabled by default for all new merge requests, enable it in the
[project's settings](../settings/index.md#merge-request-settings).
This option is also visible in an existing merge request next to
the merge request button and can be selected/deselected before merging.
It's only visible to users with [Maintainer permissions](../../permissions.md)
in the source project.
If the user viewing the merge request does not have the correct
permissions to delete the source branch and the source branch
is set for deletion, the merge request widget will show the
**Deletes source branch** text.
![Delete source branch status](img/remove_source_branch_status.png)
## Recommendations and best practices for Merge Requests
- When working locally in your branch, add multiple commits and only push when you're done, so GitLab will run only one pipeline for all the commits pushed at once. By doing so, you save pipeline minutes.
- Delete feature branches on merge or after merging them to keep your repository clean.
- Take one thing at a time and ship the smallest changes possible. By doing so, you'll have faster reviews and your changes will be less prone to errors.
- Don't use capital letters nor special chars in branch names.
This diff is collapsed.
......@@ -85,7 +85,7 @@ specific commit page.
You can append `?w=1` while on the diffs page of a merge request to ignore any
whitespace changes.
## Commenting on any file line in merge requests
## Perform inline code reviews
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/13950) in GitLab 11.5.
......@@ -94,20 +94,7 @@ in a Merge Request. To do so, click the **...** button in the gutter of the Merg
![Comment on any diff file line](img/comment-on-any-diff-line.png)
## Live preview with Review Apps
If you configured [Review Apps](https://about.gitlab.com/product/review-apps/) for your project,
you can preview the changes submitted to a feature-branch through a merge request
in a per-branch basis. No need to checkout the branch, install and preview locally;
all your changes will be available to preview by anyone with the Review Apps link.
With GitLab's [Route Maps](../../../ci/review_apps/index.md#route-maps) set, the
merge request widget takes you directly to the pages changed, making it easier and
faster to preview proposed modifications.
[Read more about Review Apps](../../../ci/review_apps/index.md).
## Pipeline status in merge requests
## Pipeline status in merge requests widgets
If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,
you will be able to see:
......@@ -135,6 +122,37 @@ be disabled. If the pipeline fails to deploy, the deployment info will be hidden
For more information, [read about pipelines](../../../ci/pipelines.md).
### Merge when pipeline succeeds (MWPS)
Set a merge request that looks ready to merge to [merge automatically when CI pipeline succeeds](merge_when_pipeline_succeeds.md).
### Live preview with Review Apps
If you configured [Review Apps](https://about.gitlab.com/product/review-apps/) for your project,
you can preview the changes submitted to a feature-branch through a merge request
in a per-branch basis. No need to checkout the branch, install and preview locally;
all your changes will be available to preview by anyone with the Review Apps link.
With GitLab's [Route Maps](../../../ci/review_apps/index.md#route-maps) set, the
merge request widget takes you directly to the pages changed, making it easier and
faster to preview proposed modifications.
[Read more about Review Apps](../../../ci/review_apps/index.md).
## Associated features
There is also a large number of features to associated to merge requests:
| Feature | Description |
|-------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Bulk editing merge requests](../../project/bulk_editing.md) | Update the attributes of multiple merge requests simultaneously. |
| [Cherry-pick changes](cherry_pick_changes.md) | Cherry-pick any commit in the UI by simply clicking the **Cherry-pick** button in a merged merge requests or a commit. |
| [Fast-forward merge requests](fast_forward_merge.md) | For a linear Git history and a way to accept merge requests without creating merge commits |
| [Find the merge request that introduced a change](versions.md) | When viewing the commit details page, GitLab will link to the merge request(s) containing that commit. |
| [Merge requests versions](versions.md) | Select and compare the different versions of merge request diffs |
| [Resolve conflicts](resolve_conflicts.md) | GitLab can provide the option to resolve certain merge request conflicts in the GitLab UI. |
| [Revert changes](revert_changes.md) | Revert changes from any commit from within a merge request. |
## Troubleshooting
Sometimes things don't go as expected in a merge request, here are some
......
......@@ -57,7 +57,7 @@ Set up your project's merge request settings:
- Enable [merge request approvals](../merge_requests/merge_request_approvals.md). **(STARTER)**
- Enable [merge only if pipeline succeeds](../merge_requests/merge_when_pipeline_succeeds.md).
- Enable [merge only when all threads are resolved](../../discussions/index.md#only-allow-merge-requests-to-be-merged-if-all-threads-are-resolved).
- Enable [`delete source branch after merge` option by default](../merge_requests/creating_merge_requests.md#deleting-the-source-branch)
- Enable [`delete source branch after merge` option by default](../merge_requests/getting_started.md#deleting-the-source-branch)
![project's merge request settings](img/merge_requests_settings.png)
......
......@@ -16,7 +16,8 @@ module Gitlab
ALLOWED_KEYS = %i[tags script only except rules type image services
allow_failure type stage when start_in artifacts cache
dependencies before_script needs after_script variables
environment coverage retry parallel extends interruptible timeout].freeze
environment coverage retry parallel extends interruptible timeout
resource_group].freeze
REQUIRED_BY_NEEDS = %i[stage].freeze
......@@ -48,6 +49,7 @@ module Gitlab
validates :dependencies, array_of_strings: true
validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true
validates :resource_group, type: String
end
validates :start_in, duration: { limit: '1 week' }, if: :delayed?
......@@ -156,7 +158,7 @@ module Gitlab
attributes :script, :tags, :allow_failure, :when, :dependencies,
:needs, :retry, :parallel, :extends, :start_in, :rules,
:interruptible, :timeout
:interruptible, :timeout, :resource_group
def self.matching?(name, config)
!name.to_s.start_with?('.') &&
......@@ -243,7 +245,8 @@ module Gitlab
artifacts: artifacts_value,
after_script: after_script_value,
ignore: ignored?,
needs: needs_defined? ? needs_value : nil }
needs: needs_defined? ? needs_value : nil,
resource_group: resource_group }
end
end
end
......
......@@ -18,6 +18,7 @@ module Gitlab
@seed_attributes = attributes
@previous_stages = previous_stages
@needs_attributes = dig(:needs_attributes)
@resource_group_key = attributes.delete(:resource_group_key)
@using_rules = attributes.key?(:rules)
@using_only = attributes.key?(:only)
......@@ -78,6 +79,7 @@ module Gitlab
else
::Ci::Build.new(attributes).tap do |job|
job.deployment = Seed::Deployment.new(job).to_resource
job.resource_group = Seed::Build::ResourceGroup.new(job, @resource_group_key).to_resource
end
end
end
......
# frozen_string_literal: true
module Gitlab
module Ci
module Pipeline
module Seed
class Build
class ResourceGroup < Seed::Base
include Gitlab::Utils::StrongMemoize
attr_reader :build, :resource_group_key
def initialize(build, resource_group_key)
@build = build
@resource_group_key = resource_group_key
end
def to_resource
return unless Feature.enabled?(:ci_resource_group, build.project)
return unless resource_group_key.present?
resource_group = build.project.resource_groups
.safe_find_or_create_by(key: expanded_resource_group_key)
resource_group if resource_group.persisted?
end
private
def expanded_resource_group_key
strong_memoize(:expanded_resource_group_key) do
ExpandVariables.expand(resource_group_key, -> { build.simple_variables })
end
end
end
end
end
end
end
end
......@@ -64,6 +64,7 @@ module Gitlab
except: job[:except],
rules: job[:rules],
cache: job[:cache],
resource_group_key: job[:resource_group],
options: {
image: job[:image],
services: job[:services],
......
......@@ -207,6 +207,14 @@ FactoryBot.define do
trigger_request factory: :ci_trigger_request
end
trait :resource_group do
waiting_for_resource_at { 5.minutes.ago }
after(:build) do |build, evaluator|
build.resource_group = create(:ci_resource_group, project: build.project)
end
end
after(:build) do |build, evaluator|
build.project ||= build.pipeline.project
end
......
# frozen_string_literal: true
FactoryBot.define do
factory :ci_resource, class: Ci::Resource do
resource_group factory: :ci_resource_group
trait(:retained) do
build factory: :ci_build
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :ci_resource_group, class: Ci::ResourceGroup do
project
sequence(:key) { |n| "IOS_#{n}" }
end
end
......@@ -97,7 +97,8 @@ describe 'User comments on a diff', :js do
end
context 'multiple suggestions in expanded lines' do
it 'suggestions are appliable' do
# https://gitlab.com/gitlab-org/gitlab/issues/38277
it 'suggestions are appliable', :quarantine do
diff_file = merge_request.diffs(paths: ['files/ruby/popen.rb']).diff_files.first
hash = Digest::SHA1.hexdigest(diff_file.file_path)
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Ci::Pipeline::Seed::Build::ResourceGroup do
let_it_be(:project) { create(:project) }
let(:job) { build(:ci_build, project: project) }
let(:seed) { described_class.new(job, resource_group_key) }
describe '#to_resource' do
subject { seed.to_resource }
context 'when resource group key is specified' do
let(:resource_group_key) { 'iOS' }
it 'returns a resource group object' do
is_expected.to be_a(Ci::ResourceGroup)
expect(subject.key).to eq('iOS')
end
context 'when environment has an invalid URL' do
let(:resource_group_key) { ':::' }
it 'returns nothing' do
is_expected.to be_nil
end
end
context 'when there is a resource group already' do
let!(:resource_group) { create(:ci_resource_group, project: project, key: 'iOS') }
it 'does not create a new resource group' do
expect { subject }.not_to change { Ci::ResourceGroup.count }
end
end
end
context 'when resource group key is nil' do
let(:resource_group_key) { nil }
it 'returns nothing' do
is_expected.to be_nil
end
end
end
end
......@@ -231,6 +231,15 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
end
end
end
context 'when job belongs to a resource group' do
let(:attributes) { { name: 'rspec', ref: 'master', resource_group_key: 'iOS' } }
it 'returns a job with resource group' do
expect(subject.resource_group).not_to be_nil
expect(subject.resource_group.key).to eq('iOS')
end
end
end
context 'when job is a bridge' do
......
......@@ -241,6 +241,21 @@ module Gitlab
end
end
end
describe 'resource group' do
context 'when resource group is defined' do
let(:config) do
YAML.dump(rspec: {
script: 'test',
resource_group: 'iOS'
})
end
it 'has the attributes' do
expect(subject[:resource_group_key]).to eq 'iOS'
end
end
end
end
describe '#stages_attributes' do
......
......@@ -444,6 +444,7 @@ project:
- service_desk_setting
- import_failures
- container_expiration_policy
- resource_groups
award_emoji:
- awardable
- user
......
# frozen_string_literal: true
require 'spec_helper'
describe Ci::ResourceGroup do
describe 'validation' do
it 'valids when key includes allowed character' do
resource_group = build(:ci_resource_group, key: 'test')
expect(resource_group).to be_valid
end
it 'invalids when key includes invalid character' do
resource_group = build(:ci_resource_group, key: ':::')
expect(resource_group).not_to be_valid
end
end
describe '#ensure_resource' do
it 'creates one resource when resource group is created' do
resource_group = create(:ci_resource_group)
expect(resource_group.resources.count).to eq(1)
expect(resource_group.resources.all?(&:persisted?)).to eq(true)
end
end
describe '#assign_resource_to' do
subject { resource_group.assign_resource_to(build) }
let(:build) { create(:ci_build) }
let(:resource_group) { create(:ci_resource_group) }
it 'retains resource for the build' do
expect(resource_group.resources.first.build).to be_nil
is_expected.to eq(true)
expect(resource_group.resources.first.build).to eq(build)
end
context 'when there are no free resources' do
before do
resource_group.assign_resource_to(create(:ci_build))
end
it 'fails to retain resource' do
is_expected.to eq(false)
end
end
context 'when the build has already retained a resource' do
let!(:another_resource) { create(:ci_resource, resource_group: resource_group, build: build) }
it 'fails to retain resource' do
expect { subject }.to raise_error(ActiveRecord::RecordNotUnique)
end
end
end
describe '#release_resource_from' do
subject { resource_group.release_resource_from(build) }
let(:build) { create(:ci_build) }
let(:resource_group) { create(:ci_resource_group) }
context 'when the build has already retained a resource' do
before do
resource_group.assign_resource_to(build)
end
it 'releases resource from the build' do
expect(resource_group.resources.first.build).to eq(build)
is_expected.to eq(true)
expect(resource_group.resources.first.build).to be_nil
end
end
context 'when the build has already released a resource' do
it 'fails to release resource' do
is_expected.to eq(false)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Ci::Resource do
describe '.free' do
subject { described_class.free }
let(:resource_group) { create(:ci_resource_group) }
let!(:free_resource) { resource_group.resources.take }
let!(:retained_resource) { create(:ci_resource, :retained, resource_group: resource_group) }
it 'returns free resources' do
is_expected.to eq([free_resource])
end
end
describe '.retained_by' do
subject { described_class.retained_by(build) }
let(:build) { create(:ci_build) }
let!(:resource) { create(:ci_resource, build: build) }
it 'returns retained resources' do
is_expected.to eq([resource])
end
end
end
......@@ -4,7 +4,7 @@ require 'spec_helper'
describe SafeUrl do
describe '#safe_url' do
class TestClass
class SafeUrlTestClass
include SafeUrl
attr_reader :url
......@@ -14,7 +14,7 @@ describe SafeUrl do
end
end
let(:test_class) { TestClass.new(url) }
let(:test_class) { SafeUrlTestClass.new(url) }
let(:url) { 'http://example.com' }
subject { test_class.safe_url }
......
......@@ -915,6 +915,44 @@ describe Ci::CreatePipelineService do
end
end
context 'with resource group' do
context 'when resource group is defined' do
before do
config = YAML.dump(
test: { stage: 'test', script: 'ls', resource_group: resource_group_key }
)
stub_ci_pipeline_yaml_file(config)
end
let(:resource_group_key) { 'iOS' }
it 'persists the association correctly' do
result = execute_service
deploy_job = result.builds.find_by_name!(:test)
resource_group = project.resource_groups.find_by_key!(resource_group_key)
expect(result).to be_persisted
expect(deploy_job.resource_group.key).to eq(resource_group_key)
expect(project.resource_groups.count).to eq(1)
expect(resource_group.builds.count).to eq(1)
expect(resource_group.resources.count).to eq(1)
expect(resource_group.resources.first.build).to eq(nil)
end
context 'when resourc group key includes predefined variables' do
let(:resource_group_key) { '$CI_COMMIT_REF_NAME-$CI_JOB_NAME' }
it 'interpolates the variables into the key correctly' do
result = execute_service
expect(result).to be_persisted
expect(project.resource_groups.exists?(key: 'master-test')).to eq(true)
end
end
end
end
context 'with timeout' do
context 'when builds with custom timeouts are configured' do
before do
......
......@@ -31,7 +31,7 @@ describe Ci::RetryBuildService do
job_artifacts_container_scanning job_artifacts_dast
job_artifacts_license_management job_artifacts_performance
job_artifacts_codequality job_artifacts_metrics scheduled_at
job_variables].freeze
job_variables waiting_for_resource_at].freeze
IGNORE_ACCESSORS =
%i[type lock_version target_url base_tags trace_sections
......@@ -40,14 +40,14 @@ describe Ci::RetryBuildService do
user_id auto_canceled_by_id retried failure_reason
sourced_pipelines artifacts_file_store artifacts_metadata_store
metadata runner_session trace_chunks upstream_pipeline_id
artifacts_file artifacts_metadata artifacts_size commands].freeze
artifacts_file artifacts_metadata artifacts_size commands resource resource_group_id].freeze
shared_examples 'build duplication' do
let(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let(:build) do
create(:ci_build, :failed, :expired, :erased, :queued, :coverage, :tags,
:allowed_to_fail, :on_tag, :triggered, :teardown_environment,
:allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group,
description: 'my-job', stage: 'test', stage_id: stage.id,
pipeline: pipeline, auto_canceled_by: another_pipeline,
scheduled_at: 10.seconds.since)
......
......@@ -3,7 +3,7 @@
require 'spec_helper'
describe QualifiedDomainArrayValidator do
class TestClass
class QualifiedDomainArrayValidatorTestClass
include ActiveModel::Validations
attr_accessor :domain_array
......@@ -14,7 +14,7 @@ describe QualifiedDomainArrayValidator do
end
let!(:record) do
TestClass.new(['gitlab.com'])
QualifiedDomainArrayValidatorTestClass.new(['gitlab.com'])
end
subject { validator.validate(record) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment