Commit 495c22d1 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent f3b1e079
# Project Name | Migration Tracker
<!-- Please edit this header with your project / organization's name. -->
## Background
<!--
Please add information here about why you're planning on migrating. Include any initial announcements that have been made about the decision or status.
-->
### Goals
<!-- What are some of the goals of your migration to GitLab? Delete this section if you don't want to enumerate goals. -->
## Quick Facts
<!-- Please complete as many items in this list as possible. If you're not sure yet, add "TBD" (To be Decided) or "Unknown" -->
* **Timeline.** -
* **Product.** - GitLab Gold/Ultimate or Commnunity Edition
* **Project's License.** What kind of OSI-approved license does your project use?
## Current Tooling and Replacements
<!--
Please fill in the table to give an overview of your current tooling. Here's a description of what to include in each column:
- Tool: which tool or platform you are currently using
- Feature: which particular feature you are using in that tool or platform
- GitLab feature: equivalent GitLab feature (the GitLab team can help fill this in, as well as the info in the next column)
- GitLab edition: in which GitLab edition (CE or EE) is this feature available?
Here's an example of a replacements overview from one of the projects which migrated to GitLab: https://gitlab.com/gitlab-org/gitlab/-/issues/25657#gitlab-replacements
-->
| Tool | Feature | GitLab feature | GitLab edition |
| --- | --- | --- | --- |
| | | | |
## Collaborators
<!-- Please add names of collaborators in the format: Name, Title, Role (what will you be helping to do, or how should you be involved), GitLab username -->
## Related Issues
<!-- Add any related issues that are important for your project by adding the title of the issue and a link to it (preferably as an embedded link). You will probably keep editing this section as the migration progresses, so don't worry if it's mostly blank for now.
Here is an example of what this list might look like once populated: https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55039#outstanding-issues
-->
### Blockers
* [ ] ADD_LINK_TO_ISSUE_HERE
### Urgent
* [ ]
### Important but not urgent
* [ ]
### Nice to have
* [ ]
------
/label ~Open-Source ~movingtogitlab
/cc @nuritzi
\ No newline at end of file
...@@ -385,4 +385,3 @@ Performance/ChainArrayAllocation: ...@@ -385,4 +385,3 @@ Performance/ChainArrayAllocation:
RSpec/RepeatedExample: RSpec/RepeatedExample:
Exclude: Exclude:
- 'spec/features/merge_request/user_posts_diff_notes_spec.rb' - 'spec/features/merge_request/user_posts_diff_notes_spec.rb'
- 'spec/services/notification_service_spec.rb'
...@@ -192,6 +192,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -192,6 +192,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params[:application_setting][:import_sources]&.delete("") params[:application_setting][:import_sources]&.delete("")
params[:application_setting][:restricted_visibility_levels]&.delete("") params[:application_setting][:restricted_visibility_levels]&.delete("")
params[:application_setting].delete(:elasticsearch_aws_secret_access_key) if params[:application_setting][:elasticsearch_aws_secret_access_key].blank? params[:application_setting].delete(:elasticsearch_aws_secret_access_key) if params[:application_setting][:elasticsearch_aws_secret_access_key].blank?
params[:application_setting][:required_instance_ci_template] = nil if params[:application_setting][:required_instance_ci_template].blank?
# TODO Remove domain_blacklist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204) # TODO Remove domain_blacklist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-foss/issues/67204)
params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
params.delete(:domain_blacklist_raw) if params[:domain_blacklist] params.delete(:domain_blacklist_raw) if params[:domain_blacklist]
......
# frozen_string_literal: true
class Projects::StaticSiteEditorController < Projects::ApplicationController
layout 'fullscreen'
prepend_before_action :authenticate_user!, only: [:show]
def show
end
end
...@@ -110,7 +110,7 @@ module HasRepository ...@@ -110,7 +110,7 @@ module HasRepository
end end
def web_url(only_path: nil) def web_url(only_path: nil)
raise NotImplementedError Gitlab::UrlBuilder.build(self, only_path: only_path)
end end
def repository_size_checker def repository_size_checker
......
...@@ -172,8 +172,8 @@ class Group < Namespace ...@@ -172,8 +172,8 @@ class Group < Namespace
"#{self.class.reference_prefix}#{full_path}" "#{self.class.reference_prefix}#{full_path}"
end end
def web_url def web_url(only_path: nil)
Gitlab::Routing.url_helpers.group_canonical_url(self) Gitlab::UrlBuilder.build(self, only_path: only_path)
end end
def human_name def human_name
......
...@@ -2,8 +2,4 @@ ...@@ -2,8 +2,4 @@
class PersonalSnippet < Snippet class PersonalSnippet < Snippet
include WithUploads include WithUploads
def web_url(only_path: nil)
Gitlab::Routing.url_helpers.snippet_url(self, only_path: only_path)
end
end end
...@@ -1121,10 +1121,6 @@ class Project < ApplicationRecord ...@@ -1121,10 +1121,6 @@ class Project < ApplicationRecord
end end
end end
def web_url(only_path: nil)
Gitlab::Routing.url_helpers.project_url(self, only_path: only_path)
end
def readme_url def readme_url
readme_path = repository.readme_path readme_path = repository.readme_path
if readme_path if readme_path
......
...@@ -5,8 +5,4 @@ class ProjectSnippet < Snippet ...@@ -5,8 +5,4 @@ class ProjectSnippet < Snippet
validates :project, presence: true validates :project, presence: true
validates :secret, inclusion: { in: [false] } validates :secret, inclusion: { in: [false] }
def web_url(only_path: nil)
Gitlab::Routing.url_helpers.project_snippet_url(project, self, only_path: only_path)
end
end end
...@@ -44,8 +44,8 @@ class ProjectWiki ...@@ -44,8 +44,8 @@ class ProjectWiki
# @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem # @deprecated use full_path when you need it for an URL route or disk_path when you want to point to the filesystem
alias_method :path_with_namespace, :full_path alias_method :path_with_namespace, :full_path
def web_url def web_url(only_path: nil)
Gitlab::Routing.url_helpers.project_wiki_url(@project, :home) Gitlab::UrlBuilder.build(self, only_path: only_path)
end end
def url_to_repo def url_to_repo
......
...@@ -18,7 +18,7 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated ...@@ -18,7 +18,7 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated
end end
def web_url def web_url
Gitlab::UrlBuilder.new(commit).url url_builder.build(commit)
end end
def signature_html def signature_html
......
...@@ -4,20 +4,14 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated ...@@ -4,20 +4,14 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated
presents :issue presents :issue
def web_url def web_url
url_builder.url url_builder.build(issue)
end end
def issue_path def issue_path
url_builder.issue_path(issue) url_builder.build(issue, only_path: true)
end end
def subscribed? def subscribed?
issue.subscribed?(current_user, issue.project) issue.subscribed?(current_user, issue.project)
end end
private
def url_builder
@url_builder ||= Gitlab::UrlBuilder.new(issue)
end
end end
...@@ -4,12 +4,6 @@ class MilestonePresenter < Gitlab::View::Presenter::Delegated ...@@ -4,12 +4,6 @@ class MilestonePresenter < Gitlab::View::Presenter::Delegated
presents :milestone presents :milestone
def milestone_path def milestone_path
url_builder.milestone_path(milestone) url_builder.build(milestone, only_path: true)
end
private
def url_builder
@url_builder ||= Gitlab::UrlBuilder.new(milestone)
end end
end end
...@@ -35,9 +35,7 @@ module Snippets ...@@ -35,9 +35,7 @@ module Snippets
private private
def save_and_commit(snippet) def save_and_commit(snippet)
snippet.with_transaction_returning_status do return false unless snippet.save
snippet.save.tap do |saved|
break false unless saved
# In order to avoid non migrated snippets scenarios, # In order to avoid non migrated snippets scenarios,
# if the snippet does not have a repository we created it # if the snippet does not have a repository we created it
...@@ -50,13 +48,30 @@ module Snippets ...@@ -50,13 +48,30 @@ module Snippets
# If the snippet repository exists we commit always # If the snippet repository exists we commit always
# the changes # the changes
create_commit(snippet) if snippet.repository_exists? create_commit(snippet) if snippet.repository_exists?
end
true
rescue => e rescue => e
snippet.errors.add(:repository, e.message) # Restore old attributes
unless snippet.previous_changes.empty?
snippet.previous_changes.each { |attr, value| snippet[attr] = value[0] }
snippet.save
end
snippet.errors.add(:repository, 'Error updating the snippet')
log_error(e.message) log_error(e.message)
false # If the commit action failed we remove it because
# we don't want to leave empty repositories
# around, to allow cloning them.
if repository_empty?(snippet)
snippet.repository.remove
snippet.snippet_repository&.delete
end end
# Purge any existing value for repository_exists?
snippet.repository.expire_exists_cache
false
end end
def create_repository_for(snippet) def create_repository_for(snippet)
...@@ -81,5 +96,13 @@ module Snippets ...@@ -81,5 +96,13 @@ module Snippets
file_path: params[:file_name], file_path: params[:file_name],
content: params[:content] }] content: params[:content] }]
end end
# Because we are removing repositories we don't want to remove
# any existing repository with data. Therefore, we cannot
# rely on cached methods for that check in order to avoid losing
# data.
def repository_empty?(snippet)
snippet.repository._uncached_exists? && !snippet.repository._uncached_has_visible_content?
end
end end
end end
---
title: Fix race condition updating snippet without repository
merge_request: 28851
author:
type: fixed
---
title: Update duplicate specs in notification service spec
merge_request: 28742
author: Rajendra Kadam
type: fixed
...@@ -67,6 +67,10 @@ scope format: false do ...@@ -67,6 +67,10 @@ scope format: false do
end end
end end
scope controller: :static_site_editor do
get '/sse/*id', action: :show, as: :show_sse
end
get '/tree/*id', to: 'tree#show', as: :tree get '/tree/*id', to: 'tree#show', as: :tree
get '/raw/*id', to: 'raw#show', as: :raw get '/raw/*id', to: 'raw#show', as: :raw
get '/blame/*id', to: 'blame#show', as: :blame get '/blame/*id', to: 'blame#show', as: :blame
......
# frozen_string_literal: true # frozen_string_literal: true
TELEMETRY_CHANGED_FILES_MESSAGE = <<~MSG TELEMETRY_CHANGED_FILES_MESSAGE = <<~MSG
This merge request adds or changes files for which a This merge request includes changes for which a review from the Data team and Telemetry team is recommended.
review from the Data team and Telemetry team is recommended. Please reach out to @gitlab-org/growth/telemetry/engineers group for a review.
@gitlab-org/growth/telemetry group is mentioned in order to notify team members.
MSG MSG
USAGE_DATA_FILES_MESSAGE = <<~MSG USAGE_DATA_FILES_MESSAGE = <<~MSG
For the following files, a review from the [Data team and Telemetry team](https://gitlab.com/groups/gitlab-org/growth/telemetry/-/group_members?with_inherited_permissions=exclude) is recommended: For the following files, a review from the [Data team and Telemetry team](https://gitlab.com/groups/gitlab-org/growth/telemetry/engineers/-/group_members?with_inherited_permissions=exclude) is recommended:
MSG MSG
usage_data_changed_files = git.modified_files.grep(%r{usage_data}) usage_data_changed_files = git.modified_files.grep(%r{usage_data})
......
...@@ -434,6 +434,10 @@ I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory ...@@ -434,6 +434,10 @@ I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory
User clone/fetch activity using SSH transport appears in this log as `executing git command <gitaly-upload-pack...`. User clone/fetch activity using SSH transport appears in this log as `executing git command <gitaly-upload-pack...`.
## `current`
This file lives in `/var/log/gitlab/gitaly/current` and is produced by [runit](http://smarden.org/runit/). `runit` is packaged with Omnibus and a brief explanation of its purpose is available [in the omnibus documentation](https://docs.gitlab.com/omnibus/architecture/#runit). [Log files are rotated](http://smarden.org/runit/svlogd.8.html), renamed in unix timestamp format and `gzip`-compressed (e.g. `@1584057562.s`).
## `unicorn_stderr.log` ## `unicorn_stderr.log`
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
......
...@@ -167,7 +167,7 @@ The following variables are used for configuring specific analyzers (used for a ...@@ -167,7 +167,7 @@ The following variables are used for configuring specific analyzers (used for a
| `DS_PIP_VERSION` | `gemnasium-python` | | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12811) in GitLab 12.7) | | `DS_PIP_VERSION` | `gemnasium-python` | | Force the install of a specific pip version (example: `"19.3"`), otherwise the pip installed in the Docker image is used. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12811) in GitLab 12.7) |
| `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) | | `DS_PIP_DEPENDENCY_PATH` | `gemnasium-python` | | Path to load Python pip dependencies from. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12412) in GitLab 12.2) |
| `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)| | `DS_PYTHON_VERSION` | `retire.js` | | Version of Python. If set to 2, dependencies are installed using Python 2.7 instead of Python 3.6. ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12296) in GitLab 12.1)|
| `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that will be passed to `maven` by the analyzer. See an example for [using private repos](#using-private-maven-repos). | | `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that will be passed to `maven` by the analyzer. See an example for [using private repos](../index.md#using-private-maven-repos). |
| `BUNDLER_AUDIT_UPDATE_DISABLED` | `bundler-audit` | `"false"` | Disable automatic updates for the `bundler-audit` analyzer. Useful if you're running Dependency Scanning in an offline, air-gapped environment.| | `BUNDLER_AUDIT_UPDATE_DISABLED` | `bundler-audit` | `"false"` | Disable automatic updates for the `bundler-audit` analyzer. Useful if you're running Dependency Scanning in an offline, air-gapped environment.|
| `BUNDLER_AUDIT_ADVISORY_DB_URL` | `bundler-audit` | `https://github.com/rubysec/ruby-advisory-db` | URL of the advisory database used by bundler-audit. | | `BUNDLER_AUDIT_ADVISORY_DB_URL` | `bundler-audit` | `https://github.com/rubysec/ruby-advisory-db` | URL of the advisory database used by bundler-audit. |
| `BUNDLER_AUDIT_ADVISORY_DB_REF_NAME` | `bundler-audit` | `master` | Git ref for the advisory database specified by `BUNDLER_AUDIT_ADVISORY_DB_URL`. | | `BUNDLER_AUDIT_ADVISORY_DB_REF_NAME` | `bundler-audit` | `master` | Git ref for the advisory database specified by `BUNDLER_AUDIT_ADVISORY_DB_URL`. |
...@@ -177,28 +177,9 @@ The following variables are used for configuring specific analyzers (used for a ...@@ -177,28 +177,9 @@ The following variables are used for configuring specific analyzers (used for a
### Using private Maven repos ### Using private Maven repos
If you have a private Maven repository which requires login credentials, If you have a private Maven repository which requires login credentials,
you can use the `MAVEN_CLI_OPTS` environment variable to pass variables you can use the `MAVEN_CLI_OPTS` environment variable.
specified in your settings (e.g., username, password, etc.).
Read more on [how to use private Maven repos](../index.md#using-private-maven-repos).
For example, if you have a settings file in your project source (e.g., `mysettings.xml`)
that looks like the following, you can specify the variables
[by adding an entry under your project's settings](../../../ci/variables/README.md#via-the-ui),
so that you don't have to expose your private data in `.gitlab-ci.yml` (e.g., adding
`MAVEN_CLI_OPTS` with value `--settings mysettings.xml -Dprivate.username=foo -Dprivate.password=bar`).
```xml
<!-- mysettings.xml -->
<settings>
...
<servers>
<server>
<id>private_server</id>
<username>${private.username}</username>
<password>${private.password}</password>
</server>
</servers>
</settings>
```
### Disabling Docker in Docker for Dependency Scanning ### Disabling Docker in Docker for Dependency Scanning
...@@ -217,6 +198,14 @@ variables: ...@@ -217,6 +198,14 @@ variables:
This will create individual `<analyzer-name>-dependency_scanning` jobs for each analyzer that runs in your CI/CD pipeline. This will create individual `<analyzer-name>-dependency_scanning` jobs for each analyzer that runs in your CI/CD pipeline.
By removing Docker-in-Docker (DIND), GitLab relies on [Linguist](https://github.com/github/linguist)
to start relevant analyzers depending on the detected repository language(s) instead of the
[orchestrator](https://gitlab.com/gitlab-org/security-products/dependency-scanning/). However, there
are some differences in the way repository languages are detected between DIND and non-DIND. You can
observe these differences by checking both Linguist and the common library. For instance, Linguist
looks for `*.java` files to spin up the [gemnasium-maven](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven)
image, while orchestrator only looks for the existence of `pom.xml` or `build.gradle`.
## Interacting with the vulnerabilities ## Interacting with the vulnerabilities
Once a vulnerability is found, you can interact with it. Read more on how to Once a vulnerability is found, you can interact with it. Read more on how to
......
...@@ -251,6 +251,35 @@ environment. ...@@ -251,6 +251,35 @@ environment.
Read how to [operate the Secure scanners in an offline environment](offline_deployments/index.md). Read how to [operate the Secure scanners in an offline environment](offline_deployments/index.md).
## Using private Maven repos
If you have a private Apache Maven repository that requires login credentials,
you can use the `MAVEN_CLI_OPTS` environment variable
to pass a username and password. You can set it under your project's settings
so that your credentials aren't exposed in `.gitlab-ci.yml`.
If the username is `myuser` and the password is `verysecret` then you would
[set the following variable](../../ci/variables/README.md#via-the-ui)
under your project's settings:
| Type | Key | Value |
| ---- | --- | ----- |
| Variable | `MAVEN_CLI_OPTS` | `--settings mysettings.xml -Drepository.password=verysecret -Drepository.user=myuser` |
```xml
<!-- mysettings.xml -->
<settings>
...
<servers>
<server>
<id>private_server</id>
<username>${private.username}</username>
<password>${private.password}</password>
</server>
</servers>
</settings>
```
## Outdated security reports ## Outdated security reports
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4913) in GitLab 12.7. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/4913) in GitLab 12.7.
......
...@@ -166,18 +166,10 @@ it via [custom environment variables](#custom-environment-variables). ...@@ -166,18 +166,10 @@ it via [custom environment variables](#custom-environment-variables).
#### Using a variable to pass username and password to a private Maven repository #### Using a variable to pass username and password to a private Maven repository
If you have a private Apache Maven repository that requires login credentials, If you have a private Maven repository which requires login credentials,
you can use the `MAVEN_CLI_OPTS` [environment variable](#available-variables) you can use the `MAVEN_CLI_OPTS` environment variable.
to pass a username and password. You can set it under your project's settings
so that your credentials aren't exposed in `.gitlab-ci.yml`.
If the username is `myuser` and the password is `verysecret` then you would Read more on [how to use private Maven repos](../index.md#using-private-maven-repos).
[set the following variable](../../../ci/variables/README.md#via-the-ui)
under your project's settings:
| Type | Key | Value |
| ---- | --- | ----- |
| Variable | `MAVEN_CLI_OPTS` | `-Drepository.password=verysecret -Drepository.user=myuser` |
### Disabling Docker in Docker for SAST ### Disabling Docker in Docker for SAST
...@@ -194,6 +186,15 @@ variables: ...@@ -194,6 +186,15 @@ variables:
This will create individual `<analyzer-name>-sast` jobs for each analyzer that runs in your CI/CD pipeline. This will create individual `<analyzer-name>-sast` jobs for each analyzer that runs in your CI/CD pipeline.
By removing Docker-in-Docker (DIND), GitLab relies on [Linguist](https://github.com/github/linguist)
to start relevant analyzers depending on the detected repository language(s) instead of the
[orchestrator](https://gitlab.com/gitlab-org/security-products/dependency-scanning/). However, there
are some differences in the way repository languages are detected between DIND and non-DIND. You can
observe these differences by checking both Linguist and the common library. For instance, Linguist
looks for `*.java` files to spin up the [spotbugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs)
image, while orchestrator only looks for the existence of `pom.xml`, `build.xml`, `gradlew`,
`grailsw`, or `mvnw`.
#### Enabling kubesec analyzer #### Enabling kubesec analyzer
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12752) in GitLab Ultimate 12.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12752) in GitLab Ultimate 12.6.
......
...@@ -191,35 +191,10 @@ If you still need to run tests during `mvn install`, add `-DskipTests=false` to ...@@ -191,35 +191,10 @@ If you still need to run tests during `mvn install`, add `-DskipTests=false` to
#### Using private Maven repos #### Using private Maven repos
If you have a private Maven repository that requires login credentials, you can use the If you have a private Maven repository which requires login credentials,
`MAVEN_CLI_OPTS` variable to specify a custom [`settings.xml`](http://maven.apache.org/settings.html) you can use the `MAVEN_CLI_OPTS` environment variable.
file.
For example, you may have a settings file like this in your project source:
```xml
<settings>
<servers>
<server>
<id>my-server</id>
<username>${private.username}</username>
<username>${private.password}</username>
</server>
</servers>
</settings>
```
You can use this file through the following declaration in your `gitlab-ci.yml` file:
```yaml
license_scanning:
variables:
MAVEN_CLI_OPTS: --settings settings.xml -Dprivate.username=foo -Dprivate.password=bar
```
NOTE: **Note:** Read more on [how to use private Maven repos](../../application_security/index.md#using-private-maven-repos).
If you don't want to expose the credentials in your `.gitlab-ci.yml` file, then
you can [set the variable in your project's settings](../../../ci/variables/README.md#via-the-ui).
### Selecting the version of Python ### Selecting the version of Python
......
...@@ -98,6 +98,7 @@ module Gitlab ...@@ -98,6 +98,7 @@ module Gitlab
preview preview
raw raw
refs refs
sse
tree tree
update update
wikis wikis
......
...@@ -2,76 +2,74 @@ ...@@ -2,76 +2,74 @@
module Gitlab module Gitlab
class UrlBuilder class UrlBuilder
include Singleton
include Gitlab::Routing include Gitlab::Routing
include GitlabRoutingHelper include GitlabRoutingHelper
include ActionView::RecordIdentifier
attr_reader :object, :opts delegate :build, to: :class
def self.build(object, opts = {}) class << self
new(object, opts).url include ActionView::RecordIdentifier
end
def url def build(object, **options)
# Objects are sometimes wrapped in a BatchLoader instance # Objects are sometimes wrapped in a BatchLoader instance
case object.itself case object.itself
when ::Ci::Build
instance.project_job_url(object.project, object, **options)
when Commit when Commit
commit_url commit_url(object, **options)
when Group
instance.group_canonical_url(object, **options)
when Issue when Issue
issue_url(object) instance.issue_url(object, **options)
when MergeRequest when MergeRequest
merge_request_url(object) instance.merge_request_url(object, **options)
when Milestone
instance.milestone_url(object, **options)
when Note when Note
note_url note_url(object, **options)
when WikiPage when Project
wiki_page_url instance.project_url(object, **options)
when Snippet when Snippet
opts[:raw].present? ? gitlab_raw_snippet_url(object) : gitlab_snippet_url(object) snippet_url(object, **options)
when Milestone
milestone_url(object)
when ::Ci::Build
project_job_url(object.project, object)
when User when User
user_url(object) instance.user_url(object, **options)
when ProjectWiki
instance.project_wiki_url(object.project, :home, **options)
when WikiPage
instance.project_wiki_url(object.wiki.project, object.slug, **options)
else else
raise NotImplementedError.new("No URL builder defined for #{object.inspect}") raise NotImplementedError.new("No URL builder defined for #{object.inspect}")
end end
end end
private def commit_url(commit, **options)
return '' unless commit.project
def initialize(object, opts = {}) instance.commit_url(commit, **options)
@object = object
@opts = opts
end end
def commit_url(opts = {}) def note_url(note, **options)
return '' if object.project.nil? if note.for_commit?
return '' unless note.project
namespace_project_commit_url({ instance.project_commit_url(note.project, note.commit_id, anchor: dom_id(note), **options)
namespace_id: object.project.namespace, elsif note.for_issue?
project_id: object.project, instance.issue_url(note.noteable, anchor: dom_id(note), **options)
id: object.id elsif note.for_merge_request?
}.merge!(opts)) instance.merge_request_url(note.noteable, anchor: dom_id(note), **options)
elsif note.for_snippet?
instance.gitlab_snippet_url(note.noteable, anchor: dom_id(note), **options)
end
end end
def note_url def snippet_url(snippet, **options)
if object.for_commit? if options.delete(:raw).present?
commit_url(id: object.commit_id, anchor: dom_id(object)) instance.gitlab_raw_snippet_url(snippet, **options)
else
elsif object.for_issue? instance.gitlab_snippet_url(snippet, **options)
issue_url(object.noteable, anchor: dom_id(object))
elsif object.for_merge_request?
merge_request_url(object.noteable, anchor: dom_id(object))
elsif object.for_snippet?
gitlab_snippet_url(object.noteable, anchor: dom_id(object))
end end
end end
def wiki_page_url
project_wiki_url(object.wiki.project, object.slug)
end end
end end
end end
......
...@@ -26,6 +26,10 @@ module Gitlab ...@@ -26,6 +26,10 @@ module Gitlab
self self
end end
def url_builder
Gitlab::UrlBuilder.instance
end
class_methods do class_methods do
def presenter? def presenter?
true true
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::StaticSiteEditorController do
let(:project) { create(:project, :public, :repository) }
describe 'GET show' do
let(:default_params) do
{
namespace_id: project.namespace,
project_id: project,
id: 'master/README.md'
}
end
context 'User roles' do
context 'anonymous' do
before do
get :show, params: default_params
end
it 'redirects to sign in and returns' do
expect(response).to redirect_to(new_user_session_path)
end
end
%w[guest developer maintainer].each do |role|
context "as #{role}" do
let(:user) { create(:user) }
before do
project.add_role(user, role)
sign_in(user)
get :show, params: default_params
end
it 'renders the edit page' do
expect(response).to render_template(:show)
end
end
end
end
end
end
...@@ -25,6 +25,14 @@ FactoryBot.define do ...@@ -25,6 +25,14 @@ FactoryBot.define do
due_date { Date.new(2000, 1, 30) } due_date { Date.new(2000, 1, 30) }
end end
trait :on_project do
project
end
trait :on_group do
group
end
after(:build, :stub) do |milestone, evaluator| after(:build, :stub) do |milestone, evaluator|
if evaluator.group if evaluator.group
milestone.group = evaluator.group milestone.group = evaluator.group
...@@ -44,5 +52,7 @@ FactoryBot.define do ...@@ -44,5 +52,7 @@ FactoryBot.define do
factory :active_milestone, traits: [:active] factory :active_milestone, traits: [:active]
factory :closed_milestone, traits: [:closed] factory :closed_milestone, traits: [:closed]
factory :project_milestone, traits: [:on_project]
factory :group_milestone, traits: [:on_group]
end end
end end
...@@ -54,11 +54,9 @@ describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -54,11 +54,9 @@ describe 'Projects > Snippets > User updates a snippet', :js do
end end
context 'when the git operation fails' do context 'when the git operation fails' do
let(:error_message) { 'foobar' }
before do before do
allow_next_instance_of(Snippets::UpdateService) do |instance| allow_next_instance_of(Snippets::UpdateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError, error_message) allow(instance).to receive(:create_commit).and_raise(StandardError)
end end
fill_in('project_snippet_title', with: 'Snippet new title') fill_in('project_snippet_title', with: 'Snippet new title')
...@@ -67,7 +65,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do ...@@ -67,7 +65,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do
end end
it 'renders edit page and displays the error' do it 'renders edit page and displays the error' do
expect(page.find('.flash-container span').text).to eq(error_message) expect(page.find('.flash-container span').text).to eq('Error updating the snippet')
expect(page).to have_content('Edit Snippet') expect(page).to have_content('Edit Snippet')
end end
end end
......
...@@ -85,11 +85,9 @@ describe 'User edits snippet', :js do ...@@ -85,11 +85,9 @@ describe 'User edits snippet', :js do
end end
context 'when the git operation fails' do context 'when the git operation fails' do
let(:error_message) { 'foobar' }
before do before do
allow_next_instance_of(Snippets::UpdateService) do |instance| allow_next_instance_of(Snippets::UpdateService) do |instance|
allow(instance).to receive(:create_commit).and_raise(StandardError, error_message) allow(instance).to receive(:create_commit).and_raise(StandardError)
end end
fill_in 'personal_snippet_title', with: 'New Snippet Title' fill_in 'personal_snippet_title', with: 'New Snippet Title'
...@@ -98,7 +96,7 @@ describe 'User edits snippet', :js do ...@@ -98,7 +96,7 @@ describe 'User edits snippet', :js do
end end
it 'renders edit page and displays the error' do it 'renders edit page and displays the error' do
expect(page.find('.flash-container span').text).to eq(error_message) expect(page.find('.flash-container span').text).to eq('Error updating the snippet')
expect(page).to have_content('Edit Snippet') expect(page).to have_content('Edit Snippet')
end end
end end
......
...@@ -31,6 +31,6 @@ export const GlPopover = { ...@@ -31,6 +31,6 @@ export const GlPopover = {
}, },
}, },
render(h) { render(h) {
return h('div', this.$attrs, this.$slots.default); return h('div', this.$attrs, Object.keys(this.$slots).map(s => this.$slots[s]));
}, },
}; };
...@@ -3,186 +3,116 @@ ...@@ -3,186 +3,116 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::UrlBuilder do describe Gitlab::UrlBuilder do
describe '.build' do subject { described_class }
context 'when passing a Commit' do
it 'returns a proper URL' do
commit = build_stubbed(:commit)
url = described_class.build(commit)
expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.full_path}/-/commit/#{commit.id}"
end
end
context 'when passing a batch loaded Commit' do
it 'returns a proper URL' do
commit = BatchLoader.for(:commit).batch do |batch, loader|
batch.each { |commit| loader.call(:commit, build_stubbed(:commit)) }
end
url = described_class.build(commit)
expect(url).to eq "#{Settings.gitlab['url']}/#{commit.project.full_path}/-/commit/#{commit.id}"
end
end
context 'when passing an Issue' do
it 'returns a proper URL' do
issue = build_stubbed(:issue, iid: 42)
url = described_class.build(issue)
expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/-/issues/#{issue.iid}"
end
end
context 'when passing a Milestone' do
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
context 'belonging to a project' do
it 'returns a proper URL' do
milestone = create(:milestone, project: project)
url = described_class.build(milestone)
expect(url).to eq "#{Settings.gitlab['url']}/#{milestone.project.full_path}/-/milestones/#{milestone.iid}"
end
end
context 'belonging to a group' do describe '#build' do
it 'returns a proper URL' do it 'delegates to the class method' do
milestone = create(:milestone, group: group) expect(subject).to receive(:build).with(:foo, bar: :baz)
url = described_class.build(milestone) subject.instance.build(:foo, bar: :baz)
expect(url).to eq "#{Settings.gitlab['url']}/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}"
end
end end
end end
context 'when passing a MergeRequest' do describe '.build' do
it 'returns a proper URL' do using RSpec::Parameterized::TableSyntax
merge_request = build_stubbed(:merge_request, iid: 42)
url = described_class.build(merge_request)
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}"
end
end
context 'when passing a ProjectSnippet' do
it 'returns a proper URL' do
project_snippet = create(:project_snippet)
url = described_class.build(project_snippet)
expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.full_path}/snippets/#{project_snippet.id}" where(:factory, :path_generator) do
end :project | ->(project) { "/#{project.full_path}" }
end :commit | ->(commit) { "/#{commit.project.full_path}/-/commit/#{commit.id}" }
:issue | ->(issue) { "/#{issue.project.full_path}/-/issues/#{issue.iid}" }
:merge_request | ->(merge_request) { "/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}" }
:project_milestone | ->(milestone) { "/#{milestone.project.full_path}/-/milestones/#{milestone.iid}" }
:project_snippet | ->(snippet) { "/#{snippet.project.full_path}/snippets/#{snippet.id}" }
:project_wiki | ->(wiki) { "/#{wiki.project.full_path}/-/wikis/home" }
:ci_build | ->(build) { "/#{build.project.full_path}/-/jobs/#{build.id}" }
context 'when passing a PersonalSnippet' do :group | ->(group) { "/groups/#{group.full_path}" }
it 'returns a proper URL' do :group_milestone | ->(milestone) { "/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}" }
personal_snippet = create(:personal_snippet)
url = described_class.build(personal_snippet) :user | ->(user) { "/#{user.full_path}" }
:personal_snippet | ->(snippet) { "/snippets/#{snippet.id}" }
:wiki_page | ->(wiki_page) { "#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" }
expect(url).to eq "#{Settings.gitlab['url']}/snippets/#{personal_snippet.id}" :note_on_commit | ->(note) { "/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}" }
end :diff_note_on_commit | ->(note) { "/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}" }
end :discussion_note_on_commit | ->(note) { "/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}" }
:legacy_diff_note_on_commit | ->(note) { "/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}" }
context 'when passing a Note' do :note_on_issue | ->(note) { "/#{note.project.full_path}/-/issues/#{note.noteable.iid}#note_#{note.id}" }
context 'on a Commit' do :discussion_note_on_issue | ->(note) { "/#{note.project.full_path}/-/issues/#{note.noteable.iid}#note_#{note.id}" }
it 'returns a proper URL' do
note = build_stubbed(:note_on_commit)
url = described_class.build(note) :note_on_merge_request | ->(note) { "/#{note.project.full_path}/-/merge_requests/#{note.noteable.iid}#note_#{note.id}" }
:diff_note_on_merge_request | ->(note) { "/#{note.project.full_path}/-/merge_requests/#{note.noteable.iid}#note_#{note.id}" }
:discussion_note_on_merge_request | ->(note) { "/#{note.project.full_path}/-/merge_requests/#{note.noteable.iid}#note_#{note.id}" }
:legacy_diff_note_on_merge_request | ->(note) { "/#{note.project.full_path}/-/merge_requests/#{note.noteable.iid}#note_#{note.id}" }
expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}" :note_on_project_snippet | ->(note) { "/#{note.project.full_path}/snippets/#{note.noteable_id}#note_#{note.id}" }
end :discussion_note_on_project_snippet | ->(note) { "/#{note.project.full_path}/snippets/#{note.noteable_id}#note_#{note.id}" }
:discussion_note_on_personal_snippet | ->(note) { "/snippets/#{note.noteable_id}#note_#{note.id}" }
:note_on_personal_snippet | ->(note) { "/snippets/#{note.noteable_id}#note_#{note.id}" }
end end
context 'on a Commit Diff' do with_them do
it 'returns a proper URL' do let(:object) { build_stubbed(factory) }
note = build_stubbed(:diff_note_on_commit) let(:path) { path_generator.call(object) }
url = described_class.build(note) it 'returns the full URL' do
expect(subject.build(object)).to eq("#{Gitlab.config.gitlab.url}#{path}")
expect(url).to eq "#{Settings.gitlab['url']}/#{note.project.full_path}/-/commit/#{note.commit_id}#note_#{note.id}"
end
end end
context 'on an Issue' do it 'returns only the path if only_path is given' do
it 'returns a proper URL' do expect(subject.build(object, only_path: true)).to eq(path)
issue = create(:issue, iid: 42)
note = build_stubbed(:note_on_issue, noteable: issue)
url = described_class.build(note)
expect(url).to eq "#{Settings.gitlab['url']}/#{issue.project.full_path}/-/issues/#{issue.iid}#note_#{note.id}"
end end
end end
context 'on a MergeRequest' do context 'when passing a commit without a project' do
it 'returns a proper URL' do let(:commit) { build_stubbed(:commit) }
merge_request = create(:merge_request, iid: 42)
note = build_stubbed(:note_on_merge_request, noteable: merge_request)
url = described_class.build(note) it 'returns an empty string' do
allow(commit).to receive(:project).and_return(nil)
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}#note_#{note.id}" expect(subject.build(commit)).to eq('')
end end
end end
context 'on a MergeRequest Diff' do context 'when passing a commit note without a project' do
it 'returns a proper URL' do let(:note) { build_stubbed(:note_on_commit) }
merge_request = create(:merge_request, iid: 42)
note = build_stubbed(:diff_note_on_merge_request, noteable: merge_request)
url = described_class.build(note) it 'returns an empty string' do
allow(note).to receive(:project).and_return(nil)
expect(url).to eq "#{Settings.gitlab['url']}/#{merge_request.project.full_path}/-/merge_requests/#{merge_request.iid}#note_#{note.id}" expect(subject.build(note)).to eq('')
end end
end end
context 'on a ProjectSnippet' do context 'when passing a Snippet' do
it 'returns a proper URL' do let(:snippet) { build_stubbed(:personal_snippet) }
project_snippet = create(:project_snippet)
note = build_stubbed(:note_on_project_snippet, noteable: project_snippet)
url = described_class.build(note) it 'returns a raw snippet URL if requested' do
url = subject.build(snippet, raw: true)
expect(url).to eq "#{Settings.gitlab['url']}/#{project_snippet.project.full_path}/snippets/#{note.noteable_id}#note_#{note.id}" expect(url).to eq "#{Gitlab.config.gitlab.url}/snippets/#{snippet.id}/raw"
end end
end end
context 'on a PersonalSnippet' do context 'when passing an unsupported class' do
it 'returns a proper URL' do let(:object) { Object.new }
personal_snippet = create(:personal_snippet)
note = build_stubbed(:note_on_personal_snippet, noteable: personal_snippet)
url = described_class.build(note)
expect(url).to eq "#{Settings.gitlab['url']}/snippets/#{note.noteable_id}#note_#{note.id}" it 'raises an exception' do
expect { subject.build(object) }.to raise_error(NotImplementedError)
end end
end end
context 'on another object' do context 'when passing a batch loaded model' do
it 'returns a proper URL' do let(:project) { build_stubbed(:project) }
project = build_stubbed(:project) let(:object) do
BatchLoader.for(:project).batch do |batch, loader|
expect { described_class.build(project) } batch.each { |_| loader.call(:project, project) }
.to raise_error(NotImplementedError, "No URL builder defined for #{project.inspect}")
end end
end end
end
context 'when passing a WikiPage' do
it 'returns a proper URL' do
wiki_page = build(:wiki_page)
url = described_class.build(wiki_page)
expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" it 'returns the URL for the real object' do
expect(subject.build(object, only_path: true)).to eq("/#{project.full_path}")
end end
end end
end end
......
...@@ -21,8 +21,6 @@ describe PersonalSnippet do ...@@ -21,8 +21,6 @@ describe PersonalSnippet do
let_it_be(:container) { create(:personal_snippet, :repository) } let_it_be(:container) { create(:personal_snippet, :repository) }
let(:stubbed_container) { build_stubbed(:personal_snippet) } let(:stubbed_container) { build_stubbed(:personal_snippet) }
let(:expected_full_path) { "@snippets/#{container.id}" } let(:expected_full_path) { "@snippets/#{container.id}" }
let(:expected_repository_klass) { Repository }
let(:expected_storage_klass) { Storage::Hashed }
let(:expected_web_url_path) { "snippets/#{container.id}" } let(:expected_web_url_path) { "snippets/#{container.id}" }
end end
end end
...@@ -37,8 +37,6 @@ describe ProjectSnippet do ...@@ -37,8 +37,6 @@ describe ProjectSnippet do
let_it_be(:container) { create(:project_snippet, :repository) } let_it_be(:container) { create(:project_snippet, :repository) }
let(:stubbed_container) { build_stubbed(:project_snippet) } let(:stubbed_container) { build_stubbed(:project_snippet) }
let(:expected_full_path) { "#{container.project.full_path}/@snippets/#{container.id}" } let(:expected_full_path) { "#{container.project.full_path}/@snippets/#{container.id}" }
let(:expected_repository_klass) { Repository }
let(:expected_storage_klass) { Storage::Hashed }
let(:expected_web_url_path) { "#{container.project.full_path}/snippets/#{container.id}" } let(:expected_web_url_path) { "#{container.project.full_path}/snippets/#{container.id}" }
end end
end end
...@@ -116,9 +116,6 @@ describe Project do ...@@ -116,9 +116,6 @@ describe Project do
let_it_be(:container) { create(:project, :repository, path: 'somewhere') } let_it_be(:container) { create(:project, :repository, path: 'somewhere') }
let(:stubbed_container) { build_stubbed(:project) } let(:stubbed_container) { build_stubbed(:project) }
let(:expected_full_path) { "#{container.namespace.full_path}/somewhere" } let(:expected_full_path) { "#{container.namespace.full_path}/somewhere" }
let(:expected_repository_klass) { Repository }
let(:expected_storage_klass) { Storage::Hashed }
let(:expected_web_url_path) { "#{container.namespace.full_path}/somewhere" }
end end
it 'has an inverse relationship with merge requests' do it 'has an inverse relationship with merge requests' do
......
...@@ -28,7 +28,7 @@ describe ProjectWiki do ...@@ -28,7 +28,7 @@ describe ProjectWiki do
describe '#web_url' do describe '#web_url' do
it 'returns the full web URL to the wiki' do it 'returns the full web URL to the wiki' do
expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/-/wikis/home") expect(subject.web_url).to eq(Gitlab::UrlBuilder.build(subject))
end end
end end
......
...@@ -1022,24 +1022,6 @@ describe NotificationService, :mailer do ...@@ -1022,24 +1022,6 @@ describe NotificationService, :mailer do
should_not_email(@u_lazy_participant) should_not_email(@u_lazy_participant)
end end
it 'emails new assignee' do
issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_disabled, [@u_mentioned])
expect(issue.assignees.first).to be @u_mentioned
should_email(issue.assignees.first)
should_email(@u_watcher)
should_email(@u_guest_watcher)
should_email(@u_guest_custom)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@u_custom_global)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
end
it 'does not email new assignee if they are the current user' do it 'does not email new assignee if they are the current user' do
issue.assignees = [@u_mentioned] issue.assignees = [@u_mentioned]
notification.reassigned_issue(issue, @u_mentioned, [@u_mentioned]) notification.reassigned_issue(issue, @u_mentioned, [@u_mentioned])
......
...@@ -139,18 +139,80 @@ describe Snippets::UpdateService do ...@@ -139,18 +139,80 @@ describe Snippets::UpdateService do
subject subject
end end
end end
end
it 'returns error when the commit action fails' do shared_examples 'commit operation fails' do
error_message = 'foobar' let_it_be(:gitlab_shell) { Gitlab::Shell.new }
allow_next_instance_of(SnippetRepository) do |instance| before do
allow(instance).to receive(:multi_files_action).and_raise(SnippetRepository::CommitError, error_message) allow(service).to receive(:create_commit).and_raise(SnippetRepository::CommitError)
end end
it 'returns error' do
response = subject response = subject
expect(response).to be_error expect(response).to be_error
expect(response.payload[:snippet].errors[:repository].to_sentence).to eq error_message expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet'
end
context 'when repository is empty' do
before do
allow(service).to receive(:repository_empty?).and_return(true)
end
it 'destroys the created repository in disk' do
subject
expect(gitlab_shell.repository_exists?(snippet.repository.storage, "#{snippet.disk_path}.git")).to be_falsey
end
it 'destroys the SnippetRepository object' do
subject
expect(snippet.reload.snippet_repository).to be_nil
end
it 'expires the repository exists method cache' do
response = subject
expect(response).to be_error
expect(response.payload[:snippet].repository_exists?).to be_falsey
end
end
context 'when repository is not empty' do
before do
allow(service).to receive(:repository_empty?).and_return(false)
end
it 'does not destroy the repository' do
subject
expect(gitlab_shell.repository_exists?(snippet.repository.storage, "#{snippet.disk_path}.git")).to be_truthy
end
it 'does not destroy the snippet repository' do
subject
expect(snippet.reload.snippet_repository).not_to be_nil
end
it 'expires the repository exists method cache' do
response = subject
expect(response).to be_error
expect(response.payload[:snippet].repository_exists?).to be_truthy
end
end
it 'rolls back any snippet modifications' do
option_keys = options.stringify_keys.keys
orig_attrs = snippet.attributes.select { |k, v| k.in?(option_keys) }
subject
current_attrs = snippet.attributes.select { |k, v| k.in?(option_keys) }
expect(orig_attrs).to eq current_attrs
end end
end end
...@@ -186,12 +248,13 @@ describe Snippets::UpdateService do ...@@ -186,12 +248,13 @@ describe Snippets::UpdateService do
response = subject response = subject
expect(response).to be_error expect(response).to be_error
expect(response.payload[:snippet].errors[:repository].to_sentence).to eq error_message expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet'
end end
end end
it 'returns error if snippet does not have a snippet_repository' do it 'returns error if snippet does not have a snippet_repository' do
allow(snippet).to receive(:snippet_repository).and_return(nil) allow(snippet).to receive(:snippet_repository).and_return(nil)
allow(snippet).to receive(:track_snippet_repository).and_return(nil)
expect(subject).to be_error expect(subject).to be_error
end end
...@@ -219,11 +282,13 @@ describe Snippets::UpdateService do ...@@ -219,11 +282,13 @@ describe Snippets::UpdateService do
it_behaves_like 'public visibility level restrictions apply' it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'snippet update data is tracked' it_behaves_like 'snippet update data is tracked'
it_behaves_like 'updates repository content' it_behaves_like 'updates repository content'
it_behaves_like 'commit operation fails'
context 'when snippet does not have a repository' do context 'when snippet does not have a repository' do
let!(:snippet) { create(:project_snippet, author: user, project: project) } let!(:snippet) { create(:project_snippet, author: user, project: project) }
it_behaves_like 'creates repository and creates file' it_behaves_like 'creates repository and creates file'
it_behaves_like 'commit operation fails'
end end
end end
...@@ -235,11 +300,13 @@ describe Snippets::UpdateService do ...@@ -235,11 +300,13 @@ describe Snippets::UpdateService do
it_behaves_like 'public visibility level restrictions apply' it_behaves_like 'public visibility level restrictions apply'
it_behaves_like 'snippet update data is tracked' it_behaves_like 'snippet update data is tracked'
it_behaves_like 'updates repository content' it_behaves_like 'updates repository content'
it_behaves_like 'commit operation fails'
context 'when snippet does not have a repository' do context 'when snippet does not have a repository' do
let!(:snippet) { create(:personal_snippet, author: user, project: project) } let!(:snippet) { create(:personal_snippet, author: user, project: project) }
it_behaves_like 'creates repository and creates file' it_behaves_like 'creates repository and creates file'
it_behaves_like 'commit operation fails'
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'model with repository' do RSpec.shared_examples 'model with repository' do
let(:container) { raise NotImplementedError }
let(:stubbed_container) { raise NotImplementedError }
let(:expected_full_path) { raise NotImplementedError }
let(:expected_web_url_path) { expected_full_path }
describe '#commits_by' do describe '#commits_by' do
let(:commits) { container.repository.commits('HEAD', limit: 3).commits } let(:commits) { container.repository.commits('HEAD', limit: 3).commits }
let(:commit_shas) { commits.map(&:id) } let(:commit_shas) { commits.map(&:id) }
...@@ -46,74 +51,33 @@ RSpec.shared_examples 'model with repository' do ...@@ -46,74 +51,33 @@ RSpec.shared_examples 'model with repository' do
end end
end end
describe '#ssh_url_to_repo' do describe '#url_to_repo' do
it 'returns container ssh address' do it 'returns the SSH URL to the repository' do
expect(container.ssh_url_to_repo).to eq container.url_to_repo expect(container.url_to_repo).to eq("#{Gitlab.config.gitlab_shell.ssh_path_prefix}#{expected_web_url_path}.git")
end
end
describe '#http_url_to_repo' do
subject { container.http_url_to_repo }
context 'when a custom HTTP clone URL root is not set' do
it 'returns the url to the repo without a username' do
expect(subject).to eq("#{container.web_url}.git")
expect(subject).not_to include('@')
end
end
context 'when a custom HTTP clone URL root is set' do
before do
stub_application_setting(custom_http_clone_url_root: custom_http_clone_url_root)
end
context 'when custom HTTP clone URL root has a relative URL root' do
context 'when custom HTTP clone URL root ends with a slash' do
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab/' }
it 'returns the url to the repo, with the root replaced with the custom one' do
expect(subject).to eq("#{custom_http_clone_url_root}#{expected_web_url_path}.git")
end
end
context 'when custom HTTP clone URL root does not end with a slash' do
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/mygitlab' }
it 'returns the url to the repo, with the root replaced with the custom one' do
expect(subject).to eq("#{custom_http_clone_url_root}/#{expected_web_url_path}.git")
end
end end
end end
context 'when custom HTTP clone URL root does not have a relative URL root' do describe '#ssh_url_to_repo' do
context 'when custom HTTP clone URL root ends with a slash' do it 'returns the SSH URL to the repository' do
let(:custom_http_clone_url_root) { 'https://git.example.com:51234/' } expect(container.ssh_url_to_repo).to eq(container.url_to_repo)
it 'returns the url to the repo, with the root replaced with the custom one' do
expect(subject).to eq("#{custom_http_clone_url_root}#{expected_web_url_path}.git")
end end
end end
context 'when custom HTTP clone URL root does not end with a slash' do describe '#http_url_to_repo' do
let(:custom_http_clone_url_root) { 'https://git.example.com:51234' } it 'returns the HTTP URL to the repository' do
expect(container.http_url_to_repo).to eq("#{Gitlab.config.gitlab.url}/#{expected_web_url_path}.git")
it 'returns the url to the repo, with the root replaced with the custom one' do
expect(subject).to eq("#{custom_http_clone_url_root}/#{expected_web_url_path}.git")
end
end
end
end end
end end
describe '#repository' do describe '#repository' do
it 'returns valid repo' do it 'returns valid repo' do
expect(container.repository).to be_kind_of(expected_repository_klass) expect(container.repository).to be_kind_of(Repository)
end end
end end
describe '#storage' do describe '#storage' do
it 'returns valid storage' do it 'returns valid storage' do
expect(container.storage).to be_kind_of(expected_storage_klass) expect(container.storage).to be_kind_of(Storage::Hashed)
end end
end end
......
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