Commit dd58b194 authored by Luke Bennett's avatar Luke Bennett

Merge branch 'ce-to-ee-2018-01-16' into 'master'

CE upstream - Tuesday

Closes gitaly#915

See merge request gitlab-org/gitlab-ee!4103
parents d4b0b39e 926d8457
......@@ -11,4 +11,6 @@ See the guidelines: http://docs.gitlab.com/ce/development/doc_styleguide.html#ch
- [ ] Make sure the old link is not removed and has its contents replaced with a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken.
- [ ] Search and replace any links referring to old docs in GitLab Rails app, specifically under the `app/views/` directory.
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/doc_styleguide.html#redirections-for-pages-with-disqus-comments) to the new document if there are any Disqus comments on the old document thread.
- [ ] If working on CE, submit an MR to EE with the changes as well.
- [ ] Ping one of the technical writers for review.
......@@ -110,7 +110,7 @@ MergeRequest.prototype.initCommitMessageListeners = function() {
});
};
MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, newStatusText) {
MergeRequest.updateStatusText = function(classToRemove, classToAdd, newStatusText) {
$('.detail-page-header .status-box')
.removeClass(classToRemove)
.addClass(classToAdd)
......@@ -118,14 +118,14 @@ MergeRequest.prototype.updateStatusText = function(classToRemove, classToAdd, ne
.text(newStatusText);
};
MergeRequest.prototype.decreaseCounter = function(by = 1) {
const $el = $('.nav-links .js-merge-counter');
MergeRequest.decreaseCounter = function(by = 1) {
const $el = $('.js-merge-counter');
const count = Math.max((parseInt($el.text().replace(/[^\d]/, ''), 10) - by), 0);
$el.text(addDelimiter(count));
};
MergeRequest.prototype.hideCloseButton = function() {
MergeRequest.hideCloseButton = function() {
const el = document.querySelector('.merge-request .js-issuable-actions');
const closeDropdownItem = el.querySelector('li.close-item');
if (closeDropdownItem) {
......
import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon';
import eventHub from '../../event_hub';
......@@ -169,11 +170,9 @@ export default {
// If state is merged we should update the widget and stop the polling
eventHub.$emit('MRWidgetUpdateRequested');
eventHub.$emit('FetchActionsContent');
if (window.mergeRequest) {
window.mergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged');
window.mergeRequest.hideCloseButton();
window.mergeRequest.decreaseCounter();
}
MergeRequest.updateStatusText('status-box-open', 'status-box-merged', 'Merged');
MergeRequest.hideCloseButton();
MergeRequest.decreaseCounter();
stopPolling();
// If user checked remove source branch and we didn't remove the branch yet
......
......@@ -45,11 +45,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
end
def diffs
@diffs = if @merge_request.can_be_created
@merge_request.diffs(diff_options)
else
[]
end
@diffs = @merge_request.diffs(diff_options) if @merge_request.can_be_created
@diff_notes_disabled = true
......
......@@ -372,19 +372,19 @@ class Commit
# uri_type('doc/README.md') # => :blob
# uri_type('doc/logo.png') # => :raw
# uri_type('doc/api') # => :tree
# uri_type('not/found') # => :nil
# uri_type('not/found') # => nil
#
# Returns a symbol
def uri_type(path)
entry = @raw.rugged_tree_entry(path)
entry = @raw.tree_entry(path)
return unless entry
if entry[:type] == :blob
blob = ::Blob.decorate(Gitlab::Git::Blob.new(name: entry[:name]), @project)
blob.image? || blob.video? ? :raw : :blob
else
entry[:type]
end
rescue Rugged::TreeError
nil
end
def raw_diffs(*args)
......
......@@ -987,9 +987,11 @@ class Repository
end
def search_files_by_name(query, ref)
return [] if empty? || query.blank?
safe_query = Regexp.escape(query.sub(/^\/*/, ""))
return [] if empty? || safe_query.blank?
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)})
args = %W(ls-tree --full-tree -r #{ref || root_ref} --name-status | #{safe_query})
run_git(args).first.lines.map(&:strip)
end
......
......@@ -68,8 +68,6 @@ module SystemNoteService
#
# Returns the created Note object
def change_issue_assignees(issue, project, author, old_assignees)
body =
if issue.assignees.any? && old_assignees.any?
unassigned_users = old_assignees - issue.assignees
added_users = issue.assignees.to_a - old_assignees
......@@ -77,12 +75,7 @@ module SystemNoteService
text_parts << "assigned to #{added_users.map(&:to_reference).to_sentence}" if added_users.any?
text_parts << "unassigned #{unassigned_users.map(&:to_reference).to_sentence}" if unassigned_users.any?
text_parts.join(' and ')
elsif old_assignees.any?
"removed assignee"
elsif issue.assignees.any?
"assigned to #{issue.assignees.map(&:to_reference).to_sentence}"
end
body = text_parts.join(' and ')
create_note(NoteSummary.new(issue, project, author, body, action: 'assignee'))
end
......
= render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false
- if @merge_request.can_be_created
= render "projects/diffs/diffs", diffs: @diffs, environment: @environment, show_whitespace_toggle: false
- else
.nothing-here-block
This merge request cannot be created.
......@@ -23,6 +23,3 @@
%h4.underlined-title Available shared Runners : #{@shared_runners_count}
%ul.bordered-list.available-shared-runners
= render partial: 'projects/runners/runner', collection: @shared_runners, as: :runner
- if @shared_runners_count > 10
.light
and #{@shared_runners_count - 10} more...
---
title: "Update 'removed assignee' note to include old assignee reference"
merge_request: 16301
author: Maurizio De Santis
type: changed
---
title: Ignore leading slashes when searching for files within context of repository.
merge_request:
author: Andrew McCallum
type: fixed
---
title: Enables Project Milestone Deletion via the API
merge_request: 16478
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Update marked from 0.3.6 to 0.3.12
merge_request: 16480
author: Takuya Noguchi
type: security
---
title: Fix error on changes tab when merge request cannot be created
merge_request:
author:
type: fixed
---
title: Fixed merge request status badge not updating after merging
merge_request:
author:
type: fixed
---
title: Remove erroneous text in shared runners page that suggested more runners available
merge_request:
author:
type: fixed
......@@ -93,6 +93,19 @@ Parameters:
- `start_date` (optional) - The start date of the milestone
- `state_event` (optional) - The state event of the milestone (close|activate)
## Delete project milestone
Only for user with developer access to the project.
```
DELETE /projects/:id/milestones/:milestone_id
```
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of the project's milestone
## Get all issues assigned to a single milestone
Gets all issues assigned to a single project milestone.
......
......@@ -34,7 +34,6 @@ The table below shows what kind of documentation goes where.
| `doc/install/`| Probably the most visited directory, since `installation.md` is there. Ideally this should go under `doc/administration/`, but it's best to leave it as-is in order to avoid confusion (still debated though). |
| `doc/update/` | Same with `doc/install/`. Should be under `administration/`, but this is a well known location, better leave as-is, at least for now. |
| `doc/topics/` | Indexes per Topic (`doc/topics/topic-name/index.md`): all resources for that topic (user and admin documentation, articles, and third-party docs) |
| `doc/articles/` | [Technical Articles](writing_documentation.md#technical-articles): user guides, admin guides, technical overviews, tutorials (`doc/articles/article-title/index.md`). |
---
......@@ -67,11 +66,10 @@ The table below shows what kind of documentation goes where.
1. The `doc/topics/` directory holds topic-related technical content. Create
`doc/topics/topic-name/subtopic-name/index.md` when subtopics become necessary.
General user- and admin- related documentation, should be placed accordingly.
1. For technical articles, place their images under `doc/articles/article-title/img/`.
---
If you are unsure where a document should live, you can ping `@axil` in your
If you are unsure where a document should live, you can ping `@axil` or `@marcia` in your
merge request.
## Text
......@@ -108,8 +106,8 @@ merge request.
- Avoid adding things that show ephemeral statuses. For example, if a feature is
considered beta or experimental, put this info in a note, not in the heading.
- When introducing a new document, be careful for the headings to be
grammatically and syntactically correct. It is advised to mention one or all
of the following GitLab members for a review: `@axil`, `@rspeicher`, `@marcia`.
grammatically and syntactically correct. Mention one or all
of the following GitLab members for a review: `@axil` or `@marcia`.
This is to ensure that no document with wrong heading is going
live without an audit, thus preventing dead links and redirection issues when
corrected
......@@ -330,6 +328,10 @@ For example, if you were to move `doc/workflow/lfs/lfs_administration.md` to
git grep -n "lfs/lfs_administration"
```
NOTE: **Note:**
If the document being moved has any Disqus comments on it, there are extra steps
to follow documented just [below](#redirections-for-pages-with-disqus-comments).
Things to note:
- Since we also use inline documentation, except for the documentation itself,
......@@ -342,6 +344,32 @@ Things to note:
documentation, sometimes it might be useful to search a path deeper.
- The `*.md` extension is not used when a document is linked to GitLab's
built-in help page, that's why we omit it in `git grep`.
- Use the checklist on the documentation MR description template.
### Redirections for pages with Disqus comments
If the documentation page being relocated already has any Disqus comments,
we need to preserve the Disqus thread.
Disqus uses an identifier per page, and for docs.gitlab.com, the page identifier
is configured to be the page URL. Therefore, when we change the document location,
we need to preserve the old URL as the same Disqus identifier.
To do that, add to the frontmatter the variable `redirect_from`,
using the old URL as value. For example, let's say I moved the document
available under `https://docs.gitlab.com/my-old-location/README.html` to a new location,
`https://docs.gitlab.com/my-new-location/index.html`.
Into the **new document** frontmatter add the following:
```yaml
---
redirect_from: 'https://docs.gitlab.com/my-old-location/README.html'
---
```
Note: it is necessary to include the file name in the `redirect_from` URL,
even if it's `index.html` or `README.html`.
## Configuration documentation for source and Omnibus installations
......
......@@ -25,6 +25,26 @@ them to review it for you.
We use the [monthly release blog post](https://about.gitlab.com/handbook/marketing/blog/release-posts/#monthly-releases) as a changelog checklist to ensure everything
is documented.
Whenever you submit a merge request for the documentation, use the documentation MR description template.
### Documentation directory structure
The documentation is structured based on the GitLab UI structure itself,
separated by [`user`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/user),
[`administrator`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/administration), and [`contributor`](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/development).
To learn where to place a new document, check the [documentation style guide](doc_styleguide.md#location-and-naming-of-documents).
In order to have a [solid site structure](https://searchengineland.com/seo-benefits-developing-solid-site-structure-277456) for our documentation,
all docs should be linked. Every new document should be cross-linked to its related documentation, and linked from its topic-related index, when existent.
The directories `/workflow/`, `/gitlab-basics/`, `/university/`, and `/articles/` have
been deprecated and the majority their docs have been moved to their correct location
in small iterations. Please don't create new docs in these folders.
To move a document from its location to another directory, read the section
[changing document location](doc_styleguide.md#changing-document-location) of the doc style guide.
### Feature overview and use cases
Every major feature (regardless if present in GitLab Community or Enterprise editions)
......
......@@ -60,6 +60,15 @@ module API
update_milestone_for(user_project)
end
desc 'Remove a project milestone'
delete ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
user_project.milestones.find(params[:milestone_id]).destroy
status(204)
end
desc 'Get all issues for a single project milestone' do
success Entities::IssueBasic
end
......
......@@ -436,6 +436,16 @@ module Gitlab
parent_ids.size > 1
end
def tree_entry(path)
@repository.gitaly_migrate(:commit_tree_entry) do |is_migrated|
if is_migrated
gitaly_tree_entry(path)
else
rugged_tree_entry(path)
end
end
end
def to_gitaly_commit
return raw_commit if raw_commit.is_a?(Gitaly::GitCommit)
......@@ -450,11 +460,6 @@ module Gitlab
)
end
# Is this the same as Blob.find_entry_by_path ?
def rugged_tree_entry(path)
rugged_commit.tree.path(path)
end
private
def init_from_hash(hash)
......@@ -501,6 +506,28 @@ module Gitlab
SERIALIZE_KEYS
end
def gitaly_tree_entry(path)
# We're only interested in metadata, so limit actual data to 1 byte
# since Gitaly doesn't support "send no data" option.
entry = @repository.gitaly_commit_client.tree_entry(id, path, 1)
return unless entry
# To be compatible with the rugged format
entry = entry.to_h
entry.delete(:data)
entry[:name] = File.basename(path)
entry[:type] = entry[:type].downcase
entry
end
# Is this the same as Blob.find_entry_by_path ?
def rugged_tree_entry(path)
rugged_commit.tree.path(path)
rescue Rugged::TreeError
nil
end
def gitaly_commit_author_from_rugged(author_or_committer)
Gitaly::CommitAuthor.new(
name: author_or_committer[:name].b,
......
......@@ -4,6 +4,16 @@ describe Projects::MergeRequests::CreationsController do
let(:project) { create(:project, :repository) }
let(:user) { project.owner }
let(:fork_project) { create(:forked_project_with_submodules) }
let(:get_diff_params) do
{
namespace_id: fork_project.namespace.to_param,
project_id: fork_project,
merge_request: {
source_branch: 'remove-submodule',
target_branch: 'master'
}
}
end
before do
fork_project.add_master(user)
......@@ -13,22 +23,27 @@ describe Projects::MergeRequests::CreationsController do
describe 'GET new' do
context 'merge request that removes a submodule' do
render_views
it 'renders new merge request widget template' do
get :new,
namespace_id: fork_project.namespace.to_param,
project_id: fork_project,
merge_request: {
source_branch: 'remove-submodule',
target_branch: 'master'
}
get :new, get_diff_params
expect(response).to be_success
end
end
end
describe 'GET diffs' do
context 'when merge request cannot be created' do
it 'does not assign diffs var' do
allow_any_instance_of(MergeRequest).to receive(:can_be_created).and_return(false)
get :diffs, get_diff_params.merge(format: 'json')
expect(response).to be_success
expect(assigns[:diffs]).to be_nil
end
end
end
describe 'GET pipelines' do
before do
create(:ci_pipeline, sha: fork_project.commit('remove-submodule').id,
......@@ -37,14 +52,7 @@ describe Projects::MergeRequests::CreationsController do
end
it 'renders JSON including serialized pipelines' do
get :pipelines,
namespace_id: fork_project.namespace.to_param,
project_id: fork_project,
merge_request: {
source_branch: 'remove-submodule',
target_branch: 'master'
},
format: :json
get :pipelines, get_diff_params.merge(format: 'json')
expect(response).to be_ok
expect(json_response).to have_key 'pipelines'
......
......@@ -70,8 +70,8 @@ import IssuablesHelper from '~/helpers/issuables_helper';
beforeEach(() => {
loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
this.el = document.querySelector('.js-issuable-actions');
const merge = new MergeRequest();
merge.hideCloseButton();
new MergeRequest(); // eslint-disable-line no-new
MergeRequest.hideCloseButton();
});
it('hides the dropdown close item and selects the next item', () => {
......@@ -90,8 +90,7 @@ import IssuablesHelper from '~/helpers/issuables_helper';
beforeEach(() => {
loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
this.el = document.querySelector('.js-issuable-actions');
const merge = new MergeRequest();
merge.hideCloseButton();
MergeRequest.hideCloseButton();
});
it('hides the close button', () => {
......
......@@ -371,6 +371,10 @@ describe('MRWidgetReadyToMerge', () => {
});
});
beforeEach(() => {
loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
});
it('should call start and stop polling when MR merged', (done) => {
spyOn(eventHub, '$emit');
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
......@@ -392,6 +396,47 @@ describe('MRWidgetReadyToMerge', () => {
}, 333);
});
it('updates status box', (done) => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
vm.handleMergePolling(() => {}, () => {});
setTimeout(() => {
const statusBox = document.querySelector('.status-box');
expect(statusBox.classList.contains('status-box-merged')).toBeTruthy();
expect(statusBox.textContent).toContain('Merged');
done();
});
});
it('hides close button', (done) => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
vm.handleMergePolling(() => {}, () => {});
setTimeout(() => {
expect(document.querySelector('.btn-close').classList.contains('hidden')).toBeTruthy();
done();
});
});
it('updates merge request count badge', (done) => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('merged'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
vm.handleMergePolling(() => {}, () => {});
setTimeout(() => {
expect(document.querySelector('.js-merge-counter').textContent).toBe('0');
done();
});
});
it('should continue polling until MR is merged', (done) => {
spyOn(vm.service, 'poll').and.returnValue(returnPromise('some_other_state'));
spyOn(vm, 'initiateRemoveSourceBranchPolling');
......
......@@ -15,6 +15,10 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :t
.to receive(:commits_count=).and_return(nil)
end
after do
[Project, MergeRequest, MergeRequestDiff].each(&:reset_column_information)
end
def diffs_to_hashes(diffs)
diffs.as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS).map(&:with_indifferent_access)
end
......
......@@ -439,6 +439,7 @@ eos
end
describe '#uri_type' do
shared_examples 'URI type' do
it 'returns the URI type at the given path' do
expect(commit.uri_type('files/html')).to be(:tree)
expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
......@@ -451,6 +452,15 @@ eos
end
end
context 'when Gitaly commit_tree_entry feature is enabled' do
it_behaves_like 'URI type'
end
context 'when Gitaly commit_tree_entry feature is disabled', :disable_gitaly do
it_behaves_like 'URI type'
end
end
describe '.from_hash' do
let(:new_commit) { described_class.from_hash(commit.to_hash, project) }
......
......@@ -668,6 +668,18 @@ describe Repository do
expect(results.first).to eq('files/html/500.html')
end
it 'ignores leading slashes' do
results = repository.search_files_by_name('/files', 'master')
expect(results.first).to eq('files/html/500.html')
end
it 'properly handles when query is only slashes' do
results = repository.search_files_by_name('//', 'master')
expect(results).to match_array([])
end
it 'properly handles when query is not present' do
results = repository.search_files_by_name('', 'master')
......
......@@ -14,6 +14,46 @@ describe API::ProjectMilestones do
let(:route) { "/projects/#{project.id}/milestones" }
end
describe 'DELETE /projects/:id/milestones/:milestone_id' do
let(:guest) { create(:user) }
let(:reporter) { create(:user) }
before do
project.add_reporter(reporter)
end
it 'returns 404 response when the project does not exists' do
delete api("/projects/999/milestones/#{milestone.id}", user)
expect(response).to have_gitlab_http_status(404)
end
it 'returns 404 response when the milestone does not exists' do
delete api("/projects/#{project.id}/milestones/999", user)
expect(response).to have_gitlab_http_status(404)
end
it "returns 404 from guest user deleting a milestone" do
delete api("/projects/#{project.id}/milestones/#{milestone.id}", guest)
expect(response).to have_gitlab_http_status(404)
end
it "rejects a member with reporter access from deleting a milestone" do
delete api("/projects/#{project.id}/milestones/#{milestone.id}", reporter)
expect(response).to have_gitlab_http_status(403)
end
it 'deletes the milestone when the user has developer access to the project' do
delete api("/projects/#{project.id}/milestones/#{milestone.id}", user)
expect(project.milestones.find_by_id(milestone.id)).to be_nil
expect(response).to have_gitlab_http_status(204)
end
end
describe 'PUT /projects/:id/milestones/:milestone_id to test observer on close' do
it 'creates an activity event when an milestone is closed' do
expect(Event).to receive(:create!)
......
......@@ -160,7 +160,7 @@ describe SystemNoteService do
end
it 'builds a correct phrase when assignee removed' do
expect(build_note([assignee1], [])).to eq 'removed assignee'
expect(build_note([assignee1], [])).to eq "unassigned @#{assignee1.username}"
end
it 'builds a correct phrase when assignees changed' do
......
......@@ -90,10 +90,14 @@ codequality:
performance:
stage: performance
image:
name: sitespeedio/sitespeed.io:6.0.3
entrypoint: [""]
image: docker:latest
variables:
DOCKER_DRIVER: overlay2
allow_failure: true
services:
- docker:dind
script:
- setup_docker
- performance
artifacts:
paths:
......@@ -478,9 +482,9 @@ production:
if [ -f .gitlab-urls.txt ]
then
sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
/start.sh --plugins.add gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt
docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.0.3 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results .gitlab-urls.txt
else
/start.sh --plugins.add gitlab-exporter --outputFolder sitespeed-results $CI_ENVIRONMENT_URL
docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.0.3 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
fi
mv sitespeed-results/data/performance.json performance.json
......
......@@ -4191,9 +4191,9 @@ map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
marked@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
marked@^0.3.12:
version "0.3.12"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
math-expression-evaluator@^1.2.14:
version "1.2.16"
......
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