Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-03 06:08:34 +00:00
parent 2ead415d7a
commit 6b7b853dff
72 changed files with 897 additions and 299 deletions

View file

@ -22,8 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
def update
@group_link = @project.project_group_links.find(params[:id])
@group_link.update(group_link_params)
Projects::GroupLinks::UpdateService.new(@group_link).execute(group_link_params)
end
def destroy

View file

@ -22,7 +22,7 @@ module DesignManagement
validates :project, :filename, presence: true
validates :issue, presence: true, unless: :importing?
validates :filename, uniqueness: { scope: :issue_id }
validates :filename, uniqueness: { scope: :issue_id }, length: { maximum: 255 }
validate :validate_file_is_image
alias_attribute :title, :filename

View file

@ -15,8 +15,6 @@ class ProjectGroupLink < ApplicationRecord
scope :non_guests, -> { where('group_access > ?', Gitlab::Access::GUEST) }
after_commit :refresh_group_members_authorized_projects
alias_method :shared_with_group, :group
def self.access_options
@ -49,10 +47,6 @@ class ProjectGroupLink < ApplicationRecord
errors.add(:base, _("Project cannot be shared with the group it is in or one of its ancestors."))
end
end
def refresh_group_members_authorized_projects
group.refresh_members_authorized_projects
end
end
ProjectGroupLink.prepend_if_ee('EE::ProjectGroupLink')

View file

@ -13,6 +13,7 @@ module Projects
)
if link.save
group.refresh_members_authorized_projects
success(link: link)
else
error(link.errors.full_messages.to_sentence, 409)

View file

@ -12,7 +12,9 @@ module Projects
TodosDestroyer::ConfidentialIssueWorker.perform_in(Todo::WAIT_FOR_DELETE, nil, project.id)
end
group_link.destroy
group_link.destroy.tap do |link|
link.group.refresh_members_authorized_projects
end
end
end
end

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Projects
module GroupLinks
class UpdateService < BaseService
def initialize(group_link, user = nil)
super(group_link.project, user)
@group_link = group_link
end
def execute(group_link_params)
group_link.update!(group_link_params)
if requires_authorization_refresh?(group_link_params)
group_link.group.refresh_members_authorized_projects
end
end
private
attr_reader :group_link
def requires_authorization_refresh?(params)
params.include?(:group_access)
end
end
end
end

View file

@ -1,9 +1,9 @@
- if (@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project)
= link_to project_wikis_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do
= link_to project_wikis_new_path(@project), class: "add-new-wiki btn btn-success", role: "button", data: { qa_selector: 'new_page_button' } do
= s_("Wiki|New page")
= link_to project_wiki_history_path(@project, @page), class: "btn", role: "button" do
= link_to project_wiki_history_path(@project, @page), class: "btn", role: "button", data: { qa_selector: 'page_history_button' } do
= s_("Wiki|Page history")
- if can?(current_user, :create_wiki, @project) && @page.latest? && @valid_encoding
= link_to project_wiki_edit_path(@project, @page), class: "btn js-wiki-edit", role: "button" do
= link_to project_wiki_edit_path(@project, @page), class: "btn js-wiki-edit", role: "button", data: { qa_selector: 'edit_page_button' } do
= _("Edit")

View file

@ -5,7 +5,7 @@
= icon('angle-double-right')
- git_access_url = project_wikis_git_access_path(@project)
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '' do
= link_to git_access_url, class: active_nav_link?(path: 'wikis#git_access') ? 'active' : '', data: { qa_selector: 'clone_repository_link' } do
= icon('cloud-download', class: 'append-right-5')
%span= _("Clone repository")

View file

@ -9,7 +9,7 @@
= icon('angle-double-left')
.nav-text.flex-fill
%h2.wiki-page-title= @page.human_title
%h2.wiki-page-title{ data: { qa_selector: 'wiki_page_title' } }= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe

View file

@ -163,6 +163,14 @@
:weight: 1
:idempotent:
:tags: []
- :name: cronjob:metrics_dashboard_schedule_annotations_prune
:feature_category: :metrics
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: cronjob:namespaces_prune_aggregation_schedules
:feature_category: :source_code_management
:has_external_dependencies:
@ -1355,6 +1363,14 @@
:weight: 1
:idempotent: true
:tags: []
- :name: metrics_dashboard_prune_old_annotations
:feature_category: :metrics
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: migrate_external_diffs
:feature_category: :source_code_management
:has_external_dependencies:

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Metrics
module Dashboard
class PruneOldAnnotationsWorker
include ApplicationWorker
DELETE_LIMIT = 10_000
DEFAULT_CUT_OFF_PERIOD = 2.weeks
feature_category :metrics
idempotent! # in the scope of 24 hours
def perform
stale_annotations = ::Metrics::Dashboard::Annotation.ending_before(DEFAULT_CUT_OFF_PERIOD.ago.beginning_of_day)
stale_annotations.delete_with_limit(DELETE_LIMIT)
self.class.perform_async if stale_annotations.exists?
end
end
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
module Metrics
module Dashboard
class ScheduleAnnotationsPruneWorker
include ApplicationWorker
# rubocop:disable Scalability/CronWorkerContext
# This worker does not perform work scoped to a context
include CronjobQueue
# rubocop:enable Scalability/CronWorkerContext
feature_category :metrics
idempotent! # PruneOldAnnotationsWorker worker is idempotent in the scope of 24 hours
def perform
# Process is split into two jobs to avoid long running jobs, which are more prone to be disrupted
# mid work, which may cause some data not be delete, especially because cronjobs has retry option disabled
PruneOldAnnotationsWorker.perform_async
end
end
end
end

View file

@ -7,7 +7,9 @@ class RemoveExpiredGroupLinksWorker # rubocop:disable Scalability/IdempotentWork
feature_category :authentication_and_authorization
def perform
ProjectGroupLink.expired.destroy_all # rubocop: disable Cop/DestroyAll
ProjectGroupLink.expired.find_each do |link|
Projects::GroupLinks::DestroyService.new(link.project, nil).execute(link)
end
GroupGroupLink.expired.find_in_batches do |link_batch|
Groups::GroupLinks::DestroyService.new(nil, nil).execute(link_batch)

View file

@ -0,0 +1,5 @@
---
title: Remove metrics dashboard annotations attached to time periods older than two weeks.
merge_request: 32838
author:
type: added

View file

