diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-25 09:06:04 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-09-25 09:06:04 +0000 |
commit | 1cfd8874ee6702184d5608f533b30bab722b4f9d (patch) | |
tree | 4921070efdc0ad2d1c3c7d8a3a60c73928b57d25 | |
parent | fbcb36880cda3a29cfa4ebed4d080701c302256b (diff) | |
download | gitlab-ce-1cfd8874ee6702184d5608f533b30bab722b4f9d.tar.gz |
Add latest changes from gitlab-org/gitlab@master
56 files changed, 812 insertions, 148 deletions
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index e0832ab535d..f0d39fa7149 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -547,3 +547,13 @@ export const calculateRemainingMilliseconds = endDate => { const remainingMilliseconds = new Date(endDate).getTime() - Date.now(); return Math.max(remainingMilliseconds, 0); }; + +/** + * Subtracts a given number of days from a given date and returns the new date. + * + * @param {Date} date the date that we will substract days from + * @param {number} daysInPast number of days that are subtracted from a given date + * @returns {String} Date string in ISO format + */ +export const getDateInPast = (date, daysInPast) => + new Date(date.setTime(date.getTime() - daysInPast * 24 * 60 * 60 * 1000)).toISOString(); diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index 61c8b8803d7..0f2cc57b1f9 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -106,3 +106,14 @@ export const sum = (a = 0, b = 0) => a + b; * @param {Int} number */ export const isOdd = (number = 0) => number % 2; + +/** + * Computes the median for a given array. + * @param {Array} arr An array of numbers + * @returns {Number} The median of the given array + */ +export const median = arr => { + const middle = Math.floor(arr.length / 2); + const sorted = arr.sort((a, b) => a - b); + return arr.length % 2 !== 0 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2; +}; diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 9a7c3dc03c3..fb90ddc1048 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -120,6 +120,13 @@ class Namespace < ApplicationRecord uniquify = Uniquify.new uniquify.string(path) { |s| Namespace.find_by_path_or_name(s) } end + + def find_by_pages_host(host) + gitlab_host = "." + Settings.pages.host.downcase + name = host.downcase.delete_suffix(gitlab_host) + + Namespace.find_by_full_path(name) + end end def visibility_level_field @@ -305,8 +312,16 @@ class Namespace < ApplicationRecord aggregation_schedule.present? end + def pages_virtual_domain + Pages::VirtualDomain.new(all_projects_with_pages, trim_prefix: full_path) + end + private + def all_projects_with_pages + all_projects.with_pages_deployed + end + def parent_changed? parent_id_changed? end diff --git a/app/models/pages/lookup_path.rb b/app/models/pages/lookup_path.rb index 1b3183a2a43..51c496c77d3 100644 --- a/app/models/pages/lookup_path.rb +++ b/app/models/pages/lookup_path.rb @@ -2,9 +2,10 @@ module Pages class LookupPath - def initialize(project, domain: nil) + def initialize(project, trim_prefix: nil, domain: nil) @project = project @domain = domain + @trim_prefix = trim_prefix || project.full_path end def project_id @@ -28,11 +29,15 @@ module Pages end def prefix - '/' + if project.pages_group_root? + '/' + else + project.full_path.delete_prefix(trim_prefix) + '/' + end end private - attr_reader :project, :domain + attr_reader :project, :trim_prefix, :domain end end diff --git a/app/models/pages/virtual_domain.rb b/app/models/pages/virtual_domain.rb index 3a876dc06a2..7e42b8e6ae2 100644 --- a/app/models/pages/virtual_domain.rb +++ b/app/models/pages/virtual_domain.rb @@ -2,8 +2,9 @@ module Pages class VirtualDomain - def initialize(projects, domain: nil) + def initialize(projects, trim_prefix: nil, domain: nil) @projects = projects + @trim_prefix = trim_prefix @domain = domain end @@ -17,12 +18,12 @@ module Pages def lookup_paths projects.map do |project| - project.pages_lookup_path(domain: domain) + project.pages_lookup_path(trim_prefix: trim_prefix, domain: domain) end.sort_by(&:prefix).reverse end private - attr_reader :projects, :domain + attr_reader :projects, :trim_prefix, :domain end end diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 22a6bae7cf7..6be3053f637 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -186,11 +186,17 @@ class PagesDomain < ApplicationRecord end def pages_virtual_domain + return unless pages_deployed? + Pages::VirtualDomain.new([project], domain: self) end private + def pages_deployed? + project.pages_metadatum&.deployed? + end + def set_verification_code return if self.verification_code.present? diff --git a/app/models/project.rb b/app/models/project.rb index 18afccf7ddc..883df947ccb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -104,6 +104,9 @@ class Project < ApplicationRecord unless: :ci_cd_settings, if: proc { ProjectCiCdSetting.available? } + after_create :create_pages_metadatum, + unless: :pages_metadatum + after_create :set_timestamps_for_create after_update :update_forks_visibility_level @@ -295,6 +298,8 @@ class Project < ApplicationRecord has_many :external_pull_requests, inverse_of: :project + has_one :pages_metadatum, class_name: 'ProjectPagesMetadatum', inverse_of: :project + accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :project_feature, update_only: true accepts_nested_attributes_for :import_data @@ -425,6 +430,10 @@ class Project < ApplicationRecord .where(project_ci_cd_settings: { group_runners_enabled: true }) end + scope :with_pages_deployed, -> do + joins(:pages_metadatum).merge(ProjectPagesMetadatum.deployed) + end + enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 } chronic_duration_attr :build_timeout_human_readable, :build_timeout, @@ -1643,6 +1652,10 @@ class Project < ApplicationRecord "#{url}/#{url_path}" end + def pages_group_root? + pages_group_url == pages_url + end + def pages_subdomain full_path.partition('/').first end @@ -1681,6 +1694,7 @@ class Project < ApplicationRecord # Projects with a missing namespace cannot have their pages removed return unless namespace + mark_pages_as_not_deployed unless destroyed? ::Projects::UpdatePagesConfigurationService.new(self).execute # 1. We rename pages to temporary directory @@ -1694,6 +1708,14 @@ class Project < ApplicationRecord end # rubocop: enable CodeReuse/ServiceClass + def mark_pages_as_deployed + ensure_pages_metadatum.update!(deployed: true) + end + + def mark_pages_as_not_deployed + ensure_pages_metadatum.update!(deployed: false) + end + # rubocop:disable Gitlab/RailsLogger def write_repository_config(gl_full_path: full_path) # We'd need to keep track of project full path otherwise directory tree @@ -2213,8 +2235,8 @@ class Project < ApplicationRecord members.maintainers.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT) end - def pages_lookup_path(domain: nil) - Pages::LookupPath.new(self, domain: domain) + def pages_lookup_path(trim_prefix: nil, domain: nil) + Pages::LookupPath.new(self, trim_prefix: trim_prefix, domain: domain) end private @@ -2342,6 +2364,13 @@ class Project < ApplicationRecord def services_templates @services_templates ||= Service.where(template: true) end + + def ensure_pages_metadatum + pages_metadatum || create_pages_metadatum! + rescue ActiveRecord::RecordNotUnique + reset + retry + end end Project.prepend_if_ee('EE::Project') diff --git a/app/models/project_pages_metadatum.rb b/app/models/project_pages_metadatum.rb new file mode 100644 index 00000000000..1fda388b1ae --- /dev/null +++ b/app/models/project_pages_metadatum.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class ProjectPagesMetadatum < ApplicationRecord + self.primary_key = :project_id + + belongs_to :project, inverse_of: :pages_metadatum + + scope :deployed, -> { where(deployed: true) } +end diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index fa7a4f0ed82..e8a87fc4320 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -53,6 +53,7 @@ module Projects def success @status.success + @project.mark_pages_as_deployed super end diff --git a/changelogs/unreleased/28781-pages-namespaces-virtual-domain.yml b/changelogs/unreleased/28781-pages-namespaces-virtual-domain.yml new file mode 100644 index 00000000000..6725d070440 --- /dev/null +++ b/changelogs/unreleased/28781-pages-namespaces-virtual-domain.yml @@ -0,0 +1,5 @@ +--- +title: Add project_pages_metadata DB table +merge_request: 17197 +author: +type: added diff --git a/db/migrate/20190909045845_create_project_pages_metadata.rb b/db/migrate/20190909045845_create_project_pages_metadata.rb new file mode 100644 index 00000000000..5fc8fc6e6c1 --- /dev/null +++ b/db/migrate/20190909045845_create_project_pages_metadata.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateProjectPagesMetadata < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + create_table :project_pages_metadata, id: false do |t| + t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade } + t.boolean :deployed, null: false, default: false + + t.index :project_id, name: 'index_project_pages_metadata_on_project_id_and_deployed_is_true', where: "deployed = TRUE" + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 392db66f5b6..7703628d433 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2774,6 +2774,13 @@ ActiveRecord::Schema.define(version: 2019_09_19_162036) do t.index ["status"], name: "index_project_mirror_data_on_status" end + create_table "project_pages_metadata", id: false, force: :cascade do |t| + t.bigint "project_id", null: false + t.boolean "deployed", default: false, null: false + t.index ["project_id"], name: "index_project_pages_metadata_on_project_id", unique: true + t.index ["project_id"], name: "index_project_pages_metadata_on_project_id_and_deployed_is_true", where: "(deployed = true)" + end + create_table "project_repositories", force: :cascade do |t| t.integer "shard_id", null: false t.string "disk_path", null: false @@ -4084,6 +4091,7 @@ ActiveRecord::Schema.define(version: 2019_09_19_162036) do add_foreign_key "project_incident_management_settings", "projects", on_delete: :cascade add_foreign_key "project_metrics_settings", "projects", on_delete: :cascade add_foreign_key "project_mirror_data", "projects", name: "fk_d1aad367d7", on_delete: :cascade + add_foreign_key "project_pages_metadata", "projects", on_delete: :cascade add_foreign_key "project_repositories", "projects", on_delete: :cascade add_foreign_key "project_repositories", "shards", on_delete: :restrict add_foreign_key "project_repository_states", "projects", on_delete: :cascade diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md index ef35a2d5266..4b6dc803189 100644 --- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md +++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md @@ -32,9 +32,9 @@ For example, [Active Directory](https://technet.microsoft.com/en-us/library/hh83 We won't cover the installation and configuration of Windows Server or Active Directory Domain Services in this tutorial. There are a number of resources online to guide you through this process: -- Install Windows Server 2012 - (_technet.microsoft.com_) - [Installing Windows Server 2012](https://technet.microsoft.com/en-us/library/jj134246(v=ws.11).aspx) +- Install Windows Server 2012 - (`technet.microsoft.com`) - [Installing Windows Server 2012](https://technet.microsoft.com/en-us/library/jj134246(v=ws.11).aspx) -- Install Active Directory Domain Services (AD DS) (_technet.microsoft.com_)- [Install Active Directory Domain Services](https://technet.microsoft.com/windows-server-docs/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS) +- Install Active Directory Domain Services (AD DS) (`technet.microsoft.com`)- [Install Active Directory Domain Services](https://technet.microsoft.com/windows-server-docs/identity/ad-ds/deploy/install-active-directory-domain-services--level-100-#BKMK_PS) > **Shortcut:** You can quickly install AD DS via PowerShell using `Install-WindowsFeature AD-Domain-Services -IncludeManagementTools` diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md index 728add05bd8..ee3a992cc72 100644 --- a/doc/administration/auth/ldap.md +++ b/doc/administration/auth/ldap.md @@ -413,7 +413,7 @@ nested members in the user filter should not be confused with [group sync nested groups support](ldap-ee.md#supported-ldap-group-typesattributes). **(STARTER ONLY)** Please note that GitLab does not support the custom filter syntax used by -omniauth-ldap. +OmniAuth LDAP. ### Escaping special characters @@ -536,7 +536,7 @@ ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$ba - Variables beginning with a `$` refer to a variable from the LDAP section of your configuration file. -- Replace ldaps:// with ldap:// if you are using the plain authentication method. +- Replace `ldaps://` with `ldap://` if you are using the plain authentication method. Port `389` is the default `ldap://` port and `636` is the default `ldaps://` port. - We are assuming the password for the bind_dn user is in bind_dn_password.txt. diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md index 2d2734096ed..eb63df6b482 100644 --- a/doc/administration/auth/smartcard.md +++ b/doc/administration/auth/smartcard.md @@ -206,7 +206,7 @@ attribute. As a prerequisite, you must use an LDAP server that: **For installations from source** -1. Add the `san_extensions` line to config/gitlab.yml` within the smartcard section: +1. Add the `san_extensions` line to `config/gitlab.yml` within the smartcard section: ```yaml smartcard: diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md index db256728f4d..e09202e1d79 100644 --- a/doc/administration/merge_request_diffs.md +++ b/doc/administration/merge_request_diffs.md @@ -96,7 +96,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a | `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true | | `region` | AWS region | us-east-1 | | `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com | -| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) | +| `endpoint` | Can be used when configuring an S3 compatible service such as [MinIO](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) | | `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false | | `use_iam_profile` | Set to true to use IAM profile instead of access keys | false diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md index c6dadbb500b..8a8c6202bf7 100644 --- a/doc/administration/uploads.md +++ b/doc/administration/uploads.md @@ -81,7 +81,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a | `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true | | `region` | AWS region | us-east-1 | | `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com | -| `endpoint` | Can be used when configuring an S3 compatible service such as [Minio](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) | +| `endpoint` | Can be used when configuring an S3 compatible service such as [MinIO](https://www.minio.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) | | `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false | | `use_iam_profile` | Set to true to use IAM profile instead of access keys | false @@ -165,7 +165,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a |---------|-------------|---------| | `provider` | Always `OpenStack` for compatible hosts | OpenStack | | `openstack_username` | OpenStack username | | -| `openstack_api_key` | OpenStack api key | | +| `openstack_api_key` | OpenStack API key | | | `openstack_temp_url_key` | OpenStack key for generating temporary urls | | | `openstack_auth_url` | OpenStack authentication endpont | | | `openstack_region` | OpenStack region | | diff --git a/doc/customization/system_header_and_footer_messages.md b/doc/customization/system_header_and_footer_messages.md index b3aa48b1e2f..5db971d0c1e 100644 --- a/doc/customization/system_header_and_footer_messages.md +++ b/doc/customization/system_header_and_footer_messages.md @@ -14,7 +14,7 @@ Note that color settings will only be applied within the app interface and not t ![appearance](system_header_and_footer_messages/appearance.png) -After saving, all GitLab pages will contain the custom system header and/or footer messages: +After saving, all pages within GitLab will contain the custom system header and/or footer messages: ![custom_header_footer](system_header_and_footer_messages/custom_header_footer.png) diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md index 9bc0f64b68d..043345093f5 100644 --- a/doc/user/admin_area/index.md +++ b/doc/user/admin_area/index.md @@ -27,7 +27,7 @@ The Admin Area is made up of the following sections: | Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. | | Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. | | License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). | -| Push Rules **(STARTER)** | Configure pre-defined git [push rules](../../push_rules/push_rules.md) for projects. | +| Push Rules **(STARTER)** | Configure pre-defined Git [push rules](../../push_rules/push_rules.md) for projects. | | Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). | | Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). | | Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. | diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md index f5e812cad1b..ec043bcffdb 100644 --- a/doc/user/admin_area/monitoring/health_check.md +++ b/doc/user/admin_area/monitoring/health_check.md @@ -59,7 +59,7 @@ GitLab OK ## Readiness -The readiness probe checks whether the Gitlab instance is ready to use. It checks the dependent services (Database, Redis, Gitaly etc.) and gives a status for each. +The readiness probe checks whether the GitLab instance is ready to use. It checks the dependent services (Database, Redis, Gitaly etc.) and gives a status for each. ```text GET /-/readiness diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index ad08c852332..400cb59a7ba 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -58,7 +58,7 @@ not selected. CAUTION: **Important:** Starting with [GitLab 10.7][ce-18021], HTTP(s) protocol will be allowed for -git clone/fetch requests done by GitLab Runner from CI/CD Jobs, even if +Git clone/fetch requests done by GitLab Runner from CI/CD Jobs, even if _Only SSH_ was selected. > **Note:** Please keep in mind that disabling an access protocol does not actually diff --git a/doc/user/group/custom_project_templates.md b/doc/user/group/custom_project_templates.md index 094732e6a93..e47a281141d 100644 --- a/doc/user/group/custom_project_templates.md +++ b/doc/user/group/custom_project_templates.md @@ -7,7 +7,7 @@ type: reference > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/6861) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.6. When you create a new [project](../project/index.md), creating it based on custom project templates is -a convenient bootstrap option. +a convenient option. Users can configure a GitLab group that serves as template source under a group's **Settings > General > Custom project templates**. diff --git a/doc/user/project/highlighting.md b/doc/user/project/highlighting.md index 73a2d176b54..a715a313adf 100644 --- a/doc/user/project/highlighting.md +++ b/doc/user/project/highlighting.md @@ -1,8 +1,6 @@ -[Rouge]: https://rubygems.org/gems/rouge - # Syntax Highlighting -GitLab provides syntax highlighting on all files and snippets through the [Rouge][] rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient. +GitLab provides syntax highlighting on all files and snippets through the [Rouge](https://rubygems.org/gems/rouge) rubygem. It will try to guess what language to use based on the file extension, which most of the time is sufficient. If GitLab is guessing wrong, you can override its choice of language using the `gitlab-language` attribute in `.gitattributes`. For example, if you are working in a Prolog project and using the `.pl` file extension (which would normally be highlighted as Perl), you can add the following to your `.gitattributes` file: @@ -12,7 +10,7 @@ If GitLab is guessing wrong, you can override its choice of language using the ` When you check in and push that change, all `*.pl` files in your project will be highlighted as Prolog. -The paths here are simply git's builtin [`.gitattributes` interface](https://git-scm.com/docs/gitattributes). So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is: +The paths here are simply Git's built-in [`.gitattributes` interface](https://git-scm.com/docs/gitattributes). So, if you were to invent a file format called a `Nicefile` at the root of your project that used ruby syntax, all you need is: ``` conf /Nicefile gitlab-language=ruby diff --git a/doc/user/project/index.md b/doc/user/project/index.md index a6e9ef8a7c0..de4db7c80ef 100644 --- a/doc/user/project/index.md +++ b/doc/user/project/index.md @@ -129,7 +129,7 @@ Read through the documentation on [project settings](settings/index.md). - [Import a project](import/index.md) from: - [GitHub to GitLab](import/github.md) - - [BitBucket to GitLab](import/bitbucket.md) + - [Bitbucket to GitLab](import/bitbucket.md) - [Gitea to GitLab](import/gitea.md) - [FogBugz to GitLab](import/fogbugz.md) - [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data) diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 3f563d58287..a583ef45a76 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -311,7 +311,7 @@ as pushing changes: - Set the description of the merge request to a particular description. - Add or remove labels from the merge request. -### Create a new merge request using git push options +### Create a new merge request using Git push options To create a new merge request for a branch, use the `merge_request.create` push option: @@ -320,7 +320,7 @@ To create a new merge request for a branch, use the git push -o merge_request.create ``` -### Set the target branch of a merge request using git push options +### Set the target branch of a merge request using Git push options To update an existing merge request's target branch, use the `merge_request.target=<branch_name>` push option: @@ -336,7 +336,7 @@ same time using a `-o` flag per push option: git push -o merge_request.create -o merge_request.target=branch_name ``` -### Set merge when pipeline succeeds using git push options +### Set merge when pipeline succeeds using Git push options To set an existing merge request to [merge when its pipeline succeeds](merge_when_pipeline_succeeds.md), use @@ -353,7 +353,7 @@ pipeline succeeds at the same time using a `-o` flag per push option: git push -o merge_request.create -o merge_request.merge_when_pipeline_succeeds ``` -### Set removing the source branch using git push options +### Set removing the source branch using Git push options > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/64320) in GitLab 12.2. @@ -368,7 +368,7 @@ git push -o merge_request.remove_source_branch You can also use this push option in addition to the `merge_request.create` push option. -### Set merge request title using git push options +### Set merge request title using Git push options > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/64320) in GitLab 12.2. @@ -382,7 +382,7 @@ git push -o merge_request.title="The title I want" You can also use this push option in addition to the `merge_request.create` push option. -### Set merge request description using git push options +### Set merge request description using Git push options > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/64320) in GitLab 12.2. @@ -396,7 +396,7 @@ git push -o merge_request.description="The description I want" You can also use this push option in addition to the `merge_request.create` push option. -### Add or remove labels using git push options +### Add or remove labels using Git push options > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31831) in GitLab 12.3. @@ -666,7 +666,7 @@ tricks to checkout a merge request locally. Please note that you can checkout a merge request locally even if the source project is a fork (even a private fork) of the target project. -#### Checkout locally by adding a git alias +#### Checkout locally by adding a Git alias Add the following alias to your `~/.gitconfig`: @@ -736,9 +736,8 @@ And to check out a particular merge request: git checkout origin/merge-requests/1 ``` -all the above can be done with [git-mr] script. +All the above can be done with the [`git-mr`](https://gitlab.com/glensc/git-mr) script. -[git-mr]: https://gitlab.com/glensc/git-mr [products]: https://about.gitlab.com/products/ "GitLab products page" [protected branches]: ../protected_branches.md [ci]: ../../../ci/README.md diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md index 9daf42f5152..b9f229783f1 100644 --- a/doc/user/project/merge_requests/merge_request_dependencies.md +++ b/doc/user/project/merge_requests/merge_request_dependencies.md @@ -97,9 +97,9 @@ merge. ## Limitations -- API support: [gitlab#12551](https://gitlab.com/gitlab-org/gitlab/issues/12551) -- Dependencies are not preserved across project export/import: [gitlab#12549](https://gitlab.com/gitlab-org/gitlab/issues/12549) -- Complex merge order dependencies are not supported: [gitlab#11393](https://gitlab.com/gitlab-org/gitlab/issues/11393) +- API support: [issue #12551](https://gitlab.com/gitlab-org/gitlab/issues/12551) +- Dependencies are not preserved across project export/import: [issue #12549](https://gitlab.com/gitlab-org/gitlab/issues/12549) +- Complex merge order dependencies are not supported: [issue #11393](https://gitlab.com/gitlab-org/gitlab/issues/11393) The last item merits a little more explanation. Dependencies between merge requests can be described as a graph of relationships. The simplest possible diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md index f909725f07e..9b08dd1dbb7 100644 --- a/doc/user/project/protected_branches.md +++ b/doc/user/project/protected_branches.md @@ -41,7 +41,7 @@ that the `master` branch is protected by default. ## Using the Allowed to merge and Allowed to push settings -> [Introduced][ce-5081] in GitLab 8.11. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/5081) in GitLab 8.11. Since GitLab 8.11, we added another layer of branch protection which provides more granular management of protected branches. The "Developers can push" @@ -71,7 +71,7 @@ they are set to "Maintainers" by default. ## Restricting push and merge access to certain users **(STARTER)** -> [Introduced][ce-5081] in [GitLab Starter][ee] 8.11. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/5081) in [GitLab Starter](https://about.gitlab.com/pricing/) 8.11. With GitLab Enterprise Edition you can restrict access to protected branches by choosing a role (Maintainers, Developers) as well as certain users. From the @@ -86,7 +86,7 @@ Click **Protect** and the branch will appear in the "Protected branch" list. ## Wildcard protected branches -> [Introduced][ce-4665] in GitLab 8.10. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4665) in GitLab 8.10. You can specify a wildcard protected branch, which will protect all branches matching the wildcard. For example: @@ -131,12 +131,12 @@ To create a new branch through the user interface: ## Deleting a protected branch -> [Introduced][ce-21393] in GitLab 9.3. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/21393) in GitLab 9.3. From time to time, it may be required to delete or clean up branches that are protected. -User with [Maintainer permissions][perm] and up can manually delete protected +User with [Maintainer permissions](../permissions.md) and up can manually delete protected branches via GitLab's web interface: 1. Visit **Repository > Branches** @@ -166,23 +166,16 @@ for details about the pipelines security model. **9.2** -- Allow deletion of protected branches via the web interface [gitlab-org/gitlab-foss#21393][ce-21393] +- Allow deletion of protected branches via the web interface ([issue #21393](https://gitlab.com/gitlab-org/gitlab-foss/issues/21393)). **8.11** -- Allow creating protected branches that can't be pushed to [gitlab-org/gitlab-foss!5081][ce-5081] +- Allow creating protected branches that can't be pushed to ([merge request !5081](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/5081)). **8.10** -- Allow developers to merge into a protected branch without having push access [gitlab-org/gitlab-foss!4892][ce-4892] -- Allow specifying protected branches using wildcards [gitlab-org/gitlab-foss!4665][ce-4665] - -[ce-4665]: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4665 "Allow specifying protected branches using wildcards" -[ce-4892]: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4892 "Allow developers to merge into a protected branch without having push access" -[ce-5081]: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/5081 "Allow creating protected branches that can't be pushed to" -[ce-21393]: https://gitlab.com/gitlab-org/gitlab-foss/issues/21393 -[perm]: ../permissions.md -[ee]: https://about.gitlab.com/pricing/ +- Allow developers without push access to merge into a protected branch ([merge request !4892](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4892)). +- Allow specifying protected branches using wildcards ([merge request !4665](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/4665)). <!-- ## Troubleshooting diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md index fb07981e033..98bc6880176 100644 --- a/doc/user/project/repository/reducing_the_repo_size_using_git.md +++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md @@ -99,7 +99,7 @@ removed from the repository. ![Repository settings cleanup form](img/repository_cleanup.png) Upload the `object-id-map.old-new.txt` file and press **Start cleanup**. - This will remove any internal git references to the old commits, and run + This will remove any internal Git references to the old commits, and run `git gc` against the repository. You will receive an email once it has completed. diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md index 70c6c885c86..b160cb03f94 100644 --- a/doc/user/reserved_names.md +++ b/doc/user/reserved_names.md @@ -4,7 +4,8 @@ Not all project & group names are allowed because they would conflict with existing routes used by GitLab. For a list of words that are not allowed to be used as group or project names, see the -[`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists: +[`path_regex.rb` file](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/path_regex.rb) +under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists: - `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups - `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects. @@ -40,52 +41,50 @@ It is currently not possible to create a project with the following names: Currently the following names are reserved as top level groups: -- \- -- .well-known -- 404.html -- 422.html -- 500.html -- 502.html -- 503.html -- abuse_reports -- admin -- api -- apple-touch-icon-precomposed.png -- apple-touch-icon.png -- assets -- autocomplete -- ci -- dashboard -- deploy.html -- explore -- favicon.ico -- favicon.png -- files -- groups -- health_check -- help -- import -- invites -- jwt -- login -- notification_settings -- oauth -- profile -- projects -- public -- robots.txt -- s -- search -- sent_notifications -- slash-command-logo.png -- snippets -- unsubscribes -- uploads -- users -- v2 +- `\-` +- `.well-known` +- `404.html` +- `422.html` +- `500.html` +- `502.html` +- `503.html` +- `abuse_reports` +- `admin` +- `api` +- `apple-touch-icon-precomposed.png` +- `apple-touch-icon.png` +- `assets` +- `autocomplete` +- `ci` +- `dashboard` +- `deploy.html` +- `explore` +- `favicon.ico` +- `favicon.png` +- `files` +- `groups` +- `health_check` +- `help` +- `import` +- `invites` +- `jwt` +- `login` +- `notification_settings` +- `oauth` +- `profile` +- `projects` +- `public` +- `robots.txt` +- `s` +- `search` +- `sent_notifications` +- `slash-command-logo.png` +- `snippets` +- `unsubscribes` +- `uploads` +- `users` +- `v2` These group names are unavailable as subgroup names: -- \- - -[reserved]: https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/path_regex.rb +- `\-` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 94fa174d4dc..9d393f0217b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -965,13 +965,7 @@ module API end expose :target_url do |todo, options| - target_type = todo.target_type.underscore - target_url = "#{todo.parent.class.to_s.underscore}_#{target_type}_url" - target_anchor = "note_#{todo.note_id}" if todo.note_id? - - Gitlab::Routing - .url_helpers - .public_send(target_url, todo.parent, todo.target, anchor: target_anchor) # rubocop:disable GitlabSecurity/PublicSend + todo_target_url(todo) end expose :body @@ -983,6 +977,19 @@ module API # see also https://gitlab.com/gitlab-org/gitlab-foss/issues/59719 ::API::Entities.const_get(target_type, false) end + + def todo_target_url(todo) + target_type = todo.target_type.underscore + target_url = "#{todo.parent.class.to_s.underscore}_#{target_type}_url" + + Gitlab::Routing + .url_helpers + .public_send(target_url, todo.parent, todo.target, anchor: todo_target_anchor(todo)) # rubocop:disable GitlabSecurity/PublicSend + end + + def todo_target_anchor(todo) + "note_#{todo.note_id}" if todo.note_id? + end end class NamespaceBasic < Grape::Entity diff --git a/lib/api/internal/pages.rb b/lib/api/internal/pages.rb index eaa434cff51..003af7f6dd4 100644 --- a/lib/api/internal/pages.rb +++ b/lib/api/internal/pages.rb @@ -17,11 +17,18 @@ module API namespace 'internal' do namespace 'pages' do + desc 'Get GitLab Pages domain configuration by hostname' do + detail 'This feature was introduced in GitLab 12.3.' + end + params do + requires :host, type: String, desc: 'The host to query for' + end get "/" do - host = PagesDomain.find_by_domain(params[:host]) + host = Namespace.find_by_pages_host(params[:host]) || PagesDomain.find_by_domain(params[:host]) not_found! unless host virtual_domain = host.pages_virtual_domain + no_content! unless virtual_domain present virtual_domain, with: Entities::Internal::Pages::VirtualDomain end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 42cf1ec1f0e..038067eeae4 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -81,3 +81,5 @@ module Gitlab end end end + +::Gitlab::UrlBuilder.prepend_if_ee('EE::Gitlab::UrlBuilder') diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0112d267c7c..5ac4a2f1e32 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4752,6 +4752,9 @@ msgstr "" msgid "Days" msgstr "" +msgid "Days to merge" +msgstr "" + msgid "Debug" msgstr "" @@ -8096,9 +8099,6 @@ msgstr "" msgid "Hook was successfully updated." msgstr "" -msgid "Hours" -msgstr "" - msgid "Housekeeping" msgstr "" @@ -11599,15 +11599,42 @@ msgstr "" msgid "Productivity analytics can help identify the problems that are delaying your team" msgstr "" +msgid "ProductivityAanalytics|Merge requests" +msgstr "" + msgid "ProductivityAnalytics|Ascending" msgstr "" +msgid "ProductivityAnalytics|Days" +msgstr "" + msgid "ProductivityAnalytics|Days to merge" msgstr "" msgid "ProductivityAnalytics|Descending" msgstr "" +msgid "ProductivityAnalytics|Hours" +msgstr "" + +msgid "ProductivityAnalytics|List" +msgstr "" + +msgid "ProductivityAnalytics|Merge Requests" +msgstr "" + +msgid "ProductivityAnalytics|Merge date" +msgstr "" + +msgid "ProductivityAnalytics|Merge requests" +msgstr "" + +msgid "ProductivityAnalytics|Time to merge" +msgstr "" + +msgid "ProductivityAnalytics|Trendline" +msgstr "" + msgid "Profile" msgstr "" @@ -114,6 +114,7 @@ module QA module Integration autoload :Github, 'qa/scenario/test/integration/github' autoload :LDAPNoTLS, 'qa/scenario/test/integration/ldap_no_tls' + autoload :LDAPNoServer, 'qa/scenario/test/integration/ldap_no_server' autoload :LDAPTLS, 'qa/scenario/test/integration/ldap_tls' autoload :InstanceSAML, 'qa/scenario/test/integration/instance_saml' autoload :OAuth, 'qa/scenario/test/integration/oauth' @@ -394,6 +395,7 @@ module QA autoload :KubernetesCluster, 'qa/service/kubernetes_cluster' autoload :Omnibus, 'qa/service/omnibus' autoload :Runner, 'qa/service/runner' + autoload :LDAP, 'qa/service/ldap' module ClusterProvider autoload :Base, 'qa/service/cluster_provider/base' diff --git a/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif b/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif new file mode 100644 index 00000000000..1476f53a0b9 --- /dev/null +++ b/qa/qa/fixtures/ldap/admin/1_add_nodes.ldif @@ -0,0 +1,7 @@ +dn: ou=Global Groups,dc=example,dc=org +objectClass: organizationalUnit +ou: Global Groups + +dn: ou=People,ou=Global Groups,dc=example,dc=org +objectClass: organizationalUnit +ou: People diff --git a/qa/qa/fixtures/ldap/admin/2_add_users.ldif b/qa/qa/fixtures/ldap/admin/2_add_users.ldif new file mode 100644 index 00000000000..00ba2fe8397 --- /dev/null +++ b/qa/qa/fixtures/ldap/admin/2_add_users.ldif @@ -0,0 +1,63 @@ + +# 1. hruser1 + +dn: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org +cn: HR User 1 +givenName: HR +sn: User1 +uid: hruser1 +uidNumber: 5000 +gidNumber: 10000 +homeDirectory: /home/hruser1 +mail: hruser1@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI + +# 2. adminuser1 + +dn: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org +cn: Admin User 1 +givenName: Admin +sn: User1 +uid: adminuser1 +uidNumber: 5009 +gidNumber: 10009 +homeDirectory: /home/adminuser1 +mail: adminuser1@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI + +# 2. adminuser2 + +dn: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org +cn: Admin User 2 +givenName: Admin +sn: User1 +uid: adminuser2 +uidNumber: 5010 +gidNumber: 10010 +homeDirectory: /home/adminuser2 +mail: adminuser2@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI diff --git a/qa/qa/fixtures/ldap/admin/3_add_groups.ldif b/qa/qa/fixtures/ldap/admin/3_add_groups.ldif new file mode 100644 index 00000000000..db8b15bc9ea --- /dev/null +++ b/qa/qa/fixtures/ldap/admin/3_add_groups.ldif @@ -0,0 +1,16 @@ +# 1. Human Resources + +dn: cn=Human Resources,ou=Global Groups,dc=example,dc=org +objectClass: groupofnames +cn: Human Resources +description: Human Resources +member: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org + +# 2. Admin + +dn: cn=AdminGroup,ou=Global Groups,dc=example,dc=org +objectClass: groupofnames +cn: AdminGroup +description: Human Resources +member: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org +member: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org diff --git a/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif b/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif new file mode 100644 index 00000000000..1476f53a0b9 --- /dev/null +++ b/qa/qa/fixtures/ldap/non_admin/1_add_nodes.ldif @@ -0,0 +1,7 @@ +dn: ou=Global Groups,dc=example,dc=org +objectClass: organizationalUnit +ou: Global Groups + +dn: ou=People,ou=Global Groups,dc=example,dc=org +objectClass: organizationalUnit +ou: People diff --git a/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif b/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif new file mode 100644 index 00000000000..cf8accc97e1 --- /dev/null +++ b/qa/qa/fixtures/ldap/non_admin/2_add_users.ldif @@ -0,0 +1,61 @@ + +# 1. Human Resources + +dn: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org +cn: HR User 1 +givenName: HR +sn: User1 +uid: hruser1 +uidNumber: 5000 +gidNumber: 10000 +homeDirectory: /home/hruser1 +mail: hruser1@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI + +# 2. Admin + +dn: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org +cn: Admin User 1 +givenName: Admin +sn: User1 +uid: adminuser1 +uidNumber: 5009 +gidNumber: 10009 +homeDirectory: /home/adminuser1 +mail: adminuser1@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI + +dn: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org +cn: Admin User 2 +givenName: Admin +sn: User1 +uid: adminuser2 +uidNumber: 5010 +gidNumber: 10010 +homeDirectory: /home/adminuser2 +mail: adminuser2@example.org +objectClass: top +objectClass: posixAccount +objectClass: shadowAccount +objectClass: inetOrgPerson +objectClass: organizationalPerson +objectClass: person +loginShell: /bin/bash +# hashed value for 'password' +userPassword: {SSHA}ICMhr6Jxt5bk2awD7HL7GxRTM3BZ1pFI diff --git a/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif b/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif new file mode 100644 index 00000000000..65487a3b2c6 --- /dev/null +++ b/qa/qa/fixtures/ldap/non_admin/3_add_groups.ldif @@ -0,0 +1,16 @@ +# 1. Human Resources + +dn: cn=Human Resources,ou=Global Groups,dc=example,dc=org +objectClass: groupofnames +cn: Human Resources +description: Human Resources +member: uid=hruser1,ou=People,ou=Global Groups,dc=example,dc=org +member: uid=adminuser1,ou=People,ou=Global Groups,dc=example,dc=org + +# 2. Admin + +dn: cn=AdminGroup,ou=Global Groups,dc=example,dc=org +objectClass: groupofnames +cn: AdminGroup +description: Human Resources +member: uid=adminuser2,ou=People,ou=Global Groups,dc=example,dc=org diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb index c373ec6d4fd..6e266e26d78 100644 --- a/qa/qa/page/main/login.rb +++ b/qa/qa/page/main/login.rb @@ -87,7 +87,7 @@ module QA click_element :sign_in_button end - Page::Main::Menu.perform(&:has_personal_area?) + Page::Main::Menu.perform(&:signed_in?) end def self.path diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 3391ef454ee..bff1b4cc836 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -96,6 +96,10 @@ module QA has_element?(:admin_area_link, wait: wait) end + def has_no_admin_area_link?(wait: Capybara.default_max_wait_time) + has_no_element?(:admin_area_link, wait: wait) + end + def click_stop_impersonation_link click_element(:stop_impersonation_link) end diff --git a/qa/qa/scenario/test/integration/ldap_no_server.rb b/qa/qa/scenario/test/integration/ldap_no_server.rb new file mode 100644 index 00000000000..be71fc0ef6d --- /dev/null +++ b/qa/qa/scenario/test/integration/ldap_no_server.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module QA + module Scenario + module Test + module Integration + class LDAPNoServer < Test::Instance::All + tags :ldap_no_server + end + end + end + end +end diff --git a/qa/qa/service/ldap.rb b/qa/qa/service/ldap.rb new file mode 100644 index 00000000000..7a02cebeb8f --- /dev/null +++ b/qa/qa/service/ldap.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module QA + module Service + class LDAP + include Service::Shellout + + def initialize(volume) + @image = 'osixia/openldap:latest' + @name = 'ldap-server' + @network = Runtime::Scenario.attributes[:network] || 'test' + @volume = volume + end + + def network + shell "docker network inspect #{@network}" + rescue CommandError + 'bridge' + else + @network + end + + def pull + shell "docker pull #{@image}" + end + + def host_name + "#{@name}.#{network}" + end + + def register! + shell <<~CMD.tr("\n", ' ') + docker run -d --rm + --network #{network} + --hostname #{host_name} + --name #{@name} + -p 389:389 + --volume #{volume_or_fixture(@volume)}:/container/service/slapd/assets/config/bootstrap/ldif/custom + #{@image} --copy-service + CMD + end + + def remove! + shell "docker rm -f #{@name}" if running? + end + + def running? + `docker ps -f name=#{@name}`.include?(@name) + end + + def volume_or_fixture(volume_name) + if volume_exists?(volume_name) + volume_name + else + File.expand_path("../fixtures/ldap/#{volume_name}", __dir__) + end + end + + def volume_exists?(volume_name) + `docker volume ls -q -f name=#{volume_name}`.include?(volume_name) + end + end + end +end diff --git a/qa/spec/scenario/test/integration/ldap_spec.rb b/qa/spec/scenario/test/integration/ldap_spec.rb index b6d798bf504..86747cd8eb7 100644 --- a/qa/spec/scenario/test/integration/ldap_spec.rb +++ b/qa/spec/scenario/test/integration/ldap_spec.rb @@ -8,6 +8,14 @@ describe QA::Scenario::Test::Integration::LDAPNoTLS do end end +describe QA::Scenario::Test::Integration::LDAPNoServer do + context '#perform' do + it_behaves_like 'a QA scenario class' do + let(:tags) { [:ldap_no_server] } + end + end +end + describe QA::Scenario::Test::Integration::LDAPTLS do context '#perform' do it_behaves_like 'a QA scenario class' do diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index c5290fdd5c9..5ef8a15d702 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -98,8 +98,11 @@ describe ApplicationController do # remove line below once `privacy_policy_update_callout` # feature flag is removed and `gon` reverts back to # to not setting any variables. - it_behaves_like 'setting gon variables' - # it_behaves_like 'not setting gon variables' + if Gitlab.ee? + it_behaves_like 'setting gon variables' + else + it_behaves_like 'not setting gon variables' + end end end @@ -110,8 +113,11 @@ describe ApplicationController do # remove line below once `privacy_policy_update_callout` # feature flag is removed and `gon` reverts back to # to not setting any variables. - it_behaves_like 'setting gon variables' - # it_behaves_like 'not setting gon variables' + if Gitlab.ee? + it_behaves_like 'setting gon variables' + else + it_behaves_like 'not setting gon variables' + end end end diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js index 8cbf170b3e6..80d1dff676b 100644 --- a/spec/frontend/lib/utils/datetime_utility_spec.js +++ b/spec/frontend/lib/utils/datetime_utility_spec.js @@ -426,3 +426,13 @@ describe('newDate', () => { expect(initialDate instanceof Date).toBe(true); }); }); + +describe('getDateInPast', () => { + it('returns the correct date in the past', () => { + const date = new Date(1563235200000); // 2019-07-16T00:00:00.00Z + const daysInPast = 90; + const dateInPast = datetimeUtility.getDateInPast(date, daysInPast); + + expect(dateInPast).toBe('2019-04-17T00:00:00.000Z'); + }); +}); diff --git a/spec/frontend/lib/utils/number_utility_spec.js b/spec/frontend/lib/utils/number_utility_spec.js index 77d7478d317..381d7c6f8d9 100644 --- a/spec/frontend/lib/utils/number_utility_spec.js +++ b/spec/frontend/lib/utils/number_utility_spec.js @@ -6,6 +6,7 @@ import { numberToHumanSize, sum, isOdd, + median, } from '~/lib/utils/number_utils'; describe('Number Utils', () => { @@ -109,4 +110,16 @@ describe('Number Utils', () => { expect(isOdd(1)).toEqual(1); }); }); + + describe('median', () => { + it('computes the median for a given array with odd length', () => { + const items = [10, 27, 20, 5, 19]; + expect(median(items)).toBe(19); + }); + + it('computes the median for a given array with even length', () => { + const items = [10, 27, 20, 5, 19, 4]; + expect(median(items)).toBe(14.5); + }); + }); }); diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 3b43ff3a4e1..3315dd3b974 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -410,6 +410,7 @@ project: - designs - project_aliases - external_pull_requests +- pages_metadatum award_emoji: - awardable - user diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 972f26ac745..e72e272f4d2 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -191,6 +191,16 @@ describe Namespace do end end + describe '.find_by_pages_host' do + it 'finds namespace by GitLab Pages host and is case-insensitive' do + namespace = create(:namespace, name: 'topnamespace') + create(:namespace, name: 'annother_namespace') + host = "TopNamespace.#{Settings.pages.host.upcase}" + + expect(described_class.find_by_pages_host(host)).to eq(namespace) + end + end + describe '#ancestors_upto' do let(:parent) { create(:group) } let(:child) { create(:group, parent: parent) } @@ -913,4 +923,18 @@ describe Namespace do end end end + + describe '#pages_virtual_domain' do + let(:project) { create(:project, namespace: namespace) } + + context 'when there are pages deployed for the project' do + before do + project.mark_pages_as_deployed + end + + it 'returns the virual domain' do + expect(namespace.pages_virtual_domain).to be_an_instance_of(Pages::VirtualDomain) + end + end + end end diff --git a/spec/models/pages/lookup_path_spec.rb b/spec/models/pages/lookup_path_spec.rb index 2146b0c9abd..c05d4c82634 100644 --- a/spec/models/pages/lookup_path_spec.rb +++ b/spec/models/pages/lookup_path_spec.rb @@ -57,8 +57,18 @@ describe Pages::LookupPath do end describe '#prefix' do - it 'returns "/"' do + it 'returns "/" for pages group root projects' do + project = instance_double(Project, pages_group_root?: true) + lookup_path = described_class.new(project, trim_prefix: 'mygroup') + expect(lookup_path.prefix).to eq('/') end + + it 'returns the project full path with the provided prefix removed' do + project = instance_double(Project, pages_group_root?: false, full_path: 'mygroup/myproject') + lookup_path = described_class.new(project, trim_prefix: 'mygroup') + + expect(lookup_path.prefix).to eq('/myproject/') + end end end diff --git a/spec/models/pages/virtual_domain_spec.rb b/spec/models/pages/virtual_domain_spec.rb index eaa57b7acd6..a5310738482 100644 --- a/spec/models/pages/virtual_domain_spec.rb +++ b/spec/models/pages/virtual_domain_spec.rb @@ -25,19 +25,33 @@ describe Pages::VirtualDomain do end describe '#lookup_paths' do - let(:domain) { instance_double(PagesDomain) } let(:project_a) { instance_double(Project) } let(:project_z) { instance_double(Project) } let(:pages_lookup_path_a) { instance_double(Pages::LookupPath, prefix: 'aaa') } let(:pages_lookup_path_z) { instance_double(Pages::LookupPath, prefix: 'zzz') } - subject(:virtual_domain) { described_class.new([project_a, project_z], domain: domain) } + context 'when there is pages domain provided' do + let(:domain) { instance_double(PagesDomain) } - it 'returns collection of projects pages lookup paths sorted by prefix in reverse' do - expect(project_a).to receive(:pages_lookup_path).with(domain: domain).and_return(pages_lookup_path_a) - expect(project_z).to receive(:pages_lookup_path).with(domain: domain).and_return(pages_lookup_path_z) + subject(:virtual_domain) { described_class.new([project_a, project_z], domain: domain) } - expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_z, pages_lookup_path_a]) + it 'returns collection of projects pages lookup paths sorted by prefix in reverse' do + expect(project_a).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_a) + expect(project_z).to receive(:pages_lookup_path).with(domain: domain, trim_prefix: nil).and_return(pages_lookup_path_z) + + expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_z, pages_lookup_path_a]) + end + end + + context 'when there is trim_prefix provided' do + subject(:virtual_domain) { described_class.new([project_a, project_z], trim_prefix: 'group/') } + + it 'returns collection of projects pages lookup paths sorted by prefix in reverse' do + expect(project_a).to receive(:pages_lookup_path).with(trim_prefix: 'group/', domain: nil).and_return(pages_lookup_path_a) + expect(project_z).to receive(:pages_lookup_path).with(trim_prefix: 'group/', domain: nil).and_return(pages_lookup_path_z) + + expect(virtual_domain.lookup_paths).to eq([pages_lookup_path_z, pages_lookup_path_a]) + end end end end diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb index f745820a404..9ac80f8b795 100644 --- a/spec/models/pages_domain_spec.rb +++ b/spec/models/pages_domain_spec.rb @@ -557,15 +557,27 @@ describe PagesDomain do end end - describe '.pages_virtual_domain' do - let(:project) { build(:project) } + describe '#pages_virtual_domain' do + let(:project) { create(:project) } + let(:pages_domain) { create(:pages_domain, project: project) } - subject(:pages_domain) { build(:pages_domain, project: project) } + context 'when there are no pages deployed for the project' do + it 'returns nil' do + expect(pages_domain.pages_virtual_domain).to be_nil + end + end - it 'returns instance of Pages::VirtualDomain' do - expect(Pages::VirtualDomain).to receive(:new).with([project], domain: pages_domain).and_call_original + context 'when there are pages deployed for the project' do + before do + project.mark_pages_as_deployed + project.reload + end + + it 'returns the virual domain' do + expect(Pages::VirtualDomain).to receive(:new).with([project], domain: pages_domain).and_call_original - expect(pages_domain.pages_virtual_domain).to be_a(Pages::VirtualDomain) + expect(pages_domain.pages_virtual_domain).to be_an_instance_of(Pages::VirtualDomain) + end end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 67f64822184..e97e8c58bbd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -132,6 +132,13 @@ describe Project do expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting) expect(project.ci_cd_settings).to be_persisted end + + it 'automatically creates a Pages metadata row' do + project = create(:project) + + expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum) + expect(project.pages_metadatum).to be_persisted + end end context 'updating cd_cd_settings' do @@ -3526,7 +3533,8 @@ describe Project do end describe '#remove_pages' do - let(:project) { create(:project) } + let(:project) { create(:project).tap { |project| project.mark_pages_as_deployed } } + let(:pages_metadatum) { project.pages_metadatum } let(:namespace) { project.namespace } let(:pages_path) { project.pages_path } @@ -3539,12 +3547,12 @@ describe Project do end end - it 'removes the pages directory' do + it 'removes the pages directory and marks the project as not having pages deployed' do expect_any_instance_of(Projects::UpdatePagesConfigurationService).to receive(:execute) expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project).and_return(true) expect(PagesWorker).to receive(:perform_in).with(5.minutes, :remove, namespace.full_path, anything) - project.remove_pages + expect { project.remove_pages }.to change { pages_metadatum.reload.deployed }.from(true).to(false) end it 'is a no-op when there is no namespace' do @@ -3554,13 +3562,13 @@ describe Project do expect_any_instance_of(Projects::UpdatePagesConfigurationService).not_to receive(:execute) expect_any_instance_of(Gitlab::PagesTransfer).not_to receive(:rename_project) - project.remove_pages + expect { project.remove_pages }.not_to change { pages_metadatum.reload.deployed } end it 'is run when the project is destroyed' do expect(project).to receive(:remove_pages).and_call_original - project.destroy + expect { project.destroy }.not_to raise_error end end @@ -5014,6 +5022,35 @@ describe Project do end end + context 'pages deployed' do + let(:project) { create(:project) } + + { + mark_pages_as_deployed: true, + mark_pages_as_not_deployed: false + }.each do |method_name, flag| + describe method_name do + it "creates new record and sets deployed to #{flag} if none exists yet" do + project.pages_metadatum.destroy! + project.reload + + project.send(method_name) + + expect(project.pages_metadatum.reload.deployed).to eq(flag) + end + + it "updates the existing record and sets deployed to #{flag}" do + pages_metadatum = project.pages_metadatum + pages_metadatum.update!(deployed: !flag) + + expect { project.send(method_name) }.to change { + pages_metadatum.reload.deployed + }.from(!flag).to(flag) + end + end + end + end + describe '#has_pool_repsitory?' do it 'returns false when it does not have a pool repository' do subject = create(:project, :repository) @@ -5054,9 +5091,34 @@ describe Project do let(:project) { build(:project) } it 'returns instance of Pages::LookupPath' do - expect(Pages::LookupPath).to receive(:new).with(project, domain: pages_domain).and_call_original + expect(Pages::LookupPath).to receive(:new).with(project, domain: pages_domain, trim_prefix: 'mygroup').and_call_original + + expect(project.pages_lookup_path(domain: pages_domain, trim_prefix: 'mygroup')).to be_a(Pages::LookupPath) + end + end + + describe '.with_pages_deployed' do + it 'returns only projects that have pages deployed' do + _project_without_pages = create(:project) + project_with_pages = create(:project) + project_with_pages.mark_pages_as_deployed + + expect(described_class.with_pages_deployed).to contain_exactly(project_with_pages) + end + end + + describe '#pages_group_root?' do + it 'returns returns true if pages_url is same as pages_group_url' do + project = build(:project) + expect(project).to receive(:pages_url).and_return(project.pages_group_url) + + expect(project.pages_group_root?).to eq(true) + end + + it 'returns returns false if pages_url is different than pages_group_url' do + project = build(:project) - expect(project.pages_lookup_path(domain: pages_domain)).to be_a(Pages::LookupPath) + expect(project.pages_group_root?).to eq(false) end end diff --git a/spec/requests/api/internal/pages_spec.rb b/spec/requests/api/internal/pages_spec.rb index e1b563b92f4..03bf748b471 100644 --- a/spec/requests/api/internal/pages_spec.rb +++ b/spec/requests/api/internal/pages_spec.rb @@ -43,6 +43,10 @@ describe API::Internal::Pages do super(host, headers) end + def deploy_pages(project) + project.mark_pages_as_deployed + end + context 'not existing host' do it 'responds with 404 Not Found' do query_host('pages.gitlab.io') @@ -56,18 +60,104 @@ describe API::Internal::Pages do let(:project) { create(:project, namespace: namespace, name: 'gitlab-ce') } let!(:pages_domain) { create(:pages_domain, domain: 'pages.gitlab.io', project: project) } - it 'responds with the correct domain configuration' do - query_host('pages.gitlab.io') + context 'when there are no pages deployed for the related project' do + it 'responds with 204 No Content' do + query_host('pages.gitlab.io') - expect(response).to have_gitlab_http_status(200) - expect(response).to match_response_schema('internal/pages/virtual_domain') + expect(response).to have_gitlab_http_status(204) + end + end - expect(json_response['certificate']).to eq(pages_domain.certificate) - expect(json_response['key']).to eq(pages_domain.key) + context 'when there are pages deployed for the related project' do + it 'responds with the correct domain configuration' do + deploy_pages(project) + + query_host('pages.gitlab.io') + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('internal/pages/virtual_domain') + + expect(json_response['certificate']).to eq(pages_domain.certificate) + expect(json_response['key']).to eq(pages_domain.key) + + expect(json_response['lookup_paths']).to eq( + [ + { + 'project_id' => project.id, + 'access_control' => false, + 'https_only' => false, + 'prefix' => '/', + 'source' => { + 'type' => 'file', + 'path' => 'gitlab-org/gitlab-ce/public/' + } + } + ] + ) + end + end + end + + context 'namespaced domain' do + let(:group) { create(:group, name: 'mygroup') } + + before do + allow(Settings.pages).to receive(:host).and_return('gitlab-pages.io') + allow(Gitlab.config.pages).to receive(:url).and_return("http://gitlab-pages.io") + end + + context 'regular project' do + it 'responds with the correct domain configuration' do + project = create(:project, group: group, name: 'myproject') + deploy_pages(project) + + query_host('mygroup.gitlab-pages.io') + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('internal/pages/virtual_domain') + + expect(json_response['lookup_paths']).to eq( + [ + { + 'project_id' => project.id, + 'access_control' => false, + 'https_only' => false, + 'prefix' => '/myproject/', + 'source' => { + 'type' => 'file', + 'path' => 'mygroup/myproject/public/' + } + } + ] + ) + end + end - lookup_path = json_response['lookup_paths'][0] - expect(lookup_path['prefix']).to eq('/') - expect(lookup_path['source']['path']).to eq('gitlab-org/gitlab-ce/public/') + context 'group root project' do + it 'responds with the correct domain configuration' do + project = create(:project, group: group, name: 'mygroup.gitlab-pages.io') + deploy_pages(project) + + query_host('mygroup.gitlab-pages.io') + + expect(response).to have_gitlab_http_status(200) + expect(response).to match_response_schema('internal/pages/virtual_domain') + + expect(json_response['lookup_paths']).to eq( + [ + { + 'project_id' => project.id, + 'access_control' => false, + 'https_only' => false, + 'prefix' => '/', + 'source' => { + 'type' => 'file', + 'path' => 'mygroup/mygroup.gitlab-pages.io/public/' + } + } + ] + ) + end end end end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index b597717c347..fe92b53cd91 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -40,6 +40,7 @@ describe Projects::UpdatePagesService do it "doesn't delete artifacts after deploying" do expect(execute).to eq(:success) + expect(project.pages_metadatum).to be_deployed expect(build.artifacts?).to eq(true) end end @@ -47,6 +48,7 @@ describe Projects::UpdatePagesService do it 'succeeds' do expect(project.pages_deployed?).to be_falsey expect(execute).to eq(:success) + expect(project.pages_metadatum).to be_deployed expect(project.pages_deployed?).to be_truthy # Check that all expected files are extracted @@ -63,16 +65,23 @@ describe Projects::UpdatePagesService do it 'removes pages after destroy' do expect(PagesWorker).to receive(:perform_in) expect(project.pages_deployed?).to be_falsey + expect(execute).to eq(:success) + + expect(project.pages_metadatum).to be_deployed expect(project.pages_deployed?).to be_truthy + project.destroy + expect(project.pages_deployed?).to be_falsey + expect(ProjectPagesMetadatum.find_by_project_id(project)).to be_nil end it 'fails if sha on branch is not latest' do build.update(ref: 'feature') expect(execute).not_to eq(:success) + expect(project.pages_metadatum).not_to be_deployed end context 'when using empty file' do @@ -94,6 +103,7 @@ describe Projects::UpdatePagesService do it 'succeeds to extract' do expect(execute).to eq(:success) + expect(project.pages_metadatum).to be_deployed end end end @@ -109,6 +119,7 @@ describe Projects::UpdatePagesService do build.reload expect(deploy_status).to be_failed + expect(project.pages_metadatum).not_to be_deployed end end @@ -125,6 +136,7 @@ describe Projects::UpdatePagesService do build.reload expect(deploy_status).to be_failed + expect(project.pages_metadatum).not_to be_deployed end end @@ -138,6 +150,7 @@ describe Projects::UpdatePagesService do build.reload expect(deploy_status).to be_failed + expect(project.pages_metadatum).not_to be_deployed end end end @@ -179,6 +192,7 @@ describe Projects::UpdatePagesService do expect(deploy_status.description) .to match(/artifacts for pages are too large/) expect(deploy_status).to be_script_failure + expect(project.pages_metadatum).not_to be_deployed end end @@ -196,6 +210,7 @@ describe Projects::UpdatePagesService do subject.execute expect(deploy_status.description).not_to be_present + expect(project.pages_metadatum).to be_deployed end end |