BigW Consortium Gitlab

Commit 9f57d082 by Mark Fletcher

Merge branch '10-4-stable-with-rc5-and-rc6' into '10-4-stable'

10-4-stable with rc5 and rc6 from dev See merge request gitlab-org/gitlab-ce!16508
parents 904b1cda 3cf7681a
app/assets/images/multi-editor-on.png

5.34 KB | W: | H:

app/assets/images/multi-editor-on.png

3.88 KB | W: | H:

app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
app/assets/images/multi-editor-on.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -30,8 +30,12 @@
shouldRenderContent() {
return !this.isLoading && Object.keys(this.job).length;
},
/**
* When job has not started the key will be `false`
* When job started the key will be a string with a date.
*/
jobStarted() {
return this.job.started;
return !this.job.started === false;
},
},
methods: {
......
......@@ -19,11 +19,8 @@ document.addEventListener('DOMContentLoaded', () => {
return;
}
$(navEl).on('show.bs.dropdown', (e) => {
const dropdownEl = $(e.currentTarget).find('.projects-dropdown-menu');
dropdownEl.one('transitionend', () => {
eventHub.$emit('dropdownOpen');
});
$(navEl).on('shown.bs.dropdown', () => {
eventHub.$emit('dropdownOpen');
});
// eslint-disable-next-line no-new
......
......@@ -651,12 +651,18 @@
min-width: 0;
}
.diff-changed-file-name {
.diff-changed-file-name,
.diff-changed-blank-file-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.diff-changed-blank-file-name {
color: $gl-text-color-tertiary;
font-style: italic;
}
.diff-changed-file-path {
color: $gl-text-color-tertiary;
}
......
......@@ -303,7 +303,6 @@
.gutter-toggle {
margin-top: 7px;
border-left: 1px solid $border-gray-normal;
padding-left: 0;
text-align: center;
}
......
......@@ -29,7 +29,7 @@ class Projects::JobsController < Projects::ApplicationController
:project,
:tags
])
@builds = @builds.page(params[:page]).per(30)
@builds = @builds.page(params[:page]).per(30).without_count
end
def cancel_all
......
......@@ -46,7 +46,7 @@ module BlobHelper
end
def ide_edit_text
"#{_('Multi Edit')} <span class='label label-primary'>#{_('Beta')}</span>".html_safe
"#{_('Web IDE')}"
end
def ide_blob_link(project = @project, ref = @ref, path = @path, options = {})
......
......@@ -103,6 +103,10 @@ class Repository
"#<#{self.class.name}:#{@disk_path}>"
end
def create_hooks
Gitlab::Git::Repository.create_hooks(path_to_repo, Gitlab.config.gitlab_shell.hooks_path)
end
def commit(ref = 'HEAD')
return nil unless exists?
return ref if ref.is_a?(::Commit)
......
......@@ -154,13 +154,9 @@ module MergeRequests
end
def assign_title_from_issue
return unless issue
return unless issue && issue.is_a?(Issue)
merge_request.title =
case issue
when Issue then "Resolve \"#{issue.title}\""
when ExternalIssue then "Resolve #{issue.title}"
end
merge_request.title = "Resolve \"#{issue.title}\""
end
def issue_iid
......
......@@ -56,8 +56,6 @@
= link_to "Profile", current_user, class: 'profile-link', data: { user: current_user.username }
%li
= link_to "Settings", profile_path
%li
= link_to "Turn on multi edit", profile_preferences_path
- if current_user
%li
= link_to "Help", help_path
......
......@@ -5,8 +5,8 @@
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-4
%h4.prepend-top-0
GitLab multi file editor
%p Unlock an additional editing experience which makes it possible to edit and commit multiple files
Web IDE (Beta)
%p Enable the new web IDE on this device to make it possible to open and edit multiple files with a single commit
.col-lg-8.multi-file-editor-options
= label_tag do
.preview.append-bottom-10= image_tag "multi-editor-off.png"
......
......@@ -11,7 +11,7 @@
- unless diff_file.submodule?
- blob = diff_file.blob
.file-actions.hidden-xs
- if blob.readable_text?
- if blob&.readable_text?
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
= icon('comment')
\
......
......@@ -35,3 +35,6 @@
- if diff_file.mode_changed?
%small
#{diff_file.a_mode}#{diff_file.b_mode}
- if diff_file.stored_externally? && diff_file.external_storage == :lfs
%span.label.label-lfs.append-right-5 LFS
......@@ -24,7 +24,12 @@
%a.diff-changed-file{ href: "##{hexdigest(diff_file.file_path)}", title: diff_file.new_path }
= sprite_icon(diff_file_changed_icon(diff_file), size: 16, css_class: "#{diff_file_changed_icon_color(diff_file)} diff-file-changed-icon append-right-8")
%span.diff-changed-file-content.append-right-8
%strong.diff-changed-file-name= diff_file.blob.name
- if diff_file.blob&.name
%strong.diff-changed-file-name
= diff_file.blob.name
- else
%strong.diff-changed-blank-file-name
= s_('Diffs|No file name available')
%span.diff-changed-file-path.prepend-top-5= diff_file_path_text(diff_file)
%span.diff-changed-stats
%span.cgreen<
......
- illustration = local_assigns.fetch(:illustration)
- illustration_size = local_assigns.fetch(:illustration_size)
- title = local_assigns.fetch(:title)
- content = local_assigns.fetch(:content)
- content = local_assigns.fetch(:content, nil)
- action = local_assigns.fetch(:action, nil)
.row.empty-state
......@@ -11,7 +11,8 @@
.col-xs-12
.text-content
%h4.text-center= title
%p= content
- if content
%p= content
- if action
.text-center
= action
......@@ -22,4 +22,4 @@
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, admin: admin }
= paginate builds, theme: 'gitlab'
= paginate_collection(builds)
......@@ -93,14 +93,13 @@
illustration: 'illustrations/manual_action.svg',
illustration_size: 'svg-394',
title: _('This job requires a manual action'),
content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.'),
action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), class: 'btn btn-primary', title: _('Trigger this manual action') )
content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'),
action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') )
- else
= render 'empty_state',
illustration: 'illustrations/job_not_triggered.svg',
illustration_size: 'svg-306',
title: _('This job has not been triggered yet'),
content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered.')
title: _('This job has not been triggered yet')
= render "sidebar"
......
---
title: Default merge request title is set correctly again when external issue tracker is activated
merge_request: 16356
author: Ben305
type: fixed
---
title: Fix 500 error when visiting a commit where the blobs do not exist
merge_request:
author:
type: fixed
---
title: Fix web ide user preferences copy and buttons
merge_request: 41789
author:
type: other
---
title: Ensure that emails contain absolute, rather than relative, links to user uploads
merge_request: 16364
author:
type: fixed
---
title: "[API] Fix creating issue when assignee_id is empty"
merge_request:
author:
type: fixed
---
title: Use simple Next/Prev paging for jobs to avoid large count queries on arbitrarily
large sets of historical jobs
merge_request:
author:
type: performance
---
title: Fixing rack request mime type when using rack attack
merge_request: 16427
author:
type: fixed
---
title: Fix hooks not being set up properly for bare import Rake task
merge_request:
author:
type: fixed
......@@ -16,7 +16,8 @@ codequality:
- docker:dind
script:
- docker pull codeclimate/codeclimate
- docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze -f json > codeclimate.json || true
- docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 init
- docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc codeclimate/codeclimate:0.69.0 analyze -f json > codeclimate.json || true
artifacts:
paths: [codeclimate.json]
```
......
......@@ -64,7 +64,7 @@ up-to-date and install it.
Install the required packages (needed to compile Ruby and native extensions to Ruby gems):
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate rsync python-docutils pkg-config cmake
Ubuntu 14.04 (Trusty Tahr) doesn't have the `libre2-dev` package available, but
you can [install re2 manually](https://github.com/google/re2/wiki/Install).
......
......@@ -5,8 +5,8 @@
An application data backup creates an archive file that contains the database,
all repositories and all attachments.
You can only restore a backup to **exactly the same version and type (CE/EE)**
of GitLab on which it was created. The best way to migrate your repositories
You can only restore a backup to **exactly the same version and type (CE/EE)**
of GitLab on which it was created. The best way to migrate your repositories
from one server to another is through backup restore.
## Backup
......@@ -14,6 +14,19 @@ from one server to another is through backup restore.
GitLab provides a simple command line interface to backup your whole installation,
and is flexible enough to fit your needs.
### Requirements
If you're using GitLab with the Omnibus package, you're all set. If you
installed GitLab from source, make sure the following packages are installed:
* rsync
If you're using Ubuntu, you could run:
```
sudo apt-get install -y rsync
```
### Backup timestamp
>**Note:**
......@@ -431,7 +444,7 @@ The [restore prerequisites section](#restore-prerequisites) includes crucial
information. Make sure to read and test the whole restore process at least once
before attempting to perform it in a production environment.
You can only restore a backup to **exactly the same version and type (CE/EE)** of
You can only restore a backup to **exactly the same version and type (CE/EE)** of
GitLab that you created it on, for example CE 9.1.0.
### Restore prerequisites
......@@ -511,7 +524,7 @@ sudo service gitlab restart
This procedure assumes that:
- You have installed the **exact same version and type (CE/EE)** of GitLab
- You have installed the **exact same version and type (CE/EE)** of GitLab
Omnibus with which the backup was created.
- You have run `sudo gitlab-ctl reconfigure` at least once.
- GitLab is running. If not, start it using `sudo gitlab-ctl start`.
......
......@@ -21,6 +21,8 @@ sudo service gitlab stop
### 2. Backup
NOTE: If you installed GitLab from source, make sure `rsync` is installed.
```bash
cd /home/git/gitlab
......
......@@ -2,9 +2,6 @@
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35954) in 10.1.
CAUTION: **Warning:**
The Cluster integration is currently in **Beta**.
With a cluster associated to your project, you can use Review Apps, deploy your
applications, run your pipelines, and much more, in an easy way.
......
......@@ -3,8 +3,10 @@ module API
module CommonHelpers
def convert_parameters_from_legacy_format(params)
params.tap do |params|
if params[:assignee_id].present?
params[:assignee_ids] = [params.delete(:assignee_id)]
assignee_id = params.delete(:assignee_id)
if assignee_id.present?
params[:assignee_ids] = [assignee_id]
end
end
end
......
......@@ -50,15 +50,22 @@ module Banzai
end
def process_link_to_upload_attr(html_attr)
uri_parts = [html_attr.value]
path_parts = [html_attr.value]
if group
uri_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
elsif project
uri_parts.unshift(relative_url_root, project.full_path)
path_parts.unshift(relative_url_root, project.full_path)
end
html_attr.value = File.join(*uri_parts)
path = File.join(*path_parts)
html_attr.value =
if context[:only_path]
path
else
URI.join(Gitlab.config.gitlab.base_url, path).to_s
end
end
def process_link_to_repository_attr(html_attr)
......
......@@ -96,9 +96,7 @@ module Gitlab
end
def ensure_action_dispatch_request(request)
return request if request.is_a?(ActionDispatch::Request)
ActionDispatch::Request.new(request.env)
ActionDispatch::Request.new(request.env.dup)
end
def current_request
......
......@@ -63,6 +63,7 @@ module Gitlab
log " * Created #{project.name} (#{project_full_path})".color(:green)
project.write_repository_config
project.repository.create_hooks
ProjectCacheWorker.perform_async(project.id)
else
......
require 'spec_helper'
describe 'User broweses commits' do
describe 'User browses commits' do
let(:user) { create(:user) }
let(:project) { create(:project, :repository, namespace: user.namespace) }
......@@ -31,6 +31,19 @@ describe 'User broweses commits' do
check_author_link(RepoHelpers.sample_commit.author_email, user)
end
end
context 'when the blob does not exist' do
let(:commit) { create(:commit, project: project) }
it 'shows a blank label' do
allow_any_instance_of(Gitlab::Diff::File).to receive(:blob).and_return(nil)
allow_any_instance_of(Gitlab::Diff::File).to receive(:raw_binary?).and_return(true)
visit(project_commit_path(project, commit))
expect(find('.diff-file-changes', visible: false)).to have_content('No file name available')
end
end
end
private
......
......@@ -380,9 +380,18 @@ feature 'Jobs' do
it 'shows manual action empty state' do
expect(page).to have_content('This job requires a manual action')
expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments.')
expect(page).to have_content('This job depends on a user to trigger its process. Often they are used to deploy code to production environments')
expect(page).to have_link('Trigger this manual action')
end
it 'plays manual action', :js do
click_link 'Trigger this manual action'
wait_for_requests
expect(page).to have_content('This job has not been triggered')
expect(page).to have_content('This job is stuck, because the project doesn\'t have any runners online assigned to it.')
expect(page).to have_content('pending')
end
end
context 'Non triggered job' do
......@@ -392,9 +401,8 @@ feature 'Jobs' do
visit project_job_path(project, job)
end
it 'shows manual action empty state' do
it 'shows empty state' do
expect(page).to have_content('This job has not been triggered yet')
expect(page).to have_content('This job depends on upstream jobs that need to succeed in order for this job to be triggered.')
end
end
end
......
......@@ -14,7 +14,7 @@ feature 'Multi-file editor new directory', :js do
wait_for_requests
click_link('Multi Edit')
click_link('Web IDE')
wait_for_requests
end
......
......@@ -14,7 +14,7 @@ feature 'Multi-file editor new file', :js do
wait_for_requests
click_link('Multi Edit')
click_link('Web IDE')
wait_for_requests
end
......
......@@ -16,7 +16,7 @@ feature 'Multi-file editor upload file', :js do
wait_for_requests
click_link('Multi Edit')
click_link('Web IDE')
wait_for_requests
end
......
......@@ -31,6 +31,7 @@ describe('Job details header', () => {
email: 'foo@bar.com',
avatar_url: 'link',
},
started: '2018-01-08T09:48:27.319Z',
new_issue_path: 'path',
},
isLoading: false,
......@@ -43,15 +44,32 @@ describe('Job details header', () => {
vm.$destroy();
});
it('should render provided job information', () => {
expect(
vm.$el.querySelector('.header-main-content').textContent.replace(/\s+/g, ' ').trim(),
).toEqual('failed Job #123 triggered 3 weeks ago by Foo');
describe('triggered job', () => {
beforeEach(() => {
vm = mountComponent(HeaderComponent, props);
});
it('should render provided job information', () => {
expect(
vm.$el.querySelector('.header-main-content').textContent.replace(/\s+/g, ' ').trim(),
).toEqual('failed Job #123 triggered 3 weeks ago by Foo');
});
it('should render new issue link', () => {
expect(
vm.$el.querySelector('.js-new-issue').getAttribute('href'),
).toEqual(props.job.new_issue_path);
});
});
it('should render new issue link', () => {
expect(
vm.$el.querySelector('.js-new-issue').getAttribute('href'),
).toEqual(props.job.new_issue_path);
describe('created job', () => {
it('should render created key', () => {
props.job.started = false;
vm = mountComponent(HeaderComponent, props);
expect(
vm.$el.querySelector('.header-main-content').textContent.replace(/\s+/g, ' ').trim(),
).toEqual('failed Job #123 created 3 weeks ago by Foo');
});
});
});
......@@ -8,7 +8,8 @@ describe Banzai::Filter::RelativeLinkFilter do
group: group,
project_wiki: project_wiki,
ref: ref,
requested_path: requested_path
requested_path: requested_path,
only_path: only_path
})
described_class.call(doc, contexts)
......@@ -37,6 +38,7 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:commit) { project.commit(ref) }
let(:project_wiki) { nil }
let(:requested_path) { '/' }
let(:only_path) { true }
shared_examples :preserve_unchanged do
it 'does not modify any relative URL in anchor' do
......@@ -240,26 +242,35 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:commit) { nil }
let(:ref) { nil }
let(:requested_path) { nil }
let(:upload_path) { '/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg' }
let(:relative_path) { "/#{project.full_path}#{upload_path}" }
context 'to a project upload' do
context 'with an absolute URL' do
let(:absolute_path) { Gitlab.config.gitlab.url + relative_path }
let(:only_path) { false }
it 'rewrites the link correctly' do
doc = filter(link(upload_path))
expect(doc.at_css('a')['href']).to eq(absolute_path)
end
end
it 'rebuilds relative URL for a link' do
doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('a')['href'])
.to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(link(upload_path))
expect(doc.at_css('a')['href']).to eq(relative_path)
doc = filter(nested(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')))
expect(doc.at_css('a')['href'])
.to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested(link(upload_path)))
expect(doc.at_css('a')['href']).to eq(relative_path)
end
it 'rebuilds relative URL for an image' do
doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
expect(doc.at_css('img')['src'])
.to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(image(upload_path))
expect(doc.at_css('img')['src']).to eq(relative_path)
doc = filter(nested(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg')))
expect(doc.at_css('img')['src'])
.to eq "/#{project.full_path}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
doc = filter(nested(image(upload_path)))
expect(doc.at_css('img')['src']).to eq(relative_path)
end
it 'does not modify absolute URL' do
......@@ -288,6 +299,17 @@ describe Banzai::Filter::RelativeLinkFilter do
let(:project) { nil }
let(:relative_path) { "/groups/#{group.full_path}/-/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg" }
context 'with an absolute URL' do
let(:absolute_path) { Gitlab.config.gitlab.url + relative_path }
let(:only_path) { false }
it 'rewrites the link correctly' do
doc = filter(upload_link)
expect(doc.at_css('a')['href']).to eq(absolute_path)
end
end
it 'rewrites the link correctly' do
doc = filter(upload_link)
......
......@@ -76,6 +76,16 @@ describe Gitlab::Auth::UserAuthFinders do
expect(find_user_from_rss_token).to be_nil
end
end
context 'when the request format is empty' do
it 'the method call does not modify the original value' do
env['action_dispatch.request.formats'] = nil
find_user_from_rss_token
expect(env['action_dispatch.request.formats']).to be_nil
end
end
end
describe '#find_user_from_access_token' do
......
......@@ -74,14 +74,18 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
importer.create_project_if_needed
end
it 'creates the Git repo in disk' do
it 'creates the Git repo on disk with the proper symlink for hooks' do
create_bare_repository("#{project_path}.git")
importer.create_project_if_needed
project = Project.find_by_full_path(project_path)
repo_path = File.join(project.repository_storage_path, project.disk_path + '.git')
hook_path = File.join(repo_path, 'hooks')
expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.git'))
expect(File).to exist(repo_path)
expect(File.symlink?(hook_path)).to be true
expect(File.readlink(hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
context 'hashed storage enabled' do
......
......@@ -412,6 +412,28 @@ describe Repository do
end
end
describe '#create_hooks' do
let(:hook_path) { File.join(repository.path_to_repo, 'hooks') }
it 'symlinks the global hooks directory' do
repository.create_hooks
expect(File.symlink?(hook_path)).to be true
expect(File.readlink(hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
it 'replaces existing symlink with the right directory' do
FileUtils.mkdir_p(hook_path)
expect(File.symlink?(hook_path)).to be false
repository.create_hooks
expect(File.symlink?(hook_path)).to be true
expect(File.readlink(hook_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
end
end
describe "#create_dir" do
it "commits a change that creates a new directory" do
expect do
......
......@@ -847,6 +847,15 @@ describe API::Issues, :mailer do
expect(json_response['assignee']['name']).to eq(user2.name)
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
it 'creates a new project issue when assignee_id is empty' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', assignee_id: ''
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('new issue')
expect(json_response['assignee']).to be_nil
end
end
context 'single assignee restrictions' do
......
......@@ -171,6 +171,24 @@ describe MergeRequests::BuildService do
end
end
end
context 'branch starts with external issue IID followed by a hyphen' do
let(:source_branch) { '12345-fix-issue' }
before do
allow(project).to receive(:external_issue_tracker).and_return(true)
end
it 'uses the title of the commit as the title of the merge request' do
expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first)
end
it 'uses the description of the commit as the description of the merge request and appends the closes text' do
commit_description = commit_1.safe_message.split(/\n+/, 2).last
expect(merge_request.description).to eq("#{commit_description}\n\nCloses #12345")
end
end
end
context 'more than one commit in the diff' do
......@@ -241,8 +259,12 @@ describe MergeRequests::BuildService do
allow(project).to receive(:external_issue_tracker).and_return(true)
end
it 'sets the title to: Resolves External Issue $issue-iid' do
expect(merge_request.title).to eq('Resolve External Issue 12345')
it 'sets the title to the humanized branch title' do
expect(merge_request.title).to eq('12345 fix issue')
end
it 'appends the closes text' do
expect(merge_request.description).to eq('Closes #12345')
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