@ -481,6 +481,9 @@ Settings.cron_jobs['issue_due_scheduler_worker']['job_class'] = 'IssueDueSchedul
Settings.cron_jobs['prune_web_hook_logs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['prune_web_hook_logs_worker']['cron'] ||= '0 */1 * * *'
Settings.cron_jobs['prune_web_hook_logs_worker']['job_class'] = 'PruneWebHookLogsWorker'
Settings.cron_jobs['metrics_dashboard_schedule_annotations_prune_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['metrics_dashboard_schedule_annotations_prune_worker']['cron'] ||= '0 1 * * *'
Settings.cron_jobs['metrics_dashboard_schedule_annotations_prune_worker']['job_class'] = 'Metrics::Dashboard::ScheduleAnnotationsPruneWorker'
Settings.cron_jobs['schedule_migrate_external_diffs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['schedule_migrate_external_diffs_worker']['cron'] ||= '15 * * * *'
Settings.cron_jobs['schedule_migrate_external_diffs_worker']['job_class'] = 'ScheduleMigrateExternalDiffsWorker'

View file

@ -150,6 +150,8 @@
- 5
- - merge_request_mergeability_check
- 1
- - metrics_dashboard_prune_old_annotations
- 1
- - migrate_external_diffs
- 1
- - namespaceless_project_destroy

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
class UpdateResourceStateEventsConstraintToSupportEpicId < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
OLD_CONSTRAINT = 'resource_state_events_must_belong_to_issue_or_merge_request'
CONSTRAINT_NAME = 'resource_state_events_must_belong_to_issue_or_merge_request_or_epic'
def up
remove_check_constraint :resource_state_events, OLD_CONSTRAINT
add_check_constraint :resource_state_events, "(issue_id != NULL AND merge_request_id IS NULL AND epic_id IS NULL) OR " \
"(issue_id IS NULL AND merge_request_id != NULL AND epic_id IS NULL) OR" \
"(issue_id IS NULL AND merge_request_id IS NULL AND epic_id != NULL)", CONSTRAINT_NAME
end
def down
remove_check_constraint :resource_state_events, CONSTRAINT_NAME
add_check_constraint :resource_state_events, '(issue_id != NULL AND merge_request_id IS NULL) OR (merge_request_id != NULL AND issue_id IS NULL)', OLD_CONSTRAINT
end
end

View file

@ -5895,7 +5895,7 @@ CREATE TABLE public.resource_state_events (
created_at timestamp with time zone NOT NULL,
state smallint NOT NULL,
epic_id integer,
CONSTRAINT resource_state_events_must_belong_to_issue_or_merge_request CHECK ((((issue_id <> NULL::bigint) AND (merge_request_id IS NULL)) OR ((merge_request_id <> NULL::bigint) AND (issue_id IS NULL))))
CONSTRAINT resource_state_events_must_belong_to_issue_or_merge_request_or_ CHECK ((((issue_id <> NULL::bigint) AND (merge_request_id IS NULL) AND (epic_id IS NULL)) OR ((issue_id IS NULL) AND (merge_request_id <> NULL::bigint) AND (epic_id IS NULL)) OR ((issue_id IS NULL) AND (merge_request_id IS NULL) AND (epic_id <> NULL::integer))))
);
CREATE SEQUENCE public.resource_state_events_id_seq
@ -14026,6 +14026,7 @@ COPY "schema_migrations" (version) FROM STDIN;
20200515155620
20200518091745
20200518133123
20200519074709
20200519101002
20200519115908
20200519171058

View file

@ -1,6 +1,9 @@
Akismet
Alertmanager
Algolia
allowlist
allowlisting
allowlists
Ansible
Anthos
API
@ -29,6 +32,7 @@ autoscaling
awardable
Axios
Azure
B-tree
backport
backported
backporting
@ -57,6 +61,7 @@ CAS
CentOS
Chatops
Citrix
Citus
clonable
Cloudwatch
Cobertura
@ -83,6 +88,9 @@ deduplicated
deduplicates
deduplicating
deduplication
denylist
denylisting
denylists
deprovision
deprovisioned
deprovisioning
@ -114,6 +122,7 @@ Fluentd
Forgerock
Gantt
Gemnasium
gettext
Git
Gitaly
Gitea
@ -129,6 +138,7 @@ Gradle
Grafana
gravatar
Gzip
Haml
hardcode
hardcoded
hardcodes
@ -168,6 +178,7 @@ kanbans
Karma
Kerberos
Kibana
Kinesis
Knative
Kramdown
Kubernetes
@ -190,6 +201,10 @@ Markdown
markdownlint
Mattermost
mbox
memoization
memoize
memoized
memoizing
mergeable
Microsoft
middleware
@ -204,6 +219,8 @@ misconfiguration
misconfigurations
misconfiguring
mitigations
mixin
mixins
mockup
mockups
ModSecurity
@ -224,6 +241,7 @@ offboarded
offboarding
offboards
OmniAuth
onboarding
OpenID
OpenShift
Packagist
@ -235,6 +253,8 @@ Pipfiles
Piwik
PgBouncer
plaintext
Poedit
pooler
PostgreSQL
precompile
preconfigure
@ -299,6 +319,7 @@ reverified
reverifies
reverify
Rubix
Rubocop
runbook
runbooks
runit
@ -306,11 +327,13 @@ runtime
runtimes
Salesforce
SAML
sandboxing
sbt
Sendmail
Sentry
serverless
Sidekiq
Sisense
sharding
shfmt
Shibboleth
@ -330,6 +353,7 @@ spidering
Splunk
SpotBugs
SSH
Stackdriver
storable
strace
strikethrough

View file

@ -245,6 +245,9 @@ following command:
```ruby
Feature.enable(:junit_pipeline_view)
# Enable the feature for a specific project
Feature.enable(:junit_pipeline_view, Project.find(<your-project-id-here>))
```
## Viewing JUnit screenshots on GitLab

View file

@ -94,7 +94,7 @@ projects that need updating. Those projects can be:
[Geo admin panel](../user/admin_area/geo_nodes.md).
When we fail to fetch a repository on the secondary `RETRIES_BEFORE_REDOWNLOAD`
times, Geo does a so-called _redownload_. It will do a clean clone
times, Geo does a so-called _re-download_. It will do a clean clone
into the `@geo-temporary` directory in the root of the storage. When
it's successful, we replace the main repo with the newly cloned one.
@ -218,7 +218,7 @@ the performance of many synchronization operations.
FDW is a PostgreSQL extension ([`postgres_fdw`](https://www.postgresql.org/docs/11/postgres-fdw.html)) that is enabled within
the Geo Tracking Database (on a **secondary** node), which allows it
to connect to the readonly database replica and perform queries and filter
to connect to the read-only database replica and perform queries and filter
data from both instances.
This persistent connection is configured as an FDW server
@ -226,7 +226,7 @@ named `gitlab_secondary`. This configuration exists within the database's user
context only. To access the `gitlab_secondary`, GitLab needs to use the
same database user that had previously been configured.
The Geo Tracking Database accesses the readonly database replica via FDW as a regular user,
The Geo Tracking Database accesses the read-only database replica via FDW as a regular user,
limited by its own restrictions. The credentials are configured as a
`USER MAPPING` associated with the `SERVER` mapped previously
(`gitlab_secondary`).

View file

@ -54,7 +54,7 @@ The process for adding new Gitaly features is:
These steps often overlap. It is possible to use an unreleased version
of Gitaly and `gitaly-proto` during testing and development.
- See the [Gitaly repo](https://gitlab.com/gitlab-org/gitaly/blob/master/CONTRIBUTING.md#development-and-testing-with-a-custom-gitaly-proto) for instructions on writing server side code with an unreleased protocol.
- See the [Gitaly repository](https://gitlab.com/gitlab-org/gitaly/blob/master/CONTRIBUTING.md#development-and-testing-with-a-custom-gitaly-proto) for instructions on writing server side code with an unreleased protocol.
- See [below](#running-tests-with-a-locally-modified-version-of-gitaly) for instructions on running GitLab CE tests with a modified version of Gitaly.
- In GDK run `gdk install` and restart `gdk run` (or `gdk run app`) to use a locally modified Gitaly version for development
@ -67,7 +67,7 @@ This should make it easier to contribute for developers who are less
comfortable writing Go code.
There is documentation for this approach in [the Gitaly
repo](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/ruby_endpoint.md).
repository](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/ruby_endpoint.md).
## Gitaly-Related Test Failures
@ -323,8 +323,8 @@ the integration by using GDK:
1. Navigate to GDK's root directory.
1. Make sure you have the proper branch checked out for Gitaly.
1. Recompile it with `make gitaly-setup` and restart the service with `gdk restart gitaly`.
1. Make sure your setup is runnig: `gdk status | grep praefect`.
1. Check what config file is used: `cat ./services/praefect/run | grep praefect` value of the `-config` flag
1. Make sure your setup is running: `gdk status | grep praefect`.
1. Check what configuration file is used: `cat ./services/praefect/run | grep praefect` value of the `-config` flag
1. Uncomment `prometheus_listen_addr` in the configuration file and run `gdk restart gitaly`.
1. Make sure that the flag is not enabled yet:

View file

@ -155,7 +155,7 @@ refresh_service.execute(oldrev, newrev, ref)
See ["Why is it bad style to `rescue Exception => e` in Ruby?"](https://stackoverflow.com/questions/10048173/why-is-it-bad-style-to-rescue-exception-e-in-ruby).
_**Note:** This rule is [enforced automatically by
Rubocop](https://gitlab.com/gitlab-org/gitlab-foss/blob/8-4-stable/.rubocop.yml#L911-914)._
RuboCop](https://gitlab.com/gitlab-org/gitlab-foss/blob/8-4-stable/.rubocop.yml#L911-914)._
## Do not use inline JavaScript in views

View file

@ -1,6 +1,6 @@
# Hash Indexes
PostgreSQL supports hash indexes besides the regular btree
PostgreSQL supports hash indexes besides the regular B-tree
indexes. Hash indexes however are to be avoided at all costs. While they may
_sometimes_ provide better performance the cost of rehashing can be very high.
More importantly: at least until PostgreSQL 10.0 hash indexes are not
@ -17,4 +17,4 @@ documentation:
RuboCop is configured to register an offense when it detects the use of a hash
index.
Instead of using hash indexes you should use regular btree indexes.
Instead of using hash indexes you should use regular B-tree indexes.

View file

@ -5,6 +5,7 @@ are very appreciative of the work done by translators and proofreaders!
## Proofreaders
<!-- vale gitlab.Spelling = NO -->
- Albanian
- Proofreaders needed.
- Amharic
@ -104,6 +105,7 @@ are very appreciative of the work done by translators and proofreaders!
- Andrew Vityuk - [GitLab](https://gitlab.com/3_1_3_u), [CrowdIn](https://crowdin.com/profile/andruwa13)
- Welsh
- Proofreaders needed.
<!-- vale gitlab.Spelling = YES -->
## Become a proofreader

View file

@ -79,8 +79,10 @@ ethnicity.
In languages which distinguish between a male and female form, use both or
choose a neutral formulation.
<!-- vale gitlab.Spelling = NO -->
For example in German, the word "user" can be translated into "Benutzer" (male) or "Benutzerin" (female).
Therefore "create a new user" would translate into "Benutzer(in) anlegen".
<!-- vale gitlab.Spelling = YES -->
### Updating the glossary
@ -91,6 +93,8 @@ To propose additions to the glossary please
### Inclusive language in French
<!-- vale gitlab.Spelling = NO -->
In French, the "écriture inclusive" is now over (see on [Legifrance](https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036068906&categorieLien=id)).
So, to include both genders, write “Utilisateurs et utilisatrices” instead of “Utilisateur·rice·s”.
When space is missing, the male gender should be used alone.
<!-- vale gitlab.Spelling = YES -->

View file

@ -119,9 +119,9 @@ without measuring anything.
Three values are measured for a block:
- The real time elapsed, stored in NAME_real_time.
- The CPU time elapsed, stored in NAME_cpu_time.
- The call count, stored in NAME_call_count.
- The real time elapsed, stored in `NAME_real_time`.
- The CPU time elapsed, stored in `NAME_cpu_time`.
- The call count, stored in `NAME_call_count`.
Both the real and CPU timings are measured in milliseconds.

View file

@ -15,7 +15,7 @@ scanner, as well as requirements and guidelines for the Docker image.
## Job definition
This section desribes several important fields to add to the security scanner's job
This section describes several important fields to add to the security scanner's job
definition file. Full documentation on these and other available fields can be viewed
in the [CI documentation](../../ci/yaml/README.md#image).
@ -89,9 +89,9 @@ for variables such as `DEPENDENCY_SCANNING_DISABLED`, `CONTAINER_SCANNING_DISABL
disable running the custom scanner.
GitLab also defines a `CI_PROJECT_REPOSITORY_LANGUAGES` variable, which provides the list of
languages in the repo. Depending on this value, your scanner may or may not do something different.
languages in the repository. Depending on this value, your scanner may or may not do something different.
Language detection currently relies on the [`linguist`](https://github.com/github/linguist) Ruby gem.
See [GitLab CI/CD prefined variables](../../ci/variables/predefined_variables.md#variables-reference).
See [GitLab CI/CD predefined variables](../../ci/variables/predefined_variables.md#variables-reference).
#### Policy checking example

View file

@ -54,7 +54,7 @@ best place to integrate your own product and its results into GitLab.
## How to onboard
This section describes the steps you need to complete to onboard as a partner
and complete an intgration with the Secure stage.
and complete an integration with the Secure stage.
1. Read about our [partnerships](https://about.gitlab.com/partners/integrate/).
1. [Create an issue](https://gitlab.com/gitlab-com/alliances/alliances/-/issues/new?issuable_template=new_partner)

View file

@ -47,7 +47,7 @@ POST /internal/allowed
| `protocol` | string | yes | SSH when called from GitLab-shell, HTTP or SSH when called from Gitaly |
| `action` | string | yes | Git command being run (`git-upload-pack`, `git-receive-pack`, `git-upload-archive`) |
| `changes` | string | yes | `<oldrev> <newrev> <refname>` when called from Gitaly, The magic string `_any` when called from GitLab Shell |
| `check_ip` | string | no | Ip address from which call to GitLab Shell was made |
| `check_ip` | string | no | IP address from which call to GitLab Shell was made |
Example request:

View file

@ -359,7 +359,7 @@ end
1. If you add a new file, submit an issue to the [production
tracker](https://gitlab.com/gitlab-com/gl-infra/production/-/issues) or
a merge request to the [gitlab_fluentd](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
a merge request to the [`gitlab_fluentd`](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/-/merge_requests/51/diffs).
1. Be sure to update the [GitLab CE/EE documentation](../administration/logs.md) and the [GitLab.com

View file

@ -35,7 +35,7 @@ and post-deployment migrations (`db/post_migrate`) are run after the deployment
## Schema Changes
Changes to the schema should be commited to `db/structure.sql`. This
Changes to the schema should be committed to `db/structure.sql`. This
file is automatically generated by Rails, so you normally should not
edit this file by hand. If your migration is adding a column to a
table, that column will be added at the bottom. Please do not reorder
@ -49,7 +49,7 @@ regenerate a clean `db/structure.sql` for the migrations you're
adding. This script will apply all migrations found in `db/migrate`
or `db/post_migrate`, so if there are any migrations you don't want to
commit to the schema, rename or remove them. If your branch is not
targetting `master` you can set the `TARGET` environment variable.
targeting `master` you can set the `TARGET` environment variable.
```shell
# Regenerate schema against `master`
@ -343,7 +343,7 @@ def up
end
```
The RuboCop rule generally allows standard Rails migration methods, listed below. This example will cause a rubocop offense:
The RuboCop rule generally allows standard Rails migration methods, listed below. This example will cause a Rubocop offense:
```ruby
disabled_ddl_transaction!

View file

@ -1,4 +1,4 @@
# Accessiblity
# Accessibility
Using semantic HTML plays a key role when it comes to accessibility.
@ -37,7 +37,7 @@ In forms we should use the `for` attribute in the label statement:
## Testing
1. On MacOS you can use [VoiceOver](https://www.apple.com/accessibility/mac/vision/) by pressing `cmd+F5`.
1. On Windows you can use [Narrator](https://www.microsoft.com/en-us/accessibility/windows) by pressing Windows logo key + Ctrl + Enter.
1. On Windows you can use [Narrator](https://www.microsoft.com/en-us/accessibility/windows) by pressing Windows logo key + Control + Enter.
## Online resources

View file

@ -5,7 +5,7 @@
We have a performance dashboard available in one of our [Grafana instances](https://dashboards.gitlab.net/d/1EBTz3Dmz/sitespeed-page-summary?orgId=1). This dashboard automatically aggregates metric data from [sitespeed.io](https://www.sitespeed.io/) every 6 hours. These changes are displayed after a set number of pages are aggregated.
These pages can be found inside a text file in the [`gitlab-build-images` repository](https://gitlab.com/gitlab-org/gitlab-build-images) called [`gitlab.txt`](https://gitlab.com/gitlab-org/gitlab-build-images/blob/master/scripts/gitlab.txt)
Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing urls of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team/) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
Any frontend engineer can contribute to this dashboard. They can contribute by adding or removing URLs of pages from this text file. Please have a [frontend monitoring expert](https://about.gitlab.com/company/team/) review your changes before assigning to a maintainer of the `gitlab-build-images` project. The changes will go live on the next scheduled run after the changes are merged into `master`.
There are 3 recommended high impact metrics to review on each page:

View file

@ -24,7 +24,7 @@ and write it to the Rails root. In the Omnibus packages, reconfigure writes the
The Omnibus design separates code (read-only, under `/opt/gitlab`) from data
(read/write, under `/var/opt/gitlab`) and logs (read/write, under
`/var/log/gitlab`). To make this happen the reconfigure script sets custom
paths where it can in GitLab config files, and where there are no path
paths where it can in GitLab configuration files, and where there are no path
settings, it uses symlinks.
For example, `config/gitlab.yml` is treated as data so that file is a symlink.

View file

@ -14,7 +14,7 @@ Groups and projects can have the following visibility levels:
- private (`0`) - an entity is visible only to the approved members of the entity
The visibility level of a group can be changed only if all subgroups and
subprojects have the same or lower visibility level. (e.g., a group can be set
sub-projects have the same or lower visibility level. (e.g., a group can be set
to internal only if all subgroups and projects are internal or private).
Visibility levels can be found in the `Gitlab::VisibilityLevel` module.
@ -92,10 +92,10 @@ into different features like Merge Requests and CI flow.
| Activity level | Resource | Locations |Permission dependency|
|----------------|----------|-----------|-----|
| View | License information | Dependency list, License Compliance | Can view repo |
| View | Dependency information | Dependency list, License Compliance | Can view repo |
| View | License information | Dependency list, License Compliance | Can view repository |
| View | Dependency information | Dependency list, License Compliance | Can view repository |
| View | Vulnerabilities information | Dependency list | Can view security findings |
| View | Black/Whitelisted licenses for the project | License Compliance, Merge request | Can view repo |
| View | Black/Whitelisted licenses for the project | License Compliance, Merge request | Can view repository |
| View | Security findings | Merge Request, CI job page, Pipeline security tab | Can read the project and CI jobs |
| View | Vulnerability feedback | Merge Request | Can read security findings |
| View | Dependency List page | Project | Can access Dependency information |

View file

@ -416,7 +416,7 @@ of the `gitlab-org/gitlab-foss` project. These jobs are only created in the foll
- `master` commits (pushes and scheduled pipelines).
- `gitlab-org/security/gitlab` merge requests.
- Merge requests which include `RUN AS-IF-FOSS` in their title.
- Merge requests that changes the CI config.
- Merge requests that changes the CI configuration.
The `* as-if-foss` jobs have the `FOSS_ONLY='1'` variable set and gets their EE-specific
folders removed before the tests start running.
@ -546,19 +546,19 @@ The current stages are:
- `post-qa`: This stage includes jobs that build reports or gather data from
the `qa` stage's jobs (e.g. Review App performance report).
- `pages`: This stage includes a job that deploys the various reports as
GitLab Pages (e.g. <https://gitlab-org.gitlab.io/gitlab/coverage-ruby/>,
<https://gitlab-org.gitlab.io/gitlab/coverage-javascript/>,
<https://gitlab-org.gitlab.io/gitlab/webpack-report/>).
GitLab Pages (e.g. [`coverage-ruby`](https://gitlab-org.gitlab.io/gitlab/coverage-ruby/),
[`coverage-javascript`](https://gitlab-org.gitlab.io/gitlab/coverage-javascript/),
[`webpack-report`](https://gitlab-org.gitlab.io/gitlab/webpack-report/).
### Default image
The default image is defined in <https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab-ci.yml>.
The default image is defined in [`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab-ci.yml).
It includes Ruby, Go, Git, Git LFS, Chrome, Node, Yarn, PostgreSQL, and Graphics Magick.
The images used in our pipelines are configured in the
[`gitlab-org/gitlab-build-images`](https://gitlab.com/gitlab-org/gitlab-build-images)
project, which is push-mirrored to <https://dev.gitlab.org/gitlab/gitlab-build-images>
project, which is push-mirrored to [`gitlab/gitlab-build-images`](https://dev.gitlab.org/gitlab/gitlab-build-images)
for redundancy.
The current version of the build images can be found in the
@ -600,7 +600,7 @@ then included in individual jobs via [`extends`](../ci/yaml/README.md#extends).
The `rules` definitions are composed of `if:` conditions and `changes:` patterns,
which are also defined in
<https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rules.gitlab-ci.yml>
[`rules.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rules.gitlab-ci.yml)
and included in `rules` definitions via [YAML anchors](../ci/yaml/README.md#anchors)
#### `if:` conditions

View file

@ -18,7 +18,7 @@ database.
Redis is a flat namespace with no hierarchy, which means we must pay attention
to key names to avoid collisions. Typically we use colon-separated elements to
provide a semblence of structure at application level. An example might be
provide a semblance of structure at application level. An example might be
`projects:1:somekey`.
Although we split our Redis usage into three separate purposes, and those may

View file

@ -69,7 +69,7 @@ expect(cleanForSnapshot(wrapper.element)).toMatchSnapshot();
### Examples
- [Pinning test in a haml to vue refactor](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27691#pinning-tests)
- [Pinning test in a Haml to Vue refactor](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27691#pinning-tests)
- [Pinning test in isolating a bug](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/32198#note_212736225)
- [Pinning test in refactoring dropdown](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28173)
- [Pinning test in refactoring vulnerability_details.vue](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25830/commits)

View file

@ -16,7 +16,7 @@ The more of the following that are true, the more likely you should choose the f
- You are not confident the new name is permanent.
- The feature is susceptible to bugs (large, complex, needing refactor, etc).
- The renaming will be difficult to review (feature spans many lines/files/repos).
- The renaming will be difficult to review (feature spans many lines, files, or repositories).
- The renaming will be disruptive in some way (database table renaming).
## Consider a façade-first approach

View file

@ -52,10 +52,10 @@ maintain and support one database with tables with many rows.
There are two ways to deal with this:
- Partioning. Locally split up tables data.
- Partitioning. Locally split up tables data.
- Sharding. Distribute data across multiple databases.
Partioning is a built-in PostgreSQL feature and requires minimal changes
Partitioning is a built-in PostgreSQL feature and requires minimal changes
in the application. However, it [requires PostgreSQL
11](https://www.2ndquadrant.com/en/blog/partitioning-evolution-postgresql-11/).
@ -246,9 +246,9 @@ lifting of many activities, including:
- Processing CI builds and pipelines.
The full list of jobs can be found in the
[app/workers](https://gitlab.com/gitlab-org/gitlab/tree/master/app/workers)
[`app/workers`](https://gitlab.com/gitlab-org/gitlab/tree/master/app/workers)
and
[ee/app/workers](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/workers)
[`ee/app/workers`](https://gitlab.com/gitlab-org/gitlab/tree/master/ee/app/workers)
directories in the GitLab code base.
#### Runaway Queues
@ -281,7 +281,7 @@ in a timely manner:
benefits.
From the Sidekiq logs, it's possible to see which jobs run the most
frequently and/or take the longest. For example, theis Kibana
frequently and/or take the longest. For example, these Kibana
visualizations show the jobs that consume the most total time:
![Most time-consuming Sidekiq jobs](img/sidekiq_most_time_consuming_jobs.png)

View file

@ -52,7 +52,7 @@ Some example of well implemented access controls and tests:
1. [example2](https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/2511/diffs#ed3aaab1510f43b032ce345909a887e5b167e196_142_155)
1. [example3](https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/3170/diffs?diff_id=17494)
**NB:** any input from development team is welcome, e.g. about rubocop rules.
**NB:** any input from development team is welcome, e.g. about Rubocop rules.
## Regular Expressions guidelines
@ -67,7 +67,7 @@ matches = re.findall("^bar$",text)
print(matches)
```
The Python example will output an emtpy array (`[]`) as the matcher considers the whole string `foo\nbar` including the newline (`\n`). In contrast Ruby's Regular Expression engine acts differently:
The Python example will output an empty array (`[]`) as the matcher considers the whole string `foo\nbar` including the newline (`\n`). In contrast Ruby's Regular Expression engine acts differently:
```ruby
text = "foo\nbar"
@ -111,7 +111,7 @@ or controls the regular expression (regex) used, and is able to enter user input
### Impact
The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes a long time to evaulate the bad regex match.
The resource, for example Unicorn, Puma, or Sidekiq, can be made to hang as it takes a long time to evaluate the bad regex match.
### Examples
@ -140,9 +140,9 @@ class Email < ApplicationRecord
GitLab has `Gitlab::UntrustedRegexp` which internally uses the [`re2`](https://github.com/google/re2/wiki/Syntax) library.
By utilizing `re2`, we get a strict limit on total execution time, and a smaller subset of available regex features.
All user-provided regexes should use `Gitlab::UntrustedRegexp`.
All user-provided regular expressions should use `Gitlab::UntrustedRegexp`.
For other regexes, here are a few guidelines:
For other regular expressions, here are a few guidelines:
- Remove unnecessary backtracking.
- Avoid nested quantifiers if possible.
@ -206,14 +206,14 @@ The [GitLab::HTTP](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab
`Outbound requests` options that allow instance administrators to block all internal connections, or limit the networks to which connections can be made.
In some cases, it has been possible to configure GitLab::HTTP as the HTTP
connection library for 3rd-party gems. This is preferrable over re-implementing
connection library for 3rd-party gems. This is preferable over re-implementing
the mitigations for a new feature.
- [More details](https://dev.gitlab.org/gitlab/gitlabhq/-/merge_requests/2530/diffs)
#### Feature-specific Mitigations
For situtions in which an allowlist or GitLab:HTTP cannot be used, it will be necessary to implement mitigations directly in the feature. It is best to validate the destination IP addresses themselves, not just domain names, as DNS can be controlled by the attacker. Below are a list of mitigations that should be implemented.
For situations in which an allowlist or GitLab:HTTP cannot be used, it will be necessary to implement mitigations directly in the feature. It is best to validate the destination IP addresses themselves, not just domain names, as DNS can be controlled by the attacker. Below are a list of mitigations that should be implemented.
**Important Note:** There are many tricks to bypass common SSRF validations. If feature-specific mitigations are necessary, they should be reviewed by the AppSec team, or a developer who has worked on SSRF mitigations previously.
@ -230,7 +230,7 @@ For situtions in which an allowlist or GitLab:HTTP cannot be used, it will be ne
- For HTTP connections: Disable redirects or validate the redirect destination
- To mitigate DNS rebinding attacks, validate and use the first IP address received
See [url_blocker_spec.rb](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/lib/gitlab/url_blocker_spec.rb) for examples of SSRF payloads
See [`url_blocker_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/lib/gitlab/url_blocker_spec.rb) for examples of SSRF payloads
## XSS guidelines

View file

@ -127,7 +127,7 @@ Below is an example of `data-track-*` attributes assigned to a button:
/>
```
Event listeners are bound at the document level to handle click events on or within elements with these data attributes. This allows for them to be properly handled on rerendering and changes to the DOM, but it's important to know that because of the way these events are bound, click events shouldn't be stopped from propagating up the DOM tree. If for any reason click events are being stopped from propagating, you'll need to implement your own listeners and follow the instructions in [Tracking in raw JavaScript](#tracking-in-raw-javascript).
Event listeners are bound at the document level to handle click events on or within elements with these data attributes. This allows for them to be properly handled on re-rendering and changes to the DOM, but it's important to know that because of the way these events are bound, click events shouldn't be stopped from propagating up the DOM tree. If for any reason click events are being stopped from propagating, you'll need to implement your own listeners and follow the instructions in [Tracking in raw JavaScript](#tracking-in-raw-javascript).
Below is a list of supported `data-track-*` attributes:
@ -219,7 +219,7 @@ button.addEventListener('click', () => {
### Tests and test helpers
In Jest particularly in vue tests, you can use the following:
In Jest particularly in Vue tests, you can use the following:
```javascript
import { mockTracking } from 'helpers/tracking_helper';
@ -339,7 +339,7 @@ Snowplow Micro is a very small version of a full Snowplow data collection pipeli
Snowplow Micro is a Docker-based solution for testing frontend and backend events in a local development environment. You need to modify GDK using the instructions below to set this up.
- Read [Introducing Snowplow Micro](https://snowplowanalytics.com/blog/2019/07/17/introducing-snowplow-micro/)
- Look at the [Snowplow Micro repo](https://github.com/snowplow-incubator/snowplow-micro)
- Look at the [Snowplow Micro repository](https://github.com/snowplow-incubator/snowplow-micro)
- Watch our [installation guide recording](https://www.youtube.com/watch?v=OX46fo_A0Ag)
1. Install [Snowplow Micro](https://github.com/snowplow-incubator/snowplow-micro)

View file

@ -513,7 +513,7 @@ underlying projects, issues, etc, by IP address. This can help ensure that
particular content doesn't leave the premises, while not blocking off access to
the entire instance.
Add one or more whitelisted IP subnets using CIDR notation in comma separated format to the group settings and anyone
Add one or more allowed IP subnets using CIDR notation in comma separated format to the group settings and anyone
coming from a different IP address won't be able to access the restricted
content.
@ -533,7 +533,7 @@ the group regardless of the IP restriction.
You can restrict access to groups by
allowing only users with email addresses in particular domains to be added to the group.
Add email domains you want to whitelist and users with emails from different
Add email domains you want to allow and users with emails from different
domains won't be allowed to be added to this group.
Some domains cannot be restricted. These are the most popular public email domains, such as:

View file

@ -249,11 +249,12 @@ group.
| View/manage group-level Kubernetes cluster | | | | ✓ | ✓ |
| Create subgroup | | | | ✓ (1) | ✓ |
| Edit epic comments (posted by any user) **(ULTIMATE)** | | | | ✓ (2) | ✓ (2) |
| Edit group | | | | | ✓ |
| Edit group settings | | | | | ✓ |
| Manage group level CI/CD variables | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
| Delete group | | | | | ✓ |
| Delete group epic **(ULTIMATE)** | | | | | ✓ |
| Edit SAML SSO Billing **(SILVER ONLY)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
| View group Audit Events | | | | | ✓ |
| Disable notification emails | | | | | ✓ |
| View Contribution analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
@ -261,6 +262,8 @@ group.
| View Issues analytics **(PREMIUM)** | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Productivity analytics **(PREMIUM)** | | ✓ | ✓ | ✓ | ✓ |
| View Value Stream analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Billing **(FREE ONLY)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
| View Usage Quotas **(FREE ONLY)** | ✓ | ✓ | ✓ | ✓ | ✓ (4) |
1. Groups can be set to [allow either Owners or Owners and
Maintainers to create subgroups](group/subgroups/index.md#creating-a-subgroup)
@ -268,6 +271,7 @@ group.
1. Default project creation role can be changed at:
- The [instance level](admin_area/settings/visibility_and_access_controls.md#default-project-creation-protection).
- The [group level](group/index.md#default-project-creation-level).
1. Does not apply to subgroups.
### Subgroup permissions

View file

@ -827,6 +827,14 @@ You can create annotations by making requests to the
![Annotations UI](img/metrics_dashboard_annotations_ui_v13.0.png)
#### Retention policy
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/211433) in GitLab 13.01.
To avoid excessive storage space consumption by stale annotations, records attached
to time periods older than two weeks are removed daily. This recurring background
job runs at 1:00 a.m. local server time.
### Expand panel
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/3100) in GitLab 13.0.

View file

@ -502,7 +502,9 @@ module API
link = user_project.project_group_links.find_by(group_id: params[:group_id])
not_found!('Group Link') unless link
destroy_conditionally!(link)
destroy_conditionally!(link) do
::Projects::GroupLinks::DestroyService.new(user_project, current_user).execute(link)
end
end
# rubocop: enable CodeReuse/ActiveRecord

View file

@ -80,7 +80,6 @@ module QA
autoload :User, 'qa/resource/user'
autoload :ProjectMilestone, 'qa/resource/project_milestone'
autoload :Members, 'qa/resource/members'
autoload :Wiki, 'qa/resource/wiki'
autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key'
@ -112,6 +111,10 @@ module QA
module Settings
autoload :HashedStorage, 'qa/resource/settings/hashed_storage'
end
module Wiki
autoload :ProjectPage, 'qa/resource/wiki/project_page'
end
end
##
@ -327,7 +330,6 @@ module QA
module Wiki
autoload :Edit, 'qa/page/project/wiki/edit'
autoload :New, 'qa/page/project/wiki/new'
autoload :Show, 'qa/page/project/wiki/show'
autoload :GitAccess, 'qa/page/project/wiki/git_access'
end
@ -439,6 +441,10 @@ module QA
autoload :Common, 'qa/page/component/issuable/common'
end
module IssueBoard
autoload :Show, 'qa/page/component/issue_board/show'
end
module WebIDE
autoload :Alert, 'qa/page/component/web_ide/alert'

View file

@ -0,0 +1,138 @@
# frozen_string_literal: true
module QA
module Page
module Component
module IssueBoard
class Show < QA::Page::Base
view 'app/assets/javascripts/boards/components/board_card.vue' do
element :board_card
end
view 'app/assets/javascripts/boards/components/board_form.vue' do
element :board_name_field
end
view 'app/assets/javascripts/boards/components/board_list.vue' do
element :board_list_cards_area
end
view 'app/assets/javascripts/boards/components/boards_selector.vue' do
element :boards_dropdown
element :boards_dropdown_content
element :create_new_board_button
end
view 'app/assets/javascripts/vue_shared/components/deprecated_modal.vue' do
element :save_changes_button
end
view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue' do
element :labels_dropdown_content
end
view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_title.vue' do
element :labels_edit_button
end
view 'app/views/shared/boards/_show.html.haml' do
element :boards_list
end
view 'app/views/shared/boards/components/_board.html.haml' do
element :board_list
element :board_list_header
end
view 'app/assets/javascripts/boards/toggle_focus.js' do
element :focus_mode_button
end
# The `focused_board` method does not use `find_element` with an element defined
# with the attribute `data-qa-selector` since such element is not unique when the
# `is-focused` class is not set, and it was not possible to find a better solution.
def focused_board
find('.issue-boards-content.js-focus-mode-board.is-focused')
end
def boards_dropdown
find_element(:boards_dropdown)
end
def boards_dropdown_content
find_element(:boards_dropdown_content)
end
def boards_list_cards_area_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_list_cards_area)
end
end
end
def boards_list_header_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_list_header)
end
end
end
def card_of_list_with_index(index)
wait_boards_list_finish_loading do
within_element_by_index(:board_list, index) do
find_element(:board_card)
end
end
end
def click_boards_dropdown_button
# The dropdown button comes from the `GlDropdown` component of `@gitlab/ui`,
# so it wasn't possible to add a `data-qa-selector` to it.
find_element(:boards_dropdown).find('button').click
end
def click_focus_mode_button
click_element(:focus_mode_button)
end
def configure_by_label(label)
click_boards_config_button
click_element(:labels_edit_button)
find_element(:labels_dropdown_content).find('li', text: label).click
click_element(:save_changes_button)
wait_boards_list_finish_loading
end
def create_new_board(board_name)
click_boards_dropdown_button
click_element(:create_new_board_button)
set_name(board_name)
end
def has_modal_board_name_field?
has_element?(:board_name_field, wait: 1)
end
def set_name(name)
find_element(:board_name_field).set(name)
click_element(:save_changes_button)
end
private
def wait_boards_list_finish_loading
within_element(:boards_list) do
wait_until(reload: false, max_duration: 5, sleep_interval: 1) do
finished_loading? && (block_given? ? yield : true)
end
end
end
end
end
end
end
end
QA::Page::Component::IssueBoard::Show.prepend_if_ee('QA::EE::Page::Component::IssueBoard::Show')

View file

@ -5,14 +5,32 @@ module QA
module Project
module Wiki
class Edit < Page::Base
view 'app/views/projects/wikis/_main_links.html.haml' do
element :new_page_link, 'New page' # rubocop:disable QA/ElementWithPattern
element :page_history_link, 'Page history' # rubocop:disable QA/ElementWithPattern
element :edit_page_link, 'Edit' # rubocop:disable QA/ElementWithPattern
view 'app/views/projects/wikis/_form.html.haml' do
element :wiki_title_textbox
element :wiki_content_textarea
element :wiki_message_textbox
element :save_changes_button
element :create_page_button
end
def click_edit
click_on 'Edit'
def set_title(title)
fill_element :wiki_title_textbox, title
end
def set_content(content)
fill_element :wiki_content_textarea, content
end
def set_message(message)
fill_element :wiki_message_textbox, message
end
def click_save_changes
click_element :save_changes_button
end
def click_create_page
click_element :create_page_button
end
end
end

View file

@ -1,61 +0,0 @@
# frozen_string_literal: true
module QA
module Page
module Project
module Wiki
class New < Page::Base
include Component::LazyLoader
view 'app/views/projects/wikis/_form.html.haml' do
element :wiki_title_textbox
element :wiki_content_textarea
element :wiki_message_textbox
element :save_changes_button
element :create_page_button
end
view 'app/views/shared/empty_states/_wikis.html.haml' do
element :create_first_page_link
end
view 'app/views/shared/empty_states/_wikis_layout.html.haml' do
element :svg_content
end
def click_create_your_first_page_button
# The svg takes a fraction of a second to load after which the
# "Create your first page" button shifts up a bit. This can cause
# webdriver to miss the hit so we wait for the svg to load before
# clicking the button.
within_element(:svg_content) do
has_element? :js_lazy_loaded
end
click_element :create_first_page_link
end
def set_title(title)
fill_element :wiki_title_textbox, title
end
def set_content(content)
fill_element :wiki_content_textarea, content
end
def set_message(message)
fill_element :wiki_message_textbox, message
end
def save_changes
click_element :save_changes_button
end
def create_new_page
click_element :create_page_button
end
end
end
end
end
end

View file

@ -5,23 +5,70 @@ module QA
module Project
module Wiki
class Show < Page::Base
include Page::Component::LegacyClonePanel
include Component::LazyLoader
view 'app/views/projects/wikis/pages.html.haml' do
element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern
view 'app/views/projects/wikis/_sidebar.html.haml' do
element :clone_repository_link
end
view 'app/views/projects/wikis/show.html.haml' do
element :wiki_page_title
element :wiki_page_content
end
view 'app/views/projects/wikis/_main_links.html.haml' do
element :new_page_button
element :page_history_button
element :edit_page_button
end
view 'app/views/shared/empty_states/_wikis.html.haml' do
element :create_first_page_link
end
view 'app/views/shared/empty_states/_wikis_layout.html.haml' do
element :svg_content
end
def click_create_your_first_page
# The svg takes a fraction of a second to load after which the
# "Create your first page" button shifts up a bit. This can cause
# webdriver to miss the hit so we wait for the svg to load before
# clicking the button.
within_element(:svg_content) do
has_element? :js_lazy_loaded
end
click_element :create_first_page_link
end
def click_new_page
click_element(:new_page_button)
end
def click_page_history
click_element(:page_history_button)
end
def click_edit
click_element(:edit_page_button)
end
def click_clone_repository
click_on 'Clone repository'
click_element(:clone_repository_link)
end
def wiki_text
find_element(:wiki_page_content).text
end
def has_title?(title)
has_element?(:wiki_page_title, title)
end
def has_content?(content)
has_element?(:wiki_page_content, content)
end
end
end
end

View file

@ -56,6 +56,8 @@ module QA
@auto_devops_enabled = false
@visibility = :public
@template_name = nil
self.name = "the_awesome_project"
end
def name=(raw_name)

View file

@ -5,17 +5,17 @@ module QA
module Repository
class WikiPush < Repository::Push
attribute :wiki do
Wiki.fabricate! do |resource|
# We are using the project based wiki as a standard.
Wiki::ProjectPage.fabricate_via_api! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
resource.message = 'Update home'
end
end
def initialize
@file_name = 'Home.md'
@file_content = '# Welcome to My Wiki'
@commit_message = 'Updating Home Page'
@file_content = 'This line was created using git push'
@commit_message = 'Updating using git push'
@branch_name = 'master'
@new_branch = false
end
@ -28,9 +28,10 @@ module QA
@repository_ssh_uri ||= wiki.repository_ssh_location.uri
end
def fabricate!
super
wiki.visit!
def web_url
# TODO
# workaround
repository_http_uri.to_s.gsub(".wiki.git", "/-/wikis/#{@file_name.gsub('.md', '')}")
end
end
end

View file

@ -1,48 +0,0 @@
# frozen_string_literal: true
module QA
module Resource
class Wiki < Base
attr_accessor :title, :content, :message
attribute :project do
Project.fabricate! do |resource|
resource.name = 'project-for-wikis'
resource.description = 'project for adding wikis'
end
end
attribute :repository_http_location do
Page::Project::Wiki::Show.perform(&:click_clone_repository)
Page::Project::Wiki::GitAccess.perform do |git_access|
git_access.choose_repository_clone_http
git_access.repository_location
end
end
attribute :repository_ssh_location do
Page::Project::Wiki::Show.perform(&:click_clone_repository)
Page::Project::Wiki::GitAccess.perform do |git_access|
git_access.choose_repository_clone_ssh
git_access.repository_location
end
end
def fabricate!
project.visit!
Page::Project::Menu.perform { |menu_side| menu_side.click_wiki }
Page::Project::Wiki::New.perform do |wiki_new|
wiki_new.click_create_your_first_page_button
wiki_new.set_title(@title)
wiki_new.set_content(@content)
wiki_new.set_message(@message)
wiki_new.create_new_page
end
end
end
end
end

View file

@ -0,0 +1,66 @@
# frozen_string_literal: true
module QA
module Resource
module Wiki
class ProjectPage < Base
attribute :title
attribute :content
attribute :slug
attribute :format
attribute :project do
Project.fabricate_via_api! do |project|
project.name = 'wiki_testing'
project.description = 'project for testing wikis'
end
end
attribute :repository_http_location do
switching_to_wiki_url project.repository_http_location.git_uri
end
attribute :repository_ssh_location do
switching_to_wiki_url project.repository_ssh_location.git_uri
end
def initialize
@title = 'Home'
@content = 'This wiki page is created by the API'
end
def resource_web_url(resource)
super
rescue ResourceURLMissingError
# TODO
# workaround
project.web_url.concat("/-/wikis/#{slug}")
end
def api_get_path
"/projects/#{project.id}/wikis/#{slug}"
end
def api_post_path
"/projects/#{project.id}/wikis"
end
def api_post_body
{
id: project.id,
content: content,
title: title
}
end
private
def switching_to_wiki_url(url)
# TODO
# workaround
Git::Location.new(url.to_s.gsub('.git', '.wiki.git'))
end
end
end
end
end

View file

@ -17,7 +17,7 @@ module QA
project.visit!
Page::Project::Menu.perform(&:go_to_boards)
EE::Page::Component::IssueBoard::Show.perform do |show|
Page::Component::IssueBoard::Show.perform do |show|
show.click_focus_mode_button
expect(show.focused_board).to be_visible

View file

@ -1,42 +0,0 @@
# frozen_string_literal: true
module QA
context 'Create' do
describe 'Wiki management' do
it 'user creates, edits, clones, and pushes to the wiki' do
Flow::Login.sign_in
wiki = Resource::Wiki.fabricate_via_browser_ui! do |resource|
resource.title = 'Home'
resource.content = '# My First Wiki Content'
resource.message = 'Update home'
end
validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.perform(&:click_edit)
Page::Project::Wiki::New.perform do |wiki|
wiki.set_content("My Second Wiki Content")
wiki.save_changes
end
validate_content('My Second Wiki Content')
Resource::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki
push.file_name = 'Home.md'
push.file_content = '# My Third Wiki Content'
push.commit_message = 'Update Home.md'
end
Page::Project::Menu.perform(&:click_wiki)
expect(page).to have_content('My Third Wiki Content')
end
def validate_content(content)
expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/)
end
end
end
end

View file

@ -0,0 +1,93 @@
# frozen_string_literal: true
module QA
context 'Create' do
context 'Wiki' do
describe 'testing wiki content creation inside a project' do
let(:new_wiki_title) { "just_another_wiki_page" }
let(:new_wiki_content) { "this content is changed or added" }
let(:commit_message) { "this is a new addition to the wiki" }
let(:project) { Resource::Project.fabricate_via_api! }
let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
before do
Flow::Login.sign_in
end
it 'by adding a home page to the wiki' do
project.visit!
Page::Project::Menu.perform(&:click_wiki)
Page::Project::Wiki::Show.perform(&:click_create_your_first_page)
Page::Project::Wiki::Edit.perform do |edit|
edit.set_title new_wiki_title
edit.set_content new_wiki_content
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_create_page)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
expect(wiki).to have_content new_wiki_content
end
end
it 'by adding a second page to the wiki' do
wiki.visit!
Page::Project::Wiki::Show.perform(&:click_new_page)
Page::Project::Wiki::Edit.perform do |edit|
edit.set_title new_wiki_title
edit.set_content new_wiki_content
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_create_page)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
expect(wiki).to have_content new_wiki_content
end
end
it 'by adding a home page to the wiki using git push' do
empty_wiki = Resource::Wiki::ProjectPage.new do |empty_wiki|
empty_wiki.project = project
end
Resource::Repository::WikiPush.fabricate! do |push|
push.file_name = "#{new_wiki_title}.md"
push.file_content = new_wiki_content
push.commit_message = commit_message
push.wiki = empty_wiki
push.new_branch = true
end.visit!
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
expect(wiki).to have_content new_wiki_content
end
end
it 'by adding a second page to the wiki using git push' do
Resource::Repository::WikiPush.fabricate! do |push|
push.file_name = "#{new_wiki_title}.md"
push.file_content = new_wiki_content
push.commit_message = commit_message
push.wiki = wiki
push.new_branch = false
end.visit!
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
expect(wiki).to have_content new_wiki_content
end
end
end
end
end
end

View file

@ -0,0 +1,51 @@
# frozen_string_literal: true
module QA
context 'Create' do
context 'Wiki' do
describe 'testing wiki content manipulation inside a project' do
let(:new_wiki_title) { "just_another_wiki_page" }
let(:new_wiki_content) { "this content is changed or added" }
let(:commit_message) { "this is a new addition to the wiki" }
let(:wiki) { Resource::Wiki::ProjectPage.fabricate_via_api! }
before do
Flow::Login.sign_in
end
it 'by manipulating content on the page' do
wiki.visit!
Page::Project::Wiki::Show.perform(&:click_edit)
Page::Project::Wiki::Edit.perform do |edit|
edit.set_title new_wiki_title
edit.set_content new_wiki_content
edit.set_message commit_message
end
Page::Project::Wiki::Edit.perform(&:click_save_changes)
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_title new_wiki_title
expect(wiki).to have_content new_wiki_content
end
end
it 'by manipulating content on the page using git push' do
Resource::Repository::WikiPush.fabricate! do |push|
push.file_content = new_wiki_content
push.commit_message = commit_message
push.wiki = wiki
push.new_branch = false
end.visit!
Page::Project::Wiki::Show.perform do |wiki|
expect(wiki).to have_content new_wiki_content
end
end
end
end
end
end

View file

@ -5,10 +5,15 @@ FactoryBot.define do
project
group
expires_at { nil }
group_access { Gitlab::Access::DEVELOPER }
trait(:guest) { group_access { Gitlab::Access::GUEST } }
trait(:reporter) { group_access { Gitlab::Access::REPORTER } }
trait(:developer) { group_access { Gitlab::Access::DEVELOPER } }
trait(:maintainer) { group_access { Gitlab::Access::MAINTAINER } }
after(:create) do |project_group_link, evaluator|
project_group_link.group.refresh_members_authorized_projects
end
end
end

View file

@ -27,6 +27,7 @@ describe DesignManagement::Design do
it { is_expected.to validate_presence_of(:project) }
it { is_expected.to validate_presence_of(:issue) }
it { is_expected.to validate_presence_of(:filename) }
it { is_expected.to validate_length_of(:filename).is_at_most(255) }
it { is_expected.to validate_uniqueness_of(:filename).scoped_to(:issue_id) }
it "validates that the extension is an image" do

View file

@ -49,22 +49,6 @@ describe ProjectGroupLink do
end
end
describe "destroying a record", :delete do
it "refreshes group users' authorized projects" do
project = create(:project, :private)
group = create(:group)
reporter = create(:user)
group_users = group.users
group.add_reporter(reporter)
project.project_group_links.create(group: group)
group_users.each { |user| expect(user.authorized_projects).to include(project) }
project.project_group_links.destroy_all # rubocop: disable Cop/DestroyAll
group_users.each { |user| expect(user.authorized_projects).not_to include(project) }
end
end
describe 'search by group name' do
let_it_be(:project_group_link) { create(:project_group_link) }
let_it_be(:group) { project_group_link.group }

View file

@ -96,11 +96,9 @@ describe ProjectTeam do
it 'returns invited members of a group' do
group_member = create(:group_member)
project.project_group_links.create!(
group: group_member.group,
group_access: Gitlab::Access::GUEST
)
create(:project_group_link, group: group_member.group,
project: project,
group_access: Gitlab::Access::GUEST)
expect(project.team.members)
.to contain_exactly(group_member.user, project.owner)
@ -108,11 +106,9 @@ describe ProjectTeam do
it 'returns invited members of a group of a specified level' do
group_member = create(:group_member)
project.project_group_links.create!(
group: group_member.group,
group_access: Gitlab::Access::REPORTER
)
create(:project_group_link, group: group_member.group,
project: project,
group_access: Gitlab::Access::REPORTER)
expect(project.team.guests).to be_empty
expect(project.team.reporters).to contain_exactly(group_member.user)

View file

@ -2845,10 +2845,10 @@ describe User do
it "includes projects shared with user's group" do
user = create(:user)
project = create(:project, :private)
group = create(:group)
group.add_reporter(user)
project.project_group_links.create(group: group)
group = create(:group) do |group|
group.add_reporter(user)
end
create(:project_group_link, group: group, project: project)
expect(user.authorized_projects).to include(project)
end

View file

@ -2055,10 +2055,12 @@ describe API::Projects do
end
describe "POST /projects/:id/share" do
let(:group) { create(:group) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:group_user) { create(:user) }
before do
group.add_developer(user)
group.add_developer(group_user)
end
it "shares project with group" do
@ -2074,6 +2076,14 @@ describe API::Projects do
expect(json_response['expires_at']).to eq(expires_at.to_s)
end
it 'updates project authorization' do
expect do
post api("/projects/#{project.id}/share", user), params: { group_id: group.id, group_access: Gitlab::Access::DEVELOPER }
end.to(
change { group_user.can?(:read_project, project) }.from(false).to(true)
)
end
it "returns a 400 error when group id is not given" do
post api("/projects/#{project.id}/share", user), params: { group_access: Gitlab::Access::DEVELOPER }
expect(response).to have_gitlab_http_status(:bad_request)
@ -2123,9 +2133,12 @@ describe API::Projects do
describe 'DELETE /projects/:id/share/:group_id' do
context 'for a valid group' do
let(:group) { create(:group, :public) }
let_it_be(:group) { create(:group, :private) }
let_it_be(:group_user) { create(:user) }
before do
group.add_developer(group_user)
create(:project_group_link, group: group, project: project)
end
@ -2136,6 +2149,14 @@ describe API::Projects do
expect(project.project_group_links).to be_empty
end
it 'updates project authorization' do
expect do
delete api("/projects/#{project.id}/share/#{group.id}", user)
end.to(
change { group_user.can?(:read_project, project) }.from(true).to(false)
)
end
it_behaves_like '412 response' do
let(:request) { api("/projects/#{project.id}/share/#{group.id}", user) }
end

View file

@ -3,16 +3,17 @@
require 'spec_helper'
describe Projects::GroupLinks::CreateService, '#execute' do
let(:user) { create :user }
let(:group) { create :group }
let(:project) { create :project }
let_it_be(:user) { create :user }
let_it_be(:group) { create :group }
let_it_be(:project) { create :project }
let(:opts) do
{
link_group_access: '30',
expires_at: nil
}
end
let(:subject) { described_class.new(project, user, opts) }
subject { described_class.new(project, user, opts) }
before do
group.add_developer(user)
@ -22,6 +23,12 @@ describe Projects::GroupLinks::CreateService, '#execute' do
expect { subject.execute(group) }.to change { project.project_group_links.count }.from(0).to(1)
end
it 'updates authorization' do
expect { subject.execute(group) }.to(
change { Ability.allowed?(user, :read_project, project) }
.from(false).to(true))
end
it 'returns false if group is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end

View file

@ -3,15 +3,25 @@
require 'spec_helper'
describe Projects::GroupLinks::DestroyService, '#execute' do
let(:project) { create(:project, :private) }
let!(:group_link) { create(:project_group_link, project: project) }
let(:user) { create :user }
let(:subject) { described_class.new(project, user) }
let_it_be(:user) { create :user }
let_it_be(:project) { create(:project, :private) }
let_it_be(:group) { create(:group) }
let!(:group_link) { create(:project_group_link, project: project, group: group) }
subject { described_class.new(project, user) }
it 'removes group from project' do
expect { subject.execute(group_link) }.to change { project.project_group_links.count }.from(1).to(0)
end
it 'updates authorization' do
group.add_maintainer(user)
expect { subject.execute(group_link) }.to(
change { Ability.allowed?(user, :read_project, project) }
.from(true).to(false))
end
it 'returns false if group_link is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end

View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
require 'spec_helper'
describe Projects::GroupLinks::UpdateService, '#execute' do
let_it_be(:user) { create :user }
let_it_be(:group) { create :group }
let_it_be(:project) { create :project }
let!(:link) { create(:project_group_link, project: project, group: group) }
let(:expiry_date) { 1.month.from_now.to_date }
let(:group_link_params) do
{ group_access: Gitlab::Access::GUEST,
expires_at: expiry_date }
end
subject { described_class.new(link).execute(group_link_params) }
before do
group.add_developer(user)
end
it 'updates existing link' do
expect(link.group_access).to eq(Gitlab::Access::DEVELOPER)
expect(link.expires_at).to be_nil
subject
link.reload
expect(link.group_access).to eq(Gitlab::Access::GUEST)
expect(link.expires_at).to eq(expiry_date)
end
it 'updates project permissions' do
expect { subject }.to change { user.can?(:create_release, project) }.from(true).to(false)
end
it 'executes UserProjectAccessChangedService' do
expect_next_instance_of(UserProjectAccessChangedService) do |service|
expect(service).to receive(:execute)
end
subject
end
context 'with only param not requiring authorization refresh' do
let(:group_link_params) { { expires_at: Date.tomorrow } }
it 'does not execute UserProjectAccessChangedService' do
expect(UserProjectAccessChangedService).not_to receive(:new)
subject
end
end
end

View file

@ -0,0 +1,40 @@
# frozen_string_literal: true
require 'spec_helper'
describe Metrics::Dashboard::PruneOldAnnotationsWorker do
let_it_be(:now) { DateTime.parse('2020-06-02T00:12:00Z') }
let_it_be(:two_weeks_old_annotation) { create(:metrics_dashboard_annotation, starting_at: now.advance(weeks: -2)) }
let_it_be(:one_day_old_annotation) { create(:metrics_dashboard_annotation, starting_at: now.advance(days: -1)) }
let_it_be(:month_old_annotation) { create(:metrics_dashboard_annotation, starting_at: now.advance(months: -1)) }
describe '#perform' do
it 'removes all annotations older than cut off', :aggregate_failures do
Timecop.freeze(now) do
described_class.new.perform
expect(Metrics::Dashboard::Annotation.all).to match_array([one_day_old_annotation, two_weeks_old_annotation])
# is idempotent in the scope of 24h
expect { described_class.new.perform }.not_to change { Metrics::Dashboard::Annotation.all.to_a }
Timecop.travel(24.hours.from_now) do
described_class.new.perform
expect(Metrics::Dashboard::Annotation.all).to match_array([one_day_old_annotation])
end
end
end
context 'batch to be deleted is bigger than upper limit' do
it 'schedules second job to clear remaining records' do
Timecop.freeze(now) do
create(:metrics_dashboard_annotation, starting_at: 1.month.ago)
stub_const("#{described_class}::DELETE_LIMIT", 1)
expect(described_class).to receive(:perform_async)
described_class.new.perform
end
end
end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
require 'spec_helper'
describe Metrics::Dashboard::ScheduleAnnotationsPruneWorker do
describe '#perform' do
it 'schedules annotations prune job with default cut off date' do
expect(Metrics::Dashboard::PruneOldAnnotationsWorker).to receive(:perform_async)
described_class.new.perform
end
end
end

View file

@ -23,30 +23,53 @@ describe RemoveExpiredGroupLinksWorker do
subject.perform
expect(non_expiring_project_group_link.reload).to be_present
end
it 'removes project authorization' do
user = create(:user)
project = expired_project_group_link.project
group = expired_project_group_link.group
group.add_maintainer(user)
expect { subject.perform }.to(
change { user.can?(:read_project, project) }.from(true).to(false))
end
end
context 'GroupGroupLinks' do
let(:mock_destroy_service) { instance_double(Groups::GroupLinks::DestroyService) }
before do
allow(Groups::GroupLinks::DestroyService).to(
receive(:new).and_return(mock_destroy_service))
end
context 'expired GroupGroupLink exists' do
before do
create(:group_group_link, expires_at: 1.hour.ago)
end
let!(:group_group_link) { create(:group_group_link, expires_at: 1.hour.ago) }
it 'calls Groups::GroupLinks::DestroyService' do
mock_destroy_service = instance_double(Groups::GroupLinks::DestroyService)
allow(Groups::GroupLinks::DestroyService).to(
receive(:new).and_return(mock_destroy_service))
expect(mock_destroy_service).to receive(:execute).once
subject.perform
end
it 'removes project authorization' do
shared_group = group_group_link.shared_group
shared_with_group = group_group_link.shared_with_group
project = create(:project, group: shared_group)
user = create(:user)
shared_with_group.add_maintainer(user)
expect { subject.perform }.to(
change { user.can?(:read_project, project) }.from(true).to(false))
end
end
context 'expired GroupGroupLink does not exist' do
it 'does not call Groups::GroupLinks::DestroyService' do
mock_destroy_service = instance_double(Groups::GroupLinks::DestroyService)
allow(Groups::GroupLinks::DestroyService).to(
receive(:new).and_return(mock_destroy_service))
expect(mock_destroy_service).not_to receive(:execute)
subject.perform