Commit c7d2bd4d authored by Robert Speicher's avatar Robert Speicher

Merge remote-tracking branch 'ce/master'

parents e7a5fd1b 2860cfaa
......@@ -311,7 +311,8 @@ export default class LabelsSelect {
// We need to identify which items are actually labels
if (label.id) {
selectedClass.push('label-item');
const selectedLayoutClasses = ['d-flex', 'flex-row', 'text-break-word'];
selectedClass.push('label-item', ...selectedLayoutClasses);
linkEl.dataset.labelId = label.id;
}
......
......@@ -137,6 +137,7 @@ export default {
:path="entry.flatPath"
:type="entry.type"
:url="entry.webUrl"
:submodule-tree-url="entry.treeUrl"
:lfs-oid="entry.lfsOid"
/>
</template>
......
......@@ -62,6 +62,11 @@ export default {
required: false,
default: null,
},
submoduleTreeUrl: {
type: String,
required: false,
default: null,
},
},
data() {
return {
......@@ -112,7 +117,7 @@ export default {
</component>
<gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge>
<template v-if="isSubmodule">
@ <gl-link href="#" class="commit-sha">{{ shortSha }}</gl-link>
@ <gl-link :href="submoduleTreeUrl" class="commit-sha">{{ shortSha }}</gl-link>
</template>
</td>
<td class="d-none d-sm-table-cell tree-commit">
......
......@@ -35,6 +35,8 @@ query getFiles(
edges {
node {
...TreeEntry
webUrl
treeUrl
}
}
pageInfo {
......
......@@ -30,6 +30,10 @@
.dropdown-content {
max-height: 135px;
}
.dropdown-label-box {
flex: 0 0 auto;
}
}
.dropdown-new-label {
......
......@@ -7,6 +7,9 @@ module Types
implements Types::Tree::EntryType
graphql_name 'Submodule'
field :web_url, type: GraphQL::STRING_TYPE, null: true
field :tree_url, type: GraphQL::STRING_TYPE, null: true
end
# rubocop: enable Graphql/AuthorizeTypes
end
......
......@@ -15,7 +15,9 @@ module Types
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository)
end
field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true
field :submodules, Types::Tree::SubmoduleType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::SubmoduleTreeEntry.decorate(obj.submodules, obj)
end
field :blobs, Types::Tree::BlobType.connection_type, null: false, calls_gitaly: true, resolve: -> (obj, args, ctx) do
Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository)
......
......@@ -9,6 +9,10 @@ module SubmoduleHelper
def submodule_links(submodule_item, ref = nil, repository = @repository)
url = repository.submodule_url_for(ref, submodule_item.path)
submodule_links_for_url(submodule_item.id, url, repository)
end
def submodule_links_for_url(submodule_item_id, url, repository)
if url == '.' || url == './'
url = File.join(Gitlab.config.gitlab.url, repository.project.full_path)
end
......@@ -31,13 +35,13 @@ module SubmoduleHelper
if self_url?(url, namespace, project)
[namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)]
namespace_project_tree_path(namespace, project, submodule_item_id)]
elsif relative_self_url?(url)
relative_self_links(url, submodule_item.id, repository.project)
relative_self_links(url, submodule_item_id, repository.project)
elsif github_dot_com_url?(url)
standard_links('github.com', namespace, project, submodule_item.id)
standard_links('github.com', namespace, project, submodule_item_id)
elsif gitlab_dot_com_url?(url)
standard_links('gitlab.com', namespace, project, submodule_item.id)
standard_links('gitlab.com', namespace, project, submodule_item_id)
else
[sanitize_submodule_url(url), nil]
end
......
......@@ -86,6 +86,8 @@ class ProjectFeature < ApplicationRecord
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
default_value_for(:pages_access_level, allows_nil: false) { |feature| feature.project&.public? ? ENABLED : PRIVATE }
def feature_available?(feature, user)
# This feature might not be behind a feature flag at all, so default to true
return false unless ::Feature.enabled?(feature, user, default_enabled: true)
......
......@@ -3,7 +3,6 @@
class DiffFileBaseEntity < Grape::Entity
include RequestAwareEntity
include BlobHelper
include SubmoduleHelper
include DiffHelper
include TreeHelper
include ChecksCollaboration
......@@ -12,12 +11,12 @@ class DiffFileBaseEntity < Grape::Entity
expose :content_sha
expose :submodule?, as: :submodule
expose :submodule_link do |diff_file|
memoized_submodule_links(diff_file).first
expose :submodule_link do |diff_file, options|
memoized_submodule_links(diff_file, options).first
end
expose :submodule_tree_url do |diff_file|
memoized_submodule_links(diff_file).last
memoized_submodule_links(diff_file, options).last
end
expose :edit_path, if: -> (_, options) { options[:merge_request] } do |diff_file|
......@@ -92,10 +91,10 @@ class DiffFileBaseEntity < Grape::Entity
private
def memoized_submodule_links(diff_file)
def memoized_submodule_links(diff_file, options)
strong_memoize(:submodule_links) do
if diff_file.submodule?
submodule_links(diff_file.blob, diff_file.content_sha, diff_file.repository)
options[:submodule_links].for(diff_file.blob, diff_file.content_sha)
else
[]
end
......
......@@ -64,7 +64,10 @@ class DiffsEntity < Grape::Entity
merge_request_path(merge_request, format: :diff)
end
expose :diff_files, using: DiffFileEntity
expose :diff_files do |diffs, options|
submodule_links = Gitlab::SubmoduleLinks.new(merge_request.project.repository)
DiffFileEntity.represent(diffs.diff_files, options.merge(submodule_links: submodule_links))
end
expose :merge_request_diffs, using: MergeRequestDiffEntity, if: -> (_, options) { options[:merge_request_diffs]&.any? } do |diffs|
options[:merge_request_diffs]
......
......@@ -2,4 +2,18 @@
class DiscussionSerializer < BaseSerializer
entity DiscussionEntity
def represent(resource, opts = {}, entity_class = nil)
super(resource, with_additional_opts(opts), entity_class)
end
private
def with_additional_opts(opts)
additional_opts = {
submodule_links: Gitlab::SubmoduleLinks.new(@request.project.repository)
}
opts.merge(additional_opts)
end
end
# frozen_string_literal: true
class SubmoduleEntity < Grape::Entity
include RequestAwareEntity
expose :id, :path, :name, :mode
expose :icon do |blob|
'archive'
end
expose :url do |blob|
submodule_links(blob, request).first
end
expose :tree_url do |blob|
submodule_links(blob, request).last
end
private
def submodule_links(blob, request)
@submodule_links ||= SubmoduleHelper.submodule_links(blob, request.ref, request.repository)
end
end
......@@ -33,7 +33,7 @@
= sprite_icon("status_success_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-checked")
= sprite_icon("status_failed_borderless", size: 16, css_class: "toggle-icon-svg toggle-status-unchecked")
%p.text-secondary.mt-3
- docs_link_url = help_page_path("user/project/pages/lets_encrypt_for_gitlab_pages.md", anchor: "lets-encrypt-for-gitlab-pages")
- docs_link_url = help_page_path("user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md")
- docs_link_start = "<a href=\"%{docs_link_url}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"text-nowrap\">".html_safe % { docs_link_url: docs_link_url }
- docs_link_end = "</a>".html_safe
= _("Let's Encrypt is a free, automated, and open certificate authority (CA) that gives digital certificates in order to enable HTTPS (SSL/TLS) for websites. Learn more about Let's Encrypt configuration by following the %{docs_link_start}documentation on GitLab Pages%{docs_link_end}.").html_safe % { docs_link_url: docs_link_url, docs_link_start: docs_link_start, docs_link_end: docs_link_end }
......
---
title: 'Resolve Label picker: Line break on long label titles'
merge_request: 30610
author:
type: fixed
---
title: "Preserve footnote link ids in Asciidoctor"
merge_request: 30790
author: Guillaume Grossetie
type: added
\ No newline at end of file
# rubocop:disable Style/SignalException
THROUGHPUT_LABELS = [
'Community contribution',
'security',
'bug',
'feature',
'backstage'
].freeze
if gitlab.mr_body.size < 5
fail "Please provide a proper merge request description."
end
......@@ -8,6 +16,10 @@ if gitlab.mr_labels.empty?
fail "Please add labels to this merge request."
end
if (THROUGHPUT_LABELS & gitlab.mr_labels).empty?
warn 'Please add a [throughput label](https://about.gitlab.com/handbook/engineering/management/throughput/#implementation) to this merge request.'
end
unless gitlab.mr_json["assignee"]
warn "This merge request does not have any assignee yet. Setting an assignee clarifies who needs to take action on the merge request at any given time."
end
......
# frozen_string_literal: true
class FixWrongPagesAccessLevel < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'FixPagesAccessLevel'
BATCH_SIZE = 20_000
BATCH_TIME = 2.minutes
disable_ddl_transaction!
class ProjectFeature < ActiveRecord::Base
include ::EachBatch
self.table_name = 'project_features'
self.inheritance_column = :_type_disabled
end
def up
queue_background_migration_jobs_by_range_at_intervals(
ProjectFeature,
MIGRATION,
BATCH_TIME,
batch_size: BATCH_SIZE)
end
end
# frozen_string_literal: true
class DropProjectFeaturesPagesAccessLevelDefault < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
ENABLED_VALUE = 20
def change
change_column_default :project_features, :pages_access_level, from: ENABLED_VALUE, to: nil
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_07_03_130053) do
ActiveRecord::Schema.define(version: 2019_07_15_114644) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
......@@ -2507,7 +2507,7 @@ ActiveRecord::Schema.define(version: 2019_07_03_130053) do
t.datetime "created_at"
t.datetime "updated_at"
t.integer "repository_access_level", default: 20, null: false
t.integer "pages_access_level", default: 20, null: false
t.integer "pages_access_level", null: false
t.index ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree
end
......
......@@ -265,6 +265,23 @@ verification requirement. Navigate to `Admin area ➔ Settings` and uncheck
**Require users to prove ownership of custom domains** in the Pages section.
This setting is enabled by default.
### Let's Encrypt integration
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28996) in GitLab 12.1.
[GitLab Pages' Let's Encrypt integration](../../user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md)
allows users to add Let's Encrypt SSL certificates for GitLab Pages
sites served under a custom domain.
To enable it, you'll need to:
1. Choose an email on which you will recieve notifications about expiring domains.
1. Navigate to your instance's **Admin Area > Settings > Preferences** and expand **Pages** settings.
1. Enter the email for receiving notifications and accept Let's Encrypt's Terms of Service as shown below.
1. Click **Save changes**.
![Let's Encrypt settings](img/lets_encrypt_integration_v12_1.png)
### Access control
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/33422) in GitLab 11.5.
......
......@@ -204,6 +204,14 @@ can use the following setup:
Read this document for an [overview on SSL/TLS certification](ssl_tls_concepts.md).
To secure your custom domain with GitLab Pages you can opt by:
- Using the [Let's Encrypt integration with GitLab Pages](lets_encrypt_integration.md),
which automatically obtains and renews SSL certificates
for your Pages domains.
- Manually adding SSL/TLS certificates to GitLab Pages websites
by following the steps below.
### Requirements
- A GitLab Pages website up and running accessible via a custom domain.
......@@ -255,6 +263,7 @@ To enable this setting:
1. Navigate to your project's **Settings > Pages**.
1. Tick the checkbox **Force HTTPS (requires valid certificates)**.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
......
---
type: reference
description: "Automatic Let's Encrypt SSL certificates for GitLab Pages."
---
# GitLab Pages integration with Let's Encrypt
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28996) in GitLab 12.1.
The GitLab Pages integration with Let's Encrypt (LE) allows you
to use LE certificates for your Pages website with custom domains
without the hassle of having to issue and update them yourself;
GitLab does it for you, out-of-the-box.
[Let's Encrypt](https://letsencrypt.org) is a free, automated, and
open source Certificate Authority.
## Requirements
Before you can enable automatic provisioning of a SSL certificate for your domain, make sure you have:
- Created a [project](../getting_started_part_two.md) in GitLab
containing your website's source code.
- Acquired a domain (`example.com`) and added a [DNS entry](index.md)
pointing it to your Pages website.
- [Added your domain to your Pages project](index.md#1-add-a-custom-domain-to-pages)
and verified your ownership.
- Have your website up and running, accessible through your custom domain.
NOTE: **Note:**
GitLab's Let's Encrypt integration is enabled and available on GitLab.com.
For **self-managed** GitLab instances, make sure your administrator has
[enabled it](../../../../administration/pages/index.md#lets-encrypt-integration).
## Enabling Let's Encrypt integration for your custom domain
Once you've met the requirements, to enable Let's Encrypt integration:
1. Navigate to your project's **Settings > Pages**.
1. Find your domain and click **Details**.
1. Click **Edit** in the top-right corner.
1. Enable Let's Encrypt integration by switching **Automatic certificate management using Let's Encrypt**:
![Enable Let's Encrypt](img/lets_encrypt_integration_v12_1.png)
1. Click **Save changes**.
Once enabled, GitLab will obtain a LE certificate and add it to the
associated Pages domain. It will be also renewed automatically by GitLab.
> **Notes:**
>
> - Issuing the certificate and updating Pages configuration
> **can take up to an hour**.
> - If you already have SSL certificate in domain settings it
> will continue to work until it will be replaced by Let's Encrypt's certificate.
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
one might have when setting this up, or when something is changed, or on upgrading, it's
important to describe those, too. Think of things that may go wrong and include them here.
This is important to minimize requests for support, and to avoid doc comments with
questions that you know someone might ask.
Each scenario can be a third-level heading, e.g. `### Getting error message X`.
If you have none to add when creating a doc, leave this section in place
but commented out to help encourage others to add to it in the future. -->
......@@ -143,8 +143,8 @@ To learn more about configuration options for GitLab Pages, read the following:
| [Exploring GitLab Pages](introduction.md) | Requirements, technical aspects, specific GitLab CI's configuration options, Access Control, custom 404 pages, limitations, FAQ. |
|---+---|
| [Custom domains and SSL/TLS Certificates](custom_domains_ssl_tls_certification/index.md) | How to add custom domains and subdomains to your website, configure DNS records and SSL/TLS certificates. |
| [Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md) | Secure your Pages sites with Let's Encrypt certificates automatically obtained and renewed by GitLab. |
| [CloudFlare certificates](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/) | Secure your Pages site with CloudFlare certificates. |
| [Let's Encrypt certificates](lets_encrypt_for_gitlab_pages.md) | Secure your Pages site with Let's Encrypt certificates. |
|---+---|
| [Static vs dynamic websites](https://about.gitlab.com/2016/06/03/ssg-overview-gitlab-pages-part-1-dynamic-x-static/) | A conceptual overview on static versus dynamic sites. |
| [Modern static site generators](https://about.gitlab.com/2016/06/10/ssg-overview-gitlab-pages-part-2/) | A conceptual overview on SSGs. |
......
---
description: "How to secure GitLab Pages websites with Let's Encrypt."
description: "How to secure GitLab Pages websites with Let's Encrypt (manual process, deprecated)."
type: howto
last_updated: 2019-06-04
last_updated: 2019-07-15
---
# Let's Encrypt for GitLab Pages
# Let's Encrypt for GitLab Pages (manual process, deprecated)
CAUTION: **Warning:**
This method is still valid but was **deprecated** in favor of the
[Let's Encrypt integration](custom_domains_ssl_tls_certification/lets_encrypt_integration.md)
introduced in GitLab 12.1.
If you have a GitLab Pages website served under your own domain,
you might want to secure it with a SSL/TSL certificate.
......
......@@ -8,12 +8,18 @@ module Banzai
class AsciiDocSanitizationFilter < Banzai::Filter::BaseSanitizationFilter
# Section anchor link pattern
SECTION_LINK_REF_PATTERN = /\A#{Gitlab::Asciidoc::DEFAULT_ADOC_ATTRS['idprefix']}(:?[[:alnum:]]|-|_)+\z/.freeze
SECTION_HEADINGS = %w(h2 h3 h4 h5 h6).freeze
# Footnote link patterns
FOOTNOTE_LINK_ID_PATTERNS = {
a: /\A_footnoteref_\d+\z/,
div: /\A_footnotedef_\d+\z/
}.freeze
# Classes used by Asciidoctor to style components
ADMONITION_CLASSES = %w(fa icon-note icon-tip icon-warning icon-caution icon-important).freeze
CALLOUT_CLASSES = ['conum'].freeze
CHECKLIST_CLASSES = %w(fa fa-check-square-o fa-square-o).freeze
LIST_CLASSES = %w(checklist none no-bullet unnumbered unstyled).freeze
ELEMENT_CLASSES_WHITELIST = {
......@@ -26,8 +32,6 @@ module Banzai
a: ['anchor'].freeze
}.freeze
ALLOWED_HEADERS = %w(h2 h3 h4 h5 h6).freeze
def customize_whitelist(whitelist)
# Allow marks
whitelist[:elements].push('mark')
......@@ -44,20 +48,39 @@ module Banzai
whitelist[:transformers].push(self.class.remove_element_classes)
# Allow `id` in heading elements for section anchors
ALLOWED_HEADERS.each do |header|
SECTION_HEADINGS.each do |header|
whitelist[:attributes][header] = %w(id)
end
whitelist[:transformers].push(self.class.remove_non_heading_ids)
# Allow `id` in footnote elements
FOOTNOTE_LINK_ID_PATTERNS.keys.each do |element|
whitelist[:attributes][element.to_s].push('id')
end
whitelist[:transformers].push(self.class.remove_non_footnote_ids)
whitelist
end
class << self
def remove_non_footnote_ids
lambda do |env|
node = env[:node]
return unless (pattern = FOOTNOTE_LINK_ID_PATTERNS[node.name.to_sym])
return unless node.has_attribute?('id')
return if node['id'] =~ pattern
node.remove_attribute('id')
end
end
def remove_non_heading_ids
lambda do |env|
node = env[:node]
return unless ALLOWED_HEADERS.any?(node.name)
return unless SECTION_HEADINGS.any?(node.name)
return unless node.has_attribute?('id')
return if node['id'] =~ SECTION_LINK_REF_PATTERN
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# corrects stored pages access level on db depending on project visibility
class FixPagesAccessLevel
# Copy routable here to avoid relying on application logic
module Routable
def build_full_path
if parent && path
parent.build_full_path + '/' + path
else
path
end
end
end
# Namespace
class Namespace < ApplicationRecord
self.table_name = 'namespaces'
self.inheritance_column = :_type_disabled
include Routable
belongs_to :parent, class_name: "Namespace"
end
# Project
class Project < ActiveRecord::Base
self.table_name = 'projects'
self.inheritance_column = :_type_disabled
include Routable
belongs_to :namespace
alias_method :parent, :namespace
alias_attribute :parent_id, :namespace_id
PRIVATE = 0
INTERNAL = 10
PUBLIC = 20
def pages_deployed?
Dir.exist?(public_pages_path)
end
def public_pages_path
File.join(pages_path, 'public')
end
def pages_path
# TODO: when we migrate Pages to work with new storage types, change here to use disk_path
File.join(Settings.pages.path, build_full_path)
end
end
# ProjectFeature
class ProjectFeature < ActiveRecord::Base
include ::EachBatch
self.table_name = 'project_features'
belongs_to :project
PRIVATE = 10
ENABLED = 20
PUBLIC = 30
end
def perform(start_id, stop_id)
fix_public_access_level(start_id, stop_id)
make_internal_projects_public(start_id, stop_id)
fix_private_access_level(start_id, stop_id)
end
private
def access_control_is_enabled
@access_control_is_enabled = Gitlab.config.pages.access_control
end
# Public projects are allowed to have only enabled pages_access_level
# which is equivalent to public
def fix_public_access_level(start_id, stop_id)
project_features(start_id, stop_id, ProjectFeature::PUBLIC, Project::PUBLIC).each_batch do |features|
features.update_all(pages_access_level: ProjectFeature::ENABLED)
end
end
# If access control is disabled and project has pages deployed
# project will become unavailable when access control will become enabled
# we make these projects public to avoid negative surprise to user
def make_internal_projects_public(start_id, stop_id)
return if access_control_is_enabled
project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::INTERNAL).find_each do |project_feature|
next unless project_feature.project.pages_deployed?
project_feature.update(pages_access_level: ProjectFeature::PUBLIC)
end
end
# Private projects are not allowed to have enabled access level, only `private` and `public`
# If access control is enabled, these projects currently behave as if the have `private` pages_access_level
# if access control is disabled, these projects currently behave as if the have `public` pages_access_level
# so we preserve this behaviour for projects with pages already deployed
# for project without pages we always set `private` access_level
def fix_private_access_level(start_id, stop_id)
project_features(start_id, stop_id, ProjectFeature::ENABLED, Project::PRIVATE).find_each do |project_feature|
if access_control_is_enabled
project_feature.update!(pages_access_level: ProjectFeature::PRIVATE)
else
fixed_access_level = project_feature.project.pages_deployed? ? ProjectFeature::PUBLIC : ProjectFeature::PRIVATE
project_feature.update!(pages_access_level: fixed_access_level)
end
end
end
def project_features(start_id, stop_id, pages_access_level, project_visibility_level)
ProjectFeature.where(id: start_id..stop_id).joins(:project)
.where(pages_access_level: pages_access_level)
.where(projects: { visibility_level: project_visibility_level })
end
end
end
end
......@@ -464,6 +464,18 @@ module Gitlab
end
end
# Returns path to url mappings for submodules
#
# Ex.
# @repository.submodule_urls_for('master')
# # => { 'rack' => 'git@localhost:rack.git' }
#
def submodule_urls_for(ref)
wrapped_gitaly_errors do
gitaly_submodule_urls_for(ref)
end
end
# Return total commits count accessible from passed ref
def commit_count(ref)
wrapped_gitaly_errors do
......@@ -1059,12 +1071,16 @@ module Gitlab
return unless commit_object && commit_object.type == :COMMIT
urls = gitaly_submodule_urls_for(ref)
urls && urls[path]
end
def gitaly_submodule_urls_for(ref)
gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
return unless gitmodules
found_module = GitmodulesParser.new(gitmodules.data).parse[path]
found_module && found_module['url']
submodules = GitmodulesParser.new(gitmodules.data).parse
submodules.transform_values { |submodule| submodule['url'] }
end
# Returns true if the given ref name exists
......
# frozen_string_literal: true
module Gitlab
module Graphql
module Representation
class SubmoduleTreeEntry < SimpleDelegator
class << self
def decorate(submodules, tree)
repository = tree.repository
submodule_links = Gitlab::SubmoduleLinks.new(repository)
submodules.map do |submodule|
self.new(submodule, submodule_links.for(submodule, tree.sha))
end
end
end
def initialize(submodule, submodule_links)
@submodule_links = submodule_links
super(submodule)
end
def web_url
@submodule_links.first
end
def tree_url
@submodule_links.last
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
class SubmoduleLinks
include Gitlab::Utils::StrongMemoize
def initialize(repository)
@repository = repository
end
def for(submodule, sha)
submodule_url = submodule_url_for(sha)[submodule.path]
SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository)
end
private
attr_reader :repository
def submodule_url_for(sha)
strong_memoize(:"submodule_links_for_#{sha}") do
repository.submodule_urls_for(sha)
end
end
end
end
......@@ -381,7 +381,7 @@ describe 'Issues > Labels bulk assignment' do
if unmark
items.map do |item|
# Make sure we are unmarking the item no matter the state it has currently
click_link item until find('a', text: item)[:class] == 'label-item'
click_link item until find('a', text: item)[:class].include? 'label-item'
end
end
end
......
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlBadge } from '@gitlab/ui';
import { GlBadge, GlLink } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue';
......@@ -142,4 +142,18 @@ describe('Repository table row component', () => {
expect(vm.find(GlBadge).exists()).toBe(true);
});
it('renders commit and web links with href for submodule', () => {
factory({
id: '1',
path: 'test',
type: 'commit',
url: 'https://test.com',
submoduleTreeUrl: 'https://test.com/commit',
currentPath: '/',
});
expect(vm.find('a').attributes('href')).toEqual('https://test.com');
expect(vm.find(GlLink).attributes('href')).toEqual('https://test.com/commit');
});
});
......@@ -5,5 +5,5 @@ require 'spec_helper'
describe Types::Tree::SubmoduleType do
it { expect(described_class.graphql_name).to eq('Submodule') }
it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path) }
it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url, :tree_url) }
end
......@@ -110,6 +110,56 @@ module Gitlab
expect(render(input, context)).to include(output.strip)
end
it 'removes non footnote def ids' do
input = <<~ADOC
++++
<div id="def">Footnote definition</div>
++++
ADOC
output = <<~HTML
<div>Footnote definition</div>
HTML
expect(render(input, context)).to include(output.strip)
end
it 'removes non footnote ref ids' do
input = <<~ADOC
++++
<a id="ref">Footnote reference</a>
++++
ADOC
output = <<~HTML
<a>Footnote reference</a>
HTML
expect(render(input, context)).to include(output.strip)
end
end
context 'with footnotes' do
it 'preserves ids and links' do
input = <<~ADOC
This paragraph has a footnote.footnote:[This is the text of the footnote.]
ADOC
output = <<~HTML
<div>
<p>This paragraph has a footnote.<sup>[<a id="_footnoteref_1" href="#_footnotedef_1" title="View footnote.">1</a>]</sup></p>
</div>
<div>
<hr>
<div id="_footnotedef_1">
<a href="#_footnoteref_1">1</a>. This is the text of the footnote.
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
end
end
context 'with section anchors' do
......
......@@ -256,6 +256,22 @@ describe Gitlab::Git::Repository, :seed_helper do
end
end
describe '#submodule_urls_for' do
let(:ref) { 'master' }
it 'returns url mappings for submodules' do
urls = repository.submodule_urls_for(ref)
expect(urls).to eq({
"deeper/nested/six" => "git://github.com/randx/six.git",
"gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
"gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
"nested/six" => "git://github.com/randx/six.git",
"six" => "git://github.com/randx/six.git"
})
end
end
describe '#commit_count' do
it { expect(repository.commit_count("master")).to eq(25) }
it { expect(repository.commit_count("feature")).to eq(9) }
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Graphql::Representation::SubmoduleTreeEntry do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
describe '.decorate' do
let(:submodules) { repository.tree.submodules }
it 'returns array of SubmoduleTreeEntry' do
entries = described_class.decorate(submodules, repository.tree)
expect(entries.first).to be_a(described_class)
expect(entries.map(&:web_url)).to contain_exactly(
"https://gitlab.com/gitlab-org/gitlab-grack",
"https://github.com/gitlabhq/gitlab-shell",
"https://github.com/randx/six"
)
expect(entries.map(&:tree_url)).to contain_exactly(
"https://gitlab.com/gitlab-org/gitlab-grack/tree/645f6c4c82fd3f5e06f67134450a570b795e55a6",
"https://github.com/gitlabhq/gitlab-shell/tree/79bceae69cb5750d6567b223597999bfa91cb3b9",
"https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
)
end
end
end
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20190703185326_fix_wrong_pages_access_level.rb')
describe FixWrongPagesAccessLevel, :migration, :sidekiq, schema: 20190628185004 do
using RSpec::Parameterized::TableSyntax
let(:migration_class) { described_class::MIGRATION }
let(:migration_name) { migration_class.to_s.demodulize }
project_class = ::Gitlab::BackgroundMigration::FixPagesAccessLevel::Project
feature_class = ::Gitlab::BackgroundMigration::FixPagesAccessLevel::ProjectFeature
let(:namespaces_table) { table(:namespaces) }
let(:projects_table) { table(:projects) }
let(:features_table) { table(:project_features) }
let(:subgroup) do
root_group = namespaces_table.create(path: "group", name: "group")
namespaces_table.create!(path: "subgroup", name: "group", parent_id: root_group.id)
end
def create_project_feature(path, project_visibility, pages_access_level)
project = projects_table.create!(path: path, visibility_level: project_visibility,
namespace_id: subgroup.id)
features_table.create!(project_id: project.id, pages_access_level: pages_access_level)
end
it 'correctly schedules background migrations' do
Sidekiq::Testing.fake! do
Timecop.freeze do
first_id = create_project_feature("project1", project_class::PRIVATE, feature_class::PRIVATE).id
last_id = create_project_feature("project2", project_class::PRIVATE, feature_class::PUBLIC).id
migrate!
expect(migration_name).to be_scheduled_delayed_migration(2.minutes, first_id, last_id)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
def expect_migration
expect do
perform_enqueued_jobs do
migrate!
end
end
end
where(:project_visibility, :pages_access_level, :access_control_is_enabled,
:pages_deployed, :resulting_pages_access_level) do
# update settings for public projects regardless of access_control being enabled
project_class::PUBLIC | feature_class::PUBLIC | true | true | feature_class::ENABLED
project_class::PUBLIC | feature_class::PUBLIC | false | true | feature_class::ENABLED
# don't update public level for private and internal projects
project_class::PRIVATE | feature_class::PUBLIC | true | true | feature_class::PUBLIC
project_class::INTERNAL | feature_class::PUBLIC | true | true | feature_class::PUBLIC
# if access control is disabled but pages are deployed we make them public
project_class::INTERNAL | feature_class::ENABLED | false | true | feature_class::PUBLIC
# don't change anything if one of the conditions is not satisfied
project_class::INTERNAL | feature_class::ENABLED | true | true | feature_class::ENABLED
project_class::INTERNAL | feature_class::ENABLED | true | false | feature_class::ENABLED
# private projects
# if access control is enabled update pages_access_level to private regardless of deployment
project_class::PRIVATE | feature_class::ENABLED | true | true | feature_class::PRIVATE
project_class::PRIVATE | feature_class::ENABLED | true | false | feature_class::PRIVATE
# if access control is disabled and pages are deployed update pages_access_level to public
project_class::PRIVATE | feature_class::ENABLED | false | true | feature_class::PUBLIC
# if access control is disabled but pages aren't deployed update pages_access_level to private
project_class::PRIVATE | feature_class::ENABLED | false | false | feature_class::PRIVATE
end
with_them do
let!(:project_feature) do
create_project_feature("projectpath", project_visibility, pages_access_level)
end
before do
tested_path = File.join(Settings.pages.path, "group/subgroup/projectpath", "public")
allow(Dir).to receive(:exist?).with(tested_path).and_return(pages_deployed)
stub_pages_setting(access_control: access_control_is_enabled)
end
it "sets proper pages_access_level" do
expect(project_feature.reload.pages_access_level).to eq(pages_access_level)
perform_enqueued_jobs do
migrate!
end
expect(project_feature.reload.pages_access_level).to eq(resulting_pages_access_level)
end
end
end
......@@ -150,4 +150,32 @@ describe ProjectFeature do
end
end
end
describe 'default pages access level' do
subject { project.project_feature.pages_access_level }
before do
# project factory overrides all values in project_feature after creation
project.project_feature.destroy!
project.build_project_feature.save!
end
context 'when new project is private' do
let(:project) { create(:project, :private) }
it { is_expected.to eq(ProjectFeature::PRIVATE) }
end
context 'when new project is internal' do
let(:project) { create(:project, :internal) }
it { is_expected.to eq(ProjectFeature::PRIVATE) }
end
context 'when new project is public' do
let(:project) { create(:project, :public) }
it { is_expected.to eq(ProjectFeature::ENABLED) }
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe DiffFileBaseEntity do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
context 'diff for a changed submodule' do
let(:commit_sha_with_changed_submodule) do
"cfe32cf61b73a0d5e9f13e774abde7ff789b1660"
end
let(:commit) { project.commit(commit_sha_with_changed_submodule) }
let(:diff_file) { commit.diffs.diff_files.to_a.last }
let(:options) { { request: {}, submodule_links: Gitlab::SubmoduleLinks.new(repository) } }
let(:entity) { described_class.new(diff_file, options).as_json }
it do
expect(entity[:submodule]).to eq(true)
expect(entity[:submodule_link]).to eq("https://github.com/randx/six")
expect(entity[:submodule_tree_url]).to eq(
"https://github.com/randx/six/tree/409f37c4f05865e4fb208c771485f211a22c4c2d"
)
end
end
end
......@@ -65,6 +65,10 @@ module StubConfiguration
allow(Gitlab.config.artifacts).to receive_messages(to_settings(messages))
end
def stub_pages_setting(messages)
allow(Gitlab.config.pages).to receive_messages(to_settings(messages))
end
def stub_storage_settings(messages)
messages.deep_stringify_keys!
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment