Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-04-13 09:10:26 +00:00
parent bddd19ac33
commit 0b679004a6
22 changed files with 730 additions and 273 deletions

View File

@ -13,8 +13,12 @@ import {
const ALLOWED_URL_HASHES = ['#diff', '#note'];
export default class Tracking {
static queuedEvents = [];
static nonInitializedQueue = [];
static initialized = false;
static definitionsLoaded = false;
static definitionsManifest = {};
static definitionsEventsQueue = [];
static definitions = [];
/**
* (Legacy) Determines if tracking is enabled at the user level.
@ -54,13 +58,71 @@ export default class Tracking {
}
if (!this.initialized) {
this.queuedEvents.push(eventData);
this.nonInitializedQueue.push(eventData);
return false;
}
return dispatchSnowplowEvent(...eventData);
}
/**
* Preloads event definitions.
*
* @returns {undefined}
*/
static loadDefinitions() {
// TODO: fetch definitions from the server and flush the queue
// See https://gitlab.com/gitlab-org/gitlab/-/issues/358256
this.definitionsLoaded = true;
while (this.definitionsEventsQueue.length) {
this.dispatchFromDefinition(...this.definitionsEventsQueue.shift());
}
}
/**
* Dispatches a structured event with data from its event definition.
*
* @param {String} basename
* @param {Object} eventData
* @returns {undefined|Boolean}
*/
static definition(basename, eventData = {}) {
if (!this.enabled()) {
return false;
}
if (!(basename in this.definitionsManifest)) {
throw new Error(`Missing Snowplow event definition "${basename}"`);
}
return this.dispatchFromDefinition(basename, eventData);
}
/**
* Builds an event with data from a valid definition and sends it to
* Snowplow. If the definitions are not loaded, it pushes the data to a queue.
*
* @param {String} basename
* @param {Object} eventData
* @returns {undefined|Boolean}
*/
static dispatchFromDefinition(basename, eventData) {
if (!this.definitionsLoaded) {
this.definitionsEventsQueue.push([basename, eventData]);
return false;
}
const eventDefinition = this.definitions.find((definition) => definition.key === basename);
return this.event(
eventData.category ?? eventDefinition.category,
eventData.action ?? eventDefinition.action,
eventData,
);
}
/**
* Dispatches any event emitted before initialization.
*
@ -69,8 +131,8 @@ export default class Tracking {
static flushPendingEvents() {
this.initialized = true;
while (this.queuedEvents.length) {
dispatchSnowplowEvent(...this.queuedEvents.shift());
while (this.nonInitializedQueue.length) {
dispatchSnowplowEvent(...this.nonInitializedQueue.shift());
}
}

View File

@ -1,5 +1,5 @@
- if @page&.persisted?
= link_to wiki_page_path(@wiki, @page, action: :history), class: "btn gl-button", role: "button", data: { qa_selector: 'page_history_button' } do
= link_to wiki_page_path(@wiki, @page, action: :history), class: "btn gl-button btn-default", role: "button", data: { qa_selector: 'page_history_button' } do
= s_("Wiki|Page history")
- if can?(current_user, :create_wiki, @wiki.container)
= link_to wiki_path(@wiki, action: :new), class: "btn gl-button btn-confirm-secondary", role: "button", data: { qa_selector: 'new_page_button' } do

View File

@ -168,6 +168,7 @@ options:
- p_ci_templates_kaniko
- p_ci_templates_qualys_iac_security
- p_ci_templates_liquibase
- p_ci_templates_matlab
distribution:
- ce
- ee

View File

@ -0,0 +1,25 @@
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_matlab_monthly
description: ""
product_section: ""
product_stage: ""
product_group: ""
product_category: ""
value_type: number
status: active
milestone: "14.10"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82914
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- p_ci_templates_matlab

View File

@ -168,6 +168,7 @@ options:
- p_ci_templates_kaniko
- p_ci_templates_qualys_iac_security
- p_ci_templates_liquibase
- p_ci_templates_matlab
distribution:
- ce
- ee

View File

@ -0,0 +1,25 @@
---
key_path: redis_hll_counters.ci_templates.p_ci_templates_matlab_weekly
description: ""
product_section: ""
product_stage: ""
product_group: ""
product_category: ""
value_type: number
status: active
milestone: "14.10"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82914
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
distribution:
- ce
- ee
tier:
- free
- premium
- ultimate
options:
events:
- p_ci_templates_matlab

View File

@ -430,7 +430,7 @@ to any of the [available reference architectures](#available-reference-architect
> - Required domain knowledge: PostgreSQL, HAProxy, shared storage, distributed systems
GitLab supports [zero-downtime upgrades](../../update/zero_downtime.md).
Single GitLab nodes can be updated with only a [few minutes of downtime](../../update/zero_downtime.md#single-node-deployment).
Single GitLab nodes can be updated with only a [few minutes of downtime](../../update/index.md#upgrade-based-on-installation-method).
To avoid this, we recommend to separate GitLab into several application nodes.
As long as at least one of each component is online and capable of handling the instance's usage load, your team's productivity will not be interrupted during the update.

View File

@ -15,14 +15,12 @@ there are the following requirements:
sequence [and leave the database schema in a broken state](https://gitlab.com/gitlab-org/gitlab/-/issues/321542).
- You have to use [post-deployment migrations](../development/database/post_deployment_migrations.md).
- You are using PostgreSQL. Starting from GitLab 12.1, MySQL is not supported.
- Multi-node GitLab instance. Single-node instances may experience brief interruptions
[as services restart (Puma in particular)](#single-node-deployment).
- You have set up a multi-node GitLab instance. Single-node instances do not support zero-downtime upgrades.
If you meet all the requirements above, follow these instructions in order. There are three sets of steps, depending on your deployment type:
| Deployment type | Description |
| --------------------------------------------------------------- | ------------------------------------------------ |
| [Single-node](#single-node-deployment) | GitLab CE/EE on a single node |
| [Gitaly Cluster](#gitaly-cluster) | GitLab CE/EE using HA architecture for Gitaly Cluster |
| [Multi-node / PostgreSQL HA](#use-postgresql-ha) | GitLab CE/EE using HA architecture for PostgreSQL |
| [Multi-node / Redis HA](#use-redis-ha-using-sentinel) | GitLab CE/EE using HA architecture for Redis |
@ -87,80 +85,6 @@ migrations this could potentially lead to hours of downtime, depending on the
size of your database. To work around this you must use PostgreSQL and
meet the other online upgrade requirements mentioned above.
## Single-node deployment
WARNING:
You can only upgrade one minor release at a time.
Before following these instructions, note the following **important** information:
- You can only upgrade one minor release at a time. So from 13.6 to 13.7, not to 13.8.
If you attempt more than one minor release, the upgrade may fail.
- On single-node Omnibus deployments, updates with no downtime are not possible when
using Puma because Puma always requires a complete restart. This is because the
[phased restart](https://github.com/puma/puma/blob/master/README.md#clustered-mode)
feature of Puma does not work with the way it is configured in GitLab all-in-one
packages (cluster-mode with app preloading).
- While it is possible to minimize downtime on a single-node instance by following
these instructions, **it is not possible to always achieve true zero downtime
updates**. Users may see some connections timeout or be refused for a few minutes,
depending on which services need to restart.
- On Omnibus deployments, the `/etc/gitlab/gitlab.rb` configuration file must **not** have
`gitlab_rails['auto_migrate'] = true`.
1. Create an empty file at `/etc/gitlab/skip-auto-reconfigure`. This prevents upgrades from running `gitlab-ctl reconfigure`, which by default automatically stops GitLab, runs all database migrations, and restarts GitLab.
```shell
sudo touch /etc/gitlab/skip-auto-reconfigure
```
1. Update the GitLab package:
- For GitLab [Enterprise Edition](https://about.gitlab.com/pricing/):
```shell
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install gitlab-ee
# Centos/RHEL
sudo yum install gitlab-ee
```
- For GitLab Community Edition:
```shell
# Debian/Ubuntu
sudo apt-get update
sudo apt-get install gitlab-ce
# Centos/RHEL
sudo yum install gitlab-ce
```
1. To get the regular migrations and latest code in place, run
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-ctl reconfigure
```
1. Once the node is updated and `reconfigure` finished successfully, run post-deployment migrations with
```shell
sudo gitlab-rake db:migrate
```
1. Hot reload `puma` and `sidekiq` services
```shell
sudo gitlab-ctl hup puma
sudo gitlab-ctl restart sidekiq
```
If you do not want to run zero downtime upgrades in the future, make
sure you remove `/etc/gitlab/skip-auto-reconfigure` after
you've completed these steps.
## Multi-node / HA deployment
WARNING:

View File

@ -38,4 +38,4 @@ Merge request approval settings that can be set at an instance level are:
See also the following, which are affected by instance-level rules:
- [Project merge request approval rules](../project/merge_requests/approvals/index.md).
- [Group merge request approval rules](../group/index.md#group-approval-rules) available in GitLab 13.9 and later.
- [Group merge request approval settings](../group/index.md#group-approval-settings) available in GitLab 13.9 and later.

View File

@ -190,6 +190,26 @@ of your GitLab instance (`.gitlab-ci.yml` if not set):
It is also possible to specify a [custom CI/CD configuration file for a specific project](../../../ci/pipelines/settings.md#specify-a-custom-cicd-configuration-file).
## Set CI/CD limits
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352175) in GitLab 14.10.
You can configure some [CI/CD limits](../../../administration/instance_limits.md#cicd-limits)
from the Admin Area:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Continuous Integration and Deployment** section.
1. In the **CI/CD limits** section, you can set the following limits:
- **Maximum number of jobs in a single pipeline**
- **Total number of jobs in currently active pipelines**
- **Maximum number of active pipelines per project**
- **Maximum number of pipeline subscriptions to and from a project**
- **Maximum number of pipeline schedules**
- **Maximum number of DAG dependencies that a job can have**
- **Maximum number of runners registered per group**
- **Maximum number of runners registered per project**
## Enable or disable the pipeline suggestion banner
By default, a banner displays in merge requests with no pipeline suggesting a

View File

@ -796,23 +796,25 @@ The group's new subgroups have push rules set for them based on either:
- The closest parent group with push rules defined.
- Push rules set at the instance level, if no parent groups have push rules defined.
## Group approval rules **(PREMIUM)**
## Group approval settings **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285458) in GitLab 13.9. [Deployed behind the `group_merge_request_approval_settings_feature_flag` flag](../../administration/feature_flags.md), disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/285410) in GitLab 14.5.
> - [Feature flag `group_merge_request_approval_settings_feature_flag`](https://gitlab.com/gitlab-org/gitlab/-/issues/343872) removed in GitLab 14.9.
Group approval rules manage [project merge request approval rules](../project/merge_requests/approvals/index.md)
at the top-level group level. These rules [cascade to all projects](../project/merge_requests/approvals/settings.md#settings-cascading)
Group approval settings manage [project merge request approval settings](../project/merge_requests/approvals/settings.md)
at the top-level group level. These settings [cascade to all projects](../project/merge_requests/approvals/settings.md#settings-cascading)
that belong to the group.
To view the merge request approval rules for a group:
To view the merge request approval settings for a group:
1. Go to the top-level group's **Settings > General** page.
1. Expand the **Merge request approvals** section.
1. Select the settings you want.
1. Select **Save changes**.
Support for group-level settings for merge request approval rules is tracked in this [epic](https://gitlab.com/groups/gitlab-org/-/epics/4367).
## Related topics
- [Group wikis](../project/wiki/index.md)

View File

@ -91,49 +91,29 @@ in the template you fetched to customize your configuration.
## GitLab-managed Terraform state
[Terraform remote backends](https://www.terraform.io/docs/language/settings/backends/index.html)
enable you to store the state file in a remote, shared store. GitLab uses the
[Terraform HTTP backend](https://www.terraform.io/docs/language/settings/backends/http.html)
to securely store the state files in local storage (the default) or
[the remote store of your choice](../../../administration/terraform_state.md).
The GitLab-managed Terraform state backend can safely store your Terraform state. It spares you from setting up additional remote resources like
Amazon S3 or Google Cloud Storage. Its features include:
- Supporting encryption of the state file both in transit and at rest.
- Locking and unlocking state.
- Remote Terraform plan and apply execution.
Read more about setting up and [using GitLab-managed Terraform states](terraform_state.md).
Use the [GitLab-managed Terraform state](terraform_state.md) to store state
files in local storage or in a remote store of your choice.
## Terraform module registry
GitLab can be used as a [Terraform module registry](../../packages/terraform_module_registry/index.md)
to create and publish Terraform modules to a private registry specific to your
top-level namespace.
Use GitLab as a [Terraform module registry](../../packages/terraform_module_registry/index.md)
to create and publish Terraform modules to a private registry.
## Terraform integration in merge requests
Collaborating around Infrastructure as Code (IaC) changes requires both code changes
and expected infrastructure changes to be checked and approved. GitLab provides a
solution to help collaboration around Terraform code changes and their expected
effects using the merge request pages. This way users don't have to build custom
tools or rely on 3rd party solutions to streamline their IaC workflows.
Read more on setting up and [using the merge request integrations](mr_integration.md).
Use the [Terraform integration in merge requests](mr_integration.md)
to collaborate on Terraform code changes and Infrastructure-as-Code
workflows.
## The GitLab Terraform provider
WARNING:
NOTE:
The GitLab Terraform provider is released separately from GitLab.
We are working on migrating the GitLab Terraform provider for GitLab.com.
We are working on migrating the GitLab Terraform provider to GitLab.com.
You can use the [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab)
to manage various aspects of GitLab using Terraform. The provider is an open source project,
owned by GitLab, where everyone can contribute.
The [documentation of the provider](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs)
is available as part of the official Terraform provider documentation.
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).
## Create a new cluster through IaC

View File

@ -146,7 +146,7 @@ You can also enforce merge request approval settings:
- At the [instance level](../../../admin_area/merge_requests_approvals.md), which apply to all groups
on an instance and, therefore, all projects.
- On a [top-level group](../../../group/index.md#group-approval-rules), which apply to all subgroups
- On a [top-level group](../../../group/index.md#group-approval-settings), which apply to all subgroups
and projects.
If the settings are inherited by a group or project, they cannot be changed in the group or project

View File

@ -0,0 +1,96 @@
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/MATLAB.gitlab-ci.yml
# Use this template to run MATLAB and Simulink as part of your CI/CD pipeline. The template has three jobs:
# - `command`: Run MATLAB scripts, functions, and statements.
# - `test`: Run tests authored using the MATLAB unit testing framework or Simulink Test.
# - `test_artifacts_job`: Run MATLAB and Simulink tests, and generate test and coverage artifacts.
#
# You can copy and paste one or more jobs in this template into your `.gitlab-ci.yml` file.
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
#
# - To run MATLAB and Simulink, MATLAB must be installed on the runner that will run the jobs.
# The runner will use the topmost MATLAB version on the system path.
# The build fails if the operating system cannot find MATLAB on the path.
# - The jobs in this template use the `matlab -batch` syntax to start MATLAB. The `-batch` option is supported
# in MATLAB R2019a and later.
# The `command` runs MATLAB scripts, functions, and statements. To use the job in your pipeline,
# substitute `command` with the code you want to run.
#
command:
script: matlab -batch command
# If the value of `command` is the name of a MATLAB script or function, do not specify the file extension.
# For example, to run a script named `myscript.m` in the root of your repository, specify the `command` like this:
#
# "myscript"
#
# If you specify more than one script, function, or statement, use a comma or semicolon to separate them.
# For example, to run `myscript.m` in a folder named `myfolder` located in the root of the repository,
# you can specify the `command` like this:
#
# "addpath('myfolder'), myscript"
#
# MATLAB exits with exit code 0 if the specified script, function, or statement executes successfully without
# error. Otherwise, MATLAB terminates with a nonzero exit code, which causes the job to fail. To have the
# job fail in certain conditions, use the [`assert`][1] or [`error`][2] functions.
#
# [1] https://www.mathworks.com/help/matlab/ref/assert.html
# [2] https://www.mathworks.com/help/matlab/ref/error.html
# The `test` runs the MATLAB and Simulink tests in your project. It calls the [`runtests`][3] function
# to run the tests and then the [`assertSuccess`][4] method to fail the job if any of the tests fail.
#
test:
script: matlab -batch "results = runtests('IncludeSubfolders',true), assertSuccess(results);"
# By default, the job includes any files in your [MATLAB Project][5] that have a `Test` label. If your repository
# does not have a MATLAB project, then the job includes all tests in the root of your repository or in any of
# its subfolders.
#
# [3] https://www.mathworks.com/help/matlab/ref/runtests.html
# [4] https://www.mathworks.com/help/matlab/ref/matlab.unittest.testresult.assertsuccess.html
# [5] https://www.mathworks.com/help/matlab/projects.html
# The `test_artifacts_job` runs your tests and additionally generates test and coverage artifacts.
# It uses the plugin classes in the [`matlab.unittest.plugins`][6] package to generate a JUnit test results
# report and a Cobertura code coverage report. Like the `run_tests` job, this job runs all the tests in your
# project and fails the build if any of the tests fail.
#
test_artifacts_job:
script: |
matlab -batch "
import matlab.unittest.TestRunner
import matlab.unittest.Verbosity
import matlab.unittest.plugins.CodeCoveragePlugin
import matlab.unittest.plugins.XMLPlugin
import matlab.unittest.plugins.codecoverage.CoberturaFormat
suite = testsuite(pwd,'IncludeSubfolders',true);
[~,~] = mkdir('artifacts');
runner = TestRunner.withTextOutput('OutputDetail',Verbosity.Detailed);
runner.addPlugin(XMLPlugin.producingJUnitFormat('artifacts/results.xml'))
runner.addPlugin(CodeCoveragePlugin.forFolder(pwd,'IncludingSubfolders',true, ...
'Producing',CoberturaFormat('artifacts/cobertura.xml')))
results = runner.run(suite)
assertSuccess(results);"
artifacts:
reports:
junit: "./artifacts/results.xml"
cobertura: "./artifacts/cobertura.xml"
paths:
- "./artifacts"
# You can modify the contents of the `test_artifacts_job` depending on your goals. For more
# information on how to customize the test runner and generate various test and coverage artifacts,
# see [Generate Artifacts Using MATLAB Unit Test Plugins][7].
#
# [6] https://www.mathworks.com/help/matlab/ref/matlab.unittest.plugins-package.html
# [7] https://www.mathworks.com/help/matlab/matlab_prog/generate-artifacts-using-matlab-unit-test-plugins.html

View File

@ -619,3 +619,7 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
- name: p_ci_templates_matlab
category: ci_templates
redis_slot: ci_templates
aggregation: weekly

View File

@ -3,11 +3,11 @@
require 'spec_helper'
RSpec.describe "Admin Runners" do
include StubENV
include Spec::Support::Helpers::Features::RunnersHelpers
let_it_be(:admin) { create(:admin) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
admin = create(:admin)
sign_in(admin)
gitlab_enable_admin_mode_sign_in(admin)
@ -20,47 +20,54 @@ RSpec.describe "Admin Runners" do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace, creator: user) }
context "runners registration" do
before do
visit admin_runners_path
end
it_behaves_like "shows and resets runner registration token" do
let(:dropdown_text) { 'Register an instance runner' }
let(:registration_token) { Gitlab::CurrentSettings.runners_registration_token }
end
end
context "when there are runners" do
it 'has all necessary texts' do
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.zone.now)
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.week.ago)
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.year.ago)
context "with an instance runner" do
let!(:instance_runner) { create(:ci_runner, :instance) }
visit admin_runners_path
before do
visit admin_runners_path
end
expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Online runners 1"
expect(page).to have_text "Offline runners 2"
expect(page).to have_text "Stale runners 1"
end
it_behaves_like 'shows runner in list' do
let(:runner) { instance_runner }
end
it 'with an instance runner shows an instance badge' do
runner = create(:ci_runner, :instance)
it_behaves_like 'pauses, resumes and deletes a runner' do
let(:runner) { instance_runner }
end
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}']" do
expect(page).to have_selector '.badge', text: 'shared'
it 'shows an instance badge' do
within_runner_row(instance_runner.id) do
expect(page).to have_selector '.badge', text: 'shared'
end
end
end
it 'with a group runner shows a group badge' do
runner = create(:ci_runner, :group, groups: [group])
context "with multiple runners" do
before do
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.zone.now)
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.week.ago)
create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.year.ago)
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}']" do
expect(page).to have_selector '.badge', text: 'group'
visit admin_runners_path
end
end
it 'with a project runner shows a project badge' do
runner = create(:ci_runner, :project, projects: [project])
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}']" do
expect(page).to have_selector '.badge', text: 'specific'
it 'has all necessary texts' do
expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Online runners 1"
expect(page).to have_text "Offline runners 2"
expect(page).to have_text "Stale runners 1"
end
end
@ -72,44 +79,8 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}'] [data-label='Jobs']" do
expect(page).to have_content '2'
end
end
describe 'delete runner' do
let!(:runner) { create(:ci_runner, description: 'runner-foo') }
before do
visit admin_runners_path
within "[data-testid='runner-row-#{runner.id}']" do
click_on 'Delete runner'
end
end
it 'shows a confirmation modal' do
expect(page).to have_text "Delete runner ##{runner.id} (#{runner.short_sha})?"
expect(page).to have_text "Are you sure you want to continue?"
end
it 'deletes a runner' do
within '.modal' do
click_on 'Delete runner'
end
expect(page.find('.gl-toast')).to have_text(/Runner .+ deleted/)
expect(page).not_to have_content 'runner-foo'
end
it 'cancels runner deletion' do
within '.modal' do
click_on 'Cancel'
end
wait_for_requests
expect(page).to have_content 'runner-foo'
within_runner_row(runner.id) do
expect(find("[data-label='Jobs']")).to have_content '2'
end
end
@ -249,7 +220,7 @@ RSpec.describe "Admin Runners" do
expect(page).not_to have_content 'runner-paused'
expect(page).to have_content 'runner-never-contacted'
within "[data-testid='runner-row-#{never_contacted.id}']" do
within_runner_row(never_contacted.id) do
expect(page).to have_selector '.badge', text: 'never contacted'
end
end
@ -447,15 +418,7 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path
end
it 'has all necessary texts including no runner message' do
expect(page).to have_text "Register an instance runner"
expect(page).to have_text "Online runners 0"
expect(page).to have_text "Offline runners 0"
expect(page).to have_text "Stale runners 0"
expect(page).to have_text 'No runners found'
end
it_behaves_like "shows no runners"
it 'shows tabs with total counts equal to 0' do
expect(page).to have_link('All 0')
@ -484,17 +447,6 @@ RSpec.describe "Admin Runners" do
expect(page).to have_current_path(admin_runners_path('paused[]': 'true') )
end
end
describe "runners registration" do
before do
visit admin_runners_path
end
it_behaves_like "shows and resets runner registration token" do
let(:dropdown_text) { 'Register an instance runner' }
let(:registration_token) { Gitlab::CurrentSettings.runners_registration_token }
end
end
end
describe "Runner show page", :js do
@ -644,57 +596,4 @@ RSpec.describe "Admin Runners" do
end
end
end
private
def search_bar_selector
'[data-testid="runners-filtered-search"]'
end
# The filters must be clicked first to be able to receive events
# See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1493
def focus_filtered_search
page.within(search_bar_selector) do
page.find('.gl-filtered-search-term-token').click
end
end
def input_filtered_search_keys(search_term)
focus_filtered_search
page.within(search_bar_selector) do
page.find('input').send_keys(search_term)
click_on 'Search'
end
wait_for_requests
end
def open_filtered_search_suggestions(filter)
focus_filtered_search
page.within(search_bar_selector) do
click_on filter
end
wait_for_requests
end
def input_filtered_search_filter_is_only(filter, value)
focus_filtered_search
page.within(search_bar_selector) do
click_on filter
# For OPERATOR_IS_ONLY, clicking the filter
# immediately preselects "=" operator
page.find('input').send_keys(value)
page.find('input').send_keys(:enter)
click_on 'Search'
end
wait_for_requests
end
end

View File

@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe "Group Runners" do
include Spec::Support::Helpers::Features::RunnersHelpers
let_it_be(:group_owner) { create(:user) }
let_it_be(:group) { create(:group) }
let!(:group_registration_token) { group.runners_token }
let_it_be(:project) { create(:project, group: group) }
before do
group.add_owner(group_owner)
@ -14,7 +15,9 @@ RSpec.describe "Group Runners" do
end
describe "Group runners page", :js do
describe "runners registration" do
let!(:group_registration_token) { group.runners_token }
context "runners registration" do
before do
visit group_runners_path(group)
end
@ -24,5 +27,142 @@ RSpec.describe "Group Runners" do
let(:registration_token) { group_registration_token }
end
end
context "with no runners" do
before do
visit group_runners_path(group)
end
it_behaves_like "shows no runners"
it 'shows tabs with total counts equal to 0' do
expect(page).to have_link('All 0')
expect(page).to have_link('Group 0')
expect(page).to have_link('Project 0')
end
end
context "with an online group runner" do
let!(:group_runner) do
create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now)
end
before do
visit group_runners_path(group)
end
it_behaves_like 'shows runner in list' do
let(:runner) { group_runner }
end
it_behaves_like 'pauses, resumes and deletes a runner' do
let(:runner) { group_runner }
end
it 'shows a group badge' do
within_runner_row(group_runner.id) do
expect(page).to have_selector '.badge', text: 'group'
end
end
it 'can edit runner information' do
within_runner_row(group_runner.id) do
expect(find_link('Edit')[:href]).to end_with(edit_group_runner_path(group, group_runner))
end
end
end
context "with an online project runner" do
let!(:project_runner) do
create(:ci_runner, :project, projects: [project], description: 'runner-bar', contacted_at: Time.zone.now)
end
before do
visit group_runners_path(group)
end
it_behaves_like 'shows runner in list' do
let(:runner) { project_runner }
end
it_behaves_like 'pauses, resumes and deletes a runner' do
let(:runner) { project_runner }
end
it 'shows a project (specific) badge' do
within_runner_row(project_runner.id) do
expect(page).to have_selector '.badge', text: 'specific'
end
end
it 'can edit runner information' do
within_runner_row(project_runner.id) do
expect(find_link('Edit')[:href]).to end_with(edit_group_runner_path(group, project_runner))
end
end
end
context 'with a multi-project runner' do
let(:project) { create(:project, group: group) }
let(:project_2) { create(:project, group: group) }
let!(:runner) { create(:ci_runner, :project, projects: [project, project_2], description: 'group-runner') }
it 'user cannot remove the project runner' do
visit group_runners_path(group)
within_runner_row(runner.id) do
expect(page).to have_button 'Delete runner', disabled: true
end
end
end
context 'filtered search' do
before do
visit group_runners_path(group)
end
it 'allows user to search by paused and status', :js do
focus_filtered_search
page.within(search_bar_selector) do
expect(page).to have_link('Paused')
expect(page).to have_content('Status')
end
end
end
end
describe "Group runner edit page", :js do
let!(:runner) do
create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now)
end
it 'user edits the runner to be protected' do
visit edit_group_runner_path(group, runner)
expect(page.find_field('runner[access_level]')).not_to be_checked
check 'runner_access_level'
click_button 'Save changes'
expect(page).to have_content 'Protected Yes'
end
context 'when a runner has a tag' do
before do
runner.update!(tag_list: ['tag'])
end
it 'user edits runner not to run untagged jobs' do
visit edit_group_runner_path(group, runner)
expect(page.find_field('runner[run_untagged]')).to be_checked
uncheck 'runner_run_untagged'
click_button 'Save changes'
expect(page).to have_content 'Can run untagged jobs No'
end
end
end
end

View File

@ -129,6 +129,72 @@ describe('Tracking', () => {
});
});
describe('.definition', () => {
const TEST_VALID_BASENAME = '202108302307_default_click_button';
const TEST_EVENT_DATA = { category: undefined, action: 'click_button' };
let eventSpy;
let dispatcherSpy;
beforeAll(() => {
Tracking.definitionsManifest = {
'202108302307_default_click_button': 'config/events/202108302307_default_click_button.yml',
};
});
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
dispatcherSpy = jest.spyOn(Tracking, 'dispatchFromDefinition');
});
it('throws an error if the definition does not exists', () => {
const basename = '20220230_default_missing_definition';
const expectedError = new Error(`Missing Snowplow event definition "${basename}"`);
expect(() => Tracking.definition(basename)).toThrow(expectedError);
});
it('dispatches an event from a definition present in the manifest', () => {
Tracking.definition(TEST_VALID_BASENAME);
expect(dispatcherSpy).toHaveBeenCalledWith(TEST_VALID_BASENAME, {});
});
it('push events to the queue if not loaded', () => {
Tracking.definitionsLoaded = false;
Tracking.definitionsEventsQueue = [];
const dispatched = Tracking.definition(TEST_VALID_BASENAME);
expect(dispatched).toBe(false);
expect(Tracking.definitionsEventsQueue[0]).toStrictEqual([TEST_VALID_BASENAME, {}]);
expect(eventSpy).not.toHaveBeenCalled();
});
it('dispatch events when the definition is loaded', () => {
const definition = { key: TEST_VALID_BASENAME, ...TEST_EVENT_DATA };
Tracking.definitions = [{ ...definition }];
Tracking.definitionsEventsQueue = [];
Tracking.definitionsLoaded = true;
const dispatched = Tracking.definition(TEST_VALID_BASENAME);
expect(dispatched).not.toBe(false);
expect(Tracking.definitionsEventsQueue).toEqual([]);
expect(eventSpy).toHaveBeenCalledWith(definition.category, definition.action, {});
});
it('lets defined event data takes precedence', () => {
const definition = { key: TEST_VALID_BASENAME, category: undefined, action: 'click_button' };
const eventData = { category: TEST_CATEGORY };
Tracking.definitions = [{ ...definition }];
Tracking.definitionsLoaded = true;
Tracking.definition(TEST_VALID_BASENAME, eventData);
expect(eventSpy).toHaveBeenCalledWith(TEST_CATEGORY, definition.action, eventData);
});
});
describe('.enableFormTracking', () => {
it('tells snowplow to enable form tracking, with only explicit contexts', () => {
const config = { forms: { allow: ['form-class1'] }, fields: { allow: ['input-class1'] } };

View File

@ -6,11 +6,15 @@ RSpec.describe Gitlab::Auth::OAuth::User do
include LdapHelpers
let(:oauth_user) { described_class.new(auth_hash) }
let(:oauth_user_2) { described_class.new(auth_hash_2) }
let(:gl_user) { oauth_user.gl_user }
let(:gl_user_2) { oauth_user_2.gl_user }
let(:uid) { 'my-uid' }
let(:uid_2) { 'my-uid-2' }
let(:dn) { 'uid=user1,ou=people,dc=example' }
let(:provider) { 'my-provider' }
let(:auth_hash) { OmniAuth::AuthHash.new(uid: uid, provider: provider, info: info_hash) }
let(:auth_hash_2) { OmniAuth::AuthHash.new(uid: uid_2, provider: provider, info: info_hash) }
let(:info_hash) do
{
nickname: '-john+gitlab-ETC%.git@gmail.com',
@ -24,6 +28,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
end
let(:ldap_user) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
let(:ldap_user_2) { Gitlab::Auth::Ldap::Person.new(Net::LDAP::Entry.new, 'ldapmain') }
describe '.find_by_uid_and_provider' do
let(:dn) { 'CN=John Åström, CN=Users, DC=Example, DC=com' }
@ -46,12 +51,12 @@ RSpec.describe Gitlab::Auth::OAuth::User do
let!(:existing_user) { create(:omniauth_user, extern_uid: 'my-uid', provider: 'my-provider') }
it "finds an existing user based on uid and provider (facebook)" do
expect( oauth_user.persisted? ).to be_truthy
expect(oauth_user.persisted?).to be_truthy
end
it 'returns false if user is not found in database' do
allow(auth_hash).to receive(:uid).and_return('non-existing')
expect( oauth_user.persisted? ).to be_falsey
expect(oauth_user.persisted?).to be_falsey
end
end
@ -78,15 +83,27 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
stub_omniauth_config(allow_single_sign_on: [provider])
end
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: [provider])
oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user).to be_persisted
end
it 'does not repeat the default user password' do
oauth_user.save # rubocop:disable Rails/SaveBang
oauth_user_2.save # rubocop:disable Rails/SaveBang
expect(gl_user.password).not_to eq(gl_user_2.password)
end
it 'has the password length within specified range' do
oauth_user.save # rubocop:disable Rails/SaveBang
expect(gl_user.password.length).to be_between(Devise.password_length.min, Devise.password_length.max)
end
end
context 'when user confirmation email is enabled' do
@ -330,6 +347,12 @@ RSpec.describe Gitlab::Auth::OAuth::User do
allow(ldap_user).to receive(:name) { 'John Doe' }
allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] }
allow(ldap_user).to receive(:dn) { dn }
allow(ldap_user_2).to receive(:uid) { uid_2 }
allow(ldap_user_2).to receive(:username) { uid_2 }
allow(ldap_user_2).to receive(:name) { 'Beck Potter' }
allow(ldap_user_2).to receive(:email) { ['beckpotter@example.com', 'beck2@example.com'] }
allow(ldap_user_2).to receive(:dn) { dn }
end
context "and no account for the LDAP user" do
@ -340,6 +363,14 @@ RSpec.describe Gitlab::Auth::OAuth::User do
oauth_user.save # rubocop:disable Rails/SaveBang
end
it 'does not repeat the default user password' do
allow(Gitlab::Auth::Ldap::Person).to receive(:find_by_uid).and_return(ldap_user_2)
oauth_user_2.save # rubocop:disable Rails/SaveBang
expect(gl_user.password).not_to eq(gl_user_2.password)
end
it "creates a user with dual LDAP and omniauth identities" do
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
@ -609,6 +640,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'signup with SAML' do
let(:provider) { 'saml' }
let(:block_auto_created_users) { false }
before do
stub_omniauth_config({
@ -625,6 +657,13 @@ RSpec.describe Gitlab::Auth::OAuth::User do
it_behaves_like 'not being blocked on creation' do
let(:block_auto_created_users) { false }
end
it 'does not repeat the default user password' do
oauth_user.save # rubocop:disable Rails/SaveBang
oauth_user_2.save # rubocop:disable Rails/SaveBang
expect(gl_user.password).not_to eq(gl_user_2.password)
end
end
context 'signup with omniauth only' do

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'MATLAB.gitlab-ci.yml' do
subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('MATLAB') }
describe 'the created pipeline' do
let_it_be(:project) { create(:project, :auto_devops, :custom_repo, files: { 'README.md' => '' }) }
let(:user) { project.first_owner }
let(:default_branch) { 'master' }
let(:pipeline_branch) { default_branch }
let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
let(:pipeline) { service.execute!(:push).payload }
let(:build_names) { pipeline.builds.pluck(:name) }
before do
stub_ci_pipeline_yaml_file(template.content)
end
it 'creates all jobs' do
expect(build_names).to include('command', 'test', 'test_artifacts_job')
end
end
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
module Spec
module Support
module Helpers
module Features
module RunnersHelpers
def within_runner_row(runner_id)
within "[data-testid='runner-row-#{runner_id}']" do
yield
end
end
def search_bar_selector
'[data-testid="runners-filtered-search"]'
end
# The filters must be clicked first to be able to receive events
# See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1493
def focus_filtered_search
page.within(search_bar_selector) do
page.find('.gl-filtered-search-term-token').click
end
end
def input_filtered_search_keys(search_term)
focus_filtered_search
page.within(search_bar_selector) do
page.find('input').send_keys(search_term)
click_on 'Search'
end
wait_for_requests
end
def open_filtered_search_suggestions(filter)
focus_filtered_search
page.within(search_bar_selector) do
click_on filter
end
wait_for_requests
end
def input_filtered_search_filter_is_only(filter, value)
focus_filtered_search
page.within(search_bar_selector) do
click_on filter
# For OPERATOR_IS_ONLY, clicking the filter
# immediately preselects "=" operator
page.find('input').send_keys(value)
page.find('input').send_keys(:enter)
click_on 'Search'
end
wait_for_requests
end
end
end
end
end
end

View File

@ -2,6 +2,7 @@
RSpec.shared_examples 'shows and resets runner registration token' do
include Spec::Support::Helpers::ModalHelpers
include Spec::Support::Helpers::Features::RunnersHelpers
before do
click_on dropdown_text
@ -60,3 +61,81 @@ RSpec.shared_examples 'shows and resets runner registration token' do
end
end
end
RSpec.shared_examples 'shows no runners' do
it 'shows counts with 0' do
expect(page).to have_text "Online runners 0"
expect(page).to have_text "Offline runners 0"
expect(page).to have_text "Stale runners 0"
end
it 'shows "no runners" message' do
expect(page).to have_text 'No runners found'
end
end
RSpec.shared_examples 'shows runner in list' do
it 'does not show empty state' do
expect(page).not_to have_content 'No runners found'
end
it 'shows runner row' do
within_runner_row(runner.id) do
expect(page).to have_text "##{runner.id}"
expect(page).to have_text runner.short_sha
expect(page).to have_text runner.description
end
end
end
RSpec.shared_examples 'pauses, resumes and deletes a runner' do
include Spec::Support::Helpers::ModalHelpers
it 'pauses and resumes runner' do
within_runner_row(runner.id) do
click_button "Pause"
expect(page).to have_text 'paused'
expect(page).to have_button 'Resume'
expect(page).not_to have_button 'Pause'
click_button "Resume"
expect(page).not_to have_text 'paused'
expect(page).not_to have_button 'Resume'
expect(page).to have_button 'Pause'
end
end
describe 'deletes runner' do
before do
within_runner_row(runner.id) do
click_on 'Delete runner'
end
end
it 'shows a confirmation modal' do
expect(page).to have_text "Delete runner ##{runner.id} (#{runner.short_sha})?"
expect(page).to have_text "Are you sure you want to continue?"
end
it 'deletes a runner' do
within_modal do
click_on 'Delete runner'
end
expect(page.find('.gl-toast')).to have_text(/Runner .+ deleted/)
expect(page).not_to have_content runner.description
end
it 'cancels runner deletion' do
within_modal do
click_on 'Cancel'
end
wait_for_requests
expect(page).to have_content runner.description
end
end
end