From 8bf52a4ae3aebc8c58f51cff696e99ecafe9c7c8 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Fri, 16 Dec 2016 02:12:21 -0200 Subject: Show directory hierarchy when listing wiki pages --- app/controllers/projects/wikis_controller.rb | 3 ++- app/models/wiki_page.rb | 25 ++++++++++++++++++++++ app/views/projects/wikis/_sidebar.html.haml | 12 +++++++---- app/views/projects/wikis/pages.html.haml | 14 +++++++----- .../23535-folders-in-wiki-repository.yml | 4 ++++ spec/models/wiki_page_spec.rb | 17 +++++++++++++++ 6 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/23535-folders-in-wiki-repository.yml diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index c3353446fd1..45a42400b2a 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -8,6 +8,7 @@ class Projects::WikisController < Projects::ApplicationController def pages @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]) + @wiki_directories = WikiPage.group_by_directory(@wiki_pages) end def show @@ -116,7 +117,7 @@ class Projects::WikisController < Projects::ApplicationController # Call #wiki to make sure the Wiki Repo is initialized @project_wiki.wiki - @sidebar_wiki_pages = @project_wiki.pages.first(15) + @sidebar_wiki_directories = WikiPage.group_by_directory(@project_wiki.pages.first(15)) rescue ProjectWiki::CouldNotCreateWikiError flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." redirect_to project_path(@project) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index c3de278f5b7..30db2b13dc0 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -12,6 +12,23 @@ class WikiPage ActiveModel::Name.new(self, nil, 'wiki') end + def self.group_by_directory(pages) + directories = {} + + pages.each do |page| + if page.slug.include?('/') + # Directory hierarchy is given by matching from the beginning up to + # the last forward slash. + directory = page.slug.match(/\A(.+)\//)[1] + directories[directory] = add_to_directory(directories[directory], page) + else + directories['root'] = add_to_directory(directories['root'], page) + end + end + + directories + end + def to_key [:slug] end @@ -176,6 +193,14 @@ class WikiPage private + def self.add_to_directory(directory, page) + if directory.present? + directory << page + else + [page] + end + end + def set_attributes attributes[:slug] = @page.url_path attributes[:title] = @page.title diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index cad9c15a49e..5aee1a136f5 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -12,10 +12,14 @@ .blocks-container .block.block-first %ul.wiki-pages - - @sidebar_wiki_pages.each do |wiki_page| - %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do - = wiki_page.title.capitalize + - @sidebar_wiki_directories.each do |wiki_directory, wiki_pages| + %li + = wiki_directory + %ul + - wiki_pages.each do |wiki_page| + %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do + = wiki_page.title.capitalize .block = link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do More Pages diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index e1eaffc6884..274afb1bdea 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -14,10 +14,14 @@ Clone repository %ul.content-list - - @wiki_pages.each do |wiki_page| + - @wiki_directories.each do |wiki_directory, wiki_pages| %li - = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) - %small (#{wiki_page.format}) - .pull-right - %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} + = wiki_directory + %ul + - wiki_pages.each do |wiki_page| + %li + = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) + %small (#{wiki_page.format}) + .pull-right + %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} = paginate @wiki_pages, theme: 'gitlab' diff --git a/changelogs/unreleased/23535-folders-in-wiki-repository.yml b/changelogs/unreleased/23535-folders-in-wiki-repository.yml new file mode 100644 index 00000000000..7361b182a94 --- /dev/null +++ b/changelogs/unreleased/23535-folders-in-wiki-repository.yml @@ -0,0 +1,4 @@ +--- +title: Show directory hierarchy when listing wiki pages +merge_request: +author: Alex Braha Stoll diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 5c34b1b0a30..25e7b517fe6 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -7,6 +7,23 @@ describe WikiPage, models: true do subject { WikiPage.new(wiki) } + describe '::group_by_directory' do + context 'when there are no pages' do + it 'returns an empty hash' do + end + end + + context 'when there are pages' do + let!(:page_1) { create_page('page_1', 'content') } + let!(:page_2) { create_page('directory/page_2', 'content') } + let(:pages) { [page_1, page_2] } + + xit 'returns a hash in which keys are directories and values are their pages' do + expected_grouped_pages = { 'root' => [page_1], 'directory' => [page_2] } + end + end + end + describe "#initialize" do context "when initialized with an existing gollum page" do before do -- cgit v1.2.1 From 083442bc716d7e69cbb9e7852159b0f3ba9a4610 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 17 Dec 2016 16:38:26 -0200 Subject: Add specs for WikiPage.group_by_directory --- spec/models/wiki_page_spec.rb | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 25e7b517fe6..595d4a621c1 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -7,19 +7,37 @@ describe WikiPage, models: true do subject { WikiPage.new(wiki) } - describe '::group_by_directory' do + describe '.group_by_directory' do context 'when there are no pages' do it 'returns an empty hash' do + expect(WikiPage.group_by_directory(nil)).to eq({}) + expect(WikiPage.group_by_directory([])).to eq({}) end end context 'when there are pages' do - let!(:page_1) { create_page('page_1', 'content') } - let!(:page_2) { create_page('directory/page_2', 'content') } - let(:pages) { [page_1, page_2] } + before do + create_page('page_1', 'content') + create_page('dir_1/page_2', 'content') + create_page('dir_1/dir_2/page_3', 'content') + end + + it 'returns a hash in which keys are directories and values are their pages' do + page_1 = wiki.find_page('page_1') + page_2 = wiki.find_page('dir_1/page_2') + page_3 = wiki.find_page('dir_1/dir_2/page_3') + expected_grouped_pages = { + '/' => [page_1], 'dir_1' => [page_2], 'dir_1/dir_2' => [page_3] + } - xit 'returns a hash in which keys are directories and values are their pages' do - expected_grouped_pages = { 'root' => [page_1], 'directory' => [page_2] } + grouped_pages = WikiPage.group_by_directory(wiki.pages) + + grouped_pages.each do |dir, pages| + expected_slugs = expected_grouped_pages.fetch(dir).map(&:slug) + slugs = pages.map(&:slug) + + expect(slugs).to match_array(expected_slugs) + end end end end -- cgit v1.2.1 From bebfba3e6de520f98d263ced2d2a17f6ddfc4a6f Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 17 Dec 2016 16:38:55 -0200 Subject: Refactor WikiPage.group_by_directory --- app/models/wiki_page.rb | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 30db2b13dc0..425384d3df4 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -13,17 +13,14 @@ class WikiPage end def self.group_by_directory(pages) - directories = {} + return {} if pages.blank? + directories = { '/' => [] } pages.each do |page| - if page.slug.include?('/') - # Directory hierarchy is given by matching from the beginning up to - # the last forward slash. - directory = page.slug.match(/\A(.+)\//)[1] - directories[directory] = add_to_directory(directories[directory], page) - else - directories['root'] = add_to_directory(directories['root'], page) - end + directory = page.wiki.page_title_and_dir(page.slug).last + directory = '/' if directory.blank? + directories[directory] ||= [] + directories[directory] << page end directories -- cgit v1.2.1 From c7294dded2fc869d6431ac192649f11ca7e96375 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 17 Dec 2016 16:39:55 -0200 Subject: Remove WikiPage.add_to_directory --- app/models/wiki_page.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 425384d3df4..aeacb6f8995 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -190,14 +190,6 @@ class WikiPage private - def self.add_to_directory(directory, page) - if directory.present? - directory << page - else - [page] - end - end - def set_attributes attributes[:slug] = @page.url_path attributes[:title] = @page.title -- cgit v1.2.1 From 91e1701b6abfb9a4b9ad50996bf4383d63b97e74 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 17 Dec 2016 17:38:45 -0200 Subject: Remove root directory name from the sidebar of wikis --- app/views/projects/wikis/_sidebar.html.haml | 16 +++++++++------- app/views/projects/wikis/_sidebar_wiki_pages.html.haml | 4 ++++ 2 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 app/views/projects/wikis/_sidebar_wiki_pages.html.haml diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index 5aee1a136f5..b7464180a0c 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -13,13 +13,15 @@ .block.block-first %ul.wiki-pages - @sidebar_wiki_directories.each do |wiki_directory, wiki_pages| - %li - = wiki_directory - %ul - - wiki_pages.each do |wiki_page| - %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do - = wiki_page.title.capitalize + - if wiki_directory == '/' + = render 'sidebar_wiki_pages', wiki_pages: wiki_pages + - else + %li + = wiki_directory + %ul + = render 'sidebar_wiki_pages', wiki_pages: wiki_pages + + .block = link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do More Pages diff --git a/app/views/projects/wikis/_sidebar_wiki_pages.html.haml b/app/views/projects/wikis/_sidebar_wiki_pages.html.haml new file mode 100644 index 00000000000..65453a384d2 --- /dev/null +++ b/app/views/projects/wikis/_sidebar_wiki_pages.html.haml @@ -0,0 +1,4 @@ +- wiki_pages.each do |wiki_page| + %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do + = wiki_page.title.capitalize -- cgit v1.2.1 From 294acf1c5cd2aea353081059c60b3951a2cf7c77 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 17 Dec 2016 18:00:29 -0200 Subject: Remove root directory name from index of wikis --- app/views/projects/wikis/_wiki_pages.html.haml | 6 ++++++ app/views/projects/wikis/pages.html.haml | 17 ++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 app/views/projects/wikis/_wiki_pages.html.haml diff --git a/app/views/projects/wikis/_wiki_pages.html.haml b/app/views/projects/wikis/_wiki_pages.html.haml new file mode 100644 index 00000000000..ac98599d96b --- /dev/null +++ b/app/views/projects/wikis/_wiki_pages.html.haml @@ -0,0 +1,6 @@ +- wiki_pages.each do |wiki_page| + %li + = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) + %small (#{wiki_page.format}) + .pull-right + %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 274afb1bdea..2813b3a1c81 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -15,13 +15,12 @@ %ul.content-list - @wiki_directories.each do |wiki_directory, wiki_pages| - %li - = wiki_directory - %ul - - wiki_pages.each do |wiki_page| - %li - = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) - %small (#{wiki_page.format}) - .pull-right - %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} + - if wiki_directory == '/' + = render 'wiki_pages', wiki_pages: wiki_pages + - else + %li + = wiki_directory + %ul + = render 'wiki_pages', wiki_pages: wiki_pages + = paginate @wiki_pages, theme: 'gitlab' -- cgit v1.2.1 From 5bbe6559917e1e64cdb047b6235715e2a7f002f2 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 18 Dec 2016 21:22:20 -0200 Subject: Add component to show the full path of a wiki page when viewing its content --- app/assets/stylesheets/pages/wiki.scss | 3 ++- app/models/wiki_page.rb | 11 +++++++++ app/views/projects/wikis/show.html.haml | 2 +- spec/models/wiki_page_spec.rb | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index b9f81533150..7afadb7364d 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -14,7 +14,8 @@ font-size: 22px; } - .wiki-last-edit-by { + .wiki-last-edit-by, .wiki-page-full-path { + display: block; color: $gl-gray-light; strong { diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index aeacb6f8995..e970cfbfff8 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -88,6 +88,12 @@ class WikiPage end end + # The hierarchy of the directory this page is contained in. + def directory + dir = wiki.page_title_and_dir(slug).last + dir.present? ? dir : '/' + end + # The processed/formatted content of this page. def formatted_content @attributes[:formatted_content] ||= if @page @@ -100,6 +106,11 @@ class WikiPage @attributes[:format] || :markdown end + # The full path for this page, including its filename and extension. + def full_path + "/#{directory}/#{page.filename}".gsub(/\/+/, '/') + end + # The commit message for this page version. def message version.try(:message) diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 1b6dceee241..25ae5c587ec 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -8,7 +8,7 @@ .nav-text %h2.wiki-page-title= @page.title.capitalize - + %span.wiki-page-full-path= "(#{@page.full_path})" %span.wiki-last-edit-by Last edited by %strong diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 595d4a621c1..c40a89b9dfb 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -224,6 +224,46 @@ describe WikiPage, models: true do end end + describe '#directory' do + context 'when the page is at the root directory' do + it 'returns /' do + create_page('file', 'content') + page = wiki.find_page('file') + + expect(page.directory).to eq('/') + end + end + + context 'when the page is inside an actual directory' do + it 'returns the full directory hierarchy' do + create_page('dir_1/dir_1_1/file', 'content') + page = wiki.find_page('dir_1/dir_1_1/file') + + expect(page.directory).to eq('dir_1/dir_1_1') + end + end + end + + describe '#full_path' do + context 'when the page is at the root directory' do + it 'returns /filename.fileextension' do + create_page('file', 'content') + page = wiki.find_page('file') + + expect(page.full_path).to eq('/file.md') + end + end + + context 'when the page is inside an actual directory' do + it 'returns /directory/filename.fileextension' do + create_page('dir/file', 'content') + page = wiki.find_page('dir/file') + + expect(page.full_path).to eq('/dir/file.md') + end + end + end + describe '#historical?' do before do create_page('Update', 'content') -- cgit v1.2.1 From 904aa039e5ccb4d9f653d254ea5818be130fb218 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 18 Dec 2016 21:27:30 -0200 Subject: Change WikiPage.group_by_directory to use WikiPage#directory --- app/models/wiki_page.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index e970cfbfff8..1dbb3407623 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -17,10 +17,8 @@ class WikiPage directories = { '/' => [] } pages.each do |page| - directory = page.wiki.page_title_and_dir(page.slug).last - directory = '/' if directory.blank? - directories[directory] ||= [] - directories[directory] << page + directories[page.directory] ||= [] + directories[page.directory] << page end directories -- cgit v1.2.1 From 5607bb8f0921cbfa4586bb7b92acb6666a65b4e2 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 18 Dec 2016 21:37:10 -0200 Subject: Change WikiPage#directory to always start a directory hierarchy with '/' --- app/models/wiki_page.rb | 4 ++-- spec/models/wiki_page_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 1dbb3407623..a563b0b7a72 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -89,7 +89,7 @@ class WikiPage # The hierarchy of the directory this page is contained in. def directory dir = wiki.page_title_and_dir(slug).last - dir.present? ? dir : '/' + "/#{dir}" end # The processed/formatted content of this page. @@ -106,7 +106,7 @@ class WikiPage # The full path for this page, including its filename and extension. def full_path - "/#{directory}/#{page.filename}".gsub(/\/+/, '/') + "#{directory}/#{page.filename}".gsub(/\/+/, '/') end # The commit message for this page version. diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index c40a89b9dfb..91d5fccce60 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -27,7 +27,7 @@ describe WikiPage, models: true do page_2 = wiki.find_page('dir_1/page_2') page_3 = wiki.find_page('dir_1/dir_2/page_3') expected_grouped_pages = { - '/' => [page_1], 'dir_1' => [page_2], 'dir_1/dir_2' => [page_3] + '/' => [page_1], '/dir_1' => [page_2], '/dir_1/dir_2' => [page_3] } grouped_pages = WikiPage.group_by_directory(wiki.pages) @@ -239,7 +239,7 @@ describe WikiPage, models: true do create_page('dir_1/dir_1_1/file', 'content') page = wiki.find_page('dir_1/dir_1_1/file') - expect(page.directory).to eq('dir_1/dir_1_1') + expect(page.directory).to eq('/dir_1/dir_1_1') end end end -- cgit v1.2.1 From 7f914ec73f9bdb3d2d4e51d48906a36186f496e3 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 18 Dec 2016 22:30:07 -0200 Subject: Add tip about specifying the full path when creating new wiki pages --- app/assets/stylesheets/pages/wiki.scss | 8 ++++++++ app/views/projects/wikis/_new.html.haml | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 7afadb7364d..6423c7d6302 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -1,3 +1,11 @@ +.new-wiki-page { + .new-wiki-page-slug-tip { + display: inline-block; + max-width: 100%; + margin-top: 5px; + } +} + .title .edit-wiki-header { width: 780px; margin-left: auto; diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index c74f53b4c39..f9f8fc63288 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -13,5 +13,9 @@ = label_tag :new_wiki_path do %span Page slug = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true + %span.new-wiki-page-slug-tip + %i.fa.fa-lightbulb-o +  Tip: You can specify the full path for the new file. + We will automatically create any missing directories. .form-actions = button_tag 'Create Page', class: 'build-new-wiki btn btn-create' -- cgit v1.2.1 From f25344e36e9c1b0d0df2211b82b26c6515e96c31 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 19 Dec 2016 02:34:35 -0200 Subject: Change WikiPage.group_by_directory to order by directory and file alphabetical order --- app/models/wiki_page.rb | 29 ++++++++++++++++++++++++++++- spec/models/wiki_page_spec.rb | 30 ++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index a563b0b7a72..a84f84c67cd 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -12,10 +12,17 @@ class WikiPage ActiveModel::Name.new(self, nil, 'wiki') end + # Sorts and groups pages by directory. + # + # pages - an array of WikiPage objects. + # + # Returns a hash whose keys are directories and whose values are WikiPage + # arrays. See WikiPage.sort_by_directory for more info about the ordering. def self.group_by_directory(pages) return {} if pages.blank? - directories = { '/' => [] } + pages = sort_by_directory(pages) + directories = {} pages.each do |page| directories[page.directory] ||= [] directories[page.directory] << page @@ -199,6 +206,26 @@ class WikiPage private + # Sorts an array of pages by directory and file alphabetical order. + # Pages at the root directory will come first. The next pages will be + # sorted by their directories. Within directories, pages are sorted by + # filename alphabetical order. Pages are sorted in such a fashion that + # nested directories will always follow their parents (e.g. pages in + # dir_1/nested_dir_1 will follow pages inside dir_1). + # + # pages - an array of WikiPage objects. + # + # Returns a sorted array of WikiPage objects. + def self.sort_by_directory(pages) + pages.sort do |page, next_page| + if page.directory == next_page.directory + page.slug <=> next_page.slug + else + page.directory <=> next_page.directory + end + end + end + def set_attributes attributes[:slug] = @page.url_path attributes[:title] = @page.title diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 91d5fccce60..374849e1932 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -17,17 +17,22 @@ describe WikiPage, models: true do context 'when there are pages' do before do - create_page('page_1', 'content') + create_page('dir_1/dir_1_1/page_3', 'content') create_page('dir_1/page_2', 'content') - create_page('dir_1/dir_2/page_3', 'content') + create_page('dir_2/page_5', 'content') + create_page('dir_2/page_4', 'content') + create_page('page_1', 'content') end it 'returns a hash in which keys are directories and values are their pages' do page_1 = wiki.find_page('page_1') page_2 = wiki.find_page('dir_1/page_2') - page_3 = wiki.find_page('dir_1/dir_2/page_3') + page_3 = wiki.find_page('dir_1/dir_1_1/page_3') + page_4 = wiki.find_page('dir_2/page_4') + page_5 = wiki.find_page('dir_2/page_5') expected_grouped_pages = { - '/' => [page_1], '/dir_1' => [page_2], '/dir_1/dir_2' => [page_3] + '/' => [page_1], '/dir_1' => [page_2], '/dir_1/dir_1_1' => [page_3], + '/dir_2' => [page_4, page_5] } grouped_pages = WikiPage.group_by_directory(wiki.pages) @@ -39,6 +44,23 @@ describe WikiPage, models: true do expect(slugs).to match_array(expected_slugs) end end + + it 'returns a hash in which keys (directories) are sorted by alphabetical position' do + expected_ordered_directories = ['/', '/dir_1', '/dir_1/dir_1_1', '/dir_2'] + + grouped_pages = WikiPage.group_by_directory(wiki.pages) + + expect(grouped_pages.keys).to eq(expected_ordered_directories) + end + + it 'returns a hash in which values (pages) are sorted by alphabetical position' do + expected_ordered_page_slugs = ['dir_2/page_4', 'dir_2/page_5'] + + grouped_pages = WikiPage.group_by_directory(wiki.pages) + + dir_2_page_slugs = grouped_pages.fetch('/dir_2').map(&:slug) + expect(dir_2_page_slugs).to eq(expected_ordered_page_slugs) + end end end -- cgit v1.2.1 From e66fa4105b4ae275dc76a6594346367bd32b5ce9 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 19 Dec 2016 03:13:26 -0200 Subject: Improve style of wiki page lists --- app/assets/stylesheets/pages/wiki.scss | 16 ++++++++++++++++ app/views/projects/wikis/pages.html.haml | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 6423c7d6302..819e4c3e3d8 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -130,6 +130,10 @@ margin: 5px 0 10px; } + ul.wiki-pages ul { + padding-left: 15px; + } + .wiki-sidebar-header { padding: 0 $gl-padding $gl-padding; @@ -138,3 +142,15 @@ } } } + +ul.wiki-pages-list.content-list { + & ul { + list-style: none; + margin-left: 0; + padding-left: 15px; + } + + & ul li { + padding: 5px 0; + } +} diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 2813b3a1c81..28dd81e5c3f 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -13,7 +13,7 @@ = icon('cloud-download') Clone repository - %ul.content-list + %ul.wiki-pages-list.content-list - @wiki_directories.each do |wiki_directory, wiki_pages| - if wiki_directory == '/' = render 'wiki_pages', wiki_pages: wiki_pages -- cgit v1.2.1 From 50e3e796ea5ca12addbfb438feae606ca7067a22 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Tue, 20 Dec 2016 12:15:56 -0200 Subject: Fix scss style violation --- app/assets/stylesheets/pages/wiki.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 819e4c3e3d8..1a22cd7d33f 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -22,7 +22,8 @@ font-size: 22px; } - .wiki-last-edit-by, .wiki-page-full-path { + .wiki-page-full-path, + .wiki-last-edit-by { display: block; color: $gl-gray-light; -- cgit v1.2.1 From 645aaf6e3d18007b56e5bbb33c6fda1526bdc716 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 25 Dec 2016 22:13:20 -0200 Subject: Use the icon helper at wikis/_new.html.haml --- app/views/projects/wikis/_new.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index f9f8fc63288..d91a7096701 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -14,8 +14,8 @@ %span Page slug = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true %span.new-wiki-page-slug-tip - %i.fa.fa-lightbulb-o -  Tip: You can specify the full path for the new file. + =icon('lightbulb-o') + Tip: You can specify the full path for the new file. We will automatically create any missing directories. .form-actions = button_tag 'Create Page', class: 'build-new-wiki btn btn-create' -- cgit v1.2.1 From 77fe503a1fd01eaa8b790d1aacc0cdab159f015e Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 25 Dec 2016 22:50:36 -0200 Subject: Remove WikiPage.sort_by_directory --- app/models/wiki_page.rb | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index a84f84c67cd..efb6ff9bf2b 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -17,10 +17,10 @@ class WikiPage # pages - an array of WikiPage objects. # # Returns a hash whose keys are directories and whose values are WikiPage - # arrays. See WikiPage.sort_by_directory for more info about the ordering. + # arrays. def self.group_by_directory(pages) return {} if pages.blank? - pages = sort_by_directory(pages) + pages = pages.sort_by { |page| [page.directory, page.slug] } directories = {} pages.each do |page| @@ -206,26 +206,6 @@ class WikiPage private - # Sorts an array of pages by directory and file alphabetical order. - # Pages at the root directory will come first. The next pages will be - # sorted by their directories. Within directories, pages are sorted by - # filename alphabetical order. Pages are sorted in such a fashion that - # nested directories will always follow their parents (e.g. pages in - # dir_1/nested_dir_1 will follow pages inside dir_1). - # - # pages - an array of WikiPage objects. - # - # Returns a sorted array of WikiPage objects. - def self.sort_by_directory(pages) - pages.sort do |page, next_page| - if page.directory == next_page.directory - page.slug <=> next_page.slug - else - page.directory <=> next_page.directory - end - end - end - def set_attributes attributes[:slug] = @page.url_path attributes[:title] = @page.title -- cgit v1.2.1 From 8d8c5d9f61491c63e89d73a3f77244d3cd6406da Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sun, 25 Dec 2016 23:05:04 -0200 Subject: Simplify WikiPage.group_by_directory by using Enumerable#group_by --- app/models/wiki_page.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index efb6ff9bf2b..0e905cb9a00 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -20,15 +20,8 @@ class WikiPage # arrays. def self.group_by_directory(pages) return {} if pages.blank? - pages = pages.sort_by { |page| [page.directory, page.slug] } - - directories = {} - pages.each do |page| - directories[page.directory] ||= [] - directories[page.directory] << page - end - - directories + pages.sort_by { |page| [page.directory, page.slug] }. + group_by { |page| page.directory } end def to_key -- cgit v1.2.1 From b361a67fb019e5c7f5361bbd3c43545da3ab0288 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 26 Dec 2016 13:07:40 -0200 Subject: Add model WikiDirectory --- app/models/wiki_directory.rb | 13 +++++++++++ spec/factories/wiki_directories.rb | 6 +++++ spec/models/wiki_directory_spec.rb | 45 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 app/models/wiki_directory.rb create mode 100644 spec/factories/wiki_directories.rb create mode 100644 spec/models/wiki_directory_spec.rb diff --git a/app/models/wiki_directory.rb b/app/models/wiki_directory.rb new file mode 100644 index 00000000000..c126a4d0421 --- /dev/null +++ b/app/models/wiki_directory.rb @@ -0,0 +1,13 @@ +class WikiDirectory + include ActiveModel::Validations + + attr_accessor :slug, :pages, :directories + + validates :slug, presence: true + + def initialize(slug, pages = [], directories = []) + @slug = slug + @pages = pages + @directories = directories + end +end diff --git a/spec/factories/wiki_directories.rb b/spec/factories/wiki_directories.rb new file mode 100644 index 00000000000..3f3c864ac2b --- /dev/null +++ b/spec/factories/wiki_directories.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :wiki_directory do + slug '/path_up_to/dir' + initialize_with { new(slug) } + end +end diff --git a/spec/models/wiki_directory_spec.rb b/spec/models/wiki_directory_spec.rb new file mode 100644 index 00000000000..8362a285c54 --- /dev/null +++ b/spec/models/wiki_directory_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +RSpec.describe WikiDirectory, models: true do + describe 'validations' do + subject { build(:wiki_directory) } + + it { is_expected.to validate_presence_of(:slug) } + end + + describe '#initialize' do + context 'when there are pages and directories' do + let(:pages) { [build(:wiki_page)] } + let(:other_directories) { [build(:wiki_directory)] } + let(:directory) { WikiDirectory.new('/path_up_to/dir', pages, other_directories) } + + it 'sets the slug attribute' do + expect(directory.slug).to eq('/path_up_to/dir') + end + + it 'sets the pages attribute' do + expect(directory.pages).to eq(pages) + end + + it 'sets the directories attribute' do + expect(directory.directories).to eq(other_directories) + end + end + + context 'when there are no pages or directories' do + let(:directory) { WikiDirectory.new('/path_up_to/dir') } + + it 'sets the slug attribute' do + expect(directory.slug).to eq('/path_up_to/dir') + end + + it 'sets the pages attribute to an empty array' do + expect(directory.pages).to eq([]) + end + + it 'sets the directories attribute to an empty array' do + expect(directory.directories).to eq([]) + end + end + end +end -- cgit v1.2.1 From c8a1e9682656b6b3ec714e38459e089df2ee106c Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 26 Dec 2016 20:12:15 -0200 Subject: Change WikiPage.group_by_directory to use WikiDirectory --- app/models/wiki_page.rb | 18 ++++++++--- spec/models/wiki_page_spec.rb | 72 +++++++++++++++++++++++++------------------ 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 0e905cb9a00..63e5aa0e519 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -16,12 +16,22 @@ class WikiPage # # pages - an array of WikiPage objects. # - # Returns a hash whose keys are directories and whose values are WikiPage - # arrays. + # Returns an array of WikiPage and WikiDirectory objects. The entries are + # sorted by alphabetical order (directories and pages inside each directory). + # Pages at the root level come before everything. def self.group_by_directory(pages) - return {} if pages.blank? + return [] if pages.blank? + pages.sort_by { |page| [page.directory, page.slug] }. - group_by { |page| page.directory } + group_by { |page| page.directory }. + map do |dir, pages| + if dir == '/' + pages + else + WikiDirectory.new(dir, pages) + end + end. + flatten end def to_key diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 374849e1932..9eb94cb028d 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -9,9 +9,9 @@ describe WikiPage, models: true do describe '.group_by_directory' do context 'when there are no pages' do - it 'returns an empty hash' do - expect(WikiPage.group_by_directory(nil)).to eq({}) - expect(WikiPage.group_by_directory([])).to eq({}) + it 'returns an empty array' do + expect(WikiPage.group_by_directory(nil)).to eq([]) + expect(WikiPage.group_by_directory([])).to eq([]) end end @@ -23,43 +23,47 @@ describe WikiPage, models: true do create_page('dir_2/page_4', 'content') create_page('page_1', 'content') end + let(:page_1) { wiki.find_page('page_1') } + let(:dir_1) do + WikiDirectory.new('dir_1', [wiki.find_page('dir_1/page_2')]) + end + let(:dir_1_1) do + WikiDirectory.new('dir_1/dir_1_1', [wiki.find_page('dir_1/dir_1_1/page_3')]) + end + let(:dir_2) do + pages = [wiki.find_page('dir_2/page_5'), + wiki.find_page('dir_2/page_4')] + WikiDirectory.new('dir_2', pages) + end - it 'returns a hash in which keys are directories and values are their pages' do - page_1 = wiki.find_page('page_1') - page_2 = wiki.find_page('dir_1/page_2') - page_3 = wiki.find_page('dir_1/dir_1_1/page_3') - page_4 = wiki.find_page('dir_2/page_4') - page_5 = wiki.find_page('dir_2/page_5') - expected_grouped_pages = { - '/' => [page_1], '/dir_1' => [page_2], '/dir_1/dir_1_1' => [page_3], - '/dir_2' => [page_4, page_5] - } + it 'returns an array with pages and directories' do + expected_grouped_entries = [page_1, dir_1, dir_1_1, dir_2] - grouped_pages = WikiPage.group_by_directory(wiki.pages) + grouped_entries = WikiPage.group_by_directory(wiki.pages) - grouped_pages.each do |dir, pages| - expected_slugs = expected_grouped_pages.fetch(dir).map(&:slug) - slugs = pages.map(&:slug) + grouped_entries.each_with_index do |page_or_dir, i| + expected_page_or_dir = expected_grouped_entries[i] + expected_slugs = get_slugs(expected_page_or_dir) + slugs = get_slugs(page_or_dir) expect(slugs).to match_array(expected_slugs) end end - it 'returns a hash in which keys (directories) are sorted by alphabetical position' do - expected_ordered_directories = ['/', '/dir_1', '/dir_1/dir_1_1', '/dir_2'] - - grouped_pages = WikiPage.group_by_directory(wiki.pages) - - expect(grouped_pages.keys).to eq(expected_ordered_directories) - end - - it 'returns a hash in which values (pages) are sorted by alphabetical position' do - expected_ordered_page_slugs = ['dir_2/page_4', 'dir_2/page_5'] + it 'returns an array sorted by alphabetical position' do + # Directories and pages within directories are sorted alphabetically. + # Pages at root come before everything. + expected_order = ['page_1', 'dir_1/page_2', 'dir_1/dir_1_1/page_3', + 'dir_2/page_4', 'dir_2/page_5'] - grouped_pages = WikiPage.group_by_directory(wiki.pages) + grouped_entries = WikiPage.group_by_directory(wiki.pages) - dir_2_page_slugs = grouped_pages.fetch('/dir_2').map(&:slug) - expect(dir_2_page_slugs).to eq(expected_ordered_page_slugs) + actual_order = + grouped_entries.map do |page_or_dir| + get_slugs(page_or_dir) + end. + flatten + expect(actual_order).to eq(expected_order) end end end @@ -336,4 +340,12 @@ describe WikiPage, models: true do page = wiki.wiki.paged(title) wiki.wiki.delete_page(page, commit_details) end + + def get_slugs(page_or_dir) + if page_or_dir.is_a? WikiPage + [page_or_dir.slug] + else + page_or_dir.pages.present? ? page_or_dir.pages.map(&:slug) : [] + end + end end -- cgit v1.2.1 From 84735186a8ae73a722715f286653ccd71e7e48e8 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 26 Dec 2016 23:51:34 -0200 Subject: Add WikiPage#to_partial_path --- app/models/wiki_page.rb | 6 ++++++ spec/models/wiki_page_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 63e5aa0e519..96d03d510ff 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -207,6 +207,12 @@ class WikiPage end end + # Relative path to the partial to be used when rendering collections + # of this object. + def to_partial_path + 'projects/wikis/wiki_page' + end + private def set_attributes diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 9eb94cb028d..11efd0415d9 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -322,6 +322,14 @@ describe WikiPage, models: true do end end + describe '#to_partial_path' do + it 'returns the relative path to the partial to be used' do + page = build(:wiki_page) + + expect(page.to_partial_path).to eq('projects/wikis/wiki_page') + end + end + private def remove_temp_repo(path) -- cgit v1.2.1 From 7bd68ae0799a982a4113de3480bef0d51ecb2f1c Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 26 Dec 2016 23:52:26 -0200 Subject: Add WikiDirectory#to_partial_path --- app/models/wiki_directory.rb | 6 ++++++ spec/models/wiki_directory_spec.rb | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/app/models/wiki_directory.rb b/app/models/wiki_directory.rb index c126a4d0421..561e5a497bc 100644 --- a/app/models/wiki_directory.rb +++ b/app/models/wiki_directory.rb @@ -10,4 +10,10 @@ class WikiDirectory @pages = pages @directories = directories end + + # Relative path to the partial to be used when rendering collections + # of this object. + def to_partial_path + 'projects/wikis/wiki_directory' + end end diff --git a/spec/models/wiki_directory_spec.rb b/spec/models/wiki_directory_spec.rb index 8362a285c54..fac70f8d3c7 100644 --- a/spec/models/wiki_directory_spec.rb +++ b/spec/models/wiki_directory_spec.rb @@ -42,4 +42,12 @@ RSpec.describe WikiDirectory, models: true do end end end + + describe '#to_partial_path' do + it 'returns the relative path to the partial to be used' do + directory = build(:wiki_directory) + + expect(directory.to_partial_path).to eq('projects/wikis/wiki_directory') + end + end end -- cgit v1.2.1 From a5625c749b31760daf104241475a9b3527eb223c Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 26 Dec 2016 23:54:36 -0200 Subject: Render wiki entries using a collection of WikiPage and WikiDirectory objects --- app/controllers/projects/wikis_controller.rb | 4 ++-- app/views/projects/wikis/_sidebar.html.haml | 10 +--------- app/views/projects/wikis/_sidebar_wiki_pages.html.haml | 4 ---- app/views/projects/wikis/_wiki_directory.html.haml | 4 ++++ app/views/projects/wikis/_wiki_page.html.haml | 10 ++++++++++ app/views/projects/wikis/_wiki_pages.html.haml | 6 ------ app/views/projects/wikis/pages.html.haml | 9 +-------- 7 files changed, 18 insertions(+), 29 deletions(-) delete mode 100644 app/views/projects/wikis/_sidebar_wiki_pages.html.haml create mode 100644 app/views/projects/wikis/_wiki_directory.html.haml create mode 100644 app/views/projects/wikis/_wiki_page.html.haml delete mode 100644 app/views/projects/wikis/_wiki_pages.html.haml diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 45a42400b2a..116c854b1ae 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -8,7 +8,7 @@ class Projects::WikisController < Projects::ApplicationController def pages @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]) - @wiki_directories = WikiPage.group_by_directory(@wiki_pages) + @wiki_entries = WikiPage.group_by_directory(@wiki_pages) end def show @@ -117,7 +117,7 @@ class Projects::WikisController < Projects::ApplicationController # Call #wiki to make sure the Wiki Repo is initialized @project_wiki.wiki - @sidebar_wiki_directories = WikiPage.group_by_directory(@project_wiki.pages.first(15)) + @sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.pages.first(15)) rescue ProjectWiki::CouldNotCreateWikiError flash[:notice] = "Could not create Wiki Repository at this time. Please try again later." redirect_to project_path(@project) diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml index b7464180a0c..e3fddfba689 100644 --- a/app/views/projects/wikis/_sidebar.html.haml +++ b/app/views/projects/wikis/_sidebar.html.haml @@ -12,15 +12,7 @@ .blocks-container .block.block-first %ul.wiki-pages - - @sidebar_wiki_directories.each do |wiki_directory, wiki_pages| - - if wiki_directory == '/' - = render 'sidebar_wiki_pages', wiki_pages: wiki_pages - - else - %li - = wiki_directory - %ul - = render 'sidebar_wiki_pages', wiki_pages: wiki_pages - + = render @sidebar_wiki_entries, context: 'sidebar' .block = link_to namespace_project_wikis_pages_path(@project.namespace, @project), class: 'btn btn-block' do diff --git a/app/views/projects/wikis/_sidebar_wiki_pages.html.haml b/app/views/projects/wikis/_sidebar_wiki_pages.html.haml deleted file mode 100644 index 65453a384d2..00000000000 --- a/app/views/projects/wikis/_sidebar_wiki_pages.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -- wiki_pages.each do |wiki_page| - %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do - = wiki_page.title.capitalize diff --git a/app/views/projects/wikis/_wiki_directory.html.haml b/app/views/projects/wikis/_wiki_directory.html.haml new file mode 100644 index 00000000000..0e5f32ed859 --- /dev/null +++ b/app/views/projects/wikis/_wiki_directory.html.haml @@ -0,0 +1,4 @@ +%li + = wiki_directory.slug + %ul + = render wiki_directory.pages, context: context diff --git a/app/views/projects/wikis/_wiki_page.html.haml b/app/views/projects/wikis/_wiki_page.html.haml new file mode 100644 index 00000000000..cea27388a0d --- /dev/null +++ b/app/views/projects/wikis/_wiki_page.html.haml @@ -0,0 +1,10 @@ +- if context == 'sidebar' + %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do + = wiki_page.title.capitalize +- else + %li + = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) + %small (#{wiki_page.format}) + .pull-right + %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/_wiki_pages.html.haml b/app/views/projects/wikis/_wiki_pages.html.haml deleted file mode 100644 index ac98599d96b..00000000000 --- a/app/views/projects/wikis/_wiki_pages.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -- wiki_pages.each do |wiki_page| - %li - = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) - %small (#{wiki_page.format}) - .pull-right - %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml index 28dd81e5c3f..5fba2b1a5ae 100644 --- a/app/views/projects/wikis/pages.html.haml +++ b/app/views/projects/wikis/pages.html.haml @@ -14,13 +14,6 @@ Clone repository %ul.wiki-pages-list.content-list - - @wiki_directories.each do |wiki_directory, wiki_pages| - - if wiki_directory == '/' - = render 'wiki_pages', wiki_pages: wiki_pages - - else - %li - = wiki_directory - %ul - = render 'wiki_pages', wiki_pages: wiki_pages + = render @wiki_entries, context: 'pages' = paginate @wiki_pages, theme: 'gitlab' -- cgit v1.2.1 From 84cc7c3704cc0cc22a325572f35cd21d0e2a6cc7 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Tue, 27 Dec 2016 00:14:30 -0200 Subject: Stop rendering page full path at projects/wikis/show.html.haml --- app/assets/stylesheets/pages/wiki.scss | 1 - app/views/projects/wikis/show.html.haml | 1 - 2 files changed, 2 deletions(-) diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 1a22cd7d33f..369fb44d818 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -22,7 +22,6 @@ font-size: 22px; } - .wiki-page-full-path, .wiki-last-edit-by { display: block; color: $gl-gray-light; diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 25ae5c587ec..87b9ff6e415 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -8,7 +8,6 @@ .nav-text %h2.wiki-page-title= @page.title.capitalize - %span.wiki-page-full-path= "(#{@page.full_path})" %span.wiki-last-edit-by Last edited by %strong -- cgit v1.2.1 From 94dcadd62ac66cc5c52579ae9c288314bbca0c20 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Tue, 27 Dec 2016 01:44:03 -0200 Subject: Add a breadcrumb at projects/wikis/show.html.haml --- app/assets/stylesheets/pages/wiki.scss | 5 +++++ app/helpers/wiki_helper.rb | 13 +++++++++++++ app/views/projects/wikis/show.html.haml | 3 +++ spec/helpers/wiki_helper_spec.rb | 21 +++++++++++++++++++++ 4 files changed, 42 insertions(+) create mode 100644 app/helpers/wiki_helper.rb create mode 100644 spec/helpers/wiki_helper_spec.rb diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 369fb44d818..480cb2b9f0d 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -17,6 +17,11 @@ @extend .top-area; position: relative; + .wiki-breadcrumb { + border-bottom: 1px solid $white-normal; + padding: 11px 0; + } + .wiki-page-title { margin: 0; font-size: 22px; diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb new file mode 100644 index 00000000000..76ee632ab6d --- /dev/null +++ b/app/helpers/wiki_helper.rb @@ -0,0 +1,13 @@ +module WikiHelper + # Produces a pure text breadcrumb for a given page. + # + # page_slug - The slug of a WikiPage object. + # + # Returns a String composed of the capitalized name of each directory and the + # capitalized name of the page itself. + def breadcrumb(page_slug) + page_slug.split('/'). + map { |dir_or_page| dir_or_page.gsub(/-+/, ' ').capitalize }. + join(' / ') + end +end diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index 87b9ff6e415..3609461b721 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -6,6 +6,9 @@ %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } = icon('angle-double-left') + .wiki-breadcrumb + %span= breadcrumb(@page.slug) + .nav-text %h2.wiki-page-title= @page.title.capitalize %span.wiki-last-edit-by diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb new file mode 100644 index 00000000000..92c6f27a867 --- /dev/null +++ b/spec/helpers/wiki_helper_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe WikiHelper do + describe '#breadcrumb' do + context 'when the page is at the root level' do + it 'returns the capitalized page name' do + slug = 'page-name' + + expect(helper.breadcrumb(slug)).to eq('Page name') + end + end + + context 'when the page is inside a directory' do + it 'returns the capitalized name of each directory and of the page itself' do + slug = 'dir_1/page-name' + + expect(helper.breadcrumb(slug)).to eq('Dir_1 / Page name') + end + end + end +end -- cgit v1.2.1 From 104bfa2a3187aefebd4a53be1ad14600dc7781e9 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Tue, 27 Dec 2016 01:52:50 -0200 Subject: Remove WikiPage#full_path --- app/models/wiki_page.rb | 5 ----- spec/models/wiki_page_spec.rb | 20 -------------------- 2 files changed, 25 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 96d03d510ff..dec58681198 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -114,11 +114,6 @@ class WikiPage @attributes[:format] || :markdown end - # The full path for this page, including its filename and extension. - def full_path - "#{directory}/#{page.filename}".gsub(/\/+/, '/') - end - # The commit message for this page version. def message version.try(:message) diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 11efd0415d9..482f98e22f1 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -270,26 +270,6 @@ describe WikiPage, models: true do end end - describe '#full_path' do - context 'when the page is at the root directory' do - it 'returns /filename.fileextension' do - create_page('file', 'content') - page = wiki.find_page('file') - - expect(page.full_path).to eq('/file.md') - end - end - - context 'when the page is inside an actual directory' do - it 'returns /directory/filename.fileextension' do - create_page('dir/file', 'content') - page = wiki.find_page('dir/file') - - expect(page.full_path).to eq('/dir/file.md') - end - end - end - describe '#historical?' do before do create_page('Update', 'content') -- cgit v1.2.1 From d2b3fe45af8d458b935b3bbfc1558e21c1476d0a Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Tue, 27 Dec 2016 02:05:53 -0200 Subject: Change WikiPage#directory --- app/models/wiki_page.rb | 9 ++++----- spec/models/wiki_page_spec.rb | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index dec58681198..6c237306eff 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -25,10 +25,10 @@ class WikiPage pages.sort_by { |page| [page.directory, page.slug] }. group_by { |page| page.directory }. map do |dir, pages| - if dir == '/' - pages - else + if dir.present? WikiDirectory.new(dir, pages) + else + pages end end. flatten @@ -98,8 +98,7 @@ class WikiPage # The hierarchy of the directory this page is contained in. def directory - dir = wiki.page_title_and_dir(slug).last - "/#{dir}" + wiki.page_title_and_dir(slug).last end # The processed/formatted content of this page. diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 482f98e22f1..109a0499090 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -252,11 +252,11 @@ describe WikiPage, models: true do describe '#directory' do context 'when the page is at the root directory' do - it 'returns /' do + it 'returns an empty string' do create_page('file', 'content') page = wiki.find_page('file') - expect(page.directory).to eq('/') + expect(page.directory).to eq('') end end @@ -265,7 +265,7 @@ describe WikiPage, models: true do create_page('dir_1/dir_1_1/file', 'content') page = wiki.find_page('dir_1/dir_1_1/file') - expect(page.directory).to eq('/dir_1/dir_1_1') + expect(page.directory).to eq('dir_1/dir_1_1') end end end -- cgit v1.2.1 From 389bd6b7356e78668831e4628f9ca8dadb01fcf2 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 31 Dec 2016 17:03:20 -0200 Subject: Improve WikiPage.group_by_directory --- app/models/wiki_page.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 6c237306eff..20bd9719b2f 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -23,7 +23,7 @@ class WikiPage return [] if pages.blank? pages.sort_by { |page| [page.directory, page.slug] }. - group_by { |page| page.directory }. + group_by(&:directory). map do |dir, pages| if dir.present? WikiDirectory.new(dir, pages) -- cgit v1.2.1 From b0ad4e0e87c642efefa840eeeea5824191e81405 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 31 Dec 2016 17:14:45 -0200 Subject: Add new wiki related partials --- app/views/projects/wikis/_pages_wiki_page.html.haml | 5 +++++ app/views/projects/wikis/_sidebar_wiki_page.html.haml | 3 +++ app/views/projects/wikis/_wiki_page.html.haml | 11 +---------- 3 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 app/views/projects/wikis/_pages_wiki_page.html.haml create mode 100644 app/views/projects/wikis/_sidebar_wiki_page.html.haml diff --git a/app/views/projects/wikis/_pages_wiki_page.html.haml b/app/views/projects/wikis/_pages_wiki_page.html.haml new file mode 100644 index 00000000000..6298cf6c8da --- /dev/null +++ b/app/views/projects/wikis/_pages_wiki_page.html.haml @@ -0,0 +1,5 @@ +%li + = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) + %small (#{wiki_page.format}) + .pull-right + %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} diff --git a/app/views/projects/wikis/_sidebar_wiki_page.html.haml b/app/views/projects/wikis/_sidebar_wiki_page.html.haml new file mode 100644 index 00000000000..eb9bd14920d --- /dev/null +++ b/app/views/projects/wikis/_sidebar_wiki_page.html.haml @@ -0,0 +1,3 @@ +%li{ class: params[:id] == wiki_page.slug ? 'active' : '' } + = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do + = wiki_page.title.capitalize diff --git a/app/views/projects/wikis/_wiki_page.html.haml b/app/views/projects/wikis/_wiki_page.html.haml index cea27388a0d..c84d06dad02 100644 --- a/app/views/projects/wikis/_wiki_page.html.haml +++ b/app/views/projects/wikis/_wiki_page.html.haml @@ -1,10 +1 @@ -- if context == 'sidebar' - %li{ class: params[:id] == wiki_page.slug ? 'active' : '' } - = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do - = wiki_page.title.capitalize -- else - %li - = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page) - %small (#{wiki_page.format}) - .pull-right - %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)} += render "#{context}_wiki_page", wiki_page: wiki_page -- cgit v1.2.1 From 48417893d7456dc0d46b0a514a2326cc8ce6076f Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Sat, 31 Dec 2016 17:27:03 -0200 Subject: Remove directories as one of the attributes of WikiDirectory --- app/models/wiki_directory.rb | 5 ++--- spec/models/wiki_directory_spec.rb | 15 +++------------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/app/models/wiki_directory.rb b/app/models/wiki_directory.rb index 561e5a497bc..9340fc2dbbe 100644 --- a/app/models/wiki_directory.rb +++ b/app/models/wiki_directory.rb @@ -1,14 +1,13 @@ class WikiDirectory include ActiveModel::Validations - attr_accessor :slug, :pages, :directories + attr_accessor :slug, :pages validates :slug, presence: true - def initialize(slug, pages = [], directories = []) + def initialize(slug, pages = []) @slug = slug @pages = pages - @directories = directories end # Relative path to the partial to be used when rendering collections diff --git a/spec/models/wiki_directory_spec.rb b/spec/models/wiki_directory_spec.rb index fac70f8d3c7..1caaa557085 100644 --- a/spec/models/wiki_directory_spec.rb +++ b/spec/models/wiki_directory_spec.rb @@ -8,10 +8,9 @@ RSpec.describe WikiDirectory, models: true do end describe '#initialize' do - context 'when there are pages and directories' do + context 'when there are pages' do let(:pages) { [build(:wiki_page)] } - let(:other_directories) { [build(:wiki_directory)] } - let(:directory) { WikiDirectory.new('/path_up_to/dir', pages, other_directories) } + let(:directory) { WikiDirectory.new('/path_up_to/dir', pages) } it 'sets the slug attribute' do expect(directory.slug).to eq('/path_up_to/dir') @@ -20,13 +19,9 @@ RSpec.describe WikiDirectory, models: true do it 'sets the pages attribute' do expect(directory.pages).to eq(pages) end - - it 'sets the directories attribute' do - expect(directory.directories).to eq(other_directories) - end end - context 'when there are no pages or directories' do + context 'when there are no pages' do let(:directory) { WikiDirectory.new('/path_up_to/dir') } it 'sets the slug attribute' do @@ -36,10 +31,6 @@ RSpec.describe WikiDirectory, models: true do it 'sets the pages attribute to an empty array' do expect(directory.pages).to eq([]) end - - it 'sets the directories attribute to an empty array' do - expect(directory.directories).to eq([]) - end end end -- cgit v1.2.1 From 7a109402a866db1c84ef9af1c14e148ec944aa22 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Fri, 20 Jan 2017 21:57:01 +0800 Subject: Prefer service object over after_save hook Closes #26921 --- app/controllers/admin/runners_controller.rb | 6 +++--- app/controllers/projects/runners_controller.rb | 6 +++--- app/models/ci/runner.rb | 8 -------- app/services/ci/update_runner_service.rb | 15 +++++++++++++++ lib/ci/api/runners.rb | 20 ++++++++++++-------- spec/models/ci/runner_spec.rb | 2 +- spec/services/ci/update_runner_service_spec.rb | 18 ++++++++++++++++++ 7 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 app/services/ci/update_runner_service.rb create mode 100644 spec/services/ci/update_runner_service_spec.rb diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 7345c91f67d..348641e5ecb 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -13,7 +13,7 @@ class Admin::RunnersController < Admin::ApplicationController end def update - if @runner.update_attributes(runner_params) + if Ci::UpdateRunnerService.new(@runner).update(runner_params) respond_to do |format| format.js format.html { redirect_to admin_runner_path(@runner) } @@ -31,7 +31,7 @@ class Admin::RunnersController < Admin::ApplicationController end def resume - if @runner.update_attributes(active: true) + if Ci::UpdateRunnerService.new(@runner).update(active: true) redirect_to admin_runners_path, notice: 'Runner was successfully updated.' else redirect_to admin_runners_path, alert: 'Runner was not updated.' @@ -39,7 +39,7 @@ class Admin::RunnersController < Admin::ApplicationController end def pause - if @runner.update_attributes(active: false) + if Ci::UpdateRunnerService.new(@runner).update(active: false) redirect_to admin_runners_path, notice: 'Runner was successfully updated.' else redirect_to admin_runners_path, alert: 'Runner was not updated.' diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 53c36635efe..ff75c408beb 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -16,7 +16,7 @@ class Projects::RunnersController < Projects::ApplicationController end def update - if @runner.update_attributes(runner_params) + if Ci::UpdateRunnerService.new(@runner).update(runner_params) redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' else render 'edit' @@ -32,7 +32,7 @@ class Projects::RunnersController < Projects::ApplicationController end def resume - if @runner.update_attributes(active: true) + if Ci::UpdateRunnerService.new(@runner).update(active: true) redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' else redirect_to runner_path(@runner), alert: 'Runner was not updated.' @@ -40,7 +40,7 @@ class Projects::RunnersController < Projects::ApplicationController end def pause - if @runner.update_attributes(active: false) + if Ci::UpdateRunnerService.new(@runner).update(active: false) redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' else redirect_to runner_path(@runner), alert: 'Runner was not updated.' diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 6e58a1878c8..b4760b5baaa 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -22,8 +22,6 @@ module Ci scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :ordered, ->() { order(id: :desc) } - after_save :tick_runner_queue, if: :form_editable_changed? - scope :owned_or_shared, ->(project_id) do joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id') .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) @@ -149,12 +147,6 @@ module Ci "runner:build_queue:#{self.token}" end - def form_editable_changed? - FORM_EDITABLE.any? do |editable| - public_send("#{editable}_changed?") - end - end - def tag_constraints unless has_tags? || run_untagged? errors.add(:tags_list, diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb new file mode 100644 index 00000000000..198b29b3a9d --- /dev/null +++ b/app/services/ci/update_runner_service.rb @@ -0,0 +1,15 @@ +module Ci + class UpdateRunnerService + attr_reader :runner + + def initialize(runner) + @runner = runner + end + + def update(params) + runner.update(params).tap do + runner.tick_runner_queue + end + end + end +end diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index bcc82969eb3..c10858f4c5e 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -28,23 +28,27 @@ module Ci post "register" do required_attributes! [:token] - attributes = attributes_for_keys( - [:description, :tag_list, :run_untagged, :locked] - ) - + project = nil runner = if runner_registration_token_valid? # Create shared runner. Requires admin access - Ci::Runner.create(attributes.merge(is_shared: true)) + Ci::Runner.new(is_shared: true) elsif project = Project.find_by(runners_token: params[:token]) - # Create a specific runner for project. - project.runners.create(attributes) + Ci::Runner.new end return forbidden! unless runner + attributes = attributes_for_keys( + [:description, :tag_list, :run_untagged, :locked] + ).merge(get_runner_version_from_params || {}) + + Ci::UpdateRunnerService.new(runner).update(attributes) + + # Assign the specific runner for the project + project.runners << runner if project + if runner.id - runner.update(get_runner_version_from_params) present runner, with: Entities::Runner else not_found! diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 2b856ca7af7..7b993a454b7 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -291,7 +291,7 @@ describe Ci::Runner, models: true do let!(:last_update) { runner.ensure_runner_queue_value } before do - runner.update(description: 'new runner') + Ci::UpdateRunnerService.new(runner).update(description: 'new runner') end it 'sets a new last_update value' do diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb new file mode 100644 index 00000000000..8429881dd15 --- /dev/null +++ b/spec/services/ci/update_runner_service_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Ci::UpdateRunnerService, services: true do + let(:runner) { create(:ci_runner) } + + describe '#update' do + before do + allow(runner).to receive(:tick_runner_queue) + + described_class.new(runner).update(description: 'new runner') + end + + it 'updates the runner and ticking the queue' do + expect(runner.description).to eq('new runner') + expect(runner).to have_received(:tick_runner_queue) + end + end +end -- cgit v1.2.1 From 7a4d723e6ba287fe792dca0a8ddc3d8a77b1876c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 21 Jan 2017 02:38:58 +0800 Subject: Remove the key from the queue when runner is deleted --- app/models/ci/runner.rb | 8 ++++++++ spec/models/ci/runner_spec.rb | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index b4760b5baaa..f30253eefe3 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -38,6 +38,8 @@ module Ci acts_as_taggable + after_destroy :cleanup_runner_queue + # Searches for runners matching the given query. # # This method uses ILIKE on PostgreSQL and LIKE on MySQL. @@ -143,6 +145,12 @@ module Ci private + def cleanup_runner_queue + Gitlab::Redis.with do |redis| + redis.del(runner_queue_key) + end + end + def runner_queue_key "runner:build_queue:#{self.token}" end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 7b993a454b7..6283673d7ae 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -319,6 +319,25 @@ describe Ci::Runner, models: true do end end + describe '#destroy' do + let(:runner) { create(:ci_runner) } + + context 'when there is a tick in the queue' do + let!(:queue_key) { runner.send(:runner_queue_key) } + + before do + runner.tick_runner_queue + runner.destroy + end + + it 'cleans up the queue' do + Gitlab::Redis.with do |redis| + expect(redis.get(queue_key)).to be_nil + end + end + end + end + describe '.assignable_for' do let(:runner) { create(:ci_runner) } let(:project) { create(:project) } -- cgit v1.2.1 From d5fa77d53d73b9d47647443460d2ea95babb52e4 Mon Sep 17 00:00:00 2001 From: Poornima M Date: Wed, 18 Jan 2017 16:08:59 +0530 Subject: Adding links to user & build in Chat Notifications --- .../project_services/chat_message/base_message.rb | 4 ++++ .../project_services/chat_message/build_message.rb | 28 ++++++++++++++++++---- .../project_services/chat_message/issue_message.rb | 4 ++-- .../project_services/chat_message/merge_message.rb | 4 ++-- .../project_services/chat_message/note_message.rb | 9 ++++--- .../26500-informative-slack-notifications.yml | 4 ++++ lib/gitlab/data_builder/build.rb | 10 ++++++++ spec/factories/ci/builds.rb | 12 ++++++++++ spec/factories/commits.rb | 10 ++++++++ spec/lib/gitlab/data_builder/build_spec.rb | 26 ++++++++++++++++++++ .../chat_message/build_message_spec.rb | 28 ++++++++++++++++++---- 11 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 changelogs/unreleased/26500-informative-slack-notifications.yml diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb index a03605d01fb..86d271a3f69 100644 --- a/app/models/project_services/chat_message/base_message.rb +++ b/app/models/project_services/chat_message/base_message.rb @@ -30,5 +30,9 @@ module ChatMessage def attachment_color '#345' end + + def link(text, url) + "[#{text}](#{url})" + end end end diff --git a/app/models/project_services/chat_message/build_message.rb b/app/models/project_services/chat_message/build_message.rb index 53e35cb21bf..c776e0a20c4 100644 --- a/app/models/project_services/chat_message/build_message.rb +++ b/app/models/project_services/chat_message/build_message.rb @@ -7,7 +7,11 @@ module ChatMessage attr_reader :project_name attr_reader :project_url attr_reader :user_name + attr_reader :user_url attr_reader :duration + attr_reader :stage + attr_reader :build_id + attr_reader :build_name def initialize(params) @sha = params[:sha] @@ -17,7 +21,11 @@ module ChatMessage @project_url = params[:project_url] @status = params[:commit][:status] @user_name = params[:commit][:author_name] + @user_url = params[:commit][:author_url] @duration = params[:commit][:duration] + @stage = params[:build_stage] + @build_name = params[:build_name] + @build_id = params[:build_id] end def pretext @@ -35,7 +43,19 @@ module ChatMessage private def message - "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} #{'second'.pluralize(duration)}" + "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_link} #{humanized_status} on build #{build_link} of stage #{stage} in #{duration} #{'second'.pluralize(duration)}" + end + + def build_url + "#{project_url}/builds/#{build_id}" + end + + def build_link + link(build_name, build_url) + end + + def user_link + link(user_name, user_url) end def format(string) @@ -64,11 +84,11 @@ module ChatMessage end def branch_link - "[#{ref}](#{branch_url})" + link(ref, branch_url) end def project_link - "[#{project_name}](#{project_url})" + link(project_name, project_url) end def commit_url @@ -76,7 +96,7 @@ module ChatMessage end def commit_link - "[#{Commit.truncate_sha(sha)}](#{commit_url})" + link(Commit.truncate_sha(sha), commit_url) end end end diff --git a/app/models/project_services/chat_message/issue_message.rb b/app/models/project_services/chat_message/issue_message.rb index 14fd64e5332..b96aca47e65 100644 --- a/app/models/project_services/chat_message/issue_message.rb +++ b/app/models/project_services/chat_message/issue_message.rb @@ -55,11 +55,11 @@ module ChatMessage end def project_link - "[#{project_name}](#{project_url})" + link(project_name, project_url) end def issue_link - "[#{issue_title}](#{issue_url})" + link(issue_title, issue_url) end def issue_title diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb index ab5e8b24167..5e5efca7bec 100644 --- a/app/models/project_services/chat_message/merge_message.rb +++ b/app/models/project_services/chat_message/merge_message.rb @@ -42,7 +42,7 @@ module ChatMessage end def project_link - "[#{project_name}](#{project_url})" + link(project_name, project_url) end def merge_request_message @@ -50,7 +50,7 @@ module ChatMessage end def merge_request_link - "[merge request !#{merge_request_id}](#{merge_request_url})" + link("merge request !#{merge_request_id}", merge_request_url) end def merge_request_url diff --git a/app/models/project_services/chat_message/note_message.rb b/app/models/project_services/chat_message/note_message.rb index ca1d7207034..552113bac29 100644 --- a/app/models/project_services/chat_message/note_message.rb +++ b/app/models/project_services/chat_message/note_message.rb @@ -3,10 +3,9 @@ module ChatMessage attr_reader :message attr_reader :user_name attr_reader :project_name - attr_reader :project_link + attr_reader :project_url attr_reader :note attr_reader :note_url - attr_reader :title def initialize(params) params = HashWithIndifferentAccess.new(params) @@ -69,15 +68,15 @@ module ChatMessage end def description_message - [{ text: format(@note), color: attachment_color }] + [{ text: format(note), color: attachment_color }] end def project_link - "[#{@project_name}](#{@project_url})" + link(project_name, project_url) end def commented_on_message(target, title) - @message = "#{@user_name} [commented on #{target}](#{@note_url}) in #{project_link}: *#{title}*" + @message = "#{user_name} #{link('commented on ' + target, note_url)} in #{project_link}: *#{title}*" end end end diff --git a/changelogs/unreleased/26500-informative-slack-notifications.yml b/changelogs/unreleased/26500-informative-slack-notifications.yml new file mode 100644 index 00000000000..342235424f4 --- /dev/null +++ b/changelogs/unreleased/26500-informative-slack-notifications.yml @@ -0,0 +1,4 @@ +--- +title: Add user & build links in Slack Notifications +merge_request: 8641 +author: Poornima M diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb index 6548e6475c6..f78106f5b10 100644 --- a/lib/gitlab/data_builder/build.rb +++ b/lib/gitlab/data_builder/build.rb @@ -8,6 +8,8 @@ module Gitlab commit = build.pipeline user = build.user + author_url = build_author_url(build.commit, commit) + data = { object_kind: 'build', @@ -43,6 +45,7 @@ module Gitlab message: commit.git_commit_message, author_name: commit.git_author_name, author_email: commit.git_author_email, + author_url: author_url, status: commit.status, duration: commit.duration, started_at: commit.started_at, @@ -62,6 +65,13 @@ module Gitlab data end + + private + + def build_author_url(commit, pipeline) + author = commit.try(:author) + author ? Gitlab::Routing.url_helpers.user_url(author) : "mailto:#{pipeline.git_author_email}" + end end end end diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 0397d5d4001..e4cac0e1058 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -128,5 +128,17 @@ FactoryGirl.define do build.save! end end + + trait :with_commit do + after(:build) do |build| + allow(build).to receive(:commit).and_return build(:commit, :without_author) + end + end + + trait :with_commit_and_author do + after(:build) do |build| + allow(build).to receive(:commit).and_return build(:commit) + end + end end end diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb index ac6eb0a7897..89e260cf65b 100644 --- a/spec/factories/commits.rb +++ b/spec/factories/commits.rb @@ -8,5 +8,15 @@ FactoryGirl.define do initialize_with do new(git_commit, project) end + + after(:build) do |commit| + allow(commit).to receive(:author).and_return build(:author) + end + + trait :without_author do + after(:build) do |commit| + allow(commit).to receive(:author).and_return nil + end + end end end diff --git a/spec/lib/gitlab/data_builder/build_spec.rb b/spec/lib/gitlab/data_builder/build_spec.rb index 6c71e98066b..91c43f2bdc0 100644 --- a/spec/lib/gitlab/data_builder/build_spec.rb +++ b/spec/lib/gitlab/data_builder/build_spec.rb @@ -17,5 +17,31 @@ describe Gitlab::DataBuilder::Build do it { expect(data[:build_allow_failure]).to eq(false) } it { expect(data[:project_id]).to eq(build.project.id) } it { expect(data[:project_name]).to eq(build.project.name_with_namespace) } + + context 'commit author_url' do + context 'when no commit present' do + let(:build) { create(:ci_build) } + + it 'sets to mailing address of git_author_email' do + expect(data[:commit][:author_url]).to eq("mailto:#{build.pipeline.git_author_email}") + end + end + + context 'when commit present but has no author' do + let(:build) { create(:ci_build, :with_commit) } + + it 'sets to mailing address of git_author_email' do + expect(data[:commit][:author_url]).to eq("mailto:#{build.pipeline.git_author_email}") + end + end + + context 'when commit and author are present' do + let(:build) { create(:ci_build, :with_commit_and_author) } + + it 'sets to GitLab user url' do + expect(data[:commit][:author_url]).to eq(Gitlab::Routing.url_helpers.user_url(username: build.commit.author.username)) + end + end + end end end diff --git a/spec/models/project_services/chat_message/build_message_spec.rb b/spec/models/project_services/chat_message/build_message_spec.rb index 50ad5013df9..3bd7ec18ae0 100644 --- a/spec/models/project_services/chat_message/build_message_spec.rb +++ b/spec/models/project_services/chat_message/build_message_spec.rb @@ -11,21 +11,28 @@ describe ChatMessage::BuildMessage do project_name: 'project_name', project_url: 'http://example.gitlab.com', + build_id: 1, + build_name: build_name, + build_stage: stage, commit: { status: status, author_name: 'hacker', + author_url: 'http://example.gitlab.com/hacker', duration: duration, }, } end let(:message) { build_message } + let(:stage) { 'test' } + let(:status) { 'success' } + let(:build_name) { 'rspec' } + let(:duration) { 10 } context 'build succeeded' do let(:status) { 'success' } let(:color) { 'good' } - let(:duration) { 10 } let(:message) { build_message('passed') } it 'returns a message with information about succeeded build' do @@ -38,7 +45,6 @@ describe ChatMessage::BuildMessage do context 'build failed' do let(:status) { 'failed' } let(:color) { 'danger' } - let(:duration) { 10 } it 'returns a message with information about failed build' do expect(subject.pretext).to be_empty @@ -47,11 +53,25 @@ describe ChatMessage::BuildMessage do end end - def build_message(status_text = status) + it 'returns a message with information on build' do + expect(subject.fallback).to include("on build ") + end + + it 'returns a message with stage name' do + expect(subject.fallback).to include("of stage #{stage}") + end + + it 'returns a message with link to author' do + expect(subject.fallback).to include("by ") + end + + def build_message(status_text = status, stage_text = stage, build_text = build_name) ":" \ " Commit " \ " of branch" \ - " by hacker #{status_text} in #{duration} #{'second'.pluralize(duration)}" + " by #{status_text}" \ + " on build " \ + " of stage #{stage_text} in #{duration} #{'second'.pluralize(duration)}" end end -- cgit v1.2.1 From 683097666aa01ef6a5b490be67a4a0d9733152e3 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 30 Jan 2017 01:07:31 -0200 Subject: Add WikiPage.unhyphenize --- app/helpers/wiki_helper.rb | 2 +- app/models/wiki_page.rb | 6 +++++- spec/models/wiki_page_spec.rb | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 76ee632ab6d..3e3f6246fc5 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -7,7 +7,7 @@ module WikiHelper # capitalized name of the page itself. def breadcrumb(page_slug) page_slug.split('/'). - map { |dir_or_page| dir_or_page.gsub(/-+/, ' ').capitalize }. + map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }. join(' / ') end end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 20bd9719b2f..2f4f92846b4 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -34,6 +34,10 @@ class WikiPage flatten end + def self.unhyphenize(name) + name.gsub(/-+/, ' ') + end + def to_key [:slug] end @@ -78,7 +82,7 @@ class WikiPage # The formatted title of this page. def title if @attributes[:title] - @attributes[:title].gsub(/-+/, ' ') + self.class.unhyphenize(@attributes[:title]) else "" end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 109a0499090..579ebac7afb 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -68,6 +68,14 @@ describe WikiPage, models: true do end end + describe '.unhyphenize' do + it 'removes hyphens from a name' do + name = 'a-name--with-hyphens' + + expect(WikiPage.unhyphenize(name)).to eq('a name with hyphens') + end + end + describe "#initialize" do context "when initialized with an existing gollum page" do before do -- cgit v1.2.1 From 89347fb688820667ce55089daa600277796871a5 Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 30 Jan 2017 01:08:36 -0200 Subject: Add merge request number --- changelogs/unreleased/23535-folders-in-wiki-repository.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelogs/unreleased/23535-folders-in-wiki-repository.yml b/changelogs/unreleased/23535-folders-in-wiki-repository.yml index 7361b182a94..05212b608d4 100644 --- a/changelogs/unreleased/23535-folders-in-wiki-repository.yml +++ b/changelogs/unreleased/23535-folders-in-wiki-repository.yml @@ -1,4 +1,4 @@ --- title: Show directory hierarchy when listing wiki pages -merge_request: +merge_request: 8133 author: Alex Braha Stoll -- cgit v1.2.1 From 67cec150cc5a991846a45dffdd699efbb1b65187 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 01:31:42 -0800 Subject: Add controller spec for Profiles::NotificationsController --- .../profiles/notifications_controller_spec.rb | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 spec/controllers/profiles/notifications_controller_spec.rb diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb new file mode 100644 index 00000000000..55acc445e43 --- /dev/null +++ b/spec/controllers/profiles/notifications_controller_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' + +describe Profiles::NotificationsController do + describe 'GET show' do + it 'renders' do + user = create_user + sign_in(user) + + get :show + expect(response).to render_template :show + end + end + + describe 'POST update' do + it 'updates only permitted attributes' do + user = create_user + sign_in(user) + + put :update, user: { notification_email: 'new@example.com', admin: true } + + user.reload + expect(user.notification_email).to eq('new@example.com') + expect(user.admin).to eq(false) + expect(controller).to set_flash[:notice].to('Notification settings saved') + end + + it 'shows an error message if the params are invalid' do + user = create_user + sign_in(user) + + put :update, user: { notification_email: '' } + + expect(user.reload.notification_email).to eq('original@example.com') + expect(controller).to set_flash[:alert].to('Failed to save new settings') + end + end + + def create_user + create(:user) do |user| + user.emails.create(email: 'original@example.com') + user.emails.create(email: 'new@example.com') + user.update(notification_email: 'original@example.com') + user.save! + end + end +end -- cgit v1.2.1 From bd03ca4a8e5b041f84db85f9043aaefd669afb82 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 01:31:49 -0800 Subject: Add notified_of_own_activity column to users table --- ...20170123061730_add_notified_of_own_activity_to_users.rb | 14 ++++++++++++++ db/schema.rb | 1 + 2 files changed, 15 insertions(+) create mode 100644 db/migrate/20170123061730_add_notified_of_own_activity_to_users.rb diff --git a/db/migrate/20170123061730_add_notified_of_own_activity_to_users.rb b/db/migrate/20170123061730_add_notified_of_own_activity_to_users.rb new file mode 100644 index 00000000000..f90637e1e35 --- /dev/null +++ b/db/migrate/20170123061730_add_notified_of_own_activity_to_users.rb @@ -0,0 +1,14 @@ +class AddNotifiedOfOwnActivityToUsers < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + DOWNTIME = false + + def up + add_column_with_default :users, :notified_of_own_activity, :boolean, default: false + end + + def down + remove_column :users, :notified_of_own_activity + end +end diff --git a/db/schema.rb b/db/schema.rb index 5efb4f6595c..4ef8d4bbe10 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1257,6 +1257,7 @@ ActiveRecord::Schema.define(version: 20170130204620) do t.string "organization" t.string "incoming_email_token" t.boolean "authorized_projects_populated" + t.boolean "notified_of_own_activity", default: false, null: false end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree -- cgit v1.2.1 From 0a0207ea91fdbe869ac70c23178b876bcbeb3021 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 01:31:53 -0800 Subject: Add notified_of_own_activity to permitted attributes in Profiles::NotificationsController#update --- app/controllers/profiles/notifications_controller.rb | 2 +- spec/controllers/profiles/notifications_controller_spec.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index b8b71d295f6..a271e2dfc4b 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -17,6 +17,6 @@ class Profiles::NotificationsController < Profiles::ApplicationController end def user_params - params.require(:user).permit(:notification_email) + params.require(:user).permit(:notification_email, :notified_of_own_activity) end end diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb index 55acc445e43..54324cece6c 100644 --- a/spec/controllers/profiles/notifications_controller_spec.rb +++ b/spec/controllers/profiles/notifications_controller_spec.rb @@ -16,10 +16,11 @@ describe Profiles::NotificationsController do user = create_user sign_in(user) - put :update, user: { notification_email: 'new@example.com', admin: true } + put :update, user: { notification_email: 'new@example.com', notified_of_own_activity: true, admin: true } user.reload expect(user.notification_email).to eq('new@example.com') + expect(user.notified_of_own_activity).to eq(true) expect(user.admin).to eq(false) expect(controller).to set_flash[:notice].to('Notification settings saved') end -- cgit v1.2.1 From 3e81bc7b1daec9dfda602165d7e36cf5b6a39e20 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 01:31:57 -0800 Subject: Update NotificationService to respect User#notified_of_own_activity --- app/services/notification_service.rb | 6 +-- spec/services/notification_service_spec.rb | 59 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index b2cc39763f3..5a7d5ef8747 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -217,7 +217,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, note.noteable) recipients = reject_users_without_access(recipients, note.noteable) - recipients.delete(note.author) + recipients.delete(note.author) unless note.author.notified_of_own_activity? recipients = recipients.uniq notify_method = "note_#{note.to_ability_name}_email".to_sym @@ -627,7 +627,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, target) recipients = reject_users_without_access(recipients, target) - recipients.delete(current_user) if skip_current_user + recipients.delete(current_user) if skip_current_user && !current_user.try(:notified_of_own_activity?) recipients.uniq end @@ -636,7 +636,7 @@ class NotificationService recipients = add_labels_subscribers([], project, target, labels: labels) recipients = reject_unsubscribed_users(recipients, target) recipients = reject_users_without_access(recipients, target) - recipients.delete(current_user) + recipients.delete(current_user) unless current_user.notified_of_own_activity? recipients.uniq end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 7cf2cd9968f..839250b7d84 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -146,6 +146,16 @@ describe NotificationService, services: true do should_not_email(@u_lazy_participant) end + it "emails the note author if they've opted into notifications about their activity" do + add_users_with_subscription(note.project, issue) + note.author.notified_of_own_activity = true + reset_delivered_emails! + + notification.new_note(note) + + should_email(note.author) + end + it 'filters out "mentioned in" notes' do mentioned_note = SystemNoteService.cross_reference(mentioned_issue, issue, issue.author) @@ -476,6 +486,20 @@ describe NotificationService, services: true do should_not_email(issue.assignee) end + it "emails the author if they've opted into notifications about their activity" do + issue.author.notified_of_own_activity = true + + notification.new_issue(issue, issue.author) + + should_email(issue.author) + end + + it "doesn't email the author if they haven't opted into notifications about their activity" do + notification.new_issue(issue, issue.author) + + should_not_email(issue.author) + end + it "emails subscribers of the issue's labels" do user_1 = create(:user) user_2 = create(:user) @@ -665,6 +689,19 @@ describe NotificationService, services: true do should_email(subscriber_to_label_2) end + it "emails the current user if they've opted into notifications about their activity" do + subscriber_to_label_2.notified_of_own_activity = true + notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) + + should_email(subscriber_to_label_2) + end + + it "doesn't email the current user if they haven't opted into notifications about their activity" do + notification.relabeled_issue(issue, [group_label_2, label_2], subscriber_to_label_2) + + should_not_email(subscriber_to_label_2) + end + it "doesn't send email to anyone but subscribers of the given labels" do notification.relabeled_issue(issue, [group_label_2, label_2], @u_disabled) @@ -818,6 +855,20 @@ describe NotificationService, services: true do should_not_email(@u_lazy_participant) end + it "emails the author if they've opted into notifications about their activity" do + merge_request.author.notified_of_own_activity = true + + notification.new_merge_request(merge_request, merge_request.author) + + should_email(merge_request.author) + end + + it "doesn't email the author if they haven't opted into notifications about their activity" do + notification.new_merge_request(merge_request, merge_request.author) + + should_not_email(merge_request.author) + end + it "emails subscribers of the merge request's labels" do user_1 = create(:user) user_2 = create(:user) @@ -1013,6 +1064,14 @@ describe NotificationService, services: true do should_not_email(@u_watcher) end + it "notifies the merger when merge_when_build_succeeds is false but they've opted into notifications about their activity" do + merge_request.merge_when_build_succeeds = false + @u_watcher.notified_of_own_activity = true + notification.merge_mr(merge_request, @u_watcher) + + should_email(@u_watcher) + end + it_behaves_like 'participating notifications' do let(:participant) { create(:user, username: 'user-participant') } let(:issuable) { merge_request } -- cgit v1.2.1 From 530d0fda7b97a9a3d8836a36b02e50bc5d408464 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 01:32:00 -0800 Subject: Add checkbox in UI to opt into receiving notifications about your activity --- app/assets/javascripts/profile/profile.js.es6 | 1 + app/views/profiles/notifications/show.html.haml | 5 ++++ .../user_changes_notified_of_own_activity_spec.rb | 32 ++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 spec/features/profiles/user_changes_notified_of_own_activity_spec.rb diff --git a/app/assets/javascripts/profile/profile.js.es6 b/app/assets/javascripts/profile/profile.js.es6 index 5aec9c813fe..81374296522 100644 --- a/app/assets/javascripts/profile/profile.js.es6 +++ b/app/assets/javascripts/profile/profile.js.es6 @@ -25,6 +25,7 @@ bindEvents() { $('.js-preferences-form').on('change.preference', 'input[type=radio]', this.submitForm); $('#user_notification_email').on('change', this.submitForm); + $('#user_notified_of_own_activity').on('change', this.submitForm); $('.update-username').on('ajax:before', this.beforeUpdateUsername); $('.update-username').on('ajax:complete', this.afterUpdateUsername); $('.update-notifications').on('ajax:success', this.onUpdateNotifs); diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 5c5e5940365..51c4e8e5a73 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -34,6 +34,11 @@ .clearfix + = form_for @user, url: profile_notifications_path, method: :put do |f| + %label{ for: 'user_notified_of_own_activity' } + = f.check_box :notified_of_own_activity + %span Receive notifications about your own activity + %hr %h5 Groups (#{@group_notifications.count}) diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb new file mode 100644 index 00000000000..0709f32bf0c --- /dev/null +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +feature 'Profile > Notifications > User changes notified_of_own_activity setting', feature: true, js: true do + let(:user) { create(:user) } + + before do + login_as(user) + end + + scenario 'User opts into receiving notifications about their own activity' do + visit profile_notifications_path + + expect(page).not_to have_checked_field('user[notified_of_own_activity]') + + page.find('#user_notified_of_own_activity').set(true) + + expect(page).to have_content('Notification settings saved') + expect(page).to have_checked_field('user[notified_of_own_activity]') + end + + scenario 'User opts out of receiving notifications about their own activity' do + user.update!(notified_of_own_activity: true) + visit profile_notifications_path + + expect(page).to have_checked_field('user[notified_of_own_activity]') + + page.find('#user_notified_of_own_activity').set(false) + + expect(page).to have_content('Notification settings saved') + expect(page).not_to have_checked_field('user[notified_of_own_activity]') + end +end -- cgit v1.2.1 From 2f17a583934a68eafb87cdabcb4ac3d53135c7ec Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 27 Jan 2017 02:02:50 -0800 Subject: Add changelog entry for option to be notified of your own activity --- changelogs/unreleased/option-to-be-notified-of-own-activity.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/option-to-be-notified-of-own-activity.yml diff --git a/changelogs/unreleased/option-to-be-notified-of-own-activity.yml b/changelogs/unreleased/option-to-be-notified-of-own-activity.yml new file mode 100644 index 00000000000..c2e0410cc33 --- /dev/null +++ b/changelogs/unreleased/option-to-be-notified-of-own-activity.yml @@ -0,0 +1,4 @@ +--- +title: Add option to receive email notifications about your own activity +merge_request: 8836 +author: Richard Macklin -- cgit v1.2.1 From 946efd9fa690de68c6766cba063ff078af8699e1 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Tue, 31 Jan 2017 18:27:02 -0800 Subject: Add missing newline in Profiles::NotificationsController spec --- spec/controllers/profiles/notifications_controller_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb index 54324cece6c..c056ba852f0 100644 --- a/spec/controllers/profiles/notifications_controller_spec.rb +++ b/spec/controllers/profiles/notifications_controller_spec.rb @@ -7,6 +7,7 @@ describe Profiles::NotificationsController do sign_in(user) get :show + expect(response).to render_template :show end end -- cgit v1.2.1 From 4647d13893d84dea5d0863c48a933dcc8a1ba679 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Tue, 31 Jan 2017 18:27:14 -0800 Subject: Use check and uncheck methods from Capybara DSL in user_changes_notified_of_own_activity_spec --- spec/features/profiles/user_changes_notified_of_own_activity_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb index 0709f32bf0c..e05fbb3715c 100644 --- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb +++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb @@ -12,7 +12,7 @@ feature 'Profile > Notifications > User changes notified_of_own_activity setting expect(page).not_to have_checked_field('user[notified_of_own_activity]') - page.find('#user_notified_of_own_activity').set(true) + check 'user[notified_of_own_activity]' expect(page).to have_content('Notification settings saved') expect(page).to have_checked_field('user[notified_of_own_activity]') @@ -24,7 +24,7 @@ feature 'Profile > Notifications > User changes notified_of_own_activity setting expect(page).to have_checked_field('user[notified_of_own_activity]') - page.find('#user_notified_of_own_activity').set(false) + uncheck 'user[notified_of_own_activity]' expect(page).to have_content('Notification settings saved') expect(page).not_to have_checked_field('user[notified_of_own_activity]') -- cgit v1.2.1 From 1ee838190e7f7e93dfe50ba26dde549d0fa1aa4e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 4 Feb 2017 00:21:29 +0800 Subject: No need to tick the queue when creating the runner Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8664/diffs#note_22190504 --- lib/ci/api/runners.rb | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index c10858f4c5e..bcc82969eb3 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -28,27 +28,23 @@ module Ci post "register" do required_attributes! [:token] - project = nil + attributes = attributes_for_keys( + [:description, :tag_list, :run_untagged, :locked] + ) + runner = if runner_registration_token_valid? # Create shared runner. Requires admin access - Ci::Runner.new(is_shared: true) + Ci::Runner.create(attributes.merge(is_shared: true)) elsif project = Project.find_by(runners_token: params[:token]) - Ci::Runner.new + # Create a specific runner for project. + project.runners.create(attributes) end return forbidden! unless runner - attributes = attributes_for_keys( - [:description, :tag_list, :run_untagged, :locked] - ).merge(get_runner_version_from_params || {}) - - Ci::UpdateRunnerService.new(runner).update(attributes) - - # Assign the specific runner for the project - project.runners << runner if project - if runner.id + runner.update(get_runner_version_from_params) present runner, with: Entities::Runner else not_found! -- cgit v1.2.1 From 0e2c96e709161b35daeae2dad3aefcf9c85a75aa Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 3 Feb 2017 20:49:45 -0800 Subject: Use `let` in Profiles::NotificationsController spec --- .../profiles/notifications_controller_spec.rb | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb index c056ba852f0..58caf7999cf 100644 --- a/spec/controllers/profiles/notifications_controller_spec.rb +++ b/spec/controllers/profiles/notifications_controller_spec.rb @@ -1,9 +1,17 @@ require 'spec_helper' describe Profiles::NotificationsController do + let(:user) do + create(:user) do |user| + user.emails.create(email: 'original@example.com') + user.emails.create(email: 'new@example.com') + user.update(notification_email: 'original@example.com') + user.save! + end + end + describe 'GET show' do it 'renders' do - user = create_user sign_in(user) get :show @@ -14,7 +22,6 @@ describe Profiles::NotificationsController do describe 'POST update' do it 'updates only permitted attributes' do - user = create_user sign_in(user) put :update, user: { notification_email: 'new@example.com', notified_of_own_activity: true, admin: true } @@ -27,7 +34,6 @@ describe Profiles::NotificationsController do end it 'shows an error message if the params are invalid' do - user = create_user sign_in(user) put :update, user: { notification_email: '' } @@ -36,13 +42,4 @@ describe Profiles::NotificationsController do expect(controller).to set_flash[:alert].to('Failed to save new settings') end end - - def create_user - create(:user) do |user| - user.emails.create(email: 'original@example.com') - user.emails.create(email: 'new@example.com') - user.update(notification_email: 'original@example.com') - user.save! - end - end end -- cgit v1.2.1 From 7cd260b10727cef0621ecef429ec89e00873b1b7 Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 3 Feb 2017 21:01:45 -0800 Subject: Refactor NotificationService#pipeline_finished to use skip_current_user instead of passing nil for current_user --- app/services/notification_service.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 5a7d5ef8747..e5283720913 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -327,8 +327,9 @@ class NotificationService recipients ||= build_recipients( pipeline, pipeline.project, - nil, # The acting user, who won't be added to recipients - action: pipeline.status).map(&:notification_email) + pipeline.user, + action: pipeline.status, + skip_current_user: false).map(&:notification_email) if recipients.any? mailer.public_send(email_template, pipeline, recipients).deliver_later -- cgit v1.2.1 From 9493791d1212f484217e74757550353a5ef07dcf Mon Sep 17 00:00:00 2001 From: Richard Macklin Date: Fri, 3 Feb 2017 21:03:26 -0800 Subject: Remove `try` from NotificationService#build_recipients After refactoring pipeline_finished to avoid passing `nil` for current_user, we shouldn't need to use `try` here anymore. --- app/services/notification_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index e5283720913..3734e3c4253 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -628,7 +628,7 @@ class NotificationService recipients = reject_unsubscribed_users(recipients, target) recipients = reject_users_without_access(recipients, target) - recipients.delete(current_user) if skip_current_user && !current_user.try(:notified_of_own_activity?) + recipients.delete(current_user) if skip_current_user && !current_user.notified_of_own_activity? recipients.uniq end -- cgit v1.2.1 From 4d3f2ff1f27bc65dbfa9a3ffb6b64fc88a4cd9a0 Mon Sep 17 00:00:00 2001 From: Takuya Noguchi Date: Sat, 4 Feb 2017 03:24:17 +0900 Subject: Update doc for enabling or disabling GitLab CI --- changelogs/unreleased/27656-doc-ci-enable-ci.yml | 4 ++++ doc/ci/enable_or_disable_ci.md | 18 +++++++++--------- doc/ci/img/features_settings.png | Bin 9243 -> 0 bytes doc/ci/img/permissions_settings.png | Bin 0 -> 39194 bytes 4 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/27656-doc-ci-enable-ci.yml delete mode 100644 doc/ci/img/features_settings.png create mode 100644 doc/ci/img/permissions_settings.png diff --git a/changelogs/unreleased/27656-doc-ci-enable-ci.yml b/changelogs/unreleased/27656-doc-ci-enable-ci.yml new file mode 100644 index 00000000000..e6315d683d4 --- /dev/null +++ b/changelogs/unreleased/27656-doc-ci-enable-ci.yml @@ -0,0 +1,4 @@ +--- +title: Update doc for enabling or disabling GitLab CI +merge_request: 8965 +author: Takuya Noguchi diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md index c10f82054e2..7971daf2637 100644 --- a/doc/ci/enable_or_disable_ci.md +++ b/doc/ci/enable_or_disable_ci.md @@ -11,10 +11,10 @@ API. --- -As of GitLab 8.2, GitLab CI is mainly exposed via the `/builds` page of a -project. Disabling GitLab CI in a project does not delete any previous builds. -In fact, the `/builds` page can still be accessed, although it's hidden from -the left sidebar menu. +GitLab CI is exposed via the `/pipelines` and `/builds` pages of a project. +Disabling GitLab CI in a project does not delete any previous builds. +In fact, the `/pipelines` and `/builds` pages can still be accessed, although +it's hidden from the left sidebar menu. GitLab CI is enabled by default on new installations and can be disabled either individually under each project's settings, or site-wide by modifying the @@ -23,12 +23,12 @@ respectively. ### Per-project user setting -The setting to enable or disable GitLab CI can be found with the name **Builds** -under the **Features** area of a project's settings along with **Issues**, -**Merge Requests**, **Wiki** and **Snippets**. Select or deselect the checkbox -and hit **Save** for the settings to take effect. +The setting to enable or disable GitLab CI can be found with the name **Pipelines** +under the **Sharing & Permissions** area of a project's settings along with +**Merge Requests**. Choose one of **Disabled**, **Only team members** and +**Everyone with access** and hit **Save changes** for the settings to take effect. -![Features settings](img/features_settings.png) +![Sharing & Permissions settings](img/permissions_settings.png) --- diff --git a/doc/ci/img/features_settings.png b/doc/ci/img/features_settings.png deleted file mode 100644 index c159253d1c9..00000000000 Binary files a/doc/ci/img/features_settings.png and /dev/null differ diff --git a/doc/ci/img/permissions_settings.png b/doc/ci/img/permissions_settings.png new file mode 100644 index 00000000000..1454c75fd24 Binary files /dev/null and b/doc/ci/img/permissions_settings.png differ -- cgit v1.2.1 From 105154aeaed5dfcd643e4a6955d947e524699e45 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 7 Feb 2017 02:49:37 +0800 Subject: Just pass :services, feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8664#note_22853463 --- spec/services/ci/update_runner_service_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb index 8429881dd15..41372476228 100644 --- a/spec/services/ci/update_runner_service_spec.rb +++ b/spec/services/ci/update_runner_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::UpdateRunnerService, services: true do +describe Ci::UpdateRunnerService, :services do let(:runner) { create(:ci_runner) } describe '#update' do -- cgit v1.2.1 From 80bc66596adee89b423d4da8cafcddf3b98c9678 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 7 Feb 2017 03:05:19 +0800 Subject: Only tick queue if anything is updated Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8664#note_22853522 --- app/services/ci/update_runner_service.rb | 4 ++-- spec/services/ci/update_runner_service_spec.rb | 31 ++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/app/services/ci/update_runner_service.rb b/app/services/ci/update_runner_service.rb index 198b29b3a9d..450ee7da1c9 100644 --- a/app/services/ci/update_runner_service.rb +++ b/app/services/ci/update_runner_service.rb @@ -7,8 +7,8 @@ module Ci end def update(params) - runner.update(params).tap do - runner.tick_runner_queue + runner.update(params).tap do |updated| + runner.tick_runner_queue if updated end end end diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/update_runner_service_spec.rb index 41372476228..e429fcfc72f 100644 --- a/spec/services/ci/update_runner_service_spec.rb +++ b/spec/services/ci/update_runner_service_spec.rb @@ -6,13 +6,36 @@ describe Ci::UpdateRunnerService, :services do describe '#update' do before do allow(runner).to receive(:tick_runner_queue) + end + + context 'with description params' do + let(:params) { { description: 'new runner' } } + + it 'updates the runner and ticking the queue' do + expect(update).to be_truthy + + runner.reload + + expect(runner).to have_received(:tick_runner_queue) + expect(runner.description).to eq('new runner') + end + end + + context 'when params are not valid' do + let(:params) { { run_untagged: false } } + + it 'does not update and give false because it is not valid' do + expect(update).to be_falsey + + runner.reload - described_class.new(runner).update(description: 'new runner') + expect(runner).not_to have_received(:tick_runner_queue) + expect(runner.run_untagged).to be_truthy + end end - it 'updates the runner and ticking the queue' do - expect(runner.description).to eq('new runner') - expect(runner).to have_received(:tick_runner_queue) + def update + described_class.new(runner).update(params) end end end -- cgit v1.2.1 From 345e94fdf9b99a15d65856abd1532eda256e2639 Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Mon, 6 Feb 2017 16:49:23 -0600 Subject: Document types of pipelines --- doc/ci/pipelines.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index f91b9d350f7..590748cf9a7 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -13,6 +13,16 @@ executed. ![Pipelines example](img/pipelines.png) +### Types of Pipelines + +There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline. + +![](/images/direction/cicd/types-of-pipelines.svg) + +1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml` +2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production +3. **Project Pipeline**: Cross-project CI dependencies [triggered via API]((triggers)), particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. + ## Builds Builds are individual runs of [jobs]. Not to be confused with a `build` job or -- cgit v1.2.1 From 6a2eda9d8a8e552b7a45e1eccfc76054911f8242 Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Mon, 6 Feb 2017 17:10:00 -0600 Subject: Add image --- doc/ci/img/types-of-pipelines.svg | 4 ++++ doc/ci/pipelines.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 doc/ci/img/types-of-pipelines.svg diff --git a/doc/ci/img/types-of-pipelines.svg b/doc/ci/img/types-of-pipelines.svg new file mode 100644 index 00000000000..5467ec02c78 --- /dev/null +++ b/doc/ci/img/types-of-pipelines.svg @@ -0,0 +1,4 @@ + + + + diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 590748cf9a7..d099628c012 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -17,7 +17,7 @@ executed. There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline. -![](/images/direction/cicd/types-of-pipelines.svg) +![](img/types-of-pipelines.svg) 1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml` 2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production -- cgit v1.2.1 From e278feb2158cbe9595e2afc9da019d97b40e1cdc Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Mon, 6 Feb 2017 17:20:21 -0600 Subject: Update image --- doc/ci/img/types-of-pipelines.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/img/types-of-pipelines.svg b/doc/ci/img/types-of-pipelines.svg index 5467ec02c78..b63b5f56ba6 100644 --- a/doc/ci/img/types-of-pipelines.svg +++ b/doc/ci/img/types-of-pipelines.svg @@ -1,4 +1,4 @@ - + -- cgit v1.2.1 From 4a38a3bb2fc0abaad175d9287e17998b6c5bb93b Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Mon, 6 Feb 2017 17:30:55 -0600 Subject: Add development workflows --- doc/ci/img/pipelines-goal.svg | 4 ++++ doc/ci/pipelines.md | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 doc/ci/img/pipelines-goal.svg diff --git a/doc/ci/img/pipelines-goal.svg b/doc/ci/img/pipelines-goal.svg new file mode 100644 index 00000000000..a925e2282a4 --- /dev/null +++ b/doc/ci/img/pipelines-goal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index d099628c012..0a9f4631614 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -13,7 +13,7 @@ executed. ![Pipelines example](img/pipelines.png) -### Types of Pipelines +## Types of Pipelines There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline. @@ -23,6 +23,18 @@ There are three types of pipelines that often use the single shorthand of "pipel 2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production 3. **Project Pipeline**: Cross-project CI dependencies [triggered via API]((triggers)), particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. +## Development Workflows + +Pipelines accommodate several development workflows: + +1. **Branch Flow** (e.g. different branch for dev, qa, staging, production) +2. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases) +3. **Fork-based Flow** (e.g. merge requests come from forks) + +Example flow: + +![](img/pipelines-goal.svg) + ## Builds Builds are individual runs of [jobs]. Not to be confused with a `build` job or -- cgit v1.2.1 From 2338aa6a25ae07b6c00841431264fb02f77db36a Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Mon, 6 Feb 2017 17:36:34 -0600 Subject: Add image titles --- doc/ci/pipelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 0a9f4631614..35a80dd2977 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -17,7 +17,7 @@ executed. There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline. -![](img/types-of-pipelines.svg) +![Types of Pipelines](img/types-of-pipelines.svg) 1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml` 2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production @@ -31,7 +31,7 @@ Pipelines accommodate several development workflows: 2. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases) 3. **Fork-based Flow** (e.g. merge requests come from forks) -Example flow: +Example continuous delivery flow: ![](img/pipelines-goal.svg) -- cgit v1.2.1 From c1a37c6032c3a53a79027c25c33660ad018359e4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 8 Feb 2017 22:29:44 +0800 Subject: Use UpdateRunnerService to update runner in API: TODO: Add tests to make sure controllers and API would tick the queue. --- lib/api/runners.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/api/runners.rb b/lib/api/runners.rb index 4816b5ed1b7..4fbd4096533 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -60,8 +60,9 @@ module API put ':id' do runner = get_runner(params.delete(:id)) authenticate_update_runner!(runner) + update_service = Ci::UpdateRunnerService.new(runner) - if runner.update(declared_params(include_missing: false)) + if update_service.update(declared_params(include_missing: false)) present runner, with: Entities::RunnerDetails, current_user: current_user else render_validation_error!(runner) -- cgit v1.2.1 From efa0502386a1868f7120ffd4291175291f0094ed Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 3 Feb 2017 14:28:44 +0100 Subject: Enable grouping and pagination in environmnets API --- .../projects/environments_controller.rb | 4 +++- .../projects/environments_controller_spec.rb | 26 +++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 0ec8f5bd64a..0d1095da6c2 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -16,7 +16,9 @@ class Projects::EnvironmentsController < Projects::ApplicationController format.html format.json do render json: EnvironmentSerializer - .new(project: @project, user: current_user) + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .within_folders .represent(@environments) end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 7ac1d62d1b1..4ec91738b9b 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -3,9 +3,13 @@ require 'spec_helper' describe Projects::EnvironmentsController do include ApiHelpers - let(:environment) { create(:environment) } - let(:project) { environment.project } - let(:user) { create(:user) } + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + + let(:environment) do + create(:environment, name: 'production', + project: project) + end before do project.team << [user, :master] @@ -22,14 +26,20 @@ describe Projects::EnvironmentsController do end end - context 'when requesting JSON response' do + context 'when requesting JSON response for folders' do + before do + create(:environment, project: project, name: 'staging/review-1') + create(:environment, project: project, name: 'staging/review-2') + end + it 'responds with correct JSON' do get :index, environment_params(format: :json) - first_environment = json_response.first - - expect(first_environment).not_to be_empty - expect(first_environment['name']). to eq environment.name + expect(json_response.count).to eq 2 + expect(json_response.first['name']).to eq 'production' + expect(json_response.second['name']).to eq 'staging' + expect(json_response.second['size']).to eq 2 + expect(json_response.second['latest']['name']).to eq 'staging/review-2' end end end -- cgit v1.2.1 From b3309bb2fad36372b1e4821410691fa9f720bbe4 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 3 Feb 2017 19:47:56 +0000 Subject: Adjustments to receive new data schema --- .../environments/components/environment.js.es6 | 35 +--- .../components/environment_item.js.es6 | 211 ++++++++------------- .../environments/stores/environments_store.js.es6 | 168 ++-------------- app/assets/stylesheets/pages/environments.scss | 16 +- .../environments/environment_item_spec.js.es6 | 137 ++++++------- .../environments/environments_store_spec.js.es6 | 48 +---- spec/javascripts/environments/mock_data.js.es6 | 180 +++++------------- 7 files changed, 219 insertions(+), 576 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 91553bda4dc..93f65ba0ea8 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -69,12 +69,10 @@ require('./environment_item'); * Toggles loading property. */ created() { - gl.environmentsService = new EnvironmentsService(this.endpoint); + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const endpoint = `${this.endpoint}?scope=${scope}`; - const scope = this.$options.getQueryParameter('scope'); - if (scope) { - this.store.storeVisibility(scope); - } + gl.environmentsService = new EnvironmentsService(endpoint); this.isLoading = true; @@ -82,6 +80,8 @@ require('./environment_item'); .then(resp => resp.json()) .then((json) => { this.store.storeEnvironments(json); + }) + .then(() => { this.isLoading = false; }) .catch(() => { @@ -165,8 +165,7 @@ require('./environment_item');

- New Environment @@ -174,7 +173,7 @@ require('./environment_item');
+ v-if="!isLoading && state.environments.length > 0"> @@ -187,31 +186,15 @@ require('./environment_item'); -
diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 33a99231315..ae37bc24396 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -15,12 +15,7 @@ require('./environment_terminal_button'); /** * Envrionment Item Component * - * Used in a hierarchical structure to show folders with children - * in a table. - * Recursive component based on [Tree View](https://vuejs.org/examples/tree-view.html) - * - * See this [issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/22539) - * for more information.15 + * Renders a table row for each environment. */ window.gl = window.gl || {}; @@ -45,11 +40,6 @@ require('./environment_terminal_button'); default: () => ({}), }, - toggleRow: { - type: Function, - required: false, - }, - canCreateDeployment: { type: Boolean, required: false, @@ -76,50 +66,9 @@ require('./environment_terminal_button'); type: String, required: false, }, - - }, - - data() { - return { - rowClass: { - 'children-row': this.model['vue-isChildren'], - }, - }; }, computed: { - - /** - * If an item has a `children` entry it means it is a folder. - * Folder items have different behaviours - it is possible to toggle - * them and show their children. - * - * @returns {Boolean|Undefined} - */ - isFolder() { - return this.model.children && this.model.children.length > 0; - }, - - /** - * If an item is inside a folder structure will return true. - * Used for css purposes. - * - * @returns {Boolean|undefined} - */ - isChildren() { - return this.model['vue-isChildren']; - }, - - /** - * Counts the number of environments in each folder. - * Used to show a badge with the counter. - * - * @returns {Number|Undefined} The number of environments for the current folder. - */ - childrenCounter() { - return this.model.children && this.model.children.length; - }, - /** * Verifies if `last_deployment` key exists in the current Envrionment. * This key is required to render most of the html - this method works has @@ -128,8 +77,8 @@ require('./environment_terminal_button'); * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.last_deployment && - !this.$options.isObjectEmpty(this.model.last_deployment)) { + if (this.model.latest.last_deployment && + !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } return false; @@ -142,8 +91,9 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.last_deployment && this.model.last_deployment.manual_actions && - this.model.last_deployment.manual_actions.length > 0; + return this.model.latest.last_deployment && + this.model.latest.last_deployment.manual_actions && + this.model.latest.last_deployment.manual_actions.length > 0; }, /** @@ -163,8 +113,8 @@ require('./environment_terminal_button'); */ canRetry() { return this.hasLastDeploymentKey && - this.model.last_deployment && - this.model.last_deployment.deployable; + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable; }, /** @@ -173,9 +123,9 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.last_deployment && - this.model.last_deployment.deployable && - this.model.last_deployment.deployable !== undefined; + return this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable !== undefined; }, /** @@ -185,7 +135,7 @@ require('./environment_terminal_button'); */ createdDate() { return gl.environmentsList.timeagoInstance.format( - this.model.last_deployment.deployable.created_at, + this.model.latest.last_deployment.deployable.created_at, ); }, @@ -196,7 +146,7 @@ require('./environment_terminal_button'); */ manualActions() { if (this.hasManualActions) { - return this.model.last_deployment.manual_actions.map((action) => { + return this.model.latest.last_deployment.manual_actions.map((action) => { const parsedAction = { name: gl.text.humanize(action.name), play_path: action.play_path, @@ -213,10 +163,10 @@ require('./environment_terminal_button'); * @returns {String} */ userImageAltDescription() { - if (this.model.last_deployment && - this.model.last_deployment.user && - this.model.last_deployment.user.username) { - return `${this.model.last_deployment.user.username}'s avatar'`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.user && + this.model.latest.last_deployment.user.username) { + return `${this.model.latest.last_deployment.user.username}'s avatar'`; } return ''; }, @@ -227,9 +177,9 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitTag() { - if (this.model.last_deployment && - this.model.last_deployment.tag) { - return this.model.last_deployment.tag; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.tag) { + return this.model.latest.last_deployment.tag; } return undefined; }, @@ -240,8 +190,9 @@ require('./environment_terminal_button'); * @returns {Object|Undefined} */ commitRef() { - if (this.model.last_deployment && this.model.last_deployment.ref) { - return this.model.last_deployment.ref; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.ref) { + return this.model.latest.last_deployment.ref; } return undefined; }, @@ -252,10 +203,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitUrl() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.commit_path) { - return this.model.last_deployment.commit.commit_path; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.commit_path) { + return this.model.latest.last_deployment.commit.commit_path; } return undefined; }, @@ -266,10 +217,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitShortSha() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.short_id) { - return this.model.last_deployment.commit.short_id; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.short_id) { + return this.model.latest.last_deployment.commit.short_id; } return undefined; }, @@ -280,10 +231,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ commitTitle() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.title) { - return this.model.last_deployment.commit.title; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.title) { + return this.model.latest.last_deployment.commit.title; } return undefined; }, @@ -294,10 +245,10 @@ require('./environment_terminal_button'); * @returns {Object|Undefined} */ commitAuthor() { - if (this.model.last_deployment && - this.model.last_deployment.commit && - this.model.last_deployment.commit.author) { - return this.model.last_deployment.commit.author; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.author) { + return this.model.latest.last_deployment.commit.author; } return undefined; @@ -309,10 +260,10 @@ require('./environment_terminal_button'); * @returns {String|Undefined} */ retryUrl() { - if (this.model.last_deployment && - this.model.last_deployment.deployable && - this.model.last_deployment.deployable.retry_path) { - return this.model.last_deployment.deployable.retry_path; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.retry_path) { + return this.model.latest.last_deployment.deployable.retry_path; } return undefined; }, @@ -323,7 +274,8 @@ require('./environment_terminal_button'); * @returns {Boolean|Undefined} */ isLastDeployment() { - return this.model.last_deployment && this.model.last_deployment['last?']; + return this.model.latest.last_deployment && + this.model.latest.last_deployment['last?']; }, /** @@ -332,9 +284,9 @@ require('./environment_terminal_button'); * @returns {String} */ buildName() { - if (this.model.last_deployment && - this.model.last_deployment.deployable) { - return `${this.model.last_deployment.deployable.name} #${this.model.last_deployment.deployable.id}`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable) { + return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; } return ''; }, @@ -345,9 +297,9 @@ require('./environment_terminal_button'); * @returns {String} */ deploymentInternalId() { - if (this.model.last_deployment && - this.model.last_deployment.iid) { - return `#${this.model.last_deployment.iid}`; + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.iid) { + return `#${this.model.latest.last_deployment.iid}`; } return ''; }, @@ -358,8 +310,8 @@ require('./environment_terminal_button'); * @returns {Boolean} */ deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.user); + return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); }, /** @@ -369,9 +321,9 @@ require('./environment_terminal_button'); * @returns {Object} */ deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.user)) { - return this.model.last_deployment.user; + if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { + return this.model.latest.last_deployment.user; } return {}; }, @@ -384,9 +336,9 @@ require('./environment_terminal_button'); * @returns {Boolean} */ shouldRenderBuildName() { - return !this.isFolder && - !this.$options.isObjectEmpty(this.model.last_deployment) && - !this.$options.isObjectEmpty(this.model.last_deployment.deployable); + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, /** @@ -397,9 +349,9 @@ require('./environment_terminal_button'); * @returns {Boolean} */ shouldRenderDeploymentID() { - return !this.isFolder && - !this.$options.isObjectEmpty(this.model.last_deployment) && - this.model.last_deployment.iid !== undefined; + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + this.model.latest.last_deployment.iid !== undefined; }, }, @@ -420,16 +372,16 @@ require('./environment_terminal_button'); template: ` - -
+ + :href="model.latest.environment_path"> {{model.name}} - + - - + + @@ -437,9 +389,9 @@ require('./environment_terminal_button'); - {{childrenCounter}} + {{model.size}} - + @@ -447,7 +399,7 @@ require('./environment_terminal_button'); {{deploymentInternalId}} - + by + :href="model.latest.last_deployment.deployable.build_path"> {{buildName}} -
+
-

+

No deployments yet

- {{createdDate}} -
+
-
+ :external-url="model.latest.external_url">
+ :stop-url="model.latest.stop_path">
-
+ :terminal-path="model.latest.terminal_path">
diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 9b4090100da..a533b8b61d6 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -10,181 +10,41 @@ this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; - this.state.visibility = 'available'; this.state.filteredEnvironments = []; return this; }, /** - * In order to display a tree view we need to modify the received - * data in to a tree structure based on `environment_type` - * sorted alphabetically. - * In each children a `vue-` property will be added. This property will be - * used to know if an item is a children mostly for css purposes. This is - * needed because the children row is a fragment instance and therfore does - * not accept non-prop attributes. * + * Stores the received environments. * - * @example - * it will transform this: - * [ - * { name: "environment", environment_type: "review" }, - * { name: "environment_1", environment_type: null } - * { name: "environment_2, environment_type: "review" } - * ] - * into this: - * [ - * { name: "review", children: - * [ - * { name: "environment", environment_type: "review", vue-isChildren: true}, - * { name: "environment_2", environment_type: "review", vue-isChildren: true} - * ] - * }, - * {name: "environment_1", environment_type: null} - * ] + * Each environment has the following schema + * { name: String, size: Number, latest: Object } * + * If the `size` is bigger than 1, it means it should be rendered as a folder. + * In those cases we add `isFolder` key in order to render it properly. * - * @param {Array} environments List of environments. - * @returns {Array} Tree structured array with the received environments. + * @param {Array} environments + * @returns {Array} */ storeEnvironments(environments = []) { - this.state.stoppedCounter = this.countByState(environments, 'stopped'); - this.state.availableCounter = this.countByState(environments, 'available'); - - const environmentsTree = environments.reduce((acc, environment) => { - if (environment.environment_type !== null) { - const occurs = acc.filter(element => element.children && - element.name === environment.environment_type); - - environment['vue-isChildren'] = true; - - if (occurs.length) { - acc[acc.indexOf(occurs[0])].children.push(environment); - acc[acc.indexOf(occurs[0])].children.slice().sort(this.sortByName); - } else { - acc.push({ - name: environment.environment_type, - children: [environment], - isOpen: false, - 'vue-isChildren': environment['vue-isChildren'], - }); - } - } else { - acc.push(environment); - } - - return acc; - }, []).slice().sort(this.sortByName); - - this.state.environments = environmentsTree; - - this.filterEnvironmentsByVisibility(this.state.environments); - - return environmentsTree; - }, - - storeVisibility(visibility) { - this.state.visibility = visibility; - }, - /** - * Given the visibility prop provided by the url query parameter and which - * changes according to the active tab we need to filter which environments - * should be visible. - * - * The environments array is a recursive tree structure and we need to filter - * both root level environments and children environments. - * - * In order to acomplish that, both `filterState` and `filterEnvironmentsByVisibility` - * functions work together. - * The first one works as the filter that verifies if the given environment matches - * the given state. - * The second guarantees both root level and children elements are filtered as well. - * - * Given array of environments will return only - * the environments that match the state stored. - * - * @param {Array} array - * @return {Array} - */ - filterEnvironmentsByVisibility(arr) { - const filteredEnvironments = arr.map((item) => { - if (item.children) { - const filteredChildren = this.filterEnvironmentsByVisibility( - item.children, - ).filter(Boolean); - - if (filteredChildren.length) { - item.children = filteredChildren; - return item; - } - } - - return this.filterState(this.state.visibility, item); - }).filter(Boolean); - - this.state.filteredEnvironments = filteredEnvironments; - return filteredEnvironments; - }, - - /** - * Given the state and the environment, - * returns only if the environment state matches the one provided. - * - * @param {String} state - * @param {Object} environment - * @return {Object} - */ - filterState(state, environment) { - return environment.state === state && environment; - }, - - /** - * Toggles folder open property given the environment type. - * - * @param {String} envType - * @return {Array} - */ - toggleFolder(envType) { - const environments = this.state.environments; - - const environmentsCopy = environments.map((env) => { - if (env['vue-isChildren'] && env.name === envType) { - env.isOpen = !env.isOpen; + const filteredEnvironments = environments.map((env) => { + if (env.size > 1) { + return Object.assign({}, env, { isFolder: true }); } return env; }); - this.state.environments = environmentsCopy; + this.state.environments = filteredEnvironments; - return environmentsCopy; + return filteredEnvironments; }, - /** - * Given an array of environments, returns the number of environments - * that have the given state. - * - * @param {Array} environments - * @param {String} state - * @returns {Number} - */ - countByState(environments, state) { - return environments.filter(env => env.state === state).length; + storeCounts() { + //TODO }, - /** - * Sorts the two objects provided by their name. - * - * @param {Object} a - * @param {Object} b - * @returns {Number} - */ - sortByName(a, b) { - const nameA = a.name.toUpperCase(); - const nameB = b.name.toUpperCase(); - - return nameA < nameB ? -1 : nameA > nameB ? 1 : 0; // eslint-disable-line - }, }; })(); diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 778ef01430e..1d4d85ba6de 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -110,17 +110,19 @@ } } - .children-row .environment-name { - margin-left: 17px; - margin-right: -17px; - } - .folder-icon { - padding: 0 5px 0 0; + margin-right: 3px; + color: $gl-text-color-secondary; + + .fa:nth-child(1) { + margin-right: 3px; + } } .folder-name { cursor: pointer; + text-decoration: none; + color: $gl-text-color-secondary; } } @@ -135,4 +137,4 @@ margin-right: 0; } } -} \ No newline at end of file +} diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index d87cc0996c9..14478f1401d 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -14,33 +14,13 @@ describe('Environment item', () => { beforeEach(() => { mockItem = { name: 'review', - children: [ - { - name: 'review-app', - id: 1, - state: 'available', - external_url: '', - last_deployment: {}, - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', - }, - { - name: 'production', - id: 2, - state: 'available', - external_url: '', - last_deployment: {}, - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', - }, - ], + size: 3 }; component = new window.gl.environmentsList.EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: mockItem, - toggleRow: () => {}, canCreateDeployment: false, canReadEnvironment: true, }, @@ -53,7 +33,7 @@ describe('Environment item', () => { }); it('Should render the number of children in a badge', () => { - expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.children.length); + expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(mockItem.size); }); }); @@ -63,38 +43,23 @@ describe('Environment item', () => { beforeEach(() => { environment = { - id: 31, name: 'production', - state: 'stopped', - external_url: 'http://external.com', - environment_type: null, - last_deployment: { - id: 66, - iid: 6, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_path: 'root/ci-folders/tree/master', - }, - tag: true, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { + size: 1, + latest: { + state: 'stopped', + external_url: 'http://external.com', + environment_type: null, + last_deployment: { + id: 66, + iid: 6, + sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', + ref: { + name: 'master', + ref_path: 'root/ci-folders/tree/master', + }, + tag: true, + 'last?': true, + user: { name: 'Administrator', username: 'root', id: 1, @@ -102,34 +67,50 @@ describe('Environment item', () => { avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', web_url: 'http://localhost:3000/root', }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1279, - name: 'deploy', - build_path: '/root/ci-folders/builds/1279', - retry_path: '/root/ci-folders/builds/1279/retry', - created_at: '2016-11-29T18:11:58.430Z', - updated_at: '2016-11-29T18:11:58.430Z', - }, - manual_actions: [ - { - name: 'action', - play_path: '/play', + commit: { + id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', + short_id: '500aabcb', + title: 'Update .gitlab-ci.yml', + author_name: 'Administrator', + author_email: 'admin@example.com', + created_at: '2016-11-07T18:28:13.000+00:00', + message: 'Update .gitlab-ci.yml', + author: { + name: 'Administrator', + username: 'root', + id: 1, + state: 'active', + avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', + web_url: 'http://localhost:3000/root', + }, + commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', }, - ], + deployable: { + id: 1279, + name: 'deploy', + build_path: '/root/ci-folders/builds/1279', + retry_path: '/root/ci-folders/builds/1279/retry', + created_at: '2016-11-29T18:11:58.430Z', + updated_at: '2016-11-29T18:11:58.430Z', + }, + manual_actions: [ + { + name: 'action', + play_path: '/play', + }, + ], + }, + 'stop_action?': true, + environment_path: 'root/ci-folders/environments/31', + created_at: '2016-11-07T11:11:16.525Z', + updated_at: '2016-11-10T15:55:58.778Z', }, - 'stop_action?': true, - environment_path: 'root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-10T15:55:58.778Z', }; component = new window.gl.environmentsList.EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: environment, - toggleRow: () => {}, canCreateDeployment: true, canReadEnvironment: true, }, @@ -144,7 +125,7 @@ describe('Environment item', () => { it('should render deployment internal id', () => { expect( component.$el.querySelector('.deployment-column span').textContent, - ).toContain(environment.last_deployment.iid); + ).toContain(environment.latest.last_deployment.iid); expect( component.$el.querySelector('.deployment-column span').textContent, @@ -154,7 +135,7 @@ describe('Environment item', () => { it('should render last deployment date', () => { const timeagoInstance = new timeago(); // eslint-disable-line const formatedDate = timeagoInstance.format( - environment.last_deployment.deployable.created_at, + environment.latest.last_deployment.deployable.created_at, ); expect( @@ -166,7 +147,7 @@ describe('Environment item', () => { it('should render user avatar with link to profile', () => { expect( component.$el.querySelector('.js-deploy-user-container').getAttribute('href'), - ).toEqual(environment.last_deployment.user.web_url); + ).toEqual(environment.latest.last_deployment.user.web_url); }); }); @@ -174,13 +155,13 @@ describe('Environment item', () => { it('Should link to build url provided', () => { expect( component.$el.querySelector('.build-link').getAttribute('href'), - ).toEqual(environment.last_deployment.deployable.build_path); + ).toEqual(environment.latest.last_deployment.deployable.build_path); }); it('Should render deployable name and id', () => { expect( component.$el.querySelector('.build-link').getAttribute('href'), - ).toEqual(environment.last_deployment.deployable.build_path); + ).toEqual(environment.latest.last_deployment.deployable.build_path); }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index 9a8300d3832..d073e120290 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -20,50 +20,10 @@ require('./mock_data'); gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); }); - it('should count stopped environments and save the count in the state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(1); - }); - - it('should count available environments and save the count in the state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(3); - }); - - it('should store environments with same environment_type as sibilings', () => { - expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(3); - - const parentFolder = gl.environmentsList.EnvironmentsStore.state.environments - .filter(env => env.children && env.children.length > 0); - - expect(parentFolder[0].children.length).toBe(2); - expect(parentFolder[0].children[0].environment_type).toBe('review'); - expect(parentFolder[0].children[1].environment_type).toBe('review'); - expect(parentFolder[0].children[0].name).toBe('test-environment'); - expect(parentFolder[0].children[1].name).toBe('test-environment-1'); - }); - - it('should sort the environments alphabetically', () => { - const { environments } = gl.environmentsList.EnvironmentsStore.state; - - expect(environments[0].name).toBe('production'); - expect(environments[1].name).toBe('review'); - expect(environments[1].children[0].name).toBe('test-environment'); - expect(environments[1].children[1].name).toBe('test-environment-1'); - expect(environments[2].name).toBe('review_app'); - }); - }); - - describe('toggleFolder', () => { - beforeEach(() => { - gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); - }); - - it('should toggle the open property for the given environment', () => { - gl.environmentsList.EnvironmentsStore.toggleFolder('review'); - - const { environments } = gl.environmentsList.EnvironmentsStore.state; - const environment = environments.filter(env => env['vue-isChildren'] === true && env.name === 'review'); - - expect(environment[0].isOpen).toBe(true); + it('should store environments', () => { + expect( + gl.environmentsList.EnvironmentsStore.state.environments.length + ).toBe(environmentsList.length); }); }); }); diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index 80e1cbc6f4d..91595c049b0 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -1,153 +1,59 @@ const environmentsList = [ { - id: 31, - name: 'production', - state: 'available', - external_url: 'https://www.gitlab.com', - environment_type: null, - last_deployment: { - id: 64, - iid: 5, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_url: 'http://localhost:3000/root/ci-folders/tree/master', - }, - tag: false, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1278, - name: 'build', - build_path: '/root/ci-folders/builds/1278', - retry_path: '/root/ci-folders/builds/1278/retry', - }, - manual_actions: [], + name: 'DEV', + size: 1, + latest: { + id: 7, + name: 'DEV', + state: 'available', + external_url: null, + environment_type: null, + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/7', + stop_path: '/root/review-app/environments/7/stop', + created_at: '2017-01-31T10:53:46.894Z', + updated_at: '2017-01-31T10:53:46.894Z', }, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', }, { - id: 32, - name: 'review_app', - state: 'stopped', - external_url: 'https://www.gitlab.com', - environment_type: null, - last_deployment: { - id: 64, - iid: 5, - sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - ref: { - name: 'master', - ref_url: 'http://localhost:3000/root/ci-folders/tree/master', - }, - tag: false, - 'last?': true, - user: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit: { - id: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - short_id: '500aabcb', - title: 'Update .gitlab-ci.yml', - author_name: 'Administrator', - author_email: 'admin@example.com', - created_at: '2016-11-07T18:28:13.000+00:00', - message: 'Update .gitlab-ci.yml', - author: { - name: 'Administrator', - username: 'root', - id: 1, - state: 'active', - avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon', - web_url: 'http://localhost:3000/root', - }, - commit_path: '/root/ci-folders/tree/500aabcb17c97bdcf2d0c410b70cb8556f0362dd', - }, - deployable: { - id: 1278, - name: 'build', - build_path: '/root/ci-folders/builds/1278', - retry_path: '/root/ci-folders/builds/1278/retry', - }, - manual_actions: [], + name: 'build', + size: 5, + latest: { + id: 12, + name: 'build/update-README', + state: 'available', + external_url: null, + environment_type: 'build', + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/12', + stop_path: '/root/review-app/environments/12/stop', + created_at: '2017-02-01T19:42:18.400Z', + updated_at: '2017-02-01T19:42:18.400Z', }, - 'stop_action?': false, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', - }, - { - id: 33, - name: 'test-environment', - state: 'available', - environment_type: 'review', - last_deployment: null, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', - }, - { - id: 34, - name: 'test-environment-1', - state: 'available', - environment_type: 'review', - last_deployment: null, - 'stop_action?': true, - environment_path: '/root/ci-folders/environments/31', - created_at: '2016-11-07T11:11:16.525Z', - updated_at: '2016-11-07T11:11:16.525Z', }, ]; window.environmentsList = environmentsList; const environment = { - id: 4, - name: 'production', - state: 'available', - external_url: 'http://production.', - environment_type: null, - last_deployment: {}, - 'stop_action?': false, - environment_path: '/root/review-app/environments/4', - stop_path: '/root/review-app/environments/4/stop', - created_at: '2016-12-16T11:51:04.690Z', - updated_at: '2016-12-16T12:04:51.133Z', + name: 'DEV', + size: 1, + latest: { + id: 7, + name: 'DEV', + state: 'available', + external_url: null, + environment_type: null, + last_deployment: null, + 'stop_action?': false, + environment_path: '/root/review-app/environments/7', + stop_path: '/root/review-app/environments/7/stop', + created_at: '2017-01-31T10:53:46.894Z', + updated_at: '2017-01-31T10:53:46.894Z', + }, }; window.environment = environment; -- cgit v1.2.1 From 2aeb45bdb55a634a490bf535242a6f8c10aaa938 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 4 Feb 2017 10:38:16 +0100 Subject: Add support for environment scopes in controller --- .../projects/environments_controller.rb | 19 ++++--- .../projects/environments_controller_spec.rb | 59 ++++++++++++++++++---- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 0d1095da6c2..34b081b3e41 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -9,17 +9,22 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :verify_api_request!, only: :terminal_websocket_authorize def index - @scope = params[:scope] - @environments = project.environments.includes(:last_deployment) + @environments = project.environments + .includes(:last_deployment) + .with_state(params[:scope] || :available) respond_to do |format| format.html format.json do - render json: EnvironmentSerializer - .new(project: @project, user: @current_user) - .with_pagination(request, response) - .within_folders - .represent(@environments) + render json: { + environments: EnvironmentSerializer + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .within_folders + .represent(@environments), + available_count: project.environments.available.count, + stopped_count: project.environments.stopped.count + } end end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 4ec91738b9b..84d119f1867 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -7,8 +7,7 @@ describe Projects::EnvironmentsController do let(:project) { create(:empty_project) } let(:environment) do - create(:environment, name: 'production', - project: project) + create(:environment, name: 'production', project: project) end before do @@ -28,18 +27,56 @@ describe Projects::EnvironmentsController do context 'when requesting JSON response for folders' do before do - create(:environment, project: project, name: 'staging/review-1') - create(:environment, project: project, name: 'staging/review-2') + create(:environment, project: project, + name: 'staging/review-1', + state: :available) + + create(:environment, project: project, + name: 'staging/review-2', + state: :available) + + create(:environment, project: project, + name: 'staging/review-3', + state: :stopped) + end + + let(:environments) { json_response['environments'] } + + context 'when requesting available environments scope' do + before do + get :index, environment_params(format: :json, scope: :available) + end + + it 'responds with a payload describing available environments' do + expect(environments.count).to eq 2 + expect(environments.first['name']).to eq 'production' + expect(environments.second['name']).to eq 'staging' + expect(environments.second['size']).to eq 2 + expect(environments.second['latest']['name']).to eq 'staging/review-2' + end + + it 'contains values describing environment scopes sizes' do + expect(json_response['available_count']).to eq 3 + expect(json_response['stopped_count']).to eq 1 + end end - it 'responds with correct JSON' do - get :index, environment_params(format: :json) + context 'when requesting stopped environments scope' do + before do + get :index, environment_params(format: :json, scope: :stopped) + end - expect(json_response.count).to eq 2 - expect(json_response.first['name']).to eq 'production' - expect(json_response.second['name']).to eq 'staging' - expect(json_response.second['size']).to eq 2 - expect(json_response.second['latest']['name']).to eq 'staging/review-2' + it 'responds with a payload describing stopped environments' do + expect(environments.count).to eq 1 + expect(environments.first['name']).to eq 'staging' + expect(environments.first['size']).to eq 1 + expect(environments.first['latest']['name']).to eq 'staging/review-3' + end + + it 'contains values describing environment scopes sizes' do + expect(json_response['available_count']).to eq 3 + expect(json_response['stopped_count']).to eq 1 + end end end end -- cgit v1.2.1 From 71899e10878455277b7e2ed120d9424489a9d72b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 3 Feb 2017 21:10:50 +0000 Subject: Adjustments for the new response with counters a --- .../environments/components/environment.js.es6 | 8 ++++++-- .../environments/stores/environments_store.js.es6 | 22 ++++++++++++++++++++-- .../environments/environments_store_spec.js.es6 | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 93f65ba0ea8..3e899e5895b 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,6 +7,7 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); +require('../../vue_pagination/index'); (() => { window.gl = window.gl || {}; @@ -79,7 +80,9 @@ require('./environment_item'); return gl.environmentsService.all() .then(resp => resp.json()) .then((json) => { - this.store.storeEnvironments(json); + this.store.storeAvailableCount(json.available_count); + this.store.storeStoppedCount(json.stopped_count); + this.store.storeEnvironments(json.environments); }) .then(() => { this.isLoading = false; @@ -131,7 +134,8 @@ require('./environment_item'); {{state.availableCounter}} -
  • +
  • +
  • Stopped diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index a533b8b61d6..c05f353647c 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -42,8 +42,26 @@ return filteredEnvironments; }, - storeCounts() { - //TODO + /** + * Stores the number of available environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeAvailableCount(count = 0) { + this.state.availableCounter = count; + return count; + }, + + /** + * Stores the number of closed environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeStoppedCount(count = 0) { + this.state.stoppedCounter = count; + return count; }, }; diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index d073e120290..ef4b06dea40 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -22,7 +22,7 @@ require('./mock_data'); it('should store environments', () => { expect( - gl.environmentsList.EnvironmentsStore.state.environments.length + gl.environmentsList.EnvironmentsStore.state.environments.length, ).toBe(environmentsList.length); }); }); -- cgit v1.2.1 From 3c39cea60cede6399e7fae517b9ab3c5c86143a7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 6 Feb 2017 15:32:40 +0000 Subject: Environments folder should be underlined when on hover and on focus like a regular link --- app/assets/stylesheets/pages/environments.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 1d4d85ba6de..606cf501b82 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -121,7 +121,6 @@ .folder-name { cursor: pointer; - text-decoration: none; color: $gl-text-color-secondary; } } -- cgit v1.2.1 From 72f76c4d4981b14d1cc721b1f379832f74a509f8 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:08:44 +0000 Subject: Remove pagination from this MR --- app/assets/javascripts/environments/components/environment.js.es6 | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 3e899e5895b..073fbdffe21 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,7 +7,6 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); -require('../../vue_pagination/index'); (() => { window.gl = window.gl || {}; -- cgit v1.2.1 From acb68ae69e382a7bd949386f14d2d27f6cada086 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:16:11 +0000 Subject: Resolve 500 error --- app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 | 2 +- app/controllers/projects/environments_controller.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 index d3229f9f730..a575ae3e441 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 @@ -6,7 +6,7 @@ Vue.http.interceptors.push((request, next) => { Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; next((response) => { - if (typeof response.data === 'string') { + if (typeof response.data === 'string' && response.status !== 500) { response.data = JSON.parse(response.data); } diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 34b081b3e41..2252ece68ce 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -10,7 +10,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController def index @environments = project.environments - .includes(:last_deployment) .with_state(params[:scope] || :available) respond_to do |format| -- cgit v1.2.1 From 6077dea7b1f6fb857f786d86cd7cf9e5204ec9a9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:18:21 +0000 Subject: Remove store from global namespace, use CSJ instead --- .../environments/components/environment.js.es6 | 12 +-- .../environments/environments_bundle.js.es6 | 7 -- .../environments/stores/environments_store.js.es6 | 120 ++++++++++----------- .../vue_shared/vue_resource_interceptor.js.es6 | 2 +- 4 files changed, 65 insertions(+), 76 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 073fbdffe21..f07b650ca9f 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,18 +7,12 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); require('../services/environments_service'); require('./environment_item'); +const Store = require('../stores/environments_store'); (() => { window.gl = window.gl || {}; gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', { - props: { - store: { - type: Object, - required: true, - default: () => ({}), - }, - }, components: { 'environment-item': gl.environmentsList.EnvironmentItem, @@ -26,9 +20,11 @@ require('./environment_item'); data() { const environmentsData = document.querySelector('#environments-list-view').dataset; + const store = new Store(); return { - state: this.store.state, + store, + state: store.state, visibility: 'available', isLoading: false, cssContainerClass: environmentsData.cssClass, diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6 index 05c59d92fd4..912f50aeec1 100644 --- a/app/assets/javascripts/environments/environments_bundle.js.es6 +++ b/app/assets/javascripts/environments/environments_bundle.js.es6 @@ -1,5 +1,4 @@ window.Vue = require('vue'); -require('./stores/environments_store'); require('./components/environment'); require('../vue_shared/vue_resource_interceptor'); @@ -9,14 +8,8 @@ $(() => { if (gl.EnvironmentsListApp) { gl.EnvironmentsListApp.$destroy(true); } - const Store = gl.environmentsList.EnvironmentsStore; gl.EnvironmentsListApp = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - - propsData: { - store: Store.create(), - }, - }); }); diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index c05f353647c..52bd6b94551 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -1,68 +1,68 @@ -/* eslint-disable no-param-reassign */ -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; +/** + * Environments Store. + * + * Stores received environments, count of stopped environments and count of + * available environments. + */ +class EnvironmentsStore { + constructor() { + this.state = {}; + this.state.environments = []; + this.state.stoppedCounter = 0; + this.state.availableCounter = 0; + this.state.filteredEnvironments = []; - gl.environmentsList.EnvironmentsStore = { - state: {}, + return this; + } - create() { - this.state.environments = []; - this.state.stoppedCounter = 0; - this.state.availableCounter = 0; - this.state.filteredEnvironments = []; + /** + * + * Stores the received environments. + * + * Each environment has the following schema + * { name: String, size: Number, latest: Object } + * + * If the `size` is bigger than 1, it means it should be rendered as a folder. + * In those cases we add `isFolder` key in order to render it properly. + * + * @param {Array} environments + * @returns {Array} + */ + storeEnvironments(environments = []) { + const filteredEnvironments = environments.map((env) => { + if (env.size > 1) { + return Object.assign({}, env, { isFolder: true }); + } - return this; - }, + return env; + }); - /** - * - * Stores the received environments. - * - * Each environment has the following schema - * { name: String, size: Number, latest: Object } - * - * If the `size` is bigger than 1, it means it should be rendered as a folder. - * In those cases we add `isFolder` key in order to render it properly. - * - * @param {Array} environments - * @returns {Array} - */ - storeEnvironments(environments = []) { - const filteredEnvironments = environments.map((env) => { - if (env.size > 1) { - return Object.assign({}, env, { isFolder: true }); - } + this.state.environments = filteredEnvironments; - return env; - }); + return filteredEnvironments; + } - this.state.environments = filteredEnvironments; + /** + * Stores the number of available environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeAvailableCount(count = 0) { + this.state.availableCounter = count; + return count; + } - return filteredEnvironments; - }, + /** + * Stores the number of closed environments. + * + * @param {Number} count = 0 + * @return {Number} + */ + storeStoppedCount(count = 0) { + this.state.stoppedCounter = count; + return count; + } +} - /** - * Stores the number of available environments. - * - * @param {Number} count = 0 - * @return {Number} - */ - storeAvailableCount(count = 0) { - this.state.availableCounter = count; - return count; - }, - - /** - * Stores the number of closed environments. - * - * @param {Number} count = 0 - * @return {Number} - */ - storeStoppedCount(count = 0) { - this.state.stoppedCounter = count; - return count; - }, - - }; -})(); +module.exports = EnvironmentsStore; diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 index a575ae3e441..d3229f9f730 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js.es6 @@ -6,7 +6,7 @@ Vue.http.interceptors.push((request, next) => { Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1; next((response) => { - if (typeof response.data === 'string' && response.status !== 500) { + if (typeof response.data === 'string') { response.data = JSON.parse(response.data); } -- cgit v1.2.1 From 26a951b7ab272b80df1281464aaf656570fe214e Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:35:53 +0000 Subject: Use CJS for tests. Updates expected model in tests --- .../environments/stores/environments_store.js.es6 | 1 - .../environments/environment_item_spec.js.es6 | 8 +++-- .../environments/environment_spec.js.es6 | 18 ++++------- .../environments/environments_store_spec.js.es6 | 37 ++++++++++++---------- spec/javascripts/environments/mock_data.js.es6 | 8 ++--- 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 52bd6b94551..749dd882188 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -10,7 +10,6 @@ class EnvironmentsStore { this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; - this.state.filteredEnvironments = []; return this; } diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index 14478f1401d..5dc7ef5ad76 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -1,7 +1,7 @@ window.timeago = require('vendor/timeago'); require('~/environments/components/environment_item'); -describe('Environment item', () => { +fdescribe('Environment item', () => { preloadFixtures('static/environments/table.html.raw'); beforeEach(() => { loadFixtures('static/environments/table.html.raw'); @@ -14,7 +14,11 @@ describe('Environment item', () => { beforeEach(() => { mockItem = { name: 'review', - size: 3 + size: 3, + isFolder: true, + latest: { + environment_path: 'url', + }, }; component = new window.gl.environmentsList.EnvironmentItem({ diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 87eda136122..8b96f4b09db 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -1,9 +1,8 @@ /* global Vue, environment */ require('~/flash'); -require('~/environments/stores/environments_store'); require('~/environments/components/environment'); -require('./mock_data'); +const { environment } = require('./mock_data'); describe('Environment', () => { preloadFixtures('static/environments/environments.html.raw'); @@ -35,9 +34,6 @@ describe('Environment', () => { it('should render the empty state', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { @@ -56,7 +52,11 @@ describe('Environment', () => { describe('with environments', () => { const environmentsResponseInterceptor = (request, next) => { - next(request.respondWith(JSON.stringify([environment]), { + next(request.respondWith(JSON.stringify({ + environments: [environment], + stopped_count: 1, + available_count: 0, + }), { status: 200, })); }; @@ -74,9 +74,6 @@ describe('Environment', () => { it('should render a table with environments', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { @@ -109,9 +106,6 @@ describe('Environment', () => { it('should render empty state', (done) => { component = new gl.environmentsList.EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), - propsData: { - store: gl.environmentsList.EnvironmentsStore.create(), - }, }); setTimeout(() => { diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index ef4b06dea40..861136c621f 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -1,30 +1,33 @@ -/* global environmentsList */ - -require('~/environments/stores/environments_store'); -require('./mock_data'); +const Store = require('~/environments/stores/environments_store'); +const { environmentsList } = require('./mock_data'); (() => { describe('Store', () => { + let store; + beforeEach(() => { - gl.environmentsList.EnvironmentsStore.create(); + store = new Store(); }); it('should start with a blank state', () => { - expect(gl.environmentsList.EnvironmentsStore.state.environments.length).toBe(0); - expect(gl.environmentsList.EnvironmentsStore.state.stoppedCounter).toBe(0); - expect(gl.environmentsList.EnvironmentsStore.state.availableCounter).toBe(0); + expect(store.state.environments.length).toBe(0); + expect(store.state.stoppedCounter).toBe(0); + expect(store.state.availableCounter).toBe(0); }); - describe('store environments', () => { - beforeEach(() => { - gl.environmentsList.EnvironmentsStore.storeEnvironments(environmentsList); - }); + it('should store environments', () => { + store.storeEnvironments(environmentsList); + expect(store.state.environments.length).toBe(environmentsList.length); + }); + + it('should store available count', () => { + store.storeAvailableCount(2); + expect(store.state.availableCounter).toBe(2); + }); - it('should store environments', () => { - expect( - gl.environmentsList.EnvironmentsStore.state.environments.length, - ).toBe(environmentsList.length); - }); + it('should store stopped count', () => { + store.storeStoppedCount(2); + expect(store.state.stoppedCounter).toBe(2); }); }); })(); diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index 91595c049b0..bdecc95d219 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -1,4 +1,3 @@ - const environmentsList = [ { name: 'DEV', @@ -36,8 +35,6 @@ const environmentsList = [ }, ]; -window.environmentsList = environmentsList; - const environment = { name: 'DEV', size: 1, @@ -56,4 +53,7 @@ const environment = { }, }; -window.environment = environment; +module.exports = { + environmentsList, + environment +}; -- cgit v1.2.1 From 8f3678f1dea1134ebc41f0d519424db673b5f764 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:38:59 +0000 Subject: Use CJS in environments service --- .../environments/components/environment.js.es6 | 6 +++--- .../services/environments_service.js.es6 | 20 ++++---------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index f07b650ca9f..6f84cc625ac 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -5,7 +5,7 @@ window.Vue = require('vue'); window.Vue.use(require('vue-resource')); -require('../services/environments_service'); +const EnvironmentsService = require('../services/environments_service'); require('./environment_item'); const Store = require('../stores/environments_store'); @@ -68,11 +68,11 @@ const Store = require('../stores/environments_store'); const scope = this.$options.getQueryParameter('scope') || this.visibility; const endpoint = `${this.endpoint}?scope=${scope}`; - gl.environmentsService = new EnvironmentsService(endpoint); + const service = new EnvironmentsService(endpoint); this.isLoading = true; - return gl.environmentsService.all() + return service.all() .then(resp => resp.json()) .then((json) => { this.store.storeAvailableCount(json.available_count); diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6 index fab8d977f58..9b33fd02c53 100644 --- a/app/assets/javascripts/environments/services/environments_service.js.es6 +++ b/app/assets/javascripts/environments/services/environments_service.js.es6 @@ -1,20 +1,8 @@ -/* globals Vue */ -/* eslint-disable no-unused-vars, no-param-reassign */ +const Vue = window.Vue = require('vue'); class EnvironmentsService { - - constructor(root) { - Vue.http.options.root = root; - - this.environments = Vue.resource(root); - - Vue.http.interceptors.push((request, next) => { - // needed in order to not break the tests. - if ($.rails) { - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); - } - next(); - }); + constructor(endpoint) { + this.environments = Vue.resource(endpoint); } all() { @@ -22,4 +10,4 @@ class EnvironmentsService { } } -window.EnvironmentsService = EnvironmentsService; +module.exports = EnvironmentsService; -- cgit v1.2.1 From d6ae01da55bc8e0903c9ff13211d2e5dd29bff1f Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 11:52:22 +0000 Subject: Use CJS in all environments components --- .../environments/components/environment.js.es6 | 362 ++++---- .../components/environment_actions.js.es6 | 87 +- .../components/environment_external_url.js.es6 | 36 +- .../components/environment_item.js.es6 | 918 ++++++++++----------- .../components/environment_rollback.js.es6 | 55 +- .../components/environment_stop.js.es6 | 45 +- .../components/environment_terminal_button.js.es6 | 47 +- .../environments/environments_bundle.js.es6 | 5 +- .../services/environments_service.js.es6 | 2 +- .../vue_shared/components/commit.js.es6 | 1 + .../environments/environment_actions_spec.js.es6 | 6 +- .../environment_external_url_spec.js.es6 | 4 +- .../environments/environment_item_spec.js.es6 | 8 +- .../environments/environment_rollback_spec.js.es6 | 8 +- .../environments/environment_spec.js.es6 | 11 +- .../environments/environment_stop_spec.js.es6 | 4 +- 16 files changed, 782 insertions(+), 817 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 6f84cc625ac..11a965fcddf 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -1,205 +1,199 @@ /* eslint-disable no-param-reassign, no-new */ -/* global Vue */ -/* global EnvironmentsService */ /* global Flash */ -window.Vue = require('vue'); -window.Vue.use(require('vue-resource')); +const Vue = require('vue'); +Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -require('./environment_item'); +const EnvironmentItem = require('./environment_item'); const Store = require('../stores/environments_store'); -(() => { - window.gl = window.gl || {}; - - gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', { - - components: { - 'environment-item': gl.environmentsList.EnvironmentItem, - }, - - data() { - const environmentsData = document.querySelector('#environments-list-view').dataset; - const store = new Store(); - - return { - store, - state: store.state, - visibility: 'available', - isLoading: false, - cssContainerClass: environmentsData.cssClass, - endpoint: environmentsData.environmentsDataEndpoint, - canCreateDeployment: environmentsData.canCreateDeployment, - canReadEnvironment: environmentsData.canReadEnvironment, - canCreateEnvironment: environmentsData.canCreateEnvironment, - projectEnvironmentsPath: environmentsData.projectEnvironmentsPath, - projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, - newEnvironmentPath: environmentsData.newEnvironmentPath, - helpPagePath: environmentsData.helpPagePath, - commitIconSvg: environmentsData.commitIconSvg, - playIconSvg: environmentsData.playIconSvg, - terminalIconSvg: environmentsData.terminalIconSvg, - }; +module.exports = Vue.component('environment-component', { + + components: { + 'environment-item': EnvironmentItem, + }, + + data() { + const environmentsData = document.querySelector('#environments-list-view').dataset; + const store = new Store(); + + return { + store, + state: store.state, + visibility: 'available', + isLoading: false, + cssContainerClass: environmentsData.cssClass, + endpoint: environmentsData.environmentsDataEndpoint, + canCreateDeployment: environmentsData.canCreateDeployment, + canReadEnvironment: environmentsData.canReadEnvironment, + canCreateEnvironment: environmentsData.canCreateEnvironment, + projectEnvironmentsPath: environmentsData.projectEnvironmentsPath, + projectStoppedEnvironmentsPath: environmentsData.projectStoppedEnvironmentsPath, + newEnvironmentPath: environmentsData.newEnvironmentPath, + helpPagePath: environmentsData.helpPagePath, + commitIconSvg: environmentsData.commitIconSvg, + playIconSvg: environmentsData.playIconSvg, + terminalIconSvg: environmentsData.terminalIconSvg, + }; + }, + + computed: { + scope() { + return this.$options.getQueryParameter('scope'); }, - computed: { - scope() { - return this.$options.getQueryParameter('scope'); - }, - - canReadEnvironmentParsed() { - return this.$options.convertPermissionToBoolean(this.canReadEnvironment); - }, - - canCreateDeploymentParsed() { - return this.$options.convertPermissionToBoolean(this.canCreateDeployment); - }, - - canCreateEnvironmentParsed() { - return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); - }, + canReadEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canReadEnvironment); }, - /** - * Fetches all the environments and stores them. - * Toggles loading property. - */ - created() { - const scope = this.$options.getQueryParameter('scope') || this.visibility; - const endpoint = `${this.endpoint}?scope=${scope}`; - - const service = new EnvironmentsService(endpoint); - - this.isLoading = true; - - return service.all() - .then(resp => resp.json()) - .then((json) => { - this.store.storeAvailableCount(json.available_count); - this.store.storeStoppedCount(json.stopped_count); - this.store.storeEnvironments(json.environments); - }) - .then(() => { - this.isLoading = false; - }) - .catch(() => { - this.isLoading = false; - new Flash('An error occurred while fetching the environments.', 'alert'); - }); + canCreateDeploymentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateDeployment); }, - /** - * Transforms the url parameter into an object and - * returns the one requested. - * - * @param {String} param - * @returns {String} The value of the requested parameter. - */ - getQueryParameter(parameter) { - return window.location.search.substring(1).split('&').reduce((acc, param) => { - const paramSplited = param.split('='); - acc[paramSplited[0]] = paramSplited[1]; - return acc; - }, {})[parameter]; + canCreateEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); }, - - /** - * Converts permission provided as strings to booleans. - * @param {String} string - * @returns {Boolean} - */ - convertPermissionToBoolean(string) { - return string === 'true'; + }, + + /** + * Fetches all the environments and stores them. + * Toggles loading property. + */ + created() { + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const endpoint = `${this.endpoint}?scope=${scope}`; + + const service = new EnvironmentsService(endpoint); + + this.isLoading = true; + + return service.all() + .then(resp => resp.json()) + .then((json) => { + this.store.storeAvailableCount(json.available_count); + this.store.storeStoppedCount(json.stopped_count); + this.store.storeEnvironments(json.environments); + }) + .then(() => { + this.isLoading = false; + }) + .catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the environments.', 'alert'); + }); + }, + + /** + * Transforms the url parameter into an object and + * returns the one requested. + * + * @param {String} param + * @returns {String} The value of the requested parameter. + */ + getQueryParameter(parameter) { + return window.location.search.substring(1).split('&').reduce((acc, param) => { + const paramSplited = param.split('='); + acc[paramSplited[0]] = paramSplited[1]; + return acc; + }, {})[parameter]; + }, + + /** + * Converts permission provided as strings to booleans. + * @param {String} string + * @returns {Boolean} + */ + convertPermissionToBoolean(string) { + return string === 'true'; + }, + + methods: { + toggleRow(model) { + return this.store.toggleFolder(model.name); }, + }, + + template: ` +
    + - methods: { - toggleRow(model) { - return this.store.toggleFolder(model.name); - }, - }, +
    +
    + +
    - template: ` -
    -
    - - -
    -
    - -
    - -
    -

    - You don't have any environments right now. -

    -

    - Environments are places where code gets deployed, such as staging or production. -
    - - Read more about environments - -

    - - - New Environment - -
    - -
    - - - - - - - - - - - - - - -
    EnvironmentLast deploymentJobCommitUpdated
    -
    +
    + + + + + + + + + + + + + + +
    EnvironmentLast deploymentJobCommitUpdated
    - `, - }); -})(); +
    + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_actions.js.es6 b/app/assets/javascripts/environments/components/environment_actions.js.es6 index ed1c78945db..c5a714d9673 100644 --- a/app/assets/javascripts/environments/components/environment_actions.js.es6 +++ b/app/assets/javascripts/environments/components/environment_actions.js.es6 @@ -1,50 +1,43 @@ -/* global Vue */ - -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.ActionsComponent = Vue.component('actions-component', { - props: { - actions: { - type: Array, - required: false, - default: () => [], - }, - - playIconSvg: { - type: String, - required: false, - }, +const Vue = require('vue'); + +module.exports = Vue.component('actions-component', { + props: { + actions: { + type: Array, + required: false, + default: () => [], }, - template: ` -
    - + playIconSvg: { + type: String, + required: false, + }, + }, + + template: ` +
    + - `, - }); -})(); +
    + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_external_url.js.es6 b/app/assets/javascripts/environments/components/environment_external_url.js.es6 index 28cc0022d17..2599bba3c59 100644 --- a/app/assets/javascripts/environments/components/environment_external_url.js.es6 +++ b/app/assets/javascripts/environments/components/environment_external_url.js.es6 @@ -1,23 +1,19 @@ -/* global Vue */ +/** + * Renders the external url link in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.ExternalUrlComponent = Vue.component('external-url-component', { - props: { - externalUrl: { - type: String, - default: '', - }, +module.exports = Vue.component('external-url-component', { + props: { + externalUrl: { + type: String, + default: '', }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index ae37bc24396..cf79969471e 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -1,489 +1,481 @@ -/* global Vue */ -/* global timeago */ -window.Vue = require('vue'); -window.timeago = require('vendor/timeago'); +const Vue = require('vue'); +const Timeago = require('vendor/timeago'); require('../../lib/utils/text_utility'); require('../../vue_shared/components/commit'); -require('./environment_actions'); -require('./environment_external_url'); -require('./environment_stop'); -require('./environment_rollback'); -require('./environment_terminal_button'); +const ActionsComponent = require('./environment_actions'); +const ExternalUrlComponent = require('./environment_external_url'); +const StopComponent = require('./environment_stop'); +const RollbackComponent = require('./environment_rollback'); +const TerminalButtonComponent = require('./environment_terminal_button'); + +/** + * Envrionment Item Component + * + * Renders a table row for each environment. + */ + +const timeagoInstance = new Timeago(); + +module.exports = Vue.component('environment-item', { + + components: { + 'commit-component': gl.CommitComponent, + 'actions-component': ActionsComponent, + 'external-url-component': ExternalUrlComponent, + 'stop-component': StopComponent, + 'rollback-component': RollbackComponent, + 'terminal-button-component': TerminalButtonComponent, + }, + + props: { + model: { + type: Object, + required: true, + default: () => ({}), + }, -(() => { - /** - * Envrionment Item Component - * - * Renders a table row for each environment. - */ + canCreateDeployment: { + type: Boolean, + required: false, + default: false, + }, - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - window.gl.environmentsList.timeagoInstance = new timeago(); // eslint-disable-line + canReadEnvironment: { + type: Boolean, + required: false, + default: false, + }, - gl.environmentsList.EnvironmentItem = Vue.component('environment-item', { + commitIconSvg: { + type: String, + required: false, + }, - components: { - 'commit-component': gl.CommitComponent, - 'actions-component': gl.environmentsList.ActionsComponent, - 'external-url-component': gl.environmentsList.ExternalUrlComponent, - 'stop-component': gl.environmentsList.StopComponent, - 'rollback-component': gl.environmentsList.RollbackComponent, - 'terminal-button-component': gl.environmentsList.TerminalButtonComponent, + playIconSvg: { + type: String, + required: false, }, - props: { - model: { - type: Object, - required: true, - default: () => ({}), - }, - - canCreateDeployment: { - type: Boolean, - required: false, - default: false, - }, - - canReadEnvironment: { - type: Boolean, - required: false, - default: false, - }, - - commitIconSvg: { - type: String, - required: false, - }, - - playIconSvg: { - type: String, - required: false, - }, - - terminalIconSvg: { - type: String, - required: false, - }, + terminalIconSvg: { + type: String, + required: false, }, + }, - computed: { - /** - * Verifies if `last_deployment` key exists in the current Envrionment. - * This key is required to render most of the html - this method works has - * an helper. - * - * @returns {Boolean} - */ - hasLastDeploymentKey() { - if (this.model.latest.last_deployment && - !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { - return true; - } - return false; - }, - - /** - * Verifies is the given environment has manual actions. - * Used to verify if we should render them or nor. - * - * @returns {Boolean|Undefined} - */ - hasManualActions() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment.manual_actions && - this.model.latest.last_deployment.manual_actions.length > 0; - }, - - /** - * Returns the value of the `stop_action?` key provided in the response. - * - * @returns {Boolean} - */ - hasStopAction() { - return this.model['stop_action?']; - }, - - /** - * Verifies if the `deployable` key is present in `last_deployment` key. - * Used to verify whether we should or not render the rollback partial. - * - * @returns {Boolean|Undefined} - */ - canRetry() { - return this.hasLastDeploymentKey && - this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable; - }, - - /** - * Verifies if the date to be shown is present. - * - * @returns {Boolean|Undefined} - */ - canShowDate() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable !== undefined; - }, - - /** - * Human readable date. - * - * @returns {String} - */ - createdDate() { - return gl.environmentsList.timeagoInstance.format( - this.model.latest.last_deployment.deployable.created_at, - ); - }, - - /** - * Returns the manual actions with the name parsed. - * - * @returns {Array.|Undefined} - */ - manualActions() { - if (this.hasManualActions) { - return this.model.latest.last_deployment.manual_actions.map((action) => { - const parsedAction = { - name: gl.text.humanize(action.name), - play_path: action.play_path, - }; - return parsedAction; - }); - } - return []; - }, - - /** - * Builds the string used in the user image alt attribute. - * - * @returns {String} - */ - userImageAltDescription() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.user && - this.model.latest.last_deployment.user.username) { - return `${this.model.latest.last_deployment.user.username}'s avatar'`; - } - return ''; - }, - - /** - * If provided, returns the commit tag. - * - * @returns {String|Undefined} - */ - commitTag() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.tag) { - return this.model.latest.last_deployment.tag; - } - return undefined; - }, - - /** - * If provided, returns the commit ref. - * - * @returns {Object|Undefined} - */ - commitRef() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.ref) { - return this.model.latest.last_deployment.ref; - } - return undefined; - }, - - /** - * If provided, returns the commit url. - * - * @returns {String|Undefined} - */ - commitUrl() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.commit_path) { - return this.model.latest.last_deployment.commit.commit_path; - } - return undefined; - }, - - /** - * If provided, returns the commit short sha. - * - * @returns {String|Undefined} - */ - commitShortSha() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.short_id) { - return this.model.latest.last_deployment.commit.short_id; - } - return undefined; - }, - - /** - * If provided, returns the commit title. - * - * @returns {String|Undefined} - */ - commitTitle() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.title) { - return this.model.latest.last_deployment.commit.title; - } - return undefined; - }, - - /** - * If provided, returns the commit tag. - * - * @returns {Object|Undefined} - */ - commitAuthor() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.commit && - this.model.latest.last_deployment.commit.author) { - return this.model.latest.last_deployment.commit.author; - } - - return undefined; - }, - - /** - * Verifies if the `retry_path` key is present and returns its value. - * - * @returns {String|Undefined} - */ - retryUrl() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable.retry_path) { - return this.model.latest.last_deployment.deployable.retry_path; - } - return undefined; - }, - - /** - * Verifies if the `last?` key is present and returns its value. - * - * @returns {Boolean|Undefined} - */ - isLastDeployment() { - return this.model.latest.last_deployment && - this.model.latest.last_deployment['last?']; - }, - - /** - * Builds the name of the builds needed to display both the name and the id. - * - * @returns {String} - */ - buildName() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable) { - return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; - } - return ''; - }, - - /** - * Builds the needed string to show the internal id. - * - * @returns {String} - */ - deploymentInternalId() { - if (this.model.latest.last_deployment && - this.model.latest.last_deployment.iid) { - return `#${this.model.latest.last_deployment.iid}`; - } - return ''; - }, - - /** - * Verifies if the user object is present under last_deployment object. - * - * @returns {Boolean} - */ - deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); - }, - - /** - * Returns the user object nested with the last_deployment object. - * Used to render the template. - * - * @returns {Object} - */ - deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { - return this.model.latest.last_deployment.user; - } - return {}; - }, - - /** - * Verifies if the build name column should be rendered by verifing - * if all the information needed is present - * and if the environment is not a folder. - * - * @returns {Boolean} - */ - shouldRenderBuildName() { - return !this.model.isFolder && - !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); - }, - - /** - * Verifies if deplyment internal ID should be rendered by verifing - * if all the information needed is present - * and if the environment is not a folder. - * - * @returns {Boolean} - */ - shouldRenderDeploymentID() { - return !this.model.isFolder && - !this.$options.isObjectEmpty(this.model.latest.last_deployment) && - this.model.latest.last_deployment.iid !== undefined; - }, + computed: { + /** + * Verifies if `last_deployment` key exists in the current Envrionment. + * This key is required to render most of the html - this method works has + * an helper. + * + * @returns {Boolean} + */ + hasLastDeploymentKey() { + if (this.model.latest.last_deployment && + !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { + return true; + } + return false; + }, + + /** + * Verifies is the given environment has manual actions. + * Used to verify if we should render them or nor. + * + * @returns {Boolean|Undefined} + */ + hasManualActions() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment.manual_actions && + this.model.latest.last_deployment.manual_actions.length > 0; + }, + + /** + * Returns the value of the `stop_action?` key provided in the response. + * + * @returns {Boolean} + */ + hasStopAction() { + return this.model['stop_action?']; + }, + + /** + * Verifies if the `deployable` key is present in `last_deployment` key. + * Used to verify whether we should or not render the rollback partial. + * + * @returns {Boolean|Undefined} + */ + canRetry() { + return this.hasLastDeploymentKey && + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable; + }, + + /** + * Verifies if the date to be shown is present. + * + * @returns {Boolean|Undefined} + */ + canShowDate() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable !== undefined; + }, + + /** + * Human readable date. + * + * @returns {String} + */ + createdDate() { + return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); }, /** - * Helper to verify if certain given object are empty. - * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty - * @param {Object} object - * @returns {Bollean} + * Returns the manual actions with the name parsed. + * + * @returns {Array.|Undefined} */ - isObjectEmpty(object) { - for (const key in object) { // eslint-disable-line - if (hasOwnProperty.call(object, key)) { - return false; - } + manualActions() { + if (this.hasManualActions) { + return this.model.latest.last_deployment.manual_actions.map((action) => { + const parsedAction = { + name: gl.text.humanize(action.name), + play_path: action.play_path, + }; + return parsedAction; + }); } - return true; + return []; }, - template: ` - - - - {{model.name}} - - - - - - - - - {{model.name}} - - - - {{model.size}} - - - + /** + * Builds the string used in the user image alt attribute. + * + * @returns {String} + */ + userImageAltDescription() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.user && + this.model.latest.last_deployment.user.username) { + return `${this.model.latest.last_deployment.user.username}'s avatar'`; + } + return ''; + }, + + /** + * If provided, returns the commit tag. + * + * @returns {String|Undefined} + */ + commitTag() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.tag) { + return this.model.latest.last_deployment.tag; + } + return undefined; + }, + + /** + * If provided, returns the commit ref. + * + * @returns {Object|Undefined} + */ + commitRef() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.ref) { + return this.model.latest.last_deployment.ref; + } + return undefined; + }, + + /** + * If provided, returns the commit url. + * + * @returns {String|Undefined} + */ + commitUrl() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.commit_path) { + return this.model.latest.last_deployment.commit.commit_path; + } + return undefined; + }, + + /** + * If provided, returns the commit short sha. + * + * @returns {String|Undefined} + */ + commitShortSha() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.short_id) { + return this.model.latest.last_deployment.commit.short_id; + } + return undefined; + }, + + /** + * If provided, returns the commit title. + * + * @returns {String|Undefined} + */ + commitTitle() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.title) { + return this.model.latest.last_deployment.commit.title; + } + return undefined; + }, + + /** + * If provided, returns the commit tag. + * + * @returns {Object|Undefined} + */ + commitAuthor() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.commit && + this.model.latest.last_deployment.commit.author) { + return this.model.latest.last_deployment.commit.author; + } + + return undefined; + }, + + /** + * Verifies if the `retry_path` key is present and returns its value. + * + * @returns {String|Undefined} + */ + retryUrl() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.retry_path) { + return this.model.latest.last_deployment.deployable.retry_path; + } + return undefined; + }, + + /** + * Verifies if the `last?` key is present and returns its value. + * + * @returns {Boolean|Undefined} + */ + isLastDeployment() { + return this.model.latest.last_deployment && + this.model.latest.last_deployment['last?']; + }, + + /** + * Builds the name of the builds needed to display both the name and the id. + * + * @returns {String} + */ + buildName() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable) { + return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; + } + return ''; + }, - - - {{deploymentInternalId}} + /** + * Builds the needed string to show the internal id. + * + * @returns {String} + */ + deploymentInternalId() { + if (this.model.latest.last_deployment && + this.model.latest.last_deployment.iid) { + return `#${this.model.latest.last_deployment.iid}`; + } + return ''; + }, + + /** + * Verifies if the user object is present under last_deployment object. + * + * @returns {Boolean} + */ + deploymentHasUser() { + return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); + }, + + /** + * Returns the user object nested with the last_deployment object. + * Used to render the template. + * + * @returns {Object} + */ + deploymentUser() { + if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { + return this.model.latest.last_deployment.user; + } + return {}; + }, + + /** + * Verifies if the build name column should be rendered by verifing + * if all the information needed is present + * and if the environment is not a folder. + * + * @returns {Boolean} + */ + shouldRenderBuildName() { + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); + }, + + /** + * Verifies if deplyment internal ID should be rendered by verifing + * if all the information needed is present + * and if the environment is not a folder. + * + * @returns {Boolean} + */ + shouldRenderDeploymentID() { + return !this.model.isFolder && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + this.model.latest.last_deployment.iid !== undefined; + }, + }, + + /** + * Helper to verify if certain given object are empty. + * Should be replaced by lodash _.isEmpty - https://lodash.com/docs/4.17.2#isEmpty + * @param {Object} object + * @returns {Bollean} + */ + isObjectEmpty(object) { + for (const key in object) { // eslint-disable-line + if (hasOwnProperty.call(object, key)) { + return false; + } + } + return true; + }, + + template: ` + + + + {{model.name}} + + + + + - - by - - - + + {{model.name}} - - - - {{buildName}} + + {{model.size}} + + + + + + + {{deploymentInternalId}} + + + + by + + - - - -
    - - + + + + + + {{buildName}} + + + + +
    + + +
    +

    + No deployments yet +

    + + + + + {{createdDate}} + + + + +
    +
    + +
    -

    - No deployments yet -

    - - - - - {{createdDate}} - - - - -
    -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    - - - `, - }); -})(); +
    + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_rollback.js.es6 b/app/assets/javascripts/environments/components/environment_rollback.js.es6 index 5938340a128..daf126eb4e8 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.js.es6 +++ b/app/assets/javascripts/environments/components/environment_rollback.js.es6 @@ -1,33 +1,30 @@ -/* global Vue */ +/** + * Renders Rollback or Re deploy button in environments table depending + * of the provided property `isLastDeployment` + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.RollbackComponent = Vue.component('rollback-component', { - props: { - retryUrl: { - type: String, - default: '', - }, +module.exports = Vue.component('rollback-component', { + props: { + retryUrl: { + type: String, + default: '', + }, - isLastDeployment: { - type: Boolean, - default: true, - }, + isLastDeployment: { + type: Boolean, + default: true, }, + }, - template: ` - - - Re-deploy - - - Rollback - - - `, - }); -})(); + template: ` + + + Re-deploy + + + Rollback + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_stop.js.es6 b/app/assets/javascripts/environments/components/environment_stop.js.es6 index be9526989a0..96983a19568 100644 --- a/app/assets/javascripts/environments/components/environment_stop.js.es6 +++ b/app/assets/javascripts/environments/components/environment_stop.js.es6 @@ -1,27 +1,24 @@ -/* global Vue */ +/** + * Renders the stop "button" that allows stop an environment. + * Used in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.StopComponent = Vue.component('stop-component', { - props: { - stopUrl: { - type: String, - default: '', - }, +module.exports = Vue.component('stop-component', { + props: { + stopUrl: { + type: String, + default: '', }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 b/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 index a3ad063f7cb..481e0d15e7a 100644 --- a/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 +++ b/app/assets/javascripts/environments/components/environment_terminal_button.js.es6 @@ -1,28 +1,25 @@ -/* global Vue */ +/** + * Renders a terminal button to open a web terminal. + * Used in environments table. + */ +const Vue = require('vue'); -window.Vue = require('vue'); - -(() => { - window.gl = window.gl || {}; - window.gl.environmentsList = window.gl.environmentsList || {}; - - gl.environmentsList.TerminalButtonComponent = Vue.component('terminal-button-component', { - props: { - terminalPath: { - type: String, - default: '', - }, - terminalIconSvg: { - type: String, - default: '', - }, +module.exports = Vue.component('terminal-button-component', { + props: { + terminalPath: { + type: String, + default: '', + }, + terminalIconSvg: { + type: String, + default: '', }, + }, - template: ` - - - - `, - }); -})(); + template: ` + + + + `, +}); diff --git a/app/assets/javascripts/environments/environments_bundle.js.es6 b/app/assets/javascripts/environments/environments_bundle.js.es6 index 912f50aeec1..867eba1d384 100644 --- a/app/assets/javascripts/environments/environments_bundle.js.es6 +++ b/app/assets/javascripts/environments/environments_bundle.js.es6 @@ -1,5 +1,4 @@ -window.Vue = require('vue'); -require('./components/environment'); +const EnvironmentsComponent = require('./components/environment'); require('../vue_shared/vue_resource_interceptor'); $(() => { @@ -9,7 +8,7 @@ $(() => { gl.EnvironmentsListApp.$destroy(true); } - gl.EnvironmentsListApp = new gl.environmentsList.EnvironmentsComponent({ + gl.EnvironmentsListApp = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); }); diff --git a/app/assets/javascripts/environments/services/environments_service.js.es6 b/app/assets/javascripts/environments/services/environments_service.js.es6 index 9b33fd02c53..9cef335868e 100644 --- a/app/assets/javascripts/environments/services/environments_service.js.es6 +++ b/app/assets/javascripts/environments/services/environments_service.js.es6 @@ -1,4 +1,4 @@ -const Vue = window.Vue = require('vue'); +const Vue = require('vue'); class EnvironmentsService { constructor(endpoint) { diff --git a/app/assets/javascripts/vue_shared/components/commit.js.es6 b/app/assets/javascripts/vue_shared/components/commit.js.es6 index 7f7c18ddeb1..ff88e236829 100644 --- a/app/assets/javascripts/vue_shared/components/commit.js.es6 +++ b/app/assets/javascripts/vue_shared/components/commit.js.es6 @@ -1,4 +1,5 @@ /* global Vue */ +window.Vue = require('vue'); (() => { window.gl = window.gl || {}; diff --git a/spec/javascripts/environments/environment_actions_spec.js.es6 b/spec/javascripts/environments/environment_actions_spec.js.es6 index b1838045a06..850586f9f3a 100644 --- a/spec/javascripts/environments/environment_actions_spec.js.es6 +++ b/spec/javascripts/environments/environment_actions_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_actions'); +const ActionsComponent = require('~/environments/components/environment_actions'); describe('Actions Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -19,7 +19,7 @@ describe('Actions Component', () => { }, ]; - const component = new window.gl.environmentsList.ActionsComponent({ + const component = new ActionsComponent({ el: document.querySelector('.test-dom-element'), propsData: { actions: actionsMock, @@ -47,7 +47,7 @@ describe('Actions Component', () => { }, ]; - const component = new window.gl.environmentsList.ActionsComponent({ + const component = new ActionsComponent({ el: document.querySelector('.test-dom-element'), propsData: { actions: actionsMock, diff --git a/spec/javascripts/environments/environment_external_url_spec.js.es6 b/spec/javascripts/environments/environment_external_url_spec.js.es6 index a6a587e69f5..393dbb5aae0 100644 --- a/spec/javascripts/environments/environment_external_url_spec.js.es6 +++ b/spec/javascripts/environments/environment_external_url_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_external_url'); +const ExternalUrlComponent = require('~/environments/components/environment_external_url'); describe('External URL Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -8,7 +8,7 @@ describe('External URL Component', () => { it('should link to the provided externalUrl prop', () => { const externalURL = 'https://gitlab.com'; - const component = new window.gl.environmentsList.ExternalUrlComponent({ + const component = new ExternalUrlComponent({ el: document.querySelector('.test-dom-element'), propsData: { externalUrl: externalURL, diff --git a/spec/javascripts/environments/environment_item_spec.js.es6 b/spec/javascripts/environments/environment_item_spec.js.es6 index 5dc7ef5ad76..dd614e50d7f 100644 --- a/spec/javascripts/environments/environment_item_spec.js.es6 +++ b/spec/javascripts/environments/environment_item_spec.js.es6 @@ -1,7 +1,7 @@ window.timeago = require('vendor/timeago'); -require('~/environments/components/environment_item'); +const EnvironmentItem = require('~/environments/components/environment_item'); -fdescribe('Environment item', () => { +describe('Environment item', () => { preloadFixtures('static/environments/table.html.raw'); beforeEach(() => { loadFixtures('static/environments/table.html.raw'); @@ -21,7 +21,7 @@ fdescribe('Environment item', () => { }, }; - component = new window.gl.environmentsList.EnvironmentItem({ + component = new EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: mockItem, @@ -111,7 +111,7 @@ fdescribe('Environment item', () => { }, }; - component = new window.gl.environmentsList.EnvironmentItem({ + component = new EnvironmentItem({ el: document.querySelector('tr#environment-row'), propsData: { model: environment, diff --git a/spec/javascripts/environments/environment_rollback_spec.js.es6 b/spec/javascripts/environments/environment_rollback_spec.js.es6 index 043b8708a6e..4a596baad09 100644 --- a/spec/javascripts/environments/environment_rollback_spec.js.es6 +++ b/spec/javascripts/environments/environment_rollback_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_rollback'); +const RollbackComponent = require('~/environments/components/environment_rollback'); describe('Rollback Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -10,7 +10,7 @@ describe('Rollback Component', () => { }); it('Should link to the provided retryUrl', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, @@ -22,7 +22,7 @@ describe('Rollback Component', () => { }); it('Should render Re-deploy label when isLastDeployment is true', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, @@ -34,7 +34,7 @@ describe('Rollback Component', () => { }); it('Should render Rollback label when isLastDeployment is false', () => { - const component = new window.gl.environmentsList.RollbackComponent({ + const component = new RollbackComponent({ el: document.querySelector('.test-dom-element'), propsData: { retryUrl: retryURL, diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 8b96f4b09db..1d1a688a4a5 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -1,7 +1,6 @@ -/* global Vue, environment */ - +const Vue = require('vue'); require('~/flash'); -require('~/environments/components/environment'); +const EnvironmentsComponent = require('~/environments/components/environment'); const { environment } = require('./mock_data'); describe('Environment', () => { @@ -32,7 +31,7 @@ describe('Environment', () => { }); it('should render the empty state', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); @@ -72,7 +71,7 @@ describe('Environment', () => { }); it('should render a table with environments', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); @@ -104,7 +103,7 @@ describe('Environment', () => { }); it('should render empty state', (done) => { - component = new gl.environmentsList.EnvironmentsComponent({ + component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); diff --git a/spec/javascripts/environments/environment_stop_spec.js.es6 b/spec/javascripts/environments/environment_stop_spec.js.es6 index 2dfce5ba824..5ca65b1debc 100644 --- a/spec/javascripts/environments/environment_stop_spec.js.es6 +++ b/spec/javascripts/environments/environment_stop_spec.js.es6 @@ -1,4 +1,4 @@ -require('~/environments/components/environment_stop'); +const StopComponent = require('~/environments/components/environment_stop'); describe('Stop Component', () => { preloadFixtures('static/environments/element.html.raw'); @@ -10,7 +10,7 @@ describe('Stop Component', () => { loadFixtures('static/environments/element.html.raw'); stopURL = '/stop'; - component = new window.gl.environmentsList.StopComponent({ + component = new StopComponent({ el: document.querySelector('.test-dom-element'), propsData: { stopUrl: stopURL, -- cgit v1.2.1 From 19bac884c67acebcc0e6dda2a3df824049b1f726 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 7 Feb 2017 15:06:31 +0100 Subject: Add route for environment folder and expose an API --- app/controllers/projects/environments_controller.rb | 18 ++++++++++++++++++ config/routes/project.rb | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 2252ece68ce..3b7240d8469 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -28,6 +28,24 @@ class Projects::EnvironmentsController < Projects::ApplicationController end end + def folder + @environments = project.environments + .where(environment_type: params[:id]) + .with_state(params[:scope] || :available) + + respond_to do |format| + format.html + format.json do + render json: { + environments: EnvironmentSerializer + .new(project: @project, user: @current_user) + .with_pagination(request, response) + .represent(@environments), + } + end + end + end + def show @deployments = environment.deployments.order(id: :desc).page(params[:page]) end diff --git a/config/routes/project.rb b/config/routes/project.rb index 2ac98cf3842..84f123ff717 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -156,6 +156,10 @@ constraints(ProjectUrlConstrainer.new) do get :terminal get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil } end + + collection do + get :folder, path: 'folders/:id' + end end resource :cycle_analytics, only: [:show] -- cgit v1.2.1 From 2bd1a229060c959a177171ddbb32d1f2202b4801 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 9 Feb 2017 15:51:49 +0000 Subject: Disables add issue button Previously was disabled until any list was present (except for done) this now takes into account the welcome blank state Closes #27931 --- app/assets/javascripts/boards/boards_bundle.js.es6 | 2 +- spec/features/boards/boards_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/boards/boards_bundle.js.es6 b/app/assets/javascripts/boards/boards_bundle.js.es6 index 8f30900198e..878ad1b6031 100644 --- a/app/assets/javascripts/boards/boards_bundle.js.es6 +++ b/app/assets/javascripts/boards/boards_bundle.js.es6 @@ -95,7 +95,7 @@ $(() => { }, computed: { disabled() { - return Store.shouldAddBlankState(); + return !this.store.lists.filter(list => list.type !== 'blank' && list.type !== 'done').length; }, }, template: ` diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 7225f38b7e5..1b25b51cfb2 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -28,6 +28,12 @@ describe 'Issue Boards', feature: true, js: true do expect(page).to have_content('Welcome to your Issue Board!') end + it 'disables add issues button by default' do + button = page.find('.issue-boards-search button', text: 'Add issues') + + expect(button[:disabled]).to eq true + end + it 'hides the blank state when clicking nevermind button' do page.within(find('.board-blank-state')) do click_button("Nevermind, I'll use my own") -- cgit v1.2.1 From 5d0c5663a0368545653be3992c364eea74830062 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 16:36:32 +0000 Subject: Adds `.json` to the endpoint requested in order to avoid showing JSON --- app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 | 2 +- changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml diff --git a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 index 483b414126a..e9cae893857 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 @@ -9,7 +9,7 @@ */ class PipelinesService { constructor(endpoint) { - this.pipelines = Vue.resource(endpoint); + this.pipelines = Vue.resource(`${endpoint}.json`); } /** diff --git a/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml b/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml new file mode 100644 index 00000000000..b7505e28401 --- /dev/null +++ b/changelogs/unreleased/27932-merge-request-pipelines-displays-json.yml @@ -0,0 +1,4 @@ +--- +title: Fix Merge request pipelines displays JSON +merge_request: +author: -- cgit v1.2.1 From 482e7ff01201dba89a13f5e9979ea17c202c87c7 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 17:32:08 +0000 Subject: Fix broken test and linter error --- app/assets/javascripts/environments/components/environment_item.js.es6 | 2 +- spec/javascripts/environments/mock_data.js.es6 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index cf79969471e..d0c5a343d71 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -97,7 +97,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasStopAction() { - return this.model['stop_action?']; + return this.model.latest['stop_action?']; }, /** diff --git a/spec/javascripts/environments/mock_data.js.es6 b/spec/javascripts/environments/mock_data.js.es6 index bdecc95d219..081897f5456 100644 --- a/spec/javascripts/environments/mock_data.js.es6 +++ b/spec/javascripts/environments/mock_data.js.es6 @@ -55,5 +55,5 @@ const environment = { module.exports = { environmentsList, - environment + environment, }; -- cgit v1.2.1 From 6ed73dc7c282ebf0da9371cb8b20fe6c711a95e4 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Fri, 3 Feb 2017 18:07:09 +0500 Subject: Move dashboard issues spinach test to Rspec https://gitlab.com/gitlab-org/gitlab-ce/issues/23036 --- features/dashboard/issues.feature | 21 -------- features/steps/dashboard/issues.rb | 91 ---------------------------------- spec/features/dashboard/issues_spec.rb | 48 ++++++++++++++++++ 3 files changed, 48 insertions(+), 112 deletions(-) delete mode 100644 features/dashboard/issues.feature delete mode 100644 features/steps/dashboard/issues.rb create mode 100644 spec/features/dashboard/issues_spec.rb diff --git a/features/dashboard/issues.feature b/features/dashboard/issues.feature deleted file mode 100644 index 99dad88a402..00000000000 --- a/features/dashboard/issues.feature +++ /dev/null @@ -1,21 +0,0 @@ -@dashboard -Feature: Dashboard Issues - Background: - Given I sign in as a user - And I have authored issues - And I have assigned issues - And I have other issues - And I visit dashboard issues page - - Scenario: I should see assigned issues - Then I should see issues assigned to me - - @javascript - Scenario: I should see authored issues - When I click "Authored by me" link - Then I should see issues authored by me - - @javascript - Scenario: I should see all issues - When I click "All" link - Then I should see all issues diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb deleted file mode 100644 index 4e15d79ae74..00000000000 --- a/features/steps/dashboard/issues.rb +++ /dev/null @@ -1,91 +0,0 @@ -class Spinach::Features::DashboardIssues < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include Select2Helper - - step 'I should see issues assigned to me' do - should_see(assigned_issue) - should_not_see(authored_issue) - should_not_see(other_issue) - end - - step 'I should see issues authored by me' do - should_see(authored_issue) - should_see(authored_issue_on_public_project) - should_not_see(assigned_issue) - should_not_see(other_issue) - end - - step 'I should see all issues' do - should_see(authored_issue) - should_see(assigned_issue) - should_see(other_issue) - end - - step 'I have authored issues' do - authored_issue - authored_issue_on_public_project - end - - step 'I have assigned issues' do - assigned_issue - end - - step 'I have other issues' do - other_issue - end - - step 'I click "Authored by me" link' do - find("#assignee_id").set("") - find(".js-author-search", match: :first).click - find(".dropdown-menu-author li a", match: :first, text: current_user.to_reference).click - end - - step 'I click "All" link' do - find(".js-author-search").click - expect(page).to have_selector(".dropdown-menu-author li a") - find(".dropdown-menu-author li a", match: :first).click - expect(page).not_to have_selector(".dropdown-menu-author li a") - - find(".js-assignee-search").click - expect(page).to have_selector(".dropdown-menu-assignee li a") - find(".dropdown-menu-assignee li a", match: :first).click - expect(page).not_to have_selector(".dropdown-menu-assignee li a") - end - - def should_see(issue) - expect(page).to have_content(issue.title[0..10]) - end - - def should_not_see(issue) - expect(page).not_to have_content(issue.title[0..10]) - end - - def assigned_issue - @assigned_issue ||= create :issue, assignee: current_user, project: project - end - - def authored_issue - @authored_issue ||= create :issue, author: current_user, project: project - end - - def other_issue - @other_issue ||= create :issue, project: project - end - - def authored_issue_on_public_project - @authored_issue_on_public_project ||= create :issue, author: current_user, project: public_project - end - - def project - @project ||= begin - project = create(:empty_project) - project.team << [current_user, :master] - project - end - end - - def public_project - @public_project ||= create(:empty_project, :public) - end -end diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb new file mode 100644 index 00000000000..2db1cf71209 --- /dev/null +++ b/spec/features/dashboard/issues_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +RSpec.describe 'Dashboard Issues', feature: true do + let(:current_user) { create :user } + let(:public_project) { create(:empty_project, :public) } + let(:project) do + create(:empty_project) do |project| + project.team << [current_user, :master] + end + end + + let!(:authored_issue) { create :issue, author: current_user, project: project } + let!(:authored_issue_on_public_project) { create :issue, author: current_user, project: public_project } + let!(:assigned_issue) { create :issue, assignee: current_user, project: project } + let!(:other_issue) { create :issue, project: project } + + before do + login_as(current_user) + + visit issues_dashboard_path(assignee_id: current_user.id) + end + + it 'shows issues assigned to current user' do + expect(page).to have_content(assigned_issue.title) + expect(page).not_to have_content(authored_issue.title) + expect(page).not_to have_content(other_issue.title) + end + + it 'shows issues when current user is author', js: true do + find('#assignee_id', visible: false).set('') + find('.js-author-search', match: :first).click + find('.dropdown-menu-author li a', match: :first, text: current_user.to_reference).click + + expect(page).to have_content(authored_issue.title) + expect(page).to have_content(authored_issue_on_public_project.title) + expect(page).not_to have_content(assigned_issue.title) + expect(page).not_to have_content(other_issue.title) + end + + it 'shows all issues' do + click_link('Reset filters') + + expect(page).to have_content(authored_issue.title) + expect(page).to have_content(authored_issue_on_public_project.title) + expect(page).to have_content(assigned_issue.title) + expect(page).to have_content(other_issue.title) + end +end -- cgit v1.2.1 From d0d94e4f104c276ee4095a76d1204daae384c708 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 10 Feb 2017 09:36:52 +0100 Subject: Fix pagination headers in grouped environments API --- app/serializers/environment_serializer.rb | 23 ++++++++++++++--------- spec/serializers/environment_serializer_spec.rb | 11 +++++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index fe16a3784c4..d0a60f134da 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -20,8 +20,6 @@ class EnvironmentSerializer < BaseSerializer end def represent(resource, opts = {}) - resource = @paginator.paginate(resource) if paginated? - if itemized? itemize(resource).map do |item| { name: item.name, @@ -29,6 +27,8 @@ class EnvironmentSerializer < BaseSerializer latest: super(item.latest, opts) } end else + resource = @paginator.paginate(resource) if paginated? + super(resource, opts) end end @@ -36,15 +36,20 @@ class EnvironmentSerializer < BaseSerializer private def itemize(resource) - items = resource.group(:item_name).order('item_name ASC') - .pluck('COALESCE(environment_type, name) AS item_name', - 'COUNT(*) AS environments_count', - 'MAX(id) AS last_environment_id') + items = resource.order('folder_name ASC') + .group('COALESCE(environment_type, name)') + .select('COALESCE(environment_type, name) AS folder_name', + 'COUNT(*) AS size', 'MAX(id) AS last_id') + + # It makes a difference when you call `paginate` method, because + # although `page` is effective at the end, it calls counting methods + # immediately. + items = @paginator.paginate(items) if paginated? - environments = resource.where(id: items.map(&:last)).index_by(&:id) + environments = resource.where(id: items.map(&:last_id)).index_by(&:id) - items.map do |name, size, id| - Item.new(name, size, environments[id]) + items.map do |item| + Item.new(item.folder_name, item.size, environments[item.last_id]) end end end diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb index 1b95f1ff198..6a6df377b35 100644 --- a/spec/serializers/environment_serializer_spec.rb +++ b/spec/serializers/environment_serializer_spec.rb @@ -181,6 +181,17 @@ describe EnvironmentSerializer do expect(subject.first[:name]).to eq 'production' expect(subject.second[:name]).to eq 'staging' end + + it 'appends correct total page count header' do + expect(subject).not_to be_empty + expect(response).to have_received(:[]=).with('X-Total', '3') + end + + it 'appends correct page count headers' do + expect(subject).not_to be_empty + expect(response).to have_received(:[]=).with('X-Total-Pages', '2') + expect(response).to have_received(:[]=).with('X-Per-Page', '2') + end end end end -- cgit v1.2.1 From 293a54a9dbfc02d685296c1872199dece624f901 Mon Sep 17 00:00:00 2001 From: Mark Pundsack Date: Fri, 10 Feb 2017 13:49:35 -0600 Subject: Add image description --- doc/ci/pipelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 35a80dd2977..5700e9bb026 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -33,7 +33,7 @@ Pipelines accommodate several development workflows: Example continuous delivery flow: -![](img/pipelines-goal.svg) +![CD Flow](img/pipelines-goal.svg) ## Builds -- cgit v1.2.1 From 86558d53ddcf008fff396d25c38c9462fb77f615 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 10 Feb 2017 19:41:15 +0000 Subject: Adds verification in case the endpoint already has `.json` --- .../commit/pipelines/pipelines_service.js.es6 | 19 +++++++++++++++++-- spec/features/merge_requests/create_new_mr_spec.rb | 3 +++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 index e9cae893857..8ae98f9bf97 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_service.js.es6 @@ -8,8 +8,23 @@ * Uses Vue.Resource */ class PipelinesService { - constructor(endpoint) { - this.pipelines = Vue.resource(`${endpoint}.json`); + + /** + * FIXME: The url provided to request the pipelines in the new merge request + * page already has `.json`. + * This should be fixed when the endpoint is improved. + * + * @param {String} root + */ + constructor(root) { + let endpoint; + + if (root.indexOf('.json') === -1) { + endpoint = `${root}.json`; + } else { + endpoint = root; + } + this.pipelines = Vue.resource(endpoint); } /** diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index e853fb7e016..0832a3656a8 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' feature 'Create New Merge Request', feature: true, js: true do + include WaitForVueResource + let(:user) { create(:user) } let(:project) { create(:project, :public) } @@ -99,6 +101,7 @@ feature 'Create New Merge Request', feature: true, js: true do page.within('.merge-request') do click_link 'Pipelines' + wait_for_vue_resource expect(page).to have_content "##{pipeline.id}" end -- cgit v1.2.1 From cb8d031e959588c02efcd245ba914c07dab7f993 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Nov 2016 15:07:42 +0800 Subject: Explicitly disable the RSpec/BeEql cop This is a little too picky, even for us. --- .rubocop.yml | 4 ++++ .rubocop_todo.yml | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 88345373a5b..e73597adca2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -769,6 +769,10 @@ Rails/ScopeArgs: RSpec/AnyInstance: Enabled: false +# Check for expectations where `be(...)` can replace `eql(...)`. +RSpec/BeEql: + Enabled: false + # Check that the first argument to the top level describe is the tested class or # module. RSpec/DescribeClass: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d581610162f..c86cd714723 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -80,10 +80,6 @@ Performance/RedundantMatch: Performance/RedundantMerge: Enabled: false -# Offense count: 7 -RSpec/BeEql: - Enabled: false - # Offense count: 15 # Configuration parameters: CustomIncludeMethods. RSpec/EmptyExampleGroup: -- cgit v1.2.1 From 92cbc1e4ad8d874428089c4c65293fa218f67206 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Nov 2016 15:13:42 +0800 Subject: Enable the `RSpec/ExpectActual` cop and correct offenses --- .rubocop.yml | 4 ++ .rubocop_todo.yml | 4 -- .../issues/filtered_search/filter_issues_spec.rb | 33 ++++------ spec/lib/gitlab/regex_spec.rb | 71 ++++++++++++---------- 4 files changed, 53 insertions(+), 59 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index e73597adca2..21ea8372e4b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -801,6 +801,10 @@ RSpec/ExampleWording: not: does not IgnoredWords: [] +# Checks for `expect(...)` calls containing literal values. +RSpec/ExpectActual: + Enabled: true + # Checks the file and folder naming of the spec file. RSpec/FilePath: Enabled: false diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c86cd714723..4714b64b896 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -85,10 +85,6 @@ Performance/RedundantMerge: RSpec/EmptyExampleGroup: Enabled: false -# Offense count: 24 -RSpec/ExpectActual: - Enabled: false - # Offense count: 58 # Configuration parameters: EnforcedStyle, SupportedStyles. # SupportedStyles: implicit, each, example diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index 6f7046c8461..64f448a83b7 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -113,13 +113,11 @@ describe 'Filter issues', js: true, feature: true do end it 'filters issues by invalid author' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end it 'filters issues by multiple authors' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end @@ -158,8 +156,7 @@ describe 'Filter issues', js: true, feature: true do end it 'sorting' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end @@ -182,13 +179,11 @@ describe 'Filter issues', js: true, feature: true do end it 'filters issues by invalid assignee' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end it 'filters issues by multiple assignees' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end @@ -228,8 +223,7 @@ describe 'Filter issues', js: true, feature: true do context 'sorting' do it 'sorts' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end end @@ -253,8 +247,7 @@ describe 'Filter issues', js: true, feature: true do end it 'filters issues by invalid label' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end it 'filters issues by multiple labels' do @@ -429,8 +422,7 @@ describe 'Filter issues', js: true, feature: true do context 'sorting' do it 'sorts' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end end @@ -456,13 +448,11 @@ describe 'Filter issues', js: true, feature: true do end it 'filters issues by invalid milestones' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end it 'filters issues by multiple milestones' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end it 'filters issues by milestone containing special characters' do @@ -523,8 +513,7 @@ describe 'Filter issues', js: true, feature: true do context 'sorting' do it 'sorts' do - pending('to be tested, issue #26546') - expect(true).to be(false) + skip('to be tested, issue #26546') end end end diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index c78cd30157e..1dbc2f6eb13 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -2,47 +2,52 @@ require 'spec_helper' describe Gitlab::Regex, lib: true do - describe 'project path regex' do - it { expect('gitlab-ce').to match(Gitlab::Regex.project_path_regex) } - it { expect('gitlab_git').to match(Gitlab::Regex.project_path_regex) } - it { expect('_underscore.js').to match(Gitlab::Regex.project_path_regex) } - it { expect('100px.com').to match(Gitlab::Regex.project_path_regex) } - it { expect('?gitlab').not_to match(Gitlab::Regex.project_path_regex) } - it { expect('git lab').not_to match(Gitlab::Regex.project_path_regex) } - it { expect('gitlab.git').not_to match(Gitlab::Regex.project_path_regex) } - end + describe '.project_path_regex' do + subject { described_class.project_path_regex } - describe 'project name regex' do - it { expect('gitlab-ce').to match(Gitlab::Regex.project_name_regex) } - it { expect('GitLab CE').to match(Gitlab::Regex.project_name_regex) } - it { expect('100 lines').to match(Gitlab::Regex.project_name_regex) } - it { expect('gitlab.git').to match(Gitlab::Regex.project_name_regex) } - it { expect('Český název').to match(Gitlab::Regex.project_name_regex) } - it { expect('Dash – is this').to match(Gitlab::Regex.project_name_regex) } - it { expect('?gitlab').not_to match(Gitlab::Regex.project_name_regex) } + it { is_expected.to match('gitlab-ce') } + it { is_expected.to match('gitlab_git') } + it { is_expected.to match('_underscore.js') } + it { is_expected.to match('100px.com') } + it { is_expected.not_to match('?gitlab') } + it { is_expected.not_to match('git lab') } + it { is_expected.not_to match('gitlab.git') } end - describe 'file name regex' do - it { expect('foo@bar').to match(Gitlab::Regex.file_name_regex) } + describe '.project_name_regex' do + subject { described_class.project_name_regex } + + it { is_expected.to match('gitlab-ce') } + it { is_expected.to match('GitLab CE') } + it { is_expected.to match('100 lines') } + it { is_expected.to match('gitlab.git') } + it { is_expected.to match('Český název') } + it { is_expected.to match('Dash – is this') } + it { is_expected.not_to match('?gitlab') } end - describe 'file path regex' do - it { expect('foo@/bar').to match(Gitlab::Regex.file_path_regex) } + describe '.file_name_regex' do + subject { described_class.file_name_regex } + + it { is_expected.to match('foo@bar') } end - describe 'environment slug regex' do - def be_matched - match(Gitlab::Regex.environment_slug_regex) - end + describe '.file_path_regex' do + subject { described_class.file_path_regex } + + it { is_expected.to match('foo@/bar') } + end - it { expect('foo').to be_matched } - it { expect('foo-1').to be_matched } + describe '.environment_slug_regex' do + subject { described_class.environment_slug_regex } - it { expect('FOO').not_to be_matched } - it { expect('foo/1').not_to be_matched } - it { expect('foo.1').not_to be_matched } - it { expect('foo*1').not_to be_matched } - it { expect('9foo').not_to be_matched } - it { expect('foo-').not_to be_matched } + it { is_expected.to match('foo') } + it { is_expected.to match('foo-1') } + it { is_expected.not_to match('FOO') } + it { is_expected.not_to match('foo/1') } + it { is_expected.not_to match('foo.1') } + it { is_expected.not_to match('foo*1') } + it { is_expected.not_to match('9foo') } + it { is_expected.not_to match('foo-') } end end -- cgit v1.2.1 From 5db56efe424c9cd760580a755ec4e131d045769d Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 30 Nov 2016 15:34:29 +0800 Subject: Enable `Style/RedundantException` cop and correct offense --- .rubocop.yml | 4 ++++ .rubocop_todo.yml | 5 ----- app/helpers/preferences_helper.rb | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 21ea8372e4b..1061de7c797 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -339,6 +339,10 @@ Style/OpMethod: Style/ParenthesesAroundCondition: Enabled: true +# Checks for an obsolete RuntimeException argument in raise/fail. +Style/RedundantException: + Enabled: true + # Checks for parentheses that seem not to serve any purpose. Style/RedundantParentheses: Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 4714b64b896..7ef5523de4b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -416,11 +416,6 @@ Style/RaiseArgs: Style/RedundantBegin: Enabled: false -# Offense count: 1 -# Cop supports --auto-correct. -Style/RedundantException: - Enabled: false - # Offense count: 29 # Cop supports --auto-correct. Style/RedundantFreeze: diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 6e68aad4cb7..dd0a4ea03f0 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -23,7 +23,7 @@ module PreferencesHelper if defined.size != DASHBOARD_CHOICES.size # Ensure that anyone adding new options updates this method too - raise RuntimeError, "`User` defines #{defined.size} dashboard choices," + + raise "`User` defines #{defined.size} dashboard choices," \ " but `DASHBOARD_CHOICES` defined #{DASHBOARD_CHOICES.size}." else defined.map do |key, _| -- cgit v1.2.1 From c0fb47333a1336000a8ae605ef23572bb38d674a Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Fri, 10 Feb 2017 13:16:38 +1100 Subject: only print errors and warnings from webpack dev server --- config/webpack.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 00f448c1fbb..2ac779c8511 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -117,7 +117,8 @@ if (IS_PRODUCTION) { if (IS_DEV_SERVER) { config.devServer = { port: DEV_SERVER_PORT, - headers: { 'Access-Control-Allow-Origin': '*' } + headers: { 'Access-Control-Allow-Origin': '*' }, + stats: 'errors-only', }; config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath; } -- cgit v1.2.1 From d4317e4d22b33d0bfe07b231ddeb4dd774aa0bb7 Mon Sep 17 00:00:00 2001 From: winniehell Date: Thu, 9 Feb 2017 12:03:41 +0100 Subject: Use textarea for quick submit spec --- spec/javascripts/behaviors/quick_submit_spec.js | 28 ++++++++++++---------- .../fixtures/behaviors/quick_submit.html.haml | 1 - 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index 1541037888f..8113b16b74a 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -12,31 +12,33 @@ require('~/behaviors/quick_submit'); // Prevent a form submit from moving us off the testing page return e.preventDefault(); }); - return this.spies = { + this.spies = { submit: spyOnEvent('form', 'submit') }; + + this.textarea = $('.js-quick-submit textarea').first(); }); it('does not respond to other keyCodes', function() { - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ keyCode: 32 })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); it('does not respond to Enter alone', function() { - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ ctrlKey: false, metaKey: false })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); it('does not respond to repeated events', function() { - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ repeat: true })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); it('disables submit buttons', function() { - $('textarea').trigger(keydownEvent()); + this.textarea.trigger(keydownEvent()); expect($('input[type=submit]')).toBeDisabled(); return expect($('button[type=submit]')).toBeDisabled(); }); @@ -44,34 +46,34 @@ require('~/behaviors/quick_submit'); // only run the tests that apply to the current platform if (navigator.userAgent.match(/Macintosh/)) { it('responds to Meta+Enter', function() { - $('input.quick-submit-input').trigger(keydownEvent()); + this.textarea.trigger(keydownEvent()); return expect(this.spies.submit).toHaveBeenTriggered(); }); it('excludes other modifier keys', function() { - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ altKey: true })); - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ ctrlKey: true })); - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ shiftKey: true })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); } else { it('responds to Ctrl+Enter', function() { - $('input.quick-submit-input').trigger(keydownEvent()); + this.textarea.trigger(keydownEvent()); return expect(this.spies.submit).toHaveBeenTriggered(); }); it('excludes other modifier keys', function() { - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ altKey: true })); - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ metaKey: true })); - $('input.quick-submit-input').trigger(keydownEvent({ + this.textarea.trigger(keydownEvent({ shiftKey: true })); return expect(this.spies.submit).not.toHaveBeenTriggered(); diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml index dc2ceed42f4..6dd53931a42 100644 --- a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml +++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml @@ -1,5 +1,4 @@ %form.js-quick-submit{ action: '/foo' } - %input{ type: 'text', class: 'quick-submit-input'} %textarea %input{ type: 'submit'} Submit -- cgit v1.2.1 From 128d8b61180845e0465a3fe4e5f64d417f4f819a Mon Sep 17 00:00:00 2001 From: winniehell Date: Thu, 9 Feb 2017 12:47:55 +0100 Subject: Replace static fixture for behaviors/quick_submit_spec.js (!9086) --- changelogs/unreleased/quick-submit-fixture.yml | 4 ++++ spec/javascripts/behaviors/quick_submit_spec.js | 18 +++++++++++++----- .../fixtures/behaviors/quick_submit.html.haml | 5 ----- 3 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 changelogs/unreleased/quick-submit-fixture.yml delete mode 100644 spec/javascripts/fixtures/behaviors/quick_submit.html.haml diff --git a/changelogs/unreleased/quick-submit-fixture.yml b/changelogs/unreleased/quick-submit-fixture.yml new file mode 100644 index 00000000000..a2cf05dabec --- /dev/null +++ b/changelogs/unreleased/quick-submit-fixture.yml @@ -0,0 +1,4 @@ +--- +title: Replace static fixture for behaviors/quick_submit_spec.js +merge_request: 9086 +author: winniehell diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js index 8113b16b74a..0e4c2c560cc 100644 --- a/spec/javascripts/behaviors/quick_submit_spec.js +++ b/spec/javascripts/behaviors/quick_submit_spec.js @@ -5,9 +5,9 @@ require('~/behaviors/quick_submit'); (function() { describe('Quick Submit behavior', function() { var keydownEvent; - preloadFixtures('static/behaviors/quick_submit.html.raw'); + preloadFixtures('issues/open-issue.html.raw'); beforeEach(function() { - loadFixtures('static/behaviors/quick_submit.html.raw'); + loadFixtures('issues/open-issue.html.raw'); $('form').submit(function(e) { // Prevent a form submit from moving us off the testing page return e.preventDefault(); @@ -37,10 +37,18 @@ require('~/behaviors/quick_submit'); })); return expect(this.spies.submit).not.toHaveBeenTriggered(); }); - it('disables submit buttons', function() { + it('disables input of type submit', function() { + const submitButton = $('.js-quick-submit input[type=submit]'); this.textarea.trigger(keydownEvent()); - expect($('input[type=submit]')).toBeDisabled(); - return expect($('button[type=submit]')).toBeDisabled(); + expect(submitButton).toBeDisabled(); + }); + it('disables button of type submit', function() { + // button doesn't exist in fixture, add it manually + const submitButton = $(''); + submitButton.insertAfter(this.textarea); + + this.textarea.trigger(keydownEvent()); + expect(submitButton).toBeDisabled(); }); // We cannot stub `navigator.userAgent` for CI's `rake karma` task, so we'll // only run the tests that apply to the current platform diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml deleted file mode 100644 index 6dd53931a42..00000000000 --- a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%form.js-quick-submit{ action: '/foo' } - %textarea - - %input{ type: 'submit'} Submit - %button.btn{ type: 'submit' } Submit -- cgit v1.2.1 From b48d30e4d27d2482f5517fc58751953d32f0ded6 Mon Sep 17 00:00:00 2001 From: winniehell Date: Fri, 10 Feb 2017 19:29:03 +0100 Subject: Reintroduce coverage report for JavaScript (!9133) --- .gitlab-ci.yml | 4 ++-- changelogs/unreleased/cover-my-karma.yml | 4 ++++ config/karma.config.js | 7 +++++++ config/webpack.config.js | 1 + package.json | 10 ++++++++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/cover-my-karma.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 733710bb005..8d6383dfdb9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -252,7 +252,7 @@ karma: name: coverage-javascript expire_in: 31d paths: - - coverage-javascript/default/ + - coverage-javascript/ lint-doc: stage: test @@ -395,7 +395,7 @@ pages: - mv public/ .public/ - mkdir public/ - mv coverage/ public/coverage-ruby/ || true - - mv coverage-javascript/default/ public/coverage-javascript/ || true + - mv coverage-javascript/ public/coverage-javascript/ || true - mv eslint-report.html public/ || true artifacts: paths: diff --git a/changelogs/unreleased/cover-my-karma.yml b/changelogs/unreleased/cover-my-karma.yml new file mode 100644 index 00000000000..4a823dc5ca4 --- /dev/null +++ b/changelogs/unreleased/cover-my-karma.yml @@ -0,0 +1,4 @@ +--- +title: Reintroduce coverage report for JavaScript +merge_request: 9133 +author: winniehell diff --git a/config/karma.config.js b/config/karma.config.js index 44229e2ee88..a1fbeab1f46 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -15,6 +15,13 @@ module.exports = function(config) { preprocessors: { 'spec/javascripts/**/*.js?(.es6)': ['webpack', 'sourcemap'], }, + reporters: ['progress', 'coverage-istanbul'], + coverageIstanbulReporter: { + reports: ['html', 'text-summary'], + dir: 'coverage-javascript/', + subdir: '.', + fixWebpackSourcePaths: true + }, webpack: webpackConfig, webpackMiddleware: { stats: 'errors-only' }, }); diff --git a/config/webpack.config.js b/config/webpack.config.js index 00f448c1fbb..e800cc3fd84 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -54,6 +54,7 @@ var config = { exclude: /(node_modules|vendor\/assets)/, loader: 'babel-loader', options: { + plugins: ['istanbul'], presets: [ ["es2015", {"modules": false}], 'stage-2' diff --git a/package.json b/package.json index 24e11a4607f..dbdbeabfb67 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "webpack-dev-server": "^2.3.0" }, "devDependencies": { + "babel-plugin-istanbul": "^4.0.0", "eslint": "^3.10.1", "eslint-config-airbnb-base": "^10.0.1", "eslint-plugin-filenames": "^1.1.0", @@ -45,9 +46,18 @@ "jasmine-core": "^2.5.2", "jasmine-jquery": "^2.1.1", "karma": "^1.4.1", + "karma-coverage-istanbul-reporter": "^0.2.0", "karma-jasmine": "^1.1.0", "karma-phantomjs-launcher": "^1.0.2", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^2.0.2" + }, + "nyc": { + "exclude": [ + "spec/javascripts/test_bundle.js", + "spec/javascripts/**/*_spec.js", + "spec/javascripts/**/*_spec.js.es6", + "app/assets/javascripts/droplab/**/*" + ] } } -- cgit v1.2.1 From bfb82f8a0d4d837a0472342c05de18c60037a2ea Mon Sep 17 00:00:00 2001 From: winniehell Date: Sat, 11 Feb 2017 00:45:54 +0100 Subject: Include all files with 0% coverage in report --- spec/javascripts/test_bundle.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 030d2de090a..ca707d872a4 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -42,3 +42,38 @@ testsContext.keys().forEach(function (path) { }); } }); + +// workaround: include all source files to find files with 0% coverage +// see also https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15 +describe('Uncovered files', function () { + // the following files throw errors because of undefined variables + const troubleMakers = [ + './blob_edit/blob_edit_bundle.js', + './cycle_analytics/components/stage_plan_component.js', + './cycle_analytics/components/stage_staging_component.js', + './cycle_analytics/components/stage_test_component.js', + './diff_notes/components/jump_to_discussion.js', + './diff_notes/components/resolve_count.js', + './merge_conflicts/components/inline_conflict_lines.js', + './merge_conflicts/components/parallel_conflict_lines.js', + './network/branch_graph.js', + ]; + + const sourceFiles = require.context('~', true, /^\.\/(?!application\.js).*\.(js|es6)$/); + sourceFiles.keys().forEach(function (path) { + // ignore if there is a matching spec file + if (testsContext.keys().indexOf(`${path.replace(/\.js(\.es6)?$/, '')}_spec`) > -1) { + return; + } + + it(`includes '${path}'`, function () { + try { + sourceFiles(path); + } catch (err) { + if (troubleMakers.indexOf(path) === -1) { + expect(err).toBeNull(); + } + } + }); + }); +}); -- cgit v1.2.1 From f7d8c29e71dc3a5aed4cf7252beaee6fe634de6b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 12:10:29 +0000 Subject: Fix broken path --- .../components/environment_item.js.es6 | 108 ++++++++++++++++----- 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 399473bec65..391539232b1 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -1,5 +1,5 @@ const Vue = require('vue'); -const Timeago = require('timeago.j'); +const Timeago = require('timeago.js'); require('../../lib/utils/text_utility'); require('../../vue_shared/components/commit'); @@ -72,7 +72,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.latest.last_deployment && + if (this.model.lastest && this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +86,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.latest.last_deployment && + return this.model.lastest && this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, @@ -107,7 +107,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canRetry() { - return this.hasLastDeploymentKey && + return this.model.lastest && + this.hasLastDeploymentKey && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable; }, @@ -118,7 +119,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.latest.last_deployment && + return this.model.lastest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable !== undefined; }, @@ -129,7 +131,13 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ createdDate() { - return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); + if (this.model.lastest && + this.model.latest.last_deployment && + this.model.lastest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.created_at) { + return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); + } + return ''; }, /** @@ -156,7 +164,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ userImageAltDescription() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.user && this.model.latest.last_deployment.user.username) { return `${this.model.latest.last_deployment.user.username}'s avatar'`; @@ -170,7 +179,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitTag() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.tag) { return this.model.latest.last_deployment.tag; } @@ -183,7 +193,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object|Undefined} */ commitRef() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.ref) { return this.model.latest.last_deployment.ref; } @@ -196,7 +207,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitUrl() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.commit_path) { return this.model.latest.last_deployment.commit.commit_path; @@ -210,7 +222,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitShortSha() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.short_id) { return this.model.latest.last_deployment.commit.short_id; @@ -224,7 +237,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ commitTitle() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.title) { return this.model.latest.last_deployment.commit.title; @@ -238,7 +252,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object|Undefined} */ commitAuthor() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.commit && this.model.latest.last_deployment.commit.author) { return this.model.latest.last_deployment.commit.author; @@ -253,7 +268,8 @@ module.exports = Vue.component('environment-item', { * @returns {String|Undefined} */ retryUrl() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable.retry_path) { return this.model.latest.last_deployment.deployable.retry_path; @@ -267,7 +283,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ isLastDeployment() { - return this.model.latest.last_deployment && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment['last?']; }, @@ -277,7 +293,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ buildName() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable) { return `${this.model.latest.last_deployment.deployable.name} #${this.model.latest.last_deployment.deployable.id}`; } @@ -290,7 +307,8 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ deploymentInternalId() { - if (this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.iid) { return `#${this.model.latest.last_deployment.iid}`; } @@ -303,7 +321,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ deploymentHasUser() { - return !this.$options.isObjectEmpty(this.model.latest.last_deployment) && + return this.model.latest && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.user); }, @@ -314,7 +333,8 @@ module.exports = Vue.component('environment-item', { * @returns {Object} */ deploymentUser() { - if (!this.$options.isObjectEmpty(this.model.latest.last_deployment) && + if (this.model.latest && + !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.user)) { return this.model.latest.last_deployment.user; } @@ -330,10 +350,39 @@ module.exports = Vue.component('environment-item', { */ shouldRenderBuildName() { return !this.model.isFolder && + this.model.lastest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + /** + * Verifies the presence of all the keys needed to render the buil_path. + * + * @return {String} + */ + buildPath() { + if (this.model.latest && + this.model.lastest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.lastest.last_deployment.deployable.build_path) { + return this.model.lastest.last_deployment.deployable.build_path; + } + + return ''; + }, + /** + * Verifies the presence of all the keys needed to render the external_url. + * + * @return {String} + */ + externalURL() { + if (this.model.latest && this.model.latest.external_url) { + return this.model.latest.external_url; + } + + return ''; + }, + /** * Verifies if deplyment internal ID should be rendered by verifing * if all the information needed is present @@ -343,9 +392,18 @@ module.exports = Vue.component('environment-item', { */ shouldRenderDeploymentID() { return !this.model.isFolder && + this.model.lastest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && this.model.latest.last_deployment.iid !== undefined; }, + + environmentPath() { + if (this.model && this.model.lastest && this.model.latest.environment_path) { + return this.model.latest.environment_path; + } + + return ''; + }, }, /** @@ -368,7 +426,7 @@ module.exports = Vue.component('environment-item', { + :href="environmentPath"> {{model.name}} @@ -406,7 +464,7 @@ module.exports = Vue.component('environment-item', { + :href="buildPath"> {{buildName}} @@ -445,21 +503,21 @@ module.exports = Vue.component('environment-item', {
    -
    + :external-url="externalURL">
    -
    -
    Date: Sun, 12 Feb 2017 13:43:46 +0000 Subject: Fix typo --- .../components/environment_item.js.es6 | 24 +++++++++++----------- spec/features/projects/builds_spec.rb | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 391539232b1..7805152b173 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -72,7 +72,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.lastest && this.model.latest.last_deployment && + if (this.model.latest && this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +86,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.lastest && this.model.latest.last_deployment && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, @@ -107,7 +107,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canRetry() { - return this.model.lastest && + return this.model.latest && this.hasLastDeploymentKey && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable; @@ -119,7 +119,7 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ canShowDate() { - return this.model.lastest && + return this.model.latest && this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable !== undefined; @@ -131,9 +131,9 @@ module.exports = Vue.component('environment-item', { * @returns {String} */ createdDate() { - if (this.model.lastest && + if (this.model.latest && this.model.latest.last_deployment && - this.model.lastest.last_deployment.deployable && + this.model.latest.last_deployment.deployable && this.model.latest.last_deployment.deployable.created_at) { return timeagoInstance.format(this.model.latest.last_deployment.deployable.created_at); } @@ -350,7 +350,7 @@ module.exports = Vue.component('environment-item', { */ shouldRenderBuildName() { return !this.model.isFolder && - this.model.lastest && + this.model.latest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, @@ -362,10 +362,10 @@ module.exports = Vue.component('environment-item', { */ buildPath() { if (this.model.latest && - this.model.lastest.last_deployment && + this.model.latest.last_deployment && this.model.latest.last_deployment.deployable && - this.model.lastest.last_deployment.deployable.build_path) { - return this.model.lastest.last_deployment.deployable.build_path; + this.model.latest.last_deployment.deployable.build_path) { + return this.model.latest.last_deployment.deployable.build_path; } return ''; @@ -392,13 +392,13 @@ module.exports = Vue.component('environment-item', { */ shouldRenderDeploymentID() { return !this.model.isFolder && - this.model.lastest && + this.model.latest && !this.$options.isObjectEmpty(this.model.latest.last_deployment) && this.model.latest.last_deployment.iid !== undefined; }, environmentPath() { - if (this.model && this.model.lastest && this.model.latest.environment_path) { + if (this.model && this.model.latest && this.model.latest.environment_path) { return this.model.latest.environment_path; } diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index f7e0115643e..80ba5ff1a63 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -271,7 +271,7 @@ feature 'Builds', :feature do let!(:deployment) { create(:deployment, environment: environment, sha: project.commit.id) } let(:build) { create(:ci_build, :success, environment: environment.name, pipeline: pipeline) } - it 'shows a link to lastest deployment' do + it 'shows a link to latest deployment' do visit namespace_project_build_path(project.namespace, project, build) expect(page).to have_link('latest deployment') -- cgit v1.2.1 From 991a7ae86055df2d0eb619e1d29a1e24e322c741 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 12:18:47 +0000 Subject: Adds pagination component --- .../environments/components/environment.js.es6 | 25 ++++++++++++++++++++-- .../environments/stores/environments_store.js.es6 | 13 +++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 11a965fcddf..f89dbdbda1d 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -6,11 +6,13 @@ Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); const EnvironmentItem = require('./environment_item'); const Store = require('../stores/environments_store'); +require('../../vue_shared/components/table_pagination'); module.exports = Vue.component('environment-component', { components: { 'environment-item': EnvironmentItem, + 'table-pagination': gl.VueGlPagination, }, data() { @@ -34,6 +36,10 @@ module.exports = Vue.component('environment-component', { commitIconSvg: environmentsData.commitIconSvg, playIconSvg: environmentsData.playIconSvg, terminalIconSvg: environmentsData.terminalIconSvg, + + // Pagination Properties, + paginationInformation: {}, + pageNumber: 1, }; }, @@ -61,14 +67,18 @@ module.exports = Vue.component('environment-component', { */ created() { const scope = this.$options.getQueryParameter('scope') || this.visibility; - const endpoint = `${this.endpoint}?scope=${scope}`; + const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; const service = new EnvironmentsService(endpoint); this.isLoading = true; return service.all() - .then(resp => resp.json()) + .then((resp) => { + debugger; + return resp.json(); + }) .then((json) => { this.store.storeAvailableCount(json.available_count); this.store.storeStoppedCount(json.stopped_count); @@ -111,6 +121,10 @@ module.exports = Vue.component('environment-component', { toggleRow(model) { return this.store.toggleFolder(model.name); }, + + changePage(pageNumber, scope) { + gl.utils.visitUrl(`?scope=${scope}&p=${pageNumber}`); + }, }, template: ` @@ -192,6 +206,13 @@ module.exports = Vue.component('environment-component', { + + +
    diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index 749dd882188..e55b8624ac8 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -1,3 +1,4 @@ +require('~/lib/utils/common_utils'); /** * Environments Store. * @@ -10,6 +11,7 @@ class EnvironmentsStore { this.state.environments = []; this.state.stoppedCounter = 0; this.state.availableCounter = 0; + this.state.paginationInformation = {}; return this; } @@ -41,6 +43,17 @@ class EnvironmentsStore { return filteredEnvironments; } + storePagination(pagination = {}) { + const normalizedHeaders = gl.utils.normalizedHeaders(pagination); + + const paginationInformation = { + + }; + + this.paginationInformation = paginationInformation; + return paginationInformation; + } + /** * Stores the number of available environments. * -- cgit v1.2.1 From fb35980893f918f7dbad0f433447c6df13a1c757 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 14:49:13 +0000 Subject: Verify `latest` key is present when needed --- .../environments/components/environment_item.js.es6 | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 7805152b173..d3ca4cb8dde 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -355,6 +355,18 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + buildPath(){ + return this.model.latest && + this.model.latest.last_deployment && + this.model.latest.last_deployment.deployable && + this.model.latest.last_deployment.deployable.build_path || + ''; + }, + + externalURL() { + return this.model.latest && this.model.latest.external_url || ''; + }, + /** * Verifies the presence of all the keys needed to render the buil_path. * -- cgit v1.2.1 From 595afed2e3de93d1685b2f77dd8e72df2781a57b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Thu, 9 Feb 2017 15:10:16 +0000 Subject: Integrates pagination component with API Adds pagination tests Remove misplaced comment Fix broken store test --- .../environments/components/environment.js.es6 | 37 +++++++++++++--------- .../components/environment_item.js.es6 | 16 +++++++++- .../environments/stores/environments_store.js.es6 | 12 ++++--- .../vue_pipelines_index/pipelines.js.es6 | 1 + .../vue_shared/components/table_pagination.js.es6 | 4 +-- .../environments/environment_spec.js.es6 | 28 +++++++++++++--- .../environments/environments_store_spec.js.es6 | 36 +++++++++++++++++---- 7 files changed, 100 insertions(+), 34 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index f89dbdbda1d..d5bb9f91e7f 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -59,6 +59,7 @@ module.exports = Vue.component('environment-component', { canCreateEnvironmentParsed() { return this.$options.convertPermissionToBoolean(this.canCreateEnvironment); }, + }, /** @@ -68,6 +69,7 @@ module.exports = Vue.component('environment-component', { created() { const scope = this.$options.getQueryParameter('scope') || this.visibility; const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; const service = new EnvironmentsService(endpoint); @@ -75,14 +77,15 @@ module.exports = Vue.component('environment-component', { this.isLoading = true; return service.all() - .then((resp) => { - debugger; - return resp.json(); - }) - .then((json) => { - this.store.storeAvailableCount(json.available_count); - this.store.storeStoppedCount(json.stopped_count); - this.store.storeEnvironments(json.environments); + .then(resp => ({ + headers: resp.headers, + body: resp.json(), + })) + .then((response) => { + this.store.storeAvailableCount(response.body.available_count); + this.store.storeStoppedCount(response.body.stopped_count); + this.store.storeEnvironments(response.body.environments); + this.store.storePagination(response.headers); }) .then(() => { this.isLoading = false; @@ -122,8 +125,14 @@ module.exports = Vue.component('environment-component', { return this.store.toggleFolder(model.name); }, - changePage(pageNumber, scope) { - gl.utils.visitUrl(`?scope=${scope}&p=${pageNumber}`); + /** + * Will change the page number and update the URL. + * + * @param {Number} pageNumber desired page to go to. + */ + changePage(pageNumber) { + const param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`); + gl.utils.visitUrl(param); }, }, @@ -131,7 +140,7 @@ module.exports = Vue.component('environment-component', { diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index d3ca4cb8dde..1648f06a991 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -355,6 +355,11 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, + /** + * Verifies the presence of all the keys needed to render the buil_path. + * + * @return {String} + */ buildPath(){ return this.model.latest && this.model.latest.last_deployment && @@ -363,8 +368,17 @@ module.exports = Vue.component('environment-item', { ''; }, + /** + * Verifies the presence of all the keys needed to render the external_url. + * + * @return {String} + */ externalURL() { - return this.model.latest && this.model.latest.external_url || ''; + if (this.model.latest && this.model.latest.external_url) { + return this.model.latest.external_url; + } + + return ''; }, /** diff --git a/app/assets/javascripts/environments/stores/environments_store.js.es6 b/app/assets/javascripts/environments/stores/environments_store.js.es6 index e55b8624ac8..252e349962e 100644 --- a/app/assets/javascripts/environments/stores/environments_store.js.es6 +++ b/app/assets/javascripts/environments/stores/environments_store.js.es6 @@ -44,13 +44,17 @@ class EnvironmentsStore { } storePagination(pagination = {}) { - const normalizedHeaders = gl.utils.normalizedHeaders(pagination); - + const normalizedHeaders = gl.utils.normalizeHeaders(pagination); const paginationInformation = { - + perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), + page: parseInt(normalizedHeaders['X-PAGE'], 10), + total: parseInt(normalizedHeaders['X-TOTAL'], 10), + totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), + nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), + previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), }; - this.paginationInformation = paginationInformation; + this.state.paginationInformation = paginationInformation; return paginationInformation; } diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 index e47dc6935d6..9e816a285e4 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 @@ -36,6 +36,7 @@ require('../vue_shared/components/pipelines_table'); }, methods: { change(pagenum, apiScope) { + if (!apiScope) apiScope = 'all'; gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`); }, }, diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 index 67c6cb73761..d8042a9b7fc 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 +++ b/app/assets/javascripts/vue_shared/components/table_pagination.js.es6 @@ -57,9 +57,7 @@ window.Vue = require('vue'); }, methods: { changePage(e) { - let apiScope = gl.utils.getParameterByName('scope'); - - if (!apiScope) apiScope = 'all'; + const apiScope = gl.utils.getParameterByName('scope'); const text = e.target.innerText; const { totalPages, nextPage, previousPage } = this.pageInfo; diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 1d1a688a4a5..f557dcee91f 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -49,7 +49,7 @@ describe('Environment', () => { }); }); - describe('with environments', () => { + describe('with paginated environments', () => { const environmentsResponseInterceptor = (request, next) => { next(request.respondWith(JSON.stringify({ environments: [environment], @@ -57,11 +57,22 @@ describe('Environment', () => { available_count: 0, }), { status: 200, + headers: { + 'X-Next-Page': '2', + 'X-Page': '1', + 'X-Per-Page': '1', + 'X-Prev-Page': '', + 'X-Total': '37', + 'X-Total-Pages': '2', + }, })); }; beforeEach(() => { Vue.http.interceptors.push(environmentsResponseInterceptor); + component = new EnvironmentsComponent({ + el: document.querySelector('#environments-list-view'), + }); }); afterEach(() => { @@ -71,10 +82,6 @@ describe('Environment', () => { }); it('should render a table with environments', (done) => { - component = new EnvironmentsComponent({ - el: document.querySelector('#environments-list-view'), - }); - setTimeout(() => { expect( component.$el.querySelectorAll('table tbody tr').length, @@ -82,6 +89,17 @@ describe('Environment', () => { done(); }, 0); }); + + describe('pagination', () => { + it('should render pagination', (done) => { + setTimeout(() => { + expect( + component.$el.querySelectorAll('.gl-pagination li').length, + ).toEqual(5); + done(); + }, 0); + }); + }); }); }); diff --git a/spec/javascripts/environments/environments_store_spec.js.es6 b/spec/javascripts/environments/environments_store_spec.js.es6 index 861136c621f..8fd660c3edb 100644 --- a/spec/javascripts/environments/environments_store_spec.js.es6 +++ b/spec/javascripts/environments/environments_store_spec.js.es6 @@ -10,24 +10,48 @@ const { environmentsList } = require('./mock_data'); }); it('should start with a blank state', () => { - expect(store.state.environments.length).toBe(0); - expect(store.state.stoppedCounter).toBe(0); - expect(store.state.availableCounter).toBe(0); + expect(store.state.environments.length).toEqual(0); + expect(store.state.stoppedCounter).toEqual(0); + expect(store.state.availableCounter).toEqual(0); + expect(store.state.paginationInformation).toEqual({}); }); it('should store environments', () => { store.storeEnvironments(environmentsList); - expect(store.state.environments.length).toBe(environmentsList.length); + expect(store.state.environments.length).toEqual(environmentsList.length); }); it('should store available count', () => { store.storeAvailableCount(2); - expect(store.state.availableCounter).toBe(2); + expect(store.state.availableCounter).toEqual(2); }); it('should store stopped count', () => { store.storeStoppedCount(2); - expect(store.state.stoppedCounter).toBe(2); + expect(store.state.stoppedCounter).toEqual(2); + }); + + it('should store pagination information', () => { + const pagination = { + 'X-nExt-pAge': '2', + 'X-page': '1', + 'X-Per-Page': '1', + 'X-Prev-Page': '2', + 'X-TOTAL': '37', + 'X-Total-Pages': '2', + }; + + const expectedResult = { + perPage: 1, + page: 1, + total: 37, + totalPages: 2, + nextPage: 2, + previousPage: 2, + }; + + store.storePagination(pagination); + expect(store.state.paginationInformation).toEqual(expectedResult); }); }); })(); -- cgit v1.2.1 From 27d7ec70b1efcaac73095e6ea3894b0eb9ba8473 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Fri, 10 Feb 2017 17:26:48 +0000 Subject: Remove spec checking for scope 'all' since it's no longer part of component Changes after review Fix typo --- .../environments/components/environment_item.js.es6 | 6 ++++-- spec/javascripts/environments/environment_spec.js.es6 | 6 +++--- .../vue_shared/components/table_pagination_spec.js.es6 | 12 ++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 1648f06a991..871a09977a4 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -72,7 +72,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean} */ hasLastDeploymentKey() { - if (this.model.latest && this.model.latest.last_deployment && + if (this.model.latest && + this.model.latest.last_deployment && !this.$options.isObjectEmpty(this.model.latest.last_deployment)) { return true; } @@ -86,7 +87,8 @@ module.exports = Vue.component('environment-item', { * @returns {Boolean|Undefined} */ hasManualActions() { - return this.model.latest && this.model.latest.last_deployment && + return this.model.latest && + this.model.latest.last_deployment && this.model.latest.last_deployment.manual_actions && this.model.latest.last_deployment.manual_actions.length > 0; }, diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index f557dcee91f..657d8d2ab02 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -58,11 +58,11 @@ describe('Environment', () => { }), { status: 200, headers: { - 'X-Next-Page': '2', - 'X-Page': '1', + 'X-nExt-pAge': '2', + 'x-page': '1', 'X-Per-Page': '1', 'X-Prev-Page': '', - 'X-Total': '37', + 'X-TOTAL': '37', 'X-Total-Pages': '2', }, })); diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 index e84f0dcfe67..dd495cb43bc 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js.es6 @@ -34,7 +34,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '1' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the previous page', () => { @@ -55,7 +55,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Prev' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the next page', () => { @@ -76,7 +76,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Next' } }); expect(changeChanges.one).toEqual(5); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the last page', () => { @@ -97,7 +97,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: 'Last >>' } }); expect(changeChanges.one).toEqual(10); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should go to the first page', () => { @@ -118,7 +118,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '<< First' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); it('should do nothing', () => { @@ -139,7 +139,7 @@ describe('Pagination component', () => { component.changePage({ target: { innerText: '...' } }); expect(changeChanges.one).toEqual(1); - expect(changeChanges.two).toEqual('all'); + expect(changeChanges.two).toEqual(null); }); }); -- cgit v1.2.1 From c2fe699ac801fd2440cc4b57083a60a334cffa06 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 13:04:00 +0000 Subject: Add pagination tests for environments table Remove fdescribe statement Fix conflict --- .../environments/components/environment.js.es6 | 20 ++++++++++- .../components/environment_item.js.es6 | 27 +------------- .../environments/environment_spec.js.es6 | 42 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index d5bb9f91e7f..6d9599e7645 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -128,11 +128,29 @@ module.exports = Vue.component('environment-component', { /** * Will change the page number and update the URL. * + * If no search params are present, we'll add param for page + * If param for page is already present, we'll update it + * If there are params but none for page, we'll add it at the end. + * * @param {Number} pageNumber desired page to go to. */ changePage(pageNumber) { - const param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`); + let param; + if (window.location.search.length === 0) { + param = `?page=${pageNumber}`; + } + + if (window.location.search.indexOf('page') !== -1) { + param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`); + } + + if (window.location.search.length && + window.location.search.indexOf('page') === -1) { + param = `${window.location.search}&page=${pageNumber}`; + } + gl.utils.visitUrl(param); + return param; }, }, diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index 871a09977a4..fc45c3c5f53 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -357,32 +357,6 @@ module.exports = Vue.component('environment-item', { !this.$options.isObjectEmpty(this.model.latest.last_deployment.deployable); }, - /** - * Verifies the presence of all the keys needed to render the buil_path. - * - * @return {String} - */ - buildPath(){ - return this.model.latest && - this.model.latest.last_deployment && - this.model.latest.last_deployment.deployable && - this.model.latest.last_deployment.deployable.build_path || - ''; - }, - - /** - * Verifies the presence of all the keys needed to render the external_url. - * - * @return {String} - */ - externalURL() { - if (this.model.latest && this.model.latest.external_url) { - return this.model.latest.external_url; - } - - return ''; - }, - /** * Verifies the presence of all the keys needed to render the buil_path. * @@ -398,6 +372,7 @@ module.exports = Vue.component('environment-item', { return ''; }, + /** * Verifies the presence of all the keys needed to render the external_url. * diff --git a/spec/javascripts/environments/environment_spec.js.es6 b/spec/javascripts/environments/environment_spec.js.es6 index 657d8d2ab02..edd0cad32d0 100644 --- a/spec/javascripts/environments/environment_spec.js.es6 +++ b/spec/javascripts/environments/environment_spec.js.es6 @@ -99,6 +99,48 @@ describe('Environment', () => { done(); }, 0); }); + + it('should update url when no search params are present', (done) => { + spyOn(gl.utils, 'visitUrl'); + setTimeout(() => { + component.$el.querySelector('.gl-pagination li:nth-child(5) a').click(); + expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2'); + done(); + }, 0); + }); + + it('should update url when page is already present', (done) => { + spyOn(gl.utils, 'visitUrl'); + window.history.pushState({}, null, '?page=1'); + + setTimeout(() => { + component.$el.querySelector('.gl-pagination li:nth-child(5) a').click(); + expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2'); + done(); + }, 0); + }); + + it('should update url when page and scope are already present', (done) => { + spyOn(gl.utils, 'visitUrl'); + window.history.pushState({}, null, '?scope=all&page=1'); + + setTimeout(() => { + component.$el.querySelector('.gl-pagination li:nth-child(5) a').click(); + expect(gl.utils.visitUrl).toHaveBeenCalledWith('?scope=all&page=2'); + done(); + }, 0); + }); + + it('should update url when page and scope are already present and page is first param', (done) => { + spyOn(gl.utils, 'visitUrl'); + window.history.pushState({}, null, '?page=1&scope=all'); + + setTimeout(() => { + component.$el.querySelector('.gl-pagination li:nth-child(5) a').click(); + expect(gl.utils.visitUrl).toHaveBeenCalledWith('?page=2&scope=all'); + done(); + }, 0); + }); }); }); }); -- cgit v1.2.1 From fc7d2a88f4256a68eb884981fcc2dd99febaa668 Mon Sep 17 00:00:00 2001 From: winniehell Date: Sun, 12 Feb 2017 23:49:29 +0100 Subject: Replace static fixture for merge_request_tabs_spec.js (!9172) --- .../unreleased/merge-request-tabs-fixture.yml | 4 +++ .../fixtures/merge_request_tabs.html.haml | 22 ------------- spec/javascripts/fixtures/merge_requests.rb | 36 ++++++++++++++++++++++ spec/javascripts/merge_request_tabs_spec.js | 4 +-- 4 files changed, 42 insertions(+), 24 deletions(-) create mode 100644 changelogs/unreleased/merge-request-tabs-fixture.yml delete mode 100644 spec/javascripts/fixtures/merge_request_tabs.html.haml create mode 100644 spec/javascripts/fixtures/merge_requests.rb diff --git a/changelogs/unreleased/merge-request-tabs-fixture.yml b/changelogs/unreleased/merge-request-tabs-fixture.yml new file mode 100644 index 00000000000..289cd7b604a --- /dev/null +++ b/changelogs/unreleased/merge-request-tabs-fixture.yml @@ -0,0 +1,4 @@ +--- +title: Replace static fixture for merge_request_tabs_spec.js +merge_request: 9172 +author: winniehell diff --git a/spec/javascripts/fixtures/merge_request_tabs.html.haml b/spec/javascripts/fixtures/merge_request_tabs.html.haml deleted file mode 100644 index 68678c3d7e3..00000000000 --- a/spec/javascripts/fixtures/merge_request_tabs.html.haml +++ /dev/null @@ -1,22 +0,0 @@ -%ul.nav.nav-tabs.merge-request-tabs - %li.notes-tab - %a{href: '/foo/bar/merge_requests/1', data: {target: 'div#notes', action: 'notes', toggle: 'tab'}} - Discussion - %li.commits-tab - %a{href: '/foo/bar/merge_requests/1/commits', data: {target: 'div#commits', action: 'commits', toggle: 'tab'}} - Commits - %li.diffs-tab - %a{href: '/foo/bar/merge_requests/1/diffs', data: {target: 'div#diffs', action: 'diffs', toggle: 'tab'}} - Diffs - -.tab-content - #notes.notes.tab-pane - Notes Content - #commits.commits.tab-pane - Commits Content - #diffs.diffs.tab-pane - Diffs Content - -.mr-loading-status - .loading - Loading Animation diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb new file mode 100644 index 00000000000..62984097099 --- /dev/null +++ b/spec/javascripts/fixtures/merge_requests.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project, namespace: namespace, path: 'merge-requests-project') } + + render_views + + before(:all) do + clean_frontend_fixtures('merge_requests/') + end + + before(:each) do + sign_in(admin) + end + + it 'merge_requests/merge_request_with_task_list.html.raw' do |example| + merge_request = create(:merge_request, :with_diffs, source_project: project, target_project: project, description: '- [ ] Task List Item') + render_merge_request(example.description, merge_request) + end + + private + + def render_merge_request(fixture_file_name, merge_request) + get :show, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: merge_request.to_param + + expect(response).to be_success + store_frontend_fixture(response, fixture_file_name) + end +end diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 92a0f1c05f7..3810991f104 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -25,7 +25,7 @@ require('vendor/jquery.scrollTo'); }; $.extend(stubLocation, defaults, stubs || {}); }; - preloadFixtures('static/merge_request_tabs.html.raw'); + preloadFixtures('merge_requests/merge_request_with_task_list.html.raw'); beforeEach(function () { this.class = new gl.MergeRequestTabs({ stubLocation: stubLocation }); @@ -41,7 +41,7 @@ require('vendor/jquery.scrollTo'); describe('#activateTab', function () { beforeEach(function () { spyOn($, 'ajax').and.callFake(function () {}); - loadFixtures('static/merge_request_tabs.html.raw'); + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); this.subject = this.class.activateTab; }); it('shows the first tab when action is show', function () { -- cgit v1.2.1 From fe5a753be903344a6cc1dc8f1e023b62887e920b Mon Sep 17 00:00:00 2001 From: Alex Braha Stoll Date: Mon, 13 Feb 2017 02:23:54 -0200 Subject: Fix haml-lint violation --- app/views/projects/wikis/_new.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml index d91a7096701..3d33679f07d 100644 --- a/app/views/projects/wikis/_new.html.haml +++ b/app/views/projects/wikis/_new.html.haml @@ -14,7 +14,7 @@ %span Page slug = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true %span.new-wiki-page-slug-tip - =icon('lightbulb-o') + = icon('lightbulb-o') Tip: You can specify the full path for the new file. We will automatically create any missing directories. .form-actions -- cgit v1.2.1 From f2aa9b463086697771e2bf936a7d272307bcbd67 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 10 Feb 2017 16:14:18 -0600 Subject: Fix current build arrow Fix https://gitlab.com/gitlab-org/gitlab-ce/issues/27939 --- app/helpers/builds_helper.rb | 2 +- changelogs/unreleased/27939-fix-current-build-arrow.yml | 4 ++++ spec/features/projects/builds_spec.rb | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27939-fix-current-build-arrow.yml diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb index 9fc69e12266..ff937b5ebd2 100644 --- a/app/helpers/builds_helper.rb +++ b/app/helpers/builds_helper.rb @@ -1,7 +1,7 @@ module BuildsHelper def sidebar_build_class(build, current_build) build_class = '' - build_class += ' active' if build == current_build + build_class += ' active' if build.id === current_build.id build_class += ' retried' if build.retried? build_class end diff --git a/changelogs/unreleased/27939-fix-current-build-arrow.yml b/changelogs/unreleased/27939-fix-current-build-arrow.yml new file mode 100644 index 00000000000..280ab090f2c --- /dev/null +++ b/changelogs/unreleased/27939-fix-current-build-arrow.yml @@ -0,0 +1,4 @@ +--- +title: Fix current build arrow indicator +merge_request: +author: diff --git a/spec/features/projects/builds_spec.rb b/spec/features/projects/builds_spec.rb index f7e0115643e..f1036b275f7 100644 --- a/spec/features/projects/builds_spec.rb +++ b/spec/features/projects/builds_spec.rb @@ -109,6 +109,10 @@ feature 'Builds', :feature do expect(page).to have_content pipeline.git_commit_message expect(page).to have_content pipeline.git_author_name end + + it 'shows active build' do + expect(page).to have_selector('.build-job.active') + end end context "Job from other project" do -- cgit v1.2.1 From 1f94b952b5209180d4ee6cd38256258ac177e7df Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 13 Feb 2017 09:38:26 +0100 Subject: Show notifications settings when repository is disabled --- app/views/projects/_home_panel.html.haml | 4 ++- spec/views/projects/_home_panel.html.haml_spec.rb | 38 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 spec/views/projects/_home_panel.html.haml_spec.rb diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 1b9d87e9969..8d6046e3bc8 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -31,6 +31,8 @@ - if current_user && can?(current_user, :download_code, @project) = render 'projects/buttons/download', project: @project, ref: @ref = render 'projects/buttons/dropdown' - = render 'shared/notifications/button', notification_setting: @notification_setting = render 'projects/buttons/koding' + + - if current_user + = render 'shared/notifications/button', notification_setting: @notification_setting = render 'shared/members/access_request_buttons', source: @project diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb new file mode 100644 index 00000000000..0abd7c524a1 --- /dev/null +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe 'projects/_home_panel', :view do + let(:project) { create(:empty_project, :public) } + + let(:notification_settings) do + user.notification_settings_for(project) if user + end + + before do + assign(:project, project) + assign(:notification_setting, notification_settings) + + allow(view).to receive(:current_user).and_return(user) + allow(view).to receive(:can?).and_return(false) + end + + context 'user is signed in' do + let(:user) { create(:user) } + + it 'makes it possible to set notification level' do + render + + expect(view).to render_template('shared/notifications/_button') + expect(rendered).to have_selector('.notification-dropdown') + end + end + + context 'user is signed out' do + let(:user) { nil } + + it 'is not possible to set notification level' do + render + + expect(rendered).not_to have_selector('.notification_dropdown') + end + end +end -- cgit v1.2.1 From d1d5e2388d692160078647150c3fa134a205a835 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 13 Feb 2017 09:45:10 +0100 Subject: Improve home panel partial code and view specs --- app/views/projects/_home_panel.html.haml | 10 +++++----- spec/views/projects/_home_panel.html.haml_spec.rb | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index 8d6046e3bc8..79a0dc1b959 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -28,11 +28,11 @@ .project-clone-holder = render "shared/clone_panel" - - if current_user && can?(current_user, :download_code, @project) - = render 'projects/buttons/download', project: @project, ref: @ref - = render 'projects/buttons/dropdown' - = render 'projects/buttons/koding' - - if current_user + - if can?(current_user, :download_code, @project) + = render 'projects/buttons/download', project: @project, ref: @ref + = render 'projects/buttons/dropdown' + = render 'projects/buttons/koding' + = render 'shared/notifications/button', notification_setting: @notification_setting = render 'shared/members/access_request_buttons', source: @project diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index 0abd7c524a1..5af57cdf3b7 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -15,7 +15,7 @@ describe 'projects/_home_panel', :view do allow(view).to receive(:can?).and_return(false) end - context 'user is signed in' do + context 'when user is signed in' do let(:user) { create(:user) } it 'makes it possible to set notification level' do @@ -26,7 +26,7 @@ describe 'projects/_home_panel', :view do end end - context 'user is signed out' do + context 'when user is signed out' do let(:user) { nil } it 'is not possible to set notification level' do -- cgit v1.2.1 From ffc041ac19351f471c1766ecbec69c23bc4f90b0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Mon, 13 Feb 2017 09:47:01 +0100 Subject: Add Changelog entry for notifications buttons fix --- .../unreleased/fix-gb-notification-settings-when-no-repository.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml diff --git a/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml b/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml new file mode 100644 index 00000000000..17fd1336b8e --- /dev/null +++ b/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml @@ -0,0 +1,4 @@ +--- +title: Show notifications settings dropdown even if repository feature is disabled +merge_request: 9180 +author: -- cgit v1.2.1 From 188c231304845ff29506dc152aaea6ec42373015 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 13 Feb 2017 21:01:35 +0800 Subject: Add a test to make sure the queue ticks when updating runners --- spec/requests/api/runners_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index f2d81a28cb8..daef9062e7d 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -183,6 +183,7 @@ describe API::Runners, api: true do it 'updates runner' do description = shared_runner.description active = shared_runner.active + runner_queue_value = shared_runner.ensure_runner_queue_value update_runner(shared_runner.id, admin, description: "#{description}_updated", active: !active, @@ -197,18 +198,24 @@ describe API::Runners, api: true do expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql') expect(shared_runner.run_untagged?).to be(false) expect(shared_runner.locked?).to be(true) + expect(shared_runner.ensure_runner_queue_value) + .not_to eq(runner_queue_value) end end context 'when runner is not shared' do it 'updates runner' do description = specific_runner.description + runner_queue_value = specific_runner.ensure_runner_queue_value + update_runner(specific_runner.id, admin, description: 'test') specific_runner.reload expect(response).to have_http_status(200) expect(specific_runner.description).to eq('test') expect(specific_runner.description).not_to eq(description) + expect(specific_runner.ensure_runner_queue_value) + .not_to eq(runner_queue_value) end end -- cgit v1.2.1 From 7bb9cb1b0815b0b7c551e6ea74457cf099dba3b6 Mon Sep 17 00:00:00 2001 From: Pedro Moreira da Silva Date: Mon, 13 Feb 2017 13:27:49 +0000 Subject: Fix UX Guide apostrophe use for singular common nouns --- doc/development/ux_guide/copy.md | 384 +++++++++++++++++++-------------------- 1 file changed, 192 insertions(+), 192 deletions(-) diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md index 5b65d531e54..9414aafaadf 100644 --- a/doc/development/ux_guide/copy.md +++ b/doc/development/ux_guide/copy.md @@ -1,193 +1,193 @@ -# Copy - -The copy for GitLab is clear and direct. We strike a clear balance between professional and friendly. We can empathesize with users (such as celebrating completing all Todos), and remain respectful of the importance of the work. We are that trusted, friendly coworker that is helpful and understanding. - -The copy and messaging is a core part of the experience of GitLab and the conversation with our users. Follow the below conventions throughout GitLab. - -Portions of this page are inspired by work found in the [Material Design guidelines][material design]. - ->**Note:** -We are currently inconsistent with this guidance. Images below are created to illustrate the point. As this guidance is refined, we will ensure that our experiences align. - -## Contents -* [Brevity](#brevity) -* [Capitalization and punctuation](#capitalization-and-punctuation) -* [Terminology](#terminology) - ---- - -## Brevity -Users will skim content, rather than read text carefully. -When familiar with a web app, users rely on muscle memory, and may read even less when moving quickly. -A good experience should quickly orient a user, regardless of their experience, to the purpose of the current screen. This should happen without the user having to consciously read long strings of text. -In general, text is burdensome and adds cognitive load. This is especially pronounced in a powerful productivity tool such as GitLab. -We should _not_ rely on words as a crutch to explain the purpose of a screen. -The current navigation and composition of the elements on the screen should get the user 95% there, with the remaining 5% being specific elements such as text. -This means that, as a rule, copy should be very short. A long message or label is a red flag hinting at design that needs improvement. - ->**Example:** -Use `Add` instead of `Add issue` as a button label. -Preferrably use context and placement of controls to make it obvious what clicking on them will do. - ---- - -## Capitalization and punctuation - -### Case -Use sentence case for titles, headings, labels, menu items, and buttons. Use title case when referring to [features][features] or [products][products]. Note that some features are also objects (e.g. “Merge Requests” and “merge requests”). - -| :white_check_mark: Do | :no_entry_sign: Don’t | -| --- | --- | -| Add issues to the Issue Board feature in GitLab Hosted | Add Issues To The Issue Board Feature In GitLab Hosted | - -### Avoid periods -Avoid using periods in solitary sentences in these elements: - -* Labels -* Hover text -* Bulleted lists -* Dialog body text - -Periods should be used for: - -* Lists or dialogs with multiple sentences -* Any sentence followed by a link - -| :white_check_mark: **Do** place periods after sentences followed by a link | :no_entry_sign: **Don’t** place periods after a link if it‘s not followed by a sentence | -| --- | --- | -| Mention someone to notify them. [Learn more](#) | Mention someone to notify them. [Learn more](#). | - -| :white_check_mark: **Do** skip periods after solo sentences of body text | :no_entry_sign: **Don’t** place periods after body text if there is only a single sentence | -| --- | --- | -| To see the available commands, enter `/gitlab help` | To see the available commands, enter `/gitlab help`. | - -### Use contractions -Don’t make a sentence harder to understand just to follow this rule. For example, “do not” can give more emphasis than “don’t” when needed. - -| :white_check_mark: Do | :no_entry_sign: Don’t | -| --- | --- | -| it’s, can’t, wouldn’t, you’re, you’ve, haven’t, don’t | it is, cannot, would not, it’ll, should’ve | - -### Use numerals for numbers -Use “1, 2, 3” instead of “one, two, three” for numbers. One exception is when mixing uses of numbers, such as “Enter two 3s.” - -| :white_check_mark: Do | :no_entry_sign: Don’t | -| --- | --- | -| 3 new commits | Three new commits | - -### Punctuation -Omit punctuation after phrases and labels to create a cleaner and more readable interface. Use punctuation to add clarity or be grammatically correct. - -| Punctuation mark | Copy and paste | HTML entity | Unicode | Mac shortcut | Windows shortcut | Description | -|---|---|---|---|---|---|---| -| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and dialog body text.

    Use in lists or dialogs with multiple sentences, and any sentence followed by a link or inline code.

    Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. | -| Comma | **,** | | | | | Place inside quotation marks.

    Use a [serial comma][serial comma] in lists of three or more terms. | -| Exclamation point | **!** | | | | | Avoid exclamation points as they tend to come across as shouting. Some exceptions include greetings or congratulatory messages. | -| Colon | **:** | `:` | `\u003A` | | | Omit from labels, for example, in the labels for fields in a form. | -| Apostrophe | **’** | `’` | `\u2019` | ⌥ Option+⇧ Shift+] | Alt+0 1 4 6 | Use for contractions (I’m, you’re, ’89) and to show possession.

    To show possession, add an *’s* to all single nouns and names, even if they already end in an *s*: “Your issues’s status was changed.” For singular proper names ending in *s*, use only an apostrophe: “James’ commits.” For plurals of a single letter, add an *’s*: “Dot your i’s and cross your t’s.”

    Omit for decades or acronyms: “the 1990s”, “MRs.” | -| Quotation marks | **“**

    **”**

    **‘**

    **’** | `“`

    `”`

    `‘`

    `’` | `\u201C`

    `\u201D`

    `\u2018`

    `\u2019` | ⌥ Option+[

    ⌥ Option+⇧ Shift+[

    ⌥ Option+]

    ⌥ Option+⇧ Shift+] | Alt+0 1 4 7

    Alt+0 1 4 8

    Alt+0 1 4 5

    Alt+0 1 4 6 | Use proper quotation marks (also known as smart quotes, curly quotes, or typographer’s quotes) for quotes. Single quotation marks are used for quotes inside of quotes.

    The right single quotation mark symbol is also used for apostrophes.

    Don’t use primes, straight quotes, or free-standing accents for quotation marks. | -| Primes | **′**

    **″** | `′`

    `″` | `\u2032`

    `\u2033` | | Alt+8 2 4 2

    Alt+8 2 4 3 | Use prime (′) only in abbreviations for feet, arcminutes, and minutes: 3° 15′

    Use double-prime (″) only in abbreviations for inches, arcseconds, and seconds: 3° 15′ 35″

    Don’t use quotation marks, straight quotes, or free-standing accents for primes. | -| Straight quotes and accents | **"**

    **'**

    **`**

    **´** | `"`

    `'`

    ```

    `´` | `\u0022`

    `\u0027`

    `\u0060`

    `\u00B4` | | | Don’t use straight quotes or free-standing accents for primes or quotation marks.

    Proper typography never uses straight quotes. They are left over from the age of typewriters and their only modern use is for code. | -| Ellipsis | **…** | `…` | | ⌥ Option+; | Alt+0 1 3 3 | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.

    Omit from menu items or buttons that open a dialog or start some other process. | -| Chevrons | **«**

    **»**

    **‹**

    **›**

    **<**

    **>** | `«`

    `»`

    `‹`

    `›`

    `<`

    `>` | `\u00AB`

    `\u00BB`

    `\u2039`

    `\u203A`

    `\u003C`

    `\u003E`

    | | | Omit from links or buttons that open another page or move to the next or previous step in a process. Also known as angle brackets, angular quote brackets, or guillemets. | -| Em dash | **—** | `—` | `\u2014` | ⌥ Option+⇧ Shift+- | Alt+0 1 5 1 | Avoid using dashes to separate text. If you must use dashes for this purpose — like this — use an em dash surrounded by spaces. | -| En dash | **–** | `–` | `\u2013` | ⌥ Option+- | Alt+0 1 5 0 | Use an en dash without spaces instead of a hyphen to indicate a range of values, such as numbers, times, and dates: “3–5 kg”, “8:00 AM–12:30 PM”, “10–17 Jan” | -| Hyphen | **-** | | | | | Use to represent negative numbers, or to avoid ambiguity in adjective-noun or noun-participle pairs. Example: “anti-inflammatory”; “5-mile walk.”

    Omit in commonly understood terms and adverbs that end in *ly*: “frontend”, “greatly improved performance.”

    Omit in the term “open source.” | -| Parentheses | **( )** | | | | | Use only to define acronyms or jargon: “Secure web connections are based on a technology called SSL (the secure sockets layer).”

    Avoid other uses and instead rewrite the text, or use dashes or commas to set off the information. If parentheses are required: If the parenthetical is a complete, independent sentence, place the period inside the parentheses; if not, the period goes outside. | - -When using the Alt keystrokes in Windows, use the numeric keypad, not the row of numbers above the alphabet, and be sure Num Lock is turned on. - ---- - -## Terminology -Only use the terms in the tables below. - -### Projects and Groups - -| Term | Use | :no_entry_sign: Don't | -| ---- | --- | ----- | -| Members | When discussing the people who are a part of a project or a group. | Don't use `users`. | - -### Issues - -#### Adjectives (states) - -| Term | -| ---- | -| Open | -| Closed | -| Deleted | - ->**Example:** -Use `5 open issues` and don’t use `5 pending issues`. - -#### Verbs (actions) - -| Term | Use | :no_entry_sign: Don’t | -| ---- | --- | --- | -| Add | Add an issue | Don’t use `create` or `new` | -| View | View an open or closed issue || -| Edit | Edit an open or closed issue | Don’t use `update` | -| Close | Close an open issue || -| Re-open | Re-open a closed issue | There should never be a need to use `open` as a verb | -| Delete | Delete an open or closed issue || - -#### Add issue - -When viewing a list of issues, there is a button that is labeled `Add`. Given the context in the example, it is clearly referring to issues. If the context were not clear enough, the label could be `Add issue`. Clicking the button will bring you to the `Add issue` form. Other add flows should be similar. - -![Add issue button](img/copy-form-addissuebutton.png) - -The form should be titled `Add issue`. The submit button should be labeled `Submit`. Don’t use `Add`, `Create`, `New`, or `Save changes`. The cancel button should be labeled `Cancel`. Don’t use `Back`. - -![Add issue form](img/copy-form-addissueform.png) - -#### Edit issue - -When in context of an issue, the affordance to edit it is labeled `Edit`. If the context is not clear enough, `Edit issue` could be considered. Other edit flows should be similar. - -![Edit issue button](img/copy-form-editissuebutton.png) - -The form should be titled `Edit issue`. The submit button should be labeled `Save`. Don’t use `Edit`, `Update`, `Submit`, or `Save changes`. The cancel button should be labeled `Cancel`. Don’t use `Back`. - -![Edit issue form](img/copy-form-editissueform.png) - - -### Merge requests - -#### Adjectives (states) - -| Term | -| ---- | -| Open | -| Merged | - -#### Verbs (actions) - -| Term | Use | :no_entry_sign: Don’t | -| ---- | --- | --- | -| Add | Add a merge request | Do not use `create` or `new` | -| View | View an open or merged merge request || -| Edit | Edit an open or merged merge request| Do not use `update` | -| Approve | Approve an open merge request || -| Remove approval, unapproved | Remove approval of an open merge request | Do not use `unapprove` as that is not an English word| -| Merge | Merge an open merge request || - -### Comments & Discussions - -#### Comment -A **comment** is a written piece of text that users of GitLab can create. Comments have the meta data of author and timestamp. Comments can be added in a variety of contexts, such as issues, merge requests, and discussions. - -#### Discussion -A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved. - ---- - -Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons]. - -[material design]: https://material.io/guidelines/ -[features]: https://about.gitlab.com/features/ "GitLab features page" -[products]: https://about.gitlab.com/products/ "GitLab products page" -[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia" -[android project]: http://source.android.com/ +# Copy + +The copy for GitLab is clear and direct. We strike a clear balance between professional and friendly. We can empathesize with users (such as celebrating completing all Todos), and remain respectful of the importance of the work. We are that trusted, friendly coworker that is helpful and understanding. + +The copy and messaging is a core part of the experience of GitLab and the conversation with our users. Follow the below conventions throughout GitLab. + +Portions of this page are inspired by work found in the [Material Design guidelines][material design]. + +>**Note:** +We are currently inconsistent with this guidance. Images below are created to illustrate the point. As this guidance is refined, we will ensure that our experiences align. + +## Contents +* [Brevity](#brevity) +* [Capitalization and punctuation](#capitalization-and-punctuation) +* [Terminology](#terminology) + +--- + +## Brevity +Users will skim content, rather than read text carefully. +When familiar with a web app, users rely on muscle memory, and may read even less when moving quickly. +A good experience should quickly orient a user, regardless of their experience, to the purpose of the current screen. This should happen without the user having to consciously read long strings of text. +In general, text is burdensome and adds cognitive load. This is especially pronounced in a powerful productivity tool such as GitLab. +We should _not_ rely on words as a crutch to explain the purpose of a screen. +The current navigation and composition of the elements on the screen should get the user 95% there, with the remaining 5% being specific elements such as text. +This means that, as a rule, copy should be very short. A long message or label is a red flag hinting at design that needs improvement. + +>**Example:** +Use `Add` instead of `Add issue` as a button label. +Preferrably use context and placement of controls to make it obvious what clicking on them will do. + +--- + +## Capitalization and punctuation + +### Case +Use sentence case for titles, headings, labels, menu items, and buttons. Use title case when referring to [features][features] or [products][products]. Note that some features are also objects (e.g. “Merge Requests” and “merge requests”). + +| :white_check_mark: Do | :no_entry_sign: Don’t | +| --- | --- | +| Add issues to the Issue Board feature in GitLab Hosted | Add Issues To The Issue Board Feature In GitLab Hosted | + +### Avoid periods +Avoid using periods in solitary sentences in these elements: + +* Labels +* Hover text +* Bulleted lists +* Dialog body text + +Periods should be used for: + +* Lists or dialogs with multiple sentences +* Any sentence followed by a link + +| :white_check_mark: **Do** place periods after sentences followed by a link | :no_entry_sign: **Don’t** place periods after a link if it‘s not followed by a sentence | +| --- | --- | +| Mention someone to notify them. [Learn more](#) | Mention someone to notify them. [Learn more](#). | + +| :white_check_mark: **Do** skip periods after solo sentences of body text | :no_entry_sign: **Don’t** place periods after body text if there is only a single sentence | +| --- | --- | +| To see the available commands, enter `/gitlab help` | To see the available commands, enter `/gitlab help`. | + +### Use contractions +Don’t make a sentence harder to understand just to follow this rule. For example, “do not” can give more emphasis than “don’t” when needed. + +| :white_check_mark: Do | :no_entry_sign: Don’t | +| --- | --- | +| it’s, can’t, wouldn’t, you’re, you’ve, haven’t, don’t | it is, cannot, would not, it’ll, should’ve | + +### Use numerals for numbers +Use “1, 2, 3” instead of “one, two, three” for numbers. One exception is when mixing uses of numbers, such as “Enter two 3s.” + +| :white_check_mark: Do | :no_entry_sign: Don’t | +| --- | --- | +| 3 new commits | Three new commits | + +### Punctuation +Omit punctuation after phrases and labels to create a cleaner and more readable interface. Use punctuation to add clarity or be grammatically correct. + +| Punctuation mark | Copy and paste | HTML entity | Unicode | Mac shortcut | Windows shortcut | Description | +|---|---|---|---|---|---|---| +| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and dialog body text.

    Use in lists or dialogs with multiple sentences, and any sentence followed by a link or inline code.

    Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. | +| Comma | **,** | | | | | Place inside quotation marks.

    Use a [serial comma][serial comma] in lists of three or more terms. | +| Exclamation point | **!** | | | | | Avoid exclamation points as they tend to come across as shouting. Some exceptions include greetings or congratulatory messages. | +| Colon | **:** | `:` | `\u003A` | | | Omit from labels, for example, in the labels for fields in a form. | +| Apostrophe | **’** | `’` | `\u2019` | ⌥ Option+⇧ Shift+] | Alt+0 1 4 6 | Use for contractions (I’m, you’re, ’89) and to show possession.

    To show possession, add an *’s* to all singular common nouns and names, even if they already end in an *s*: “Look into this worker process’s log.” For singular proper names ending in *s*, use only an apostrophe: “James’ commits.” For plurals of a single letter, add an *’s*: “Dot your i’s and cross your t’s.”

    Omit for decades or acronyms: “the 1990s”, “MRs.” | +| Quotation marks | **“**

    **”**

    **‘**

    **’** | `“`

    `”`

    `‘`

    `’` | `\u201C`

    `\u201D`

    `\u2018`

    `\u2019` | ⌥ Option+[

    ⌥ Option+⇧ Shift+[

    ⌥ Option+]

    ⌥ Option+⇧ Shift+] | Alt+0 1 4 7

    Alt+0 1 4 8

    Alt+0 1 4 5

    Alt+0 1 4 6 | Use proper quotation marks (also known as smart quotes, curly quotes, or typographer’s quotes) for quotes. Single quotation marks are used for quotes inside of quotes.

    The right single quotation mark symbol is also used for apostrophes.

    Don’t use primes, straight quotes, or free-standing accents for quotation marks. | +| Primes | **′**

    **″** | `′`

    `″` | `\u2032`

    `\u2033` | | Alt+8 2 4 2

    Alt+8 2 4 3 | Use prime (′) only in abbreviations for feet, arcminutes, and minutes: 3° 15′

    Use double-prime (″) only in abbreviations for inches, arcseconds, and seconds: 3° 15′ 35″

    Don’t use quotation marks, straight quotes, or free-standing accents for primes. | +| Straight quotes and accents | **"**

    **'**

    **`**

    **´** | `"`

    `'`

    ```

    `´` | `\u0022`

    `\u0027`

    `\u0060`

    `\u00B4` | | | Don’t use straight quotes or free-standing accents for primes or quotation marks.

    Proper typography never uses straight quotes. They are left over from the age of typewriters and their only modern use is for code. | +| Ellipsis | **…** | `…` | | ⌥ Option+; | Alt+0 1 3 3 | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.

    Omit from menu items or buttons that open a dialog or start some other process. | +| Chevrons | **«**

    **»**

    **‹**

    **›**

    **<**

    **>** | `«`

    `»`

    `‹`

    `›`

    `<`

    `>` | `\u00AB`

    `\u00BB`

    `\u2039`

    `\u203A`

    `\u003C`

    `\u003E`

    | | | Omit from links or buttons that open another page or move to the next or previous step in a process. Also known as angle brackets, angular quote brackets, or guillemets. | +| Em dash | **—** | `—` | `\u2014` | ⌥ Option+⇧ Shift+- | Alt+0 1 5 1 | Avoid using dashes to separate text. If you must use dashes for this purpose — like this — use an em dash surrounded by spaces. | +| En dash | **–** | `–` | `\u2013` | ⌥ Option+- | Alt+0 1 5 0 | Use an en dash without spaces instead of a hyphen to indicate a range of values, such as numbers, times, and dates: “3–5 kg”, “8:00 AM–12:30 PM”, “10–17 Jan” | +| Hyphen | **-** | | | | | Use to represent negative numbers, or to avoid ambiguity in adjective-noun or noun-participle pairs. Example: “anti-inflammatory”; “5-mile walk.”

    Omit in commonly understood terms and adverbs that end in *ly*: “frontend”, “greatly improved performance.”

    Omit in the term “open source.” | +| Parentheses | **( )** | | | | | Use only to define acronyms or jargon: “Secure web connections are based on a technology called SSL (the secure sockets layer).”

    Avoid other uses and instead rewrite the text, or use dashes or commas to set off the information. If parentheses are required: If the parenthetical is a complete, independent sentence, place the period inside the parentheses; if not, the period goes outside. | + +When using the Alt keystrokes in Windows, use the numeric keypad, not the row of numbers above the alphabet, and be sure Num Lock is turned on. + +--- + +## Terminology +Only use the terms in the tables below. + +### Projects and Groups + +| Term | Use | :no_entry_sign: Don't | +| ---- | --- | ----- | +| Members | When discussing the people who are a part of a project or a group. | Don't use `users`. | + +### Issues + +#### Adjectives (states) + +| Term | +| ---- | +| Open | +| Closed | +| Deleted | + +>**Example:** +Use `5 open issues` and don’t use `5 pending issues`. + +#### Verbs (actions) + +| Term | Use | :no_entry_sign: Don’t | +| ---- | --- | --- | +| Add | Add an issue | Don’t use `create` or `new` | +| View | View an open or closed issue || +| Edit | Edit an open or closed issue | Don’t use `update` | +| Close | Close an open issue || +| Re-open | Re-open a closed issue | There should never be a need to use `open` as a verb | +| Delete | Delete an open or closed issue || + +#### Add issue + +When viewing a list of issues, there is a button that is labeled `Add`. Given the context in the example, it is clearly referring to issues. If the context were not clear enough, the label could be `Add issue`. Clicking the button will bring you to the `Add issue` form. Other add flows should be similar. + +![Add issue button](img/copy-form-addissuebutton.png) + +The form should be titled `Add issue`. The submit button should be labeled `Submit`. Don’t use `Add`, `Create`, `New`, or `Save changes`. The cancel button should be labeled `Cancel`. Don’t use `Back`. + +![Add issue form](img/copy-form-addissueform.png) + +#### Edit issue + +When in context of an issue, the affordance to edit it is labeled `Edit`. If the context is not clear enough, `Edit issue` could be considered. Other edit flows should be similar. + +![Edit issue button](img/copy-form-editissuebutton.png) + +The form should be titled `Edit issue`. The submit button should be labeled `Save`. Don’t use `Edit`, `Update`, `Submit`, or `Save changes`. The cancel button should be labeled `Cancel`. Don’t use `Back`. + +![Edit issue form](img/copy-form-editissueform.png) + + +### Merge requests + +#### Adjectives (states) + +| Term | +| ---- | +| Open | +| Merged | + +#### Verbs (actions) + +| Term | Use | :no_entry_sign: Don’t | +| ---- | --- | --- | +| Add | Add a merge request | Do not use `create` or `new` | +| View | View an open or merged merge request || +| Edit | Edit an open or merged merge request| Do not use `update` | +| Approve | Approve an open merge request || +| Remove approval, unapproved | Remove approval of an open merge request | Do not use `unapprove` as that is not an English word| +| Merge | Merge an open merge request || + +### Comments & Discussions + +#### Comment +A **comment** is a written piece of text that users of GitLab can create. Comments have the meta data of author and timestamp. Comments can be added in a variety of contexts, such as issues, merge requests, and discussions. + +#### Discussion +A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved. + +--- + +Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons]. + +[material design]: https://material.io/guidelines/ +[features]: https://about.gitlab.com/features/ "GitLab features page" +[products]: https://about.gitlab.com/products/ "GitLab products page" +[serial comma]: https://en.wikipedia.org/wiki/Serial_comma "“Serial comma” in Wikipedia" +[android project]: http://source.android.com/ [creative commons]: http://creativecommons.org/licenses/by/2.5/ \ No newline at end of file -- cgit v1.2.1 From a9924ee4d299b96da1e8dd58a6c9ed42b5dfacbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 9 Feb 2017 12:19:32 +0100 Subject: Don't instantiate AR objects in Event.in_projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/event.rb | 2 +- changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml diff --git a/app/models/event.rb b/app/models/event.rb index 2662f170765..c90fee95426 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -36,7 +36,7 @@ class Event < ActiveRecord::Base scope :code_push, -> { where(action: PUSHED) } scope :in_projects, ->(projects) do - where(project_id: projects.map(&:id)).recent + where(project_id: projects).recent end scope :with_associations, -> { includes(project: :namespace) } diff --git a/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml b/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml new file mode 100644 index 00000000000..3f6d922f2a0 --- /dev/null +++ b/changelogs/unreleased/27395-reduce-group-activity-sql-queries.yml @@ -0,0 +1,4 @@ +--- +title: Don't instantiate AR objects in Event.in_projects +merge_request: +author: -- cgit v1.2.1 From 473b04a9f07ded15538af23f986ec618f5f8160c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 9 Feb 2017 12:19:55 +0100 Subject: Include :author, :project, and :target in Event.with_associations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/event.rb | 2 +- changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml diff --git a/app/models/event.rb b/app/models/event.rb index c90fee95426..57f441187b4 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -39,7 +39,7 @@ class Event < ActiveRecord::Base where(project_id: projects).recent end - scope :with_associations, -> { includes(project: :namespace) } + scope :with_associations, -> { includes(:author, :project, :target, project: :namespace) } scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } class << self diff --git a/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml b/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml new file mode 100644 index 00000000000..f3ce1709518 --- /dev/null +++ b/changelogs/unreleased/27395-reduce-group-activity-sql-queries-2.yml @@ -0,0 +1,4 @@ +--- +title: Include :author, :project, and :target in Event.with_associations +merge_request: +author: -- cgit v1.2.1 From 1698d71ccfce229b8d7c36a87a67764d53e97c8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 13 Feb 2017 15:19:03 +0100 Subject: Use preload for Event#target since it's a polymorphic association MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also, don't use limit in subquery, MySQL don't like that. Signed-off-by: Rémy Coutable --- app/controllers/dashboard/projects_controller.rb | 23 +++++++++++------------ app/models/event.rb | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 3ba8c2f8bb9..325ae565537 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -1,19 +1,14 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController include FilterProjects - before_action :event_filter - def index - @projects = current_user.authorized_projects.sorted_by_activity - @projects = filter_projects(@projects) - @projects = @projects.includes(:namespace) + @projects = load_projects(current_user.authorized_projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]) respond_to do |format| format.html { @last_push = current_user.recent_push } format.atom do - event_filter load_events render layout: false end @@ -26,9 +21,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController end def starred - @projects = current_user.viewable_starred_projects.sorted_by_activity - @projects = filter_projects(@projects) - @projects = @projects.includes(:namespace, :forked_from_project, :tags) + @projects = load_projects(current_user.viewable_starred_projects) + @projects = @projects.includes(:forked_from_project, :tags) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]) @@ -37,7 +31,6 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController respond_to do |format| format.html - format.json do render json: { html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects }) @@ -48,9 +41,15 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController private + def load_projects(base_scope) + projects = base_scope.sorted_by_activity.includes(:namespace) + + filter_projects(projects) + end + def load_events - @events = Event.in_projects(@projects) - @events = @event_filter.apply_filter(@events).with_associations + @events = Event.in_projects(load_projects(current_user.authorized_projects)) + @events = event_filter.apply_filter(@events).with_associations @events = @events.limit(20).offset(params[:offset] || 0) end end diff --git a/app/models/event.rb b/app/models/event.rb index 57f441187b4..cf89ac5207f 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -39,7 +39,7 @@ class Event < ActiveRecord::Base where(project_id: projects).recent end - scope :with_associations, -> { includes(:author, :project, :target, project: :namespace) } + scope :with_associations, -> { includes(:author, :project, project: :namespace).preload(:target) } scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } class << self -- cgit v1.2.1 From 950e2986656e17948ef227985d786ee22fb92fb2 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 08:36:24 -0600 Subject: Change wording of MR comment box --- app/views/projects/notes/_hints.html.haml | 1 - spec/views/projects/notes/_form.html.haml_spec.rb | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml index 6c14f48d41b..81d97eabe65 100644 --- a/app/views/projects/notes/_hints.html.haml +++ b/app/views/projects/notes/_hints.html.haml @@ -1,7 +1,6 @@ - supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false) .comment-toolbar.clearfix .toolbar-text - Styling with = link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1 - if supports_slash_commands and diff --git a/spec/views/projects/notes/_form.html.haml_spec.rb b/spec/views/projects/notes/_form.html.haml_spec.rb index b14b1ece2d0..b61f016967f 100644 --- a/spec/views/projects/notes/_form.html.haml_spec.rb +++ b/spec/views/projects/notes/_form.html.haml_spec.rb @@ -21,7 +21,7 @@ describe 'projects/notes/_form' do let(:note) { build(:"note_on_#{noteable}", project: project) } it 'says that only markdown is supported, not slash commands' do - expect(rendered).to have_content('Styling with Markdown and slash commands are supported') + expect(rendered).to have_content('Markdown and slash commands are supported') end end end @@ -30,7 +30,7 @@ describe 'projects/notes/_form' do let(:note) { build(:note_on_commit, project: project) } it 'says that only markdown is supported, not slash commands' do - expect(rendered).to have_content('Styling with Markdown is supported') + expect(rendered).to have_content('Markdown is supported') end end end -- cgit v1.2.1 From e31b982a13413151dd7317ee15aadcbde0f72edb Mon Sep 17 00:00:00 2001 From: Pawel Chojnacki Date: Tue, 7 Feb 2017 13:56:50 +0100 Subject: Make deploy key not show in User's keys list --- app/models/user.rb | 7 +++++- ..._keys_should_not_show_up_in_users_keys_list.yml | 4 +++ spec/controllers/profiles/keys_controller_spec.rb | 19 +++++++------- spec/factories/keys.rb | 7 ++++-- spec/models/user_spec.rb | 29 ++++++++++++++++++++++ 5 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/27480-deploy_keys_should_not_show_up_in_users_keys_list.yml diff --git a/app/models/user.rb b/app/models/user.rb index 867e61af56a..1649bf04eaa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -51,7 +51,12 @@ class User < ActiveRecord::Base has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id # Profile - has_many :keys, dependent: :destroy + has_many :keys, -> do + type = Key.arel_table[:type] + where(type.not_eq('DeployKey').or(type.eq(nil))) + end, dependent: :destroy + has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy + has_many :emails, dependent: :destroy has_many :personal_access_tokens, dependent: :destroy has_many :identities, dependent: :destroy, autosave: true diff --git a/changelogs/unreleased/27480-deploy_keys_should_not_show_up_in_users_keys_list.yml b/changelogs/unreleased/27480-deploy_keys_should_not_show_up_in_users_keys_list.yml new file mode 100644 index 00000000000..6e9192cb632 --- /dev/null +++ b/changelogs/unreleased/27480-deploy_keys_should_not_show_up_in_users_keys_list.yml @@ -0,0 +1,4 @@ +--- +title: Do not display deploy keys in user's own ssh keys list +merge_request: 9024 +author: diff --git a/spec/controllers/profiles/keys_controller_spec.rb b/spec/controllers/profiles/keys_controller_spec.rb index 6bcfae0fc13..f7219690722 100644 --- a/spec/controllers/profiles/keys_controller_spec.rb +++ b/spec/controllers/profiles/keys_controller_spec.rb @@ -42,10 +42,9 @@ describe Profiles::KeysController do end describe "user with keys" do - before do - user.keys << create(:key) - user.keys << create(:another_key) - end + let!(:key) { create(:key, user: user) } + let!(:another_key) { create(:another_key, user: user) } + let!(:deploy_key) { create(:deploy_key, user: user) } it "does generally work" do get :get_keys, username: user.username @@ -53,16 +52,16 @@ describe Profiles::KeysController do expect(response).to be_success end - it "renders all keys separated with a new line" do + it "renders all non deploy keys separated with a new line" do get :get_keys, username: user.username - expect(response.body).not_to eq("") + expect(response.body).not_to eq('') expect(response.body).to eq(user.all_ssh_keys.join("\n")) - # Unique part of key 1 - expect(response.body).to match(/PWx6WM4lhHNedGfBpPJNPpZ/) - # Key 2 - expect(response.body).to match(/AQDmTillFzNTrrGgwaCKaSj/) + expect(response.body).to include(key.key.sub(' dummy@gitlab.com', '')) + expect(response.body).to include(another_key.key) + + expect(response.body).not_to include(deploy_key.key) end it "does not render the comment of the key" do diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb index d69c5b38d0a..dd93b439b2b 100644 --- a/spec/factories/keys.rb +++ b/spec/factories/keys.rb @@ -2,10 +2,13 @@ FactoryGirl.define do factory :key do title key do - "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= dummy@gitlab.com" + 'ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0= dummy@gitlab.com' end factory :deploy_key, class: 'DeployKey' do + key do + 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O96x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5/jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaCrzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy05qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz' + end end factory :personal_key do @@ -14,7 +17,7 @@ FactoryGirl.define do factory :another_key do key do - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmTillFzNTrrGgwaCKaSj+QCz81E6jBc/s9av0+3b1Hwfxgkqjl4nAK/OD2NjgyrONDTDfR8cRN4eAAy6nY8GLkOyYBDyuc5nTMqs5z3yVuTwf3koGm/YQQCmo91psZ2BgDFTor8SVEE5Mm1D1k3JDMhDFxzzrOtRYFPci9lskTJaBjpqWZ4E9rDTD2q/QZntCqbC3wE9uSemRQB5f8kik7vD/AD8VQXuzKladrZKkzkONCPWsXDspUitjM8HkQdOf0PsYn1CMUC1xKYbCxkg5TkEosIwGv6CoEArUrdu/4+10LVslq494mAvEItywzrluCLCnwELfW+h/m8UHoVhZ" + 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDmTillFzNTrrGgwaCKaSj+QCz81E6jBc/s9av0+3b1Hwfxgkqjl4nAK/OD2NjgyrONDTDfR8cRN4eAAy6nY8GLkOyYBDyuc5nTMqs5z3yVuTwf3koGm/YQQCmo91psZ2BgDFTor8SVEE5Mm1D1k3JDMhDFxzzrOtRYFPci9lskTJaBjpqWZ4E9rDTD2q/QZntCqbC3wE9uSemRQB5f8kik7vD/AD8VQXuzKladrZKkzkONCPWsXDspUitjM8HkQdOf0PsYn1CMUC1xKYbCxkg5TkEosIwGv6CoEArUrdu/4+10LVslq494mAvEItywzrluCLCnwELfW+h/m8UHoVhZ' end factory :another_deploy_key, class: 'DeployKey' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 7fd49c73b37..89cef7ab978 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -19,6 +19,7 @@ describe User, models: true do it { is_expected.to have_many(:project_members).dependent(:destroy) } it { is_expected.to have_many(:groups) } it { is_expected.to have_many(:keys).dependent(:destroy) } + it { is_expected.to have_many(:deploy_keys).dependent(:destroy) } it { is_expected.to have_many(:events).dependent(:destroy) } it { is_expected.to have_many(:recent_events).class_name('Event') } it { is_expected.to have_many(:issues).dependent(:destroy) } @@ -303,6 +304,34 @@ describe User, models: true do end end + shared_context 'user keys' do + let(:user) { create(:user) } + let!(:key) { create(:key, user: user) } + let!(:deploy_key) { create(:deploy_key, user: user) } + end + + describe '#keys' do + include_context 'user keys' + + context 'with key and deploy key stored' do + it 'returns stored key, but not deploy_key' do + expect(user.keys).to include key + expect(user.keys).not_to include deploy_key + end + end + end + + describe '#deploy_keys' do + include_context 'user keys' + + context 'with key and deploy key stored' do + it 'returns stored deploy key, but not normal key' do + expect(user.deploy_keys).to include deploy_key + expect(user.deploy_keys).not_to include key + end + end + end + describe '#confirm' do before do allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) -- cgit v1.2.1 From 217937e5699326e2897a8919f421b401d077225c Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Mon, 13 Feb 2017 16:04:06 +0100 Subject: Show mentioned/issues being closed from a Merge Requests title --- app/models/merge_request.rb | 4 +-- ...33-close-issues-with-merge-request-title-ui.yml | 5 ++++ spec/features/merge_requests/closes_issues_spec.rb | 32 ++++++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c0d4dd0197f..38646eba3ac 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -561,7 +561,7 @@ class MergeRequest < ActiveRecord::Base # Return the set of issues that will be closed if this merge request is accepted. def closes_issues(current_user = self.author) if target_branch == project.default_branch - messages = [description] + messages = [title, description] messages.concat(commits.map(&:safe_message)) if merge_request_diff Gitlab::ClosingIssueExtractor.new(project, current_user). @@ -575,7 +575,7 @@ class MergeRequest < ActiveRecord::Base return [] unless target_branch == project.default_branch ext = Gitlab::ReferenceExtractor.new(project, current_user) - ext.analyze(description) + ext.analyze("#{title}\n#{description}") ext.issues - closes_issues(current_user) end diff --git a/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml b/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml new file mode 100644 index 00000000000..fa137a29cb4 --- /dev/null +++ b/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml @@ -0,0 +1,5 @@ +--- +title: Show Issues mentioned / being closed from a Merge Requests title below the + 'Accept Merge Request' button +merge_request: 9194 +author: Jan Christophersen diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb index c73065cdce1..eafcab6a0d7 100644 --- a/spec/features/merge_requests/closes_issues_spec.rb +++ b/spec/features/merge_requests/closes_issues_spec.rb @@ -10,10 +10,12 @@ feature 'Merge Request closing issues message', feature: true do :merge_request, :simple, source_project: project, - description: merge_request_description + description: merge_request_description, + title: merge_request_title ) end let(:merge_request_description) { 'Merge Request Description' } + let(:merge_request_title) { 'Merge Request Title' } before do project.team << [user, :master] @@ -45,8 +47,32 @@ feature 'Merge Request closing issues message', feature: true do end end - context 'closing some issues and mentioning, but not closing, others' do - let(:merge_request_description) { "Description\n\ncloses #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } + context 'closing some issues in title and mentioning, but not closing, others' do + let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.") + end + end + + context 'closing issues using title but not mentioning any other issue' do + let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Accepting this merge request will close issues #{issue_1.to_reference} and #{issue_2.to_reference}") + end + end + + context 'mentioning issues using title but not closing them' do + let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" } + + it 'does not display closing issue message' do + expect(page).to have_content("Issues #{issue_1.to_reference} and #{issue_2.to_reference} are mentioned but will not be closed.") + end + end + + context 'closing some issues using title and mentioning, but not closing, others' do + let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" } it 'does not display closing issue message' do expect(page).to have_content("Accepting this merge request will close issue #{issue_1.to_reference}. Issue #{issue_2.to_reference} is mentioned but will not be closed.") -- cgit v1.2.1 From 8adc356902624960647f932a8c701dc6ba4c3bb9 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Fri, 27 Jan 2017 16:53:27 -0200 Subject: Remove deprecated templates endpoints in V4 --- app/assets/javascripts/api.js | 2 +- ...licence-gitignore-and-yml-endpoints-removal.yml | 4 + doc/api/v3_to_v4.md | 10 + lib/api/api.rb | 1 + lib/api/templates.rb | 95 ++++------ lib/api/v3/templates.rb | 122 +++++++++++++ spec/requests/api/templates_spec.rb | 58 ++---- spec/requests/api/v3/templates_spec.rb | 203 +++++++++++++++++++++ 8 files changed, 398 insertions(+), 97 deletions(-) create mode 100644 changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml create mode 100644 lib/api/v3/templates.rb create mode 100644 spec/requests/api/v3/templates_spec.rb diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js index b4a8c827d7f..84bbe90f3b1 100644 --- a/app/assets/javascripts/api.js +++ b/app/assets/javascripts/api.js @@ -11,7 +11,7 @@ licensePath: "/api/:version/templates/licenses/:key", gitignorePath: "/api/:version/templates/gitignores/:key", gitlabCiYmlPath: "/api/:version/templates/gitlab_ci_ymls/:key", - dockerfilePath: "/api/:version/dockerfiles/:key", + dockerfilePath: "/api/:version/templates/dockerfiles/:key", issuableTemplatePath: "/:namespace_path/:project_path/templates/:type/:key", group: function(group_id, callback) { var url = Api.buildUrl(Api.groupPath) diff --git a/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml b/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml new file mode 100644 index 00000000000..05d5993ddf3 --- /dev/null +++ b/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml @@ -0,0 +1,4 @@ +--- +title: V3 deprecated templates endpoints removal +merge_request: 8853 +author: diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 7cb83a337f2..0ae07b5d3de 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -13,3 +13,13 @@ changes are in V4: - Project snippets do not return deprecated field `expires_at` - Endpoints under `projects/:id/keys` have been removed (use `projects/:id/deploy_keys`) - Status 409 returned for POST `project/:id/members` when a member already exists +- Removed the following deprecated Templates endpoints (these are still accessible with `/templates` prefix) + - `/licences` + - `/licences/:key` + - `/gitignores` + - `/gitlab_ci_ymls` + - `/dockerfiles` + - `/gitignores/:key` + - `/gitlab_ci_ymls/:key` + - `/dockerfiles/:key` + diff --git a/lib/api/api.rb b/lib/api/api.rb index 7ec089b9c29..06346ae822a 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -11,6 +11,7 @@ module API mount ::API::V3::MergeRequests mount ::API::V3::Projects mount ::API::V3::ProjectSnippets + mount ::API::V3::Templates end before { allow_access_with_scope :api } diff --git a/lib/api/templates.rb b/lib/api/templates.rb index e23f99256a5..8a2d66efd89 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -24,7 +24,6 @@ module API /[\<\{\[] (fullname|name\sof\s(author|copyright\sowner)) [\>\}\]]/xi.freeze - DEPRECATION_MESSAGE = ' This endpoint is deprecated and will be removed in GitLab 9.0.'.freeze helpers do def parsed_license_template @@ -46,74 +45,58 @@ module API end end - { "licenses" => :deprecated, "templates/licenses" => :ok }.each do |route, status| - desc 'Get the list of the available license template' do - detailed_desc = 'This feature was introduced in GitLab 8.7.' - detailed_desc << DEPRECATION_MESSAGE unless status == :ok - detail detailed_desc - success Entities::RepoLicense - end - params do - optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' - end - get route do - options = { - featured: declared(params).popular.present? ? true : nil - } - present Licensee::License.all(options), with: Entities::RepoLicense - end + desc 'Get the list of the available license template' do + detail 'This feature was introduced in GitLab 8.7.' + success ::API::Entities::RepoLicense + end + params do + optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' + end + get "templates/licenses" do + options = { + featured: declared(params).popular.present? ? true : nil + } + present Licensee::License.all(options), with: ::API::Entities::RepoLicense end - { "licenses/:name" => :deprecated, "templates/licenses/:name" => :ok }.each do |route, status| - desc 'Get the text for a specific license' do - detailed_desc = 'This feature was introduced in GitLab 8.7.' - detailed_desc << DEPRECATION_MESSAGE unless status == :ok - detail detailed_desc - success Entities::RepoLicense - end - params do - requires :name, type: String, desc: 'The name of the template' - end - get route, requirements: { name: /[\w\.-]+/ } do - not_found!('License') unless Licensee::License.find(declared(params).name) + desc 'Get the text for a specific license' do + detail 'This feature was introduced in GitLab 8.7.' + success ::API::Entities::RepoLicense + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do + not_found!('License') unless Licensee::License.find(declared(params).name) - template = parsed_license_template + template = parsed_license_template - present template, with: Entities::RepoLicense - end + present template, with: ::API::Entities::RepoLicense end GLOBAL_TEMPLATE_TYPES.each do |template_type, properties| klass = properties[:klass] gitlab_version = properties[:gitlab_version] - { template_type => :deprecated, "templates/#{template_type}" => :ok }.each do |route, status| - desc 'Get the list of the available template' do - detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." - detailed_desc << DEPRECATION_MESSAGE unless status == :ok - detail detailed_desc - success Entities::TemplatesList - end - get route do - present klass.all, with: Entities::TemplatesList - end + desc 'Get the list of the available template' do + detail "This feature was introduced in GitLab #{gitlab_version}." + success Entities::TemplatesList + end + get "templates/#{template_type}" do + present klass.all, with: Entities::TemplatesList end - { "#{template_type}/:name" => :deprecated, "templates/#{template_type}/:name" => :ok }.each do |route, status| - desc 'Get the text for a specific template present in local filesystem' do - detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." - detailed_desc << DEPRECATION_MESSAGE unless status == :ok - detail detailed_desc - success Entities::Template - end - params do - requires :name, type: String, desc: 'The name of the template' - end - get route do - new_template = klass.find(declared(params).name) + desc 'Get the text for a specific template present in local filesystem' do + detail "This feature was introduced in GitLab #{gitlab_version}." + success Entities::Template + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get "templates/#{template_type}/:name" do + new_template = klass.find(declared(params).name) - render_response(template_type, new_template) - end + render_response(template_type, new_template) end end end diff --git a/lib/api/v3/templates.rb b/lib/api/v3/templates.rb new file mode 100644 index 00000000000..4c577a8d2b7 --- /dev/null +++ b/lib/api/v3/templates.rb @@ -0,0 +1,122 @@ +module API + module V3 + class Templates < Grape::API + GLOBAL_TEMPLATE_TYPES = { + gitignores: { + klass: Gitlab::Template::GitignoreTemplate, + gitlab_version: 8.8 + }, + gitlab_ci_ymls: { + klass: Gitlab::Template::GitlabCiYmlTemplate, + gitlab_version: 8.9 + }, + dockerfiles: { + klass: Gitlab::Template::DockerfileTemplate, + gitlab_version: 8.15 + } + }.freeze + PROJECT_TEMPLATE_REGEX = + /[\<\{\[] + (project|description| + one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here + [\>\}\]]/xi.freeze + YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze + FULLNAME_TEMPLATE_REGEX = + /[\<\{\[] + (fullname|name\sof\s(author|copyright\sowner)) + [\>\}\]]/xi.freeze + DEPRECATION_MESSAGE = ' This endpoint is deprecated and has been removed in V4.'.freeze + + helpers do + def parsed_license_template + # We create a fresh Licensee::License object since we'll modify its + # content in place below. + template = Licensee::License.new(params[:name]) + + template.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s) + template.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present? + + fullname = params[:fullname].presence || current_user.try(:name) + template.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname + template + end + + def render_response(template_type, template) + not_found!(template_type.to_s.singularize) unless template + present template, with: ::API::Entities::Template + end + end + + { "licenses" => :deprecated, "templates/licenses" => :ok }.each do |route, status| + desc 'Get the list of the available license template' do + detailed_desc = 'This feature was introduced in GitLab 8.7.' + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success ::API::Entities::RepoLicense + end + params do + optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' + end + get route do + options = { + featured: declared(params).popular.present? ? true : nil + } + present Licensee::License.all(options), with: ::API::Entities::RepoLicense + end + end + + { "licenses/:name" => :deprecated, "templates/licenses/:name" => :ok }.each do |route, status| + desc 'Get the text for a specific license' do + detailed_desc = 'This feature was introduced in GitLab 8.7.' + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success ::API::Entities::RepoLicense + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get route, requirements: { name: /[\w\.-]+/ } do + not_found!('License') unless Licensee::License.find(declared(params).name) + + template = parsed_license_template + + present template, with: ::API::Entities::RepoLicense + end + end + + GLOBAL_TEMPLATE_TYPES.each do |template_type, properties| + klass = properties[:klass] + gitlab_version = properties[:gitlab_version] + + { template_type => :deprecated, "templates/#{template_type}" => :ok }.each do |route, status| + desc 'Get the list of the available template' do + detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success ::API::Entities::TemplatesList + end + get route do + present klass.all, with: ::API::Entities::TemplatesList + end + end + + { "#{template_type}/:name" => :deprecated, "templates/#{template_type}/:name" => :ok }.each do |route, status| + desc 'Get the text for a specific template present in local filesystem' do + detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success ::API::Entities::Template + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get route do + new_template = klass.find(declared(params).name) + + render_response(template_type, new_template) + end + end + end + end + end +end diff --git a/spec/requests/api/templates_spec.rb b/spec/requests/api/templates_spec.rb index d32ba60fc4c..c0a8c0832bb 100644 --- a/spec/requests/api/templates_spec.rb +++ b/spec/requests/api/templates_spec.rb @@ -3,23 +3,23 @@ require 'spec_helper' describe API::Templates, api: true do include ApiHelpers - shared_examples_for 'the Template Entity' do |path| - before { get api(path) } + context 'the Template Entity' do + before { get api('/templates/gitignores/Ruby') } it { expect(json_response['name']).to eq('Ruby') } it { expect(json_response['content']).to include('*.gem') } end - - shared_examples_for 'the TemplateList Entity' do |path| - before { get api(path) } + + context 'the TemplateList Entity' do + before { get api('/templates/gitignores') } it { expect(json_response.first['name']).not_to be_nil } it { expect(json_response.first['content']).to be_nil } end - shared_examples_for 'requesting gitignores' do |path| + context 'requesting gitignores' do it 'returns a list of available gitignore templates' do - get api(path) + get api('/templates/gitignores') expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -27,9 +27,9 @@ describe API::Templates, api: true do end end - shared_examples_for 'requesting gitlab-ci-ymls' do |path| + context 'requesting gitlab-ci-ymls' do it 'returns a list of available gitlab_ci_ymls' do - get api(path) + get api('/templates/gitlab_ci_ymls') expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -37,17 +37,17 @@ describe API::Templates, api: true do end end - shared_examples_for 'requesting gitlab-ci-yml for Ruby' do |path| + context 'requesting gitlab-ci-yml for Ruby' do it 'adds a disclaimer on the top' do - get api(path) + get api('/templates/gitlab_ci_ymls/Ruby') expect(response).to have_http_status(200) expect(json_response['content']).to start_with("# This file is a template,") end end - shared_examples_for 'the License Template Entity' do |path| - before { get api(path) } + context 'the License Template Entity' do + before { get api('/templates/licenses/mit') } it 'returns a license template' do expect(json_response['key']).to eq('mit') @@ -64,9 +64,9 @@ describe API::Templates, api: true do end end - shared_examples_for 'GET licenses' do |path| + context 'GET templates/licenses' do it 'returns a list of available license templates' do - get api(path) + get api('/templates/licenses') expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -77,7 +77,7 @@ describe API::Templates, api: true do describe 'the popular parameter' do context 'with popular=1' do it 'returns a list of available popular license templates' do - get api("#{path}?popular=1") + get api('/templates/licenses?popular=1') expect(response).to have_http_status(200) expect(json_response).to be_an Array @@ -88,10 +88,10 @@ describe API::Templates, api: true do end end - shared_examples_for 'GET licenses/:name' do |path| + context 'GET templates/licenses/:name' do context 'with :project and :fullname given' do before do - get api("#{path}/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}") + get api("/templates/licenses/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}") end context 'for the mit license' do @@ -178,26 +178,4 @@ describe API::Templates, api: true do end end end - - describe 'with /templates namespace' do - it_behaves_like 'the Template Entity', '/templates/gitignores/Ruby' - it_behaves_like 'the TemplateList Entity', '/templates/gitignores' - it_behaves_like 'requesting gitignores', '/templates/gitignores' - it_behaves_like 'requesting gitlab-ci-ymls', '/templates/gitlab_ci_ymls' - it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/templates/gitlab_ci_ymls/Ruby' - it_behaves_like 'the License Template Entity', '/templates/licenses/mit' - it_behaves_like 'GET licenses', '/templates/licenses' - it_behaves_like 'GET licenses/:name', '/templates/licenses' - end - - describe 'without /templates namespace' do - it_behaves_like 'the Template Entity', '/gitignores/Ruby' - it_behaves_like 'the TemplateList Entity', '/gitignores' - it_behaves_like 'requesting gitignores', '/gitignores' - it_behaves_like 'requesting gitlab-ci-ymls', '/gitlab_ci_ymls' - it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/gitlab_ci_ymls/Ruby' - it_behaves_like 'the License Template Entity', '/licenses/mit' - it_behaves_like 'GET licenses', '/licenses' - it_behaves_like 'GET licenses/:name', '/licenses' - end end diff --git a/spec/requests/api/v3/templates_spec.rb b/spec/requests/api/v3/templates_spec.rb new file mode 100644 index 00000000000..4fd4e70bedd --- /dev/null +++ b/spec/requests/api/v3/templates_spec.rb @@ -0,0 +1,203 @@ +require 'spec_helper' + +describe API::V3::Templates, api: true do + include ApiHelpers + + shared_examples_for 'the Template Entity' do |path| + before { get v3_api(path) } + + it { expect(json_response['name']).to eq('Ruby') } + it { expect(json_response['content']).to include('*.gem') } + end + + shared_examples_for 'the TemplateList Entity' do |path| + before { get v3_api(path) } + + it { expect(json_response.first['name']).not_to be_nil } + it { expect(json_response.first['content']).to be_nil } + end + + shared_examples_for 'requesting gitignores' do |path| + it 'returns a list of available gitignore templates' do + get v3_api(path) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to be > 15 + end + end + + shared_examples_for 'requesting gitlab-ci-ymls' do |path| + it 'returns a list of available gitlab_ci_ymls' do + get v3_api(path) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).not_to be_nil + end + end + + shared_examples_for 'requesting gitlab-ci-yml for Ruby' do |path| + it 'adds a disclaimer on the top' do + get v3_api(path) + + expect(response).to have_http_status(200) + expect(json_response['content']).to start_with("# This file is a template,") + end + end + + shared_examples_for 'the License Template Entity' do |path| + before { get v3_api(path) } + + it 'returns a license template' do + expect(json_response['key']).to eq('mit') + expect(json_response['name']).to eq('MIT License') + expect(json_response['nickname']).to be_nil + expect(json_response['popular']).to be true + expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') + expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') + expect(json_response['description']).to include('A permissive license that is short and to the point.') + expect(json_response['conditions']).to eq(%w[include-copyright]) + expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) + expect(json_response['limitations']).to eq(%w[no-liability]) + expect(json_response['content']).to include('The MIT License (MIT)') + end + end + + shared_examples_for 'GET licenses' do |path| + it 'returns a list of available license templates' do + get v3_api(path) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(15) + expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') + end + + describe 'the popular parameter' do + context 'with popular=1' do + it 'returns a list of available popular license templates' do + get v3_api("#{path}?popular=1") + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(3) + expect(json_response.map { |l| l['key'] }).to include('apache-2.0') + end + end + end + end + + shared_examples_for 'GET licenses/:name' do |path| + context 'with :project and :fullname given' do + before do + get v3_api("#{path}/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}") + end + + context 'for the mit license' do + let(:license_type) { 'mit' } + + it 'returns the license text' do + expect(json_response['content']).to include('The MIT License (MIT)') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton") + end + end + + context 'for the agpl-3.0 license' do + let(:license_type) { 'agpl-3.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton") + end + end + + context 'for the gpl-3.0 license' do + let(:license_type) { 'gpl-3.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton") + end + end + + context 'for the gpl-2.0 license' do + let(:license_type) { 'gpl-2.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton") + end + end + + context 'for the apache-2.0 license' do + let(:license_type) { 'apache-2.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('Apache License') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include("Copyright #{Time.now.year} Anton") + end + end + + context 'for an uknown license' do + let(:license_type) { 'muth-over9000' } + + it 'returns a 404' do + expect(response).to have_http_status(404) + end + end + end + + context 'with no :fullname given' do + context 'with an authenticated user' do + let(:user) { create(:user) } + + it 'replaces the copyright owner placeholder with the name of the current user' do + get v3_api('/templates/licenses/mit', user) + + expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}") + end + end + end + end + + describe 'with /templates namespace' do + it_behaves_like 'the Template Entity', '/templates/gitignores/Ruby' + it_behaves_like 'the TemplateList Entity', '/templates/gitignores' + it_behaves_like 'requesting gitignores', '/templates/gitignores' + it_behaves_like 'requesting gitlab-ci-ymls', '/templates/gitlab_ci_ymls' + it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/templates/gitlab_ci_ymls/Ruby' + it_behaves_like 'the License Template Entity', '/templates/licenses/mit' + it_behaves_like 'GET licenses', '/templates/licenses' + it_behaves_like 'GET licenses/:name', '/templates/licenses' + end + + describe 'without /templates namespace' do + it_behaves_like 'the Template Entity', '/gitignores/Ruby' + it_behaves_like 'the TemplateList Entity', '/gitignores' + it_behaves_like 'requesting gitignores', '/gitignores' + it_behaves_like 'requesting gitlab-ci-ymls', '/gitlab_ci_ymls' + it_behaves_like 'requesting gitlab-ci-yml for Ruby', '/gitlab_ci_ymls/Ruby' + it_behaves_like 'the License Template Entity', '/licenses/mit' + it_behaves_like 'GET licenses', '/licenses' + it_behaves_like 'GET licenses/:name', '/licenses' + end +end -- cgit v1.2.1 From 17c8d15b14f6c3ecc98e1466e9148cf100cc12c6 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Fri, 10 Feb 2017 15:26:23 -0500 Subject: Enable `Lint/EmptyWhen` cop and correct offense --- .rubocop.yml | 4 ++++ .rubocop_todo.yml | 4 ---- lib/gitlab/diff/parser.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 1061de7c797..6e8f599bf25 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -572,6 +572,10 @@ Lint/ElseLayout: Lint/EmptyEnsure: Enabled: true +# Checks for the presence of `when` branches without a body. +Lint/EmptyWhen: + Enabled: true + # Align ends correctly. Lint/EndAlignment: Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 7ef5523de4b..648b3fc49d2 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -21,10 +21,6 @@ Lint/AmbiguousRegexpLiteral: Lint/AssignmentInCondition: Enabled: false -# Offense count: 1 -Lint/EmptyWhen: - Enabled: false - # Offense count: 20 Lint/HandleExceptions: Enabled: false diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb index 59a2367b65d..89320f5d9dc 100644 --- a/lib/gitlab/diff/parser.rb +++ b/lib/gitlab/diff/parser.rb @@ -45,7 +45,7 @@ module Gitlab line_new += 1 when "-" line_old += 1 - when "\\" + when "\\" # rubocop:disable Lint/EmptyWhen # No increment else line_new += 1 -- cgit v1.2.1 From 581cfbae24d629433e2e4eb6f6a2bb2a2938cada Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Mon, 13 Feb 2017 17:11:50 +0100 Subject: Don't connect in Gitlab::Database.adapter_name We don't need to connect when requesting the name of the database adapter. This in turn should prevent us from requesting/leaking connections just by asking whether we're using PostgreSQL or MySQL. --- changelogs/unreleased/fix-ar-connection-leaks.yml | 4 ++++ lib/gitlab/database.rb | 2 +- spec/lib/gitlab/database_spec.rb | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/fix-ar-connection-leaks.yml diff --git a/changelogs/unreleased/fix-ar-connection-leaks.yml b/changelogs/unreleased/fix-ar-connection-leaks.yml new file mode 100644 index 00000000000..9da715560ad --- /dev/null +++ b/changelogs/unreleased/fix-ar-connection-leaks.yml @@ -0,0 +1,4 @@ +--- +title: Don't connect in Gitlab::Database.adapter_name +merge_request: +author: diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index dc2537d36aa..a3d6be9959b 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -6,7 +6,7 @@ module Gitlab MAX_INT_VALUE = 2147483647 def self.adapter_name - connection.adapter_name + ActiveRecord::Base.configurations[Rails.env]['adapter'] end def self.mysql? diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index b142b3a2781..41252f31997 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -5,6 +5,12 @@ class MigrationTest end describe Gitlab::Database, lib: true do + describe '.adapter_name' do + it 'returns the name of the adapter' do + expect(described_class.adapter_name).to be_an_instance_of(String) + end + end + # These are just simple smoke tests to check if the methods work (regardless # of what they may return). describe '.mysql?' do -- cgit v1.2.1 From 948e1b845cd93c6450794379282712ec3b4a9caf Mon Sep 17 00:00:00 2001 From: Douwe Maan Date: Mon, 13 Feb 2017 10:35:25 -0600 Subject: Respect autocrlf setting when creating/updating file through web UI --- app/models/repository.rb | 8 ++++++++ spec/models/repository_spec.rb | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/models/repository.rb b/app/models/repository.rb index d2d92a064a4..56c582cd9be 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1230,6 +1230,14 @@ class Repository action[:content] end + detect = CharlockHolmes::EncodingDetector.new.detect(content) if content + + unless detect && detect[:type] == :binary + # When writing to the repo directly as we are doing here, + # the `core.autocrlf` config isn't taken into account. + content.gsub!("\r\n", "\n") if self.autocrlf + end + oid = rugged.write(content, :blob) index.add(path: path, oid: oid, mode: mode) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 9bfa6409607..838fd3754b2 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -351,6 +351,17 @@ describe Repository, models: true do expect(blob.data).to eq('Changelog!') end + it 'respects the autocrlf setting' do + repository.commit_file(user, 'hello.txt', "Hello,\r\nWorld", + message: 'Add hello world', + branch_name: 'master', + update: true) + + blob = repository.blob_at('master', 'hello.txt') + + expect(blob.data).to eq("Hello,\nWorld") + end + context "when an author is specified" do it "uses the given email/name to set the commit's author" do expect do -- cgit v1.2.1 From 136dc79433295aded9ecabb15aae2dc1e228b903 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 13 Feb 2017 21:45:26 +0800 Subject: Have some simple way to create connection pool --- lib/gitlab/database.rb | 24 ++++++++++++++++++++ spec/lib/gitlab/database_spec.rb | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index dc2537d36aa..e6612bc3aad 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -69,6 +69,30 @@ module Gitlab end end + def self.with_connection_pool(pool_size) + pool = create_connection_pool(pool_size) + + yield(pool) + + ensure + pool.disconnect! + end + + def self.create_connection_pool(pool_size) + # See activerecord-4.2.7.1/lib/active_record/connection_adapters/connection_specification.rb + env = Rails.env + original_config = ActiveRecord::Base.configurations + env_config = original_config[env].merge('pool' => pool_size) + config = original_config.merge(env => env_config) + + spec = + ActiveRecord:: + ConnectionAdapters:: + ConnectionSpecification::Resolver.new(config).spec(env.to_sym) + + ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec) + end + def self.connection ActiveRecord::Base.connection end diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb index b142b3a2781..c9be832b1a0 100644 --- a/spec/lib/gitlab/database_spec.rb +++ b/spec/lib/gitlab/database_spec.rb @@ -71,6 +71,54 @@ describe Gitlab::Database, lib: true do end end + describe '.with_connection_pool' do + it 'creates a new connection pool and disconnect it after used' do + closed_pool = nil + + described_class.with_connection_pool(1) do |pool| + pool.with_connection do |connection| + connection.execute('SELECT 1 AS value') + end + + expect(pool).to be_connected + + closed_pool = pool + end + + expect(closed_pool).not_to be_connected + end + + it 'disconnects the pool even an exception was raised' do + error = Class.new(RuntimeError) + closed_pool = nil + + begin + described_class.with_connection_pool(1) do |pool| + pool.with_connection do |connection| + connection.execute('SELECT 1 AS value') + end + + closed_pool = pool + + raise error.new('boom') + end + rescue error + end + + expect(closed_pool).not_to be_connected + end + end + + describe '.create_connection_pool' do + it 'creates a new connection pool with specific pool size' do + pool = described_class.create_connection_pool(5) + + expect(pool) + .to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool) + expect(pool.spec.config[:pool]).to eq(5) + end + end + describe '#true_value' do it 'returns correct value for PostgreSQL' do expect(described_class).to receive(:postgresql?).and_return(true) -- cgit v1.2.1 From 97739f15c73064ad56312f2cdc35a417985b6de4 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 12 Feb 2017 19:56:25 -0800 Subject: Add indices to improve loading of labels page --- changelogs/unreleased/sh-add-labels-index.yml | 4 ++++ ...170210062829_add_index_to_labels_for_title_and_project.rb | 12 ++++++++++++ db/schema.rb | 2 ++ 3 files changed, 18 insertions(+) create mode 100644 changelogs/unreleased/sh-add-labels-index.yml create mode 100644 db/migrate/20170210062829_add_index_to_labels_for_title_and_project.rb diff --git a/changelogs/unreleased/sh-add-labels-index.yml b/changelogs/unreleased/sh-add-labels-index.yml new file mode 100644 index 00000000000..b948a75081c --- /dev/null +++ b/changelogs/unreleased/sh-add-labels-index.yml @@ -0,0 +1,4 @@ +--- +title: Add indices to improve loading of labels page +merge_request: +author: diff --git a/db/migrate/20170210062829_add_index_to_labels_for_title_and_project.rb b/db/migrate/20170210062829_add_index_to_labels_for_title_and_project.rb new file mode 100644 index 00000000000..f922ed209aa --- /dev/null +++ b/db/migrate/20170210062829_add_index_to_labels_for_title_and_project.rb @@ -0,0 +1,12 @@ +class AddIndexToLabelsForTitleAndProject < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def change + add_concurrent_index :labels, :title + add_concurrent_index :labels, :project_id + end +end diff --git a/db/schema.rb b/db/schema.rb index d71911eaf14..feeea55ce9b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -581,6 +581,8 @@ ActiveRecord::Schema.define(version: 20170210075922) do add_index "labels", ["group_id", "project_id", "title"], name: "index_labels_on_group_id_and_project_id_and_title", unique: true, using: :btree add_index "labels", ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree + add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree + add_index "labels", ["title"], name: "index_labels_on_title", using: :btree create_table "lfs_objects", force: :cascade do |t| t.string "oid", null: false -- cgit v1.2.1 From 17f5a34bfd7d122d1bdc8e11632ff072a89e9cbd Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Fri, 10 Feb 2017 10:44:42 -0600 Subject: Display loading indicator when filtering ref switcher dropdown --- app/assets/javascripts/gl_dropdown.js | 2 ++ .../unreleased/27966-branch-ref-switcher-input-filter-broken.yml | 4 ++++ spec/features/projects/ref_switcher_spec.rb | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 77fa662892a..0d618caf350 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -47,9 +47,11 @@ } // Only filter asynchronously only if option remote is set if (this.options.remote) { + $inputContainer.parent().addClass('is-loading'); clearTimeout(timeout); return timeout = setTimeout(function() { return this.options.query(this.input.val(), function(data) { + $inputContainer.parent().removeClass('is-loading'); return this.options.callback(data); }.bind(this)); }.bind(this), 250); diff --git a/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml b/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml new file mode 100644 index 00000000000..6fa13395a7d --- /dev/null +++ b/changelogs/unreleased/27966-branch-ref-switcher-input-filter-broken.yml @@ -0,0 +1,4 @@ +--- +title: Display loading indicator when filtering ref switcher dropdown +merge_request: +author: diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb index 38fe2d92885..4eafac1acd8 100644 --- a/spec/features/projects/ref_switcher_spec.rb +++ b/spec/features/projects/ref_switcher_spec.rb @@ -20,9 +20,9 @@ feature 'Ref switcher', feature: true, js: true do input.set 'binary' wait_for_ajax - input.native.send_keys :down - input.native.send_keys :down - input.native.send_keys :enter + page.within '.dropdown-content ul' do + input.native.send_keys :enter + end end expect(page).to have_title 'binary-encoding' -- cgit v1.2.1 From 4304ea802ffd8c09a3880856db41bbe8ce4f64a0 Mon Sep 17 00:00:00 2001 From: Pratik Borsadiya Date: Mon, 13 Feb 2017 22:41:39 +0530 Subject: Layout alignment fix in new project and edit project screen --- app/assets/stylesheets/pages/projects.scss | 8 ++------ app/views/projects/edit.html.haml | 4 ++-- app/views/projects/new.html.haml | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 8b59c20cb65..9a72148f408 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -35,12 +35,8 @@ margin-bottom: 10px; } - .project-path { - padding-right: 0; - - .form-control { - border-radius: $border-radius-base; - } + .project-path .form-control { + border-radius: $border-radius-base; } .input-group > div { diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 9c5c1a6d707..c533a29999a 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -6,7 +6,7 @@ .col-lg-9 .project-edit-errors = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit-project" }, authenticity_token: true do |f| - %fieldset.append-bottom-0 + %fieldset .row .form-group.col-md-9 = f.label :name, class: 'label-light', for: 'project_name_edit' do @@ -33,7 +33,7 @@ = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" %p.help-block Separate tags with commas. %hr - %fieldset.append-bottom-0 + %fieldset %h5.prepend-top-0 Sharing & Permissions .form_group.prepend-top-20.sharing-and-permissions diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 41473fae4de..a07885537b9 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -12,7 +12,7 @@ Create or Import your project from popular Git services .col-lg-9 = form_for @project, html: { class: 'new_project' } do |f| - %fieldset.append-bottom-0 + .row .form-group.col-xs-12.col-sm-6 = f.label :namespace_id, class: 'label-light' do %span -- cgit v1.2.1 From f0a922840337f1114ae376ef43e28d480f2ad9fa Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Mon, 13 Feb 2017 18:30:26 +0100 Subject: Add 'Most Recent Activity' header to the User Profile page --- app/views/users/show.html.haml | 2 ++ changelogs/unreleased/26286-most-recent-activity-profile-header.yml | 4 ++++ 2 files changed, 6 insertions(+) create mode 100644 changelogs/unreleased/26286-most-recent-activity-profile-header.yml diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 44254040e4e..dc2fea450bd 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -106,6 +106,8 @@ %i.fa.fa-spinner.fa-spin .user-calendar-activities + %h4.prepend-top-20 + Most Recent Activity .content_list{ data: { href: user_path } } = spinner diff --git a/changelogs/unreleased/26286-most-recent-activity-profile-header.yml b/changelogs/unreleased/26286-most-recent-activity-profile-header.yml new file mode 100644 index 00000000000..74d5a43a804 --- /dev/null +++ b/changelogs/unreleased/26286-most-recent-activity-profile-header.yml @@ -0,0 +1,4 @@ +--- +title: Added 'Most Recent Activity' header to the User Profile page +merge_request: 9189 +author: Jan Christophersen -- cgit v1.2.1 From 4f51e1fad0609c0cd50468243a83970728186a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 26 Jan 2017 18:59:50 +0100 Subject: Add comment events to contributions calendar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- app/models/event.rb | 7 +- app/views/users/calendar.html.haml | 2 +- app/views/users/calendar_activities.html.haml | 6 +- .../22645-add-discussion-contribs-to-calendar.yml | 4 + lib/gitlab/contributions_calendar.rb | 6 +- spec/features/calendar_spec.rb | 222 ++++++++++++--------- 6 files changed, 142 insertions(+), 105 deletions(-) create mode 100644 changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml diff --git a/app/models/event.rb b/app/models/event.rb index 2662f170765..1a5828a2828 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -45,9 +45,10 @@ class Event < ActiveRecord::Base class << self # Update Gitlab::ContributionsCalendar#activity_dates if this changes def contributions - where("action = ? OR (target_type in (?) AND action in (?))", - Event::PUSHED, ["MergeRequest", "Issue"], - [Event::CREATED, Event::CLOSED, Event::MERGED]) + where("action = ? OR (target_type IN (?) AND action IN (?)) OR (target_type = ? AND action = ?)", + Event::PUSHED, + ["MergeRequest", "Issue"], [Event::CREATED, Event::CLOSED, Event::MERGED], + "Note", Event::COMMENTED) end def limit_recent(limit = 20, offset = nil) diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 6228245d8d0..57b8845c55d 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,7 +1,7 @@ .clearfix.calendar .js-contrib-calendar .calendar-hint - Summary of issues, merge requests, and push events + Summary of issues, merge requests, push events, and comments :javascript new Calendar( #{@activity_dates.to_json}, diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index b09782749f5..dae147ca8be 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -13,8 +13,10 @@ #{event.action_name} #{event.ref_type} #{event.ref_name} - else = event_action_name(event) - - if event.target - %strong= link_to "#{event.target.to_reference}", [event.project.namespace.becomes(Namespace), event.project, event.target] + - if event.note? + %strong= link_to event.note_target.to_reference, event_note_target_path(event) + - elsif event.target + %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target] at %strong diff --git a/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml b/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml new file mode 100644 index 00000000000..9b3c2bd9278 --- /dev/null +++ b/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml @@ -0,0 +1,4 @@ +--- +title: Add discussion events to contributions calendar +merge_request: 8821 +author: diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb index 7e3d5647b39..15992b77680 100644 --- a/lib/gitlab/contributions_calendar.rb +++ b/lib/gitlab/contributions_calendar.rb @@ -22,8 +22,10 @@ module Gitlab having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue") mr_events = event_counts(date_from, :merge_requests). having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest") + note_events = event_counts(date_from, :merge_requests). + having(action: [Event::COMMENTED], target_type: "Note") - union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events]) + union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events, note_events]) events = Event.find_by_sql(union.to_sql).map(&:attributes) @activity_events = events.each_with_object(Hash.new {|h, k| h[k] = 0 }) do |event, activities| @@ -38,7 +40,7 @@ module Gitlab # Use visible_to_user? instead of the complicated logic in activity_dates # because we're only viewing the events for a single day. - events.select {|event| event.visible_to_user?(current_user) } + events.select { |event| event.visible_to_user?(current_user) } end def starting_year diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb index 3e0b6364e0d..35d090c4b7f 100644 --- a/spec/features/calendar_spec.rb +++ b/spec/features/calendar_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' -feature 'Contributions Calendar', js: true, feature: true do +feature 'Contributions Calendar', :feature, :js do include WaitForAjax + let(:user) { create(:user) } let(:contributed_project) { create(:project, :public) } + let(:issue_note) { create(:note, project: contributed_project) } # Ex/ Sunday Jan 1, 2016 date_format = '%A %b %-d, %Y' @@ -12,31 +14,31 @@ feature 'Contributions Calendar', js: true, feature: true do issue_params = { title: issue_title } def get_cell_color_selector(contributions) - contribution_cell = '.user-contrib-cell' - activity_colors = Array['#ededed', '#acd5f2', '#7fa8c9', '#527ba0', '#254e77'] - activity_colors_index = 0 - - if contributions > 0 && contributions < 10 - activity_colors_index = 1 - elsif contributions >= 10 && contributions < 20 - activity_colors_index = 2 - elsif contributions >= 20 && contributions < 30 - activity_colors_index = 3 - elsif contributions >= 30 - activity_colors_index = 4 - end + activity_colors = %w[#ededed #acd5f2 #7fa8c9 #527ba0 #254e77] + # We currently don't actually test the cases with contributions >= 20 + activity_colors_index = + if contributions > 0 && contributions < 10 + 1 + elsif contributions >= 10 && contributions < 20 + 2 + elsif contributions >= 20 && contributions < 30 + 3 + elsif contributions >= 30 + 4 + else + 0 + end - "#{contribution_cell}[fill='#{activity_colors[activity_colors_index]}']" + ".user-contrib-cell[fill='#{activity_colors[activity_colors_index]}']" end def get_cell_date_selector(contributions, date) - contribution_text = 'No contributions' - - if contributions === 1 - contribution_text = '1 contribution' - elsif contributions > 1 - contribution_text = "#{contributions} contributions" - end + contribution_text = + if contributions.zero? + 'No contributions' + else + "#{contributions} #{'contribution'.pluralize(contributions)}" + end "#{get_cell_color_selector(contributions)}[data-original-title='#{contribution_text}
    #{date}']" end @@ -45,129 +47,155 @@ feature 'Contributions Calendar', js: true, feature: true do push_params = { project: contributed_project, action: Event::PUSHED, - author_id: @user.id, + author_id: user.id, data: { commit_count: 3 } } Event.create(push_params) end - def get_first_cell_content - find('.user-calendar-activities').text - end + def note_comment_contribution + note_comment_params = { + project: contributed_project, + action: Event::COMMENTED, + target: issue_note, + author_id: user.id + } - before do - login_as :user - visit @user.username - wait_for_ajax + Event.create(note_comment_params) end - it 'displays calendar', js: true do - expect(page).to have_css('.js-contrib-calendar') + def selected_day_activities + find('.user-calendar-activities').text end - describe 'select calendar day', js: true do - let(:cells) { page.all('.user-contrib-cell') } - let(:first_cell_content_before) { get_first_cell_content } + before do + login_as user + end + describe 'calendar day selection' do before do - cells[0].click + visit user.username wait_for_ajax - first_cell_content_before end - it 'displays calendar day activities', js: true do - expect(get_first_cell_content).not_to eq('') + it 'displays calendar' do + expect(page).to have_css('.js-contrib-calendar') end - describe 'select another calendar day', js: true do + describe 'select calendar day' do + let(:cells) { page.all('.user-contrib-cell') } + before do - cells[1].click + cells[0].click wait_for_ajax + @first_day_activities = selected_day_activities end - it 'displays different calendar day activities', js: true do - expect(get_first_cell_content).not_to eq(first_cell_content_before) + it 'displays calendar day activities' do + expect(selected_day_activities).not_to be_empty end - end - describe 'deselect calendar day', js: true do - before do - cells[0].click - wait_for_ajax + describe 'select another calendar day' do + before do + cells[1].click + wait_for_ajax + end + + it 'displays different calendar day activities' do + expect(selected_day_activities).not_to eq(@first_day_activities) + end end - it 'hides calendar day activities', js: true do - expect(get_first_cell_content).to eq('') + describe 'deselect calendar day' do + before do + cells[0].click + wait_for_ajax + end + + it 'hides calendar day activities' do + expect(selected_day_activities).to be_empty + end end end end - describe '1 calendar activity' do - before do - Issues::CreateService.new(contributed_project, @user, issue_params).execute - visit @user.username - wait_for_ajax + describe 'calendar daily activities' do + shared_context 'visit user page' do + before do + visit user.username + wait_for_ajax + end end - it 'displays calendar activity log', js: true do - expect(find('.content_list .event-note')).to have_content issue_title - end + shared_examples 'a day with activity' do |contribution_count:| + include_context 'visit user page' - it 'displays calendar activity square color for 1 contribution', js: true do - expect(page).to have_selector(get_cell_color_selector(1), count: 1) - end + it 'displays calendar activity square color for 1 contribution' do + expect(page).to have_selector(get_cell_color_selector(contribution_count), count: 1) + end - it 'displays calendar activity square on the correct date', js: true do - today = Date.today.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(1, today), count: 1) + it 'displays calendar activity square on the correct date' do + today = Date.today.strftime(date_format) + expect(page).to have_selector(get_cell_date_selector(contribution_count, today), count: 1) + end end - end - describe '10 calendar activities' do - before do - (0..9).each do |i| - push_code_contribution() + describe '1 issue creation calendar activity' do + before do + Issues::CreateService.new(contributed_project, user, issue_params).execute end - visit @user.username - wait_for_ajax - end + it_behaves_like 'a day with activity', contribution_count: 1 - it 'displays calendar activity square color for 10 contributions', js: true do - expect(page).to have_selector(get_cell_color_selector(10), count: 1) - end + describe 'issue title is shown on activity page' do + include_context 'visit user page' - it 'displays calendar activity square on the correct date', js: true do - today = Date.today.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(10, today), count: 1) + it 'displays calendar activity log' do + expect(find('.content_list .event-note')).to have_content issue_title + end + end end - end - describe 'calendar activity on two days' do - before do - push_code_contribution() - - Timecop.freeze(Date.yesterday) - Issues::CreateService.new(contributed_project, @user, issue_params).execute - Timecop.return + describe '1 comment calendar activity' do + before do + note_comment_contribution + end - visit @user.username - wait_for_ajax + it_behaves_like 'a day with activity', contribution_count: 1 end - it 'displays calendar activity squares for both days', js: true do - expect(page).to have_selector(get_cell_color_selector(1), count: 2) - end + describe '10 calendar activities' do + before do + 10.times { push_code_contribution } + end - it 'displays calendar activity square for yesterday', js: true do - yesterday = Date.yesterday.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(1, yesterday), count: 1) + it_behaves_like 'a day with activity', contribution_count: 10 end - it 'displays calendar activity square for today', js: true do - today = Date.today.strftime(date_format) - expect(page).to have_selector(get_cell_date_selector(1, today), count: 1) + describe 'calendar activity on two days' do + before do + push_code_contribution + + Timecop.freeze(Date.yesterday) do + Issues::CreateService.new(contributed_project, user, issue_params).execute + end + end + include_context 'visit user page' + + it 'displays calendar activity squares for both days' do + expect(page).to have_selector(get_cell_color_selector(1), count: 2) + end + + it 'displays calendar activity square for yesterday' do + yesterday = Date.yesterday.strftime(date_format) + expect(page).to have_selector(get_cell_date_selector(1, yesterday), count: 1) + end + + it 'displays calendar activity square for today' do + today = Date.today.strftime(date_format) + expect(page).to have_selector(get_cell_date_selector(1, today), count: 1) + end end end end -- cgit v1.2.1 From 1536109ccd297924474da3e46019b191013c55d4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 9 Feb 2017 22:35:32 -0500 Subject: Retrigger at.js for slash commands with autocomplete suffix --- app/assets/javascripts/gfm_auto_complete.js.es6 | 3 +++ ...-when-at-character-is-part-of-an-autocompleted-text.yml | 4 ++++ spec/features/issues/gfm_autocomplete_spec.rb | 14 ++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml diff --git a/app/assets/javascripts/gfm_auto_complete.js.es6 b/app/assets/javascripts/gfm_auto_complete.js.es6 index 7f1f2a5d278..0f6994dd2d1 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.es6 +++ b/app/assets/javascripts/gfm_auto_complete.js.es6 @@ -103,6 +103,9 @@ this.input.each((i, input) => { const $input = $(input); $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input)); + // This triggers at.js again + // Needed for slash commands with suffixes (ex: /label ~) + $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup')); }); }, setupAtWho: function($input) { diff --git a/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml b/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml new file mode 100644 index 00000000000..52b7e96682d --- /dev/null +++ b/changelogs/unreleased/27883-autocomplete-seems-to-not-trigger-when-at-character-is-part-of-an-autocompleted-text.yml @@ -0,0 +1,4 @@ +--- +title: Trigger autocomplete after selecting a slash command +merge_request: 9117 +author: diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index 93139dc9e94..7135565294b 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -182,6 +182,20 @@ feature 'GFM autocomplete', feature: true, js: true do expect(page).not_to have_selector('.atwho-view') end + it 'triggers autocomplete after selecting a slash command' do + note = find('#note_note') + page.within '.timeline-content-form' do + note.native.send_keys('') + note.native.send_keys('/as') + note.click + end + + find('.atwho-view li', text: '/assign').native.send_keys(:tab) + + user_item = find('.atwho-view li', text: user.username) + expect(user_item).to have_content(user.username) + end + def expect_to_wrap(should_wrap, item, note, value) expect(item).to have_content(value) expect(item).not_to have_content("\"#{value}\"") -- cgit v1.2.1 From 86ace2a9470805cd110f61c928f0857289f9af63 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 7 Feb 2017 16:34:05 -0600 Subject: remove unnecessary "npm install"s --- .gitlab-ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 733710bb005..f1df95ba618 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -246,7 +246,6 @@ karma: <<: *use-db <<: *dedicated-runner script: - - npm link istanbul - bundle exec rake karma artifacts: name: coverage-javascript @@ -326,8 +325,6 @@ lint:javascript: - node_modules/ stage: test image: "node:7.1" - before_script: - - npm install script: - npm --silent run eslint @@ -338,8 +335,6 @@ lint:javascript:report: - node_modules/ stage: post-test image: "node:7.1" - before_script: - - npm install script: - find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - npm --silent run eslint-report || true # ignore exit code -- cgit v1.2.1 From 469bc859ce92b2ef8cb5be56376504e44e239197 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 7 Feb 2017 16:49:03 -0600 Subject: remove recursive npm calls to avoid dependence on npm after switch to yarn --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 24e11a4607f..7b1c7a14d02 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,12 @@ "scripts": { "dev-server": "webpack-dev-server --config config/webpack.config.js", "eslint": "eslint --max-warnings 0 --ext .js,.js.es6 .", - "eslint-fix": "npm run eslint -- --fix", - "eslint-report": "npm run eslint -- --format html --output-file ./eslint-report.html", + "eslint-fix": "eslint --max-warnings 0 --ext .js,.js.es6 --fix .", + "eslint-report": "eslint --max-warnings 0 --ext .js,.js.es6 --format html --output-file ./eslint-report.html .", "karma": "karma start config/karma.config.js --single-run", "karma-start": "karma start config/karma.config.js", "webpack": "webpack --config config/webpack.config.js", - "webpack-prod": "NODE_ENV=production npm run webpack" + "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { "babel-core": "^6.22.1", -- cgit v1.2.1 From 8beb5998a8356feeb4ce75f7d749407bdd9034bc Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 7 Feb 2017 16:57:43 -0600 Subject: replace npm run calls with yarn --- .gitlab-ci.yml | 6 +++--- lib/tasks/eslint.rake | 2 +- lib/tasks/karma.rake | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f1df95ba618..71d5dce7314 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -107,7 +107,7 @@ setup-test-env: <<: *dedicated-runner stage: prepare script: - - npm install + - yarn install - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' artifacts: @@ -326,7 +326,7 @@ lint:javascript: stage: test image: "node:7.1" script: - - npm --silent run eslint + - yarn run eslint lint:javascript:report: <<: *dedicated-runner @@ -337,7 +337,7 @@ lint:javascript:report: image: "node:7.1" script: - find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - - npm --silent run eslint-report || true # ignore exit code + - yarn run eslint-report || true # ignore exit code artifacts: name: eslint-report expire_in: 31d diff --git a/lib/tasks/eslint.rake b/lib/tasks/eslint.rake index d43cbad1909..2514b050695 100644 --- a/lib/tasks/eslint.rake +++ b/lib/tasks/eslint.rake @@ -1,7 +1,7 @@ unless Rails.env.production? desc "GitLab | Run ESLint" task :eslint do - system("npm", "run", "eslint") + system("yarn", "run", "eslint") end end diff --git a/lib/tasks/karma.rake b/lib/tasks/karma.rake index 89812a179ec..35cfed9dc75 100644 --- a/lib/tasks/karma.rake +++ b/lib/tasks/karma.rake @@ -11,7 +11,7 @@ unless Rails.env.production? desc 'GitLab | Karma | Run JavaScript tests' task :tests do - sh "npm run karma" do |ok, res| + sh "yarn run karma" do |ok, res| abort('rake karma:tests failed') unless ok end end -- cgit v1.2.1 From a4c003632577116f55200d863a508220beacd2f2 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 7 Feb 2017 16:58:59 -0600 Subject: add yarn lock file --- yarn.lock | 3914 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3914 insertions(+) create mode 100644 yarn.lock diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000000..05aea4fd6f0 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3914 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1, abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +accepts@1.3.3, accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + +acorn@^3.0.0, acorn@^3.0.4, acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + +ajv@^4.7.0: + version "4.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.2.tgz#f166c3c11cbc6cb9dcc102a5bcfe5b72c95287e6" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +alter@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" + dependencies: + stable "~0.1.3" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.0.tgz#4d8f047a318604e18e3c06a0e52230d3d19f147b" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-slice@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arraybuffer.slice@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +ast-traverse@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" + +ast-types@0.8.12: + version "0.8.12" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" + +ast-types@0.8.15: + version "0.8.15" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" + +ast-types@0.9.5: + version "0.9.5" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.5.tgz#1a660a09945dbceb1f9c9cbb715002617424e04a" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@0.2.x, async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +async@1.x, async@^1.3.0, async@^1.4.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^0.9.0, async@~0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-code-frame@^6.16.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^5.4.0, babel-core@^5.6.21, babel-core@^5.8.38: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558" + dependencies: + babel-plugin-constant-folding "^1.0.1" + babel-plugin-dead-code-elimination "^1.0.2" + babel-plugin-eval "^1.0.1" + babel-plugin-inline-environment-variables "^1.0.1" + babel-plugin-jscript "^1.0.4" + babel-plugin-member-expression-literals "^1.0.1" + babel-plugin-property-literals "^1.0.1" + babel-plugin-proto-to-assign "^1.0.3" + babel-plugin-react-constant-elements "^1.0.3" + babel-plugin-react-display-name "^1.0.3" + babel-plugin-remove-console "^1.0.1" + babel-plugin-remove-debugger "^1.0.1" + babel-plugin-runtime "^1.0.7" + babel-plugin-undeclared-variables-check "^1.0.2" + babel-plugin-undefined-to-void "^1.1.6" + babylon "^5.8.38" + bluebird "^2.9.33" + chalk "^1.0.0" + convert-source-map "^1.1.0" + core-js "^1.0.0" + debug "^2.1.1" + detect-indent "^3.0.0" + esutils "^2.0.0" + fs-readdir-recursive "^0.1.0" + globals "^6.4.0" + home-or-tmp "^1.0.0" + is-integer "^1.0.4" + js-tokens "1.0.1" + json5 "^0.4.0" + lodash "^3.10.0" + minimatch "^2.0.3" + output-file-sync "^1.1.0" + path-exists "^1.0.0" + path-is-absolute "^1.0.0" + private "^0.1.6" + regenerator "0.8.40" + regexpu "^1.3.0" + repeating "^1.1.2" + resolve "^1.1.6" + shebang-regex "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + source-map-support "^0.2.10" + to-fast-properties "^1.0.0" + trim-right "^1.0.0" + try-resolve "^1.0.0" + +babel-loader@^5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-5.4.2.tgz#77fe28d8e60d0f056b1c1bca25b8494cdaab9c76" + dependencies: + babel-core "^5.4.0" + loader-utils "^0.2.9" + object-assign "^3.0.0" + +babel-plugin-constant-folding@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz#8361d364c98e449c3692bdba51eff0844290aa8e" + +babel-plugin-dead-code-elimination@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65" + +babel-plugin-eval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da" + +babel-plugin-inline-environment-variables@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz#1f58ce91207ad6a826a8bf645fafe68ff5fe3ffe" + +babel-plugin-jscript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz#8f342c38276e87a47d5fa0a8bd3d5eb6ccad8fcc" + +babel-plugin-member-expression-literals@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz#cc5edb0faa8dc927170e74d6d1c02440021624d3" + +babel-plugin-property-literals@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz#0252301900192980b1c118efea48ce93aab83336" + +babel-plugin-proto-to-assign@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz#c49e7afd02f577bc4da05ea2df002250cf7cd123" + dependencies: + lodash "^3.9.3" + +babel-plugin-react-constant-elements@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz#946736e8378429cbc349dcff62f51c143b34e35a" + +babel-plugin-react-display-name@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz#754fe38926e8424a4e7b15ab6ea6139dee0514fc" + +babel-plugin-remove-console@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz#d8f24556c3a05005d42aaaafd27787f53ff013a7" + +babel-plugin-remove-debugger@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz#fd2ea3cd61a428ad1f3b9c89882ff4293e8c14c7" + +babel-plugin-runtime@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz#bf7c7d966dd56ecd5c17fa1cb253c9acb7e54aaf" + +babel-plugin-undeclared-variables-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz#5cf1aa539d813ff64e99641290af620965f65dee" + dependencies: + leven "^1.0.2" + +babel-plugin-undefined-to-void@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" + +babel@^5.8.38: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babel/-/babel-5.8.38.tgz#dfb087c22894917c576fb67ce9cf328d458629fb" + dependencies: + babel-core "^5.6.21" + chokidar "^1.0.0" + commander "^2.6.0" + convert-source-map "^1.1.0" + fs-readdir-recursive "^0.1.0" + glob "^5.0.5" + lodash "^3.2.0" + output-file-sync "^1.1.0" + path-exists "^1.0.0" + path-is-absolute "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + +babylon@^5.8.38: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + +base64-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + +base64id@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" + +batch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + dependencies: + callsite "1.0.0" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +blob@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^2.9.33: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + +bluebird@^3.3.0: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + +body-parser@^1.12.4: + version "1.16.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.16.0.tgz#924a5e472c6229fb9d69b85a20d5f2532dec788b" + dependencies: + bytes "2.4.0" + content-type "~1.0.2" + debug "2.6.0" + depd "~1.1.0" + http-errors "~1.5.1" + iconv-lite "0.4.15" + on-finished "~2.3.0" + qs "6.2.1" + raw-body "~2.2.0" + type-is "~1.6.14" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +bootstrap-sass@3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.6.tgz#363b0d300e868d3e70134c1a742bb17288444fd1" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^0.1.2: + version "0.1.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" + dependencies: + expand-range "^0.1.0" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +breakable@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" + +browserify-aes@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-0.4.0.tgz#067149b668df31c4b58533e02d01e806d8608e2c" + dependencies: + inherits "^2.0.1" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer@^4.9.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +bytes@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" + +bytes@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2, camelcase@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.0.0, chokidar@^1.4.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +circular-json@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +colors@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +combine-lists@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6" + dependencies: + lodash "^4.5.0" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.5.0, commander@^2.6.0, commander@^2.8.1, commander@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commoner@~0.10.3: + version "0.10.8" + resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" + dependencies: + commander "^2.5.0" + detective "^4.3.1" + glob "^5.0.15" + graceful-fs "^4.1.2" + iconv-lite "^0.4.5" + mkdirp "^0.5.0" + private "^0.1.6" + q "^1.1.2" + recast "^0.11.17" + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + +component-emitter@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + +compressible@~2.0.8: + version "2.0.9" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.9.tgz#6daab4e2b599c2770dd9e21e7a891b1c5a755425" + dependencies: + mime-db ">= 1.24.0 < 2" + +compression-webpack-plugin@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-0.3.2.tgz#1edfb0e749d7366d3e701670c463359b2c0cf704" + dependencies: + async "0.2.x" + webpack-sources "^0.1.0" + optionalDependencies: + node-zopfli "^2.0.0" + +compression@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" + dependencies: + accepts "~1.3.3" + bytes "2.3.0" + compressible "~2.0.8" + debug "~2.2.0" + on-headers "~1.0.1" + vary "~1.1.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + +concat-stream@^1.4.6: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect-history-api-fallback@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" + +connect@^3.3.5: + version "3.5.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" + dependencies: + debug "~2.2.0" + finalhandler "0.5.0" + parseurl "~1.3.1" + utils-merge "1.0.0" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" + +convert-source-map@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c" + dependencies: + browserify-aes "0.4.0" + pbkdf2-compat "2.0.1" + ripemd160 "0.2.0" + sha.js "2.2.6" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + +d3@3.5.11: + version "3.5.11" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" + +d@^0.1.1, d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + dependencies: + es5-ext "~0.10.2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +dateformat@^1.0.6: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +debug@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + +debug@2.2.0, debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +debug@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" + dependencies: + ms "0.7.2" + +debug@2.6.0, debug@^2.1.1, debug@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" + +decamelize@^1.0.0, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defaults@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +defs@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" + dependencies: + alter "~0.2.0" + ast-traverse "~0.1.1" + breakable "~1.0.0" + esprima-fb "~15001.1001.0-dev-harmony-fb" + simple-fmt "~0.1.0" + simple-is "~0.2.0" + stringmap "~0.2.2" + stringset "~0.2.1" + tryor "~0.1.2" + yargs "~3.27.0" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" + dependencies: + get-stdin "^4.0.1" + minimist "^1.1.0" + repeating "^1.1.0" + +detective@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" + dependencies: + acorn "^3.1.0" + defined "^1.0.0" + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + +doctrine@1.5.0, doctrine@^1.2.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +dom-serialize@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +dropzone@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +engine.io-client@1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.2.tgz#c38767547f2a7d184f5752f6f0ad501006703766" + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "2.3.3" + engine.io-parser "1.3.2" + has-cors "1.1.0" + indexof "0.0.1" + parsejson "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + ws "1.1.1" + xmlhttprequest-ssl "1.5.3" + yeast "0.1.2" + +engine.io-parser@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" + dependencies: + after "0.8.2" + arraybuffer.slice "0.0.6" + base64-arraybuffer "0.1.5" + blob "0.0.4" + has-binary "0.1.7" + wtf-8 "1.0.0" + +engine.io@1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.2.tgz#6b59be730b348c0125b0a4589de1c355abcf7a7e" + dependencies: + accepts "1.3.3" + base64id "1.0.0" + cookie "0.3.1" + debug "2.3.3" + engine.io-parser "1.3.2" + ws "1.1.1" + +enhanced-resolve@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + +errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: + version "0.10.12" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" + dependencies: + d "^0.1.1" + es5-ext "^0.10.7" + es6-symbol "3" + +es6-map@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.4.tgz#a34b147be224773a4d7da8072794cefa3632b897" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-set "~0.1.3" + es6-symbol "~3.1.0" + event-emitter "~0.3.4" + +es6-promise@~4.0.3: + version "4.0.5" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" + +es6-set@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-symbol "3" + event-emitter "~0.3.4" + +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + +es6-weak-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" + dependencies: + d "^0.1.1" + es5-ext "^0.10.8" + es6-iterator "2" + es6-symbol "3" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-config-airbnb-base@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-10.0.1.tgz#f17d4e52992c1d45d1b7713efbcd5ecd0e7e0506" + +eslint-import-resolver-node@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" + dependencies: + debug "^2.2.0" + object-assign "^4.0.1" + resolve "^1.1.6" + +eslint-module-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" + dependencies: + debug "2.2.0" + pkg-dir "^1.0.0" + +eslint-plugin-filenames@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-filenames/-/eslint-plugin-filenames-1.1.0.tgz#bb925218ab25b1aad1c622cfa9cb8f43cc03a4ff" + dependencies: + lodash.camelcase "4.1.1" + lodash.kebabcase "4.0.1" + lodash.snakecase "4.0.1" + +eslint-plugin-import@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz#72ba306fad305d67c4816348a4699a4229ac8b4e" + dependencies: + builtin-modules "^1.1.1" + contains-path "^0.1.0" + debug "^2.2.0" + doctrine "1.5.0" + eslint-import-resolver-node "^0.2.0" + eslint-module-utils "^2.0.0" + has "^1.0.1" + lodash.cond "^4.3.0" + minimatch "^3.0.3" + pkg-up "^1.0.0" + +eslint-plugin-jasmine@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.2.0.tgz#7135879383c39a667c721d302b9f20f0389543de" + +eslint@^3.10.1: + version "3.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.15.0.tgz#bdcc6a6c5ffe08160e7b93c066695362a91e30f2" + dependencies: + babel-code-frame "^6.16.0" + chalk "^1.1.3" + concat-stream "^1.4.6" + debug "^2.1.1" + doctrine "^1.2.2" + escope "^3.6.0" + espree "^3.4.0" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + glob "^7.0.3" + globals "^9.14.0" + ignore "^3.2.0" + imurmurhash "^0.1.4" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" + is-resolvable "^1.0.0" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" + levn "^0.3.0" + lodash "^4.0.0" + mkdirp "^0.5.0" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.7.5" + strip-bom "^3.0.0" + strip-json-comments "~2.0.1" + table "^3.7.8" + text-table "~0.2.0" + user-home "^2.0.0" + +espree@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.0.tgz#41656fa5628e042878025ef467e78f125cb86e1d" + dependencies: + acorn "4.0.4" + acorn-jsx "^3.0.0" + +esprima-fb@~15001.1001.0-dev-harmony-fb: + version "15001.1001.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" + +esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^3.1.1, esprima@~3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esrecurse@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + dependencies: + estraverse "~4.1.0" + object-assign "^4.0.1" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +estraverse@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" + +esutils@^2.0.0, esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" + +event-emitter@~0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.4.tgz#8d63ddfb4cfe1fae3b32ca265c4c720222080bb5" + dependencies: + d "~0.1.1" + es5-ext "~0.10.7" + +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +eventsource@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + dependencies: + original ">=0.0.5" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + +expand-braces@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" + dependencies: + array-slice "^0.2.3" + array-unique "^0.2.1" + braces "^0.1.2" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" + dependencies: + is-number "^0.1.1" + repeat-string "^0.2.2" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +exports-loader@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.3.tgz#57dc78917f709b96f247fa91e69b554c855013c8" + dependencies: + loader-utils "0.2.x" + source-map "0.1.x" + +express@^4.13.3: + version "4.14.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.14.1.tgz#646c237f766f148c2120aff073817b9e4d7e0d33" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.2" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "~2.2.0" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + finalhandler "0.5.1" + fresh "0.3.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.3" + qs "6.2.0" + range-parser "~1.2.0" + send "0.14.2" + serve-static "~1.11.2" + type-is "~1.6.14" + utils-merge "1.0.0" + vary "~1.1.0" + +extend@^3.0.0, extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extract-zip@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.5.0.tgz#92ccf6d81ef70a9fa4c1747114ccef6d8688a6c4" + dependencies: + concat-stream "1.5.0" + debug "0.7.4" + mkdirp "0.5.0" + yauzl "2.4.1" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" + dependencies: + websocket-driver ">=0.5.1" + +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + dependencies: + pend "~1.2.0" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +finalhandler@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.0" + unpipe "~1.0.0" + +finalhandler@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.1.tgz#2c400d8d4530935bc232549c5fa385ec07de6fcd" + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.1" + unpipe "~1.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +flat-cache@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +for-in@^0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" + +for-own@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" + dependencies: + for-in "^0.1.5" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + +fresh@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" + +fs-extra@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + +fs-readdir-recursive@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.0.17" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.17.tgz#8537f3f12272678765b4fd6528c0f1f66f8f4558" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + +gauge@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + supports-color "^0.2.0" + wide-align "^1.1.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^5.0.15, glob@^5.0.5: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^6.4.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" + +globals@^9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +handlebars@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-binary@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" + dependencies: + isarray "0.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hasha@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" + dependencies: + is-stream "^1.0.1" + pinkie-promise "^2.0.0" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +home-or-tmp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" + dependencies: + os-tmpdir "^1.0.1" + user-home "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" + +http-errors@~1.5.0, http-errors@~1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" + dependencies: + inherits "2.0.3" + setprototypeof "1.0.2" + statuses ">= 1.3.1 < 2" + +http-proxy-middleware@~0.17.1: + version "0.17.3" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.3.tgz#940382147149b856084f5534752d5b5a8168cd1d" + dependencies: + http-proxy "^1.16.2" + is-glob "^3.1.0" + lodash "^4.17.2" + micromatch "^2.3.11" + +http-proxy@^1.13.0, http-proxy@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" + dependencies: + eventemitter3 "1.x.x" + requires-port "1.x.x" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + +iconv-lite@0.4.15, iconv-lite@^0.4.5: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +ignore@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410" + +imports-loader@^0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.6.5.tgz#ae74653031d59e37b3c2fb2544ac61aeae3530a6" + dependencies: + loader-utils "0.2.x" + source-map "0.1.x" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +interpret@^0.6.4: + version "0.6.6" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" + +interpret@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-integer@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.6.tgz#5273819fada880d123e1ac00a938e7172dd8d95e" + dependencies: + is-finite "^1.0.0" + +is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + dependencies: + tryit "^1.0.1" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isbinaryfile@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" + +isexe@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul@^0.4.0, istanbul@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +jasmine-core@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297" + +jasmine-jquery@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/jasmine-jquery/-/jasmine-jquery-2.1.1.tgz#d4095e646944a26763235769ab018d9f30f0d47b" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +"jquery-ui@github:jquery/jquery-ui#1.11.4": + version "1.11.4" + resolved "https://codeload.github.com/jquery/jquery-ui/tar.gz/d6713024e16de90ea71dc0544ba34e1df01b4d8a" + +jquery-ujs@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.1.tgz#6ee75b1ef4e9ac95e7124f8d71f7d351f5548e92" + dependencies: + jquery ">=1.8.0" + +jquery@2.2.1, jquery@>=1.8.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" + +js-tokens@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae" + +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +js-yaml@3.x, js-yaml@^3.5.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" + dependencies: + argparse "^1.0.7" + esprima "^3.1.1" + +jsbn@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json3@3.3.2, json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +json5@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + dependencies: + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +karma-coverage@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.1.tgz#5aff8b39cf6994dc22de4c84362c76001b637cf6" + dependencies: + dateformat "^1.0.6" + istanbul "^0.4.0" + lodash "^3.8.0" + minimatch "^3.0.0" + source-map "^0.5.1" + +karma-jasmine@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" + +karma-phantomjs-launcher@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.2.tgz#19e1041498fd75563ed86730a22c1fe579fa8fb1" + dependencies: + lodash "^4.0.1" + phantomjs-prebuilt "^2.1.7" + +karma-sourcemap-loader@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8" + dependencies: + graceful-fs "^4.1.2" + +karma-webpack@^1.8.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-1.8.1.tgz#39d5fd2edeea3cc3ef5b405989b37d5b0e6a3b4e" + dependencies: + async "~0.9.0" + loader-utils "^0.2.5" + lodash "^3.8.0" + source-map "^0.1.41" + webpack-dev-middleware "^1.0.11" + +karma@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/karma/-/karma-1.4.1.tgz#41981a71d54237606b0a3ea8c58c90773f41650e" + dependencies: + bluebird "^3.3.0" + body-parser "^1.12.4" + chokidar "^1.4.1" + colors "^1.1.0" + combine-lists "^1.0.0" + connect "^3.3.5" + core-js "^2.2.0" + di "^0.0.1" + dom-serialize "^2.2.0" + expand-braces "^0.1.1" + glob "^7.1.1" + graceful-fs "^4.1.2" + http-proxy "^1.13.0" + isbinaryfile "^3.0.0" + lodash "^3.8.0" + log4js "^0.6.31" + mime "^1.3.4" + minimatch "^3.0.0" + optimist "^0.6.1" + qjobs "^1.1.4" + range-parser "^1.2.0" + rimraf "^2.3.3" + safe-buffer "^5.0.1" + socket.io "1.7.2" + source-map "^0.5.3" + tmp "0.0.28" + useragent "^2.1.10" + +kew@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" + +kind-of@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + dependencies: + is-buffer "^1.0.2" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + optionalDependencies: + graceful-fs "^4.1.9" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +leven@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@^0.2.9: + version "0.2.16" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +lodash.camelcase@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.1.1.tgz#065b3ff08f0b7662f389934c46a5504c90e0b2d8" + dependencies: + lodash.capitalize "^4.0.0" + lodash.deburr "^4.0.0" + lodash.words "^4.0.0" + +lodash.capitalize@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9" + +lodash.cond@^4.3.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" + +lodash.deburr@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b" + +lodash.kebabcase@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz#5e63bc9aa2a5562ff3b97ca7af2f803de1bcb90e" + dependencies: + lodash.deburr "^4.0.0" + lodash.words "^4.0.0" + +lodash.snakecase@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.0.1.tgz#bd012e5d2f93f7b58b9303e9a7fbfd5db13d6281" + dependencies: + lodash.deburr "^4.0.0" + lodash.words "^4.0.0" + +lodash.words@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036" + +lodash@^3.10.0, lodash@^3.2.0, lodash@^3.8.0, lodash@^3.9.3: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.17.2, lodash@^4.3.0, lodash@^4.5.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +log4js@^0.6.31: + version "0.6.38" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" + dependencies: + readable-stream "~1.0.2" + semver "~4.3.3" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lru-cache@2.2.x: + version "2.2.4" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + +memory-fs@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +"mime-db@>= 1.24.0 < 2", mime-db@~1.26.0: + version "1.26.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" + +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: + version "2.1.14" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" + dependencies: + mime-db "~1.26.0" + +mime@1.3.4, mime@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimatch@^2.0.3: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mousetrap@1.4.6: + version "1.4.6" + resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + +nan@^2.0.0, nan@^2.3.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +node-libs-browser@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.9.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "3.3.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^2.0.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.6.29, node-pre-gyp@^0.6.4: + version "0.6.33" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.33.tgz#640ac55198f6a925972e0c16c4ac26a034d5ecc9" + dependencies: + mkdirp "~0.5.1" + nopt "~3.0.6" + npmlog "^4.0.1" + rc "~1.1.6" + request "^2.79.0" + rimraf "~2.5.4" + semver "~5.3.0" + tar "~2.2.1" + tar-pack "~3.3.0" + +node-zopfli@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-zopfli/-/node-zopfli-2.0.2.tgz#a7a473ae92aaea85d4c68d45bbf2c944c46116b8" + dependencies: + commander "^2.8.1" + defaults "^1.0.2" + nan "^2.0.0" + node-pre-gyp "^0.6.4" + +nopt@3.x, nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + +npmlog@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +once@1.x, once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +open@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" + +optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1, optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + +original@>=0.0.5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" + dependencies: + url-parse "1.0.x" + +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +output-file-sync@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parsejson@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" + dependencies: + better-assert "~1.0.0" + +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + dependencies: + better-assert "~1.0.0" + +parseurl@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pbkdf2-compat@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + +phantomjs-prebuilt@^2.1.7: + version "2.1.14" + resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.14.tgz#d53d311fcfb7d1d08ddb24014558f1188c516da0" + dependencies: + es6-promise "~4.0.3" + extract-zip "~1.5.0" + fs-extra "~1.0.0" + hasha "~2.2.0" + kew "~0.7.0" + progress "~1.1.8" + request "~2.79.0" + request-progress "~2.0.1" + which "~1.2.10" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +pkg-up@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" + dependencies: + find-up "^1.0.0" + +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +private@^0.1.6, private@~0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process@^0.11.0: + version "0.11.9" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + +progress@^1.1.8, progress@~1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + +proxy-addr@~1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.3.tgz#dc97502f5722e888467b3fa2297a7b1ff47df074" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.2.0" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@^1.1.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + +qjobs@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" + +qs@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" + +qs@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" + +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +querystringify@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" + dependencies: + bytes "2.4.0" + iconv-lite "0.4.15" + unpipe "1.0.0" + +rc@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~1.0.4" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.0, readable-stream@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~1.0.2: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +recast@0.10.33: + version "0.10.33" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" + dependencies: + ast-types "0.8.12" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + source-map "~0.5.0" + +recast@^0.10.10: + version "0.10.43" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" + dependencies: + ast-types "0.8.15" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + source-map "~0.5.0" + +recast@^0.11.17: + version "0.11.21" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.21.tgz#4e83081c6359ecb2e526d14f4138879333f20ac9" + dependencies: + ast-types "0.9.5" + esprima "~3.1.0" + private "~0.1.5" + source-map "~0.5.0" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +regenerate@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" + +regenerator@0.8.40: + version "0.8.40" + resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.40.tgz#a0e457c58ebdbae575c9f8cd75127e93756435d8" + dependencies: + commoner "~0.10.3" + defs "~1.1.0" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + recast "0.10.33" + through "~2.3.8" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpu@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexpu/-/regexpu-1.3.0.tgz#e534dc991a9e5846050c98de6d7dd4a55c9ea16d" + dependencies: + esprima "^2.6.0" + recast "^0.10.10" + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^1.1.0, repeating@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" + dependencies: + is-finite "^1.0.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request-progress@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" + dependencies: + throttleit "^1.0.0" + +request@^2.79.0, request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +require-uncached@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +requires-port@1.0.x, requires-port@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@~2.5.1, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +ripemd160@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + dependencies: + once "^1.3.0" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + +safe-buffer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" + +select2@3.5.2-browserify: + version "3.5.2-browserify" + resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" + +"semver@2 || 3 || 4 || 5", semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +semver@~4.3.3: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +send@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.14.2.tgz#39b0438b3f510be5dc6f667a11f71689368cdeef" + dependencies: + debug "~2.2.0" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + fresh "0.3.0" + http-errors "~1.5.1" + mime "1.3.4" + ms "0.7.2" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.1" + +serve-index@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" + dependencies: + accepts "~1.3.3" + batch "0.5.3" + debug "~2.2.0" + escape-html "~1.0.3" + http-errors "~1.5.0" + mime-types "~2.1.11" + parseurl "~1.3.1" + +serve-static@~1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.2.tgz#2cf9889bd4435a320cc36895c9aa57bd662e6ac7" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.14.2" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" + +sha.js@2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shelljs@^0.7.5: + version "0.7.6" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +simple-fmt@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" + +simple-is@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +socket.io-adapter@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" + dependencies: + debug "2.3.3" + socket.io-parser "2.3.1" + +socket.io-client@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.2.tgz#39fdb0c3dd450e321b7e40cfd83612ec533dd644" + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "2.3.3" + engine.io-client "1.8.2" + has-binary "0.1.7" + indexof "0.0.1" + object-component "0.0.3" + parseuri "0.0.5" + socket.io-parser "2.3.1" + to-array "0.1.4" + +socket.io-parser@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0" + dependencies: + component-emitter "1.1.2" + debug "2.2.0" + isarray "0.0.1" + json3 "3.3.2" + +socket.io@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.2.tgz#83bbbdf2e79263b378900da403e7843e05dc3b71" + dependencies: + debug "2.3.3" + engine.io "1.8.2" + has-binary "0.1.7" + object-assign "4.1.0" + socket.io-adapter "0.5.0" + socket.io-client "1.7.2" + socket.io-parser "2.3.1" + +sockjs-client@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.2.tgz#f0212a8550e4c9468c8cceaeefd2e3493c033ad5" + dependencies: + debug "^2.2.0" + eventsource "0.1.6" + faye-websocket "~0.11.0" + inherits "^2.0.1" + json3 "^3.3.2" + url-parse "^1.1.1" + +sockjs@^0.3.15: + version "0.3.18" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" + dependencies: + faye-websocket "^0.10.0" + uuid "^2.0.2" + +source-list-map@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" + +source-map-support@^0.2.10: + version "0.2.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" + dependencies: + source-map "0.1.32" + +source-map@0.1.32: + version "0.1.32" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" + dependencies: + amdefine ">=0.0.4" + +source-map@0.1.x, source-map@^0.1.41: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.4.4, source-map@~0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.2.tgz#d5a804ce22695515638e798dbe23273de070a5fa" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stable@~0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" + +stats-webpack-plugin@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/stats-webpack-plugin/-/stats-webpack-plugin-0.4.3.tgz#b2f618202f28dd04ab47d7ecf54ab846137b7aea" + +"statuses@>= 1.3.1 < 2", statuses@~1.3.0, statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-cache@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream-cache/-/stream-cache-0.0.2.tgz#1ac5ad6832428ca55667dbdee395dad4e6db118f" + +stream-http@^2.3.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.1.0" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^3.0.0" + +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringmap@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" + +stringset@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.0, supports-color@^3.1.1: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +tapable@^0.1.8, tapable@~0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + +tar-pack@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" + dependencies: + debug "~2.2.0" + fstream "~1.0.10" + fstream-ignore "~1.0.5" + once "~1.3.3" + readable-stream "~2.1.4" + rimraf "~2.5.1" + tar "~2.2.1" + uid-number "~0.0.6" + +tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + +through@^2.3.6, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timers-browserify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + dependencies: + setimmediate "^1.0.4" + +tmp@0.0.28, tmp@0.0.x: + version "0.0.28" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" + dependencies: + os-tmpdir "~1.0.1" + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +trim-right@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +try-resolve@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" + +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + +tryor@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@~1.6.14: + version "1.6.14" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.14.tgz#e219639c17ded1ca0789092dd54a03826b817cb2" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.13" + +typedarray@^0.0.6, typedarray@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uglify-js@^2.6, uglify-js@~2.7.3: + version "2.7.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + +underscore@1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + dependencies: + os-homedir "^1.0.0" + +useragent@^2.1.10: + version "2.1.12" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.1.12.tgz#aa7da6cdc48bdc37ba86790871a7321d64edbaa2" + dependencies: + lru-cache "2.2.x" + tmp "0.0.x" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +vary@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + +vue-resource@0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d" + +vue@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.0.3.tgz#3f7698f83d6ad1f0e35955447901672876c63fde" + +watchpack@^0.2.1: + version "0.2.9" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b" + dependencies: + async "^0.9.0" + chokidar "^1.0.0" + graceful-fs "^4.1.2" + +webpack-core@~0.6.9: + version "0.6.9" + resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" + dependencies: + source-list-map "~0.1.7" + source-map "~0.4.1" + +webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.4.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.0.tgz#7d5be2651e692fddfafd8aaed177c16ff51f0eb8" + dependencies: + memory-fs "~0.4.1" + mime "^1.3.4" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + +webpack-dev-server@^1.16.2: + version "1.16.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-1.16.3.tgz#cbb6a0d3e7c8eb5453b3e9befcbe843219f62661" + dependencies: + compression "^1.5.2" + connect-history-api-fallback "^1.3.0" + express "^4.13.3" + http-proxy-middleware "~0.17.1" + open "0.0.5" + optimist "~0.6.1" + serve-index "^1.7.2" + sockjs "^0.3.15" + sockjs-client "^1.0.3" + stream-cache "~0.0.1" + strip-ansi "^3.0.0" + supports-color "^3.1.1" + webpack-dev-middleware "^1.4.0" + +webpack-sources@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd" + dependencies: + source-list-map "~0.1.7" + source-map "~0.5.3" + +webpack@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.14.0.tgz#54f1ffb92051a328a5b2057d6ae33c289462c823" + dependencies: + acorn "^3.0.0" + async "^1.3.0" + clone "^1.0.2" + enhanced-resolve "~0.9.0" + interpret "^0.6.4" + loader-utils "^0.2.11" + memory-fs "~0.3.0" + mkdirp "~0.5.0" + node-libs-browser "^0.7.0" + optimist "~0.6.0" + supports-color "^3.1.0" + tapable "~0.1.8" + uglify-js "~2.7.3" + watchpack "^0.2.1" + webpack-core "~0.6.9" + +websocket-driver@>=0.5.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + dependencies: + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" + +which@^1.1.1, which@~1.2.10: + version "1.2.12" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" + dependencies: + isexe "^1.1.1" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +window-size@^0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@^1.0.0, wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +ws@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018" + dependencies: + options ">=0.0.5" + ultron "1.0.x" + +wtf-8@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" + +xmlhttprequest-ssl@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + +yargs@~3.27.0: + version "3.27.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" + dependencies: + camelcase "^1.2.1" + cliui "^2.1.0" + decamelize "^1.0.0" + os-locale "^1.4.0" + window-size "^0.1.2" + y18n "^3.2.0" + +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + dependencies: + fd-slicer "~1.0.1" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" -- cgit v1.2.1 From 93d4234efa43dea4165a8b0b87c51eb7d6131f28 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Tue, 7 Feb 2017 14:43:08 -0600 Subject: Add omniauth-oauth2-generic strategy - Allows configurable Single Sign On with most simple OAuth2 providers - Adds documentation for the new strategy Closes #26744 --- Gemfile | 1 + Gemfile.lock | 3 ++ doc/integration/oauth2_generic.md | 60 +++++++++++++++++++++++++++++++++++++++ doc/integration/omniauth.md | 1 + 4 files changed, 65 insertions(+) create mode 100644 doc/integration/oauth2_generic.md diff --git a/Gemfile b/Gemfile index 79433b12823..0060f122512 100644 --- a/Gemfile +++ b/Gemfile @@ -29,6 +29,7 @@ gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-gitlab', '~> 1.0.2' gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos +gem 'omniauth-oauth2-generic', '~> 0.2.2' gem 'omniauth-saml', '~> 1.7.0' gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 235426afa49..a3c2fad41ba 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -483,6 +483,8 @@ GEM omniauth-oauth2 (1.3.1) oauth2 (~> 1.0) omniauth (~> 1.2) + omniauth-oauth2-generic (0.2.2) + omniauth-oauth2 (~> 1.0) omniauth-saml (1.7.0) omniauth (~> 1.3) ruby-saml (~> 1.4) @@ -931,6 +933,7 @@ DEPENDENCIES omniauth-gitlab (~> 1.0.2) omniauth-google-oauth2 (~> 0.4.1) omniauth-kerberos (~> 0.3.0) + omniauth-oauth2-generic (~> 0.2.2) omniauth-saml (~> 1.7.0) omniauth-shibboleth (~> 1.2.0) omniauth-twitter (~> 1.2.0) diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md new file mode 100644 index 00000000000..3953df18d85 --- /dev/null +++ b/doc/integration/oauth2_generic.md @@ -0,0 +1,60 @@ +# Sign into Gitlab with (almost) any OAuth2 provider + +The `omniauth-oauth2-generic` gem allows Single Sign On between Gitlab and your own OAuth2 provider (or any simple OAuth2 provider compatible with this gem) + +This strategy is designed to allow configuration of the simple OmniAuth SSO process outlined below: + +1. Strategy directs client to your authorization URL (**configurable**), with specified ID and key +1. OAuth provider handles authentication of request, user, and (optionally) authorization to access user's profile +1. OAuth provider directs client back to Gitlab where Strategy handles retrieval of access token +1. Strategy requests user information from a **configurable** "user profile" URL (using the access token) +1. Strategy parses user information from the response, using a **configurable** format +1. Gitlab finds or creates the returned user and logs them in + +**Limitations of this Strategy:** + +- It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider (such as importing projects or users, etc). +- It only supports the Authorization Grant flow (most common for client-server applications, like Gitlab) +- It is not able to fetch user information from more than one URL +- It has not been tested with user information formats other than JSON + +### Config Instructions +1. To enable the OAuth2 generic strategy you must register your application in the OAuth2 provider you wish to authenticate with. + That provider should generate an ID and secret key for you to use with this strategy. + + The redirect URI you provide when registering the application should be: + + ``` + http://your-gitlab.host.com/users/auth/oauth2_generic/callback + ``` + +1. You should now be able to get a Client ID and Client Secret. Where this shows up will differ for each provider. + This may also be called Application ID and Secret. + +1. On your GitLab server, open the configuration file. + + For omnibus package: + + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` + + For installations from source: + + ```sh + cd /home/git/gitlab + + sudo -u git -H editor config/gitlab.yml + ``` + +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. + +1. Add the provider-specific configuration for your provider, as [described in the gem's README](https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example) + +1. Save the configuration file. + +1. Restart GitLab for the changes to take effect. + +On the sign in page there should now be a new button below the regular sign in form. +Click the button to begin your provider's authentication process. This will direct the browser to your OAuth2 Provider's authentication page. +If everything goes well the user will be returned to your GitLab instance and will be signed in. diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 98a680d0dbe..47e20d7566a 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -31,6 +31,7 @@ contains some settings that are common for all providers. - [Azure](azure.md) - [Auth0](auth0.md) - [Authentiq](../administration/auth/authentiq.md) +- [OAuth2Generic](oauth2_generic.md) ## Initial OmniAuth Configuration -- cgit v1.2.1 From be567848b386bdb28fcc35d745db1bdaaabc90e3 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Tue, 7 Feb 2017 15:05:51 -0600 Subject: Add changelog entry for #26744 --- changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml diff --git a/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml b/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml new file mode 100644 index 00000000000..abf6c3b02c2 --- /dev/null +++ b/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml @@ -0,0 +1,3 @@ +title: Add the omniaut-oauth2-generic OmniAuth strategy +merge_request: 9048 +author: Joe Marty \ No newline at end of file -- cgit v1.2.1 From 70f75ca10848df6b28a2bc60c3b8d62731a0ea77 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Tue, 7 Feb 2017 15:32:26 -0600 Subject: Edit omniauth-oauth2-generic docs for style conformance --- doc/integration/oauth2_generic.md | 57 +++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md index 3953df18d85..5ff63a56c3e 100644 --- a/doc/integration/oauth2_generic.md +++ b/doc/integration/oauth2_generic.md @@ -1,6 +1,7 @@ # Sign into Gitlab with (almost) any OAuth2 provider -The `omniauth-oauth2-generic` gem allows Single Sign On between Gitlab and your own OAuth2 provider (or any simple OAuth2 provider compatible with this gem) +The `omniauth-oauth2-generic` gem allows Single Sign On between Gitlab and your own OAuth2 provider +(or any OAuth2 provider compatible with this gem) This strategy is designed to allow configuration of the simple OmniAuth SSO process outlined below: @@ -11,50 +12,54 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc 1. Strategy parses user information from the response, using a **configurable** format 1. Gitlab finds or creates the returned user and logs them in -**Limitations of this Strategy:** +### Limitations of this Strategy: -- It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider (such as importing projects or users, etc). +- It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider + (importing projects or users, etc) - It only supports the Authorization Grant flow (most common for client-server applications, like Gitlab) - It is not able to fetch user information from more than one URL - It has not been tested with user information formats other than JSON ### Config Instructions -1. To enable the OAuth2 generic strategy you must register your application in the OAuth2 provider you wish to authenticate with. - That provider should generate an ID and secret key for you to use with this strategy. +1. Register your application in the OAuth2 provider you wish to authenticate with. The redirect URI you provide when registering the application should be: - ``` - http://your-gitlab.host.com/users/auth/oauth2_generic/callback - ``` + ``` + http://your-gitlab.host.com/users/auth/oauth2_generic/callback + ``` -1. You should now be able to get a Client ID and Client Secret. Where this shows up will differ for each provider. - This may also be called Application ID and Secret. +1. You should now be able to get a Client ID and Client Secret. + Where this shows up will differ for each provider. + This may also be called Application ID and Secret -1. On your GitLab server, open the configuration file. +1. On your GitLab server, open the configuration file. - For omnibus package: + For omnibus package: - ```sh - sudo editor /etc/gitlab/gitlab.rb - ``` + ```sh + sudo editor /etc/gitlab/gitlab.rb + ``` - For installations from source: + For installations from source: - ```sh - cd /home/git/gitlab + ```sh + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` -1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings. +1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings -1. Add the provider-specific configuration for your provider, as [described in the gem's README](https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example) +1. Add the provider-specific configuration for your provider, as [described in the gem's README][1] -1. Save the configuration file. +1. Save the configuration file -1. Restart GitLab for the changes to take effect. +1. Restart GitLab for the changes to take effect On the sign in page there should now be a new button below the regular sign in form. -Click the button to begin your provider's authentication process. This will direct the browser to your OAuth2 Provider's authentication page. -If everything goes well the user will be returned to your GitLab instance and will be signed in. +Click the button to begin your provider's authentication process. This will direct +the browser to your OAuth2 Provider's authentication page. If everything goes well +the user will be returned to your GitLab instance and will be signed in. + +[1]: https://gitlab.com/satorix/omniauth-oauth2-generic#gitlab-config-example \ No newline at end of file -- cgit v1.2.1 From 6d756b2b6a8cf470b3ba646048d3969889e55c94 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Fri, 10 Feb 2017 10:11:12 -0600 Subject: Fix spelling errors in docs and changelog --- .../26744-add-omniauth-oauth2-generic-strategy.yml | 2 +- doc/integration/oauth2_generic.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml b/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml index abf6c3b02c2..15da43b8091 100644 --- a/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml +++ b/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml @@ -1,3 +1,3 @@ -title: Add the omniaut-oauth2-generic OmniAuth strategy +title: Add the oauth2_generic OmniAuth strategy merge_request: 9048 author: Joe Marty \ No newline at end of file diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md index 5ff63a56c3e..0661bc62d2d 100644 --- a/doc/integration/oauth2_generic.md +++ b/doc/integration/oauth2_generic.md @@ -1,22 +1,22 @@ -# Sign into Gitlab with (almost) any OAuth2 provider +# Sign into GitLab with (almost) any OAuth2 provider -The `omniauth-oauth2-generic` gem allows Single Sign On between Gitlab and your own OAuth2 provider +The `omniauth-oauth2-generic` gem allows Single Sign On between GitLab and your own OAuth2 provider (or any OAuth2 provider compatible with this gem) This strategy is designed to allow configuration of the simple OmniAuth SSO process outlined below: 1. Strategy directs client to your authorization URL (**configurable**), with specified ID and key 1. OAuth provider handles authentication of request, user, and (optionally) authorization to access user's profile -1. OAuth provider directs client back to Gitlab where Strategy handles retrieval of access token +1. OAuth provider directs client back to GitLab where Strategy handles retrieval of access token 1. Strategy requests user information from a **configurable** "user profile" URL (using the access token) 1. Strategy parses user information from the response, using a **configurable** format -1. Gitlab finds or creates the returned user and logs them in +1. GitLab finds or creates the returned user and logs them in ### Limitations of this Strategy: - It can only be used for Single Sign on, and will not provide any other access granted by any OAuth provider (importing projects or users, etc) -- It only supports the Authorization Grant flow (most common for client-server applications, like Gitlab) +- It only supports the Authorization Grant flow (most common for client-server applications, like GitLab) - It is not able to fetch user information from more than one URL - It has not been tested with user information formats other than JSON @@ -35,7 +35,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc 1. On your GitLab server, open the configuration file. - For omnibus package: + For Omnibus package: ```sh sudo editor /etc/gitlab/gitlab.rb -- cgit v1.2.1 From d1e951c343a4916f19e2c275bc9cded35e5a56d8 Mon Sep 17 00:00:00 2001 From: Joe Marty Date: Mon, 13 Feb 2017 09:57:36 -0600 Subject: Adjust doc formatting --- doc/integration/oauth2_generic.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md index 0661bc62d2d..e71706fef7d 100644 --- a/doc/integration/oauth2_generic.md +++ b/doc/integration/oauth2_generic.md @@ -21,6 +21,7 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc - It has not been tested with user information formats other than JSON ### Config Instructions + 1. Register your application in the OAuth2 provider you wish to authenticate with. The redirect URI you provide when registering the application should be: @@ -38,15 +39,14 @@ This strategy is designed to allow configuration of the simple OmniAuth SSO proc For Omnibus package: ```sh - sudo editor /etc/gitlab/gitlab.rb + sudo editor /etc/gitlab/gitlab.rb ``` For installations from source: ```sh - cd /home/git/gitlab - - sudo -u git -H editor config/gitlab.yml + cd /home/git/gitlab + sudo -u git -H editor config/gitlab.yml ``` 1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration) for initial settings -- cgit v1.2.1 From c2102e6e3bf4fa5220d5fa4d3a4c1549f7385162 Mon Sep 17 00:00:00 2001 From: Oswaldo Ferreira Date: Thu, 2 Feb 2017 15:57:34 -0200 Subject: Move /projects/fork/:id to /projects/:id/fork --- .../unreleased/14492-change-fork-endpoint.yml | 4 + doc/api/projects.md | 2 +- doc/api/v3_to_v4.md | 1 + lib/api/projects.rb | 2 +- spec/requests/api/fork_spec.rb | 134 --------------------- spec/requests/api/projects_spec.rb | 126 +++++++++++++++++++ 6 files changed, 133 insertions(+), 136 deletions(-) create mode 100644 changelogs/unreleased/14492-change-fork-endpoint.yml delete mode 100644 spec/requests/api/fork_spec.rb diff --git a/changelogs/unreleased/14492-change-fork-endpoint.yml b/changelogs/unreleased/14492-change-fork-endpoint.yml new file mode 100644 index 00000000000..39024b51b54 --- /dev/null +++ b/changelogs/unreleased/14492-change-fork-endpoint.yml @@ -0,0 +1,4 @@ +--- +title: Move /projects/fork/:id to /projects/:id/fork +merge_request: 8940 +author: diff --git a/doc/api/projects.md b/doc/api/projects.md index bad238f57d7..e579b89d836 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -705,7 +705,7 @@ Parameters: Forks a project into the user namespace of the authenticated user or the one provided. ``` -POST /projects/fork/:id +POST /projects/:id/fork ``` Parameters: diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 0ae07b5d3de..7811025f34e 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -22,4 +22,5 @@ changes are in V4: - `/gitignores/:key` - `/gitlab_ci_ymls/:key` - `/dockerfiles/:key` +- Moved `/projects/fork/:id` to `/projects/:id/fork` diff --git a/lib/api/projects.rb b/lib/api/projects.rb index bd4b23195ac..2cacb246db8 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -220,7 +220,7 @@ module API params do optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into' end - post 'fork/:id' do + post ':id/fork' do fork_params = declared_params(include_missing: false) namespace_id = fork_params[:namespace] diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb deleted file mode 100644 index 92ac4fd334d..00000000000 --- a/spec/requests/api/fork_spec.rb +++ /dev/null @@ -1,134 +0,0 @@ -require 'spec_helper' - -describe API::Projects, api: true do - include ApiHelpers - let(:user) { create(:user) } - let(:user2) { create(:user) } - let(:admin) { create(:admin) } - let(:group) { create(:group) } - let(:group2) do - group = create(:group, name: 'group2_name') - group.add_owner(user2) - group - end - - describe 'POST /projects/fork/:id' do - let(:project) do - create(:project, :repository, creator: user, namespace: user.namespace) - end - - before do - project.add_reporter(user2) - end - - context 'when authenticated' do - it 'forks if user has sufficient access to project' do - post api("/projects/fork/#{project.id}", user2) - - expect(response).to have_http_status(201) - expect(json_response['name']).to eq(project.name) - expect(json_response['path']).to eq(project.path) - expect(json_response['owner']['id']).to eq(user2.id) - expect(json_response['namespace']['id']).to eq(user2.namespace.id) - expect(json_response['forked_from_project']['id']).to eq(project.id) - end - - it 'forks if user is admin' do - post api("/projects/fork/#{project.id}", admin) - - expect(response).to have_http_status(201) - expect(json_response['name']).to eq(project.name) - expect(json_response['path']).to eq(project.path) - expect(json_response['owner']['id']).to eq(admin.id) - expect(json_response['namespace']['id']).to eq(admin.namespace.id) - expect(json_response['forked_from_project']['id']).to eq(project.id) - end - - it 'fails on missing project access for the project to fork' do - new_user = create(:user) - post api("/projects/fork/#{project.id}", new_user) - - expect(response).to have_http_status(404) - expect(json_response['message']).to eq('404 Project Not Found') - end - - it 'fails if forked project exists in the user namespace' do - post api("/projects/fork/#{project.id}", user) - - expect(response).to have_http_status(409) - expect(json_response['message']['name']).to eq(['has already been taken']) - expect(json_response['message']['path']).to eq(['has already been taken']) - end - - it 'fails if project to fork from does not exist' do - post api('/projects/fork/424242', user) - - expect(response).to have_http_status(404) - expect(json_response['message']).to eq('404 Project Not Found') - end - - it 'forks with explicit own user namespace id' do - post api("/projects/fork/#{project.id}", user2), namespace: user2.namespace.id - - expect(response).to have_http_status(201) - expect(json_response['owner']['id']).to eq(user2.id) - end - - it 'forks with explicit own user name as namespace' do - post api("/projects/fork/#{project.id}", user2), namespace: user2.username - - expect(response).to have_http_status(201) - expect(json_response['owner']['id']).to eq(user2.id) - end - - it 'forks to another user when admin' do - post api("/projects/fork/#{project.id}", admin), namespace: user2.username - - expect(response).to have_http_status(201) - expect(json_response['owner']['id']).to eq(user2.id) - end - - it 'fails if trying to fork to another user when not admin' do - post api("/projects/fork/#{project.id}", user2), namespace: admin.namespace.id - - expect(response).to have_http_status(404) - end - - it 'fails if trying to fork to non-existent namespace' do - post api("/projects/fork/#{project.id}", user2), namespace: 42424242 - - expect(response).to have_http_status(404) - expect(json_response['message']).to eq('404 Target Namespace Not Found') - end - - it 'forks to owned group' do - post api("/projects/fork/#{project.id}", user2), namespace: group2.name - - expect(response).to have_http_status(201) - expect(json_response['namespace']['name']).to eq(group2.name) - end - - it 'fails to fork to not owned group' do - post api("/projects/fork/#{project.id}", user2), namespace: group.name - - expect(response).to have_http_status(404) - end - - it 'forks to not owned group when admin' do - post api("/projects/fork/#{project.id}", admin), namespace: group.name - - expect(response).to have_http_status(201) - expect(json_response['namespace']['name']).to eq(group.name) - end - end - - context 'when unauthenticated' do - it 'returns authentication error' do - post api("/projects/fork/#{project.id}") - - expect(response).to have_http_status(401) - expect(json_response['message']).to eq('401 Unauthorized') - end - end - end -end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ac0bbec44e0..17b5e372bdc 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1332,4 +1332,130 @@ describe API::Projects, api: true do end end end + + describe 'POST /projects/:id/fork' do + let(:project) do + create(:project, :repository, creator: user, namespace: user.namespace) + end + let(:group) { create(:group) } + let(:group2) do + group = create(:group, name: 'group2_name') + group.add_owner(user2) + group + end + + before do + project.add_reporter(user2) + end + + context 'when authenticated' do + it 'forks if user has sufficient access to project' do + post api("/projects/#{project.id}/fork", user2) + + expect(response).to have_http_status(201) + expect(json_response['name']).to eq(project.name) + expect(json_response['path']).to eq(project.path) + expect(json_response['owner']['id']).to eq(user2.id) + expect(json_response['namespace']['id']).to eq(user2.namespace.id) + expect(json_response['forked_from_project']['id']).to eq(project.id) + end + + it 'forks if user is admin' do + post api("/projects/#{project.id}/fork", admin) + + expect(response).to have_http_status(201) + expect(json_response['name']).to eq(project.name) + expect(json_response['path']).to eq(project.path) + expect(json_response['owner']['id']).to eq(admin.id) + expect(json_response['namespace']['id']).to eq(admin.namespace.id) + expect(json_response['forked_from_project']['id']).to eq(project.id) + end + + it 'fails on missing project access for the project to fork' do + new_user = create(:user) + post api("/projects/#{project.id}/fork", new_user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Project Not Found') + end + + it 'fails if forked project exists in the user namespace' do + post api("/projects/#{project.id}/fork", user) + + expect(response).to have_http_status(409) + expect(json_response['message']['name']).to eq(['has already been taken']) + expect(json_response['message']['path']).to eq(['has already been taken']) + end + + it 'fails if project to fork from does not exist' do + post api('/projects/424242/fork', user) + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Project Not Found') + end + + it 'forks with explicit own user namespace id' do + post api("/projects/#{project.id}/fork", user2), namespace: user2.namespace.id + + expect(response).to have_http_status(201) + expect(json_response['owner']['id']).to eq(user2.id) + end + + it 'forks with explicit own user name as namespace' do + post api("/projects/#{project.id}/fork", user2), namespace: user2.username + + expect(response).to have_http_status(201) + expect(json_response['owner']['id']).to eq(user2.id) + end + + it 'forks to another user when admin' do + post api("/projects/#{project.id}/fork", admin), namespace: user2.username + + expect(response).to have_http_status(201) + expect(json_response['owner']['id']).to eq(user2.id) + end + + it 'fails if trying to fork to another user when not admin' do + post api("/projects/#{project.id}/fork", user2), namespace: admin.namespace.id + + expect(response).to have_http_status(404) + end + + it 'fails if trying to fork to non-existent namespace' do + post api("/projects/#{project.id}/fork", user2), namespace: 42424242 + + expect(response).to have_http_status(404) + expect(json_response['message']).to eq('404 Target Namespace Not Found') + end + + it 'forks to owned group' do + post api("/projects/#{project.id}/fork", user2), namespace: group2.name + + expect(response).to have_http_status(201) + expect(json_response['namespace']['name']).to eq(group2.name) + end + + it 'fails to fork to not owned group' do + post api("/projects/#{project.id}/fork", user2), namespace: group.name + + expect(response).to have_http_status(404) + end + + it 'forks to not owned group when admin' do + post api("/projects/#{project.id}/fork", admin), namespace: group.name + + expect(response).to have_http_status(201) + expect(json_response['namespace']['name']).to eq(group.name) + end + end + + context 'when unauthenticated' do + it 'returns authentication error' do + post api("/projects/#{project.id}/fork") + + expect(response).to have_http_status(401) + expect(json_response['message']).to eq('401 Unauthorized') + end + end + end end -- cgit v1.2.1 From 2c55fd0019ea9ac33e310151a61892fada42d5a2 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Mon, 13 Feb 2017 13:26:33 +0200 Subject: Add GFM support to nested groups Signed-off-by: Dmitriy Zaporozhets --- app/models/group.rb | 2 +- app/models/user.rb | 2 +- app/services/projects/participants_service.rb | 2 +- lib/banzai/filter/abstract_reference_filter.rb | 2 +- lib/banzai/filter/user_reference_filter.rb | 6 +++--- lib/gitlab/regex.rb | 4 ++++ spec/lib/banzai/filter/user_reference_filter_spec.rb | 13 +++++++++++++ spec/lib/gitlab/regex_spec.rb | 12 ++++++++++++ 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/app/models/group.rb b/app/models/group.rb index cc6624ff4aa..240a17f1dc1 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -81,7 +81,7 @@ class Group < Namespace end def to_reference(_from_project = nil, full: nil) - "#{self.class.reference_prefix}#{name}" + "#{self.class.reference_prefix}#{full_path}" end def web_url diff --git a/app/models/user.rb b/app/models/user.rb index 1649bf04eaa..ad997ce2b13 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -335,7 +335,7 @@ class User < ActiveRecord::Base def reference_pattern %r{ #{Regexp.escape(reference_prefix)} - (?#{Gitlab::Regex::NAMESPACE_REGEX_STR}) + (?#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR}) }x end end diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index 96c363c8d1a..e6193fcacee 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -36,7 +36,7 @@ module Projects def groups current_user.authorized_groups.sort_by(&:path).map do |group| count = group.users.count - { username: group.path, name: group.name, count: count, avatar_url: group.avatar_url } + { username: group.full_path, name: group.full_name, count: count, avatar_url: group.avatar_url } end end diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index a3d495a5da0..955d857c679 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -285,7 +285,7 @@ module Banzai end def current_project_namespace_path - @current_project_namespace_path ||= project.namespace.path + @current_project_namespace_path ||= project.namespace.full_path end private diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb index 1aa9355b256..c973897f420 100644 --- a/lib/banzai/filter/user_reference_filter.rb +++ b/lib/banzai/filter/user_reference_filter.rb @@ -75,8 +75,8 @@ module Banzai # corresponding Namespace objects. def namespaces @namespaces ||= - Namespace.where(path: usernames).each_with_object({}) do |row, hash| - hash[row.path] = row + Namespace.where_full_path_in(usernames).each_with_object({}) do |row, hash| + hash[row.full_path] = row end end @@ -122,7 +122,7 @@ module Banzai def link_to_namespace(namespace, link_content: nil) if namespace.is_a?(Group) - link_to_group(namespace.path, namespace, link_content: link_content) + link_to_group(namespace.full_path, namespace, link_content: link_content) else link_to_user(namespace.path, namespace, link_content: link_content) end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index a3fa7c1331a..c77fe2d8bdc 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -13,6 +13,10 @@ module Gitlab NAMESPACE_REGEX_STR = '(?:' + NAMESPACE_REGEX_STR_SIMPLE + ')(?#{reference}<\/a>\.\)/) diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb index 1dbc2f6eb13..089ec4e2737 100644 --- a/spec/lib/gitlab/regex_spec.rb +++ b/spec/lib/gitlab/regex_spec.rb @@ -50,4 +50,16 @@ describe Gitlab::Regex, lib: true do it { is_expected.not_to match('9foo') } it { is_expected.not_to match('foo-') } end + + describe 'NAMESPACE_REF_REGEX_STR' do + subject { %r{\A#{Gitlab::Regex::NAMESPACE_REF_REGEX_STR}\z} } + + it { is_expected.to match('gitlab.org') } + it { is_expected.to match('gitlab.org/gitlab-git') } + it { is_expected.not_to match('gitlab.org.') } + it { is_expected.not_to match('gitlab.org/') } + it { is_expected.not_to match('/gitlab.org') } + it { is_expected.not_to match('gitlab.git') } + it { is_expected.not_to match('gitlab git') } + end end -- cgit v1.2.1 From 010c9337b5e024d5af586fcc8d73fe72a52fd9f1 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Mon, 13 Feb 2017 15:55:12 -0600 Subject: Add tests --- spec/javascripts/gl_dropdown_spec.js.es6 | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/javascripts/gl_dropdown_spec.js.es6 b/spec/javascripts/gl_dropdown_spec.js.es6 index 317f38c5888..c207fb00a47 100644 --- a/spec/javascripts/gl_dropdown_spec.js.es6 +++ b/spec/javascripts/gl_dropdown_spec.js.es6 @@ -139,6 +139,14 @@ require('~/lib/utils/url_utility'); this.dropdownButtonElement.click(); }); + it('should show loading indicator while search results are being fetched by backend', () => { + const dropdownMenu = document.querySelector('.dropdown-menu'); + + expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(true); + remoteCallback(); + expect(dropdownMenu.className.indexOf('is-loading') !== -1).toEqual(false); + }); + it('should not focus search input while remote task is not complete', () => { expect($(document.activeElement)).not.toEqual($(SEARCH_INPUT_SELECTOR)); remoteCallback(); -- cgit v1.2.1 From 9a3d2712941e01842f925beef2a3f0f6ca21434e Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 13 Feb 2017 12:33:23 -0600 Subject: display yarn version and node version within CI logs --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 71d5dce7314..edf008d2793 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -107,6 +107,8 @@ setup-test-env: <<: *dedicated-runner stage: prepare script: + - node --version + - yarn --version - yarn install - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' -- cgit v1.2.1 From 79ce7579bb7dad3dba344893fb20c2f1346c84a0 Mon Sep 17 00:00:00 2001 From: wendy0402 Date: Tue, 14 Feb 2017 09:28:45 +0700 Subject: Make it possible to pass coverage value to commit status API --- doc/api/commits.md | 2 ++ lib/api/commit_statuses.rb | 4 +++- lib/api/entities.rb | 2 +- spec/requests/api/commit_statuses_spec.rb | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/doc/api/commits.md b/doc/api/commits.md index 53ce381c8ae..ef2400aaf3c 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -444,6 +444,7 @@ POST /projects/:id/statuses/:sha | `name` or `context` | string | no | The label to differentiate this status from the status of other systems. Default value is `default` | `target_url` | string | no | The target URL to associate with this status | `description` | string | no | The short description of the status +| `coverage` | float | no | The total code coverage ```bash curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/17/statuses/18f3e63d05582537db6d183d9d557be09e1f90c8?state=success" @@ -464,6 +465,7 @@ Example response: "name" : "default", "sha" : "18f3e63d05582537db6d183d9d557be09e1f90c8", "status" : "success", + "coverage": 100.0, "description" : null, "id" : 93, "target_url" : null, diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index b6e6820c3f4..0b6076bd28c 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -46,6 +46,7 @@ module API optional :description, type: String, desc: 'A short description of the status' optional :name, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"' optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"' + optional :coverage, type: Float, desc: 'The total code coverage' end post ':id/statuses/:sha' do authorize! :create_commit_status, user_project @@ -75,7 +76,8 @@ module API name: name, ref: ref, target_url: params[:target_url], - description: params[:description] + description: params[:description], + coverage: params[:coverage] ) render_validation_error!(status) if status.invalid? diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 2a071e649fa..d4234ffe818 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -367,7 +367,7 @@ module API class CommitStatus < Grape::Entity expose :id, :sha, :ref, :status, :name, :target_url, :description, - :created_at, :started_at, :finished_at, :allow_failure + :created_at, :started_at, :finished_at, :allow_failure, :coverage expose :author, using: Entities::UserBasic end diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb index 88361def3cf..eb53fd71872 100644 --- a/spec/requests/api/commit_statuses_spec.rb +++ b/spec/requests/api/commit_statuses_spec.rb @@ -156,6 +156,7 @@ describe API::CommitStatuses, api: true do context: 'coverage', ref: 'develop', description: 'test', + coverage: 80.0, target_url: 'http://gitlab.com/status' } post api(post_url, developer), optional_params @@ -167,6 +168,7 @@ describe API::CommitStatuses, api: true do expect(json_response['status']).to eq('success') expect(json_response['name']).to eq('coverage') expect(json_response['ref']).to eq('develop') + expect(json_response['coverage']).to eq(80.0) expect(json_response['description']).to eq('test') expect(json_response['target_url']).to eq('http://gitlab.com/status') end -- cgit v1.2.1 From 0f206980d770826d887674e5182fa2bef232cde2 Mon Sep 17 00:00:00 2001 From: wendy0402 Date: Tue, 14 Feb 2017 11:25:52 +0700 Subject: add changelog --- changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml diff --git a/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml b/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml new file mode 100644 index 00000000000..74e0c18fa67 --- /dev/null +++ b/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml @@ -0,0 +1,4 @@ +--- +title: Make it possible to pass coverage value to commit status API +merge_request: 9214 +author: wendy0402 -- cgit v1.2.1 From 947dcfba372278f1d6e9fe3437d3f4199a79a530 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 13 Feb 2017 23:25:34 -0600 Subject: Use es6-promise package to avoid webpack warnings See https://github.com/stefanpenner/es6-promise/issues/100 ``` [WDS] Warnings while compiling. ./vendor/assets/javascripts/es6-promise.auto.js Module not found: Error: Can't resolve 'vertx' in '/Users/eric/Documents/gitlab/gitlab-development-kit/gitlab/vendor/assets/javascripts' @ ./vendor/assets/javascripts/es6-promise.auto.js 140:16-26 @ ./app/assets/javascripts/application.js @ multi (webpack)-dev-server/client?http://localhost:3808 ./application.js ``` --- app/assets/javascripts/application.js | 3 +- package.json | 1 + vendor/assets/javascripts/es6-promise.auto.js | 1156 ------------------------- 3 files changed, 2 insertions(+), 1158 deletions(-) delete mode 100644 vendor/assets/javascripts/es6-promise.auto.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index c9612784f9b..4b5c9686cab 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -56,8 +56,7 @@ requireAll(require.context('./u2f', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('./droplab', false, /^\.\/.*\.(js|es6)$/)); requireAll(require.context('.', false, /^\.\/(?!application\.js).*\.(js|es6)$/)); require('vendor/fuzzaldrin-plus'); -window.ES6Promise = require('vendor/es6-promise.auto'); -window.ES6Promise.polyfill(); +require('es6-promise').polyfill(); (function () { document.addEventListener('beforeunload', function () { diff --git a/package.json b/package.json index 24e11a4607f..e36925aa4f2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "compression-webpack-plugin": "^0.3.2", "d3": "3.5.11", "dropzone": "4.2.0", + "es6-promise": "^4.0.5", "imports-loader": "^0.6.5", "jquery": "2.2.1", "jquery-ui": "github:jquery/jquery-ui#1.11.4", diff --git a/vendor/assets/javascripts/es6-promise.auto.js b/vendor/assets/javascripts/es6-promise.auto.js deleted file mode 100644 index b8887115a37..00000000000 --- a/vendor/assets/javascripts/es6-promise.auto.js +++ /dev/null @@ -1,1156 +0,0 @@ -/*! - * @overview es6-promise - a tiny implementation of Promises/A+. - * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) - * @license Licensed under MIT license - * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE - * @version 4.0.5 - */ - -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.ES6Promise = factory()); -}(this, (function () { 'use strict'; - -function objectOrFunction(x) { - return typeof x === 'function' || typeof x === 'object' && x !== null; -} - -function isFunction(x) { - return typeof x === 'function'; -} - -var _isArray = undefined; -if (!Array.isArray) { - _isArray = function (x) { - return Object.prototype.toString.call(x) === '[object Array]'; - }; -} else { - _isArray = Array.isArray; -} - -var isArray = _isArray; - -var len = 0; -var vertxNext = undefined; -var customSchedulerFn = undefined; - -var asap = function asap(callback, arg) { - queue[len] = callback; - queue[len + 1] = arg; - len += 2; - if (len === 2) { - // If len is 2, that means that we need to schedule an async flush. - // If additional callbacks are queued before the queue is flushed, they - // will be processed by this flush that we are scheduling. - if (customSchedulerFn) { - customSchedulerFn(flush); - } else { - scheduleFlush(); - } - } -}; - -function setScheduler(scheduleFn) { - customSchedulerFn = scheduleFn; -} - -function setAsap(asapFn) { - asap = asapFn; -} - -var browserWindow = typeof window !== 'undefined' ? window : undefined; -var browserGlobal = browserWindow || {}; -var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; -var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]'; - -// test for web worker but not in IE10 -var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; - -// node -function useNextTick() { - // node version 0.10.x displays a deprecation warning when nextTick is used recursively - // see https://github.com/cujojs/when/issues/410 for details - return function () { - return process.nextTick(flush); - }; -} - -// vertx -function useVertxTimer() { - if (typeof vertxNext !== 'undefined') { - return function () { - vertxNext(flush); - }; - } - - return useSetTimeout(); -} - -function useMutationObserver() { - var iterations = 0; - var observer = new BrowserMutationObserver(flush); - var node = document.createTextNode(''); - observer.observe(node, { characterData: true }); - - return function () { - node.data = iterations = ++iterations % 2; - }; -} - -// web worker -function useMessageChannel() { - var channel = new MessageChannel(); - channel.port1.onmessage = flush; - return function () { - return channel.port2.postMessage(0); - }; -} - -function useSetTimeout() { - // Store setTimeout reference so es6-promise will be unaffected by - // other code modifying setTimeout (like sinon.useFakeTimers()) - var globalSetTimeout = setTimeout; - return function () { - return globalSetTimeout(flush, 1); - }; -} - -var queue = new Array(1000); -function flush() { - for (var i = 0; i < len; i += 2) { - var callback = queue[i]; - var arg = queue[i + 1]; - - callback(arg); - - queue[i] = undefined; - queue[i + 1] = undefined; - } - - len = 0; -} - -function attemptVertx() { - try { - var r = require; - var vertx = r('vertx'); - vertxNext = vertx.runOnLoop || vertx.runOnContext; - return useVertxTimer(); - } catch (e) { - return useSetTimeout(); - } -} - -var scheduleFlush = undefined; -// Decide what async method to use to triggering processing of queued callbacks: -if (isNode) { - scheduleFlush = useNextTick(); -} else if (BrowserMutationObserver) { - scheduleFlush = useMutationObserver(); -} else if (isWorker) { - scheduleFlush = useMessageChannel(); -} else if (browserWindow === undefined && typeof require === 'function') { - scheduleFlush = attemptVertx(); -} else { - scheduleFlush = useSetTimeout(); -} - -function then(onFulfillment, onRejection) { - var _arguments = arguments; - - var parent = this; - - var child = new this.constructor(noop); - - if (child[PROMISE_ID] === undefined) { - makePromise(child); - } - - var _state = parent._state; - - if (_state) { - (function () { - var callback = _arguments[_state - 1]; - asap(function () { - return invokeCallback(_state, child, callback, parent._result); - }); - })(); - } else { - subscribe(parent, child, onFulfillment, onRejection); - } - - return child; -} - -/** - `Promise.resolve` returns a promise that will become resolved with the - passed `value`. It is shorthand for the following: - - ```javascript - let promise = new Promise(function(resolve, reject){ - resolve(1); - }); - - promise.then(function(value){ - // value === 1 - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - let promise = Promise.resolve(1); - - promise.then(function(value){ - // value === 1 - }); - ``` - - @method resolve - @static - @param {Any} value value that the returned promise will be resolved with - Useful for tooling. - @return {Promise} a promise that will become fulfilled with the given - `value` -*/ -function resolve(object) { - /*jshint validthis:true */ - var Constructor = this; - - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } - - var promise = new Constructor(noop); - _resolve(promise, object); - return promise; -} - -var PROMISE_ID = Math.random().toString(36).substring(16); - -function noop() {} - -var PENDING = void 0; -var FULFILLED = 1; -var REJECTED = 2; - -var GET_THEN_ERROR = new ErrorObject(); - -function selfFulfillment() { - return new TypeError("You cannot resolve a promise with itself"); -} - -function cannotReturnOwn() { - return new TypeError('A promises callback cannot return that same promise.'); -} - -function getThen(promise) { - try { - return promise.then; - } catch (error) { - GET_THEN_ERROR.error = error; - return GET_THEN_ERROR; - } -} - -function tryThen(then, value, fulfillmentHandler, rejectionHandler) { - try { - then.call(value, fulfillmentHandler, rejectionHandler); - } catch (e) { - return e; - } -} - -function handleForeignThenable(promise, thenable, then) { - asap(function (promise) { - var sealed = false; - var error = tryThen(then, thenable, function (value) { - if (sealed) { - return; - } - sealed = true; - if (thenable !== value) { - _resolve(promise, value); - } else { - fulfill(promise, value); - } - }, function (reason) { - if (sealed) { - return; - } - sealed = true; - - _reject(promise, reason); - }, 'Settle: ' + (promise._label || ' unknown promise')); - - if (!sealed && error) { - sealed = true; - _reject(promise, error); - } - }, promise); -} - -function handleOwnThenable(promise, thenable) { - if (thenable._state === FULFILLED) { - fulfill(promise, thenable._result); - } else if (thenable._state === REJECTED) { - _reject(promise, thenable._result); - } else { - subscribe(thenable, undefined, function (value) { - return _resolve(promise, value); - }, function (reason) { - return _reject(promise, reason); - }); - } -} - -function handleMaybeThenable(promise, maybeThenable, then$$) { - if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) { - handleOwnThenable(promise, maybeThenable); - } else { - if (then$$ === GET_THEN_ERROR) { - _reject(promise, GET_THEN_ERROR.error); - } else if (then$$ === undefined) { - fulfill(promise, maybeThenable); - } else if (isFunction(then$$)) { - handleForeignThenable(promise, maybeThenable, then$$); - } else { - fulfill(promise, maybeThenable); - } - } -} - -function _resolve(promise, value) { - if (promise === value) { - _reject(promise, selfFulfillment()); - } else if (objectOrFunction(value)) { - handleMaybeThenable(promise, value, getThen(value)); - } else { - fulfill(promise, value); - } -} - -function publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._result); - } - - publish(promise); -} - -function fulfill(promise, value) { - if (promise._state !== PENDING) { - return; - } - - promise._result = value; - promise._state = FULFILLED; - - if (promise._subscribers.length !== 0) { - asap(publish, promise); - } -} - -function _reject(promise, reason) { - if (promise._state !== PENDING) { - return; - } - promise._state = REJECTED; - promise._result = reason; - - asap(publishRejection, promise); -} - -function subscribe(parent, child, onFulfillment, onRejection) { - var _subscribers = parent._subscribers; - var length = _subscribers.length; - - parent._onerror = null; - - _subscribers[length] = child; - _subscribers[length + FULFILLED] = onFulfillment; - _subscribers[length + REJECTED] = onRejection; - - if (length === 0 && parent._state) { - asap(publish, parent); - } -} - -function publish(promise) { - var subscribers = promise._subscribers; - var settled = promise._state; - - if (subscribers.length === 0) { - return; - } - - var child = undefined, - callback = undefined, - detail = promise._result; - - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; - - if (child) { - invokeCallback(settled, child, callback, detail); - } else { - callback(detail); - } - } - - promise._subscribers.length = 0; -} - -function ErrorObject() { - this.error = null; -} - -var TRY_CATCH_ERROR = new ErrorObject(); - -function tryCatch(callback, detail) { - try { - return callback(detail); - } catch (e) { - TRY_CATCH_ERROR.error = e; - return TRY_CATCH_ERROR; - } -} - -function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value = undefined, - error = undefined, - succeeded = undefined, - failed = undefined; - - if (hasCallback) { - value = tryCatch(callback, detail); - - if (value === TRY_CATCH_ERROR) { - failed = true; - error = value.error; - value = null; - } else { - succeeded = true; - } - - if (promise === value) { - _reject(promise, cannotReturnOwn()); - return; - } - } else { - value = detail; - succeeded = true; - } - - if (promise._state !== PENDING) { - // noop - } else if (hasCallback && succeeded) { - _resolve(promise, value); - } else if (failed) { - _reject(promise, error); - } else if (settled === FULFILLED) { - fulfill(promise, value); - } else if (settled === REJECTED) { - _reject(promise, value); - } -} - -function initializePromise(promise, resolver) { - try { - resolver(function resolvePromise(value) { - _resolve(promise, value); - }, function rejectPromise(reason) { - _reject(promise, reason); - }); - } catch (e) { - _reject(promise, e); - } -} - -var id = 0; -function nextId() { - return id++; -} - -function makePromise(promise) { - promise[PROMISE_ID] = id++; - promise._state = undefined; - promise._result = undefined; - promise._subscribers = []; -} - -function Enumerator(Constructor, input) { - this._instanceConstructor = Constructor; - this.promise = new Constructor(noop); - - if (!this.promise[PROMISE_ID]) { - makePromise(this.promise); - } - - if (isArray(input)) { - this._input = input; - this.length = input.length; - this._remaining = input.length; - - this._result = new Array(this.length); - - if (this.length === 0) { - fulfill(this.promise, this._result); - } else { - this.length = this.length || 0; - this._enumerate(); - if (this._remaining === 0) { - fulfill(this.promise, this._result); - } - } - } else { - _reject(this.promise, validationError()); - } -} - -function validationError() { - return new Error('Array Methods must be provided an Array'); -}; - -Enumerator.prototype._enumerate = function () { - var length = this.length; - var _input = this._input; - - for (var i = 0; this._state === PENDING && i < length; i++) { - this._eachEntry(_input[i], i); - } -}; - -Enumerator.prototype._eachEntry = function (entry, i) { - var c = this._instanceConstructor; - var resolve$$ = c.resolve; - - if (resolve$$ === resolve) { - var _then = getThen(entry); - - if (_then === then && entry._state !== PENDING) { - this._settledAt(entry._state, i, entry._result); - } else if (typeof _then !== 'function') { - this._remaining--; - this._result[i] = entry; - } else if (c === Promise) { - var promise = new c(noop); - handleMaybeThenable(promise, entry, _then); - this._willSettleAt(promise, i); - } else { - this._willSettleAt(new c(function (resolve$$) { - return resolve$$(entry); - }), i); - } - } else { - this._willSettleAt(resolve$$(entry), i); - } -}; - -Enumerator.prototype._settledAt = function (state, i, value) { - var promise = this.promise; - - if (promise._state === PENDING) { - this._remaining--; - - if (state === REJECTED) { - _reject(promise, value); - } else { - this._result[i] = value; - } - } - - if (this._remaining === 0) { - fulfill(promise, this._result); - } -}; - -Enumerator.prototype._willSettleAt = function (promise, i) { - var enumerator = this; - - subscribe(promise, undefined, function (value) { - return enumerator._settledAt(FULFILLED, i, value); - }, function (reason) { - return enumerator._settledAt(REJECTED, i, reason); - }); -}; - -/** - `Promise.all` accepts an array of promises, and returns a new promise which - is fulfilled with an array of fulfillment values for the passed promises, or - rejected with the reason of the first passed promise to be rejected. It casts all - elements of the passed iterable to promises as it runs this algorithm. - - Example: - - ```javascript - let promise1 = resolve(1); - let promise2 = resolve(2); - let promise3 = resolve(3); - let promises = [ promise1, promise2, promise3 ]; - - Promise.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); - ``` - - If any of the `promises` given to `all` are rejected, the first promise - that is rejected will be given as an argument to the returned promises's - rejection handler. For example: - - Example: - - ```javascript - let promise1 = resolve(1); - let promise2 = reject(new Error("2")); - let promise3 = reject(new Error("3")); - let promises = [ promise1, promise2, promise3 ]; - - Promise.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(error) { - // error.message === "2" - }); - ``` - - @method all - @static - @param {Array} entries array of promises - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. - @static -*/ -function all(entries) { - return new Enumerator(this, entries).promise; -} - -/** - `Promise.race` returns a new promise which is settled in the same way as the - first passed promise to settle. - - Example: - - ```javascript - let promise1 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 1'); - }, 200); - }); - - let promise2 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 2'); - }, 100); - }); - - Promise.race([promise1, promise2]).then(function(result){ - // result === 'promise 2' because it was resolved before promise1 - // was resolved. - }); - ``` - - `Promise.race` is deterministic in that only the state of the first - settled promise matters. For example, even if other promises given to the - `promises` array argument are resolved, but the first settled promise has - become rejected before the other promises became fulfilled, the returned - promise will become rejected: - - ```javascript - let promise1 = new Promise(function(resolve, reject){ - setTimeout(function(){ - resolve('promise 1'); - }, 200); - }); - - let promise2 = new Promise(function(resolve, reject){ - setTimeout(function(){ - reject(new Error('promise 2')); - }, 100); - }); - - Promise.race([promise1, promise2]).then(function(result){ - // Code here never runs - }, function(reason){ - // reason.message === 'promise 2' because promise 2 became rejected before - // promise 1 became fulfilled - }); - ``` - - An example real-world use case is implementing timeouts: - - ```javascript - Promise.race([ajax('foo.json'), timeout(5000)]) - ``` - - @method race - @static - @param {Array} promises array of promises to observe - Useful for tooling. - @return {Promise} a promise which settles in the same way as the first passed - promise to settle. -*/ -function race(entries) { - /*jshint validthis:true */ - var Constructor = this; - - if (!isArray(entries)) { - return new Constructor(function (_, reject) { - return reject(new TypeError('You must pass an array to race.')); - }); - } else { - return new Constructor(function (resolve, reject) { - var length = entries.length; - for (var i = 0; i < length; i++) { - Constructor.resolve(entries[i]).then(resolve, reject); - } - }); - } -} - -/** - `Promise.reject` returns a promise rejected with the passed `reason`. - It is shorthand for the following: - - ```javascript - let promise = new Promise(function(resolve, reject){ - reject(new Error('WHOOPS')); - }); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - let promise = Promise.reject(new Error('WHOOPS')); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - @method reject - @static - @param {Any} reason value that the returned promise will be rejected with. - Useful for tooling. - @return {Promise} a promise rejected with the given `reason`. -*/ -function reject(reason) { - /*jshint validthis:true */ - var Constructor = this; - var promise = new Constructor(noop); - _reject(promise, reason); - return promise; -} - -function needsResolver() { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); -} - -function needsNew() { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); -} - -/** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise's eventual value or the reason - why the promise cannot be fulfilled. - - Terminology - ----------- - - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. - - A promise can be in one of three states: pending, fulfilled, or rejected. - - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. - - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. - - - Basic Usage: - ------------ - - ```js - let promise = new Promise(function(resolve, reject) { - // on success - resolve(value); - - // on failure - reject(reason); - }); - - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Advanced Usage: - --------------- - - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. - - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - let xhr = new XMLHttpRequest(); - - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); - - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); - } - } - }; - }); - } - - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Unlike callbacks, promises are great composable primitives. - - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON - - return values; - }); - ``` - - @class Promise - @param {function} resolver - Useful for tooling. - @constructor -*/ -function Promise(resolver) { - this[PROMISE_ID] = nextId(); - this._result = this._state = undefined; - this._subscribers = []; - - if (noop !== resolver) { - typeof resolver !== 'function' && needsResolver(); - this instanceof Promise ? initializePromise(this, resolver) : needsNew(); - } -} - -Promise.all = all; -Promise.race = race; -Promise.resolve = resolve; -Promise.reject = reject; -Promise._setScheduler = setScheduler; -Promise._setAsap = setAsap; -Promise._asap = asap; - -Promise.prototype = { - constructor: Promise, - - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. - - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` - - Chaining - -------- - - The return value of `then` is itself a promise. This second, 'downstream' - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. - - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return 'default name'; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `'default name'` - }); - - findUser().then(function (user) { - throw new Error('Found user, but still unhappy'); - }, function (reason) { - throw new Error('`findUser` rejected and we're unhappy'); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. - // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - - ```js - findUser().then(function (user) { - throw new PedagogicalException('Upstream error'); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` - - Assimilation - ------------ - - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` - - If the assimliated promise rejects, then the downstream promise will also reject. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` - - Simple Example - -------------- - - Synchronous Example - - ```javascript - let result; - - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` - - Promise Example; - - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` - - Advanced Example - -------------- - - Synchronous Example - - ```javascript - let author, books; - - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - - function foundBooks(books) { - - } - - function failure(reason) { - - } - - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); - ``` - - Promise Example; - - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` - - @method then - @param {Function} onFulfilled - @param {Function} onRejected - Useful for tooling. - @return {Promise} - */ - then: then, - - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. - - ```js - function findAuthor(){ - throw new Error('couldn't find that author'); - } - - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } - - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` - - @method catch - @param {Function} onRejection - Useful for tooling. - @return {Promise} - */ - 'catch': function _catch(onRejection) { - return this.then(null, onRejection); - } -}; - -function polyfill() { - var local = undefined; - - if (typeof global !== 'undefined') { - local = global; - } else if (typeof self !== 'undefined') { - local = self; - } else { - try { - local = Function('return this')(); - } catch (e) { - throw new Error('polyfill failed because global object is unavailable in this environment'); - } - } - - var P = local.Promise; - - if (P) { - var promiseToString = null; - try { - promiseToString = Object.prototype.toString.call(P.resolve()); - } catch (e) { - // silently ignored - } - - if (promiseToString === '[object Promise]' && !P.cast) { - return; - } - } - - local.Promise = Promise; -} - -// Strange compat.. -Promise.polyfill = polyfill; -Promise.Promise = Promise; - -return Promise; - -}))); -- cgit v1.2.1 From 4e3c9a57453ddf4b6b5fcc4e6f14a53142c67307 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 13 Feb 2017 16:23:15 -0600 Subject: add yarn check to ensure package.json doesn't diverge from yarn.lock --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index edf008d2793..b2622ac7e0f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -109,7 +109,8 @@ setup-test-env: script: - node --version - yarn --version - - yarn install + - yarn install --pure-lockfile + - yarn check # ensure that yarn.lock matches package.json - bundle exec rake gitlab:assets:compile - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' artifacts: -- cgit v1.2.1 From 109e8ef4485fb45f0a50690e732ee2b9c6e910b0 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Fri, 30 Dec 2016 13:26:30 +0000 Subject: Make WikiPage comparable according to StaticModel interface * Add WikiPage#id method returning associated SHA for wiki page commit --- app/models/wiki_page.rb | 4 ++++ spec/models/wiki_page_spec.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 6347b274341..2caebb496db 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -207,6 +207,10 @@ class WikiPage 'projects/wikis/wiki_page' end + def id + page.version.to_s + end + private def set_attributes diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 579ebac7afb..753dc938c52 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -318,6 +318,19 @@ describe WikiPage, models: true do end end + describe '#==' do + let(:original_wiki_page) { create(:wiki_page) } + + it 'returns true for identical wiki page' do + expect(original_wiki_page).to eq(original_wiki_page) + end + + it 'returns false for updated wiki page' do + updated_wiki_page = original_wiki_page.update("Updated content") + expect(original_wiki_page).not_to eq(updated_wiki_page) + end + end + private def remove_temp_repo(path) -- cgit v1.2.1 From a616b475b16fa2689ab09fee9bb9c79c24f8bb27 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Tue, 20 Dec 2016 13:31:21 +0000 Subject: Add tests for WikiPages services * Alter wiki_pages factory with custom creation operation --- spec/factories/wiki_pages.rb | 18 ++++++++++++ spec/services/wiki_pages/create_service_spec.rb | 36 ++++++++++++++++++++++++ spec/services/wiki_pages/update_service_spec.rb | 37 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 spec/services/wiki_pages/create_service_spec.rb create mode 100644 spec/services/wiki_pages/update_service_spec.rb diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb index efa6cbe5bb1..4105f59e289 100644 --- a/spec/factories/wiki_pages.rb +++ b/spec/factories/wiki_pages.rb @@ -2,8 +2,26 @@ require 'ostruct' FactoryGirl.define do factory :wiki_page do + transient do + attrs do + { + title: 'Title', + content: 'Content for wiki page', + format: 'markdown' + } + end + end + page { OpenStruct.new(url_path: 'some-name') } association :wiki, factory: :project_wiki, strategy: :build initialize_with { new(wiki, page, true) } + + before(:create) do |page, evaluator| + page.attributes = evaluator.attrs + end + + to_create do |page| + page.create + end end end diff --git a/spec/services/wiki_pages/create_service_spec.rb b/spec/services/wiki_pages/create_service_spec.rb new file mode 100644 index 00000000000..5341ba3d261 --- /dev/null +++ b/spec/services/wiki_pages/create_service_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' + +describe WikiPages::CreateService, services: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:opts) do + { + title: 'Title', + content: 'Content for wiki page', + format: 'markdown' + } + end + let(:service) { described_class.new(project, user, opts) } + + describe '#execute' do + context "valid params" do + before do + allow(service).to receive(:execute_hooks) + project.add_master(user) + end + + subject { service.execute } + + it 'creates a valid wiki page' do + is_expected.to be_valid + expect(subject.title).to eq(opts[:title]) + expect(subject.content).to eq(opts[:content]) + expect(subject.format).to eq(opts[:format].to_sym) + end + + it 'executes webhooks' do + expect(service).to have_received(:execute_hooks).once.with(subject, 'create') + end + end + end +end diff --git a/spec/services/wiki_pages/update_service_spec.rb b/spec/services/wiki_pages/update_service_spec.rb new file mode 100644 index 00000000000..2bccca764d7 --- /dev/null +++ b/spec/services/wiki_pages/update_service_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe WikiPages::UpdateService, services: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:wiki_page) { create(:wiki_page) } + let(:opts) do + { + content: 'New content for wiki page', + format: 'markdown', + message: 'New wiki message' + } + end + let(:service) { described_class.new(project, user, opts) } + + describe '#execute' do + context "valid params" do + before do + allow(service).to receive(:execute_hooks) + project.add_master(user) + end + + subject { service.execute(wiki_page) } + + it 'updates the wiki page' do + is_expected.to be_valid + expect(subject.content).to eq(opts[:content]) + expect(subject.format).to eq(opts[:format].to_sym) + expect(subject.message).to eq(opts[:message]) + end + + it 'executes webhooks' do + expect(service).to have_received(:execute_hooks).once.with(subject, 'update') + end + end + end +end -- cgit v1.2.1 From db6a29bcf5e938da53085535210e3982d8af17e2 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Mon, 13 Feb 2017 23:47:43 -0600 Subject: update yarn.lock to reflect latest package.json --- yarn.lock | 1618 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 1016 insertions(+), 602 deletions(-) diff --git a/yarn.lock b/yarn.lock index 05aea4fd6f0..e5bb2937cf6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13,17 +13,23 @@ accepts@1.3.3, accepts@~1.3.3: mime-types "~2.1.11" negotiator "0.6.1" +acorn-dynamic-import@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.1.tgz#23f671eb6e650dab277fef477c321b1178a8cca2" + dependencies: + acorn "^4.0.3" + acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" dependencies: acorn "^3.0.4" -acorn@4.0.4: +acorn@4.0.4, acorn@^4.0.3, acorn@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" -acorn@^3.0.0, acorn@^3.0.4, acorn@^3.1.0: +acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" @@ -31,7 +37,7 @@ after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" -ajv-keywords@^1.0.0: +ajv-keywords@^1.0.0, ajv-keywords@^1.1.1: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" @@ -50,12 +56,6 @@ align-text@^0.1.1, align-text@^0.1.3: longest "^1.0.1" repeat-string "^1.5.2" -alter@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" - dependencies: - stable "~0.1.3" - amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -64,6 +64,10 @@ ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -106,10 +110,6 @@ arr-flatten@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -140,6 +140,14 @@ arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -158,22 +166,6 @@ assert@^1.1.1: dependencies: util "0.10.3" -ast-traverse@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" - -ast-types@0.8.12: - version "0.8.12" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" - -ast-types@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" - -ast-types@0.9.5: - version "0.9.5" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.5.tgz#1a660a09945dbceb1f9c9cbb715002617424e04a" - async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -182,11 +174,17 @@ async@0.2.x, async@~0.2.6: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" -async@1.x, async@^1.3.0, async@^1.4.0: +async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^0.9.0, async@~0.9.0: +async@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" + dependencies: + lodash "^4.14.0" + +async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -202,7 +200,7 @@ aws4@^1.2.1: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-code-frame@^6.16.0: +babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" dependencies: @@ -210,149 +208,548 @@ babel-code-frame@^6.16.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^5.4.0, babel-core@^5.6.21, babel-core@^5.8.38: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558" - dependencies: - babel-plugin-constant-folding "^1.0.1" - babel-plugin-dead-code-elimination "^1.0.2" - babel-plugin-eval "^1.0.1" - babel-plugin-inline-environment-variables "^1.0.1" - babel-plugin-jscript "^1.0.4" - babel-plugin-member-expression-literals "^1.0.1" - babel-plugin-property-literals "^1.0.1" - babel-plugin-proto-to-assign "^1.0.3" - babel-plugin-react-constant-elements "^1.0.3" - babel-plugin-react-display-name "^1.0.3" - babel-plugin-remove-console "^1.0.1" - babel-plugin-remove-debugger "^1.0.1" - babel-plugin-runtime "^1.0.7" - babel-plugin-undeclared-variables-check "^1.0.2" - babel-plugin-undefined-to-void "^1.1.6" - babylon "^5.8.38" - bluebird "^2.9.33" - chalk "^1.0.0" +babel-core@^6.22.1, babel-core@^6.23.0: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.23.1.tgz#c143cb621bb2f621710c220c5d579d15b8a442df" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.23.0" + babel-helpers "^6.23.0" + babel-messages "^6.23.0" + babel-register "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" + babylon "^6.11.0" convert-source-map "^1.1.0" - core-js "^1.0.0" debug "^2.1.1" - detect-indent "^3.0.0" - esutils "^2.0.0" - fs-readdir-recursive "^0.1.0" - globals "^6.4.0" - home-or-tmp "^1.0.0" - is-integer "^1.0.4" - js-tokens "1.0.1" - json5 "^0.4.0" - lodash "^3.10.0" - minimatch "^2.0.3" - output-file-sync "^1.1.0" - path-exists "^1.0.0" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" path-is-absolute "^1.0.0" private "^0.1.6" - regenerator "0.8.40" - regexpu "^1.3.0" - repeating "^1.1.2" - resolve "^1.1.6" - shebang-regex "^1.0.0" slash "^1.0.0" source-map "^0.5.0" - source-map-support "^0.2.10" - to-fast-properties "^1.0.0" - trim-right "^1.0.0" - try-resolve "^1.0.0" -babel-loader@^5.4.2: - version "5.4.2" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-5.4.2.tgz#77fe28d8e60d0f056b1c1bca25b8494cdaab9c76" +babel-generator@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5" dependencies: - babel-core "^5.4.0" - loader-utils "^0.2.9" - object-assign "^3.0.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" -babel-plugin-constant-folding@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz#8361d364c98e449c3692bdba51eff0844290aa8e" +babel-helper-bindify-decorators@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.22.0.tgz#d7f5bc261275941ac62acfc4e20dacfb8a3fe952" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-plugin-dead-code-elimination@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65" +babel-helper-builder-binary-assignment-operator-visitor@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.22.0.tgz#29df56be144d81bdeac08262bfa41d2c5e91cdcd" + dependencies: + babel-helper-explode-assignable-expression "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-plugin-eval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da" +babel-helper-call-delegate@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" + dependencies: + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-plugin-inline-environment-variables@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz#1f58ce91207ad6a826a8bf645fafe68ff5fe3ffe" +babel-helper-define-map@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz#1444f960c9691d69a2ced6a205315f8fd00804e7" + dependencies: + babel-helper-function-name "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + lodash "^4.2.0" -babel-plugin-jscript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz#8f342c38276e87a47d5fa0a8bd3d5eb6ccad8fcc" +babel-helper-explode-assignable-expression@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.22.0.tgz#c97bf76eed3e0bae4048121f2b9dae1a4e7d0478" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-plugin-member-expression-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz#cc5edb0faa8dc927170e74d6d1c02440021624d3" +babel-helper-explode-class@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.22.0.tgz#646304924aa6388a516843ba7f1855ef8dfeb69b" + dependencies: + babel-helper-bindify-decorators "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-plugin-property-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz#0252301900192980b1c118efea48ce93aab83336" +babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz#25742d67175c8903dbe4b6cb9d9e1fcb8dcf23a6" + dependencies: + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" -babel-plugin-proto-to-assign@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz#c49e7afd02f577bc4da05ea2df002250cf7cd123" +babel-helper-get-function-arity@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" dependencies: - lodash "^3.9.3" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-plugin-react-constant-elements@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz#946736e8378429cbc349dcff62f51c143b34e35a" +babel-helper-hoist-variables@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-plugin-react-display-name@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz#754fe38926e8424a4e7b15ab6ea6139dee0514fc" +babel-helper-optimise-call-expression@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz#f3ee7eed355b4282138b33d02b78369e470622f5" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.23.0" -babel-plugin-remove-console@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz#d8f24556c3a05005d42aaaafd27787f53ff013a7" +babel-helper-regex@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz#79f532be1647b1f0ee3474b5f5c3da58001d247d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + lodash "^4.2.0" -babel-plugin-remove-debugger@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz#fd2ea3cd61a428ad1f3b9c89882ff4293e8c14c7" +babel-helper-remap-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz#2186ae73278ed03b8b15ced089609da981053383" + dependencies: + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-plugin-runtime@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz#bf7c7d966dd56ecd5c17fa1cb253c9acb7e54aaf" +babel-helper-replace-supers@^6.22.0, babel-helper-replace-supers@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz#eeaf8ad9b58ec4337ca94223bacdca1f8d9b4bfd" + dependencies: + babel-helper-optimise-call-expression "^6.23.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" -babel-plugin-undeclared-variables-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz#5cf1aa539d813ff64e99641290af620965f65dee" +babel-helpers@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" dependencies: - leven "^1.0.2" + babel-runtime "^6.22.0" + babel-template "^6.23.0" -babel-plugin-undefined-to-void@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" +babel-loader@^6.2.10: + version "6.2.10" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.2.10.tgz#adefc2b242320cd5d15e65b31cea0e8b1b02d4b0" + dependencies: + find-cache-dir "^0.1.1" + loader-utils "^0.2.11" + mkdirp "^0.5.1" + object-assign "^4.0.1" -babel@^5.8.38: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel/-/babel-5.8.38.tgz#dfb087c22894917c576fb67ce9cf328d458629fb" +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" dependencies: - babel-core "^5.6.21" - chokidar "^1.0.0" - commander "^2.6.0" - convert-source-map "^1.1.0" - fs-readdir-recursive "^0.1.0" - glob "^5.0.5" - lodash "^3.2.0" - output-file-sync "^1.1.0" - path-exists "^1.0.0" - path-is-absolute "^1.0.0" - slash "^1.0.0" - source-map "^0.5.0" + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-async-generators@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + +babel-plugin-syntax-dynamic-import@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-generator-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.22.0.tgz#a720a98153a7596f204099cd5409f4b3c05bab46" + dependencies: + babel-helper-remap-async-to-generator "^6.22.0" + babel-plugin-syntax-async-generators "^6.5.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz#194b6938ec195ad36efc4c33a971acf00d8cd35e" + dependencies: + babel-helper-remap-async-to-generator "^6.22.0" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-properties@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.23.0.tgz#187b747ee404399013563c993db038f34754ac3b" + dependencies: + babel-helper-function-name "^6.23.0" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" -babylon@^5.8.38: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" +babel-plugin-transform-decorators@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.22.0.tgz#c03635b27a23b23b7224f49232c237a73988d27c" + dependencies: + babel-helper-explode-class "^6.22.0" + babel-plugin-syntax-decorators "^6.13.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz#e48895cf0b375be148cd7c8879b422707a053b51" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" + lodash "^4.2.0" + +babel-plugin-transform-es2015-classes@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz#49b53f326202a2fd1b3bbaa5e2edd8a4f78643c1" + dependencies: + babel-helper-define-map "^6.23.0" + babel-helper-function-name "^6.23.0" + babel-helper-optimise-call-expression "^6.23.0" + babel-helper-replace-supers "^6.23.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz#7c383e9629bba4820c11b0425bdd6290f7f057e7" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.22.0" + +babel-plugin-transform-es2015-destructuring@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz#672397031c21610d72dd2bbb0ba9fb6277e1c36b" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-for-of@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" + dependencies: + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz#bf69cd34889a41c33d90dfb740e0091ccff52f21" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + +babel-plugin-transform-es2015-modules-commonjs@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.23.0.tgz#cba7aa6379fb7ec99250e6d46de2973aaffa7b92" + dependencies: + babel-plugin-transform-strict-mode "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-types "^6.23.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.23.0.tgz#ae3469227ffac39b0310d90fec73bfdc4f6317b0" + dependencies: + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + +babel-plugin-transform-es2015-modules-umd@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.23.0.tgz#8d284ae2e19ed8fe21d2b1b26d6e7e0fcd94f0f1" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz#daa60e114a042ea769dd53fe528fc82311eb98fc" + dependencies: + babel-helper-replace-supers "^6.22.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz#3a2aabb70c8af945d5ce386f1a4250625a83ae3b" + dependencies: + babel-helper-call-delegate "^6.22.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz#8ba776e0affaa60bff21e921403b8a652a2ff723" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz#ab316829e866ee3f4b9eb96939757d19a5bc4593" + dependencies: + babel-helper-regex "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz#8d9cc27e7ee1decfe65454fb986452a04a613d20" + dependencies: + babel-helper-regex "^6.22.0" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.22.0.tgz#d57c8335281918e54ef053118ce6eb108468084d" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.22.0" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz#65740593a319c44522157538d690b84094617ea6" + dependencies: + regenerator-transform "0.9.8" + +babel-plugin-transform-strict-mode@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.22.0" + +babel-preset-es2015@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.22.0.tgz#af5a98ecb35eb8af764ad8a5a05eb36dc4386835" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.22.0" + babel-plugin-transform-es2015-classes "^6.22.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.22.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.22.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.22.0" + babel-plugin-transform-es2015-modules-systemjs "^6.22.0" + babel-plugin-transform-es2015-modules-umd "^6.22.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.22.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.22.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + +babel-preset-stage-2@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.22.0.tgz#ccd565f19c245cade394b21216df704a73b27c07" + dependencies: + babel-plugin-syntax-dynamic-import "^6.18.0" + babel-plugin-transform-class-properties "^6.22.0" + babel-plugin-transform-decorators "^6.22.0" + babel-preset-stage-3 "^6.22.0" + +babel-preset-stage-3@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.22.0.tgz#a4e92bbace7456fafdf651d7a7657ee0bbca9c2e" + dependencies: + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-generator-functions "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-object-rest-spread "^6.22.0" + +babel-register@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.23.0.tgz#c9aa3d4cca94b51da34826c4a0f9e08145d74ff3" + dependencies: + babel-core "^6.23.0" + babel-runtime "^6.22.0" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.22.0, babel-template@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.11.0, babylon@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" backo2@1.0.2: version "1.0.2" @@ -408,14 +805,14 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^2.9.33: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - bluebird@^3.3.0: version "3.4.7" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + body-parser@^1.12.4: version "1.16.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.16.0.tgz#924a5e472c6229fb9d69b85a20d5f2532dec788b" @@ -462,15 +859,54 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -breakable@~1.0.0: +brorand@^1.0.1: + version "1.0.7" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.7.tgz#6677fa5e4901bdbf9c9ec2a748e28dca407a9bfc" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" -browserify-aes@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-0.4.0.tgz#067149b668df31c4b58533e02d01e806d8608e2c" +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" inherits "^2.0.1" + parse-asn1 "^5.0.0" browserify-zlib@^0.1.4: version "0.1.4" @@ -482,7 +918,11 @@ buffer-shims@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" -buffer@^4.9.0: +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -520,20 +960,13 @@ callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^1.0.2, camelcase@^1.2.1: +camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" caseless@~0.11.0: version "0.11.0" @@ -556,7 +989,7 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chokidar@^1.0.0, chokidar@^1.4.1: +chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0: version "1.6.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" dependencies: @@ -571,6 +1004,12 @@ chokidar@^1.0.0, chokidar@^1.4.1: optionalDependencies: fsevents "^1.0.0" +cipher-base@^1.0.0, cipher-base@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" + dependencies: + inherits "^2.0.1" + circular-json@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" @@ -593,6 +1032,14 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" @@ -621,25 +1068,15 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.5.0, commander@^2.6.0, commander@^2.8.1, commander@^2.9.0: +commander@^2.8.1, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: graceful-readlink ">= 1.0.0" -commoner@~0.10.3: - version "0.10.8" - resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" - dependencies: - commander "^2.5.0" - detective "^4.3.1" - glob "^5.0.15" - graceful-fs "^4.1.2" - iconv-lite "^0.4.5" - mkdirp "^0.5.0" - private "^0.1.6" - q "^1.1.2" - recast "^0.11.17" +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" component-bind@1.0.0: version "1.0.0" @@ -754,11 +1191,7 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - -core-js@^2.2.0: +core-js@^2.2.0, core-js@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" @@ -766,26 +1199,49 @@ core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^1.0.0" + sha.js "^2.3.6" + +create-hmac@^1.1.0, create-hmac@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" + dependencies: + create-hash "^1.1.0" + inherits "^2.0.1" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" dependencies: boom "2.x.x" -crypto-browserify@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c" - dependencies: - browserify-aes "0.4.0" - pbkdf2-compat "2.0.1" - ripemd160 "0.2.0" - sha.js "2.2.6" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" +crypto-browserify@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" dependencies: - array-find-index "^1.0.1" + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" custom-event@~1.0.0: version "1.0.1" @@ -811,13 +1267,6 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -dateformat@^1.0.6: - version "1.0.12" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" - dependencies: - get-stdin "^4.0.1" - meow "^3.3.0" - debug@0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" @@ -840,7 +1289,7 @@ debug@2.6.0, debug@^2.1.1, debug@^2.2.0: dependencies: ms "0.7.2" -decamelize@^1.0.0, decamelize@^1.1.2: +decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -858,25 +1307,6 @@ defaults@^1.0.2: dependencies: clone "^1.0.2" -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -defs@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" - dependencies: - alter "~0.2.0" - ast-traverse "~0.1.1" - breakable "~1.0.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - simple-fmt "~0.1.0" - simple-is "~0.2.0" - stringmap "~0.2.2" - stringset "~0.2.1" - tryor "~0.1.2" - yargs "~3.27.0" - del@^2.0.2: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" @@ -901,29 +1331,35 @@ depd@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-indent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" - dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - repeating "^1.1.0" - -detective@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" dependencies: - acorn "^3.1.0" - defined "^1.0.0" + repeating "^2.0.0" di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + doctrine@1.5.0, doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -958,6 +1394,15 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +elliptic@^6.0.0: + version "6.3.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -1005,13 +1450,14 @@ engine.io@1.8.2: engine.io-parser "1.3.2" ws "1.1.1" -enhanced-resolve@~0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" +enhanced-resolve@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83d86e17f4f421dec" dependencies: graceful-fs "^4.1.2" - memory-fs "^0.2.0" - tapable "^0.1.8" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.5" ent@~2.2.0: version "2.2.0" @@ -1205,15 +1651,11 @@ espree@^3.4.0: acorn "4.0.4" acorn-jsx "^3.0.0" -esprima-fb@~15001.1001.0-dev-harmony-fb: - version "15001.1001.0-dev-harmony-fb" - resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" - -esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: +esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" -esprima@^3.1.1, esprima@~3.1.0: +esprima@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -1236,7 +1678,7 @@ estraverse@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" -esutils@^2.0.0, esutils@^2.0.2: +esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -1259,12 +1701,18 @@ events@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" -eventsource@0.1.6: +eventsource@~0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" dependencies: original ">=0.0.5" +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -1296,13 +1744,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -exports-loader@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.3.tgz#57dc78917f709b96f247fa91e69b554c855013c8" - dependencies: - loader-utils "0.2.x" - source-map "0.1.x" - express@^4.13.3: version "4.14.1" resolved "https://registry.yarnpkg.com/express/-/express-4.14.1.tgz#646c237f766f148c2120aff073817b9e4d7e0d33" @@ -1427,6 +1868,14 @@ finalhandler@0.5.1: statuses "~1.3.1" unpipe "~1.0.0" +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -1481,10 +1930,6 @@ fs-extra@~1.0.0: jsonfile "^2.1.0" klaw "^1.0.0" -fs-readdir-recursive@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1541,9 +1986,9 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" getpass@^0.1.1: version "0.1.6" @@ -1564,7 +2009,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^5.0.15, glob@^5.0.5: +glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: @@ -1585,11 +2030,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^6.4.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" - -globals@^9.14.0: +globals@^9.0.0, globals@^9.14.0: version "9.14.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" @@ -1604,7 +2045,7 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1612,6 +2053,10 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" +handle-thing@^1.2.4: + version "1.2.5" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" + handlebars@^4.0.1: version "4.0.6" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" @@ -1661,6 +2106,12 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" +hash.js@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + dependencies: + inherits "^2.0.1" + hasha@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" @@ -1681,17 +2132,34 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -home-or-tmp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" dependencies: + os-homedir "^1.0.0" os-tmpdir "^1.0.1" - user-home "^1.1.1" hosted-git-info@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5" +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" + +http-deceiver@^1.2.4: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + http-errors@~1.5.0, http-errors@~1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" @@ -1728,7 +2196,7 @@ https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@0.4.15, iconv-lite@^0.4.5: +iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" @@ -1751,12 +2219,6 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" @@ -1798,14 +2260,16 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -interpret@^0.6.4: - version "0.6.6" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" - interpret@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" +invariant@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -1884,12 +2348,6 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-integer@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.6.tgz#5273819fada880d123e1ac00a938e7172dd8d95e" - dependencies: - is-finite "^1.0.0" - is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.15.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" @@ -1981,7 +2439,7 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul@^0.4.0, istanbul@^0.4.5: +istanbul@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" dependencies: @@ -2028,9 +2486,9 @@ jquery@2.2.1, jquery@>=1.8.0: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" -js-tokens@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae" +js-cookie@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526" js-tokens@^3.0.0: version "3.0.1" @@ -2047,6 +2505,10 @@ jsbn@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -2073,10 +2535,6 @@ json3@3.3.2, json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" -json5@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" - json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -2103,16 +2561,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -karma-coverage@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.1.tgz#5aff8b39cf6994dc22de4c84362c76001b637cf6" - dependencies: - dateformat "^1.0.6" - istanbul "^0.4.0" - lodash "^3.8.0" - minimatch "^3.0.0" - source-map "^0.5.1" - karma-jasmine@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" @@ -2130,9 +2578,9 @@ karma-sourcemap-loader@^0.3.7: dependencies: graceful-fs "^4.1.2" -karma-webpack@^1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-1.8.1.tgz#39d5fd2edeea3cc3ef5b405989b37d5b0e6a3b4e" +karma-webpack@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.2.tgz#bd38350af5645c9644090770939ebe7ce726f864" dependencies: async "~0.9.0" loader-utils "^0.2.5" @@ -2140,7 +2588,7 @@ karma-webpack@^1.8.0: source-map "^0.1.41" webpack-dev-middleware "^1.0.11" -karma@^1.3.0: +karma@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/karma/-/karma-1.4.1.tgz#41981a71d54237606b0a3ea8c58c90773f41650e" dependencies: @@ -2198,10 +2646,6 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -leven@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3" - levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -2219,7 +2663,11 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@^0.2.9: +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2266,11 +2714,11 @@ lodash.words@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036" -lodash@^3.10.0, lodash@^3.2.0, lodash@^3.8.0, lodash@^3.9.3: +lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.17.2, lodash@^4.3.0, lodash@^4.5.0: +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -2285,58 +2733,27 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" + js-tokens "^3.0.0" lru-cache@2.2.x: version "2.2.4" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -memory-fs@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" - -memory-fs@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@~0.4.1: +memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -2363,6 +2780,13 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + "mime-db@>= 1.24.0 < 2", mime-db@~1.26.0: version "1.26.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" @@ -2377,30 +2801,24 @@ mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimatch@^2.0.3: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - dependencies: - brace-expansion "^1.0.0" - -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" @@ -2413,6 +2831,10 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" +moment@2.x: + version "2.17.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" + mousetrap@1.4.6: version "1.4.6" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a" @@ -2441,16 +2863,16 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" -node-libs-browser@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.7.0.tgz#3e272c0819e308935e26674408d7af0e1491b83b" +node-libs-browser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" dependencies: assert "^1.1.1" browserify-zlib "^0.1.4" - buffer "^4.9.0" + buffer "^4.3.0" console-browserify "^1.1.0" constants-browserify "^1.0.0" - crypto-browserify "3.3.0" + crypto-browserify "^3.11.0" domain-browser "^1.1.1" events "^1.0.0" https-browserify "0.0.1" @@ -2498,7 +2920,7 @@ nopt@3.x, nopt@~3.0.6: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: +normalize-package-data@^2.3.2: version "2.3.5" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" dependencies: @@ -2532,10 +2954,6 @@ object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -2551,6 +2969,10 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" +obuf@^1.0.0, obuf@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -2577,11 +2999,14 @@ onetime@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" -open@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" +opn@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" -optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: +optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" dependencies: @@ -2627,18 +3052,20 @@ os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -output-file-sync@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" - dependencies: - graceful-fs "^4.1.4" - mkdirp "^0.5.1" - object-assign "^4.1.0" - pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" +parse-asn1@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -2680,10 +3107,6 @@ path-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" -path-exists@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" - path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -2710,9 +3133,11 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -pbkdf2-compat@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288" +pbkdf2@^3.0.3: + version "3.0.9" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + dependencies: + create-hmac "^1.1.2" pend@~1.2.0: version "1.2.0" @@ -2736,6 +3161,12 @@ pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" +pikaday@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pikaday/-/pikaday-1.5.1.tgz#0a48549bc1a14ea1d08c44074d761bc2f2bfcfd3" + optionalDependencies: + moment "2.x" + pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" @@ -2762,6 +3193,14 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +portfinder@^1.0.9: + version "1.0.13" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -2770,7 +3209,7 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -private@^0.1.6, private@~0.1.5: +private@^0.1.6: version "0.1.7" resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" @@ -2797,6 +3236,16 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -2805,10 +3254,6 @@ punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -q@^1.1.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" - qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" @@ -2844,6 +3289,10 @@ randomatic@^1.1.3: is-number "^2.0.2" kind-of "^3.0.2" +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" @@ -2941,60 +3390,27 @@ readline2@^1.0.1: is-fullwidth-code-point "^1.0.0" mute-stream "0.0.5" -recast@0.10.33: - version "0.10.33" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" - dependencies: - ast-types "0.8.12" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" - -recast@^0.10.10: - version "0.10.43" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" - dependencies: - ast-types "0.8.15" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" - -recast@^0.11.17: - version "0.11.21" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.21.tgz#4e83081c6359ecb2e526d14f4138879333f20ac9" - dependencies: - ast-types "0.9.5" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" dependencies: resolve "^1.1.6" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - regenerate@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" -regenerator@0.8.40: - version "0.8.40" - resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.40.tgz#a0e457c58ebdbae575c9f8cd75127e93756435d8" +regenerator-runtime@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" + +regenerator-transform@0.9.8: + version "0.9.8" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c" dependencies: - commoner "~0.10.3" - defs "~1.1.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - recast "0.10.33" - through "~2.3.8" + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" regex-cache@^0.4.2: version "0.4.3" @@ -3003,12 +3419,10 @@ regex-cache@^0.4.2: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" -regexpu@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexpu/-/regexpu-1.3.0.tgz#e534dc991a9e5846050c98de6d7dd4a55c9ea16d" +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" dependencies: - esprima "^2.6.0" - recast "^0.10.10" regenerate "^1.2.1" regjsgen "^0.2.0" regjsparser "^0.1.4" @@ -3035,12 +3449,6 @@ repeat-string@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^1.1.0, repeating@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" - dependencies: - is-finite "^1.0.0" - repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" @@ -3078,6 +3486,14 @@ request@^2.79.0, request@~2.79.0: tunnel-agent "~0.4.1" uuid "^3.0.0" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + require-uncached@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" @@ -3093,14 +3509,10 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@1.1.x: +resolve@1.1.x, resolve@^1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" - restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -3120,9 +3532,9 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@~2.5.1, rimraf@~2.5.4: dependencies: glob "^7.0.5" -ripemd160@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" +ripemd160@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" run-async@^0.1.0: version "0.1.0" @@ -3138,6 +3550,10 @@ safe-buffer@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + select2@3.5.2-browserify: version "3.5.2-browserify" resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" @@ -3189,7 +3605,7 @@ serve-static@~1.11.2: parseurl "~1.3.1" send "0.14.2" -set-blocking@~2.0.0: +set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3205,13 +3621,11 @@ setprototypeof@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" -sha.js@2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +sha.js@^2.3.6: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + dependencies: + inherits "^2.0.1" shelljs@^0.7.5: version "0.7.6" @@ -3225,14 +3639,6 @@ signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -simple-fmt@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" - -simple-is@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" - slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -3291,18 +3697,18 @@ socket.io@1.7.2: socket.io-client "1.7.2" socket.io-parser "2.3.1" -sockjs-client@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.2.tgz#f0212a8550e4c9468c8cceaeefd2e3493c033ad5" +sockjs-client@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0" dependencies: debug "^2.2.0" - eventsource "0.1.6" + eventsource "~0.1.6" faye-websocket "~0.11.0" inherits "^2.0.1" json3 "^3.3.2" url-parse "^1.1.1" -sockjs@^0.3.15: +sockjs@0.3.18: version "0.3.18" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" dependencies: @@ -3313,17 +3719,11 @@ source-list-map@~0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" -source-map-support@^0.2.10: - version "0.2.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" - dependencies: - source-map "0.1.32" - -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" +source-map-support@^0.4.2: + version "0.4.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" dependencies: - amdefine ">=0.0.4" + source-map "^0.5.3" source-map@0.1.x, source-map@^0.1.41: version "0.1.43" @@ -3331,13 +3731,13 @@ source-map@0.1.x, source-map@^0.1.41: dependencies: amdefine ">=0.0.4" -source-map@^0.4.4, source-map@~0.4.1: +source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.3, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" @@ -3361,6 +3761,26 @@ spdx-license-ids@^1.0.2: version "1.2.2" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" +spdy-transport@^2.0.15: + version "2.0.18" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.18.tgz#43fc9c56be2cccc12bb3e2754aa971154e836ea6" + dependencies: + debug "^2.2.0" + hpack.js "^2.1.6" + obuf "^1.1.0" + readable-stream "^2.0.1" + wbuf "^1.4.0" + +spdy@^3.4.1: + version "3.4.4" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.4.tgz#e0406407ca90ff01b553eb013505442649f5a819" + dependencies: + debug "^2.2.0" + handle-thing "^1.2.4" + http-deceiver "^1.2.4" + select-hose "^2.0.0" + spdy-transport "^2.0.15" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -3380,11 +3800,7 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -stable@~0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" - -stats-webpack-plugin@^0.4.2: +stats-webpack-plugin@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/stats-webpack-plugin/-/stats-webpack-plugin-0.4.3.tgz#b2f618202f28dd04ab47d7ecf54ab846137b7aea" @@ -3399,10 +3815,6 @@ stream-browserify@^2.0.1: inherits "~2.0.1" readable-stream "^2.0.2" -stream-cache@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/stream-cache/-/stream-cache-0.0.2.tgz#1ac5ad6832428ca55667dbdee395dad4e6db118f" - stream-http@^2.3.1: version "2.6.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3" @@ -3413,7 +3825,7 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" -string-width@^1.0.1: +string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -3432,14 +3844,6 @@ string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -stringmap@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" - -stringset@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" - stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -3460,12 +3864,6 @@ strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.0.1" - strip-json-comments@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" @@ -3499,9 +3897,9 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" -tapable@^0.1.8, tapable@~0.1.8: - version "0.1.10" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" +tapable@^0.2.5, tapable@~0.2.5: + version "0.2.6" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" tar-pack@~3.3.0: version "3.3.0" @@ -3532,10 +3930,14 @@ throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" -through@^2.3.6, through@~2.3.8: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +timeago.js@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-2.0.5.tgz#730c74fbdb0b0917a553675a4460e3a7f80db86c" + timers-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" @@ -3556,7 +3958,7 @@ to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" -to-fast-properties@^1.0.0: +to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" @@ -3566,26 +3968,14 @@ tough-cookie@~2.3.0: dependencies: punycode "^1.4.1" -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - -trim-right@^1.0.0: +trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -try-resolve@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" - tryit@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" -tryor@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" - tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -3615,7 +4005,7 @@ typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -uglify-js@^2.6, uglify-js@~2.7.3: +uglify-js@^2.6, uglify-js@^2.7.5: version "2.7.5" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" dependencies: @@ -3665,10 +4055,6 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - user-home@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" @@ -3739,22 +4125,21 @@ vue@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/vue/-/vue-2.0.3.tgz#3f7698f83d6ad1f0e35955447901672876c63fde" -watchpack@^0.2.1: - version "0.2.9" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b" +watchpack@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.2.1.tgz#01efa80c5c29e5c56ba55d6f5470a35b6402f0b2" dependencies: - async "^0.9.0" - chokidar "^1.0.0" + async "^2.1.2" + chokidar "^1.4.3" graceful-fs "^4.1.2" -webpack-core@~0.6.9: - version "0.6.9" - resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.9.tgz#fc571588c8558da77be9efb6debdc5a3b172bdc2" +wbuf@^1.1.0, wbuf@^1.4.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" dependencies: - source-list-map "~0.1.7" - source-map "~0.4.1" + minimalistic-assert "^1.0.0" -webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.4.0: +webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.0.tgz#7d5be2651e692fddfafd8aaed177c16ff51f0eb8" dependencies: @@ -3763,50 +4148,59 @@ webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.4.0: path-is-absolute "^1.0.0" range-parser "^1.0.3" -webpack-dev-server@^1.16.2: - version "1.16.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-1.16.3.tgz#cbb6a0d3e7c8eb5453b3e9befcbe843219f62661" +webpack-dev-server@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.3.0.tgz#0437704bbd4d941a6e4c061eb3cc232ed7d06101" dependencies: + ansi-html "0.0.7" + chokidar "^1.6.0" compression "^1.5.2" connect-history-api-fallback "^1.3.0" express "^4.13.3" + html-entities "^1.2.0" http-proxy-middleware "~0.17.1" - open "0.0.5" - optimist "~0.6.1" + opn "4.0.2" + portfinder "^1.0.9" serve-index "^1.7.2" - sockjs "^0.3.15" - sockjs-client "^1.0.3" - stream-cache "~0.0.1" + sockjs "0.3.18" + sockjs-client "1.1.1" + spdy "^3.4.1" strip-ansi "^3.0.0" supports-color "^3.1.1" - webpack-dev-middleware "^1.4.0" + webpack-dev-middleware "^1.9.0" + yargs "^6.0.0" -webpack-sources@^0.1.0: +webpack-sources@^0.1.0, webpack-sources@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.4.tgz#ccc2c817e08e5fa393239412690bb481821393cd" dependencies: source-list-map "~0.1.7" source-map "~0.5.3" -webpack@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.14.0.tgz#54f1ffb92051a328a5b2057d6ae33c289462c823" +webpack@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.2.1.tgz#7bb1d72ae2087dd1a4af526afec15eed17dda475" dependencies: - acorn "^3.0.0" - async "^1.3.0" - clone "^1.0.2" - enhanced-resolve "~0.9.0" - interpret "^0.6.4" - loader-utils "^0.2.11" - memory-fs "~0.3.0" + acorn "^4.0.4" + acorn-dynamic-import "^2.0.0" + ajv "^4.7.0" + ajv-keywords "^1.1.1" + async "^2.1.2" + enhanced-resolve "^3.0.0" + interpret "^1.0.0" + json-loader "^0.5.4" + loader-runner "^2.3.0" + loader-utils "^0.2.16" + memory-fs "~0.4.1" mkdirp "~0.5.0" - node-libs-browser "^0.7.0" - optimist "~0.6.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" supports-color "^3.1.0" - tapable "~0.1.8" - uglify-js "~2.7.3" - watchpack "^0.2.1" - webpack-core "~0.6.9" + tapable "~0.2.5" + uglify-js "^2.7.5" + watchpack "^1.2.0" + webpack-sources "^0.1.4" + yargs "^6.0.0" websocket-driver@>=0.5.1: version "0.6.5" @@ -3818,6 +4212,10 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + which@^1.1.1, which@~1.2.10: version "1.2.12" resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" @@ -3834,10 +4232,6 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -window-size@^0.1.2: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" @@ -3850,6 +4244,13 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -3879,10 +4280,34 @@ xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" -y18n@^3.2.0: +y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" +yargs-parser@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" + dependencies: + camelcase "^3.0.0" + +yargs@^6.0.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^4.2.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" @@ -3892,17 +4317,6 @@ yargs@~3.10.0: decamelize "^1.0.0" window-size "0.1.0" -yargs@~3.27.0: - version "3.27.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" - dependencies: - camelcase "^1.2.1" - cliui "^2.1.0" - decamelize "^1.0.0" - os-locale "^1.4.0" - window-size "^0.1.2" - y18n "^3.2.0" - yauzl@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" -- cgit v1.2.1 From 03f1abfcc3e43fce188f94a770f5f7b6af6f36b5 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 14 Feb 2017 13:48:13 +0800 Subject: Only ensure against yield so that pool should be available Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9192#note_23293693 --- lib/gitlab/database.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index e6612bc3aad..a6e9ea3dead 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -72,10 +72,11 @@ module Gitlab def self.with_connection_pool(pool_size) pool = create_connection_pool(pool_size) - yield(pool) - - ensure - pool.disconnect! + begin + yield(pool) + ensure + pool.disconnect! + end end def self.create_connection_pool(pool_size) -- cgit v1.2.1 From 142432ce5ae78cc873b9748f41e54257adc56dcc Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Tue, 14 Feb 2017 17:27:29 +1100 Subject: update issue count when closing/reopening an issue --- app/assets/javascripts/issue.js | 3 +++ spec/javascripts/issue_spec.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 6c08b1b8e61..1776b3d61f6 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -54,16 +54,19 @@ require('vendor/task_list'); success: function(data, textStatus, jqXHR) { if ('id' in data) { $(document).trigger('issuable:change'); + const currentTotal = Number($('.issue_counter').text()); if (isClose) { $('a.btn-close').addClass('hidden'); $('a.btn-reopen').removeClass('hidden'); $('div.status-box-closed').removeClass('hidden'); $('div.status-box-open').addClass('hidden'); + $('.issue_counter').text(currentTotal - 1); } else { $('a.btn-reopen').addClass('hidden'); $('a.btn-close').removeClass('hidden'); $('div.status-box-closed').addClass('hidden'); $('div.status-box-open').removeClass('hidden'); + $('.issue_counter').text(currentTotal + 1); } } else { new Flash(issueFailMessage, 'alert'); diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js index 5b0b7aa7903..beb544468ef 100644 --- a/spec/javascripts/issue_spec.js +++ b/spec/javascripts/issue_spec.js @@ -105,6 +105,7 @@ require('~/issue'); expectIssueState(false); expect($btnClose).toHaveProp('disabled', false); + expect($('.issue_counter')).toHaveText(0); }); it('fails to close an issue with success:false', function() { @@ -121,6 +122,7 @@ require('~/issue'); expectIssueState(true); expect($btnClose).toHaveProp('disabled', false); expectErrorMessage(); + expect($('.issue_counter')).toHaveText(1); }); it('fails to closes an issue with HTTP error', function() { @@ -135,6 +137,7 @@ require('~/issue'); expectIssueState(true); expect($btnClose).toHaveProp('disabled', true); expectErrorMessage(); + expect($('.issue_counter')).toHaveText(1); }); }); @@ -159,6 +162,7 @@ require('~/issue'); expectIssueState(true); expect($btnReopen).toHaveProp('disabled', false); + expect($('.issue_counter')).toHaveText(1); }); }); }).call(this); -- cgit v1.2.1 From 1fc6f6cc7101a9c4dee694682a3533a5e4ba3447 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Tue, 20 Dec 2016 13:32:43 +0000 Subject: Execute web hooks for WikiPage delete operation * Add a new DestroyService for Wiki Pages * Alter WikiPagesController to use the new service --- app/controllers/projects/wikis_controller.rb | 2 +- app/services/wiki_pages/destroy_service.rb | 11 +++++++++++ ...iki-page-delete-does-not-trigger-the-webhook.yml | 4 ++++ doc/user/project/integrations/webhooks.md | 2 +- spec/services/wiki_pages/destroy_service_spec.rb | 21 +++++++++++++++++++++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 app/services/wiki_pages/destroy_service.rb create mode 100644 changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml create mode 100644 spec/services/wiki_pages/destroy_service_spec.rb diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 0faa71c4d7d..2d8064c9878 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -84,7 +84,7 @@ class Projects::WikisController < Projects::ApplicationController def destroy @page = @project_wiki.find_page(params[:id]) - @page&.delete + WikiPages::DestroyService.new(@project, current_user).execute(@page) redirect_to( namespace_project_wiki_path(@project.namespace, @project, :home), diff --git a/app/services/wiki_pages/destroy_service.rb b/app/services/wiki_pages/destroy_service.rb new file mode 100644 index 00000000000..6b93fb2f6d7 --- /dev/null +++ b/app/services/wiki_pages/destroy_service.rb @@ -0,0 +1,11 @@ +module WikiPages + class DestroyService < WikiPages::BaseService + def execute(page) + if page&.delete + execute_hooks(page, 'delete') + end + + page + end + end +end diff --git a/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml b/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml new file mode 100644 index 00000000000..d74057dca8a --- /dev/null +++ b/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml @@ -0,0 +1,4 @@ +--- +title: Execute web hooks for WikiPage delete operation +merge_request: 8198 +author: diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md index 9df0c765f84..55d480bfb72 100644 --- a/doc/user/project/integrations/webhooks.md +++ b/doc/user/project/integrations/webhooks.md @@ -714,7 +714,7 @@ X-Gitlab-Event: Merge Request Hook ### Wiki Page events -Triggered when a wiki page is created or edited. +Triggered when a wiki page is created, edited or deleted. **Request Header**: diff --git a/spec/services/wiki_pages/destroy_service_spec.rb b/spec/services/wiki_pages/destroy_service_spec.rb new file mode 100644 index 00000000000..a4b9a390fe2 --- /dev/null +++ b/spec/services/wiki_pages/destroy_service_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe WikiPages::DestroyService, services: true do + let(:project) { create(:empty_project) } + let(:user) { create(:user) } + let(:wiki_page) { create(:wiki_page) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + before do + allow(service).to receive(:execute_hooks) + project.add_master(user) + end + + it 'executes webhooks' do + service.execute(wiki_page) + + expect(service).to have_received(:execute_hooks).once.with(wiki_page, 'delete') + end + end +end -- cgit v1.2.1 From 215fadbcc86c3aac68569fee47e8d205dd250d7a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 14 Feb 2017 00:14:28 -0600 Subject: fix eslint builds within .gitlab-ci.yml --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b2622ac7e0f..7d0c976d816 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -327,7 +327,7 @@ lint:javascript: paths: - node_modules/ stage: test - image: "node:7.1" + before_script: [] script: - yarn run eslint @@ -337,7 +337,7 @@ lint:javascript:report: paths: - node_modules/ stage: post-test - image: "node:7.1" + before_script: [] script: - find app/ spec/ -name '*.js' -or -name '*.js.es6' -exec sed --in-place 's|/\* eslint-disable .*\*/||' {} \; # run report over all files - yarn run eslint-report || true # ignore exit code -- cgit v1.2.1 From e8b5b082da28bff1c9728c913484bdf12f936b95 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 14 Feb 2017 00:55:11 -0600 Subject: add CHANGELOG.md entry for !9055 --- changelogs/unreleased/replace-npm-with-yarn.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/replace-npm-with-yarn.yml diff --git a/changelogs/unreleased/replace-npm-with-yarn.yml b/changelogs/unreleased/replace-npm-with-yarn.yml new file mode 100644 index 00000000000..5e795eb0c8d --- /dev/null +++ b/changelogs/unreleased/replace-npm-with-yarn.yml @@ -0,0 +1,4 @@ +--- +title: replace npm with yarn and add yarn.lock +merge_request: 9055 +author: -- cgit v1.2.1 From 34b8cdf87ce8e36e5f442bf7068869833f35243b Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 14 Feb 2017 01:35:27 -0600 Subject: enable eslint to resolve webpack modules according to webpack.config.js --- .eslintrc | 14 ++++++++++---- package.json | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.eslintrc b/.eslintrc index 1a2cd821af7..0fcd866778f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,12 +12,18 @@ "localStorage": false }, "plugins": [ - "filenames" + "filenames", + "import" ], + "settings": { + "import/resolver": { + "webpack": { + "config": "./config/webpack.config.js" + } + } + }, "rules": { "filenames/match-regex": [2, "^[a-z0-9_]+(.js)?$"], - "no-multiple-empty-lines": ["error", { "max": 1 }], - "import/no-extraneous-dependencies": "off", - "import/no-unresolved": "off" + "no-multiple-empty-lines": ["error", { "max": 1 }] } } diff --git a/package.json b/package.json index 24e11a4607f..3a7cdb14ad1 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "devDependencies": { "eslint": "^3.10.1", "eslint-config-airbnb-base": "^10.0.1", + "eslint-import-resolver-webpack": "^0.8.1", "eslint-plugin-filenames": "^1.1.0", "eslint-plugin-import": "^2.2.0", "eslint-plugin-jasmine": "^2.1.0", -- cgit v1.2.1 From b06ac70ab5fec093bf189e8d0efb6ddb22796051 Mon Sep 17 00:00:00 2001 From: winniehell Date: Mon, 13 Feb 2017 23:35:08 +0100 Subject: Add dynamic fixture for todos --- spec/javascripts/fixtures/todos.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 spec/javascripts/fixtures/todos.rb diff --git a/spec/javascripts/fixtures/todos.rb b/spec/javascripts/fixtures/todos.rb new file mode 100644 index 00000000000..30ccf49e7e8 --- /dev/null +++ b/spec/javascripts/fixtures/todos.rb @@ -0,0 +1,30 @@ +require 'spec_helper' + +describe Dashboard::TodosController, '(JavaScript fixtures)', type: :controller do + include JavaScriptFixturesHelpers + + let(:admin) { create(:admin) } + let(:namespace) { create(:namespace, name: 'frontend-fixtures' )} + let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') } + let(:issue_1) { create(:issue, title: 'issue_1', project: project) } + let!(:todo_1) { create(:todo, user: admin, project: project, target: issue_1, created_at: 5.hours.ago) } + let(:issue_2) { create(:issue, title: 'issue_2', project: project) } + let!(:todo_2) { create(:todo, :done, user: admin, project: project, target: issue_2, created_at: 50.hours.ago) } + + render_views + + before(:all) do + clean_frontend_fixtures('todos/') + end + + before(:each) do + sign_in(admin) + end + + it 'todos/todos.html.raw' do |example| + get :index + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end +end -- cgit v1.2.1 From d8517fd457995927f8a2196e9dba1b6d68d027ad Mon Sep 17 00:00:00 2001 From: winniehell Date: Tue, 14 Feb 2017 01:09:57 +0100 Subject: Replace static fixture for right_sidebar_spec.js (!9211) --- changelogs/unreleased/dynamic-todos-fixture.yml | 4 +++ spec/javascripts/fixtures/.gitignore | 1 + spec/javascripts/fixtures/todos.json | 4 --- spec/javascripts/fixtures/todos.rb | 40 +++++++++++++++++++------ spec/javascripts/right_sidebar_spec.js | 4 +-- 5 files changed, 38 insertions(+), 15 deletions(-) create mode 100644 changelogs/unreleased/dynamic-todos-fixture.yml delete mode 100644 spec/javascripts/fixtures/todos.json diff --git a/changelogs/unreleased/dynamic-todos-fixture.yml b/changelogs/unreleased/dynamic-todos-fixture.yml new file mode 100644 index 00000000000..580bc729e3c --- /dev/null +++ b/changelogs/unreleased/dynamic-todos-fixture.yml @@ -0,0 +1,4 @@ +--- +title: Replace static fixture for right_sidebar_spec.js +merge_request: 9211 +author: winniehell diff --git a/spec/javascripts/fixtures/.gitignore b/spec/javascripts/fixtures/.gitignore index 009b68d5d1c..0c35cdd778e 100644 --- a/spec/javascripts/fixtures/.gitignore +++ b/spec/javascripts/fixtures/.gitignore @@ -1 +1,2 @@ *.html.raw +*.json diff --git a/spec/javascripts/fixtures/todos.json b/spec/javascripts/fixtures/todos.json deleted file mode 100644 index 62c2387d515..00000000000 --- a/spec/javascripts/fixtures/todos.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "count": 1, - "delete_path": "/dashboard/todos/1" -} \ No newline at end of file diff --git a/spec/javascripts/fixtures/todos.rb b/spec/javascripts/fixtures/todos.rb index 30ccf49e7e8..2c08b06ea9e 100644 --- a/spec/javascripts/fixtures/todos.rb +++ b/spec/javascripts/fixtures/todos.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Dashboard::TodosController, '(JavaScript fixtures)', type: :controller do +describe 'Todos (JavaScript fixtures)' do include JavaScriptFixturesHelpers let(:admin) { create(:admin) } @@ -11,20 +11,42 @@ describe Dashboard::TodosController, '(JavaScript fixtures)', type: :controller let(:issue_2) { create(:issue, title: 'issue_2', project: project) } let!(:todo_2) { create(:todo, :done, user: admin, project: project, target: issue_2, created_at: 50.hours.ago) } - render_views - before(:all) do clean_frontend_fixtures('todos/') end - before(:each) do - sign_in(admin) + describe Dashboard::TodosController, '(JavaScript fixtures)', type: :controller do + render_views + + before(:each) do + sign_in(admin) + end + + it 'todos/todos.html.raw' do |example| + get :index + + expect(response).to be_success + store_frontend_fixture(response, example.description) + end end - it 'todos/todos.html.raw' do |example| - get :index + describe Projects::TodosController, '(JavaScript fixtures)', type: :controller do + render_views + + before(:each) do + sign_in(admin) + end + + it 'todos/todos.json' do |example| + post :create, + namespace_id: namespace.path, + project_id: project.path, + issuable_type: 'issue', + issuable_id: issue_2.id, + format: 'json' - expect(response).to be_success - store_frontend_fixture(response, example.description) + expect(response).to be_success + store_frontend_fixture(response, example.description) + end end end diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js index f7636865aa1..9284af8a8d9 100644 --- a/spec/javascripts/right_sidebar_spec.js +++ b/spec/javascripts/right_sidebar_spec.js @@ -34,7 +34,7 @@ require('~/extensions/jquery.js'); describe('RightSidebar', function() { var fixtureName = 'issues/open-issue.html.raw'; preloadFixtures(fixtureName); - loadJSONFixtures('todos.json'); + loadJSONFixtures('todos/todos.json'); beforeEach(function() { loadFixtures(fixtureName); @@ -64,7 +64,7 @@ require('~/extensions/jquery.js'); }); it('should broadcast todo:toggle event when add todo clicked', function() { - var todos = getJSONFixture('todos.json'); + var todos = getJSONFixture('todos/todos.json'); spyOn(jQuery, 'ajax').and.callFake(function() { var d = $.Deferred(); var response = todos; -- cgit v1.2.1 From b493360f7f8b72e91287ec5827e644074976e506 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 13 Feb 2017 07:39:57 -0500 Subject: Set max width for text in mini pipeline graph --- app/assets/stylesheets/pages/pipelines.scss | 2 +- changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 0c7019dc64f..00eb5b30fd5 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -864,7 +864,7 @@ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - width: 90px; + max-width: 70%; color: $gl-text-color-secondary; margin-left: 2px; display: inline-block; diff --git a/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml b/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml new file mode 100644 index 00000000000..53077eedc11 --- /dev/null +++ b/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml @@ -0,0 +1,4 @@ +--- +title: Set maximum width for mini pipeline graph text so it is not truncated to early +merge_request: 9188 +author: -- cgit v1.2.1 From a7702271391524262788accfc78e6ef58b63f88e Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 6 Feb 2017 12:22:55 +0800 Subject: Remove inactive default email services Note that we no longer generate this by default. This is for clearing legacy default data. --- .../remove-inactive-default-email-services.yml | 4 ++++ ...40400_remove_inactive_default_email_services.rb | 27 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 changelogs/unreleased/remove-inactive-default-email-services.yml create mode 100644 db/post_migrate/20170206040400_remove_inactive_default_email_services.rb diff --git a/changelogs/unreleased/remove-inactive-default-email-services.yml b/changelogs/unreleased/remove-inactive-default-email-services.yml new file mode 100644 index 00000000000..c32c1390e4e --- /dev/null +++ b/changelogs/unreleased/remove-inactive-default-email-services.yml @@ -0,0 +1,4 @@ +--- +title: Remove inactive default email services +merge_request: 8987 +author: diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb new file mode 100644 index 00000000000..18affebde73 --- /dev/null +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -0,0 +1,27 @@ +class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + execute <<-SQL.strip_heredoc + DELETE FROM services + WHERE type = 'BuildsEmailService' + AND active = #{false_value} + AND properties = '{"notify_only_broken_builds":true}'; + + DELETE FROM services + WHERE type = 'PipelinesEmailService' + AND active = #{false_value} + AND properties = '{"notify_only_broken_pipelines":true}'; + SQL + end + + def false_value + quote(false) + end + + def quote(value) + ActiveRecord::Base.connection.quote(value) + end +end -- cgit v1.2.1 From 25cd5aa228ebe10ce9eabb17c75eb86e9d8c152c Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 6 Feb 2017 12:39:36 +0800 Subject: Run two threads to improve migration running time --- ...6040400_remove_inactive_default_email_services.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index 18affebde73..dc7750f3244 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -4,24 +4,38 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration DOWNTIME = false def up - execute <<-SQL.strip_heredoc + builds_service = spawn <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'BuildsEmailService' AND active = #{false_value} AND properties = '{"notify_only_broken_builds":true}'; + SQL + pipelines_service = spawn <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'PipelinesEmailService' AND active = #{false_value} AND properties = '{"notify_only_broken_pipelines":true}'; SQL + + [builds_service, pipelines_service].each(&:join) end - def false_value - quote(false) + private + + def spawn(query) + Thread.new do + ActiveRecord::Base.connection_pool.with_connection do + ActiveRecord::Base.connection.execute(query) + end + end end def quote(value) ActiveRecord::Base.connection.quote(value) end + + def false_value + quote(false) + end end -- cgit v1.2.1 From 887aeefba63429b6603c75e385148a2c6be34be1 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Mon, 6 Feb 2017 21:51:19 +0800 Subject: Use IS FALSE for both pg and mysql; Handle connections by ourselves so that even if the setting has 1 connection we could still use more connections. --- ...6040400_remove_inactive_default_email_services.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index dc7750f3244..b107d9204d2 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -7,14 +7,14 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration builds_service = spawn <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'BuildsEmailService' - AND active = #{false_value} + AND active IS FALSE AND properties = '{"notify_only_broken_builds":true}'; SQL pipelines_service = spawn <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'PipelinesEmailService' - AND active = #{false_value} + AND active IS FALSE AND properties = '{"notify_only_broken_pipelines":true}'; SQL @@ -25,17 +25,19 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration def spawn(query) Thread.new do - ActiveRecord::Base.connection_pool.with_connection do - ActiveRecord::Base.connection.execute(query) + with_connection do |connection| + connection.execute(query) end end end - def quote(value) - ActiveRecord::Base.connection.quote(value) - end + def with_connection + pool = ActiveRecord::Base.establish_connection + connection = pool.connection + + yield(connection) - def false_value - quote(false) + ensure + connection.close end end -- cgit v1.2.1 From 521a7cafd4226a03d7f0b5d35a2370559fb345cb Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 7 Feb 2017 00:01:56 +0800 Subject: Try this way to provide database connection --- .../20170206040400_remove_inactive_default_email_services.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index b107d9204d2..d69f68a13d2 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -31,13 +31,10 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration end end - def with_connection + def with_connection(&block) pool = ActiveRecord::Base.establish_connection - connection = pool.connection - - yield(connection) - + pool.with_connection(&block) ensure - connection.close + pool.disconnect! end end -- cgit v1.2.1 From 0c2f4a3c422c522bd32bdcf36425e836ebee8ea6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 7 Feb 2017 19:29:17 +0800 Subject: Bump schema to pass `rake db:setup` --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index d421d5c6774..57e3f2f6c54 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170210075922) do +ActiveRecord::Schema.define(version: 20170206040400) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 7ecee7a4d79718bc46086ee8dec23a00cea39b39 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 7 Feb 2017 22:09:13 +0800 Subject: Introduce ThreadedConnectionPool for customized pool This way we could reuse this pool for other migrations Feedback: * https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8987#note_22923350 * https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8987#note_22923365 --- ...40400_remove_inactive_default_email_services.rb | 45 ++++++++------------ lib/gitlab/database/threaded_connection_pool.rb | 48 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 28 deletions(-) create mode 100644 lib/gitlab/database/threaded_connection_pool.rb diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index d69f68a13d2..52e7f91bb01 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -3,38 +3,27 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration DOWNTIME = false - def up - builds_service = spawn <<-SQL.strip_heredoc - DELETE FROM services - WHERE type = 'BuildsEmailService' - AND active IS FALSE - AND properties = '{"notify_only_broken_builds":true}'; - SQL - - pipelines_service = spawn <<-SQL.strip_heredoc - DELETE FROM services - WHERE type = 'PipelinesEmailService' - AND active IS FALSE - AND properties = '{"notify_only_broken_pipelines":true}'; - SQL + disable_ddl_transaction! - [builds_service, pipelines_service].each(&:join) - end - - private + def up + Gitlab::Database::ThreadedConnectionPool.with_pool(2) do |pool| + pool.execute_async <<-SQL.strip_heredoc + DELETE FROM services + WHERE type = 'BuildsEmailService' + AND active IS FALSE + AND properties = '{"notify_only_broken_builds":true}'; + SQL - def spawn(query) - Thread.new do - with_connection do |connection| - connection.execute(query) - end + pool.execute_async <<-SQL.strip_heredoc + DELETE FROM services + WHERE type = 'PipelinesEmailService' + AND active IS FALSE + AND properties = '{"notify_only_broken_pipelines":true}'; + SQL end end - def with_connection(&block) - pool = ActiveRecord::Base.establish_connection - pool.with_connection(&block) - ensure - pool.disconnect! + def down + # Nothing can be done to restore the records end end diff --git a/lib/gitlab/database/threaded_connection_pool.rb b/lib/gitlab/database/threaded_connection_pool.rb new file mode 100644 index 00000000000..1316b005741 --- /dev/null +++ b/lib/gitlab/database/threaded_connection_pool.rb @@ -0,0 +1,48 @@ +module Gitlab + module Database + class ThreadedConnectionPool + def self.with_pool(pool_size) + pool = new(pool_size) + + yield(pool) + + ensure + pool.join + pool.close + end + + def initialize(pool_size) + config = ActiveRecord::Base.configurations[Rails.env] + @ar_pool = ActiveRecord::Base.establish_connection( + config.merge(pool: pool_size)) + @workers = [] + @mutex = Mutex.new + end + + def execute_async(sql) + @mutex.synchronize do + @workers << Thread.new do + @ar_pool.with_connection do |connection| + connection.execute(sql) + end + end + end + end + + def join + threads = nil + + @mutex.synchronize do + threads = @workers.dup + @workers.clear + end + + threads.each(&:join) + end + + def close + @ar_pool.disconnect! + end + end + end +end -- cgit v1.2.1 From 1d8db6652f635d3f5326071b9dd00621b87e228b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 9 Feb 2017 21:11:15 +0800 Subject: Revert "Bump schema to pass `rake db:setup`" This reverts commit f2ed7cbc9b36b6ad9bcc714b271e98ead756b816. --- db/schema.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/schema.rb b/db/schema.rb index 57e3f2f6c54..de07b3837ad 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170206040400) do +ActiveRecord::Schema.define(version: 20170204181513) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 8aa1055fe3d24aa606f6a8d3c635f97b788e4e85 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Thu, 9 Feb 2017 21:11:31 +0800 Subject: Use threads directly, introduce pool later: Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8987#note_22938402 --- ...40400_remove_inactive_default_email_services.rb | 38 ++++++++++++++--- lib/gitlab/database/threaded_connection_pool.rb | 48 ---------------------- 2 files changed, 33 insertions(+), 53 deletions(-) delete mode 100644 lib/gitlab/database/threaded_connection_pool.rb diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index 52e7f91bb01..f3863881229 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -6,24 +6,52 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration disable_ddl_transaction! def up - Gitlab::Database::ThreadedConnectionPool.with_pool(2) do |pool| - pool.execute_async <<-SQL.strip_heredoc + pool = create_connection_pool + threads = [] + + threads << Thread.new do + pool.with_connection do |connection| + connection.execute <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'BuildsEmailService' AND active IS FALSE AND properties = '{"notify_only_broken_builds":true}'; - SQL + SQL + end + end - pool.execute_async <<-SQL.strip_heredoc + threads << Thread.new do + pool.with_connection do |connection| + connection.execute <<-SQL.strip_heredoc DELETE FROM services WHERE type = 'PipelinesEmailService' AND active IS FALSE AND properties = '{"notify_only_broken_pipelines":true}'; - SQL + SQL + end end + + threads.each(&:join) end def down # Nothing can be done to restore the records end + + private + + def create_connection_pool + # See activerecord-4.2.7.1/lib/active_record/connection_adapters/connection_specification.rb + env = Rails.env + original_config = ActiveRecord::Base.configurations + env_config = original_config[env].merge('pool' => 2) + config = original_config.merge(env => env_config) + + spec = + ActiveRecord:: + ConnectionAdapters:: + ConnectionSpecification::Resolver.new(config).spec(env.to_sym) + + ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec) + end end diff --git a/lib/gitlab/database/threaded_connection_pool.rb b/lib/gitlab/database/threaded_connection_pool.rb deleted file mode 100644 index 1316b005741..00000000000 --- a/lib/gitlab/database/threaded_connection_pool.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Gitlab - module Database - class ThreadedConnectionPool - def self.with_pool(pool_size) - pool = new(pool_size) - - yield(pool) - - ensure - pool.join - pool.close - end - - def initialize(pool_size) - config = ActiveRecord::Base.configurations[Rails.env] - @ar_pool = ActiveRecord::Base.establish_connection( - config.merge(pool: pool_size)) - @workers = [] - @mutex = Mutex.new - end - - def execute_async(sql) - @mutex.synchronize do - @workers << Thread.new do - @ar_pool.with_connection do |connection| - connection.execute(sql) - end - end - end - end - - def join - threads = nil - - @mutex.synchronize do - threads = @workers.dup - @workers.clear - end - - threads.each(&:join) - end - - def close - @ar_pool.disconnect! - end - end - end -end -- cgit v1.2.1 From ae93d08b9cf01b8a12a5f6cacbd9c89e87e54ac4 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Sat, 11 Feb 2017 01:10:15 +0800 Subject: Disconnect the pool after done --- db/post_migrate/20170206040400_remove_inactive_default_email_services.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index f3863881229..249a9e805f1 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -32,6 +32,7 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration end threads.each(&:join) + pool.disconnect! end def down -- cgit v1.2.1 From ca659822257f21f09d5b22660ba7ae39395d79e6 Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Tue, 14 Feb 2017 20:17:17 +0800 Subject: Use Gitlab::Database.with_connection_pool from !9192 --- ...40400_remove_inactive_default_email_services.rb | 63 ++++++++-------------- db/schema.rb | 2 +- 2 files changed, 24 insertions(+), 41 deletions(-) diff --git a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb index 249a9e805f1..a8e63e8bc7d 100644 --- a/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb +++ b/db/post_migrate/20170206040400_remove_inactive_default_email_services.rb @@ -6,53 +6,36 @@ class RemoveInactiveDefaultEmailServices < ActiveRecord::Migration disable_ddl_transaction! def up - pool = create_connection_pool - threads = [] - - threads << Thread.new do - pool.with_connection do |connection| - connection.execute <<-SQL.strip_heredoc - DELETE FROM services - WHERE type = 'BuildsEmailService' - AND active IS FALSE - AND properties = '{"notify_only_broken_builds":true}'; - SQL + Gitlab::Database.with_connection_pool(2) do |pool| + threads = [] + + threads << Thread.new do + pool.with_connection do |connection| + connection.execute <<-SQL.strip_heredoc + DELETE FROM services + WHERE type = 'BuildsEmailService' + AND active IS FALSE + AND properties = '{"notify_only_broken_builds":true}'; + SQL + end end - end - threads << Thread.new do - pool.with_connection do |connection| - connection.execute <<-SQL.strip_heredoc - DELETE FROM services - WHERE type = 'PipelinesEmailService' - AND active IS FALSE - AND properties = '{"notify_only_broken_pipelines":true}'; - SQL + threads << Thread.new do + pool.with_connection do |connection| + connection.execute <<-SQL.strip_heredoc + DELETE FROM services + WHERE type = 'PipelinesEmailService' + AND active IS FALSE + AND properties = '{"notify_only_broken_pipelines":true}'; + SQL + end end - end - threads.each(&:join) - pool.disconnect! + threads.each(&:join) + end end def down # Nothing can be done to restore the records end - - private - - def create_connection_pool - # See activerecord-4.2.7.1/lib/active_record/connection_adapters/connection_specification.rb - env = Rails.env - original_config = ActiveRecord::Base.configurations - env_config = original_config[env].merge('pool' => 2) - config = original_config.merge(env => env_config) - - spec = - ActiveRecord:: - ConnectionAdapters:: - ConnectionSpecification::Resolver.new(config).spec(env.to_sym) - - ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec) - end end diff --git a/db/schema.rb b/db/schema.rb index de07b3837ad..d421d5c6774 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170204181513) do +ActiveRecord::Schema.define(version: 20170210075922) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From c48539463ff2250058fbfa9d1811977f01313b7c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 14 Feb 2017 15:34:36 +0200 Subject: Expose Namespace#full_path in namespaces API Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/namespace_select.js | 4 ++-- app/views/admin/projects/index.html.haml | 2 +- doc/api/namespaces.md | 9 ++++++++- lib/api/entities.rb | 2 +- spec/requests/api/namespaces_spec.rb | 6 ++++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js index 514556ade0b..2ae5617206e 100644 --- a/app/assets/javascripts/namespace_select.js +++ b/app/assets/javascripts/namespace_select.js @@ -29,7 +29,7 @@ if (selected.id == null) { return selected.text; } else { - return selected.kind + ": " + selected.path; + return selected.kind + ": " + selected.full_path; } }, data: function(term, dataCallback) { @@ -50,7 +50,7 @@ if (namespace.id == null) { return namespace.text; } else { - return namespace.kind + ": " + namespace.path; + return namespace.kind + ": " + namespace.full_path; } }, renderRow: this.renderRow, diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index cf8d438670b..cdef63daca9 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -30,7 +30,7 @@ - toggle_text = 'Namespace' - if params[:namespace_id].present? - namespace = Namespace.find(params[:namespace_id]) - - toggle_text = "#{namespace.kind}: #{namespace.path}" + - toggle_text = "#{namespace.kind}: #{namespace.full_path}" = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' }) .dropdown-menu.dropdown-select.dropdown-menu-align-right = dropdown_title('Namespaces') diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md index 88cd407d792..1d97b5de688 100644 --- a/doc/api/namespaces.md +++ b/doc/api/namespaces.md @@ -35,6 +35,12 @@ Example response: "id": 2, "path": "group1", "kind": "group" + }, + { + "id": 3, + "path": "bar", + "kind": "group", + "full_path": "foo/bar", } ] ``` @@ -64,7 +70,8 @@ Example response: { "id": 4, "path": "twitter", - "kind": "group" + "kind": "group", + "full_path": "twitter", } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 2a071e649fa..2fd88380d6b 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -414,7 +414,7 @@ module API end class Namespace < Grape::Entity - expose :id, :name, :path, :kind + expose :id, :name, :path, :kind, :full_path end class MemberAccess < Grape::Entity diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index c1edf384d5c..a945d56f529 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -5,7 +5,7 @@ describe API::Namespaces, api: true do let(:admin) { create(:admin) } let(:user) { create(:user) } let!(:group1) { create(:group) } - let!(:group2) { create(:group) } + let!(:group2) { create(:group, :nested) } describe "GET /namespaces" do context "when unauthenticated" do @@ -25,11 +25,13 @@ describe API::Namespaces, api: true do end it "admin: returns an array of matched namespaces" do - get api("/namespaces?search=#{group1.name}", admin) + get api("/namespaces?search=#{group2.name}", admin) expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) + expect(json_response.last['path']).to eq(group2.path) + expect(json_response.last['full_path']).to eq(group2.full_path) end end -- cgit v1.2.1 From 6b64a2ca3d1cad7e2cffde4ab24970c709f6da3a Mon Sep 17 00:00:00 2001 From: Dongqing Hu Date: Tue, 14 Feb 2017 21:43:12 +0800 Subject: Don't ignore the parameter of MergeRequestsHelper#mr_change_branches_path --- app/helpers/merge_requests_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index b5f8c23a667..7d8505d704e 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -75,10 +75,10 @@ module MergeRequestsHelper new_namespace_project_merge_request_path( @project.namespace, @project, merge_request: { - source_project_id: @merge_request.source_project_id, - target_project_id: @merge_request.target_project_id, - source_branch: @merge_request.source_branch, - target_branch: @merge_request.target_branch, + source_project_id: merge_request.source_project_id, + target_project_id: merge_request.target_project_id, + source_branch: merge_request.source_branch, + target_branch: merge_request.target_branch, }, change_branches: true ) -- cgit v1.2.1 From 7460159ec91fc7b1ec840dc0a10aa21b3dbbf070 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 14 Feb 2017 14:56:28 +0100 Subject: Reuse pipeline methods to preserve Law of Demeter --- app/views/projects/pipelines/_info.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml index a6cd2d83bd5..e0c972aa2fb 100644 --- a/app/views/projects/pipelines/_info.html.haml +++ b/app/views/projects/pipelines/_info.html.haml @@ -7,9 +7,9 @@ = commit_author_link(@commit) .header-action-buttons - if can?(current_user, :update_pipeline, @pipeline.project) - - if @pipeline.builds.latest.failed.any?(&:retryable?) + - if @pipeline.retryable? = link_to "Retry failed", retry_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), class: 'btn btn-inverted-secondary', method: :post - - if @pipeline.builds.running_or_pending.any? + - if @pipeline.cancelable? = link_to "Cancel running", cancel_namespace_project_pipeline_path(@pipeline.project.namespace, @pipeline.project, @pipeline.id), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post - if @commit -- cgit v1.2.1 From 84b42aebbcfb71593a12c678723ac8260af2b52e Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 14 Feb 2017 15:03:20 +0100 Subject: Fix project link on issue creation --- lib/gitlab/chat_commands/presenters/issue_new.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/chat_commands/presenters/issue_new.rb b/lib/gitlab/chat_commands/presenters/issue_new.rb index 0d31660039a..3674ba25641 100644 --- a/lib/gitlab/chat_commands/presenters/issue_new.rb +++ b/lib/gitlab/chat_commands/presenters/issue_new.rb @@ -10,7 +10,7 @@ module Gitlab private - def new_issue + def new_issue { attachments: [ { @@ -38,7 +38,7 @@ module Gitlab end def project_link - "[#{project.name_with_namespace}](#{projects_url(project)})" + "[#{project.name_with_namespace}](#{project.web_url})" end def author_profile_link -- cgit v1.2.1 From 0e21f5426782cd780c703346d27f0b1edd1dccd8 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 14 Feb 2017 15:03:54 +0100 Subject: Add Changelog entry for pipeline buttons fix --- .../unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml diff --git a/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml b/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml new file mode 100644 index 00000000000..d747e0e63a3 --- /dev/null +++ b/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml @@ -0,0 +1,4 @@ +--- +title: Fix pipeline retry and cancel buttons on pipeline details page +merge_request: 9225 +author: -- cgit v1.2.1 From 178b6014f856dbca1653961a1f8341d7d1d38d2f Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 14 Feb 2017 15:15:41 +0100 Subject: Remove deprecated CI service --- app/models/project_services/gitlab_ci_service.rb | 8 -------- app/models/service.rb | 2 +- changelogs/unreleased/zj-remove-deprecated-ci-service.yml | 4 ++++ .../20170214111112_delete_deprecated_gitlab_ci_service.rb | 15 +++++++++++++++ db/schema.rb | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) delete mode 100644 app/models/project_services/gitlab_ci_service.rb create mode 100644 changelogs/unreleased/zj-remove-deprecated-ci-service.yml create mode 100644 db/post_migrate/20170214111112_delete_deprecated_gitlab_ci_service.rb diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb deleted file mode 100644 index bbc312f5215..00000000000 --- a/app/models/project_services/gitlab_ci_service.rb +++ /dev/null @@ -1,8 +0,0 @@ -# TODO(ayufan): The GitLabCiService is deprecated and the type should be removed when the database entries are removed -class GitlabCiService < CiService - # We override the active accessor to always make GitLabCiService disabled - # Otherwise the GitLabCiService can be picked, but should never be since it's deprecated - def active - false - end -end diff --git a/app/models/service.rb b/app/models/service.rb index 043be222f3a..facaaf9b331 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -27,7 +27,7 @@ class Service < ActiveRecord::Base validates :project_id, presence: true, unless: Proc.new { |service| service.template? } - scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } + scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') } scope :issue_trackers, -> { where(category: 'issue_tracker') } scope :external_wikis, -> { where(type: 'ExternalWikiService').active } scope :active, -> { where(active: true) } diff --git a/changelogs/unreleased/zj-remove-deprecated-ci-service.yml b/changelogs/unreleased/zj-remove-deprecated-ci-service.yml new file mode 100644 index 00000000000..044f4ae627d --- /dev/null +++ b/changelogs/unreleased/zj-remove-deprecated-ci-service.yml @@ -0,0 +1,4 @@ +--- +title: Remove deprecated GitlabCiService +merge_request: +author: diff --git a/db/post_migrate/20170214111112_delete_deprecated_gitlab_ci_service.rb b/db/post_migrate/20170214111112_delete_deprecated_gitlab_ci_service.rb new file mode 100644 index 00000000000..09a827d22b0 --- /dev/null +++ b/db/post_migrate/20170214111112_delete_deprecated_gitlab_ci_service.rb @@ -0,0 +1,15 @@ +class DeleteDeprecatedGitlabCiService < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + disable_statement_timeout + + execute("DELETE FROM services WHERE type = 'GitlabCiService';") + end + + def down + # noop + end +end diff --git a/db/schema.rb b/db/schema.rb index d421d5c6774..4db9849e9c4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170210075922) do +ActiveRecord::Schema.define(version: 20170214111112) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From 8f7550859179acd397ed61c7b298bad42b6a8551 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 10 Feb 2017 17:44:35 -0200 Subject: Fix timezone on issue boards due date --- app/assets/javascripts/boards/filters/due_date_filters.js.es6 | 2 +- changelogs/unreleased/issue_23317.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/issue_23317.yml diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 index ac2966cef5d..03425bb145b 100644 --- a/app/assets/javascripts/boards/filters/due_date_filters.js.es6 +++ b/app/assets/javascripts/boards/filters/due_date_filters.js.es6 @@ -3,5 +3,5 @@ Vue.filter('due-date', (value) => { const date = new Date(value); - return dateFormat(date, 'mmm d, yyyy'); + return dateFormat(date, 'mmm d, yyyy', true); }); diff --git a/changelogs/unreleased/issue_23317.yml b/changelogs/unreleased/issue_23317.yml new file mode 100644 index 00000000000..788ae159f5e --- /dev/null +++ b/changelogs/unreleased/issue_23317.yml @@ -0,0 +1,4 @@ +--- +title: Fix timezone on issue boards due date +merge_request: +author: -- cgit v1.2.1 From b65cd9df58e5eeb60f8b946651ac47e571f6804e Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Tue, 14 Feb 2017 16:07:46 +0100 Subject: Add MySQL info in install requirements [ci skip] --- doc/install/database_mysql.md | 79 ++++++++++++++++++++++--------------------- doc/install/requirements.md | 15 +++++--- 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 65bfb0f7d6e..da2dac23c6a 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -1,64 +1,67 @@ # Database MySQL -## Note - -We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). +>**Note:** +We do not recommend using MySQL due to various issues. For example, case +[(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) +and [problems](https://bugs.mysql.com/bug.php?id=65830) that +[suggested](https://bugs.mysql.com/bug.php?id=50909) +[fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164). ## Initial database setup - # Install the database packages - sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev - - # Ensure you have MySQL version 5.5.14 or later - mysql --version +``` +# Install the database packages +sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev - # Pick a MySQL root password (can be anything), type it and press enter - # Retype the MySQL root password and press enter +# Ensure you have MySQL version 5.5.14 or later +mysql --version - # Secure your installation - sudo mysql_secure_installation +# Pick a MySQL root password (can be anything), type it and press enter +# Retype the MySQL root password and press enter - # Login to MySQL - mysql -u root -p +# Secure your installation +sudo mysql_secure_installation - # Type the MySQL root password +# Login to MySQL +mysql -u root -p - # Create a user for GitLab - # do not type the 'mysql>', this is part of the prompt - # change $password in the command below to a real password you pick - mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; +# Type the MySQL root password - # Ensure you can use the InnoDB engine which is necessary to support long indexes - # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" - mysql> SET storage_engine=INNODB; +# Create a user for GitLab +# do not type the 'mysql>', this is part of the prompt +# change $password in the command below to a real password you pick +mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; - # If you have MySQL < 5.7.7 and want to enable utf8mb4 character set support with your GitLab install, you must set the following NOW: - mysql> SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda, innodb_large_prefix=1; +# Ensure you can use the InnoDB engine which is necessary to support long indexes +# If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off" +mysql> SET storage_engine=INNODB; - # Create the GitLab production database - mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_general_ci`; +# If you have MySQL < 5.7.7 and want to enable utf8mb4 character set support with your GitLab install, you must set the following NOW: +mysql> SET GLOBAL innodb_file_per_table=1, innodb_file_format=Barracuda, innodb_large_prefix=1; - # Grant the GitLab user necessary permissions on the database - mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES, REFERENCES ON `gitlabhq_production`.* TO 'git'@'localhost'; +# Create the GitLab production database +mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_general_ci`; - # Quit the database session - mysql> \q +# Grant the GitLab user necessary permissions on the database +mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, CREATE TEMPORARY TABLES, DROP, INDEX, ALTER, LOCK TABLES, REFERENCES ON `gitlabhq_production`.* TO 'git'@'localhost'; - # Try connecting to the new database with the new user - sudo -u git -H mysql -u git -p -D gitlabhq_production +# Quit the database session +mysql> \q - # Type the password you replaced $password with earlier +# Try connecting to the new database with the new user +sudo -u git -H mysql -u git -p -D gitlabhq_production - # You should now see a 'mysql>' prompt +# Type the password you replaced $password with earlier - # Quit the database session - mysql> \q +# You should now see a 'mysql>' prompt - # You are done installing the database for now and can go back to the rest of the installation. +# Quit the database session +mysql> \q +``` +You are done installing the database for now and can go back to the rest of the installation. Please proceed to the rest of the installation before running through the utf8mb4 support section. - ### MySQL utf8mb4 support After installation or upgrade, remember to [convert any new tables](#convert) to `utf8mb4`/`utf8mb4_general_ci`. diff --git a/doc/install/requirements.md b/doc/install/requirements.md index e942346e2d7..3f90597ec80 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -15,11 +15,11 @@ For the installations options please see [the installation page on the GitLab we ### Unsupported Unix distributions -- OS X - Arch Linux - Fedora -- Gentoo - FreeBSD +- Gentoo +- macOS On the above unsupported distributions is still possible to install GitLab yourself. Please see the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/installation/) for more information. @@ -120,7 +120,12 @@ To change the Unicorn workers when you have the Omnibus package please see [the ## Database -If you want to run the database separately expect a size of about 1 MB per user. +We currently support the following databases: + +- PostgreSQL (recommended) +- MySQL/MariaDB + +If you want to run the database separately, expect a size of about 1 MB per user. ### PostgreSQL Requirements @@ -128,7 +133,9 @@ Users using PostgreSQL must ensure the `pg_trgm` extension is loaded into every GitLab database. This extension can be enabled (using a PostgreSQL super user) by running the following query for every database: - CREATE EXTENSION pg_trgm; +``` +CREATE EXTENSION pg_trgm; +``` On some systems you may need to install an additional package (e.g. `postgresql-contrib`) for this extension to become available. -- cgit v1.2.1 From 4e9e29d295fe2f8cd258cde4b65e244eb74a1ae6 Mon Sep 17 00:00:00 2001 From: Toon Claes Date: Wed, 1 Feb 2017 11:23:57 +0100 Subject: API: Consolidate /projects endpoint It consolidates these endpoints: - /projects - /projects/owned - /projects/visible - /projects/starred - /projects/all Into the /projects endpoint using query parameters. --- .../unreleased/23061-consolidate-project-lists.yml | 4 + doc/api/groups.md | 2 + doc/api/projects.md | 186 +-------------- doc/api/v3_to_v4.md | 2 +- lib/api/groups.rb | 3 + lib/api/helpers.rb | 8 + lib/api/projects.rb | 59 +---- spec/requests/api/groups_spec.rb | 20 ++ spec/requests/api/projects_spec.rb | 258 ++++++++------------- 9 files changed, 146 insertions(+), 396 deletions(-) create mode 100644 changelogs/unreleased/23061-consolidate-project-lists.yml diff --git a/changelogs/unreleased/23061-consolidate-project-lists.yml b/changelogs/unreleased/23061-consolidate-project-lists.yml new file mode 100644 index 00000000000..dbb8fed55c0 --- /dev/null +++ b/changelogs/unreleased/23061-consolidate-project-lists.yml @@ -0,0 +1,4 @@ +--- +title: 'API: Consolidate /projects endpoint' +merge_request: 8962 +author: diff --git a/doc/api/groups.md b/doc/api/groups.md index a3a43ca7f1c..4a39dbc5555 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -73,6 +73,8 @@ Parameters: | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of authorized projects matching the search criteria | | `simple` | boolean | no | Return only the ID, URL, name, and path of each project | +| `owned` | boolean | no | Limit by projects owned by the current user | +| `starred` | boolean | no | Limit by projects starred by the current user | Example response: diff --git a/doc/api/projects.md b/doc/api/projects.md index e579b89d836..fa51158956a 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -36,6 +36,8 @@ Parameters: | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | | `search` | string | no | Return list of authorized projects matching the search criteria | | `simple` | boolean | no | Return only the ID, URL, name, and path of each project | +| `owned` | boolean | no | Limit by projects owned by the current user | +| `starred` | boolean | no | Limit by projects starred by the current user | ```json [ @@ -152,190 +154,6 @@ Parameters: ] ``` -Get a list of projects which the authenticated user can see. - -``` -GET /projects/visible -``` - -Parameters: - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `archived` | boolean | no | Limit by archived status | -| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | -| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | -| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Return list of authorized projects matching the search criteria | -| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | - -```json -[ - { - "id": 4, - "description": null, - "default_branch": "master", - "public": false, - "visibility_level": 0, - "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", - "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", - "web_url": "http://example.com/diaspora/diaspora-client", - "tag_list": [ - "example", - "disapora client" - ], - "owner": { - "id": 3, - "name": "Diaspora", - "created_at": "2013-09-30T13:46:02Z" - }, - "name": "Diaspora Client", - "name_with_namespace": "Diaspora / Diaspora Client", - "path": "diaspora-client", - "path_with_namespace": "diaspora/diaspora-client", - "issues_enabled": true, - "open_issues_count": 1, - "merge_requests_enabled": true, - "builds_enabled": true, - "wiki_enabled": true, - "snippets_enabled": false, - "container_registry_enabled": false, - "created_at": "2013-09-30T13:46:02Z", - "last_activity_at": "2013-09-30T13:46:02Z", - "creator_id": 3, - "namespace": { - "id": 3, - "name": "Diaspora", - "path": "diaspora", - "kind": "group" - }, - "archived": false, - "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", - "shared_runners_enabled": true, - "forks_count": 0, - "star_count": 0, - "runners_token": "b8547b1dc37721d05889db52fa2f02", - "public_builds": true, - "shared_with_groups": [] - }, - { - "id": 6, - "description": null, - "default_branch": "master", - "public": false, - "visibility_level": 0, - "ssh_url_to_repo": "git@example.com:brightbox/puppet.git", - "http_url_to_repo": "http://example.com/brightbox/puppet.git", - "web_url": "http://example.com/brightbox/puppet", - "tag_list": [ - "example", - "puppet" - ], - "owner": { - "id": 4, - "name": "Brightbox", - "created_at": "2013-09-30T13:46:02Z" - }, - "name": "Puppet", - "name_with_namespace": "Brightbox / Puppet", - "path": "puppet", - "path_with_namespace": "brightbox/puppet", - "issues_enabled": true, - "open_issues_count": 1, - "merge_requests_enabled": true, - "builds_enabled": true, - "wiki_enabled": true, - "snippets_enabled": false, - "container_registry_enabled": false, - "created_at": "2013-09-30T13:46:02Z", - "last_activity_at": "2013-09-30T13:46:02Z", - "creator_id": 3, - "namespace": { - "id": 4, - "name": "Brightbox", - "path": "brightbox", - "kind": "group" - }, - "permissions": { - "project_access": { - "access_level": 10, - "notification_level": 3 - }, - "group_access": { - "access_level": 50, - "notification_level": 3 - } - }, - "archived": false, - "avatar_url": null, - "shared_runners_enabled": true, - "forks_count": 0, - "star_count": 0, - "runners_token": "b8547b1dc37721d05889db52fa2f02", - "public_builds": true, - "shared_with_groups": [] - } -] -``` - -### List owned projects - -Get a list of projects which are owned by the authenticated user. - -``` -GET /projects/owned -``` - -Parameters: - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `archived` | boolean | no | Limit by archived status | -| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | -| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | -| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Return list of authorized projects matching the search criteria | -| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | -| `statistics` | boolean | no | Include project statistics | - -### List starred projects - -Get a list of projects which are starred by the authenticated user. - -``` -GET /projects/starred -``` - -Parameters: - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `archived` | boolean | no | Limit by archived status | -| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | -| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | -| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Return list of authorized projects matching the search criteria | -| `simple` | boolean | no | Return only the ID, URL, name, and path of each project | - -### List ALL projects - -Get a list of all GitLab projects (admin only). - -``` -GET /projects/all -``` - -Parameters: - -| Attribute | Type | Required | Description | -| --------- | ---- | -------- | ----------- | -| `archived` | boolean | no | Limit by archived status | -| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` | -| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` | -| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` | -| `search` | string | no | Return list of authorized projects matching the search criteria | -| `statistics` | boolean | no | Include project statistics | - ### Get single project Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME, which is owned by the authenticated user. diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md index 7811025f34e..84ff72bc36c 100644 --- a/doc/api/v3_to_v4.md +++ b/doc/api/v3_to_v4.md @@ -23,4 +23,4 @@ changes are in V4: - `/gitlab_ci_ymls/:key` - `/dockerfiles/:key` - Moved `/projects/fork/:id` to `/projects/:id/fork` - +- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 5c132bdd6f9..9f29c4466ab 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -143,6 +143,9 @@ module API desc: 'Return projects sorted in ascending and descending order' optional :simple, type: Boolean, default: false, desc: 'Return only the ID, URL, name, and path of each project' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' + optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' + use :pagination end get ":id/projects" do diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index dfab60f7fa5..13896dd91b9 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -256,6 +256,14 @@ module API # project helpers def filter_projects(projects) + if params[:owned] + projects = projects.merge(current_user.owned_projects) + end + + if params[:starred] + projects = projects.merge(current_user.starred_projects) + end + if params[:search].present? projects = projects.search(params[:search]) end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 2cacb246db8..68c2732ec80 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -50,6 +50,8 @@ module API optional :visibility, type: String, values: %w[public internal private], desc: 'Limit by visibility' optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' + optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' end params :statistics_params do @@ -82,62 +84,9 @@ module API params do use :collection_params end - get '/visible' do - entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails - present_projects ProjectsFinder.new.execute(current_user), with: entity - end - - desc 'Get a projects list for authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - end get do - authenticate! - - present_projects current_user.authorized_projects, - with: Entities::ProjectWithAccess - end - - desc 'Get an owned projects list for authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - use :statistics_params - end - get '/owned' do - authenticate! - - present_projects current_user.owned_projects, - with: Entities::ProjectWithAccess, - statistics: params[:statistics] - end - - desc 'Gets starred project for the authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - end - get '/starred' do - authenticate! - - present_projects current_user.viewable_starred_projects - end - - desc 'Get all projects for admin user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - use :statistics_params - end - get '/all' do - authenticated_as_admin! - - present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics] + entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails + present_projects ProjectsFinder.new.execute(current_user), with: entity, statistics: params[:statistics] end desc 'Create new project' do diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index f78bde6f53a..ccd7898586c 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -338,6 +338,26 @@ describe API::Groups, api: true do expect(json_response.length).to eq(1) expect(json_response.first['name']).to eq(project3.name) end + + it 'only returns the projects owned by user' do + project2.group.add_owner(user3) + + get api("/groups/#{project2.group.id}/projects", user3), owned: true + + expect(response).to have_http_status(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project2.name) + end + + it 'only returns the projects starred by user' do + user1.starred_projects = [project1] + + get api("/groups/#{group1.id}/projects", user1), starred: true + + expect(response).to have_http_status(200) + expect(json_response.length).to eq(1) + expect(json_response.first['name']).to eq(project1.name) + end end context "when authenticated as admin" do diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 17b5e372bdc..bca7642b6fc 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -41,26 +41,40 @@ describe API::Projects, api: true do end describe 'GET /projects' do - before { project } + shared_examples_for 'projects response' do + it 'returns an array of projects' do + get api('/projects', current_user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id)) + end + end + + let!(:public_project) { create(:empty_project, :public, name: 'public_project') } + before do + project + project2 + project3 + project4 + end context 'when unauthenticated' do - it 'returns authentication error' do - get api('/projects') - expect(response).to have_http_status(401) + it_behaves_like 'projects response' do + let(:current_user) { nil } + let(:projects) { [public_project] } end end context 'when authenticated as regular user' do - it 'returns an array of projects' do - get api('/projects', user) - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first['name']).to eq(project.name) - expect(json_response.first['owner']['username']).to eq(user.username) + it_behaves_like 'projects response' do + let(:current_user) { user } + let(:projects) { [public_project, project, project2, project3] } end it 'includes the project labels as the tag_list' do get api('/projects', user) + expect(response.status).to eq 200 expect(json_response).to be_an Array expect(json_response.first.keys).to include('tag_list') @@ -68,21 +82,39 @@ describe API::Projects, api: true do it 'includes open_issues_count' do get api('/projects', user) + expect(response.status).to eq 200 expect(json_response).to be_an Array expect(json_response.first.keys).to include('open_issues_count') end - it 'does not include open_issues_count' do + it 'does not include open_issues_count if issues are disabled' do project.project_feature.update_attribute(:issues_access_level, ProjectFeature::DISABLED) get api('/projects', user) + expect(response.status).to eq 200 expect(json_response).to be_an Array - expect(json_response.first.keys).not_to include('open_issues_count') + expect(json_response.find { |hash| hash['id'] == project.id }.keys).not_to include('open_issues_count') + end + + it "does not include statistics by default" do + get api('/projects', user) + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).not_to include('statistics') end - context 'GET /projects?simple=true' do + it "includes statistics if requested" do + get api('/projects', user), statistics: true + + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first).to include 'statistics' + end + + context 'and with simple=true' do it 'returns a simplified version of all the projects' do expected_keys = ["id", "http_url_to_repo", "web_url", "name", "name_with_namespace", "path", "path_with_namespace"] @@ -97,6 +129,7 @@ describe API::Projects, api: true do context 'and using search' do it 'returns searched project' do get api('/projects', user), { search: project.name } + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.length).to eq(1) @@ -106,196 +139,109 @@ describe API::Projects, api: true do context 'and using the visibility filter' do it 'filters based on private visibility param' do get api('/projects', user), { visibility: 'private' } + expect(response).to have_http_status(200) expect(json_response).to be_an Array - expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count) + expect(json_response.map { |p| p['id'] }).to contain_exactly(project.id, project2.id, project3.id) end it 'filters based on internal visibility param' do + project2.update_attribute(:visibility_level, Gitlab::VisibilityLevel::INTERNAL) + get api('/projects', user), { visibility: 'internal' } + expect(response).to have_http_status(200) expect(json_response).to be_an Array - expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count) + expect(json_response.map { |p| p['id'] }).to contain_exactly(project2.id) end it 'filters based on public visibility param' do get api('/projects', user), { visibility: 'public' } + expect(response).to have_http_status(200) expect(json_response).to be_an Array - expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count) + expect(json_response.map { |p| p['id'] }).to contain_exactly(public_project.id) end end context 'and using sorting' do - before do - project2 - project3 - end - it 'returns the correct order when sorted by id' do get api('/projects', user), { order_by: 'id', sort: 'desc' } + expect(response).to have_http_status(200) expect(json_response).to be_an Array expect(json_response.first['id']).to eq(project3.id) end end - end - end - describe 'GET /projects/all' do - before { project } + context 'and with owned=true' do + it 'returns an array of projects the user owns' do + get api('/projects', user4), owned: true - context 'when unauthenticated' do - it 'returns authentication error' do - get api('/projects/all') - expect(response).to have_http_status(401) - end - end - - context 'when authenticated as regular user' do - it 'returns authentication error' do - get api('/projects/all', user) - expect(response).to have_http_status(403) - end - end - - context 'when authenticated as admin' do - it 'returns an array of all projects' do - get api('/projects/all', admin) - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - - expect(json_response).to satisfy do |response| - response.one? do |entry| - entry.has_key?('permissions') && - entry['name'] == project.name && - entry['owner']['username'] == user.username - end + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.first['name']).to eq(project4.name) + expect(json_response.first['owner']['username']).to eq(user4.username) end end - it "does not include statistics by default" do - get api('/projects/all', admin) + context 'and with starred=true' do + let(:public_project) { create(:empty_project, :public) } - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first).not_to include('statistics') - end - - it "includes statistics if requested" do - get api('/projects/all', admin), statistics: true - - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first).to include 'statistics' - end - end - end - - describe 'GET /projects/owned' do - before do - project3 - project4 - end - - context 'when unauthenticated' do - it 'returns authentication error' do - get api('/projects/owned') - expect(response).to have_http_status(401) - end - end - - context 'when authenticated as project owner' do - it 'returns an array of projects the user owns' do - get api('/projects/owned', user4) - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first['name']).to eq(project4.name) - expect(json_response.first['owner']['username']).to eq(user4.username) - end - - it "does not include statistics by default" do - get api('/projects/owned', user4) - - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first).not_to include('statistics') - end - - it "includes statistics if requested" do - attributes = { - commit_count: 23, - storage_size: 702, - repository_size: 123, - lfs_objects_size: 234, - build_artifacts_size: 345, - } - - project4.statistics.update!(attributes) + before do + project_member2 + user3.update_attributes(starred_projects: [project, project2, project3, public_project]) + end - get api('/projects/owned', user4), statistics: true + it 'returns the starred projects viewable by the user' do + get api('/projects', user3), starred: true - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.first['statistics']).to eq attributes.stringify_keys + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id) + end end - end - end - describe 'GET /projects/visible' do - shared_examples_for 'visible projects response' do - it 'returns the visible projects' do - get api('/projects/visible', current_user) + context 'and with all query parameters' do + # | | project5 | project6 | project7 | project8 | project9 | + # |---------+----------+----------+----------+----------+----------| + # | search | x | | x | x | x | + # | starred | x | x | | x | x | + # | public | x | x | x | | x | + # | owned | x | x | x | x | | + let!(:project5) { create(:empty_project, :public, path: 'gitlab5', namespace: user.namespace) } + let!(:project6) { create(:empty_project, :public, path: 'project6', namespace: user.namespace) } + let!(:project7) { create(:empty_project, :public, path: 'gitlab7', namespace: user.namespace) } + let!(:project8) { create(:empty_project, path: 'gitlab8', namespace: user.namespace) } + let!(:project9) { create(:empty_project, :public, path: 'gitlab9') } - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.map { |p| p['id'] }).to contain_exactly(*projects.map(&:id)) - end - end + before do + user.update_attributes(starred_projects: [project5, project6, project8, project9]) + end - let!(:public_project) { create(:empty_project, :public) } - before do - project - project2 - project3 - project4 - end + it 'returns only projects that satify all query parameters' do + get api('/projects', user), { visibility: 'public', owned: true, starred: true, search: 'gitlab' } - context 'when unauthenticated' do - it_behaves_like 'visible projects response' do - let(:current_user) { nil } - let(:projects) { [public_project] } - end - end - - context 'when authenticated' do - it_behaves_like 'visible projects response' do - let(:current_user) { user } - let(:projects) { [public_project, project, project2, project3] } + expect(response).to have_http_status(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.first['id']).to eq(project5.id) + end end end context 'when authenticated as a different user' do - it_behaves_like 'visible projects response' do + it_behaves_like 'projects response' do let(:current_user) { user2 } let(:projects) { [public_project] } end end - end - - describe 'GET /projects/starred' do - let(:public_project) { create(:empty_project, :public) } - - before do - project_member2 - user3.update_attributes(starred_projects: [project, project2, project3, public_project]) - end - it 'returns the starred projects viewable by the user' do - get api('/projects/starred', user3) - expect(response).to have_http_status(200) - expect(json_response).to be_an Array - expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id) + context 'when authenticated as admin' do + it_behaves_like 'projects response' do + let(:current_user) { admin } + let(:projects) { Project.all } + end end end -- cgit v1.2.1 From 613856face7f2ea58c229682a3acbba3e64be01b Mon Sep 17 00:00:00 2001 From: dixpac Date: Tue, 14 Feb 2017 16:33:50 +0100 Subject: Move tag services to `Tags` namespace CreateTagService and DeleteTagService where in root namespace, by following service code organization I moved them in Tags::CreateService and Tags::DestroyService --- app/controllers/projects/tags_controller.rb | 4 +- app/services/create_tag_service.rb | 30 ------------ app/services/delete_tag_service.rb | 42 ----------------- app/services/tags/create_service.rb | 32 +++++++++++++ app/services/tags/destroy_service.rb | 44 ++++++++++++++++++ .../unreleased/move_tags_service_to_namespace.yml | 4 ++ lib/api/tags.rb | 4 +- spec/services/create_tag_service_spec.rb | 53 ---------------------- spec/services/delete_tag_service_spec.rb | 17 ------- spec/services/tags/create_service_spec.rb | 53 ++++++++++++++++++++++ spec/services/tags/destroy_service_spec.rb | 17 +++++++ 11 files changed, 154 insertions(+), 146 deletions(-) delete mode 100644 app/services/create_tag_service.rb delete mode 100644 app/services/delete_tag_service.rb create mode 100644 app/services/tags/create_service.rb create mode 100644 app/services/tags/destroy_service.rb create mode 100644 changelogs/unreleased/move_tags_service_to_namespace.yml delete mode 100644 spec/services/create_tag_service_spec.rb delete mode 100644 spec/services/delete_tag_service_spec.rb create mode 100644 spec/services/tags/create_service_spec.rb create mode 100644 spec/services/tags/destroy_service_spec.rb diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index e2d9d5ed460..33379659d73 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -27,7 +27,7 @@ class Projects::TagsController < Projects::ApplicationController end def create - result = CreateTagService.new(@project, current_user). + result = Tags::CreateService.new(@project, current_user). execute(params[:tag_name], params[:ref], params[:message], params[:release_description]) if result[:status] == :success @@ -41,7 +41,7 @@ class Projects::TagsController < Projects::ApplicationController end def destroy - DeleteTagService.new(project, current_user).execute(params[:id]) + Tags::DestroyService.new(project, current_user).execute(params[:id]) respond_to do |format| format.html do diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb deleted file mode 100644 index 6c75d1f04ff..00000000000 --- a/app/services/create_tag_service.rb +++ /dev/null @@ -1,30 +0,0 @@ -class CreateTagService < BaseService - def execute(tag_name, target, message, release_description = nil) - valid_tag = Gitlab::GitRefValidator.validate(tag_name) - return error('Tag name invalid') unless valid_tag - - repository = project.repository - message&.strip! - - new_tag = nil - - begin - new_tag = repository.add_tag(current_user, tag_name, target, message) - rescue Rugged::TagError - return error("Tag #{tag_name} already exists") - rescue GitHooksService::PreReceiveError => ex - return error(ex.message) - end - - if new_tag - if release_description - CreateReleaseService.new(@project, @current_user). - execute(tag_name, release_description) - end - - success.merge(tag: new_tag) - else - error("Target #{target} is invalid") - end - end -end diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb deleted file mode 100644 index eb726cb04b1..00000000000 --- a/app/services/delete_tag_service.rb +++ /dev/null @@ -1,42 +0,0 @@ -class DeleteTagService < BaseService - def execute(tag_name) - repository = project.repository - tag = repository.find_tag(tag_name) - - unless tag - return error('No such tag', 404) - end - - if repository.rm_tag(current_user, tag_name) - release = project.releases.find_by(tag: tag_name) - release&.destroy - - push_data = build_push_data(tag) - EventCreateService.new.push(project, current_user, push_data) - project.execute_hooks(push_data.dup, :tag_push_hooks) - project.execute_services(push_data.dup, :tag_push_hooks) - - success('Tag was removed') - else - error('Failed to remove tag') - end - end - - def error(message, return_code = 400) - super(message).merge(return_code: return_code) - end - - def success(message) - super().merge(message: message) - end - - def build_push_data(tag) - Gitlab::DataBuilder::Push.build( - project, - current_user, - tag.dereferenced_target.sha, - Gitlab::Git::BLANK_SHA, - "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", - []) - end -end diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb new file mode 100644 index 00000000000..1756da9e519 --- /dev/null +++ b/app/services/tags/create_service.rb @@ -0,0 +1,32 @@ +module Tags + class CreateService < BaseService + def execute(tag_name, target, message, release_description = nil) + valid_tag = Gitlab::GitRefValidator.validate(tag_name) + return error('Tag name invalid') unless valid_tag + + repository = project.repository + message&.strip! + + new_tag = nil + + begin + new_tag = repository.add_tag(current_user, tag_name, target, message) + rescue Rugged::TagError + return error("Tag #{tag_name} already exists") + rescue GitHooksService::PreReceiveError => ex + return error(ex.message) + end + + if new_tag + if release_description + CreateReleaseService.new(@project, @current_user). + execute(tag_name, release_description) + end + + success.merge(tag: new_tag) + else + error("Target #{target} is invalid") + end + end + end +end diff --git a/app/services/tags/destroy_service.rb b/app/services/tags/destroy_service.rb new file mode 100644 index 00000000000..910b4f5e361 --- /dev/null +++ b/app/services/tags/destroy_service.rb @@ -0,0 +1,44 @@ +module Tags + class DestroyService < BaseService + def execute(tag_name) + repository = project.repository + tag = repository.find_tag(tag_name) + + unless tag + return error('No such tag', 404) + end + + if repository.rm_tag(current_user, tag_name) + release = project.releases.find_by(tag: tag_name) + release&.destroy + + push_data = build_push_data(tag) + EventCreateService.new.push(project, current_user, push_data) + project.execute_hooks(push_data.dup, :tag_push_hooks) + project.execute_services(push_data.dup, :tag_push_hooks) + + success('Tag was removed') + else + error('Failed to remove tag') + end + end + + def error(message, return_code = 400) + super(message).merge(return_code: return_code) + end + + def success(message) + super().merge(message: message) + end + + def build_push_data(tag) + Gitlab::DataBuilder::Push.build( + project, + current_user, + tag.dereferenced_target.sha, + Gitlab::Git::BLANK_SHA, + "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", + []) + end + end +end diff --git a/changelogs/unreleased/move_tags_service_to_namespace.yml b/changelogs/unreleased/move_tags_service_to_namespace.yml new file mode 100644 index 00000000000..ba76f291162 --- /dev/null +++ b/changelogs/unreleased/move_tags_service_to_namespace.yml @@ -0,0 +1,4 @@ +--- +title: Move tag services to Tags namespace +merge_request: +author: dixpac diff --git a/lib/api/tags.rb b/lib/api/tags.rb index 5b345db3a41..b6fd8f569a9 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -40,7 +40,7 @@ module API post ':id/repository/tags' do authorize_push_project - result = CreateTagService.new(user_project, current_user). + result = ::Tags::CreateService.new(user_project, current_user). execute(params[:tag_name], params[:ref], params[:message], params[:release_description]) if result[:status] == :success @@ -59,7 +59,7 @@ module API delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do authorize_push_project - result = DeleteTagService.new(user_project, current_user). + result = ::Tags::DestroyService.new(user_project, current_user). execute(params[:tag_name]) if result[:status] == :success diff --git a/spec/services/create_tag_service_spec.rb b/spec/services/create_tag_service_spec.rb deleted file mode 100644 index 7dc43c50b0d..00000000000 --- a/spec/services/create_tag_service_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -require 'spec_helper' - -describe CreateTagService, services: true do - let(:project) { create(:project) } - let(:repository) { project.repository } - let(:user) { create(:user) } - let(:service) { described_class.new(project, user) } - - describe '#execute' do - it 'creates the tag and returns success' do - response = service.execute('v42.42.42', 'master', 'Foo') - - expect(response[:status]).to eq(:success) - expect(response[:tag]).to be_a Gitlab::Git::Tag - expect(response[:tag].name).to eq('v42.42.42') - end - - context 'when target is invalid' do - it 'returns an error' do - response = service.execute('v1.1.0', 'foo', 'Foo') - - expect(response).to eq(status: :error, - message: 'Target foo is invalid') - end - end - - context 'when tag already exists' do - it 'returns an error' do - expect(repository).to receive(:add_tag). - with(user, 'v1.1.0', 'master', 'Foo'). - and_raise(Rugged::TagError) - - response = service.execute('v1.1.0', 'master', 'Foo') - - expect(response).to eq(status: :error, - message: 'Tag v1.1.0 already exists') - end - end - - context 'when pre-receive hook fails' do - it 'returns an error' do - expect(repository).to receive(:add_tag). - with(user, 'v1.1.0', 'master', 'Foo'). - and_raise(GitHooksService::PreReceiveError, 'something went wrong') - - response = service.execute('v1.1.0', 'master', 'Foo') - - expect(response).to eq(status: :error, - message: 'something went wrong') - end - end - end -end diff --git a/spec/services/delete_tag_service_spec.rb b/spec/services/delete_tag_service_spec.rb deleted file mode 100644 index 477551f5036..00000000000 --- a/spec/services/delete_tag_service_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe DeleteTagService, services: true do - let(:project) { create(:project) } - let(:repository) { project.repository } - let(:user) { create(:user) } - let(:service) { described_class.new(project, user) } - - describe '#execute' do - it 'removes the tag' do - expect(repository).to receive(:before_remove_tag) - expect(service).to receive(:success) - - service.execute('v1.1.0') - end - end -end diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb new file mode 100644 index 00000000000..5478b8c9ec0 --- /dev/null +++ b/spec/services/tags/create_service_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe Tags::CreateService, services: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'creates the tag and returns success' do + response = service.execute('v42.42.42', 'master', 'Foo') + + expect(response[:status]).to eq(:success) + expect(response[:tag]).to be_a Gitlab::Git::Tag + expect(response[:tag].name).to eq('v42.42.42') + end + + context 'when target is invalid' do + it 'returns an error' do + response = service.execute('v1.1.0', 'foo', 'Foo') + + expect(response).to eq(status: :error, + message: 'Target foo is invalid') + end + end + + context 'when tag already exists' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(Rugged::TagError) + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'Tag v1.1.0 already exists') + end + end + + context 'when pre-receive hook fails' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(GitHooksService::PreReceiveError, 'something went wrong') + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'something went wrong') + end + end + end +end diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb new file mode 100644 index 00000000000..a388c93379a --- /dev/null +++ b/spec/services/tags/destroy_service_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Tags::DestroyService, services: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'removes the tag' do + expect(repository).to receive(:before_remove_tag) + expect(service).to receive(:success) + + service.execute('v1.1.0') + end + end +end -- cgit v1.2.1 From 6676b4f0dd05ffb6071a3329cca4459e7cddcdc3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 14 Feb 2017 16:03:24 +0200 Subject: Use Namespace#full_path instead of Namespace#path Signed-off-by: Dmitriy Zaporozhets --- app/helpers/namespaces_helper.rb | 2 +- app/helpers/submodule_helper.rb | 2 +- app/models/merge_request.rb | 4 +-- app/models/project.rb | 28 ++++++++++------ app/models/project_services/drone_ci_service.rb | 6 ++-- app/services/projects/transfer_service.rb | 6 ++-- app/views/admin/dashboard/index.html.haml | 2 +- app/views/import/base/unauthorized.js.haml | 2 +- app/views/projects/edit.html.haml | 2 +- app/views/projects/group_links/_index.html.haml | 2 +- app/views/projects/pages_domains/show.html.haml | 2 +- app/views/shared/members/_group.html.haml | 2 +- doc/api/projects.md | 27 ++++++++++----- lib/backup/repository.rb | 2 +- lib/gitlab/google_code_import/importer.rb | 2 +- lib/gitlab/import_export.rb | 2 +- lib/gitlab/import_export/importer.rb | 2 +- lib/gitlab/shell.rb | 2 +- .../lib/gitlab/import_export/import_export_spec.rb | 5 +-- spec/models/project_spec.rb | 39 ++++++++++++++++------ spec/requests/api/projects_spec.rb | 1 + spec/requests/api/v3/projects_spec.rb | 1 + 22 files changed, 90 insertions(+), 53 deletions(-) diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb index e0b8dc1393b..0676767d910 100644 --- a/app/helpers/namespaces_helper.rb +++ b/app/helpers/namespaces_helper.rb @@ -10,7 +10,7 @@ module NamespacesHelper data_attr_users = { 'data-options-parent' => 'users' } group_opts = [ - "Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.path : g.human_name, g.id, data_attr_group] } + "Groups", groups.sort_by(&:human_name).map { |g| [display_path ? g.full_path : g.human_name, g.id, data_attr_group] } ] users_opts = [ diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index b3f50ceebe4..9a748aaaf33 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -63,7 +63,7 @@ module SubmoduleHelper namespace = components.pop.gsub(/^\.\.$/, '') if namespace.empty? - namespace = @project.namespace.path + namespace = @project.namespace.full_path end [ diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 38646eba3ac..204d2b153ad 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -598,7 +598,7 @@ class MergeRequest < ActiveRecord::Base def source_project_namespace if source_project && source_project.namespace - source_project.namespace.path + source_project.namespace.full_path else "(removed)" end @@ -606,7 +606,7 @@ class MergeRequest < ActiveRecord::Base def target_project_namespace if target_project && target_project.namespace - target_project.namespace.path + target_project.namespace.full_path else "(removed)" end diff --git a/app/models/project.rb b/app/models/project.rb index aa408b4556e..ed43fc2e575 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -454,7 +454,7 @@ class Project < ActiveRecord::Base if forked? job_id = RepositoryForkWorker.perform_async(id, forked_from_project.repository_storage_path, forked_from_project.path_with_namespace, - self.namespace.path) + self.namespace.full_path) else job_id = RepositoryImportWorker.perform_async(self.id) end @@ -942,8 +942,8 @@ class Project < ActiveRecord::Base Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" - Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) - Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.path) + Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.full_path) + Gitlab::PagesTransfer.new.rename_project(path_was, path, namespace.full_path) end # Expires various caches before a project is renamed. @@ -1150,19 +1150,25 @@ class Project < ActiveRecord::Base end def pages_url + subdomain, _, url_path = full_path.partition('/') + # The hostname always needs to be in downcased # All web servers convert hostname to lowercase - host = "#{namespace.path}.#{Settings.pages.host}".downcase + host = "#{subdomain}.#{Settings.pages.host}".downcase # The host in URL always needs to be downcased url = Gitlab.config.pages.url.sub(/^https?:\/\//) do |prefix| - "#{prefix}#{namespace.path}." + "#{prefix}#{subdomain}." end.downcase # If the project path is the same as host, we serve it as group page - return url if host == path + return url if host == url_path + + "#{url}/#{url_path}" + end - "#{url}/#{path}" + def pages_subdomain + full_path.partition('/').first end def pages_path @@ -1179,8 +1185,8 @@ class Project < ActiveRecord::Base # 3. We asynchronously remove pages with force temp_path = "#{path}.#{SecureRandom.hex}.deleted" - if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.path) - PagesWorker.perform_in(5.minutes, :remove, namespace.path, temp_path) + if Gitlab::PagesTransfer.new.rename_project(path, temp_path, namespace.full_path) + PagesWorker.perform_in(5.minutes, :remove, namespace.full_path, temp_path) end end @@ -1230,7 +1236,7 @@ class Project < ActiveRecord::Base end def ensure_dir_exist - gitlab_shell.add_namespace(repository_storage_path, namespace.path) + gitlab_shell.add_namespace(repository_storage_path, namespace.full_path) end def predefined_variables @@ -1238,7 +1244,7 @@ class Project < ActiveRecord::Base { key: 'CI_PROJECT_ID', value: id.to_s, public: true }, { key: 'CI_PROJECT_NAME', value: path, public: true }, { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true }, - { key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true }, + { key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path, public: true }, { key: 'CI_PROJECT_URL', value: web_url, public: true } ] end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 0a217d8caba..942ec9371e5 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -12,7 +12,7 @@ class DroneCiService < CiService def compose_service_hook hook = service_hook || build_service_hook # If using a service template, project may not be available - hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project + hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.full_path}", "&name=#{project.path}", "&access_token=#{token}"].join if project hook.enable_ssl_verification = !!enable_ssl_verification hook.save end @@ -38,7 +38,7 @@ class DroneCiService < CiService def commit_status_path(sha, ref) url = [drone_url, - "gitlab/#{project.namespace.path}/#{project.path}/commits/#{sha}", + "gitlab/#{project.full_path}/commits/#{sha}", "?branch=#{URI::encode(ref.to_s)}&access_token=#{token}"] URI.join(*url).to_s @@ -73,7 +73,7 @@ class DroneCiService < CiService def build_page(sha, ref) url = [drone_url, - "gitlab/#{project.namespace.path}/#{project.path}/redirect/commits/#{sha}", + "gitlab/#{project.full_path}/redirect/commits/#{sha}", "?branch=#{URI::encode(ref.to_s)}"] URI.join(*url).to_s diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index 484700c8c29..20dfbddc823 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -30,7 +30,7 @@ module Projects Project.transaction do old_path = project.path_with_namespace old_group = project.group - new_path = File.join(new_namespace.try(:path) || '', project.path) + new_path = File.join(new_namespace.try(:full_path) || '', project.path) if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? raise TransferError.new("Project with same path in target namespace already exists") @@ -63,10 +63,10 @@ module Projects Labels::TransferService.new(current_user, old_group, project).execute # Move uploads - Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) + Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path) # Move pages - Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path) + Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path) project.old_path_with_namespace = old_path diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 5238623e936..e67ad663720 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -163,6 +163,6 @@ - @groups.each do |group| %p = link_to [:admin, group], class: 'str-truncated-60' do - = group.name + = group.full_name %span.light.pull-right #{time_ago_with_tooltip(group.created_at)} diff --git a/app/views/import/base/unauthorized.js.haml b/app/views/import/base/unauthorized.js.haml index 36f8069c1f7..ada5f99f4e2 100644 --- a/app/views/import/base/unauthorized.js.haml +++ b/app/views/import/base/unauthorized.js.haml @@ -4,7 +4,7 @@ import_button = tr.find(".btn-import") origin_target = target_field.text() project_name = "#{@project_name}" - origin_namespace = "#{@target_namespace.path}" + origin_namespace = "#{@target_namespace.full_path}" target_field.empty() target_field.append("

    This namespace has already been taken! Please choose another one.

    ") target_field.append("") diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 9c5c1a6d707..f2905fa3ce9 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -232,7 +232,7 @@ .form-group .input-group .input-group-addon - #{URI.join(root_url, @project.namespace.path)}/ + #{URI.join(root_url, @project.namespace.full_path)}/ = f.text_field :path, class: 'form-control' %ul %li Be careful. Renaming a project's repository can have unintended side effects. diff --git a/app/views/projects/group_links/_index.html.haml b/app/views/projects/group_links/_index.html.haml index 99d0df2ac34..b6116dbec41 100644 --- a/app/views/projects/group_links/_index.html.haml +++ b/app/views/projects/group_links/_index.html.haml @@ -39,7 +39,7 @@ = icon("folder-open-o", class: "settings-list-icon") .pull-left = link_to group do - = group.name + = group.full_name %br up to #{group_link.human_access} - if group_link.expires? diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml index 52dddb052a7..876cac0dacb 100644 --- a/app/views/projects/pages_domains/show.html.haml +++ b/app/views/projects/pages_domains/show.html.haml @@ -17,7 +17,7 @@ %p To access the domain create a new DNS record: %pre - #{@domain.domain} CNAME #{@domain.project.namespace.path}.#{Settings.pages.host}. + #{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}. %tr %td Certificate diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml index 81b5bc1de30..1d5a61cffce 100644 --- a/app/views/shared/members/_group.html.haml +++ b/app/views/shared/members/_group.html.haml @@ -6,7 +6,7 @@ %span.list-item-name = image_tag group_icon(group), class: "avatar s40", alt: '' %strong - = link_to group.name, group_path(group) + = link_to group.full_name, group_path(group) .cgray Joined #{time_ago_with_tooltip(group.created_at)} - if group_link.expires? diff --git a/doc/api/projects.md b/doc/api/projects.md index bad238f57d7..f4679d29a4b 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -75,7 +75,8 @@ Parameters: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "archived": false, "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", @@ -125,7 +126,8 @@ Parameters: "id": 4, "name": "Brightbox", "path": "brightbox", - "kind": "group" + "kind": "group", + "full_path": "brightbox" }, "permissions": { "project_access": { @@ -207,7 +209,8 @@ Parameters: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "archived": false, "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png", @@ -254,7 +257,8 @@ Parameters: "id": 4, "name": "Brightbox", "path": "brightbox", - "kind": "group" + "kind": "group", + "full_path": "brightbox" }, "permissions": { "project_access": { @@ -389,7 +393,8 @@ Parameters: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "permissions": { "project_access": { @@ -767,7 +772,8 @@ Example response: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "archived": true, "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", @@ -832,7 +838,8 @@ Example response: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "archived": true, "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png", @@ -903,7 +910,8 @@ Example response: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "permissions": { "project_access": { @@ -985,7 +993,8 @@ Example response: "id": 3, "name": "Diaspora", "path": "diaspora", - "kind": "group" + "kind": "group", + "full_path": "diaspora" }, "permissions": { "project_access": { diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index d746070913d..91e43dcb114 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -12,7 +12,7 @@ module Backup path_to_project_bundle = path_to_bundle(project) # Create namespace dir if missing - FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace + FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.full_path)) if project.namespace if project.empty_repo? $progress.puts "[SKIPPED]".color(:cyan) diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 1f4edc36928..b02b9737493 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -310,7 +310,7 @@ module Gitlab if name == project.import_source "##{id}" else - "#{project.namespace.path}/#{name}##{id}" + "#{project.namespace.full_path}/#{name}##{id}" end text = "~~#{text}~~" if deleted text diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb index d679edec36b..a46a41bc56e 100644 --- a/lib/gitlab/import_export.rb +++ b/lib/gitlab/import_export.rb @@ -35,7 +35,7 @@ module Gitlab end def export_filename(project:) - basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.path}_#{project.path}" + basename = "#{Time.now.strftime('%Y-%m-%d_%H-%M-%3N')}_#{project.namespace.full_path}_#{project.path}" "#{basename[0..FILENAME_LIMIT]}_export.tar.gz" end diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb index e9ee47fc090..063ce74ecad 100644 --- a/lib/gitlab/import_export/importer.rb +++ b/lib/gitlab/import_export/importer.rb @@ -56,7 +56,7 @@ module Gitlab end def path_with_namespace - File.join(@project.namespace.path, @project.path) + File.join(@project.namespace.full_path, @project.path) end def repo_path diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb index 82e194c1af1..942cedd6cd4 100644 --- a/lib/gitlab/shell.rb +++ b/lib/gitlab/shell.rb @@ -172,7 +172,7 @@ module Gitlab # add_namespace("/path/to/storage", "gitlab") # def add_namespace(storage, name) - FileUtils.mkdir(full_path(storage, name), mode: 0770) unless exists?(storage, name) + FileUtils.mkdir_p(full_path(storage, name), mode: 0770) unless exists?(storage, name) end # Remove directory from repositories storage diff --git a/spec/lib/gitlab/import_export/import_export_spec.rb b/spec/lib/gitlab/import_export/import_export_spec.rb index 53f7d244d88..20743811dab 100644 --- a/spec/lib/gitlab/import_export/import_export_spec.rb +++ b/spec/lib/gitlab/import_export/import_export_spec.rb @@ -2,14 +2,15 @@ require 'spec_helper' describe Gitlab::ImportExport, services: true do describe 'export filename' do - let(:project) { create(:empty_project, :public, path: 'project-path') } + let(:group) { create(:group, :nested) } + let(:project) { create(:empty_project, :public, path: 'project-path', namespace: group) } it 'contains the project path' do expect(described_class.export_filename(project: project)).to include(project.path) end it 'contains the namespace path' do - expect(described_class.export_filename(project: project)).to include(project.namespace.path) + expect(described_class.export_filename(project: project)).to include(project.namespace.full_path) end it 'does not go over a certain length' do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 35f3dd00870..b0087a9e15d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1852,8 +1852,8 @@ describe Project, models: true do end describe '#pages_url' do - let(:group) { create :group, name: group_name } - let(:project) { create :empty_project, namespace: group, name: project_name } + let(:group) { create :group, name: 'Group' } + let(:nested_group) { create :group, parent: group } let(:domain) { 'Example.com' } subject { project.pages_url } @@ -1863,18 +1863,37 @@ describe Project, models: true do allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com') end - context 'group page' do - let(:group_name) { 'Group' } - let(:project_name) { 'group.example.com' } + context 'top-level group' do + let(:project) { create :empty_project, namespace: group, name: project_name } - it { is_expected.to eq("http://group.example.com") } + context 'group page' do + let(:project_name) { 'group.example.com' } + + it { is_expected.to eq("http://group.example.com") } + end + + context 'project page' do + let(:project_name) { 'Project' } + + it { is_expected.to eq("http://group.example.com/project") } + end end - context 'project page' do - let(:group_name) { 'Group' } - let(:project_name) { 'Project' } + context 'nested group' do + let(:project) { create :empty_project, namespace: nested_group, name: project_name } + let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" } - it { is_expected.to eq("http://group.example.com/project") } + context 'group page' do + let(:project_name) { 'group.example.com' } + + it { is_expected.to eq(expected_url) } + end + + context 'project page' do + let(:project_name) { 'Project' } + + it { is_expected.to eq(expected_url) } + end end end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ac0bbec44e0..5ea89c7e27c 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -639,6 +639,7 @@ describe API::Projects, api: true do 'name' => user.namespace.name, 'path' => user.namespace.path, 'kind' => user.namespace.kind, + 'full_path' => user.namespace.full_path, }) end diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb index a495122bba7..36d99d80e79 100644 --- a/spec/requests/api/v3/projects_spec.rb +++ b/spec/requests/api/v3/projects_spec.rb @@ -682,6 +682,7 @@ describe API::V3::Projects, api: true do 'name' => user.namespace.name, 'path' => user.namespace.path, 'kind' => user.namespace.kind, + 'full_path' => user.namespace.full_path, }) end -- cgit v1.2.1 From 409a3250438981331a6f97422d53bc1095ff13d4 Mon Sep 17 00:00:00 2001 From: winniehell Date: Fri, 10 Feb 2017 00:12:51 +0100 Subject: Replace static fixture for behaviors/requires_input_spec.js (!9162) --- changelogs/unreleased/requires-input-fixture.yml | 4 ++++ spec/javascripts/behaviors/requires_input_spec.js | 13 +++++++------ .../fixtures/behaviors/requires_input.html.haml | 18 ------------------ 3 files changed, 11 insertions(+), 24 deletions(-) create mode 100644 changelogs/unreleased/requires-input-fixture.yml delete mode 100644 spec/javascripts/fixtures/behaviors/requires_input.html.haml diff --git a/changelogs/unreleased/requires-input-fixture.yml b/changelogs/unreleased/requires-input-fixture.yml new file mode 100644 index 00000000000..be674499429 --- /dev/null +++ b/changelogs/unreleased/requires-input-fixture.yml @@ -0,0 +1,4 @@ +--- +title: Replace static fixture for behaviors/requires_input_spec.js +merge_request: 9162 +author: winniehell diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js index a958ac76e66..631fca06514 100644 --- a/spec/javascripts/behaviors/requires_input_spec.js +++ b/spec/javascripts/behaviors/requires_input_spec.js @@ -4,18 +4,19 @@ require('~/behaviors/requires_input'); (function() { describe('requiresInput', function() { - preloadFixtures('static/behaviors/requires_input.html.raw'); + preloadFixtures('branches/new_branch.html.raw'); beforeEach(function() { - return loadFixtures('static/behaviors/requires_input.html.raw'); + loadFixtures('branches/new_branch.html.raw'); + this.submitButton = $('button[type="submit"]'); }); it('disables submit when any field is required', function() { $('.js-requires-input').requiresInput(); - return expect($('.submit')).toBeDisabled(); + return expect(this.submitButton).toBeDisabled(); }); it('enables submit when no field is required', function() { $('*[required=required]').removeAttr('required'); $('.js-requires-input').requiresInput(); - return expect($('.submit')).not.toBeDisabled(); + return expect(this.submitButton).not.toBeDisabled(); }); it('enables submit when all required fields are pre-filled', function() { $('*[required=required]').remove(); @@ -25,9 +26,9 @@ require('~/behaviors/requires_input'); it('enables submit when all required fields receive input', function() { $('.js-requires-input').requiresInput(); $('#required1').val('input1').change(); - expect($('.submit')).toBeDisabled(); + expect(this.submitButton).toBeDisabled(); $('#optional1').val('input1').change(); - expect($('.submit')).toBeDisabled(); + expect(this.submitButton).toBeDisabled(); $('#required2').val('input2').change(); $('#required3').val('input3').change(); $('#required4').val('input4').change(); diff --git a/spec/javascripts/fixtures/behaviors/requires_input.html.haml b/spec/javascripts/fixtures/behaviors/requires_input.html.haml deleted file mode 100644 index c3f905e912e..00000000000 --- a/spec/javascripts/fixtures/behaviors/requires_input.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -%form.js-requires-input - %input{type: 'text', id: 'required1', required: 'required'} - %input{type: 'text', id: 'required2', required: 'required'} - %input{type: 'text', id: 'required3', required: 'required', value: 'Pre-filled'} - %input{type: 'text', id: 'optional1'} - - %textarea{id: 'required4', required: 'required'} - %textarea{id: 'optional2'} - - %select{id: 'required5', required: 'required'} - %option Zero - %option{value: '1'} One - %select{id: 'optional3', required: 'required'} - %option Zero - %option{value: '1'} One - - %button.submit{type: 'submit', value: 'Submit'} - %input.submit{type: 'submit', value: 'Submit'} -- cgit v1.2.1 From b05003af4753062d3dde37f57ff28854f417f673 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 14 Feb 2017 18:37:21 +0100 Subject: SidekiqStatus need to be qualified in some cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/gitlab/sidekiq_status/client_middleware.rb | 2 +- lib/gitlab/sidekiq_status/server_middleware.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/sidekiq_status/client_middleware.rb b/lib/gitlab/sidekiq_status/client_middleware.rb index 779a9998b22..d47609f490d 100644 --- a/lib/gitlab/sidekiq_status/client_middleware.rb +++ b/lib/gitlab/sidekiq_status/client_middleware.rb @@ -2,7 +2,7 @@ module Gitlab module SidekiqStatus class ClientMiddleware def call(_, job, _, _) - SidekiqStatus.set(job['jid']) + Gitlab::SidekiqStatus.set(job['jid']) yield end end diff --git a/lib/gitlab/sidekiq_status/server_middleware.rb b/lib/gitlab/sidekiq_status/server_middleware.rb index 31dfa46ff9d..ceab10b8301 100644 --- a/lib/gitlab/sidekiq_status/server_middleware.rb +++ b/lib/gitlab/sidekiq_status/server_middleware.rb @@ -4,7 +4,7 @@ module Gitlab def call(worker, job, queue) ret = yield - SidekiqStatus.unset(job['jid']) + Gitlab::SidekiqStatus.unset(job['jid']) ret end -- cgit v1.2.1 From de12a69a3c33562e7cd4fa81b5e12799a3f68f95 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Fri, 27 Jan 2017 17:22:17 -0200 Subject: Disable invalid service templates --- changelogs/unreleased/issue_25112.yml | 4 ++++ .../20170211073944_disable_invalid_service_templates.rb | 15 +++++++++++++++ db/schema.rb | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/issue_25112.yml create mode 100644 db/post_migrate/20170211073944_disable_invalid_service_templates.rb diff --git a/changelogs/unreleased/issue_25112.yml b/changelogs/unreleased/issue_25112.yml new file mode 100644 index 00000000000..c43d2732b9a --- /dev/null +++ b/changelogs/unreleased/issue_25112.yml @@ -0,0 +1,4 @@ +--- +title: Disable invalid service templates +merge_request: +author: diff --git a/db/post_migrate/20170211073944_disable_invalid_service_templates.rb b/db/post_migrate/20170211073944_disable_invalid_service_templates.rb new file mode 100644 index 00000000000..84954b1ef64 --- /dev/null +++ b/db/post_migrate/20170211073944_disable_invalid_service_templates.rb @@ -0,0 +1,15 @@ +class DisableInvalidServiceTemplates < ActiveRecord::Migration + DOWNTIME = false + + unless defined?(Service) + class Service < ActiveRecord::Base + self.inheritance_column = nil + end + end + + def up + Service.where(template: true, active: true).each do |template| + template.update(active: false) unless template.valid? + end + end +end diff --git a/db/schema.rb b/db/schema.rb index d421d5c6774..3dde4b9c82b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170210075922) do +ActiveRecord::Schema.define(version: 20170211073944) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" -- cgit v1.2.1 From fa810e4f85a2f30535af44538428866b09d630a4 Mon Sep 17 00:00:00 2001 From: Jan Christophersen Date: Tue, 14 Feb 2017 14:56:25 +0100 Subject: Add Links to Branches in Calendar Activity --- app/views/users/calendar_activities.html.haml | 14 +++++++++----- .../unreleased/26287-link-branch-in-calendar-activity.yml | 4 ++++ 2 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/26287-link-branch-in-calendar-activity.yml diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index dae147ca8be..4afd31f788b 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -10,13 +10,17 @@ %i.fa.fa-clock-o = event.created_at.to_s(:time) - if event.push? - #{event.action_name} #{event.ref_type} #{event.ref_name} + #{event.action_name} #{event.ref_type} + %strong + - commits_path = namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) + = link_to_if event.project.repository.branch_exists?(event.ref_name), event.ref_name, commits_path - else = event_action_name(event) - - if event.note? - %strong= link_to event.note_target.to_reference, event_note_target_path(event) - - elsif event.target - %strong= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target] + %strong + - if event.note? + = link_to event.note_target.to_reference, event_note_target_path(event) + - elsif event.target + = link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target] at %strong diff --git a/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml b/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml new file mode 100644 index 00000000000..35855578d21 --- /dev/null +++ b/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml @@ -0,0 +1,4 @@ +--- +title: Add Links to Branches in Calendar Activity +merge_request: 9224 +author: Jan Christophersen -- cgit v1.2.1 From 14cfdeaae56367fa80fc67687fbbad42e6f32a2b Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 14 Feb 2017 11:46:36 -0600 Subject: Fix z index bugs --- app/assets/stylesheets/pages/issuable.scss | 1 - app/assets/stylesheets/pages/tree.scss | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index da5c44b5fdc..a53cc27fac9 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -193,7 +193,6 @@ top: $header-height; bottom: 0; right: 0; - z-index: 8; transition: width .3s; background: $gray-light; padding: 10px 20px; diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 8fafe472621..948921efc0b 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -171,6 +171,8 @@ .tree-controls { float: right; margin-top: 11px; + position: relative; + z-index: 2; .project-action-button { margin-left: $btn-side-margin; -- cgit v1.2.1 From 8ea8cfb8096378327530fb38306594cdf114c274 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 14 Feb 2017 11:58:40 -0600 Subject: Add changelog --- changelogs/unreleased/28142-overlap-bugs.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/unreleased/28142-overlap-bugs.yml diff --git a/changelogs/unreleased/28142-overlap-bugs.yml b/changelogs/unreleased/28142-overlap-bugs.yml new file mode 100644 index 00000000000..9fdabdf204a --- /dev/null +++ b/changelogs/unreleased/28142-overlap-bugs.yml @@ -0,0 +1,4 @@ +--- +title: Fix z index issues with sidebar +merge_request: +author: -- cgit v1.2.1 From 0d26545ea17c098e01f14c154b82e0645f4fb42d Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 14 Feb 2017 12:05:59 -0600 Subject: sync yarn.lock with recent changes to package.json --- yarn.lock | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e5bb2937cf6..025a3320cbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -110,6 +110,10 @@ arr-flatten@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" +array-find@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1459,6 +1463,14 @@ enhanced-resolve@^3.0.0: object-assign "^4.0.1" tapable "^0.2.5" +enhanced-resolve@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + ent@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" @@ -1501,7 +1513,7 @@ es6-map@^0.1.3: es6-symbol "~3.1.0" event-emitter "~0.3.4" -es6-promise@~4.0.3: +es6-promise@^4.0.5, es6-promise@~4.0.3: version "4.0.5" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" @@ -1571,6 +1583,22 @@ eslint-import-resolver-node@^0.2.0: object-assign "^4.0.1" resolve "^1.1.6" +eslint-import-resolver-webpack@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.8.1.tgz#c7f8b4d5bd3c5b489457e5728c5db1c4ffbac9aa" + dependencies: + array-find "^1.0.0" + debug "^2.2.0" + enhanced-resolve "~0.9.0" + find-root "^0.1.1" + has "^1.0.1" + interpret "^1.0.0" + is-absolute "^0.2.3" + lodash.get "^3.7.0" + node-libs-browser "^1.0.0" + resolve "^1.2.0" + semver "^5.3.0" + eslint-module-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" @@ -1876,6 +1904,10 @@ find-cache-dir@^0.1.1: mkdirp "^0.5.1" pkg-dir "^1.0.0" +find-root@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-0.1.2.tgz#98d2267cff1916ccaf2743b3a0eea81d79d7dcd1" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -2278,6 +2310,13 @@ ipaddr.js@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4" +is-absolute@^0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" + dependencies: + is-relative "^0.2.1" + is-windows "^0.2.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2395,6 +2434,12 @@ is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" +is-relative@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" + dependencies: + is-unc-path "^0.1.1" + is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" @@ -2409,10 +2454,20 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +is-unc-path@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" + dependencies: + unc-path-regex "^0.1.0" + is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -2676,6 +2731,16 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0. json5 "^0.5.0" object-assign "^4.0.1" +lodash._baseget@^3.0.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4" + +lodash._topath@^3.0.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac" + dependencies: + lodash.isarray "^3.0.0" + lodash.camelcase@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.1.1.tgz#065b3ff08f0b7662f389934c46a5504c90e0b2d8" @@ -2696,6 +2761,17 @@ lodash.deburr@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b" +lodash.get@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-3.7.0.tgz#3ce68ae2c91683b281cc5394128303cbf75e691f" + dependencies: + lodash._baseget "^3.0.0" + lodash._topath "^3.0.0" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + lodash.kebabcase@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz#5e63bc9aa2a5562ff3b97ca7af2f803de1bcb90e" @@ -2747,6 +2823,10 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -2863,6 +2943,34 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +node-libs-browser@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^1.4.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + node-libs-browser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" @@ -3217,7 +3325,7 @@ process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -process@^0.11.0: +process@^0.11.0, process@~0.11.0: version "0.11.9" resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" @@ -3513,6 +3621,10 @@ resolve@1.1.x, resolve@^1.1.6: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +resolve@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -3558,7 +3670,7 @@ select2@3.5.2-browserify: version "3.5.2-browserify" resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" -"semver@2 || 3 || 4 || 5", semver@~5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -3897,6 +4009,10 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" +tapable@^0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + tapable@^0.2.5, tapable@~0.2.5: version "0.2.6" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" @@ -3938,6 +4054,12 @@ timeago.js@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-2.0.5.tgz#730c74fbdb0b0917a553675a4460e3a7f80db86c" +timers-browserify@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + timers-browserify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" @@ -4026,6 +4148,10 @@ ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" +unc-path-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -- cgit v1.2.1 From 309aee45b69888f9b51aaa0ce393a3f13b08c255 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Thu, 27 Oct 2016 09:26:58 -0400 Subject: entities: use the RepoCommit entity for branch commits Fixes #23895. --- changelogs/unreleased/api-entities.yml | 4 ++++ doc/api/branches.md | 10 ++++++++++ doc/api/commits.md | 18 ++++++++++++++--- lib/api/entities.rb | 35 +++++++++++++++++----------------- spec/requests/api/branches_spec.rb | 13 ++++++++++++- spec/requests/api/commits_spec.rb | 20 ++++++++++++++----- 6 files changed, 74 insertions(+), 26 deletions(-) create mode 100644 changelogs/unreleased/api-entities.yml diff --git a/changelogs/unreleased/api-entities.yml b/changelogs/unreleased/api-entities.yml new file mode 100644 index 00000000000..2003d00fd52 --- /dev/null +++ b/changelogs/unreleased/api-entities.yml @@ -0,0 +1,4 @@ +--- +title: "Use an entity for RepoBranch commits and enhance RepoCommit" +merge_request: 7138 +author: Ben Boeckel diff --git a/doc/api/branches.md b/doc/api/branches.md index ffcfea41453..5eaa8d2e920 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -34,6 +34,8 @@ Example response: "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "short_id": "7b5c3cc", + "title": "add projects API", "message": "add projects API", "parent_ids": [ "4ad91d3c1144c406e50c7b33bae684bd6837faf8" @@ -78,6 +80,8 @@ Example response: "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "short_id": "7b5c3cc", + "title": "add projects API", "message": "add projects API", "parent_ids": [ "4ad91d3c1144c406e50c7b33bae684bd6837faf8" @@ -119,6 +123,8 @@ Example response: "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "short_id": "7b5c3cc", + "title": "add projects API", "message": "add projects API", "parent_ids": [ "4ad91d3c1144c406e50c7b33bae684bd6837faf8" @@ -163,6 +169,8 @@ Example response: "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "short_id": "7b5c3cc", + "title": "add projects API", "message": "add projects API", "parent_ids": [ "4ad91d3c1144c406e50c7b33bae684bd6837faf8" @@ -204,6 +212,8 @@ Example response: "committer_email": "john@example.com", "committer_name": "John Smith", "id": "7b5c3cc8be40ee161ae89a06bba6229da1032a0c", + "short_id": "7b5c3cc", + "title": "add projects API", "message": "add projects API", "parent_ids": [ "4ad91d3c1144c406e50c7b33bae684bd6837faf8" diff --git a/doc/api/commits.md b/doc/api/commits.md index 53ce381c8ae..abf6de5cfdc 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -29,11 +29,15 @@ Example response: "title": "Replace sanitize with escape once", "author_name": "Dmitriy Zaporozhets", "author_email": "dzaporozhets@sphereconsultinginc.com", + "authored_date": "2012-09-20T11:50:22+03:00", "committer_name": "Administrator", "committer_email": "admin@example.com", + "committed_date": "2012-09-20T11:50:22+03:00", "created_at": "2012-09-20T11:50:22+03:00", "message": "Replace sanitize with escape once", - "allow_failure": false + "parent_ids": [ + "6104942438c14ec7bd21c6cd5bd995272b3faff6" + ] }, { "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", @@ -45,7 +49,9 @@ Example response: "committer_email": "dmitriy.zaporozhets@gmail.com", "created_at": "2012-09-20T09:06:12+03:00", "message": "Sanitize for network graph", - "allow_failure": false + "parent_ids": [ + "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" + ] } ] ``` @@ -214,10 +220,16 @@ Example response: "title": "Feature added", "author_name": "Dmitriy Zaporozhets", "author_email": "dmitriy.zaporozhets@gmail.com", + "authored_date": "2016-12-12T20:10:39.000+01:00", "created_at": "2016-12-12T20:10:39.000+01:00", "committer_name": "Administrator", "committer_email": "admin@example.com", - "message": "Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n" + "committed_date": "2016-12-12T20:10:39.000+01:00", + "title": "Feature added", + "message": "Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n", + "parent_ids": [ + "a738f717824ff53aebad8b090c1b79a14f2bd9e8" + ] } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 2a071e649fa..0b8204fe7f3 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -155,10 +155,27 @@ module API expose :shared_projects, using: Entities::Project end + class RepoCommit < Grape::Entity + expose :id, :short_id, :title, :created_at + expose :parent_ids + expose :safe_message, as: :message + expose :author_name, :author_email, :authored_date + expose :committer_name, :committer_email, :committed_date + end + + class RepoCommitStats < Grape::Entity + expose :additions, :deletions, :total + end + + class RepoCommitDetail < RepoCommit + expose :stats, using: Entities::RepoCommitStats + expose :status + end + class RepoBranch < Grape::Entity expose :name - expose :commit do |repo_branch, options| + expose :commit, using: Entities::RepoCommit do |repo_branch, options| options[:project].repository.commit(repo_branch.dereferenced_target) end @@ -193,22 +210,6 @@ module API end end - class RepoCommit < Grape::Entity - expose :id, :short_id, :title, :author_name, :author_email, :created_at - expose :committer_name, :committer_email - expose :safe_message, as: :message - end - - class RepoCommitStats < Grape::Entity - expose :additions, :deletions, :total - end - - class RepoCommitDetail < RepoCommit - expose :parent_ids, :committed_date, :authored_date - expose :stats, using: Entities::RepoCommitStats - expose :status - end - class ProjectSnippet < Grape::Entity expose :id, :title, :file_name expose :author, using: Entities::UserBasic diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index 5a3ffc284f2..3e66236f6ae 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -31,7 +31,18 @@ describe API::Branches, api: true do expect(response).to have_http_status(200) expect(json_response['name']).to eq(branch_name) - expect(json_response['commit']['id']).to eq(branch_sha) + json_commit = json_response['commit'] + expect(json_commit['id']).to eq(branch_sha) + expect(json_commit).to have_key('short_id') + expect(json_commit).to have_key('title') + expect(json_commit).to have_key('message') + expect(json_commit).to have_key('author_name') + expect(json_commit).to have_key('author_email') + expect(json_commit).to have_key('authored_date') + expect(json_commit).to have_key('committer_name') + expect(json_commit).to have_key('committer_email') + expect(json_commit).to have_key('committed_date') + expect(json_commit).to have_key('parent_ids') expect(json_response['merged']).to eq(false) expect(json_response['protected']).to eq(false) expect(json_response['developers_can_push']).to eq(false) diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index af9028a8978..3d0d6735359 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -367,11 +367,21 @@ describe API::Commits, api: true do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) expect(response).to have_http_status(200) - expect(json_response['id']).to eq(project.repository.commit.id) - expect(json_response['title']).to eq(project.repository.commit.title) - expect(json_response['stats']['additions']).to eq(project.repository.commit.stats.additions) - expect(json_response['stats']['deletions']).to eq(project.repository.commit.stats.deletions) - expect(json_response['stats']['total']).to eq(project.repository.commit.stats.total) + commit = project.repository.commit + expect(json_response['id']).to eq(commit.id) + expect(json_response['short_id']).to eq(commit.short_id) + expect(json_response['title']).to eq(commit.title) + expect(json_response['message']).to eq(commit.safe_message) + expect(json_response['author_name']).to eq(commit.author_name) + expect(json_response['author_email']).to eq(commit.author_email) + expect(json_response['authored_date']).to eq(commit.authored_date.iso8601(3)) + expect(json_response['committer_name']).to eq(commit.committer_name) + expect(json_response['committer_email']).to eq(commit.committer_email) + expect(json_response['committed_date']).to eq(commit.committed_date.iso8601(3)) + expect(json_response['parent_ids']).to eq(commit.parent_ids) + expect(json_response['stats']['additions']).to eq(commit.stats.additions) + expect(json_response['stats']['deletions']).to eq(commit.stats.deletions) + expect(json_response['stats']['total']).to eq(commit.stats.total) end it "returns a 404 error if not found" do -- cgit v1.2.1 From b7aa096129c42877596c3261838439f99216c077 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Tue, 14 Feb 2017 13:50:44 -0600 Subject: Fix yarn lock and package.json mismatch caused by MR 9133 --- yarn.lock | 339 +++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 238 insertions(+), 101 deletions(-) diff --git a/yarn.lock b/yarn.lock index 025a3320cbc..42cdc8b3fcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,12 +1,10 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 - - abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@1.3.3, accepts@~1.3.3: +accepts@~1.3.3, accepts@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" dependencies: @@ -25,14 +23,14 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" -acorn@4.0.4, acorn@^4.0.3, acorn@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn@^4.0.3, acorn@^4.0.4, acorn@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -83,6 +81,12 @@ anymatch@^1.3.0: arrify "^1.0.0" micromatch "^2.1.5" +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" + aproba@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.0.tgz#4d8f047a318604e18e3c06a0e52230d3d19f147b" @@ -140,7 +144,7 @@ arraybuffer.slice@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" -arrify@^1.0.0: +arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -174,20 +178,20 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@0.2.x, async@~0.2.6: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - -async@1.x, async@^1.4.0, async@^1.5.2: +async@^1.4.0, async@^1.4.2, async@^1.5.2, async@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.1.2: +async@^2.1.2, async@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" dependencies: lodash "^4.14.0" +async@~0.2.6, async@0.2.x: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -236,7 +240,7 @@ babel-core@^6.22.1, babel-core@^6.23.0: slash "^1.0.0" source-map "^0.5.0" -babel-generator@^6.23.0: +babel-generator@^6.18.0, babel-generator@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5" dependencies: @@ -388,6 +392,14 @@ babel-plugin-check-es2015-constants@^6.22.0: dependencies: babel-runtime "^6.22.0" +babel-plugin-istanbul@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.0.0.tgz#36bde8fbef4837e5ff0366531a2beabd7b1ffa10" + dependencies: + find-up "^2.1.0" + istanbul-lib-instrument "^1.4.2" + test-exclude "^4.0.0" + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -718,7 +730,7 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.22.0, babel-template@^6.23.0: +babel-template@^6.16.0, babel-template@^6.22.0, babel-template@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" dependencies: @@ -728,7 +740,7 @@ babel-template@^6.22.0, babel-template@^6.23.0: babylon "^6.11.0" lodash "^4.2.0" -babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: +babel-traverse@^6.18.0, babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: version "6.23.1" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" dependencies: @@ -742,7 +754,7 @@ babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: +babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" dependencies: @@ -751,7 +763,7 @@ babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.11.0, babylon@^6.15.0: +babylon@^6.11.0, babylon@^6.13.0, babylon@^6.15.0: version "6.15.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" @@ -1128,14 +1140,6 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" - dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" - concat-stream@^1.4.6: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" @@ -1144,6 +1148,14 @@ concat-stream@^1.4.6: readable-stream "^2.2.2" typedarray "^0.0.6" +concat-stream@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" @@ -1251,16 +1263,16 @@ custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" -d3@3.5.11: - version "3.5.11" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" - d@^0.1.1, d@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" dependencies: es5-ext "~0.10.2" +d3@3.5.11: + version "3.5.11" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1271,28 +1283,28 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" +debug@^2.1.1, debug@^2.2.0, debug@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" -debug@2.2.0, debug@~2.2.0: +debug@~2.2.0, debug@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: ms "0.7.1" +debug@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + debug@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" dependencies: ms "0.7.2" -debug@2.6.0, debug@^2.1.1, debug@^2.2.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" - dependencies: - ms "0.7.2" - decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1305,6 +1317,12 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + defaults@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -1364,7 +1382,7 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -doctrine@1.5.0, doctrine@^1.2.2: +doctrine@^1.2.2, doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" dependencies: @@ -1527,7 +1545,7 @@ es6-set@~0.1.3: es6-symbol "3" event-emitter "~0.3.4" -es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: +es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: version "3.1.0" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" dependencies: @@ -1679,7 +1697,7 @@ espree@^3.4.0: acorn "4.0.4" acorn-jsx "^3.0.0" -esprima@2.7.x, esprima@^2.7.1: +esprima@^2.7.1, esprima@2.7.x: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -1866,6 +1884,13 @@ filename-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" + dependencies: + glob "^7.0.3" + minimatch "^3.0.3" + fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" @@ -1915,6 +1940,12 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + flat-cache@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" @@ -2089,7 +2120,7 @@ handle-thing@^1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" -handlebars@^4.0.1: +handlebars@^4.0.1, handlebars@^4.0.3: version "4.0.6" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" dependencies: @@ -2262,7 +2293,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2468,14 +2499,14 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" +isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -2494,6 +2525,70 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" +istanbul-api@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.1.tgz#d36e2f1560d1a43ce304c4ff7338182de61c8f73" + dependencies: + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.0.0" + istanbul-lib-hook "^1.0.0" + istanbul-lib-instrument "^1.3.0" + istanbul-lib-report "^1.0.0-alpha.3" + istanbul-lib-source-maps "^1.1.0" + istanbul-reports "^1.0.0" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" + +istanbul-lib-coverage@^1.0.0, istanbul-lib-coverage@^1.0.0-alpha, istanbul-lib-coverage@^1.0.0-alpha.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.1.tgz#f263efb519c051c5f1f3343034fc40e7b43ff212" + +istanbul-lib-hook@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0.tgz#fc5367ee27f59268e8f060b0c7aaf051d9c425c5" + dependencies: + append-transform "^0.4.0" + +istanbul-lib-instrument@^1.3.0, istanbul-lib-instrument@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.4.2.tgz#0e2fdfac93c1dabf2e31578637dc78a19089f43e" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.13.0" + istanbul-lib-coverage "^1.0.0" + semver "^5.3.0" + +istanbul-lib-report@^1.0.0-alpha.3: + version "1.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.0.0-alpha.3.tgz#32d5f6ec7f33ca3a602209e278b2e6ff143498af" + dependencies: + async "^1.4.2" + istanbul-lib-coverage "^1.0.0-alpha" + mkdirp "^0.5.1" + path-parse "^1.0.5" + rimraf "^2.4.3" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.1.0.tgz#9d429218f35b823560ea300a96ff0c3bbdab785f" + dependencies: + istanbul-lib-coverage "^1.0.0-alpha.0" + mkdirp "^0.5.1" + rimraf "^2.4.4" + source-map "^0.5.3" + +istanbul-reports@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.1.tgz#9a17176bc4a6cbebdae52b2f15961d52fa623fbc" + dependencies: + handlebars "^4.0.3" + istanbul@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" @@ -2537,7 +2632,7 @@ jquery-ujs@1.2.1: dependencies: jquery ">=1.8.0" -jquery@2.2.1, jquery@>=1.8.0: +jquery@>=1.8.0, jquery@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" @@ -2549,7 +2644,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@3.x, js-yaml@^3.5.1: +js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@3.x: version "3.8.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" dependencies: @@ -2586,7 +2681,7 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json3@3.3.2, json3@^3.3.2: +json3@^3.3.2, json3@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -2616,6 +2711,12 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" +karma-coverage-istanbul-reporter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-0.2.0.tgz#5766263338adeb0026f7e4ac7a89a5f056c5642c" + dependencies: + istanbul-api "^1.1.1" + karma-jasmine@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" @@ -2722,7 +2823,7 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: +loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@0.2.x: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2731,6 +2832,13 @@ loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0. json5 "^0.5.0" object-assign "^4.0.1" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + lodash._baseget@^3.0.0: version "3.7.2" resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4" @@ -2877,7 +2985,7 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: dependencies: mime-db "~1.26.0" -mime@1.3.4, mime@^1.3.4: +mime@^1.3.4, mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -2885,32 +2993,32 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3": version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimist@0.0.8, minimist@~0.0.1: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mkdirp@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" - dependencies: - minimist "0.0.8" +minimist@~0.0.1, minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.x: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + moment@2.x: version "2.17.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" @@ -3022,7 +3130,7 @@ node-zopfli@^2.0.0: nan "^2.0.0" node-pre-gyp "^0.6.4" -nopt@3.x, nopt@~3.0.6: +nopt@~3.0.6, nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -3058,14 +3166,14 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" @@ -3091,7 +3199,7 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@1.x, once@^1.3.0: +once@^1.3.0, once@^1.4.0, once@1.x: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3160,6 +3268,16 @@ os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" @@ -3221,6 +3339,10 @@ path-exists@^2.0.0: dependencies: pinkie-promise "^2.0.0" +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -3229,6 +3351,10 @@ path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -3354,18 +3480,22 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" @@ -3374,10 +3504,6 @@ qs@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" -qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -3617,7 +3743,7 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@1.1.x, resolve@^1.1.6: +resolve@^1.1.6, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -3638,7 +3764,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@~2.5.1, rimraf@~2.5.4: +rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -3670,7 +3796,7 @@ select2@3.5.2-browserify: version "3.5.2-browserify" resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: +semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -3837,7 +3963,7 @@ source-map-support@^0.4.2: dependencies: source-map "^0.5.3" -source-map@0.1.x, source-map@^0.1.41: +source-map@^0.1.41, source-map@0.1.x: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: @@ -3937,6 +4063,10 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -3952,10 +4082,6 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^3.0.0" -string_decoder@^0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -3992,7 +4118,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.1: +supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -4038,6 +4164,16 @@ tar@~2.2.1: fstream "^1.0.2" inherits "2" +test-exclude@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.0.0.tgz#0ddc0100b8ae7e88b34eb4fd98a907e961991900" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -4156,20 +4292,20 @@ underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -url-parse@1.0.x: - version "1.0.5" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" dependencies: querystringify "0.0.x" requires-port "1.0.x" -url-parse@^1.1.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" dependencies: querystringify "0.0.x" requires-port "1.0.x" @@ -4198,7 +4334,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@0.10.3, util@^0.10.3: +util@^0.10.3, util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -4358,10 +4494,6 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -4370,6 +4502,10 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -4452,3 +4588,4 @@ yauzl@2.4.1: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + -- cgit v1.2.1 From 35216e3e88ad0ef413926844dede4a57edfc9d01 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 15:44:39 -0500 Subject: Update CHANGELOG.md for 8.16.5 [ci skip] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71d38e5453d..1a2b2d3d4c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 8.16.5 (2017-02-14) + +- Patch Asciidocs rendering to block XSS. +- Fix XSS vulnerability in SVG attachments. +- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. +- Patch XSS vulnerability in RDOC support. + ## 8.16.4 (2017-02-02) - Support non-ASCII characters in GFM autocomplete. !8729 -- cgit v1.2.1 From acfc16a3f459ca6e2bffdaa2822af4418c9e87f0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 15:52:55 -0500 Subject: Update CHANGELOG.md for 8.16.5 [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2b2d3d4c6..04649cfbaeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ entry. ## 8.16.5 (2017-02-14) +- No changes. - Patch Asciidocs rendering to block XSS. - Fix XSS vulnerability in SVG attachments. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. -- cgit v1.2.1 From d06906e6245c4b6768fed30b793d30bb8fd581e0 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 15:57:17 -0500 Subject: Update CHANGELOG.md for 8.16.5 [ci skip] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04649cfbaeb..a43afa03116 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ entry. ## 8.16.5 (2017-02-14) +- No changes. - No changes. - Patch Asciidocs rendering to block XSS. - Fix XSS vulnerability in SVG attachments. -- cgit v1.2.1 From 334cd746b3870b59bdd608dddebf4662cef5fc2c Mon Sep 17 00:00:00 2001 From: nerro Date: Fri, 3 Feb 2017 11:04:55 +0000 Subject: #27631: Add missing top-area div to activity header page --- app/views/dashboard/_activity_head.html.haml | 15 ++++++++------- .../27631-fix-small-height-of-activity-header-page.yml | 4 ++++ 2 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml index 02b94beee92..68a46f61eb7 100644 --- a/app/views/dashboard/_activity_head.html.haml +++ b/app/views/dashboard/_activity_head.html.haml @@ -1,7 +1,8 @@ -%ul.nav-links - %li{ class: ("active" unless params[:filter]) }> - = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do - Your Projects - %li{ class: ("active" if params[:filter] == 'starred') }> - = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do - Starred Projects +.top-area + %ul.nav-links + %li{ class: ("active" unless params[:filter]) }> + = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do + Your Projects + %li{ class: ("active" if params[:filter] == 'starred') }> + = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do + Starred Projects diff --git a/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml b/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml new file mode 100644 index 00000000000..59da28964f7 --- /dev/null +++ b/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml @@ -0,0 +1,4 @@ +--- +title: "Fix small height of activity header page" +merge_request: 8952 +author: Pavel Sorokin -- cgit v1.2.1 From 0733b142ac705a53de4f80e1b8c4929d30012905 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 16:35:50 -0500 Subject: Update CHANGELOG.md for 8.15.6 [ci skip] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a43afa03116..6f0492700c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -183,6 +183,13 @@ entry. - Add margin to markdown math blocks. - Add hover state to MR comment reply button. +## 8.15.6 (2017-02-14) + +- Patch Asciidocs rendering to block XSS. +- Fix XSS vulnerability in SVG attachments. +- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. +- Patch XSS vulnerability in RDOC support. + ## 8.15.4 (2017-01-09) - Make successful pipeline emails off for watchers. !8176 -- cgit v1.2.1 From f3535bbff66d410983d39ec51f75d08683407fbf Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 16:48:40 -0500 Subject: Update CHANGELOG.md for 8.14.9 [ci skip] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f0492700c5..e524062a2eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -453,6 +453,13 @@ entry. - Whitelist next project names: help, ci, admin, search. !8227 - Adds back CSS for progress-bars. !8237 +## 8.14.9 (2017-02-14) + +- Patch Asciidocs rendering to block XSS. +- Fix XSS vulnerability in SVG attachments. +- Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. +- Patch XSS vulnerability in RDOC support. + ## 8.14.8 (2017-01-25) - Accept environment variables from the `pre-receive` script. !7967 -- cgit v1.2.1 From d4f7f70f1e7c0b8481d33176fd0886a79022217a Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 14 Feb 2017 21:45:36 +0000 Subject: Stop Pikaday using moment --- app/assets/javascripts/due_date_select.js.es6 | 6 ++++-- app/assets/javascripts/issuable_form.js | 3 ++- app/assets/javascripts/member_expiration_date.js.es6 | 3 ++- app/views/profiles/personal_access_tokens/index.html.haml | 2 +- config/webpack.config.js | 1 + 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/due_date_select.js.es6 b/app/assets/javascripts/due_date_select.js.es6 index ab5ce23d261..9169fcd7328 100644 --- a/app/assets/javascripts/due_date_select.js.es6 +++ b/app/assets/javascripts/due_date_select.js.es6 @@ -48,7 +48,7 @@ const calendar = new Pikaday({ field: $dueDateInput.get(0), theme: 'gitlab-theme', - format: 'YYYY-MM-DD', + format: 'yyyy-mm-dd', onSelect: (dateText) => { const formattedDate = dateFormat(new Date(dateText), 'yyyy-mm-dd'); @@ -63,6 +63,7 @@ } }); + calendar.setDate(new Date($dueDateInput.val())); this.$datePicker.append(calendar.el); this.$datePicker.data('pikaday', calendar); } @@ -169,11 +170,12 @@ const calendar = new Pikaday({ field: $datePicker.get(0), theme: 'gitlab-theme', - format: 'YYYY-MM-DD', + format: 'yyyy-mm-dd', onSelect(dateText) { $datePicker.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); } }); + calendar.setDate(new Date($datePicker.val())); $datePicker.data('pikaday', calendar); }); diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js index 2ec545db665..c7c744ef61f 100644 --- a/app/assets/javascripts/issuable_form.js +++ b/app/assets/javascripts/issuable_form.js @@ -40,11 +40,12 @@ calendar = new Pikaday({ field: $issuableDueDate.get(0), theme: 'gitlab-theme', - format: 'YYYY-MM-DD', + format: 'yyyy-mm-dd', onSelect: function(dateText) { $issuableDueDate.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); } }); + calendar.setDate(new Date($issuableDueDate.val())); } } diff --git a/app/assets/javascripts/member_expiration_date.js.es6 b/app/assets/javascripts/member_expiration_date.js.es6 index f57d4a20498..efe7c78a8ec 100644 --- a/app/assets/javascripts/member_expiration_date.js.es6 +++ b/app/assets/javascripts/member_expiration_date.js.es6 @@ -19,7 +19,7 @@ const calendar = new Pikaday({ field: $input.get(0), theme: 'gitlab-theme', - format: 'YYYY-MM-DD', + format: 'yyyy-mm-dd', minDate: new Date(), onSelect(dateText) { $input.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); @@ -30,6 +30,7 @@ }, }); + calendar.setDate(new Date($input.val())); $input.data('pikaday', calendar); }); diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index 2c006e1712d..b10f5fc08e2 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -91,7 +91,7 @@ new Pikaday({ field: $dateField.get(0), theme: 'gitlab-theme', - format: 'YYYY-MM-DD', + format: 'yyyy-mm-dd', minDate: new Date(), onSelect: function(dateText) { $dateField.val(dateFormat(new Date(dateText), 'yyyy-mm-dd')); diff --git a/config/webpack.config.js b/config/webpack.config.js index 5d5e4bb570a..07023bdee51 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -83,6 +83,7 @@ var config = { new CompressionPlugin({ asset: '[path].gz[query]', }), + new webpack.IgnorePlugin(/moment/, /pikaday/), ], resolve: { -- cgit v1.2.1 From 41ec1afd01bebd35304c0514d8dd017097410367 Mon Sep 17 00:00:00 2001 From: blackst0ne Date: Wed, 15 Feb 2017 09:28:35 +1100 Subject: Alphabetically sort tags on runner list --- app/views/admin/runners/_runner.html.haml | 2 +- app/views/projects/runners/_form.html.haml | 2 +- app/views/projects/runners/_runner.html.haml | 2 +- app/views/projects/runners/show.html.haml | 2 +- changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml | 4 ++++ 5 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml index 975bd950ae1..deb62845e1c 100644 --- a/app/views/admin/runners/_runner.html.haml +++ b/app/views/admin/runners/_runner.html.haml @@ -22,7 +22,7 @@ %td #{runner.builds.count(:all)} %td - - runner.tag_list.each do |tag| + - runner.tag_list.sort.each do |tag| %span.label.label-primary = tag %td diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml index 98e72f6c547..2ef1f98ba48 100644 --- a/app/views/projects/runners/_form.html.haml +++ b/app/views/projects/runners/_form.html.haml @@ -32,7 +32,7 @@ = label_tag :tag_list, class: 'control-label' do Tags .col-sm-10 - = f.text_field :tag_list, value: runner.tag_list.to_s, class: 'form-control' + = f.text_field :tag_list, value: runner.tag_list.sort.join(', '), class: 'form-control' .help-block You can setup jobs to only use Runners with specific tags .form-actions = f.submit 'Save changes', class: 'btn btn-save' diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 7036b8a5ccc..deeadb609f6 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -31,6 +31,6 @@ = runner.description - if runner.tag_list.present? %p - - runner.tag_list.each do |tag| + - runner.tag_list.sort.each do |tag| %span.label.label-primary = tag diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml index 61b99f35d74..49415ba557b 100644 --- a/app/views/projects/runners/show.html.haml +++ b/app/views/projects/runners/show.html.haml @@ -28,7 +28,7 @@ %tr %td Tags %td - - @runner.tag_list.each do |tag| + - @runner.tag_list.sort.each do |tag| %span.label.label-primary = tag %tr diff --git a/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml b/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml new file mode 100644 index 00000000000..ffcf197a596 --- /dev/null +++ b/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml @@ -0,0 +1,4 @@ +--- +title: Alphabetically sort tags on runner list +merge_request: 8922 +author: blackst0ne -- cgit v1.2.1 From 1452729304393978ec93b712130dff6687db01b9 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Tue, 14 Feb 2017 22:04:43 -0500 Subject: Remove duplicate CHANGELOG.md entries for 8.16.5 --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e524062a2eb..cbaac0f69d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ entry. ## 8.16.5 (2017-02-14) -- No changes. -- No changes. - Patch Asciidocs rendering to block XSS. - Fix XSS vulnerability in SVG attachments. - Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. -- cgit v1.2.1 From a855d427d238eaf7c980b418a046613e0f710955 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 13 Feb 2017 21:27:32 -0600 Subject: Fix stray pipelines API request when showing MR Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/27925 --- app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 | 9 ++++++--- app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 | 5 ++--- app/assets/javascripts/merge_request_tabs.js.es6 | 8 ++++++++ app/views/projects/commit/_pipelines_list.haml | 5 ++++- app/views/projects/merge_requests/_show.html.haml | 2 +- .../unreleased/27925-fix-mr-stray-pipelines-api-request.yml | 4 ++++ 6 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 index fbfec7743c7..b5a988df897 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js.es6 @@ -20,7 +20,10 @@ $(() => { gl.commits.PipelinesTableBundle.$destroy(true); } - gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView({ - el: document.querySelector('#commit-pipeline-table-view'), - }); + const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); + gl.commits.pipelines.PipelinesTableBundle = new gl.commits.pipelines.PipelinesTableView(); + + if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) { + gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl); + } }); diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 index ce0dbd4d56b..5983ad4afc2 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 @@ -56,15 +56,14 @@ require('./pipelines_store'); }, /** - * When the component is created the service to fetch the data will be - * initialized with the correct endpoint. + * When the component is about to be mounted, tell the service to fetch the data * * A request to fetch the pipelines will be made. * In case of a successfull response we will store the data in the provided * store, in case of a failed response we need to warn the user. * */ - created() { + beforeMount() { const pipelinesService = new gl.commits.pipelines.PipelinesService(this.endpoint); this.isLoading = true; diff --git a/app/assets/javascripts/merge_request_tabs.js.es6 b/app/assets/javascripts/merge_request_tabs.js.es6 index cc049e00477..da9bd9e9d82 100644 --- a/app/assets/javascripts/merge_request_tabs.js.es6 +++ b/app/assets/javascripts/merge_request_tabs.js.es6 @@ -61,6 +61,7 @@ require('./flash'); constructor({ action, setUrl, stubLocation } = {}) { this.diffsLoaded = false; + this.pipelinesLoaded = false; this.commitsLoaded = false; this.fixedLayoutPref = null; @@ -128,6 +129,13 @@ require('./flash'); $.scrollTo('.merge-request-details .merge-request-tabs', { offset: 0, }); + } else if (action === 'pipelines') { + if (this.pipelinesLoaded) { + return; + } + const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); + gl.commits.pipelines.PipelinesTableBundle.$mount(pipelineTableViewEl); + this.pipelinesLoaded = true; } else { this.expandView(); this.resetViewContainer(); diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml index aae2cb8a04b..33917513f37 100644 --- a/app/views/projects/commit/_pipelines_list.haml +++ b/app/views/projects/commit/_pipelines_list.haml @@ -1,4 +1,7 @@ -#commit-pipeline-table-view{ data: { endpoint: endpoint } } +- disable_initialization = local_assigns.fetch(:disable_initialization, false) +#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization, + endpoint: endpoint, +} } .pipeline-svgs{ data: { "commit_icon_svg" => custom_icon("icon_commit"), "icon_status_canceled" => custom_icon("icon_status_canceled"), "icon_status_running" => custom_icon("icon_status_running"), diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index dd615d3036c..521b0694ca9 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -94,7 +94,7 @@ -# This tab is always loaded via AJAX #pipelines.pipelines.tab-pane - if @pipelines.any? - = render 'projects/commit/pipelines_list', endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) + = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) #diffs.diffs.tab-pane -# This tab is always loaded via AJAX diff --git a/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml b/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml new file mode 100644 index 00000000000..f7bdb62b7f3 --- /dev/null +++ b/changelogs/unreleased/27925-fix-mr-stray-pipelines-api-request.yml @@ -0,0 +1,4 @@ +--- +title: Fix stray pipelines API request when showing MR +merge_request: +author: -- cgit v1.2.1 From 0dfccd995aa9bf560e59299f3a4f1705d6981115 Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sun, 12 Feb 2017 13:02:26 +0500 Subject: Add active_when helper Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/24036 --- app/helpers/application_helper.rb | 9 ++++++++ app/views/admin/logs/show.html.haml | 5 ++--- app/views/admin/projects/index.html.haml | 6 ++--- app/views/admin/users/index.html.haml | 14 ++++++------ app/views/dashboard/_activity_head.html.haml | 4 ++-- app/views/dashboard/todos/index.html.haml | 6 ++--- app/views/devise/shared/_signin_box.html.haml | 2 +- app/views/devise/shared/_tabs_ldap.html.haml | 2 +- app/views/explore/projects/_filter.html.haml | 4 ++-- app/views/kaminari/gitlab/_page.html.haml | 2 +- app/views/projects/pipelines/index.html.haml | 8 +++---- .../projects/wikis/_sidebar_wiki_page.html.haml | 2 +- app/views/search/_category.html.haml | 26 +++++++++++----------- app/views/shared/builds/_tabs.html.haml | 8 +++---- app/views/shared/issuable/_nav.html.haml | 10 ++++----- app/views/snippets/_snippets_scope_menu.html.haml | 8 +++---- spec/helpers/application_helper_spec.rb | 5 +++++ 17 files changed, 66 insertions(+), 55 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bee323993a0..6db813d4a02 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -296,4 +296,13 @@ module ApplicationHelper def page_class "issue-boards-page" if current_controller?(:boards) end + + # Returns active css class when condition returns true + # otherwise returns nil. + # + # Example: + # %li{ class: active_when(params[:filter] == '1') } + def active_when(condition) + 'active' if condition + end end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index 13d00dd1fcb..5e585ce789b 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -8,15 +8,14 @@ %div{ class: container_class } %ul.nav-links.log-tabs - loggers.each do |klass| - %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') }> + %li{ class: active_when(klass == Gitlab::GitLogger) }> = link_to klass::file_name, "##{klass::file_name_noext}", 'data-toggle' => 'tab' .row-content-block To prevent performance issues admin logs output the last 2000 lines .tab-content - loggers.each do |klass| - .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''), - id: klass::file_name_noext } + .tab-pane{ class: active_when(klass == Gitlab::GitLogger), id: klass::file_name_noext } .file-holder#README .js-file-title.file-title %i.fa.fa-file diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index cf8d438670b..756782c7d4e 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -48,13 +48,13 @@ = link_to admin_projects_path do All - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do Private - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do Internal - = nav_link(html_options: { class: params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s ? 'active' : '' }) do + = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do Public diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml index 4dc44225d49..298cf0fa950 100644 --- a/app/views/admin/users/index.html.haml +++ b/app/views/admin/users/index.html.haml @@ -38,31 +38,31 @@ .nav-block %ul.nav-links.wide.scrolling-tabs.white.scrolling-tabs .fade-left - = nav_link(html_options: { class: ('active' unless params[:filter]) }) do + = nav_link(html_options: { class: active_when(params[:filter].nil?) }) do = link_to admin_users_path do Active %small.badge= number_with_delimiter(User.active.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'admins') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'admins') }) do = link_to admin_users_path(filter: "admins") do Admins %small.badge= number_with_delimiter(User.admins.count) - = nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_enabled'} filter-two-factor-enabled" }) do + = nav_link(html_options: { class: "#{active_when(params[:filter] == 'two_factor_enabled')} filter-two-factor-enabled" }) do = link_to admin_users_path(filter: 'two_factor_enabled') do 2FA Enabled %small.badge= number_with_delimiter(User.with_two_factor.count) - = nav_link(html_options: { class: "#{'active' if params[:filter] == 'two_factor_disabled'} filter-two-factor-disabled" }) do + = nav_link(html_options: { class: "#{active_when(params[:filter] == 'two_factor_disabled')} filter-two-factor-disabled" }) do = link_to admin_users_path(filter: 'two_factor_disabled') do 2FA Disabled %small.badge= number_with_delimiter(User.without_two_factor.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'external') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'external') }) do = link_to admin_users_path(filter: 'external') do External %small.badge= number_with_delimiter(User.external.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'blocked') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'blocked') }) do = link_to admin_users_path(filter: "blocked") do Blocked %small.badge= number_with_delimiter(User.blocked.count) - = nav_link(html_options: { class: ('active' if params[:filter] == 'wop') }) do + = nav_link(html_options: { class: active_when(params[:filter] == 'wop') }) do = link_to admin_users_path(filter: "wop") do Without projects %small.badge= number_with_delimiter(User.without_projects.count) diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml index 68a46f61eb7..ecdf76ef5c5 100644 --- a/app/views/dashboard/_activity_head.html.haml +++ b/app/views/dashboard/_activity_head.html.haml @@ -1,8 +1,8 @@ .top-area %ul.nav-links - %li{ class: ("active" unless params[:filter]) }> + %li{ class: active_when(params[:filter].nil?) }> = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do Your Projects - %li{ class: ("active" if params[:filter] == 'starred') }> + %li{ class: active_when(params[:filter] == 'starred') }> = link_to activity_dashboard_path(filter: 'starred'), data: {placement: 'right'} do Starred Projects diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index c4bf2c90cc2..16a5713948a 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -4,15 +4,13 @@ - if current_user.todos.any? .top-area %ul.nav-links - - todo_pending_active = ('active' if params[:state].blank? || params[:state] == 'pending') - %li{ class: "todos-pending #{todo_pending_active}" }> + %li.todos-pending{ class: active_when(params[:state].blank? || params[:state] == 'pending') }> = link_to todos_filter_path(state: 'pending') do %span To do %span.badge = number_with_delimiter(todos_pending_count) - - todo_done_active = ('active' if params[:state] == 'done') - %li{ class: "todos-done #{todo_done_active}" }> + %li.todos-done{ class: active_when(params[:state] == 'done') }> = link_to todos_filter_path(state: 'done') do %span Done diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml index eddfce363a7..da4769e214e 100644 --- a/app/views/devise/shared/_signin_box.html.haml +++ b/app/views/devise/shared/_signin_box.html.haml @@ -4,7 +4,7 @@ .login-body = render 'devise/sessions/new_crowd' - @ldap_servers.each_with_index do |server, i| - .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: (:active if i.zero? && !crowd_enabled?) } + .login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i.zero? && !crowd_enabled?) } .login-body = render 'devise/sessions/new_ldap', server: server - if signin_enabled? diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml index 8c4ad30c832..dd34600490e 100644 --- a/app/views/devise/shared/_tabs_ldap.html.haml +++ b/app/views/devise/shared/_tabs_ldap.html.haml @@ -3,7 +3,7 @@ %li.active = link_to "Crowd", "#crowd", 'data-toggle' => 'tab' - @ldap_servers.each_with_index do |server, i| - %li{ class: (:active if i.zero? && !crowd_enabled?) } + %li{ class: active_when(i.zero? && !crowd_enabled?) } = link_to server['label'], "##{server['provider_name']}", 'data-toggle' => 'tab' - if signin_enabled? %li diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index e3088848492..56f463572bb 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -13,7 +13,7 @@ = link_to filter_projects_path(visibility_level: nil) do Any - Gitlab::VisibilityLevel.values.each do |level| - %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } + %li{ class: active_when(level.to_s == params[:visibility_level]) || 'light' } = link_to filter_projects_path(visibility_level: level) do = visibility_level_icon(level) = visibility_level_label(level) @@ -34,7 +34,7 @@ Any - @tags.each do |tag| - %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' } + %li{ class: active_when(tag.name == params[:tag]) || 'light' } = link_to filter_projects_path(tag: tag.name) do = icon('tag') = tag.name diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml index cefe0066a8f..5c5be03a7cd 100644 --- a/app/views/kaminari/gitlab/_page.html.haml +++ b/app/views/kaminari/gitlab/_page.html.haml @@ -6,5 +6,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li{ class: "page#{' active' if page.current?}#{' sibling' if page.next? || page.prev?}" } +%li.page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?)] } = link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil } diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml index 81e393d7626..6e0428e2a31 100644 --- a/app/views/projects/pipelines/index.html.haml +++ b/app/views/projects/pipelines/index.html.haml @@ -5,23 +5,23 @@ %div{ class: container_class } .top-area %ul.nav-links - %li{ class: ('active' if @scope.nil?) }> + %li{ class: active_when(@scope.nil?) }> = link_to project_pipelines_path(@project) do All %span.badge.js-totalbuilds-count = number_with_delimiter(@pipelines_count) - %li{ class: ('active' if @scope == 'running') }> + %li{ class: active_when(@scope == 'running') }> = link_to project_pipelines_path(@project, scope: :running) do Running %span.badge.js-running-count = number_with_delimiter(@running_or_pending_count) - %li{ class: ('active' if @scope == 'branches') }> + %li{ class: active_when(@scope == 'branches') }> = link_to project_pipelines_path(@project, scope: :branches) do Branches - %li{ class: ('active' if @scope == 'tags') }> + %li{ class: active_when(@scope == 'tags') }> = link_to project_pipelines_path(@project, scope: :tags) do Tags diff --git a/app/views/projects/wikis/_sidebar_wiki_page.html.haml b/app/views/projects/wikis/_sidebar_wiki_page.html.haml index eb9bd14920d..0a61d90177b 100644 --- a/app/views/projects/wikis/_sidebar_wiki_page.html.haml +++ b/app/views/projects/wikis/_sidebar_wiki_page.html.haml @@ -1,3 +1,3 @@ -%li{ class: params[:id] == wiki_page.slug ? 'active' : '' } +%li{ class: active_when(params[:id] == wiki_page.slug) } = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_page) do = wiki_page.title.capitalize diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index 8cbecb725b5..5afb95ac430 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -1,70 +1,70 @@ %ul.nav-links.search-filter - if @project - %li{ class: ("active" if @scope == 'blobs') } + %li{ class: active_when(@scope == 'blobs') } = link_to search_filter_path(scope: 'blobs') do Code %span.badge = @search_results.blobs_count - %li{ class: ("active" if @scope == 'issues') } + %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{ class: ("active" if @scope == 'merge_requests') } + %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{ class: ("active" if @scope == 'milestones') } + %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge = @search_results.milestones_count - %li{ class: ("active" if @scope == 'notes') } + %li{ class: active_when(@scope == 'notes') } = link_to search_filter_path(scope: 'notes') do Comments %span.badge = @search_results.notes_count - %li{ class: ("active" if @scope == 'wiki_blobs') } + %li{ class: active_when(@scope == 'wiki_blobs') } = link_to search_filter_path(scope: 'wiki_blobs') do Wiki %span.badge = @search_results.wiki_blobs_count - %li{ class: ("active" if @scope == 'commits') } + %li{ class: active_when(@scope == 'commits') } = link_to search_filter_path(scope: 'commits') do Commits %span.badge = @search_results.commits_count - elsif @show_snippets - %li{ class: ("active" if @scope == 'snippet_blobs') } + %li{ class: active_when(@scope == 'snippet_blobs') } = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do Snippet Contents %span.badge = @search_results.snippet_blobs_count - %li{ class: ("active" if @scope == 'snippet_titles') } + %li{ class: active_when(@scope == 'snippet_titles') } = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do Titles and Filenames %span.badge = @search_results.snippet_titles_count - else - %li{ class: ("active" if @scope == 'projects') } + %li{ class: active_when(@scope == 'projects') } = link_to search_filter_path(scope: 'projects') do Projects %span.badge = @search_results.projects_count - %li{ class: ("active" if @scope == 'issues') } + %li{ class: active_when(@scope == 'issues') } = link_to search_filter_path(scope: 'issues') do Issues %span.badge = @search_results.issues_count - %li{ class: ("active" if @scope == 'merge_requests') } + %li{ class: active_when(@scope == 'merge_requests') } = link_to search_filter_path(scope: 'merge_requests') do Merge requests %span.badge = @search_results.merge_requests_count - %li{ class: ("active" if @scope == 'milestones') } + %li{ class: active_when(@scope == 'milestones') } = link_to search_filter_path(scope: 'milestones') do Milestones %span.badge diff --git a/app/views/shared/builds/_tabs.html.haml b/app/views/shared/builds/_tabs.html.haml index b6047ece592..3baa956b910 100644 --- a/app/views/shared/builds/_tabs.html.haml +++ b/app/views/shared/builds/_tabs.html.haml @@ -1,23 +1,23 @@ %ul.nav-links - %li{ class: ('active' if scope.nil?) }> + %li{ class: active_when(scope.nil?) }> = link_to build_path_proc.call(nil) do All %span.badge.js-totalbuilds-count = number_with_delimiter(all_builds.count(:id)) - %li{ class: ('active' if scope == 'pending') }> + %li{ class: active_when(scope == 'pending') }> = link_to build_path_proc.call('pending') do Pending %span.badge = number_with_delimiter(all_builds.pending.count(:id)) - %li{ class: ('active' if scope == 'running') }> + %li{ class: active_when(scope == 'running') }> = link_to build_path_proc.call('running') do Running %span.badge = number_with_delimiter(all_builds.running.count(:id)) - %li{ class: ('active' if scope == 'finished') }> + %li{ class: active_when(scope == 'finished') }> = link_to build_path_proc.call('finished') do Finished %span.badge diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml index 1154316c03f..ad995cbe962 100644 --- a/app/views/shared/issuable/_nav.html.haml +++ b/app/views/shared/issuable/_nav.html.haml @@ -3,23 +3,23 @@ - issuables = @issues || @merge_requests %ul.nav-links.issues-state-filters - %li{ class: ("active" if params[:state] == 'opened') }> + %li{ class: active_when(params[:state] == 'opened') }> = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened." do #{issuables_state_counter_text(type, :opened)} - if type == :merge_requests - %li{ class: ("active" if params[:state] == 'merged') }> + %li{ class: active_when(params[:state] == 'merged') }> = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.' do #{issuables_state_counter_text(type, :merged)} - %li{ class: ("active" if params[:state] == 'closed') }> + %li{ class: active_when(params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.' do #{issuables_state_counter_text(type, :closed)} - else - %li{ class: ("active" if params[:state] == 'closed') }> + %li{ class: active_when(params[:state] == 'closed') }> = link_to page_filter_path(state: 'closed', label: true), id: 'state-all', title: 'Filter by issues that are currently closed.' do #{issuables_state_counter_text(type, :closed)} - %li{ class: ("active" if params[:state] == 'all') }> + %li{ class: active_when(params[:state] == 'all') }> = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}." do #{issuables_state_counter_text(type, :all)} diff --git a/app/views/snippets/_snippets_scope_menu.html.haml b/app/views/snippets/_snippets_scope_menu.html.haml index 2dda5fed647..8b6a98a054a 100644 --- a/app/views/snippets/_snippets_scope_menu.html.haml +++ b/app/views/snippets/_snippets_scope_menu.html.haml @@ -2,7 +2,7 @@ - include_private = local_assigns.fetch(:include_private, false) .nav-links.snippet-scope-menu - %li{ class: ("active" unless params[:scope]) } + %li{ class: active_when(params[:scope].nil?) } = link_to subject_snippets_path(subject) do All %span.badge @@ -12,19 +12,19 @@ = subject.snippets.public_and_internal.count - if include_private - %li{ class: ("active" if params[:scope] == "are_private") } + %li{ class: active_when(params[:scope] == "are_private") } = link_to subject_snippets_path(subject, scope: 'are_private') do Private %span.badge = subject.snippets.are_private.count - %li{ class: ("active" if params[:scope] == "are_internal") } + %li{ class: active_when(params[:scope] == "are_internal") } = link_to subject_snippets_path(subject, scope: 'are_internal') do Internal %span.badge = subject.snippets.are_internal.count - %li{ class: ("active" if params[:scope] == "are_public") } + %li{ class: active_when(params[:scope] == "are_public") } = link_to subject_snippets_path(subject, scope: 'are_public') do Public %span.badge diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 8b201f348f1..fd40fe99941 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -265,4 +265,9 @@ describe ApplicationHelper do expect(helper.render_markup('foo.adoc', content)).to eq('NOEL') end end + + describe '#active_when' do + it { expect(helper.active_when(true)).to eq('active') } + it { expect(helper.active_when(false)).to eq(nil) } + end end -- cgit v1.2.1 From a084437b5c1b7226ce64e74bab1972bb1dbd49e0 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Mon, 13 Feb 2017 17:59:57 +0100 Subject: Rename builds to jobs in docs [ci skip] --- doc/administration/build_artifacts.md | 97 +------ doc/administration/job_artifacts.md | 114 ++++++++ doc/ci/README.md | 18 +- doc/ci/build_artifacts/README.md | 5 +- doc/ci/docker/README.md | 4 +- doc/ci/docker/using_docker_build.md | 72 ++--- doc/ci/docker/using_docker_images.md | 22 +- doc/ci/enable_or_disable_ci.md | 2 +- doc/ci/environments.md | 10 +- doc/ci/examples/deployment/README.md | 2 +- doc/ci/examples/deployment/composer-npm-deploy.md | 4 +- doc/ci/examples/php.md | 12 +- doc/ci/examples/test-scala-application.md | 8 +- doc/ci/git_submodules.md | 18 +- doc/ci/pipelines.md | 28 +- doc/ci/quick_start/README.md | 78 +++--- doc/ci/quick_start/img/build_log.png | Bin 24461 -> 35261 bytes doc/ci/quick_start/img/builds_status.png | Bin 24278 -> 19127 bytes doc/ci/quick_start/img/new_commit.png | Bin 4772 -> 5584 bytes doc/ci/quick_start/img/pipelines_status.png | Bin 25494 -> 22872 bytes doc/ci/quick_start/img/runners_activated.png | Bin 12337 -> 18215 bytes .../img/single_commit_status_pending.png | Bin 15785 -> 13631 bytes doc/ci/quick_start/img/status_pending.png | Bin 9521 -> 0 bytes doc/ci/runners/README.md | 139 +++++----- doc/ci/services/mysql.md | 4 +- doc/ci/services/postgres.md | 4 +- doc/ci/services/redis.md | 2 +- doc/ci/ssh_keys/README.md | 6 +- doc/ci/triggers/README.md | 59 +++-- doc/ci/triggers/img/builds_page.png | Bin 29044 -> 20383 bytes doc/ci/triggers/img/trigger_single_build.png | Bin 8233 -> 6585 bytes doc/ci/triggers/img/trigger_variables.png | Bin 3652 -> 3637 bytes doc/ci/triggers/img/triggers_page.png | Bin 5119 -> 5116 bytes doc/ci/variables/README.md | 60 ++--- doc/ci/yaml/README.md | 295 +++++++++++---------- doc/development/code_review.md | 2 +- doc/install/installation.md | 2 +- doc/raketasks/backup_restore.md | 4 +- doc/university/glossary/README.md | 74 +++--- .../admin_area/settings/continuous_integration.md | 6 +- doc/user/permissions.md | 22 +- doc/user/project/builds/artifacts.md | 137 +--------- .../project/builds/img/build_artifacts_browser.png | Bin 3782 -> 0 bytes .../builds/img/build_artifacts_browser_button.png | Bin 4891 -> 0 bytes .../builds/img/build_artifacts_builds_page.png | Bin 22022 -> 0 bytes .../builds/img/build_artifacts_pipelines_page.png | Bin 28339 -> 0 bytes .../builds/img/build_latest_artifacts_browser.png | Bin 10551 -> 0 bytes doc/user/project/integrations/webhooks.md | 2 +- doc/user/project/merge_requests/index.md | 2 +- .../merge_requests/merge_when_pipeline_succeeds.md | 12 +- doc/user/project/new_ci_build_permissions_model.md | 82 +++--- doc/user/project/pages/index.md | 10 +- .../pipelines/img/job_artifacts_browser.png | Bin 0 -> 3771 bytes .../pipelines/img/job_artifacts_browser_button.png | Bin 0 -> 5534 bytes .../pipelines/img/job_artifacts_builds_page.png | Bin 0 -> 15191 bytes .../pipelines/img/job_artifacts_pipelines_page.png | Bin 0 -> 16550 bytes .../pipelines/img/job_latest_artifacts_browser.png | Bin 0 -> 10551 bytes .../img/pipelines_settings_test_coverage.png | Bin 2603 -> 2549 bytes .../img/pipelines_test_coverage_mr_widget.png | Bin 6391 -> 6375 bytes doc/user/project/pipelines/job_artifacts.md | 118 +++++++++ doc/user/project/pipelines/settings.md | 16 +- doc/user/project/slash_commands.md | 2 +- doc/workflow/shortcuts.md | 4 +- doc/workflow/todos.md | 4 +- 64 files changed, 789 insertions(+), 773 deletions(-) create mode 100644 doc/administration/job_artifacts.md delete mode 100644 doc/ci/quick_start/img/status_pending.png delete mode 100644 doc/user/project/builds/img/build_artifacts_browser.png delete mode 100644 doc/user/project/builds/img/build_artifacts_browser_button.png delete mode 100644 doc/user/project/builds/img/build_artifacts_builds_page.png delete mode 100644 doc/user/project/builds/img/build_artifacts_pipelines_page.png delete mode 100644 doc/user/project/builds/img/build_latest_artifacts_browser.png create mode 100644 doc/user/project/pipelines/img/job_artifacts_browser.png create mode 100644 doc/user/project/pipelines/img/job_artifacts_browser_button.png create mode 100644 doc/user/project/pipelines/img/job_artifacts_builds_page.png create mode 100644 doc/user/project/pipelines/img/job_artifacts_pipelines_page.png create mode 100644 doc/user/project/pipelines/img/job_latest_artifacts_browser.png create mode 100644 doc/user/project/pipelines/job_artifacts.md diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md index cca422892ec..5a892a35fcc 100644 --- a/doc/administration/build_artifacts.md +++ b/doc/administration/build_artifacts.md @@ -1,96 +1 @@ -# Build artifacts administration - ->**Notes:** ->- Introduced in GitLab 8.2 and GitLab Runner 0.7.0. ->- Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format - changed to `ZIP`. ->- This is the administration documentation. For the user guide see - [user/project/builds/artifacts.md](../user/project/builds/artifacts.md). - -Artifacts is a list of files and directories which are attached to a build -after it completes successfully. This feature is enabled by default in all -GitLab installations. Keep reading if you want to know how to disable it. - -## Disabling build artifacts - -To disable artifacts site-wide, follow the steps below. - ---- - -**In Omnibus installations:** - -1. Edit `/etc/gitlab/gitlab.rb` and add the following line: - - ```ruby - gitlab_rails['artifacts_enabled'] = false - ``` - -1. Save the file and [reconfigure GitLab][] for the changes to take effect. - ---- - -**In installations from source:** - -1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - - ```yaml - artifacts: - enabled: false - ``` - -1. Save the file and [restart GitLab][] for the changes to take effect. - -## Storing build artifacts - -After a successful build, GitLab Runner uploads an archive containing the build -artifacts to GitLab. - -To change the location where the artifacts are stored, follow the steps below. - ---- - -**In Omnibus installations:** - -_The artifacts are stored by default in -`/var/opt/gitlab/gitlab-rails/shared/artifacts`._ - -1. To change the storage path for example to `/mnt/storage/artifacts`, edit - `/etc/gitlab/gitlab.rb` and add the following line: - - ```ruby - gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts" - ``` - -1. Save the file and [reconfigure GitLab][] for the changes to take effect. - ---- - -**In installations from source:** - -_The artifacts are stored by default in -`/home/git/gitlab/shared/artifacts`._ - -1. To change the storage path for example to `/mnt/storage/artifacts`, edit - `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: - - ```yaml - artifacts: - enabled: true - path: /mnt/storage/artifacts - ``` - -1. Save the file and [restart GitLab][] for the changes to take effect. - -## Set the maximum file size of the artifacts - -Provided the artifacts are enabled, you can change the maximum file size of the -artifacts through the [Admin area settings](../user/admin_area/settings/continuous_integration.md#maximum-artifacts-size). - -[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab" -[restart gitlab]: restart_gitlab.md "How to restart GitLab" - -## Storage statistics - -You can see the total storage used for build artifacts on groups and projects -in the administration area, as well as through the [groups](../api/groups.md) -and [projects APIs](../api/projects.md). +This document was moved to [jobs_artifacts](jobs_artifacts.md). diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md new file mode 100644 index 00000000000..7b0610ae414 --- /dev/null +++ b/doc/administration/job_artifacts.md @@ -0,0 +1,114 @@ +# Jobs artifacts administration + +>**Notes:** +>- Introduced in GitLab 8.2 and GitLab Runner 0.7.0. +>- Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format + changed to `ZIP`. +>- Starting with GitLab 8.17, builds are renamed to jobs. +>- This is the administration documentation. For the user guide see + [pipelines/job_artifacts](../user/project/pipelines/job_artifacts.md). + +Artifacts is a list of files and directories which are attached to a job +after it completes successfully. This feature is enabled by default in all +GitLab installations. Keep reading if you want to know how to disable it. + +## Disabling job artifacts + +To disable artifacts site-wide, follow the steps below. + +--- + +**In Omnibus installations:** + +1. Edit `/etc/gitlab/gitlab.rb` and add the following line: + + ```ruby + gitlab_rails['artifacts_enabled'] = false + ``` + +1. Save the file and [reconfigure GitLab][] for the changes to take effect. + +--- + +**In installations from source:** + +1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: + + ```yaml + artifacts: + enabled: false + ``` + +1. Save the file and [restart GitLab][] for the changes to take effect. + +## Storing job artifacts + +After a successful job, GitLab Runner uploads an archive containing the job +artifacts to GitLab. + +To change the location where the artifacts are stored, follow the steps below. + +--- + +**In Omnibus installations:** + +_The artifacts are stored by default in +`/var/opt/gitlab/gitlab-rails/shared/artifacts`._ + +1. To change the storage path for example to `/mnt/storage/artifacts`, edit + `/etc/gitlab/gitlab.rb` and add the following line: + + ```ruby + gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts" + ``` + +1. Save the file and [reconfigure GitLab][] for the changes to take effect. + +--- + +**In installations from source:** + +_The artifacts are stored by default in +`/home/git/gitlab/shared/artifacts`._ + +1. To change the storage path for example to `/mnt/storage/artifacts`, edit + `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines: + + ```yaml + artifacts: + enabled: true + path: /mnt/storage/artifacts + ``` + +1. Save the file and [restart GitLab][] for the changes to take effect. + +## Set the maximum file size of the artifacts + +Provided the artifacts are enabled, you can change the maximum file size of the +artifacts through the [Admin area settings](../user/admin_area/settings/continuous_integration.md#maximum-artifacts-size). + +## Storage statistics + +You can see the total storage used for job artifacts on groups and projects +in the administration area, as well as through the [groups](../api/groups.md) +and [projects APIs](../api/projects.md). + +## Implementation details + +When GitLab receives an artifacts archive, an archive metadata file is also +generated. This metadata file describes all the entries that are located in the +artifacts archive itself. The metadata file is in a binary format, with +additional GZIP compression. + +GitLab does not extract the artifacts archive in order to save space, memory +and disk I/O. It instead inspects the metadata file which contains all the +relevant information. This is especially important when there is a lot of +artifacts, or an archive is a very large file. + +When clicking on a specific file, [GitLab Workhorse] extracts it +from the archive and the download begins. This implementation saves space, +memory and disk I/O. + +[reconfigure gitlab]: restart_gitlab.md "How to restart GitLab" +[restart gitlab]: restart_gitlab.md "How to restart GitLab" +[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository" diff --git a/doc/ci/README.md b/doc/ci/README.md index dd14698e9cd..cbab7c9f18d 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -2,22 +2,22 @@ ## CI User documentation -- [Get started with GitLab CI](quick_start/README.md) +- [Getting started with GitLab CI](quick_start/README.md) - [CI examples for various languages](examples/README.md) - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md) -- [Pipelines and builds](pipelines.md) +- [Pipelines and jobs](pipelines.md) - [Environments and deployments](environments.md) - [Learn how `.gitlab-ci.yml` works](yaml/README.md) -- [Configure a Runner, the application that runs your builds](runners/README.md) +- [Configure a Runner, the application that runs your jobs](runners/README.md) - [Use Docker images with GitLab Runner](docker/using_docker_images.md) - [Use CI to build Docker images](docker/using_docker_build.md) - [CI Variables](variables/README.md) - Learn how to use variables defined in your `.gitlab-ci.yml` or secured ones defined in your project's settings - [Use SSH keys in your build environment](ssh_keys/README.md) -- [Trigger builds through the API](triggers/README.md) -- [Build artifacts](../user/project/builds/artifacts.md) +- [Trigger jobs through the API](triggers/README.md) +- [Job artifacts](../user/project/pipelines/job_artifacts.md) - [User permissions](../user/permissions.md#gitlab-ci) -- [Build permissions](../user/permissions.md#build-permissions) +- [Jobs permissions](../user/permissions.md#jobs-permissions) - [API](../api/ci/README.md) - [CI services (linked docker containers)](services/README.md) - [CI/CD pipelines settings](../user/project/pipelines/settings.md) @@ -27,6 +27,6 @@ ## Breaking changes -- [New CI build permissions model](../user/project/new_ci_build_permissions_model.md) - Read about what changed in GitLab 8.12 and how that affects your builds. - There's a new way to access your Git submodules and LFS objects in builds. +- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md) + Read about what changed in GitLab 8.12 and how that affects your jobs. + There's a new way to access your Git submodules and LFS objects in jobs. diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md index 05605f10fb4..e7692b8b9a2 100644 --- a/doc/ci/build_artifacts/README.md +++ b/doc/ci/build_artifacts/README.md @@ -1,4 +1 @@ -This document was moved to: - -- [user/project/builds/artifacts.md](../../user/project/builds/artifacts.md) - user guide -- [administration/build_artifacts.md](../../administration/build_artifacts.md) - administrator guide +This document was moved to [user/project/job_artifacts.md](../../user/project/job_artifacts.md). diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md index 84eaf29efd1..99669a9272a 100644 --- a/doc/ci/docker/README.md +++ b/doc/ci/docker/README.md @@ -1,4 +1,4 @@ # Docker integration -+ [Using Docker Images](using_docker_images.md) -+ [Using Docker Build](using_docker_build.md) \ No newline at end of file +- [Using Docker Images](using_docker_images.md) +- [Using Docker Build](using_docker_build.md) diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 28141cced3b..2b3082acd5d 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -12,6 +12,7 @@ One of the new trends in Continuous Integration/Deployment is to: 1. deploy to a server from the pushed image. It's also useful when your application already has the `Dockerfile` that can be used to create and test an image: + ```bash $ docker build -t my-image dockerfiles/ $ docker run my-docker-image /script/to/run/tests @@ -19,23 +20,23 @@ $ docker tag my-image my-registry:5000/my-image $ docker push my-registry:5000/my-image ``` -This requires special configuration of GitLab Runner to enable `docker` support during builds. +This requires special configuration of GitLab Runner to enable `docker` support during jobs. ## Runner Configuration -There are three methods to enable the use of `docker build` and `docker run` during builds; each with their own tradeoffs. +There are three methods to enable the use of `docker build` and `docker run` during jobs; each with their own tradeoffs. ### Use shell executor The simplest approach is to install GitLab Runner in `shell` execution mode. -GitLab Runner then executes build scripts as the `gitlab-runner` user. +GitLab Runner then executes job scripts as the `gitlab-runner` user. 1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation). -1. During GitLab Runner installation select `shell` as method of executing build scripts or use command: +1. During GitLab Runner installation select `shell` as method of executing job scripts or use command: ```bash - $ sudo gitlab-ci-multi-runner register -n \ + sudo gitlab-ci-multi-runner register -n \ --url https://gitlab.com/ci \ --registration-token REGISTRATION_TOKEN \ --executor shell \ @@ -50,16 +51,17 @@ GitLab Runner then executes build scripts as the `gitlab-runner` user. 3. Add `gitlab-runner` user to `docker` group: ```bash - $ sudo usermod -aG docker gitlab-runner + sudo usermod -aG docker gitlab-runner ``` 4. Verify that `gitlab-runner` has access to Docker: ```bash - $ sudo -u gitlab-runner -H docker info + sudo -u gitlab-runner -H docker info ``` You can now verify that everything works by adding `docker info` to `.gitlab-ci.yml`: + ```yaml before_script: - docker info @@ -80,12 +82,12 @@ For more information please read [On Docker security: `docker` group considered The second approach is to use the special docker-in-docker (dind) [Docker image](https://hub.docker.com/_/docker/) with all tools installed -(`docker` and `docker-compose`) and run the build script in context of that +(`docker` and `docker-compose`) and run the job script in context of that image in privileged mode. In order to do that, follow the steps: -1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation). +1. Install [GitLab Runner](https://docs.gitlab.com/runner/install). 1. Register GitLab Runner from the command line to use `docker` and `privileged` mode: @@ -155,10 +157,10 @@ not without its own challenges: escalation which can lead to container breakout. For more information, check out the official Docker documentation on [Runtime privilege and Linux capabilities][docker-cap]. -- Using docker-in-docker, each build is in a clean environment without the past - history. Concurrent builds work fine because every build gets it's own +- When using docker-in-docker, each job is in a clean environment without the past + history. Concurrent jobs work fine because every build gets it's own instance of Docker engine so they won't conflict with each other. But this - also means builds can be slower because there's no caching of layers. + also means jobs can be slower because there's no caching of layers. - By default, `docker:dind` uses `--storage-driver vfs` which is the slowest form offered. To use a different driver, see [Using the overlayfs driver](#using-the-overlayfs-driver). @@ -171,7 +173,7 @@ The third approach is to bind-mount `/var/run/docker.sock` into the container so In order to do that, follow the steps: -1. Install [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation). +1. Install [GitLab Runner](https://docs.gitlab.com/runner/install). 1. Register GitLab Runner from the command line to use `docker` and share `/var/run/docker.sock`: @@ -187,7 +189,9 @@ In order to do that, follow the steps: The above command will register a new Runner to use the special `docker:latest` image which is provided by Docker. **Notice that it's using - the Docker daemon of the Runner itself, and any containers spawned by docker commands will be siblings of the Runner rather than children of the runner.** This may have complications and limitations that are unsuitable for your workflow. + the Docker daemon of the Runner itself, and any containers spawned by docker + commands will be siblings of the Runner rather than children of the runner.** + This may have complications and limitations that are unsuitable for your workflow. The above command will create a `config.toml` entry similar to this: @@ -206,7 +210,8 @@ In order to do that, follow the steps: Insecure = false ``` -1. You can now use `docker` in the build script (note that you don't need to include the `docker:dind` service as when using the Docker in Docker executor): +1. You can now use `docker` in the build script (note that you don't need to + include the `docker:dind` service as when using the Docker in Docker executor): ```yaml image: docker:latest @@ -221,18 +226,23 @@ In order to do that, follow the steps: - docker run my-docker-image /script/to/run/tests ``` -While the above method avoids using Docker in privileged mode, you should be aware of the following implications: -* By sharing the docker daemon, you are effectively disabling all -the security mechanisms of containers and exposing your host to privilege -escalation which can lead to container breakout. For example, if a project -ran `docker rm -f $(docker ps -a -q)` it would remove the GitLab Runner -containers. -* Concurrent builds may not work; if your tests -create containers with specific names, they may conflict with each other. -* Sharing files and directories from the source repo into containers may not -work as expected since volume mounting is done in the context of the host -machine, not the build container. -e.g. `docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests` +While the above method avoids using Docker in privileged mode, you should be +aware of the following implications: + +- By sharing the docker daemon, you are effectively disabling all + the security mechanisms of containers and exposing your host to privilege + escalation which can lead to container breakout. For example, if a project + ran `docker rm -f $(docker ps -a -q)` it would remove the GitLab Runner + containers. +- Concurrent jobs may not work; if your tests + create containers with specific names, they may conflict with each other. +- Sharing files and directories from the source repo into containers may not + work as expected since volume mounting is done in the context of the host + machine, not the build container, e.g.: + + ``` + docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests + ``` ## Using the OverlayFS driver @@ -299,7 +309,7 @@ push to the Registry connected to your project. Its password is provided in the of your Docker images. Here's a more elaborate example that splits up the tasks into 4 pipeline stages, -including two tests that run in parallel. The build is stored in the container +including two tests that run in parallel. The `build` is stored in the container registry and used by subsequent stages, downloading the image when needed. Changes to `master` also get tagged as `latest` and deployed using an application-specific deploy script: @@ -360,17 +370,17 @@ deploy: Some things you should be aware of when using the Container Registry: - You must log in to the container registry before running commands. Putting - this in `before_script` will run it before each build job. + this in `before_script` will run it before each job. - Using `docker build --pull` makes sure that Docker fetches any changes to base images before building just in case your cache is stale. It takes slightly longer, but means you don’t get stuck without security patches to base images. - Doing an explicit `docker pull` before each `docker run` makes sure to fetch the latest image that was just built. This is especially important if you are using multiple runners that cache images locally. Using the git SHA in your - image tag makes this less necessary since each build will be unique and you + image tag makes this less necessary since each job will be unique and you shouldn't ever have a stale image, but it's still possible if you re-build a given commit after a dependency has changed. -- You don't want to build directly to `latest` in case there are multiple builds +- You don't want to build directly to `latest` in case there are multiple jobs happening simultaneously. [docker-in-docker]: https://blog.docker.com/2013/09/docker-can-now-run-within-docker/ diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md index aba77490915..9dee61bfa1f 100644 --- a/doc/ci/docker/using_docker_images.md +++ b/doc/ci/docker/using_docker_images.md @@ -8,7 +8,7 @@ run applications in independent "containers" that are run within a single Linux instance. [Docker Hub][hub] has a rich database of pre-built images that can be used to test and build your applications. -Docker, when used with GitLab CI, runs each build in a separate and isolated +Docker, when used with GitLab CI, runs each job in a separate and isolated container using the predefined image that is set up in [`.gitlab-ci.yml`](../yaml/README.md). @@ -45,12 +45,12 @@ can be found at [Docker Hub][hub]. For more information about images and Docker Hub please read the [Docker Fundamentals][] documentation. In short, with `image` we refer to the docker image, which will be used to -create a container on which your build will run. +create a container on which your job will run. ## What is a service The `services` keyword defines just another docker image that is run during -your build and is linked to the docker image that the `image` keyword defines. +your job and is linked to the docker image that the `image` keyword defines. This allows you to access the service image during build time. The service image can run any application, but the most common use case is to @@ -61,13 +61,13 @@ time the project is built. You can see some widely used services examples in the relevant documentation of [CI services examples](../services/README.md). -### How services are linked to the build +### How services are linked to the job To better understand how the container linking works, read [Linking containers together][linking-containers]. To summarize, if you add `mysql` as service to your application, the image will -then be used to create a container that is linked to the build container. +then be used to create a container that is linked to the job container. The service container for MySQL will be accessible under the hostname `mysql`. So, in order to access your database service you have to connect to the host @@ -133,7 +133,7 @@ Look for the `[runners.docker]` section: services = ["mysql:latest", "postgres:latest"] ``` -The image and services defined this way will be added to all builds run by +The image and services defined this way will be added to all job run by that runner. ## Define an image from a private Docker registry @@ -167,7 +167,7 @@ services: - tutum/wordpress:latest ``` -When the build is run, `tutum/wordpress` will be started and you will have +When the job is run, `tutum/wordpress` will be started and you will have access to it from your build container under the hostname `tutum__wordpress`. The alias hostname for the service is made from the image name following these @@ -202,21 +202,21 @@ See the specific documentation for ## How Docker integration works -Below is a high level overview of the steps performed by docker during build +Below is a high level overview of the steps performed by docker during job time. 1. Create any service container: `mysql`, `postgresql`, `mongodb`, `redis`. 1. Create cache container to store all volumes as defined in `config.toml` and `Dockerfile` of build image (`ruby:2.1` as in above example). 1. Create build container and link any service container to build container. -1. Start build container and send build script to the container. -1. Run build script. +1. Start build container and send job script to the container. +1. Run job script. 1. Checkout code in: `/builds/group-name/project-name/`. 1. Run any step defined in `.gitlab-ci.yml`. 1. Check exit status of build script. 1. Remove build container and all created service containers. -## How to debug a build locally +## How to debug a job locally *Note: The following commands are run without root privileges. You should be able to run docker with your regular user account.* diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md index 7971daf2637..796a025b951 100644 --- a/doc/ci/enable_or_disable_ci.md +++ b/doc/ci/enable_or_disable_ci.md @@ -12,7 +12,7 @@ API. --- GitLab CI is exposed via the `/pipelines` and `/builds` pages of a project. -Disabling GitLab CI in a project does not delete any previous builds. +Disabling GitLab CI in a project does not delete any previous jobs. In fact, the `/pipelines` and `/builds` pages can still be accessed, although it's hidden from the left sidebar menu. diff --git a/doc/ci/environments.md b/doc/ci/environments.md index cb62ed723f0..3bba94f8b83 100644 --- a/doc/ci/environments.md +++ b/doc/ci/environments.md @@ -75,7 +75,7 @@ We have defined 3 [stages](yaml/README.md#stages): - deploy The jobs assigned to these stages will run in this order. If a job fails, then -the builds that are assigned to the next stage won't run, rendering the pipeline +the jobs that are assigned to the next stage won't run, rendering the pipeline as failed. In our case, the `test` job will run first, then the `build` and lastly the `deploy_staging`. With this, we ensure that first the tests pass, then our app is able to be built successfully, and lastly we deploy to the @@ -119,7 +119,7 @@ There's a bunch of information there, specifically you can see: - The environment's name with a link to its deployments - The last deployment ID number and who performed it -- The build ID of the last deployment with its respective job name +- The job ID of the last deployment with its respective job name - The commit information of the last deployment such as who committed, to what branch and the Git SHA of the commit - The exact time the last deployment was performed @@ -219,9 +219,9 @@ deploy_prod: The `when: manual` action exposes a play button in GitLab's UI and the `deploy_prod` job will only be triggered if and when we click that play button. -You can find it in the pipeline, build, environment, and deployment views. +You can find it in the pipeline, job, environment, and deployment views. -| Pipelines | Single pipeline | Environments | Deployments | Builds | +| Pipelines | Single pipeline | Environments | Deployments | jobs | | --------- | ----------------| ------------ | ----------- | -------| | ![Pipelines manual action](img/environments_manual_action_pipelines.png) | ![Pipelines manual action](img/environments_manual_action_single_pipeline.png) | ![Environments manual action](img/environments_manual_action_environments.png) | ![Deployments manual action](img/environments_manual_action_deployments.png) | ![Builds manual action](img/environments_manual_action_builds.png) | @@ -419,7 +419,7 @@ Behind the scenes: - GitLab Runner picks up the changes and starts running the jobs - The jobs run sequentially as defined in `stages` - First, the tests pass - - Then, the build begins and successfully also passes + - Then, the job begins and successfully also passes - Lastly, the app is deployed to an environment with a name specific to the branch diff --git a/doc/ci/examples/deployment/README.md b/doc/ci/examples/deployment/README.md index 7d91ce6710f..d28aa282825 100644 --- a/doc/ci/examples/deployment/README.md +++ b/doc/ci/examples/deployment/README.md @@ -91,7 +91,7 @@ Secure Variables can added by going to `Project > Variables > Add Variable`. **This feature requires `gitlab-runner` with version equal or greater than 0.4.0.** The variables that are defined in the project settings are sent along with the build script to the runner. The secure variables are stored out of the repository. Never store secrets in your projects' .gitlab-ci.yml. -It is also important that secret's value is hidden in the build log. +It is also important that secret's value is hidden in the job log. You access added variable by prefixing it's name with `$` (on non-Windows runners) or `%` (for Windows Batch runners): 1. `$SECRET_VARIABLE` - use it for non-Windows runners diff --git a/doc/ci/examples/deployment/composer-npm-deploy.md b/doc/ci/examples/deployment/composer-npm-deploy.md index 5334a73e1f5..8b0d8a003fd 100644 --- a/doc/ci/examples/deployment/composer-npm-deploy.md +++ b/doc/ci/examples/deployment/composer-npm-deploy.md @@ -65,7 +65,7 @@ In order, this means that: 1. We check if the `ssh-agent` is available and we install it if it's not; 2. We create the `~/.ssh` folder; 3. We make sure we're running bash; -4. We disable host checking (we don't ask for user accept when we first connect to a server; and since every build will equal a first connect, we kind of need this) +4. We disable host checking (we don't ask for user accept when we first connect to a server; and since every job will equal a first connect, we kind of need this) And this is basically all you need in the `before_script` section. @@ -153,4 +153,4 @@ stage_deploy: - scp -P22 -r build/* server_user@server_host:htdocs/wp-content/themes/_tmp - ssh -p22 server_user@server_host "mv htdocs/wp-content/themes/live htdocs/wp-content/themes/_old && mv htdocs/wp-content/themes/_tmp htdocs/wp-content/themes/live" - ssh -p22 server_user@server_host "rm -rf htdocs/wp-content/themes/_old" -``` \ No newline at end of file +``` diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md index 5eeec92d976..f2dd12b67d3 100644 --- a/doc/ci/examples/php.md +++ b/doc/ci/examples/php.md @@ -15,10 +15,10 @@ This will allow us to test PHP projects against different versions of PHP. However, not everything is plug 'n' play, you still need to configure some things manually. -As with every build, you need to create a valid `.gitlab-ci.yml` describing the +As with every job, you need to create a valid `.gitlab-ci.yml` describing the build environment. -Let's first specify the PHP image that will be used for the build process +Let's first specify the PHP image that will be used for the job process (you can read more about what an image means in the Runner's lingo reading about [Using Docker images](../docker/using_docker_images.md#what-is-image)). @@ -58,8 +58,8 @@ docker-php-ext-install pdo_mysql ``` You might wonder what `docker-php-ext-install` is. In short, it is a script -provided by the official php docker image that you can use to easilly install -extensions. For more information read the the documentation at +provided by the official php docker image that you can use to easily install +extensions. For more information read the documentation at . Now that we created the script that contains all prerequisites for our build @@ -142,7 +142,7 @@ Of course, `my_php.ini` must be present in the root directory of your repository ## Test PHP projects using the Shell executor -The shell executor runs your builds in a terminal session on your server. +The shell executor runs your job in a terminal session on your server. Thus, in order to test your projects you first need to make sure that all dependencies are installed. @@ -280,7 +280,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few -moments the changes will be picked by a public runner and the build will begin. +moments the changes will be picked by a public runner and the job will begin. [php-hub]: https://hub.docker.com/r/_/php/ [phpenv]: https://github.com/phpenv/phpenv diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md index 85f8849fa99..01c13941c21 100644 --- a/doc/ci/examples/test-scala-application.md +++ b/doc/ci/examples/test-scala-application.md @@ -51,14 +51,14 @@ The `deploy` stage automatically deploys the project to Heroku using dpl. You can use other versions of Scala and SBT by defining them in `build.sbt`. -## Display test coverage in build +## Display test coverage in job Add the `Coverage was \[\d+.\d+\%\]` regular expression in the -**Settings ➔ Edit Project ➔ Test coverage parsing** project setting to +**Settings ➔ CI/CD Pipelines ➔ Coverage report** project setting to retrieve the [test coverage] rate from the build trace and have it -displayed with your builds. +displayed with your jobs. -**Builds** must be enabled for this option to appear. +**Pipelines** must be enabled for this option to appear. ## Heroku application diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md index 869743ce80a..36c6e153d95 100644 --- a/doc/ci/git_submodules.md +++ b/doc/ci/git_submodules.md @@ -1,14 +1,14 @@ # Using Git submodules with GitLab CI > **Notes:** -- GitLab 8.12 introduced a new [CI build permissions model][newperms] and you +- GitLab 8.12 introduced a new [CI job permissions model][newperms] and you are encouraged to upgrade your GitLab instance if you haven't done already. If you are **not** using GitLab 8.12 or higher, you would need to work your way around submodules in order to access the sources of e.g., `gitlab.com/group/project` with the use of [SSH keys](ssh_keys/README.md). -- With GitLab 8.12 onward, your permissions are used to evaluate what a CI build +- With GitLab 8.12 onward, your permissions are used to evaluate what a CI job can access. More information about how this system works can be found in the - [Build permissions model](../user/permissions.md#builds-permissions). + [Jobs permissions model](../user/permissions.md#jobs-permissions). - The HTTP(S) Git protocol [must be enabled][gitpro] in your GitLab instance. ## Configuring the `.gitmodules` file @@ -27,7 +27,7 @@ Let's consider the following example: If you are using GitLab 8.12+ and your submodule is on the same GitLab server, you must update your `.gitmodules` file to use **relative URLs**. Since Git allows the usage of relative URLs for your `.gitmodules` configuration, -this easily allows you to use HTTP(S) for cloning all your CI builds and SSH +this easily allows you to use HTTP(S) for cloning all your CI jobs and SSH for all your local checkouts. The `.gitmodules` would look like: ```ini @@ -38,7 +38,7 @@ for all your local checkouts. The `.gitmodules` would look like: The above configuration will instruct Git to automatically deduce the URL that should be used when cloning sources. Whether you use HTTP(S) or SSH, Git will use -that same channel and it will allow to make all your CI builds use HTTP(S) +that same channel and it will allow to make all your CI jobs use HTTP(S) (because GitLab CI only uses HTTP(S) for cloning your sources), and all your local clones will continue using SSH. @@ -57,13 +57,13 @@ Once `.gitmodules` is correctly configured, you can move on to ## Using Git submodules in your CI jobs There are a few steps you need to take in order to make submodules work -correctly with your CI builds: +correctly with your CI jobs: 1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file) for the submodules located in the same GitLab server. 1. Next, if you are using `gitlab-ci-multi-runner` v1.10+, you can set the `GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` to tell - the runner to fetch your submodules before the build: + the runner to fetch your submodules before the job: ```yaml variables: GIT_SUBMODULE_STRATEGY: recursive @@ -87,9 +87,9 @@ The rationale to set the `sync` and `update` in `before_script` is because of the way Git submodules work. On a fresh Runner workspace, Git will set the submodule URL including the token in `.git/config` (or `.git/modules//config`) based on `.gitmodules` and the current -remote URL. On subsequent builds on the same Runner, `.git/config` is cached +remote URL. On subsequent jobs on the same Runner, `.git/config` is cached and already contains a full URL for the submodule, corresponding to the previous -build, and to **a token from a previous build**. `sync` allows to force updating +job, and to **a token from a previous job**. `sync` allows to force updating the full URL. [gitpro]: ../user/admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index ab289876252..3134405e10b 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -1,14 +1,14 @@ -# Introduction to pipelines and builds +# Introduction to pipelines and jobs >**Note:** Introduced in GitLab 8.8. ## Pipelines -A pipeline is a group of [builds][] that get executed in [stages][](batches). -All of the builds in a stage are executed in parallel (if there are enough +A pipeline is a group of [jobs][] that get executed in [stages][](batches). +All of the jobs in a stage are executed in parallel (if there are enough concurrent [Runners]), and if they all succeed, the pipeline moves on to the -next stage. If one of the builds fails, the next stage is not (usually) +next stage. If one of the jobs fails, the next stage is not (usually) executed. ![Pipelines example](img/pipelines.png) @@ -35,10 +35,10 @@ Example continuous delivery flow: ![CD Flow](img/pipelines-goal.svg) -## Builds +## Jobs -Builds are individual runs of [jobs]. Not to be confused with a `build` job or -`build` stage. +Jobs can be defined in the [`.gitlab-ci.yml`][jobs-yaml] file. Not to be +confused with a `build` job or `build` stage. ## Defining pipelines @@ -52,11 +52,11 @@ See full [documentation](yaml/README.md#jobs). You can find the current and historical pipeline runs under **Pipelines** for your project. -## Seeing build status +## Seeing job status -Clicking on a pipeline will show the builds that were run for that pipeline. -Clicking on an individual build will show you its build trace, and allow you to -cancel the build, retry it, or erase the build trace. +Clicking on a pipeline will show the jobs that were run for that pipeline. +Clicking on an individual job will show you its job trace, and allow you to +cancel the job, retry it, or erase the job trace. ## How the pipeline duration is calculated @@ -91,11 +91,11 @@ total running time should be: ## Badges -Build status and test coverage report badges are available. You can find their +Job status and test coverage report badges are available. You can find their respective link in the [Pipelines settings] page. -[builds]: #builds -[jobs]: yaml/README.md#jobs +[jobs]: #jobs +[jobs-yaml]: yaml/README.md#jobs [stages]: yaml/README.md#stages [runners]: runners/README.html [pipelines settings]: ../user/project/pipelines/settings.md diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md index 1104edaabe9..2a5401ac13a 100644 --- a/doc/ci/quick_start/README.md +++ b/doc/ci/quick_start/README.md @@ -1,4 +1,4 @@ -# Quick Start +# Getting started with GitLab CI >**Note:** Starting from version 8.0, GitLab [Continuous Integration][ci] (CI) is fully integrated into GitLab itself and is [enabled] by default on all @@ -7,7 +7,7 @@ projects. GitLab offers a [continuous integration][ci] service. If you [add a `.gitlab-ci.yml` file][yaml] to the root directory of your repository, and configure your GitLab project to use a [Runner], then each merge request or -push triggers your CI [pipeline]. +push, triggers your CI [pipeline]. The `.gitlab-ci.yml` file tells the GitLab runner what to do. By default it runs a pipeline with three [stages]: `build`, `test`, and `deploy`. You don't need to @@ -31,13 +31,13 @@ So in brief, the steps needed to have a working CI can be summed up to: From there on, on every push to your Git repository, the Runner will automagically start the pipeline and the pipeline will appear under the -project's `/pipelines` page. +project's **Pipelines** page. --- This guide assumes that you: -- have a working GitLab instance of version 8.0 or higher or are using +- have a working GitLab instance of version 8.0+r or are using [GitLab.com](https://gitlab.com) - have a project in GitLab that you would like to use CI for @@ -54,7 +54,7 @@ The `.gitlab-ci.yml` file is where you configure what CI does with your project. It lives in the root of your repository. On any push to your repository, GitLab will look for the `.gitlab-ci.yml` -file and start builds on _Runners_ according to the contents of the file, +file and start jobs on _Runners_ according to the contents of the file, for that commit. Because `.gitlab-ci.yml` is in the repository and is version controlled, old @@ -63,11 +63,12 @@ have different pipelines and jobs, and you have a single source of truth for CI. You can read more about the reasons why we are using `.gitlab-ci.yml` [in our blog about it][blog-ci]. -**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file -so you have to pay extra attention to indentation. Always use spaces, not tabs. - ### Creating a simple `.gitlab-ci.yml` file +>**Note:** +`.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file +so you have to pay extra attention to indentation. Always use spaces, not tabs. + You need to create a file named `.gitlab-ci.yml` in the root directory of your repository. Below is an example for a Ruby on Rails project. @@ -88,7 +89,7 @@ rubocop: - bundle exec rubocop ``` -This is the simplest possible build configuration that will work for most Ruby +This is the simplest possible configuration that will work for most Ruby applications: 1. Define two jobs `rspec` and `rubocop` (the names are arbitrary) with @@ -98,22 +99,22 @@ applications: The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when they should be run. The jobs are defined as top-level elements with a name (in our case `rspec` and `rubocop`) and always have to contain the `script` keyword. -Jobs are used to create builds, which are then picked by +Jobs are used to create jobs, which are then picked by [Runners](../runners/README.md) and executed within the environment of the Runner. What is important is that each job is run independently from each other. If you want to check whether your `.gitlab-ci.yml` file is valid, there is a Lint tool under the page `/ci/lint` of your GitLab instance. You can also find -a "CI Lint" button to go to this page under **Pipelines > Pipelines** and -**Pipelines > Builds** in your project. +a "CI Lint" button to go to this page under **Pipelines ➔ Pipelines** and +**Pipelines ➔ Jobs** in your project. For more information and a complete `.gitlab-ci.yml` syntax, please read -[the documentation on .gitlab-ci.yml](../yaml/README.md). +[the reference documentation on .gitlab-ci.yml](../yaml/README.md). ### Push `.gitlab-ci.yml` to GitLab -Once you've created `.gitlab-ci.yml`, you should add it to your git repository +Once you've created `.gitlab-ci.yml`, you should add it to your Git repository and push it to GitLab. ```bash @@ -125,28 +126,27 @@ git push origin master Now if you go to the **Pipelines** page you will see that the pipeline is pending. -You can also go to the **Commits** page and notice the little clock icon next +You can also go to the **Commits** page and notice the little pause icon next to the commit SHA. ![New commit pending](img/new_commit.png) -Clicking on the clock icon you will be directed to the builds page for that -specific commit. +Clicking on it you will be directed to the jobs page for that specific commit. -![Single commit builds page](img/single_commit_status_pending.png) +![Single commit jobs page](img/single_commit_status_pending.png) Notice that there are two jobs pending which are named after what we wrote in `.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured -yet for these builds. +yet for these jobs. -The next step is to configure a Runner so that it picks the pending builds. +The next step is to configure a Runner so that it picks the pending jobs. ## Configuring a Runner -In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`. A Runner +In GitLab, Runners run the jobs that you define in `.gitlab-ci.yml`. A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker container or even a cluster of containers. GitLab and the Runners communicate through an API, -so the only requirement is that the Runner's machine has Internet access. +so the only requirement is that the Runner's machine has [Internet] access. A Runner can be specific to a certain project or serve multiple projects in GitLab. If it serves all projects it's called a _Shared Runner_. @@ -155,9 +155,9 @@ Find more information about different Runners in the [Runners](../runners/README.md) documentation. You can find whether any Runners are assigned to your project by going to -**Settings > Runners**. Setting up a Runner is easy and straightforward. The -official Runner supported by GitLab is written in Go and can be found at -. +**Settings ➔ Runners**. Setting up a Runner is easy and straightforward. The +official Runner supported by GitLab is written in Go and its documentation +can be found at . In order to have a functional Runner you need to follow two steps: @@ -167,28 +167,25 @@ In order to have a functional Runner you need to follow two steps: Follow the links above to set up your own Runner or use a Shared Runner as described in the next section. -For other types of unofficial Runners written in other languages, see the -[instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner). - Once the Runner has been set up, you should see it on the Runners page of your -project, following **Settings > Runners**. +project, following **Settings ➔ Runners**. ![Activated runners](img/runners_activated.png) ### Shared Runners -If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners** +If you use [GitLab.com](https://gitlab.com/) you can use the **Shared Runners** provided by GitLab Inc. These are special virtual machines that run on GitLab's infrastructure and can build any project. -To enable **Shared Runners** you have to go to your project's -**Settings > Runners** and click **Enable shared runners**. +To enable the **Shared Runners** you have to go to your project's +**Settings ➔ Runners** and click **Enable shared runners**. [Read more on Shared Runners](../runners/README.md). -## Seeing the status of your pipeline and builds +## Seeing the status of your pipeline and jobs After configuring the Runner successfully, you should see the status of your last commit change from _pending_ to either _running_, _success_ or _failed_. @@ -197,23 +194,23 @@ You can view all pipelines by going to the **Pipelines** page in your project. ![Commit status](img/pipelines_status.png) -Or you can view all builds, by going to the **Pipelines > Builds** page. +Or you can view all jobs, by going to the **Pipelines ➔ Jobs** page. ![Commit status](img/builds_status.png) -By clicking on a Build ID, you will be able to see the log of that build. -This is important to diagnose why a build failed or acted differently than +By clicking on a job's status, you will be able to see the log of that job. +This is important to diagnose why a job failed or acted differently than you expected. ![Build log](img/build_log.png) You are also able to view the status of any commit in the various pages in -GitLab, such as **Commits** and **Merge Requests**. +GitLab, such as **Commits** and **Merge requests**. ## Enabling build emails If you want to receive e-mail notifications about the result status of the -builds, you should explicitly enable the **Builds Emails** service under your +jobs, you should explicitly enable the **Builds Emails** service under your project's settings. For more information read the @@ -224,9 +221,7 @@ For more information read the Visit the [examples README][examples] to see a list of examples using GitLab CI with various languages. -Awesome! You started using CI in GitLab! - -[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#install-gitlab-runner +[runner-install]: https://docs.gitlab.com/runner/install/ [blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/ [examples]: ../examples/README.md [ci]: https://about.gitlab.com/gitlab-ci/ @@ -235,3 +230,4 @@ Awesome! You started using CI in GitLab! [enabled]: ../enable_or_disable_ci.md [stages]: ../yaml/README.md#stages [pipeline]: ../pipelines.md +[internet]: https://about.gitlab.com/images/theinternet.png diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png index 87643d62d58..3a7248ca772 100644 Binary files a/doc/ci/quick_start/img/build_log.png and b/doc/ci/quick_start/img/build_log.png differ diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png index d287ae3064f..f829240f3b3 100644 Binary files a/doc/ci/quick_start/img/builds_status.png and b/doc/ci/quick_start/img/builds_status.png differ diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png index 29c2fea5d6d..b3dd848b294 100644 Binary files a/doc/ci/quick_start/img/new_commit.png and b/doc/ci/quick_start/img/new_commit.png differ diff --git a/doc/ci/quick_start/img/pipelines_status.png b/doc/ci/quick_start/img/pipelines_status.png index 53ccc49bd66..06d1559f5d2 100644 Binary files a/doc/ci/quick_start/img/pipelines_status.png and b/doc/ci/quick_start/img/pipelines_status.png differ diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png index 5ce6fe8e17c..cd83c1a7e4c 100644 Binary files a/doc/ci/quick_start/img/runners_activated.png and b/doc/ci/quick_start/img/runners_activated.png differ diff --git a/doc/ci/quick_start/img/single_commit_status_pending.png b/doc/ci/quick_start/img/single_commit_status_pending.png index 91fc9011847..ffc7054d3b0 100644 Binary files a/doc/ci/quick_start/img/single_commit_status_pending.png and b/doc/ci/quick_start/img/single_commit_status_pending.png differ diff --git a/doc/ci/quick_start/img/status_pending.png b/doc/ci/quick_start/img/status_pending.png deleted file mode 100644 index cbd44a189d3..00000000000 Binary files a/doc/ci/quick_start/img/status_pending.png and /dev/null differ diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index ddebd987650..e15b6891334 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -1,162 +1,163 @@ # Runners In GitLab CI, Runners run your [yaml](../yaml/README.md). -A runner is an isolated (virtual) machine that picks up builds +A Runner is an isolated (virtual) machine that picks up jobs through the coordinator API of GitLab CI. -A runner can be specific to a certain project or serve any project -in GitLab CI. A runner that serves all projects is called a shared runner. +A Runner can be specific to a certain project or serve any project +in GitLab CI. A Runner that serves all projects is called a shared Runner. Ideally, GitLab Runner should not be installed on the same machine as GitLab. -Read the [requirements documentation](../../install/requirements.md#gitlab-runner) +Read the [requirements documentation](../../install/requirements.md#gitlab-Runner) for more information. ## Shared vs. Specific Runners -A runner that is specific only runs for the specified project. A shared runner +A Runner that is specific only runs for the specified project. A shared Runner can run jobs for every project that has enabled the option -`Allow shared runners`. +`Allow shared Runners`. -**Shared runners** are useful for jobs that have similar requirements, -between multiple projects. Rather than having multiple runners idling for -many projects, you can have a single or a small number of runners that handle -multiple projects. This makes it easier to maintain and update runners. +**Shared Runners** are useful for jobs that have similar requirements, +between multiple projects. Rather than having multiple Runners idling for +many projects, you can have a single or a small number of Runners that handle +multiple projects. This makes it easier to maintain and update Runners. -**Specific runners** are useful for jobs that have special requirements or for +**Specific Runners** are useful for jobs that have special requirements or for projects with a specific demand. If a job has certain requirements, you can set -up the specific runner with this in mind, while not having to do this for all -runners. For example, if you want to deploy a certain project, you can setup -a specific runner to have the right credentials for this. +up the specific Runner with this in mind, while not having to do this for all +Runners. For example, if you want to deploy a certain project, you can setup +a specific Runner to have the right credentials for this. -Projects with high demand of CI activity can also benefit from using specific runners. -By having dedicated runners you are guaranteed that the runner is not being held +Projects with high demand of CI activity can also benefit from using specific Runners. +By having dedicated Runners you are guaranteed that the Runner is not being held up by another project's jobs. -You can set up a specific runner to be used by multiple projects. The difference -with a shared runner is that you have to enable each project explicitly for -the runner to be able to run its jobs. +You can set up a specific Runner to be used by multiple projects. The difference +with a shared Runner is that you have to enable each project explicitly for +the Runner to be able to run its jobs. -Specific runners do not get shared with forked projects automatically. +Specific Runners do not get shared with forked projects automatically. A fork does copy the CI settings (jobs, allow shared, etc) of the cloned repository. # Creating and Registering a Runner -There are several ways to create a runner. Only after creation, upon +There are several ways to create a Runner. Only after creation, upon registration its status as Shared or Specific is determined. -[See the documentation for](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/#installation) +[See the documentation for](https://gitlab.com/gitlab-org/gitlab-ci-multi-Runner/#installation) the different methods of installing a Runner instance. -After installing the runner, you can either register it as `Shared` or as `Specific`. +After installing the Runner, you can either register it as `Shared` or as `Specific`. You can only register a Shared Runner if you have admin access to the GitLab instance. ## Registering a Shared Runner -You can only register a shared runner if you are an admin on the linked +You can only register a shared Runner if you are an admin on the linked GitLab instance. -Grab the shared-runner token on the `admin/runners` page of your GitLab CI +Grab the shared-Runner token on the `admin/Runners` page of your GitLab CI instance. -![shared token](shared_runner.png) +![shared token](shared_Runner.png) -Now simply register the runner as any runner: +Now simply register the Runner as any Runner: ``` -sudo gitlab-ci-multi-runner register +sudo gitlab-ci-multi-Runner register ``` -Shared runners are enabled by default as of GitLab 8.2, but can be disabled with the -`DISABLE SHARED RUNNERS` button. Previous versions of GitLab defaulted shared runners to +Shared Runners are enabled by default as of GitLab 8.2, but can be disabled with the +`DISABLE SHARED RunnerS` button. Previous versions of GitLab defaulted shared Runners to disabled. ## Registering a Specific Runner Registering a specific can be done in two ways: -1. Creating a runner with the project registration token -1. Converting a shared runner into a specific runner (one-way, admin only) +1. Creating a Runner with the project registration token +1. Converting a shared Runner into a specific Runner (one-way, admin only) -There are several ways to create a runner instance. The steps below only -concern registering the runner on GitLab CI. +There are several ways to create a Runner instance. The steps below only +concern registering the Runner on GitLab CI. ### Registering a Specific Runner with a Project Registration token -To create a specific runner without having admin rights to the GitLab instance, -visit the project you want to make the runner work for in GitLab CI. +To create a specific Runner without having admin rights to the GitLab instance, +visit the project you want to make the Runner work for in GitLab CI. -Click on the runner tab and use the registration token you find there to -setup a specific runner for this project. +Click on the Runner tab and use the registration token you find there to +setup a specific Runner for this project. -![project runners in GitLab CI](project_specific.png) +![project Runners in GitLab CI](project_specific.png) -To register the runner, run the command below and follow instructions: +To register the Runner, run the command below and follow instructions: ``` -sudo gitlab-ci-multi-runner register +sudo gitlab-ci-multi-Runner register ``` -### Lock a specific runner from being enabled for other projects +### Lock a specific Runner from being enabled for other projects -You can configure a runner to assign it exclusively to a project. When a -runner is locked this way, it can no longer be enabled for other projects. -This setting is available on each runner in *Project Settings* > *Runners*. +You can configure a Runner to assign it exclusively to a project. When a +Runner is locked this way, it can no longer be enabled for other projects. +This setting is available on each Runner in *Project Settings* > *Runners*. ### Making an existing Shared Runner Specific If you are an admin on your GitLab instance, -you can make any shared runner a specific runner, _but you can not -make a specific runner a shared runner_. +you can make any shared Runner a specific Runner, _but you can not +make a specific Runner a shared Runner_. -To make a shared runner specific, go to the runner page (`/admin/runners`) -and find your runner. Add any projects on the left to make this runner -run exclusively for these projects, therefore making it a specific runner. +To make a shared Runner specific, go to the Runner page (`/admin/Runners`) +and find your Runner. Add any projects on the left to make this Runner +run exclusively for these projects, therefore making it a specific Runner. -![making a shared runner specific](shared_to_specific_admin.png) +![making a shared Runner specific](shared_to_specific_admin.png) ## Using Shared Runners Effectively -If you are planning to use shared runners, there are several things you +If you are planning to use shared Runners, there are several things you should keep in mind. ### Use Tags -You must setup a runner to be able to run all the different types of jobs +You must setup a Runner to be able to run all the different types of jobs that it may encounter on the projects it's shared over. This would be problematic for large amounts of projects, if it wasn't for tags. By tagging a Runner for the types of jobs it can handle, you can make sure -shared runners will only run the jobs they are equipped to run. +shared Runners will only run the jobs they are equipped to run. -For instance, at GitLab we have runners tagged with "rails" if they contain +For instance, at GitLab we have Runners tagged with "rails" if they contain the appropriate dependencies to run Rails test suites. -### Prevent runner with tags from picking jobs without tags +### Prevent Runner with tags from picking jobs without tags -You can configure a runner to prevent it from picking jobs with tags when -the runner does not have tags assigned. This setting is available on each -runner in *Project Settings* > *Runners*. +You can configure a Runner to prevent it from picking jobs with tags when +the Runner does not have tags assigned. This setting is available on each +Runner in *Project Settings* > *Runners*. ### Be careful with sensitive information -If you can run a build on a runner, you can get access to any code it runs -and get the token of the runner. With shared runners, this means that anyone -that runs jobs on the runner, can access anyone else's code that runs on the runner. +If you can run a job on a Runner, you can get access to any code it runs +and get the token of the Runner. With shared Runners, this means that anyone +that runs jobs on the Runner, can access anyone else's code that runs on the Runner. -In addition, because you can get access to the runner token, it is possible -to create a clone of a runner and submit false builds, for example. +In addition, because you can get access to the Runner token, it is possible +to create a clone of a Runner and submit false jobs, for example. -The above is easily avoided by restricting the usage of shared runners +The above is easily avoided by restricting the usage of shared Runners on large public GitLab instances and controlling access to your GitLab instance. ### Forks Whenever a project is forked, it copies the settings of the jobs that relate -to it. This means that if you have shared runners setup for a project and -someone forks that project, the shared runners will also serve jobs of this +to it. This means that if you have shared Runners setup for a project and +someone forks that project, the shared Runners will also serve jobs of this project. ## Attack vectors in Runners -Mentioned briefly earlier, but the following things of runners can be exploited. -We're always looking for contributions that can mitigate these [Security Considerations](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md). +Mentioned briefly earlier, but the following things of Runners can be exploited. +We're always looking for contributions that can mitigate these +[Security Considerations](https://docs.gitlab.com/runner/security/). diff --git a/doc/ci/services/mysql.md b/doc/ci/services/mysql.md index aaf3aa77837..338368dbbc9 100644 --- a/doc/ci/services/mysql.md +++ b/doc/ci/services/mysql.md @@ -31,7 +31,7 @@ Database: el_duderino ``` If you are wondering why we used `mysql` for the `Host`, read more at -[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build). +[How is service linked to the job](../docker/using_docker_images.md#how-is-service-linked-to-the-job). You can also use any other docker image available on [Docker Hub][hub-mysql]. For example, to use MySQL 5.5 the service becomes `mysql:5.5`. @@ -112,7 +112,7 @@ convenience that runs on [GitLab.com](https://gitlab.com) using our publicly available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few -moments the changes will be picked by a public runner and the build will begin. +moments the changes will be picked by a public runner and the job will begin. [hub-mysql]: https://hub.docker.com/r/_/mysql/ [mysql-example-repo]: https://gitlab.com/gitlab-examples/mysql diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index f787cc0a124..3899b555f32 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -31,7 +31,7 @@ Database: nice_marmot ``` If you are wondering why we used `postgres` for the `Host`, read more at -[How is service linked to the build](../docker/using_docker_images.md#how-is-service-linked-to-the-build). +[How is service linked to the job](../docker/using_docker_images.md#how-is-service-linked-to-the-job). You can also use any other docker image available on [Docker Hub][hub-pg]. For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`. @@ -108,7 +108,7 @@ convenience that runs on [GitLab.com](https://gitlab.com) using our publicly available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few -moments the changes will be picked by a public runner and the build will begin. +moments the changes will be picked by a public runner and the job will begin. [hub-pg]: https://hub.docker.com/r/_/postgres/ [postgres-example-repo]: https://gitlab.com/gitlab-examples/postgres diff --git a/doc/ci/services/redis.md b/doc/ci/services/redis.md index 80705024d2f..554c321fd0c 100644 --- a/doc/ci/services/redis.md +++ b/doc/ci/services/redis.md @@ -63,7 +63,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few -moments the changes will be picked by a public runner and the build will begin. +moments the changes will be picked by a public runner and the job will begin. [hub-redis]: https://hub.docker.com/r/_/redis/ [redis-example-repo]: https://gitlab.com/gitlab-examples/redis diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md index b858029d25e..49e7ac38b26 100644 --- a/doc/ci/ssh_keys/README.md +++ b/doc/ci/ssh_keys/README.md @@ -25,7 +25,7 @@ This is the universal solution which works with any type of executor 1. Create a new SSH key pair with [ssh-keygen][] 2. Add the private key as a **Secret Variable** to the project -3. Run the [ssh-agent][] during build to load the private key. +3. Run the [ssh-agent][] during job to load the private key. ## SSH keys when using the Docker executor @@ -77,7 +77,7 @@ SSH key. You can generate the SSH key from the machine that GitLab Runner is installed on, and use that key for all projects that are run on this machine. -First, you need to login to the server that runs your builds. +First, you need to login to the server that runs your jobs. Then from the terminal login as the `gitlab-runner` user and generate the SSH key pair as described in the [SSH keys documentation](../../ssh/README.md). @@ -103,7 +103,7 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available [shared runners](../runners/README.md). Want to hack on it? Simply fork it, commit and push your changes. Within a few -moments the changes will be picked by a public runner and the build will begin. +moments the changes will be picked by a public runner and the job will begin. [ssh-keygen]: http://linux.die.net/man/1/ssh-keygen [ssh-agent]: http://linux.die.net/man/1/ssh-agent diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md index efca05af7b8..740edba1f59 100644 --- a/doc/ci/triggers/README.md +++ b/doc/ci/triggers/README.md @@ -1,19 +1,19 @@ -# Triggering Builds through the API - -> [Introduced][ci-229] in GitLab CE 7.14. +# Triggering jobs through the API > **Note**: -GitLab 8.12 has a completely redesigned build permissions system. -Read all about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#build-triggers). +- [Introduced][ci-229] in GitLab CE 7.14. +- GitLab 8.12 has a completely redesigned job permissions system. Read all + about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#job-triggers). Triggers can be used to force a rebuild of a specific `ref` (branch or tag) with an API call. ## Add a trigger -You can add a new trigger by going to your project's **Settings > Triggers**. -The **Add trigger** button will create a new token which you can then use to -trigger a rebuild of this particular project. +You can add a new trigger by going to your project's +**Settings ➔ CI/CD Pipelines ➔ Triggers**. The **Add trigger** button will +create a new token which you can then use to trigger a rerun of this +particular project's pipeline. Every new trigger you create, gets assigned a different token which you can then use inside your scripts or `.gitlab-ci.yml`. You also have a nice @@ -27,13 +27,13 @@ You can revoke a trigger any time by going at your project's **Settings > Triggers** and hitting the **Revoke** button. The action is irreversible. -## Trigger a build +## Trigger a job > **Note**: Valid refs are only the branches and tags. If you pass a commit SHA as a ref, -it will not trigger a build. +it will not trigger a job. -To trigger a build you need to send a `POST` request to GitLab's API endpoint: +To trigger a job you need to send a `POST` request to GitLab's API endpoint: ``` POST /projects/:id/trigger/builds @@ -42,31 +42,32 @@ POST /projects/:id/trigger/builds The required parameters are the trigger's `token` and the Git `ref` on which the trigger will be performed. Valid refs are the branch and the tag. The `:id` of a project can be found by [querying the API](../../api/projects.md) -or by visiting the **Triggers** page which provides self-explanatory examples. +or by visiting the **CI/CD Pipelines** settings page which provides +self-explanatory examples. -When a rebuild is triggered, the information is exposed in GitLab's UI under -the **Builds** page and the builds are marked as `triggered`. +When a rerun of a pipeline is triggered, the information is exposed in GitLab's +UI under the **Jobs** page and the jobs are marked as triggered 'by API'. -![Marked rebuilds as triggered on builds page](img/builds_page.png) +![Marked rebuilds as on jobs page](img/builds_page.png) --- -You can see which trigger caused the rebuild by visiting the single build page. -The token of the trigger is exposed in the UI as you can see from the image +You can see which trigger caused the rebuild by visiting the single job page. +A part of the trigger's token is exposed in the UI as you can see from the image below. -![Marked rebuilds as triggered on a single build page](img/trigger_single_build.png) +![Marked rebuilds as triggered on a single job page](img/trigger_single_build.png) --- See the [Examples](#examples) section for more details on how to actually trigger a rebuild. -## Trigger a build from webhook +## Trigger a job from webhook > Introduced in GitLab 8.14. -To trigger a build from webhook of another project you need to add the following +To trigger a job from webhook of another project you need to add the following webhook url for Push and Tag push events: ``` @@ -78,7 +79,7 @@ https://gitlab.example.com/api/v3/projects/:id/ref/:ref/trigger/builds?token=TOK from webhook body that designates the branchref that fired the trigger in the source repository. - `ref` should be url encoded if contains slashes. -## Pass build variables to a trigger +## Pass job variables to a trigger You can pass any number of arbitrary variables in the trigger API call and they will be available in GitLab CI so that they can be used in your `.gitlab-ci.yml` @@ -90,7 +91,7 @@ variables[key]=value This information is also exposed in the UI. -![Build variables in UI](img/trigger_variables.png) +![Job variables in UI](img/trigger_variables.png) --- @@ -116,7 +117,7 @@ curl --request POST \ "https://gitlab.example.com/api/v3/projects/9/trigger/builds?token=TOKEN&ref=master" ``` -### Triggering a build within `.gitlab-ci.yml` +### Triggering a job within `.gitlab-ci.yml` You can also benefit by using triggers in your `.gitlab-ci.yml`. Let's say that you have two projects, A and B, and you want to trigger a rebuild on the `master` @@ -132,7 +133,7 @@ build_docs: - tags ``` -Now, whenever a new tag is pushed on project A, the build will run and the +Now, whenever a new tag is pushed on project A, the job will run and the `build_docs` job will be executed, triggering a rebuild of project B. The `stage: deploy` ensures that this job will run only after all jobs with `stage: test` complete successfully. @@ -189,18 +190,18 @@ curl --request POST \ https://gitlab.example.com/api/v3/projects/9/trigger/builds ``` -### Using webhook to trigger builds +### Using webhook to trigger job -You can add the following webhook to another project in order to trigger a build: +You can add the following webhook to another project in order to trigger a job: ``` https://gitlab.example.com/api/v3/projects/9/ref/master/trigger/builds?token=TOKEN&variables[UPLOAD_TO_S3]=true ``` -### Using cron to trigger nightly builds +### Using cron to trigger nightly jobs -Whether you craft a script or just run cURL directly, you can trigger builds -in conjunction with cron. The example below triggers a build on the `master` +Whether you craft a script or just run cURL directly, you can trigger jobs +in conjunction with cron. The example below triggers a job on the `master` branch of project with ID `9` every night at `00:30`: ```bash diff --git a/doc/ci/triggers/img/builds_page.png b/doc/ci/triggers/img/builds_page.png index fded5839f76..c9cc8f308f4 100644 Binary files a/doc/ci/triggers/img/builds_page.png and b/doc/ci/triggers/img/builds_page.png differ diff --git a/doc/ci/triggers/img/trigger_single_build.png b/doc/ci/triggers/img/trigger_single_build.png index c4a5550d640..837bbeffe9f 100644 Binary files a/doc/ci/triggers/img/trigger_single_build.png and b/doc/ci/triggers/img/trigger_single_build.png differ diff --git a/doc/ci/triggers/img/trigger_variables.png b/doc/ci/triggers/img/trigger_variables.png index 65fe1ea9ab6..0c2a761cfa9 100644 Binary files a/doc/ci/triggers/img/trigger_variables.png and b/doc/ci/triggers/img/trigger_variables.png differ diff --git a/doc/ci/triggers/img/triggers_page.png b/doc/ci/triggers/img/triggers_page.png index 56d13905ce6..8ebf68d0384 100644 Binary files a/doc/ci/triggers/img/triggers_page.png and b/doc/ci/triggers/img/triggers_page.png differ diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 49fca884f35..8a638ed3df8 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -1,6 +1,6 @@ # Variables -When receiving a build from GitLab CI, the [Runner] prepares the build environment. +When receiving a job from GitLab CI, the [Runner] prepares the build environment. It starts by setting a list of **predefined variables** (environment variables) and a list of **user-defined variables**. @@ -29,22 +29,22 @@ version of Runner required. | Variable | GitLab | Runner | Description | |-------------------------|--------|--------|-------------| -| **CI** | all | 0.4 | Mark that build is executed in CI environment | -| **GITLAB_CI** | all | all | Mark that build is executed in GitLab CI environment | -| **CI_SERVER** | all | all | Mark that build is executed in CI environment | -| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate builds | -| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule builds | -| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule builds | -| **CI_BUILD_ID** | all | all | The unique id of the current build that GitLab CI uses internally | +| **CI** | all | 0.4 | Mark that job is executed in CI environment | +| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | +| **CI_SERVER** | all | all | Mark that job is executed in CI environment | +| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate jobs | +| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | +| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | +| **CI_BUILD_ID** | all | all | The unique id of the current job that GitLab CI uses internally | | **CI_BUILD_REF** | all | all | The commit revision for which project is built | | **CI_BUILD_TAG** | all | 0.5 | The commit tag name. Present only when building tags. | -| **CI_BUILD_NAME** | all | 0.5 | The name of the build as defined in `.gitlab-ci.yml` | +| **CI_BUILD_NAME** | all | 0.5 | The name of the job as defined in `.gitlab-ci.yml` | | **CI_BUILD_STAGE** | all | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | | **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built | | **CI_BUILD_REF_SLUG** | 8.15 | all | `$CI_BUILD_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. | | **CI_BUILD_REPO** | all | all | The URL to clone the Git repository | -| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that build was [triggered] | -| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that build was manually started | +| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that job was [triggered] | +| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that job was manually started | | **CI_BUILD_TOKEN** | all | 1.2 | Token used for authenticating with the GitLab Container Registry | | **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | | **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | @@ -52,8 +52,8 @@ version of Runner required. | **CI_PROJECT_NAMESPACE**| 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built | | **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name | | **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project | -| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the build is run | -| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this build | +| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | +| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | | **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | | **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry | | **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project | @@ -61,11 +61,11 @@ version of Runner required. | **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab | | **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags | | **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | -| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a build | -| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a build | -| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a build | -| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the build | -| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the build | +| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | +| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job | +| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job | +| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job | +| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job | Example values: @@ -136,7 +136,7 @@ job_name: >**Notes:** - This feature requires GitLab Runner 0.4.0 or higher. - Be aware that secret variables are not masked, and their values can be shown - in the build logs if explicitly asked to do so. If your project is public or + in the job logs if explicitly asked to do so. If your project is public or internal, you can set the pipelines private from your project's Pipelines settings. Follow the discussion in issue [#13784][ce-13784] for masking the secret variables. @@ -150,7 +150,7 @@ storing things like passwords, secret keys and credentials. Secret variables can be added by going to your project's **Settings ➔ Variables ➔ Add variable**. -Once you set them, they will be available for all subsequent builds. +Once you set them, they will be available for all subsequent jobs. ## Deployment variables @@ -160,7 +160,7 @@ This feature requires GitLab CI 8.15 or higher. [Project services](../../user/project/integrations/project_services.md) that are responsible for deployment configuration may define their own variables that are set in the build environment. These variables are only defined for -[deployment builds](../environments.md). Please consult the documentation of +[deployment jobs](../environments.md). Please consult the documentation of the project services that you are using to learn which variables they define. An example project service that defines deployment variables is @@ -173,21 +173,21 @@ An example project service that defines deployment variables is > **WARNING:** Enabling debug tracing can have severe security implications. The output **will** contain the content of all your secret variables and any other secrets! The output **will** be uploaded to the GitLab server and made visible - in build traces! + in job traces! By default, GitLab Runner hides most of the details of what it is doing when -processing a job. This behaviour keeps build traces short, and prevents secrets +processing a job. This behaviour keeps job traces short, and prevents secrets from being leaked into the trace unless your script writes them to the screen. If a job isn't working as expected, this can make the problem difficult to investigate; in these cases, you can enable debug tracing in `.gitlab-ci.yml`. Available on GitLab Runner v1.7+, this feature enables the shell's execution -trace, resulting in a verbose build trace listing all commands that were run, +trace, resulting in a verbose job trace listing all commands that were run, variables that were set, etc. -Before enabling this, you should ensure builds are visible to +Before enabling this, you should ensure jobs are visible to [team members only](../../user/permissions.md#project-features). You should -also [erase](../pipelines.md#seeing-build-status) all generated build traces +also [erase](../pipelines.md#seeing-build-status) all generated job traces before making them visible again. To enable debug traces, set the `CI_DEBUG_TRACE` variable to `true`: @@ -320,7 +320,7 @@ MIIFQzCCBCugAwIBAgIRAL/ElDjuf15xwja1ZnCocWAwDQYJKoZIhvcNAQELBQAw' All variables are set as environment variables in the build environment, and they are accessible with normal methods that are used to access such variables. -In most cases `bash` or `sh` is used to execute the build script. +In most cases `bash` or `sh` is used to execute the job script. To access the variables (predefined and user-defined) in a `bash`/`sh` environment, prefix the variable name with the dollar sign (`$`): @@ -328,12 +328,12 @@ prefix the variable name with the dollar sign (`$`): ``` job_name: script: - - echo $CI_BUILD_ID + - echo $CI_job_ID ``` You can also list all environment variables with the `export` command, but be aware that this will also expose the values of all the secret variables -you set, in the build log: +you set, in the job log: ``` job_name: @@ -344,4 +344,4 @@ job_name: [ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 [runner]: https://docs.gitlab.com/runner/ [triggered]: ../triggers/README.md -[triggers]: ../triggers/README.md#pass-build-variables-to-a-trigger +[triggers]: ../triggers/README.md#pass-job-variables-to-a-trigger diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index cd492d16747..63be61d1bca 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1,7 +1,7 @@ -# Configuration of your builds with .gitlab-ci.yml +# Configuration of your jobs with .gitlab-ci.yml This document describes the usage of `.gitlab-ci.yml`, the file that is used by -GitLab Runner to manage your project's builds. +GitLab Runner to manage your project's jobs. If you want a quick introduction to GitLab CI, follow our [quick start guide](../quick_start/README.md). @@ -30,10 +30,9 @@ jobs, where each of the jobs executes a different command. Of course a command can execute code directly (`./configure;make;make install`) or run a script (`test.sh`) in the repository. -Jobs are used to create builds, which are then picked up by -[Runners](../runners/README.md) and executed within the environment of the -Runner. What is important, is that each job is run independently from each -other. +Jobs are picked up by [Runners](../runners/README.md) and executed within the +environment of the Runner. What is important, is that each job is run +independently from each other. The YAML syntax allows for using more complex job specifications than in the above example: @@ -80,30 +79,31 @@ There are a few reserved `keywords` that **cannot** be used as job names: ### image and services This allows to specify a custom Docker image and a list of services that can be -used for time of the build. The configuration of this feature is covered in +used for time of the job. The configuration of this feature is covered in [a separate document](../docker/README.md). ### before_script `before_script` is used to define the command that should be run before all -builds, including deploy builds, but after the restoration of artifacts. This can be an array or a multi-line string. +jobs, including deploy jobs, but after the restoration of artifacts. This can +be an array or a multi-line string. ### after_script > Introduced in GitLab 8.7 and requires Gitlab Runner v1.2 `after_script` is used to define the command that will be run after for all -builds. This has to be an array or a multi-line string. +jobs. This has to be an array or a multi-line string. ### stages -`stages` is used to define build stages that can be used by jobs. +`stages` is used to define stages that can be used by jobs. The specification of `stages` allows for having flexible multi stage pipelines. -The ordering of elements in `stages` defines the ordering of builds' execution: +The ordering of elements in `stages` defines the ordering of jobs' execution: -1. Builds of the same stage are run in parallel. -1. Builds of the next stage are run after the jobs from the previous stage +1. Jobs of the same stage are run in parallel. +1. Jobs of the next stage are run after the jobs from the previous stage complete successfully. Let's consider the following example, which defines 3 stages: @@ -115,7 +115,7 @@ stages: - deploy ``` -1. First all jobs of `build` are executed in parallel. +1. First, all jobs of `build` are executed in parallel. 1. If all jobs of `build` succeed, the `test` jobs are executed in parallel. 1. If all jobs of `test` succeed, the `deploy` jobs are executed in parallel. 1. If all jobs of `deploy` succeed, the commit is marked as `success`. @@ -124,7 +124,7 @@ stages: There are also two edge cases worth mentioning: -1. If no `stages` are defined in `.gitlab-ci.yml`, then by default the `build`, +1. If no `stages` are defined in `.gitlab-ci.yml`, then the `build`, `test` and `deploy` are allowed to be used as job's stage by default. 2. If a job doesn't specify a `stage`, the job is assigned the `test` stage. @@ -137,7 +137,7 @@ Alias for [stages](#stages). > Introduced in GitLab Runner v0.5.0. GitLab CI allows you to add variables to `.gitlab-ci.yml` that are set in the -build environment. The variables are stored in the Git repository and are meant +job environment. The variables are stored in the Git repository and are meant to store non-sensitive project configuration, for example: ```yaml @@ -163,7 +163,7 @@ which can be set in GitLab's UI. > Introduced in GitLab Runner v0.7.0. `cache` is used to specify a list of files and directories which should be -cached between builds. You can only use paths that are within the project +cached between jobs. You can only use paths that are within the project workspace. **By default the caching is enabled per-job and per-branch.** @@ -202,8 +202,8 @@ rspec: - binaries/ ``` -Locally defined cache overwrites globally defined options. This will cache only -`binaries/`: +Locally defined cache overwrites globally defined options. The following `rspec` +job will cache only `binaries/`: ```yaml cache: @@ -281,8 +281,8 @@ cache: ## Jobs `.gitlab-ci.yml` allows you to specify an unlimited number of jobs. Each job -must have a unique name, which is not one of the Keywords mentioned above. -A job is defined by a list of parameters that define the build behavior. +must have a unique name, which is not one of the keywords mentioned above. +A job is defined by a list of parameters that define the job behavior. ```yaml job_name: @@ -302,24 +302,24 @@ job_name: | Keyword | Required | Description | |---------------|----------|-------------| -| script | yes | Defines a shell script which is executed by Runner | -| image | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | -| services | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | -| stage | no | Defines a build stage (default: `test`) | -| type | no | Alias for `stage` | -| variables | no | Define build variables on a job level | -| only | no | Defines a list of git refs for which build is created | -| except | no | Defines a list of git refs for which build is not created | -| tags | no | Defines a list of tags which are used to select Runner | -| allow_failure | no | Allow build to fail. Failed build doesn't contribute to commit status | -| when | no | Define when to run build. Can be `on_success`, `on_failure`, `always` or `manual` | -| dependencies | no | Define other builds that a build depends on so that you can pass artifacts between them| -| artifacts | no | Define list of build artifacts | -| cache | no | Define list of files that should be cached between subsequent runs | -| before_script | no | Override a set of commands that are executed before build | -| after_script | no | Override a set of commands that are executed after build | -| environment | no | Defines a name of environment to which deployment is done by this build | -| coverage | no | Define code coverage settings for a given job | +| script | yes | Defines a shell script which is executed by Runner | +| image | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | +| services | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) | +| stage | no | Defines a job stage (default: `test`) | +| type | no | Alias for `stage` | +| variables | no | Define job variables on a job level | +| only | no | Defines a list of git refs for which job is created | +| except | no | Defines a list of git refs for which job is not created | +| tags | no | Defines a list of tags which are used to select Runner | +| allow_failure | no | Allow job to fail. Failed job doesn't contribute to commit status | +| when | no | Define when to run job. Can be `on_success`, `on_failure`, `always` or `manual` | +| dependencies | no | Define other jobs that a job depends on so that you can pass artifacts between them| +| artifacts | no | Define list of [job artifacts](../../user/project/pipelines/job_artifacts.md) | +| cache | no | Define list of files that should be cached between subsequent runs | +| before_script | no | Override a set of commands that are executed before job | +| after_script | no | Override a set of commands that are executed after job | +| environment | no | Defines a name of environment to which deployment is done by this job | +| coverage | no | Define code coverage settings for a given job | ### script @@ -339,11 +339,15 @@ job: - bundle exec rspec ``` -Sometimes, `script` commands will need to be wrapped in single or double quotes. For example, commands that contain a colon (`:`) need to be wrapped in quotes so that the YAML parser knows to interpret the whole thing as a string rather than a "key: value" pair. Be careful when using special characters (`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``). +Sometimes, `script` commands will need to be wrapped in single or double quotes. +For example, commands that contain a colon (`:`) need to be wrapped in quotes so +that the YAML parser knows to interpret the whole thing as a string rather than +a "key: value" pair. Be careful when using special characters: +`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``. ### stage -`stage` allows to group build into different stages. Builds of the same `stage` +`stage` allows to group jobs into different stages. Jobs of the same `stage` are executed in `parallel`. For more info about the use of `stage` please check [stages](#stages). @@ -352,10 +356,9 @@ are executed in `parallel`. For more info about the use of `stage` please check `only` and `except` are two parameters that set a refs policy to limit when jobs are built: -1. `only` defines the names of branches and tags for which the job will be - built. +1. `only` defines the names of branches and tags for which the job will run. 2. `except` defines the names of branches and tags for which the job will - **not** be built. + **not** run. There are a few rules that apply to the usage of refs policy: @@ -379,8 +382,8 @@ job: - branches ``` -In this example, `job` will run only for refs that are tagged, or if a build is explicitly requested -via an API trigger. +In this example, `job` will run only for refs that are tagged, or if a build is +explicitly requested via an API trigger. ```yaml job: @@ -404,14 +407,14 @@ job: The above example will run `job` for all branches on `gitlab-org/gitlab-ce`, except master. -### job variables +### Job variables -It is possible to define build variables using a `variables` keyword on a job -level. It works basically the same way as its [global-level equivalent](#variables) -but allows you to define job-specific build variables. +It is possible to define job variables using a `variables` keyword on a job +level. It works basically the same way as its [global-level equivalent](#variables), +but allows you to define job-specific variables. -When the `variables` keyword is used on a job level, it overrides global YAML -build variables and predefined variables. To turn off global defined variables +When the `variables` keyword is used on a job level, it overrides the global YAML +job variables and predefined ones. To turn off global defined variables in your job, define an empty array: ```yaml @@ -419,8 +422,7 @@ job_name: variables: [] ``` -Build variables priority is defined in the -[variables documentation][variables]. +Job variables priority is defined in the [variables documentation][variables]. ### tags @@ -430,7 +432,7 @@ allowed to run this project. During the registration of a Runner, you can specify the Runner's tags, for example `ruby`, `postgres`, `development`. -`tags` allow you to run builds with Runners that have the specified tags +`tags` allow you to run jobs with Runners that have the specified tags assigned to them: ```yaml @@ -445,13 +447,13 @@ has both `ruby` AND `postgres` tags defined. ### allow_failure -`allow_failure` is used when you want to allow a build to fail without impacting -the rest of the CI suite. Failed builds don't contribute to the commit status. +`allow_failure` is used when you want to allow a job to fail without impacting +the rest of the CI suite. Failed jobs don't contribute to the commit status. -When enabled and the build fails, the pipeline will be successful/green for all +When enabled and the job fails, the pipeline will be successful/green for all intents and purposes, but a "CI build passed with warnings" message will be -displayed on the merge request or commit or build page. This is to be used by -builds that are allowed to fail, but where failure indicates some other (manual) +displayed on the merge request or commit or job page. This is to be used by +jobs that are allowed to fail, but where failure indicates some other (manual) steps should be taken elsewhere. In the example below, `job1` and `job2` will run in parallel, but if `job1` @@ -483,12 +485,12 @@ failure. `when` can be set to one of the following values: -1. `on_success` - execute build only when all builds from prior stages +1. `on_success` - execute job only when all jobs from prior stages succeed. This is the default. -1. `on_failure` - execute build only when at least one build from prior stages +1. `on_failure` - execute job only when at least one job from prior stages fails. -1. `always` - execute build regardless of the status of builds from prior stages. -1. `manual` - execute build manually (added in GitLab 8.10). Read about +1. `always` - execute job regardless of the status of jobs from prior stages. +1. `manual` - execute job manually (added in GitLab 8.10). Read about [manual actions](#manual-actions) below. For example: @@ -526,7 +528,7 @@ deploy_job: cleanup_job: stage: cleanup script: - - cleanup after builds + - cleanup after jobs when: always ``` @@ -552,10 +554,11 @@ Read more at the [environments documentation][env-manual]. ### environment -> Introduced in GitLab 8.9. - -> You can read more about environments and find more examples in the -[documentation about environments][environment]. +> +**Notes:** +- Introduced in GitLab 8.9. +- You can read more about environments and find more examples in the + [documentation about environments][environment]. `environment` is used to define that a job deploys to a specific environment. If `environment` is specified and no environment under that name exists, a new @@ -563,7 +566,7 @@ one will be created automatically. In its simplest form, the `environment` keyword can be defined like: -``` +```yaml deploy to production: stage: deploy script: git push production HEAD:master @@ -576,12 +579,12 @@ deployment to the `production` environment. #### environment:name -> Introduced in GitLab 8.11. - ->**Note:** -Before GitLab 8.11, the name of an environment could be defined as a string like -`environment: production`. The recommended way now is to define it under the -`name` keyword. +> +**Notes:** +- Introduced in GitLab 8.11. +- Before GitLab 8.11, the name of an environment could be defined as a string like + `environment: production`. The recommended way now is to define it under the + `name` keyword. The `environment` name can contain: @@ -602,7 +605,7 @@ Instead of defining the name of the environment right after the `environment` keyword, it is also possible to define it as a separate value. For that, use the `name` keyword under `environment`: -``` +```yaml deploy to production: stage: deploy script: git push production HEAD:master @@ -612,11 +615,11 @@ deploy to production: #### environment:url -> Introduced in GitLab 8.11. - ->**Note:** -Before GitLab 8.11, the URL could be added only in GitLab's UI. The -recommended way now is to define it in `.gitlab-ci.yml`. +> +**Notes:** +- Introduced in GitLab 8.11. +- Before GitLab 8.11, the URL could be added only in GitLab's UI. The + recommended way now is to define it in `.gitlab-ci.yml`. This is an optional value that when set, it exposes buttons in various places in GitLab which when clicked take you to the defined URL. @@ -625,7 +628,7 @@ In the example below, if the job finishes successfully, it will create buttons in the merge requests and in the environments/deployments pages which will point to `https://prod.example.com`. -``` +```yaml deploy to production: stage: deploy script: git push production HEAD:master @@ -690,8 +693,10 @@ The `stop_review_app` job is **required** to have the following keywords defined #### dynamic environments -> [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. - `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15 +> +**Notes:** +- [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6. +- The `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15. `environment` can also represent a configuration hash with `name` and `url`. These parameters can use any of the defined [CI variables](#variables) @@ -699,7 +704,7 @@ These parameters can use any of the defined [CI variables](#variables) For example: -``` +```yaml deploy as review app: stage: deploy script: make deploy @@ -714,28 +719,27 @@ is an [environment variable][variables] set by the Runner. The `$CI_ENVIRONMENT_SLUG` variable is based on the environment name, but suitable for inclusion in URLs. In this case, if the `deploy as review app` job was run in a branch named `pow`, this environment would be accessible with an URL like -`https://review-pow-aaaaaa.example.com/`. +`https://review-pow.example.com/`. This of course implies that the underlying server which hosts the application is properly configured. The common use case is to create dynamic environments for branches and use them as Review Apps. You can see a simple example using Review Apps at -https://gitlab.com/gitlab-examples/review-apps-nginx/. +. ### artifacts ->**Notes:** > -> - Introduced in GitLab Runner v0.7.0 for non-Windows platforms. -> - Windows support was added in GitLab Runner v.1.0.0. -> - Currently not all executors are supported. -> - Build artifacts are only collected for successful builds by default. +**Notes:** +- Introduced in GitLab Runner v0.7.0 for non-Windows platforms. +- Windows support was added in GitLab Runner v.1.0.0. +- Currently not all executors are supported. +- Job artifacts are only collected for successful jobs by default. `artifacts` is used to specify a list of files and directories which should be -attached to the build after success. You can only use paths that are within the -project workspace. To pass artifacts between different builds, see [dependencies](#dependencies). - +attached to the job after success. You can only use paths that are within the +project workspace. To pass artifacts between different jobs, see [dependencies](#dependencies). Below are some examples. Send all files in `binaries` and `.config`: @@ -794,7 +798,7 @@ release-job: - tags ``` -The artifacts will be sent to GitLab after a successful build and will +The artifacts will be sent to GitLab after the job finishes successfully and will be available for download in the GitLab UI. #### artifacts:name @@ -811,7 +815,7 @@ The default name is `artifacts`, which becomes `artifacts.zip` when downloaded. **Example configurations** -To create an archive with a name of the current build: +To create an archive with a name of the current job: ```yaml job: @@ -829,7 +833,7 @@ job: untracked: true ``` -To create an archive with a name of the current build and the current branch or +To create an archive with a name of the current job and the current branch or tag including only the files that are untracked by Git: ```yaml @@ -864,20 +868,20 @@ job: > Introduced in GitLab 8.9 and GitLab Runner v1.3.0. -`artifacts:when` is used to upload artifacts on build failure or despite the +`artifacts:when` is used to upload artifacts on job failure or despite the failure. `artifacts:when` can be set to one of the following values: -1. `on_success` - upload artifacts only when the build succeeds. This is the default. -1. `on_failure` - upload artifacts only when the build fails. -1. `always` - upload artifacts regardless of the build status. +1. `on_success` - upload artifacts only when the job succeeds. This is the default. +1. `on_failure` - upload artifacts only when the job fails. +1. `always` - upload artifacts regardless of the job status. --- **Example configurations** -To upload artifacts only when build fails. +To upload artifacts only when job fails. ```yaml job: @@ -894,13 +898,14 @@ time. By default, artifacts are stored on GitLab forever. `expire_in` allows you to specify how long artifacts should live before they expire, counting from the time they are uploaded and stored on GitLab. -You can use the **Keep** button on the build page to override expiration and +You can use the **Keep** button on the job page to override expiration and keep artifacts forever. After expiry, artifacts are actually deleted hourly by default (via a cron job), but they are not accessible after expiry. The value of `expire_in` is an elapsed time. Examples of parseable values: + - '3 mins 4 sec' - '2 hrs 20 min' - '2h20min' @@ -925,14 +930,14 @@ job: > Introduced in GitLab 8.6 and GitLab Runner v1.1.1. This feature should be used in conjunction with [`artifacts`](#artifacts) and -allows you to define the artifacts to pass between different builds. +allows you to define the artifacts to pass between different jobs. Note that `artifacts` from all previous [stages](#stages) are passed by default. To use this feature, define `dependencies` in context of the job and pass -a list of all previous builds from which the artifacts should be downloaded. -You can only define builds from stages that are executed before the current one. -An error will be shown if you define builds from the current stage or next ones. +a list of all previous jobs from which the artifacts should be downloaded. +You can only define jobs from stages that are executed before the current one. +An error will be shown if you define jobs from the current stage or next ones. Defining an empty array will skip downloading any artifacts for that job. --- @@ -942,7 +947,7 @@ In the following example, we define two jobs with artifacts, `build:osx` and will be downloaded and extracted in the context of the build. The same happens for `test:linux` and artifacts from `build:linux`. -The job `deploy` will download artifacts from all previous builds because of +The job `deploy` will download artifacts from all previous jobs because of the [stage](#stages) precedence: ```yaml @@ -979,7 +984,7 @@ deploy: ### before_script and after_script -It's possible to overwrite globally defined `before_script` and `after_script`: +It's possible to overwrite the globally defined `before_script` and `after_script`: ```yaml before_script: @@ -1027,7 +1032,7 @@ There are three possible values: `clone`, `fetch`, and `none`. `clone` is the slowest option. It clones the repository from scratch for every job, ensuring that the project workspace is always pristine. -``` +```yaml variables: GIT_STRATEGY: clone ``` @@ -1036,7 +1041,7 @@ variables: if it doesn't exist). `git clean` is used to undo any changes made by the last job, and `git fetch` is used to retrieve commits made since the last job ran. -``` +```yaml variables: GIT_STRATEGY: fetch ``` @@ -1047,7 +1052,7 @@ for jobs that operate exclusively on artifacts (e.g., `deploy`). Git repository data may be present, but it is certain to be out of date, so you should only rely on files brought into the project workspace from cache or artifacts. -``` +```yaml variables: GIT_STRATEGY: none ``` @@ -1061,56 +1066,59 @@ submodules are included when fetching the code before a build. Like `GIT_STRATEGY`, it can be set in either the global [`variables`](#variables) section or the [`variables`](#job-variables) section for individual jobs. -There are three posible values: `none`, `normal`, and `recursive`: +There are three possible values: `none`, `normal`, and `recursive`: - `none` means that submodules will not be included when fetching the project code. This is the default, which matches the pre-v1.10 behavior. - `normal` means that only the top-level submodules will be included. It is equivalent to: + ``` - $ git submodule sync - $ git submodule update --init + git submodule sync + git submodule update --init ``` - `recursive` means that all submodules (including submodules of submodules) will be included. It is equivalent to: + ``` - $ git submodule sync --recursive - $ git submodule update --init --recursive + git submodule sync --recursive + git submodule update --init --recursive ``` Note that for this feature to work correctly, the submodules must be configured (in `.gitmodules`) with either: + - the HTTP(S) URL of a publicly-accessible repository, or - a relative path to another repository on the same GitLab server. See the [Git submodules](../git_submodules.md) documentation. -## Build stages attempts +## Job stages attempts > Introduced in GitLab, it requires GitLab Runner v1.9+. -You can set the number for attempts the running build will try to execute each +You can set the number for attempts the running job will try to execute each of the following stages: -| Variable | Description | -|-------------------------|-------------| -| **GET_SOURCES_ATTEMPTS** | Number of attempts to fetch sources running a build | -| **ARTIFACT_DOWNLOAD_ATTEMPTS** | Number of attempts to download artifacts running a build | -| **RESTORE_CACHE_ATTEMPTS** | Number of attempts to restore the cache running a build | +| Variable | Description | +|-------------------------------- |-------------| +| **GET_SOURCES_ATTEMPTS** | Number of attempts to fetch sources running a job | +| **ARTIFACT_DOWNLOAD_ATTEMPTS** | Number of attempts to download artifacts running a job | +| **RESTORE_CACHE_ATTEMPTS** | Number of attempts to restore the cache running a job | The default is one single attempt. Example: -``` +```yaml variables: GET_SOURCES_ATTEMPTS: "3" ``` -You can set them in the global [`variables`](#variables) section or the [`variables`](#job-variables) -section for individual jobs. +You can set them in the global [`variables`](#variables) section or the +[`variables`](#job-variables) section for individual jobs. ## Shallow cloning @@ -1123,21 +1131,22 @@ repositories with a large number of commits or old, large binaries. The value is passed to `git fetch` and `git clone`. >**Note:** -If you use a depth of 1 and have a queue of builds or retry -builds, jobs may fail. +If you use a depth of 1 and have a queue of jobs or retry +jobs, jobs may fail. -Since Git fetching and cloning is based on a ref, such as a branch name, runners -can't clone a specific commit SHA. If there are multiple builds in the queue, or -you are retrying an old build, the commit to be tested needs to be within the -git history that is cloned. Setting too small a value for `GIT_DEPTH` can make +Since Git fetching and cloning is based on a ref, such as a branch name, Runners +can't clone a specific commit SHA. If there are multiple jobs in the queue, or +you are retrying an old job, the commit to be tested needs to be within the +Git history that is cloned. Setting too small a value for `GIT_DEPTH` can make it impossible to run these old commits. You will see `unresolved reference` in -build logs. You should then reconsider changing `GIT_DEPTH` to a higher value. +job logs. You should then reconsider changing `GIT_DEPTH` to a higher value. -Builds that rely on `git describe` may not work correctly when `GIT_DEPTH` is -set since only part of the git history is present. +Jobs that rely on `git describe` may not work correctly when `GIT_DEPTH` is +set since only part of the Git history is present. To fetch or clone only the last 3 commits: -``` + +```yaml variables: GIT_DEPTH: "3" ``` @@ -1174,7 +1183,7 @@ Read more about the various [YAML features](https://learnxinyminutes.com/docs/ya > Introduced in GitLab 8.6 and GitLab Runner v1.1.1. -YAML also has a handy feature called 'anchors', which let you easily duplicate +YAML has a handy feature called 'anchors', which lets you easily duplicate content across your document. Anchors can be used to duplicate/inherit properties, and is a perfect example to be used with [hidden keys](#hidden-keys) to provide templates for your jobs. @@ -1326,17 +1335,17 @@ pages: - master ``` -Read more on [GitLab Pages user documentation](../../pages/README.md). +Read more on [GitLab Pages user documentation](../../user/project/pages/index.md). ## Validate the .gitlab-ci.yml Each instance of GitLab CI has an embedded debug tool called Lint. You can find the link under `/ci/lint` of your gitlab instance. -## Skipping builds +## Skipping jobs If your commit message contains `[ci skip]` or `[skip ci]`, using any -capitalization, the commit will be created but the builds will be skipped. +capitalization, the commit will be created but the jobs will be skipped. ## Examples diff --git a/doc/development/code_review.md b/doc/development/code_review.md index e4a0e0b92bc..819578404b6 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -69,7 +69,7 @@ experience, refactors the existing code). Then: someone else would be confused by it as well. - After a round of line notes, it can be helpful to post a summary note such as "LGTM :thumbsup:", or "Just a couple things to address." -- Avoid accepting a merge request before the build succeeds. Of course, "Merge +- Avoid accepting a merge request before the job succeeds. Of course, "Merge When Pipeline Succeeds" (MWPS) is fine. - If you set the MR to "Merge When Pipeline Succeeds", you should take over subsequent revisions for anything that would be spotted after that. diff --git a/doc/install/installation.md b/doc/install/installation.md index 355179960b3..0f07085942a 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -307,7 +307,7 @@ sudo usermod -aG redis git # now that files in public/uploads are served by gitlab-workhorse sudo chmod 0700 public/uploads - # Change the permissions of the directory where CI build traces are stored + # Change the permissions of the directory where CI job traces are stored sudo chmod -R u+rwX builds/ # Change the permissions of the directory where CI artifacts are stored diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 0fb69d63dbe..b4e13f5812a 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -44,8 +44,8 @@ environment variable `SKIP`. You can skip: - `db` (database) - `uploads` (attachments) - `repositories` (Git repositories data) -- `builds` (CI build output logs) -- `artifacts` (CI build artifacts) +- `builds` (CI job output logs) +- `artifacts` (CI job artifacts) - `lfs` (LFS objects) - `registry` (Container Registry images) diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md index 979a1c5d310..ec565c3e7bf 100644 --- a/doc/university/glossary/README.md +++ b/doc/university/glossary/README.md @@ -30,15 +30,15 @@ A version control [system](https://www.jfrog.com/open-source/#os-arti) for non-t ### Artifacts -Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents. +Objects (usually binary and large) created by a build process. These can include use cases, class diagrams, requirements and design documents. ### Atlassian -A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. +A [company](https://www.atlassian.com) that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. ### Audit Log -Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system. +Also called an [audit trail](https://en.wikipedia.org/wiki/Audit_trail), an audit log is a document that records an event in an IT system. ### Auto Defined User Group @@ -55,7 +55,7 @@ Entry level [subscription](https://about.gitlab.com/pricing/) for GitLab EE curr ### Bitbucket -Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_bitbucket.html) from BitBucket to a GitLab instance. +Atlassian's web hosting service for Git and Mercurial Projects. Read about [migrating](https://docs.gitlab.com/ce/workflow/importing/import_projects_from_bitbucket.html) from BitBucket to a GitLab instance. ### Branch @@ -65,8 +65,8 @@ A branch is a parallel version of a repository. This allows you to work on the r Having your own logo on [your GitLab instance login page](https://docs.gitlab.com/ee/customization/branded_login_page.html) instead of the GitLab logo. -### Build triggers -These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](https://docs.gitlab.com/ce/ci/triggers/README.html) build triggers. +### Job triggers +These protect your code base against breaks, for instance when a team is working on the same project. Learn about [setting up](https://docs.gitlab.com/ce/ci/triggers/README.html) job triggers. ### CEPH @@ -74,7 +74,7 @@ These protect your code base against breaks, for instance when a team is working ### ChatOps -The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat. +The ability to [initiate an action](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/1412) from chat. ChatBots run in your chat application and give you the ability to do "anything" from chat. ### Clone @@ -82,7 +82,7 @@ A [copy](https://git-scm.com/docs/git-clone) of a repository stored on your mach ### Code Review -Examination of a progam's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab. +Examination of a progam's code. The main aim is to maintain high quality standards of code that is being shipped. Merge requests [serve as a code review tool](https://about.gitlab.com/2014/09/29/gitlab-flow/) in GitLab. ### Code Snippet @@ -140,7 +140,7 @@ A [SSH key](https://docs.gitlab.com/ce/gitlab-basics/create-your-ssh-keys.html)s For us at GitLab, this means a software developer, or someone who makes software. It is also one of the levels of access in our multi-level approval system. -### DevOps +### DevOps The intersection of software engineering, quality assurance, and technology operations. Explore more DevOps topics in the [glossary by XebiaLabs](https://xebialabs.com/glossary/) @@ -160,7 +160,7 @@ A [feature](https://docs.gitlab.com/ce/user/project/container_registry.html) of ### ElasticSearch -Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data. +Elasticsearch is a flexible, scalable and powerful search service. When [enabled](https://gitlab.com/help/integration/elasticsearch.md), it helps keep GitLab's search fast when dealing with a huge amount of data. ### Emacs @@ -174,7 +174,7 @@ A code review [tool](https://www.gerritcodereview.com/) built on top of Git. ### Git Attributes -A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames. +A [git attributes file](https://git-scm.com/docs/gitattributes) is a simple text file that gives attributes to pathnames. ### Git Hooks @@ -209,7 +209,7 @@ Our free SaaS for public and private repositories. Allows you to replicate your GitLab instance to other geographical locations as a read-only fully operational version. It [can be used](https://docs.gitlab.com/ee/gitlab-geo/README.html) for cloning and fetching projects, in addition to reading any data. This will make working with large repositories over large distances much faster. ### GitLab Pages -These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account. +These allow you to [create websites](https://gitlab.com/help/pages/README.md) for your GitLab projects, groups, or user account. ### Gitolite @@ -253,7 +253,7 @@ A [tool](https://docs.gitlab.com/ee/integration/external-issue-tracker.html) use ### Jenkins -An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. +An Open Source CI tool written using the Java programming language. [Jenkins](https://jenkins-ci.org/) does the same job as GitLab CI, Bamboo, and Travis CI. It is extremely popular. ### Jira @@ -289,7 +289,7 @@ Allows you to synchronize the members of a GitLab group with one or more LDAP gr ### Load Balancer -A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers. +A [device](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that distributes network or application traffic across multiple servers. ### Git Large File Storage (LFS) @@ -301,7 +301,7 @@ An operating system like Windows or OS X. It is mostly used by software develope ### Markdown -A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md). +A lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name. Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. Checkout GitLab's [Markdown guide](https://gitlab.com/help/user/markdown.md). ### Maria DB @@ -313,11 +313,11 @@ Name of the [default branch](https://git-scm.com/book/en/v1/Git-Branching-What-a ### Mattermost -An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost). +An open source, self-hosted messaging alternative to Slack. View GitLab's Mattermost [feature](https://gitlab.com/gitlab-org/gitlab-mattermost). ### Mercurial -A free distributed version control system similar to and a competitor with Git. +A free distributed version control system similar to and a competitor with Git. ### Merge @@ -325,7 +325,7 @@ Takes changes from one branch, and [applies them](https://git-scm.com/docs/git-m ### Merge Conflict -[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file. +[Arises](https://about.gitlab.com/2016/09/06/resolving-merge-conflicts-from-the-gitlab-ui/) when a merge can't be performed cleanly between two versions of the same file. ### Meteor @@ -345,7 +345,7 @@ A type of software license. It lets people do anything with your code with prope ### Mondo Rescue -A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi). +A free disaster recovery [software](https://help.ubuntu.com/community/MondoMindi). ### MySQL @@ -361,7 +361,7 @@ A web [server](https://www.nginx.com/resources/wiki/) (pronounced "engine x"). I ### OAuth -An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](https://docs.gitlab.com/ce/integration/oauth_provider.html) an OAuth2 authentication service provider. +An open standard for authorization, commonly used as a way for internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password. GitLab [is](https://docs.gitlab.com/ce/integration/oauth_provider.html) an OAuth2 authentication service provider. ### Omnibus Packages @@ -371,13 +371,13 @@ A way to [package different services and tools](https://docs.gitlab.com/omnibus/ On your own server. In GitLab, this [refers](https://about.gitlab.com/2015/02/12/why-ship-on-premises-in-the-saas-era/) to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com, which is hosted by GitLab Inc's servers. -### Open Core +### Open Core GitLab's [business model](https://about.gitlab.com/2016/07/20/gitlab-is-open-core-github-is-closed-source/). Coined by Andrew Lampitt in 2008, the [open core model](https://en.wikipedia.org/wiki/Open_core) primarily involves offering a "core" or feature-limited version of a software product as free and open-source software, while offering "commercial" versions or add-ons as proprietary software. ### Open Source Software -Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/). +Software for which the original source code is freely [available](https://opensource.org/docs/osd) and may be redistributed and modified. GitLab prioritizes open source [stewardship](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/). ### Owner @@ -405,7 +405,7 @@ GitLab Premium EE [subscription](https://about.gitlab.com/pricing/) that include ### PostgreSQL -An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL. +An [object-relational](https://en.wikipedia.org/wiki/PostgreSQL) database. Touted as the most advanced open source database, it is one of two database management systems [supported by](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/database.md) GitLab, the other being MySQL. ### Protected Branches @@ -421,7 +421,7 @@ A popular DevOps [automation tool](https://puppet.com/product/how-puppet-works). ### Push -Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab. +Git [command](https://git-scm.com/docs/git-push) to send commits from the local repository to the remote repository. Read about [advanced push rules](https://gitlab.com/help/pages/README.md) in GitLab. ### RE Read Only @@ -429,7 +429,7 @@ Permissions to see a file and its contents, but not change it. ### Rebase -In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another. +In addition to the merge, the [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) is a main way to integrate changes from one branch into another. ### (Git) Repository @@ -449,7 +449,7 @@ An open source chat application for teams, RocketChat is very similar to Slack b ### Route Table -A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table. +A route table contains rules (called routes) that determine where network traffic is directed. Each [subnet in a VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Route_Tables.html) must be associated with a route table. ### Runners @@ -477,15 +477,15 @@ The board used to track the status and progress of each of the sprint backlog it ### Shell -Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git]() and make changes to GitLab projects in your shell. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) and make changes to GitLab projects in your shell. +Terminal on Mac OSX, GitBash on Windows, or Linux Terminal on Linux. You [use git]() and make changes to GitLab projects in your shell. You [use git](https://docs.gitlab.com/ce/gitlab-basics/start-using-git.html) and make changes to GitLab projects in your shell. ### Single-tenant -The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances. +The tenant purchases their own copy of the software and the software can be customized to meet the specific and needs of that customer. [GitHost.io](https://about.gitlab.com/handbook/positioning-faq/) is our provider of single-tenant 'managed cloud' GitLab instances. ### Slack -Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](https://docs.gitlab.com/ce/project_services/slack.html) to trigger push, issue, and merge request events among others. +Real time messaging app for teams that is used internally by GitLab team members. GitLab users can enable [Slack integration](https://docs.gitlab.com/ce/project_services/slack.html) to trigger push, issue, and merge request events among others. ### Slave Servers @@ -529,7 +529,7 @@ A program that allows you to perform superuser/administrator actions on Unix Ope ### Subversion (SVN) -An open source version control system. Read about [migrating from SVN](https://docs.gitlab.com/ce/workflow/importing/migrating_from_svn.html) to GitLab using SubGit. +An open source version control system. Read about [migrating from SVN](https://docs.gitlab.com/ce/workflow/importing/migrating_from_svn.html) to GitLab using SubGit. ### Tag @@ -545,7 +545,7 @@ An open source project management and bug tracking web [application](https://tra ### Untracked files -New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously. +New files that Git has not [been told](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository) to track previously. ### User @@ -553,11 +553,11 @@ Anyone interacting with the software. ### Version Control Software (VCS) -Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present distributed version control systems like Git, Mercurial, Bazaar, and Darcs. +Version control is a system that records changes to a file or set of files over time so that you can recall specific versions later. VCS [has evolved](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.gd69537a19_0_32) from local version control systems, to centralized version control systems, to the present distributed version control systems like Git, Mercurial, Bazaar, and Darcs. ### Virtual Private Cloud (VPC) -An on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/). +An on demand configurable pool of shared computing resources allocated within a public cloud environment, providing some isolation between the different users using the resources. GitLab users need to create a new Amazon VPC in order to [setup High Availability](https://docs.gitlab.com/ce/university/high-availability/aws/). ### Virtual private server (VPS) @@ -565,15 +565,15 @@ A [virtual machine](https://en.wikipedia.org/wiki/Virtual_private_server) sold a ### VM Instance -In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any object. An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system. +In object-oriented programming, an [instance](http://stackoverflow.com/questions/20461907/what-is-meaning-of-instance-in-programming) is a specific realization of any object. An object may be varied in a number of ways. Each realized variation of that object is an instance. Therefore, a VM instance is an instance of a virtual machine, which is an emulation of a computer system. ### Waterfall -A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. +A [model](http://www.umsl.edu/~hugheyd/is6840/waterfall.html) of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the complete software to the customer that meets all the requirements they specified. ### Webhooks -A way for for an app to [provide](https://docs.gitlab.com/ce/user/project/integrations/webhooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](https://gitlab.com/help/administration/custom_hooks.md) for when webhooks are insufficient. +A way for for an app to [provide](https://docs.gitlab.com/ce/user/project/integrations/webhooks.html) other applications with real-time information (e.g., send a message to a slack channel when a commit is pushed.) Read about setting up [custom git hooks](https://gitlab.com/help/administration/custom_hooks.md) for when webhooks are insufficient. ### Wiki @@ -585,5 +585,5 @@ A [website/system](http://www.wiki.com/) that allows for collaborative editing o ### YAML -A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail. +A human-readable data serialization [language](http://www.yaml.org/about.html) that takes concepts from programming languages such as C, Perl, and Python, and ideas from XML and the data format of electronic mail. diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md index 34e2e557f89..b8d24cb2d3b 100644 --- a/doc/user/admin_area/settings/continuous_integration.md +++ b/doc/user/admin_area/settings/continuous_integration.md @@ -2,9 +2,9 @@ ## Maximum artifacts size -The maximum size of the [build artifacts][art-yml] can be set in the Admin area +The maximum size of the [job artifacts][art-yml] can be set in the Admin area of your GitLab instance. The value is in MB and the default is 100MB. Note that -this setting is set for each build. +this setting is set for each job. 1. Go to **Admin area > Settings** (`/admin/application_settings`). @@ -17,4 +17,4 @@ this setting is set for each build. 1. Hit **Save** for the changes to take effect. -[art-yml]: ../../../administration/build_artifacts.md +[art-yml]: ../../../administration/job_artifacts.md diff --git a/doc/user/permissions.md b/doc/user/permissions.md index e87cae092a5..b49a244160a 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -22,9 +22,9 @@ The following table depicts the various user permission levels in a project. | Create confidential issue | ✓ | ✓ | ✓ | ✓ | ✓ | | View confidential issues | (✓) [^1] | ✓ | ✓ | ✓ | ✓ | | Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ | -| See a list of builds | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | -| See a build log | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | -| Download and browse build artifacts | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | +| See a list of jobs | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | +| See a job log | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | +| Download and browse job artifacts | ✓ [^2] | ✓ | ✓ | ✓ | ✓ | | View wiki pages | ✓ | ✓ | ✓ | ✓ | ✓ | | Pull project code | | ✓ | ✓ | ✓ | ✓ | | Download project | | ✓ | ✓ | ✓ | ✓ | @@ -46,7 +46,7 @@ The following table depicts the various user permission levels in a project. | Remove non-protected branches | | | ✓ | ✓ | ✓ | | Add tags | | | ✓ | ✓ | ✓ | | Write a wiki | | | ✓ | ✓ | ✓ | -| Cancel and retry builds | | | ✓ | ✓ | ✓ | +| Cancel and retry jobs | | | ✓ | ✓ | ✓ | | Create or update commit status | | | ✓ | ✓ | ✓ | | Update a container registry | | | ✓ | ✓ | ✓ | | Remove a container registry image | | | ✓ | ✓ | ✓ | @@ -60,7 +60,7 @@ The following table depicts the various user permission levels in a project. | Add deploy keys to project | | | | ✓ | ✓ | | Configure project hooks | | | | ✓ | ✓ | | Manage runners | | | | ✓ | ✓ | -| Manage build triggers | | | | ✓ | ✓ | +| Manage job triggers | | | | ✓ | ✓ | | Manage variables | | | | ✓ | ✓ | | Manage pages | | | | ✓ | ✓ | | Manage pages domains and certificates | | | | ✓ | ✓ | @@ -134,8 +134,8 @@ instance and project. In addition, all admins can use the admin interface under | Action | Guest, Reporter | Developer | Master | Admin | |---------------------------------------|-----------------|-------------|----------|--------| -| See commits and builds | ✓ | ✓ | ✓ | ✓ | -| Retry or cancel build | | ✓ | ✓ | ✓ | +| See commits and jobs | ✓ | ✓ | ✓ | ✓ | +| Retry or cancel job | | ✓ | ✓ | ✓ | | Remove project | | | ✓ | ✓ | | Create project | | | ✓ | ✓ | | Change project configuration | | | ✓ | ✓ | @@ -144,18 +144,18 @@ instance and project. In addition, all admins can use the admin interface under | See events in the system | | | | ✓ | | Admin interface | | | | ✓ | -### Builds permissions +### Jobs permissions >**Note:** -GitLab 8.12 has a completely redesigned build permissions system. +GitLab 8.12 has a completely redesigned job permissions system. Read all about the [new model and its implications][new-mod]. -This table shows granted privileges for builds triggered by specific types of +This table shows granted privileges for jobs triggered by specific types of users: | Action | Guest, Reporter | Developer | Master | Admin | |---------------------------------------------|-----------------|-------------|----------|--------| -| Run CI build | | ✓ | ✓ | ✓ | +| Run CI job | | ✓ | ✓ | ✓ | | Clone source and LFS from current project | | ✓ | ✓ | ✓ | | Clone source and LFS from public projects | | ✓ | ✓ | ✓ | | Clone source and LFS from internal projects | | ✓ [^4] | ✓ [^4] | ✓ | diff --git a/doc/user/project/builds/artifacts.md b/doc/user/project/builds/artifacts.md index 88f1863dddb..514c729b37d 100644 --- a/doc/user/project/builds/artifacts.md +++ b/doc/user/project/builds/artifacts.md @@ -1,136 +1 @@ -# Introduction to build artifacts - ->**Notes:** ->- Since GitLab 8.2 and GitLab Runner 0.7.0, build artifacts that are created by - GitLab Runner are uploaded to GitLab and are downloadable as a single archive - (`tar.gz`) using the GitLab UI. ->- Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format - changed to `ZIP`, and it is now possible to browse its contents, with the added - ability of downloading the files separately. ->- The artifacts browser will be available only for new artifacts that are sent - to GitLab using GitLab Runner version 1.0 and up. It will not be possible to - browse old artifacts already uploaded to GitLab. ->- This is the user documentation. For the administration guide see - [administration/build_artifacts.md](../../../administration/build_artifacts.md). - -Artifacts is a list of files and directories which are attached to a build -after it completes successfully. This feature is enabled by default in all GitLab installations. - -## Defining artifacts in `.gitlab-ci.yml` - -A simple example of using the artifacts definition in `.gitlab-ci.yml` would be -the following: - -```yaml -pdf: - script: xelatex mycv.tex - artifacts: - paths: - - mycv.pdf -``` - -A job named `pdf` calls the `xelatex` command in order to build a pdf file from -the latex source file `mycv.tex`. We then define the `artifacts` paths which in -turn are defined with the `paths` keyword. All paths to files and directories -are relative to the repository that was cloned during the build. - -For more examples on artifacts, follow the artifacts reference in -[`.gitlab-ci.yml` documentation](../../../ci/yaml/README.md#artifacts). - -## Browsing build artifacts - -When GitLab receives an artifacts archive, an archive metadata file is also -generated. This metadata file describes all the entries that are located in the -artifacts archive itself. The metadata file is in a binary format, with -additional GZIP compression. - -GitLab does not extract the artifacts archive in order to save space, memory -and disk I/O. It instead inspects the metadata file which contains all the -relevant information. This is especially important when there is a lot of -artifacts, or an archive is a very large file. - ---- - -After a build finishes, if you visit the build's specific page, you can see -that there are two buttons. One is for downloading the artifacts archive and -the other for browsing its contents. - -![Build artifacts browser button](img/build_artifacts_browser_button.png) - ---- - -The archive browser shows the name and the actual file size of each file in the -archive. If your artifacts contained directories, then you are also able to -browse inside them. - -Below you can see how browsing looks like. In this case we have browsed inside -the archive and at this point there is one directory and one HTML file. - -![Build artifacts browser](img/build_artifacts_browser.png) - ---- - -## Downloading build artifacts - ->**Note:** -GitLab does not extract the entire artifacts archive to send just a single file -to the user. When clicking on a specific file, [GitLab Workhorse] extracts it -from the archive and the download begins. This implementation saves space, -memory and disk I/O. - -If you need to download the whole archive, there are buttons in various places -inside GitLab that make that possible. - -1. While on the pipelines page, you can see the download icon for each build's - artifacts archive in the right corner: - - ![Build artifacts in Pipelines page](img/build_artifacts_pipelines_page.png) - -1. While on the builds page, you can see the download icon for each build's - artifacts archive in the right corner: - - ![Build artifacts in Builds page](img/build_artifacts_builds_page.png) - -1. While inside a specific build, you are presented with a download button - along with the one that browses the archive: - - ![Build artifacts browser button](img/build_artifacts_browser_button.png) - -1. And finally, when browsing an archive you can see the download button at - the top right corner: - - ![Build artifacts browser](img/build_artifacts_browser.png) - -## Downloading the latest build artifacts - -It is possible to download the latest artifacts of a build via a well known URL -so you can use it for scripting purposes. - -The structure of the URL is the following: - -``` -https://example.com///builds/artifacts//download?job= -``` - -For example, to download the latest artifacts of the job named `rspec 6 20` of -the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org` -namespace, the URL would be: - -``` -https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=rspec+6+20 -``` - -The latest builds are also exposed in the UI in various places. Specifically, -look for the download button in: - -- the main project's page -- the branches page -- the tags page - -If the latest build has failed to upload the artifacts, you can see that -information in the UI. - -![Latest artifacts button](img/build_latest_artifacts_browser.png) - - -[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository" +This document was moved to [pipelines/job_artifacts](../pipelines/job_artifacts.md). diff --git a/doc/user/project/builds/img/build_artifacts_browser.png b/doc/user/project/builds/img/build_artifacts_browser.png deleted file mode 100644 index 686273948d6..00000000000 Binary files a/doc/user/project/builds/img/build_artifacts_browser.png and /dev/null differ diff --git a/doc/user/project/builds/img/build_artifacts_browser_button.png b/doc/user/project/builds/img/build_artifacts_browser_button.png deleted file mode 100644 index 33ef7de0415..00000000000 Binary files a/doc/user/project/builds/img/build_artifacts_browser_button.png and /dev/null differ diff --git a/doc/user/project/builds/img/build_artifacts_builds_page.png b/doc/user/project/builds/img/build_artifacts_builds_page.png deleted file mode 100644 index 8f75602d592..00000000000 Binary files a/doc/user/project/builds/img/build_artifacts_builds_page.png and /dev/null differ diff --git a/doc/user/project/builds/img/build_artifacts_pipelines_page.png b/doc/user/project/builds/img/build_artifacts_pipelines_page.png deleted file mode 100644 index 4bbd00ddaa0..00000000000 Binary files a/doc/user/project/builds/img/build_artifacts_pipelines_page.png and /dev/null differ diff --git a/doc/user/project/builds/img/build_latest_artifacts_browser.png b/doc/user/project/builds/img/build_latest_artifacts_browser.png deleted file mode 100644 index c6d8856078b..00000000000 Binary files a/doc/user/project/builds/img/build_latest_artifacts_browser.png and /dev/null differ diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md index 55d480bfb72..ed1e867f5fb 100644 --- a/doc/user/project/integrations/webhooks.md +++ b/doc/user/project/integrations/webhooks.md @@ -11,7 +11,7 @@ a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. GitLab will send a POST request with data to the webhook URL. -Webhooks can be used to update an external issue tracker, trigger CI builds, +Webhooks can be used to update an external issue tracker, trigger CI jobs, update a backup mirror, or even deploy to your production server. Navigate to the webhooks page by going to the **Integrations** page from your diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index a27e683143d..abd3740ef20 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -22,7 +22,7 @@ in a merged merge requests or a commit. ## Merge when pipeline succeeds When reviewing a merge request that looks ready to merge but still has one or -more CI builds running, you can set it to be merged automatically when CI +more CI jobs running, you can set it to be merged automatically when CI pipeline succeeds. This way, you don't have to wait for the pipeline to finish and remember to merge the request manually. diff --git a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md index 75ad18b28cf..c63a408505f 100644 --- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md +++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md @@ -1,8 +1,8 @@ # Merge When Pipeline Succeeds When reviewing a merge request that looks ready to merge but still has one or -more CI builds running, you can set it to be merged automatically when the -builds pipeline succeeds. This way, you don't have to wait for the builds to +more CI jobs running, you can set it to be merged automatically when the +jobs pipeline succeeds. This way, you don't have to wait for the jobs to finish and remember to merge the request manually. ![Enable](img/merge_when_build_succeeds_enable.png) @@ -19,10 +19,10 @@ after all. ![Status](img/merge_when_build_succeeds_status.png) When the pipeline succeeds, the merge request will automatically be merged. -When the pipeline fails, the author gets a chance to retry any failed builds, +When the pipeline fails, the author gets a chance to retry any failed jobs, or to push new commits to fix the failure. -When the builds are retried and succeed on the second try, the merge request +When the jobs are retried and succeed on the second try, the merge request will automatically be merged after all. When the merge request is updated with new commits, the automatic merge is automatically canceled to allow the new changes to be reviewed. @@ -30,7 +30,7 @@ changes to be reviewed. ## Only allow merge requests to be merged if the pipeline succeeds > **Note:** -You need to have builds configured to enable this feature. +You need to have jobs configured to enable this feature. You can prevent merge requests from being merged if their pipeline did not succeed. @@ -41,6 +41,6 @@ hit **Save** for the changes to take effect. ![Only allow merge if pipeline succeeds settings](img/merge_when_build_succeeds_only_if_succeeds_settings.png) From now on, every time the pipeline fails you will not be able to merge the -merge request from the UI, until you make all relevant builds pass. +merge request from the UI, until you make all relevant jobs pass. ![Only allow merge if pipeline succeeds message](img/merge_when_build_succeeds_only_if_succeeds_msg.png) diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md index 320faff65c5..5f631f63050 100644 --- a/doc/user/project/new_ci_build_permissions_model.md +++ b/doc/user/project/new_ci_build_permissions_model.md @@ -1,55 +1,55 @@ -# New CI build permissions model +# New CI job permissions model > Introduced in GitLab 8.12. -GitLab 8.12 has a completely redesigned [build permissions] system. You can find +GitLab 8.12 has a completely redesigned [job permissions] system. You can find all discussion and all our concerns when choosing the current approach in issue [#18994](https://gitlab.com/gitlab-org/gitlab-ce/issues/18994). --- -Builds permissions should be tightly integrated with the permissions of a user -who is triggering a build. +Jobs permissions should be tightly integrated with the permissions of a user +who is triggering a job. The reasons to do it like that are: - We already have a permissions system in place: group and project membership of users. -- We already fully know who is triggering a build (using `git push`, using the +- We already fully know who is triggering a job (using `git push`, using the web UI, executing triggers). - We already know what user is allowed to do. -- We use the user permissions for builds that are triggered by the user. +- We use the user permissions for jobs that are triggered by the user. - It opens a lot of possibilities to further enforce user permissions, like allowing only specific users to access runners or use secure variables and environments. -- It is simple and convenient that your build can access everything that you +- It is simple and convenient that your job can access everything that you as a user have access to. -- Short living unique tokens are now used, granting access for time of the build +- Short living unique tokens are now used, granting access for time of the job and maximizing security. -With the new behavior, any build that is triggered by the user, is also marked +With the new behavior, any job that is triggered by the user, is also marked with their permissions. When a user does a `git push` or changes files through the web UI, a new pipeline will be usually created. This pipeline will be marked -as created be the pusher (local push or via the UI) and any build created in this +as created be the pusher (local push or via the UI) and any job created in this pipeline will have the permissions of the pusher. This allows us to make it really easy to evaluate the access for all projects that have [Git submodules][gitsub] or are using container images that the pusher -would have access too. **The permission is granted only for time that build is -running. The access is revoked after the build is finished.** +would have access too. **The permission is granted only for time that job is +running. The access is revoked after the job is finished.** ## Types of users It is important to note that we have a few types of users: -- **Administrators**: CI builds created by Administrators will not have access +- **Administrators**: CI jobs created by Administrators will not have access to all GitLab projects, but only to projects and container images of projects that the administrator is a member of.That means that if a project is either public or internal users have access anyway, but if a project is private, the Administrator will have to be a member of it in order to have access to it - via another project's build. + via another project's job. -- **External users**: CI builds created by [external users][ext] will have +- **External users**: CI jobs created by [external users][ext] will have access only to projects to which user has at least reporter access. This rules out accessing all internal projects by default, @@ -57,46 +57,46 @@ This allows us to make the CI and permission system more trustworthy. Let's consider the following scenario: 1. You are an employee of a company. Your company has a number of internal tools - hosted in private repositories and you have multiple CI builds that make use + hosted in private repositories and you have multiple CI jobs that make use of these repositories. -2. You invite a new [external user][ext]. CI builds created by that user do not +2. You invite a new [external user][ext]. CI jobs created by that user do not have access to internal repositories, because the user also doesn't have the access from within GitLab. You as an employee have to grant explicit access for this user. This allows us to prevent from accidental data leakage. -## Build token +## Job token -A unique build token is generated for each build and it allows the user to +A unique job token is generated for each job and it allows the user to access all projects that would be normally accessible to the user creating that -build. +job. We try to make sure that this token doesn't leak by: -1. Securing all API endpoints to not expose the build token. -1. Masking the build token from build logs. -1. Allowing to use the build token **only** when build is running. +1. Securing all API endpoints to not expose the job token. +1. Masking the job token from job logs. +1. Allowing to use the job token **only** when job is running. However, this brings a question about the Runners security. To make sure that this token doesn't leak, you should also make sure that you configure your Runners in the most possible secure way, by avoiding the following: 1. Any usage of Docker's `privileged` mode is risky if the machines are re-used. -1. Using the `shell` executor since builds run on the same machine. +1. Using the `shell` executor since jobs run on the same machine. By using an insecure GitLab Runner configuration, you allow the rogue developers -to steal the tokens of other builds. +to steal the tokens of other jobs. -## Build triggers +## job triggers -[Build triggers][triggers] do not support the new permission model. -They continue to use the old authentication mechanism where the CI build +[job triggers][triggers] do not support the new permission model. +They continue to use the old authentication mechanism where the CI job can access only its own sources. We plan to remove that limitation in one of the upcoming releases. ## Before GitLab 8.12 -In versions before GitLab 8.12, all CI builds would use the CI Runner's token +In versions before GitLab 8.12, all CI jobs would use the CI Runner's token to checkout project sources. The project's Runner's token was a token that you could find under the @@ -105,7 +105,7 @@ project. It could be used for registering new specific Runners assigned to the project and to checkout project sources. It could also be used with the GitLab Container Registry for that project, -allowing pulling and pushing Docker images from within the CI build. +allowing pulling and pushing Docker images from within the CI job. --- @@ -115,7 +115,7 @@ GitLab would create a special checkout URL like: https://gitlab-ci-token:/gitlab.com/gitlab-org/gitlab-ce.git ``` -And then the users could also use it in their CI builds all Docker related +And then the users could also use it in their CI jobs all Docker related commands to interact with GitLab Container Registry. For example: ``` @@ -125,7 +125,7 @@ docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com Using single token had multiple security implications: - The token would be readable to anyone who had developer access to a project - that could run CI builds, allowing the developer to register any specific + that could run CI jobs, allowing the developer to register any specific Runner for that project. - The token would allow to access only the project's sources, forbidding from accessing any other projects. @@ -133,12 +133,12 @@ Using single token had multiple security implications: for registering specific runners and for accessing a project's container registry with read-write permissions. -All the above led to a new permission model for builds that was introduced +All the above led to a new permission model for jobs that was introduced with GitLab 8.12. -## Making use of the new CI build permissions model +## Making use of the new CI job permissions model -With the new build permissions model, there is now an easy way to access all +With the new job permissions model, there is now an easy way to access all dependent source code in a project. That way, we can: 1. Access a project's [Git submodules][gitsub] @@ -151,9 +151,9 @@ the container registry. ### Prerequisites to use the new permissions model -With the new permissions model in place, there may be times that your build will +With the new permissions model in place, there may be times that your job will fail. This is most likely because your project tries to access other project's -sources, and you don't have the appropriate permissions. In the build log look +sources, and you don't have the appropriate permissions. In the job log look for information about 403 or forbidden access messages. In short here's what you need to do should you encounter any issues. @@ -175,7 +175,7 @@ As a user: - Make sure you are a member of the group or project you're trying to have access to. As an Administrator, you can verify that by impersonating the user - and retry the failing build in order to verify that everything is correct. + and retry the failing job in order to verify that everything is correct. ### Git submodules @@ -199,9 +199,9 @@ Container Registries for private projects. to pass a personal access token instead of your password in order to login to GitLab's Container Registry. -Your builds can access all container images that you would normally have access +Your jobs can access all container images that you would normally have access to. The only implication is that you can push to the Container Registry of the -project for which the build is triggered. +project for which the job is triggered. This is how an example usage can look like: @@ -213,7 +213,7 @@ test: - docker run $CI_REGISTRY/group/other-project:latest ``` -[build permissions]: ../permissions.md#builds-permissions +[job permissions]: ../permissions.md#jobs-permissions [comment]: https://gitlab.com/gitlab-org/gitlab-ce/issues/22484#note_16648302 [ext]: ../permissions.md#external-users [gitsub]: ../../ci/git_submodules.md diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index b814e3fccb2..044c89a03c0 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -100,7 +100,7 @@ whereas a group's project under `http(s)://groupname.example.io/projectname`. The key thing about GitLab Pages is the `.gitlab-ci.yml` file, something that gives you absolute control over the build process. You can actually watch your -website being built live by following the CI build traces. +website being built live by following the CI job traces. > **Note:** > Before reading this section, make sure you familiarize yourself with GitLab CI @@ -127,7 +127,7 @@ pages: ``` When the Runner reaches to build the `pages` job, it executes whatever is -defined in the `script` parameter and if the build completes with a non-zero +defined in the `script` parameter and if the job completes with a non-zero exit status, it then uploads the `public/` directory to GitLab Pages. The `public/` directory should contain all the static content of your website. @@ -211,11 +211,11 @@ pages: # the build job must be named pages ``` Here, we used the Docker executor and in the first line we specified the base -image against which our builds will run. +image against which our jobs will run. You have to make sure that the generated static files are ultimately placed under the `public` directory, that's why in the `script` section we run the -`jekyll` command that builds the website and puts all content in the `public/` +`jekyll` command that jobs the website and puts all content in the `public/` directory. Depending on the static generator of your choice, this command will differ. Search in the documentation of the static generator you will use if there is an option to explicitly set the output directory. If there is not @@ -403,7 +403,7 @@ don't have to create and edit HTML files manually. For example, Jekyll has the ### Can I download my generated pages? -Sure. All you need to do is download the artifacts archive from the build page. +Sure. All you need to do is download the artifacts archive from the job page. ### Can I use GitLab Pages if my project is private? diff --git a/doc/user/project/pipelines/img/job_artifacts_browser.png b/doc/user/project/pipelines/img/job_artifacts_browser.png new file mode 100644 index 00000000000..145fe156bbb Binary files /dev/null and b/doc/user/project/pipelines/img/job_artifacts_browser.png differ diff --git a/doc/user/project/pipelines/img/job_artifacts_browser_button.png b/doc/user/project/pipelines/img/job_artifacts_browser_button.png new file mode 100644 index 00000000000..21072ce1248 Binary files /dev/null and b/doc/user/project/pipelines/img/job_artifacts_browser_button.png differ diff --git a/doc/user/project/pipelines/img/job_artifacts_builds_page.png b/doc/user/project/pipelines/img/job_artifacts_builds_page.png new file mode 100644 index 00000000000..13e039ba934 Binary files /dev/null and b/doc/user/project/pipelines/img/job_artifacts_builds_page.png differ diff --git a/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png new file mode 100644 index 00000000000..3ccce4f9bb4 Binary files /dev/null and b/doc/user/project/pipelines/img/job_artifacts_pipelines_page.png differ diff --git a/doc/user/project/pipelines/img/job_latest_artifacts_browser.png b/doc/user/project/pipelines/img/job_latest_artifacts_browser.png new file mode 100644 index 00000000000..c6d8856078b Binary files /dev/null and b/doc/user/project/pipelines/img/job_latest_artifacts_browser.png differ diff --git a/doc/user/project/pipelines/img/pipelines_settings_test_coverage.png b/doc/user/project/pipelines/img/pipelines_settings_test_coverage.png index 2a99201e014..13ed69be810 100644 Binary files a/doc/user/project/pipelines/img/pipelines_settings_test_coverage.png and b/doc/user/project/pipelines/img/pipelines_settings_test_coverage.png differ diff --git a/doc/user/project/pipelines/img/pipelines_test_coverage_mr_widget.png b/doc/user/project/pipelines/img/pipelines_test_coverage_mr_widget.png index c166bb8bec8..fbcd612f3f2 100644 Binary files a/doc/user/project/pipelines/img/pipelines_test_coverage_mr_widget.png and b/doc/user/project/pipelines/img/pipelines_test_coverage_mr_widget.png differ diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md new file mode 100644 index 00000000000..f85f4bf8e1e --- /dev/null +++ b/doc/user/project/pipelines/job_artifacts.md @@ -0,0 +1,118 @@ +# Introduction to job artifacts + +>**Notes:** +>- Since GitLab 8.2 and GitLab Runner 0.7.0, job artifacts that are created by + GitLab Runner are uploaded to GitLab and are downloadable as a single archive + (`tar.gz`) using the GitLab UI. +>- Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format + changed to `ZIP`, and it is now possible to browse its contents, with the added + ability of downloading the files separately. +>- Starting with GitLab 8.17, builds are renamed to jobs. +>- The artifacts browser will be available only for new artifacts that are sent + to GitLab using GitLab Runner version 1.0 and up. It will not be possible to + browse old artifacts already uploaded to GitLab. +>- This is the user documentation. For the administration guide see + [administration/job_artifacts.md](../../../administration/job_artifacts.md). + +Artifacts is a list of files and directories which are attached to a job +after it completes successfully. This feature is enabled by default in all +GitLab installations. + +## Defining artifacts in `.gitlab-ci.yml` + +A simple example of using the artifacts definition in `.gitlab-ci.yml` would be +the following: + +```yaml +pdf: + script: xelatex mycv.tex + artifacts: + paths: + - mycv.pdf +``` + +A job named `pdf` calls the `xelatex` command in order to build a pdf file from +the latex source file `mycv.tex`. We then define the `artifacts` paths which in +turn are defined with the `paths` keyword. All paths to files and directories +are relative to the repository that was cloned during the build. + +For more examples on artifacts, follow the artifacts reference in +[`.gitlab-ci.yml` documentation](../../../ci/yaml/README.md#artifacts). + +## Browsing job artifacts + +After a job finishes, if you visit the job's specific page, you can see +that there are two buttons. One is for downloading the artifacts archive and +the other for browsing its contents. + +![Job artifacts browser button](img/job_artifacts_browser_button.png) + +--- + +The archive browser shows the name and the actual file size of each file in the +archive. If your artifacts contained directories, then you are also able to +browse inside them. + +Below you can see how browsing looks like. In this case we have browsed inside +the archive and at this point there is one directory and one HTML file. + +![Job artifacts browser](img/job_artifacts_browser.png) + +--- + +## Downloading job artifacts + +If you need to download the whole archive, there are buttons in various places +inside GitLab that make that possible. + +1. While on the pipelines page, you can see the download icon for each job's + artifacts archive in the right corner: + + ![Job artifacts in Pipelines page](img/job_artifacts_pipelines_page.png) + +1. While on the **Jobs** page, you can see the download icon for each job's + artifacts archive in the right corner: + + ![Job artifacts in Builds page](img/job_artifacts_builds_page.png) + +1. While inside a specific job, you are presented with a download button + along with the one that browses the archive: + + ![Job artifacts browser button](img/job_artifacts_browser_button.png) + +1. And finally, when browsing an archive you can see the download button at + the top right corner: + + ![Job artifacts browser](img/job_artifacts_browser.png) + +## Downloading the latest job artifacts + +It is possible to download the latest artifacts of a job via a well known URL +so you can use it for scripting purposes. + +The structure of the URL is the following: + +``` +https://example.com///builds/artifacts//download?job= +``` + +For example, to download the latest artifacts of the job named `rspec 6 20` of +the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org` +namespace, the URL would be: + +``` +https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=rspec+6+20 +``` + +The latest builds are also exposed in the UI in various places. Specifically, +look for the download button in: + +- the main project's page +- the branches page +- the tags page + +If the latest job has failed to upload the artifacts, you can see that +information in the UI. + +![Latest artifacts button](img/job_latest_artifacts_browser.png) + diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md index 6cbcf3c400f..80cdb49a1d3 100644 --- a/doc/user/project/pipelines/settings.md +++ b/doc/user/project/pipelines/settings.md @@ -35,7 +35,7 @@ if the job surpasses the threshold, it is marked as failed. ## Test coverage parsing If you use test coverage in your code, GitLab can capture its output in the -build log using a regular expression. In the pipelines settings, search for the +job log using a regular expression. In the pipelines settings, search for the "Test coverage parsing" section. ![Pipelines settings test coverage](img/pipelines_settings_test_coverage.png) @@ -44,7 +44,7 @@ Leave blank if you want to disable it or enter a ruby regular expression. You can use http://rubular.com to test your regex. If the pipeline succeeds, the coverage is shown in the merge request widget and -in the builds table. +in the jobs table. ![MR widget coverage](img/pipelines_test_coverage_mr_widget.png) @@ -62,9 +62,9 @@ pipelines** checkbox and save the changes. ## Badges -In the pipelines settings page you can find build status and test coverage +In the pipelines settings page you can find job status and test coverage badges for your project. The latest successful pipeline will be used to read -the build status and test coverage values. +the job status and test coverage values. Visit the pipelines settings page in your project to see the exact link to your badges, as well as ways to embed the badge image in your HTML or Markdown @@ -72,9 +72,9 @@ pages. ![Pipelines badges](img/pipelines_settings_badges.png) -### Build status badge +### Job status badge -Depending on the status of your build, a badge can have the following values: +Depending on the status of your job, a badge can have the following values: - running - success @@ -82,7 +82,7 @@ Depending on the status of your build, a badge can have the following values: - skipped - unknown -You can access a build status badge image using the following link: +You can access a job status badge image using the following link: ``` https://example.gitlab.com///badges//build.svg @@ -91,7 +91,7 @@ https://example.gitlab.com///badges//build.svg ### Test coverage report badge GitLab makes it possible to define the regular expression for [coverage report], -that each build log will be matched against. This means that each build in the +that each job log will be matched against. This means that each job in the pipeline can have the test coverage percentage value defined. The test coverage badge can be accessed using following link: diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md index 2fddd7c6503..ad5d51d34f2 100644 --- a/doc/user/project/slash_commands.md +++ b/doc/user/project/slash_commands.md @@ -14,7 +14,7 @@ do. |:---------------------------|:-------------| | `/close` | Close the issue or merge request | | `/reopen` | Reopen the issue or merge request | -| `/merge` | Merge (when build succeeds) | +| `/merge` | Merge (when pipeline succeeds) | | `/title ` | Change title | | `/assign @username` | Assign | | `/unassign` | Remove assignee | diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md index 36516883ef6..2a5e622dc7d 100644 --- a/doc/workflow/shortcuts.md +++ b/doc/workflow/shortcuts.md @@ -45,7 +45,7 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | g + e | Go to the project's activity feed | | g + f | Go to files | | g + c | Go to commits | -| g + b | Go to builds | +| g + b | Go to jobs | | g + n | Go to network graph | | g + g | Go to graphs | | g + i | Go to issues | @@ -73,4 +73,4 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?' | m | Change milestone | | r | Reply (quoting selected text) | | e | Edit issue/merge request | -| l | Change label | \ No newline at end of file +| l | Change label | diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md index 1a8fc39bb33..99d7c18f072 100644 --- a/doc/workflow/todos.md +++ b/doc/workflow/todos.md @@ -27,8 +27,8 @@ A Todo appears in your Todos dashboard when: - an issue or merge request is assigned to you, - you are `@mentioned` in an issue or merge request, be it the description of the issue/merge request or in a comment, -- build in the CI pipeline running for your merge request failed, but this - build is not allowed to fail. +- a job in the CI pipeline running for your merge request failed, but this + job is not allowed to fail. >**Note:** Commenting on a commit will _not_ trigger a Todo. -- cgit v1.2.1 From 3856a3daa14bdfc2abed28d355a5244e32a81d6a Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 15 Feb 2017 19:16:12 +0800 Subject: Add some tests for admin/project runners page --- spec/controllers/admin/runners_controller_spec.rb | 85 ++++++++++++++++++++++ .../projects/runners_controller_spec.rb | 75 +++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 spec/controllers/admin/runners_controller_spec.rb create mode 100644 spec/controllers/projects/runners_controller_spec.rb diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb new file mode 100644 index 00000000000..ae55ce87f0b --- /dev/null +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe Admin::RunnersController do + let(:runner) { create(:ci_runner) } + + before do + sign_in(create(:admin)) + end + + describe '#index' do + it 'lists all runners' do + get :index + + expect(response).to have_http_status(200) + end + end + + describe '#show' do + it 'shows a particular runner' do + get :show, id: runner.id + + expect(response).to have_http_status(200) + end + + it 'shows 404 for unknown runner' do + get :show, id: 0 + + expect(response).to have_http_status(404) + end + end + + describe '#update' do + it 'updates the runner and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + new_desc = runner.description.swapcase + + post :update, id: runner.id, runner: { description: new_desc } + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.description).to eq(new_desc) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end + + describe '#destroy' do + it 'destroys the runner' do + delete :destroy, id: runner.id + + expect(response).to have_http_status(302) + expect(Ci::Runner.find_by(id: runner.id)).to be_nil + end + end + + describe '#resume' do + it 'marks the runner as active and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + runner.update(active: false) + + post :resume, id: runner.id + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.active).to eq(true) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end + + describe '#pause' do + it 'marks the runner as inactive and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + runner.update(active: true) + + post :pause, id: runner.id + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.active).to eq(false) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end +end diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb new file mode 100644 index 00000000000..6dec12f1815 --- /dev/null +++ b/spec/controllers/projects/runners_controller_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe Projects::RunnersController do + let(:user) { create(:user) } + let(:project) { create(:empty_project) } + let(:runner) { create(:ci_runner) } + + let(:params) do + { + namespace_id: project.namespace, + project_id: project, + id: runner + } + end + + before do + sign_in(user) + project.add_master(user) + project.runners << runner + end + + describe '#update' do + it 'updates the runner and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + new_desc = runner.description.swapcase + + post :update, params.merge(runner: { description: new_desc } ) + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.description).to eq(new_desc) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end + + describe '#destroy' do + it 'destroys the runner' do + delete :destroy, params + + expect(response).to have_http_status(302) + expect(Ci::Runner.find_by(id: runner.id)).to be_nil + end + end + + describe '#resume' do + it 'marks the runner as active and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + runner.update(active: false) + + post :resume, params + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.active).to eq(true) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end + + describe '#pause' do + it 'marks the runner as inactive and ticks the queue' do + old_tick = runner.ensure_runner_queue_value + runner.update(active: true) + + post :pause, params + + runner.reload + + expect(response).to have_http_status(302) + expect(runner.active).to eq(false) + expect(runner.ensure_runner_queue_value).not_to eq(old_tick) + end + end +end -- cgit v1.2.1 From 8fa175613dad6b6ec70136ee090663d521a498c9 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 15 Feb 2017 11:37:29 +0000 Subject: Centers loading icon vertically and horizontally in pipelines table in commit view --- app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 | 4 ++-- changelogs/unreleased/28229-pipelines-loading-icon.yml | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/28229-pipelines-loading-icon.yml diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 index 5983ad4afc2..5c1a7eb1052 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js.es6 @@ -81,8 +81,8 @@ require('./pipelines_store'); }, template: ` -
    -
    +
    +
    diff --git a/changelogs/unreleased/28229-pipelines-loading-icon.yml b/changelogs/unreleased/28229-pipelines-loading-icon.yml new file mode 100644 index 00000000000..d8f82f658c2 --- /dev/null +++ b/changelogs/unreleased/28229-pipelines-loading-icon.yml @@ -0,0 +1,5 @@ +--- +title: Centers loading icon vertically and horizontally in pipelines table in commit + view +merge_request: +author: -- cgit v1.2.1 From 60288d6c62d7e65ed5a93a72ba047ccaa2daa22b Mon Sep 17 00:00:00 2001 From: Lin Jen-Shin Date: Wed, 15 Feb 2017 20:21:51 +0800 Subject: Use expect { }.to change { } Feedback: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8664#note_23427575 --- spec/controllers/admin/runners_controller_spec.rb | 18 +++++++++--------- spec/controllers/projects/runners_controller_spec.rb | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb index ae55ce87f0b..b5fe40d0510 100644 --- a/spec/controllers/admin/runners_controller_spec.rb +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -31,16 +31,16 @@ describe Admin::RunnersController do describe '#update' do it 'updates the runner and ticks the queue' do - old_tick = runner.ensure_runner_queue_value new_desc = runner.description.swapcase - post :update, id: runner.id, runner: { description: new_desc } + expect do + post :update, id: runner.id, runner: { description: new_desc } + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.description).to eq(new_desc) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end @@ -55,31 +55,31 @@ describe Admin::RunnersController do describe '#resume' do it 'marks the runner as active and ticks the queue' do - old_tick = runner.ensure_runner_queue_value runner.update(active: false) - post :resume, id: runner.id + expect do + post :resume, id: runner.id + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.active).to eq(true) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end describe '#pause' do it 'marks the runner as inactive and ticks the queue' do - old_tick = runner.ensure_runner_queue_value runner.update(active: true) - post :pause, id: runner.id + expect do + post :pause, id: runner.id + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.active).to eq(false) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end end diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb index 6dec12f1815..0fa249e4405 100644 --- a/spec/controllers/projects/runners_controller_spec.rb +++ b/spec/controllers/projects/runners_controller_spec.rb @@ -21,16 +21,16 @@ describe Projects::RunnersController do describe '#update' do it 'updates the runner and ticks the queue' do - old_tick = runner.ensure_runner_queue_value new_desc = runner.description.swapcase - post :update, params.merge(runner: { description: new_desc } ) + expect do + post :update, params.merge(runner: { description: new_desc } ) + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.description).to eq(new_desc) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end @@ -45,31 +45,31 @@ describe Projects::RunnersController do describe '#resume' do it 'marks the runner as active and ticks the queue' do - old_tick = runner.ensure_runner_queue_value runner.update(active: false) - post :resume, params + expect do + post :resume, params + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.active).to eq(true) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end describe '#pause' do it 'marks the runner as inactive and ticks the queue' do - old_tick = runner.ensure_runner_queue_value runner.update(active: true) - post :pause, params + expect do + post :pause, params + end.to change { runner.ensure_runner_queue_value } runner.reload expect(response).to have_http_status(302) expect(runner.active).to eq(false) - expect(runner.ensure_runner_queue_value).not_to eq(old_tick) end end end -- cgit v1.2.1 From 7a8d0aab61fa5d59a4bde5330948f1adcfbb542c Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Wed, 15 Feb 2017 18:54:18 +0530 Subject: Ensure only commit comments relevant to target project are returned --- ...projects-commit-comments-are-shared-across-projects.yml | 4 ++++ lib/api/commits.rb | 2 +- spec/requests/api/commits_spec.rb | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml diff --git a/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml b/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml new file mode 100644 index 00000000000..89e2bdc69bc --- /dev/null +++ b/changelogs/unreleased/27873-when-a-commit-appears-in-several-projects-commit-comments-are-shared-across-projects.yml @@ -0,0 +1,4 @@ +--- +title: Only return target project's comments for a commit +merge_request: +author: diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 2fefe760d24..173083d0ade 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -114,7 +114,7 @@ module API commit = user_project.commit(params[:sha]) not_found! 'Commit' unless commit - notes = Note.where(commit_id: commit.id).order(:created_at) + notes = user_project.notes.where(commit_id: commit.id).order(:created_at) present paginate(notes), with: Entities::CommitNote end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index af9028a8978..cb11cf98bf4 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -464,6 +464,20 @@ describe API::Commits, api: true do expect(response).to have_http_status(401) end end + + context 'when the commit is present on two projects' do + let(:forked_project) { create(:project, :repository, creator: user2, namespace: user2.namespace) } + let!(:forked_project_note) { create(:note_on_commit, author: user2, project: forked_project, commit_id: forked_project.repository.commit.id, note: 'a comment on a commit for fork') } + + it 'returns the comments for the target project' do + get api("/projects/#{forked_project.id}/repository/commits/#{forked_project.repository.commit.id}/comments", user2) + + expect(response).to have_http_status(200) + expect(json_response.length).to eq(1) + expect(json_response.first['note']).to eq('a comment on a commit for fork') + expect(json_response.first['author']['id']).to eq(user2.id) + end + end end describe 'POST :id/repository/commits/:sha/cherry_pick' do -- cgit v1.2.1 From e15032eded3b35097409817ddb1844164173ce1a Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Thu, 16 Feb 2017 01:07:29 +1100 Subject: override favicon for development to find tabs more easily --- app/assets/images/favicon-purple.ico | Bin 0 -> 5430 bytes app/helpers/page_layout_helper.rb | 4 ++++ app/views/layouts/_head.html.haml | 2 +- spec/helpers/page_layout_helper_spec.rb | 12 ++++++++++++ 4 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/favicon-purple.ico diff --git a/app/assets/images/favicon-purple.ico b/app/assets/images/favicon-purple.ico new file mode 100644 index 00000000000..71acdf670ab Binary files /dev/null and b/app/assets/images/favicon-purple.ico differ diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 7d4d049101a..aee8099aeb2 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -34,6 +34,10 @@ module PageLayoutHelper end end + def favicon + Rails.env.development? ? 'favicon-purple.ico' : 'favicon.ico' + end + def page_image default = image_url('gitlab_logo.png') diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index f2d355587bd..302c1794628 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -23,7 +23,7 @@ %title= page_title(site_name) %meta{ name: "description", content: page_description } - = favicon_link_tag 'favicon.ico' + = favicon_link_tag favicon = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index dc07657e101..872679a6ce3 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -40,6 +40,18 @@ describe PageLayoutHelper do end end + describe 'favicon' do + it 'defaults to favicon.ico' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production')) + expect(helper.favicon).to eq 'favicon.ico' + end + + it 'has purple favicon for development' do + allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) + expect(helper.favicon).to eq 'favicon-purple.ico' + end + end + describe 'page_image' do it 'defaults to the GitLab logo' do expect(helper.page_image).to end_with 'assets/gitlab_logo.png' -- cgit v1.2.1 From 19cb1fcdf364a31077a70d8cf2af09d674f32eaa Mon Sep 17 00:00:00 2001 From: winniehell Date: Sat, 11 Feb 2017 22:22:22 +0100 Subject: Make Karma output look nicer for CI (!9165) --- changelogs/unreleased/beautiful-karma-output.yml | 4 + config/karma.config.js | 3 +- package.json | 1 + yarn.lock | 193 ++++++++++++----------- 4 files changed, 107 insertions(+), 94 deletions(-) create mode 100644 changelogs/unreleased/beautiful-karma-output.yml diff --git a/changelogs/unreleased/beautiful-karma-output.yml b/changelogs/unreleased/beautiful-karma-output.yml new file mode 100644 index 00000000000..6ccddebab68 --- /dev/null +++ b/changelogs/unreleased/beautiful-karma-output.yml @@ -0,0 +1,4 @@ +--- +title: Make Karma output look nicer for CI +merge_request: 9165 +author: winniehell diff --git a/config/karma.config.js b/config/karma.config.js index a1fbeab1f46..5b472780aed 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -4,6 +4,7 @@ var ROOT_PATH = path.resolve(__dirname, '..'); // Karma configuration module.exports = function(config) { + var progressReporter = process.env.CI ? 'mocha' : 'progress'; config.set({ basePath: ROOT_PATH, browsers: ['PhantomJS'], @@ -15,7 +16,7 @@ module.exports = function(config) { preprocessors: { 'spec/javascripts/**/*.js?(.es6)': ['webpack', 'sourcemap'], }, - reporters: ['progress', 'coverage-istanbul'], + reporters: [progressReporter, 'coverage-istanbul'], coverageIstanbulReporter: { reports: ['html', 'text-summary'], dir: 'coverage-javascript/', diff --git a/package.json b/package.json index 0500ba9670e..08bde1bc313 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "jquery-ui": "github:jquery/jquery-ui#1.11.4", "jquery-ujs": "1.2.1", "js-cookie": "^2.1.3", + "karma-mocha-reporter": "^2.2.2", "mousetrap": "1.4.6", "pikaday": "^1.5.1", "select2": "3.5.2-browserify", diff --git a/yarn.lock b/yarn.lock index 42cdc8b3fcd..99db6f61bcd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,10 +1,12 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@~1.3.3, accepts@1.3.3: +accepts@1.3.3, accepts@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" dependencies: @@ -23,14 +25,14 @@ acorn-jsx@^3.0.0: dependencies: acorn "^3.0.4" +acorn@4.0.4, acorn@^4.0.3, acorn@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + acorn@^3.0.4: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" -acorn@^4.0.3, acorn@^4.0.4, acorn@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" - after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -178,7 +180,11 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@^1.4.0, async@^1.4.2, async@^1.5.2, async@1.x: +async@0.2.x, async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -188,10 +194,6 @@ async@^2.1.2, async@^2.1.4: dependencies: lodash "^4.14.0" -async@~0.2.6, async@0.2.x: - version "0.2.10" - resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" - async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -995,7 +997,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1140,14 +1142,6 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.6: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-stream@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.0.tgz#53f7d43c51c5e43f81c8fdd03321c631be68d611" @@ -1156,6 +1150,14 @@ concat-stream@1.5.0: readable-stream "~2.0.0" typedarray "~0.0.5" +concat-stream@^1.4.6: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" @@ -1263,16 +1265,16 @@ custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" +d3@3.5.11: + version "3.5.11" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" + d@^0.1.1, d@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" dependencies: es5-ext "~0.10.2" -d3@3.5.11: - version "3.5.11" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.11.tgz#d130750eed0554db70e8432102f920a12407b69c" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1283,28 +1285,28 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@^2.1.1, debug@^2.2.0, debug@2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" - dependencies: - ms "0.7.2" +debug@0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" -debug@~2.2.0, debug@2.2.0: +debug@2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: ms "0.7.1" -debug@0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" - debug@2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" dependencies: ms "0.7.2" +debug@2.6.0, debug@^2.1.1, debug@^2.2.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + dependencies: + ms "0.7.2" + decamelize@^1.0.0, decamelize@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1382,7 +1384,7 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -doctrine@^1.2.2, doctrine@1.5.0: +doctrine@1.5.0, doctrine@^1.2.2: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" dependencies: @@ -1545,7 +1547,7 @@ es6-set@~0.1.3: es6-symbol "3" event-emitter "~0.3.4" -es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" dependencies: @@ -1697,7 +1699,7 @@ espree@^3.4.0: acorn "4.0.4" acorn-jsx "^3.0.0" -esprima@^2.7.1, esprima@2.7.x: +esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -2293,7 +2295,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@2, inherits@2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -2499,14 +2501,14 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" -isarray@^1.0.0, isarray@~1.0.0, isarray@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" @@ -2632,7 +2634,7 @@ jquery-ujs@1.2.1: dependencies: jquery ">=1.8.0" -jquery@>=1.8.0, jquery@2.2.1: +jquery@2.2.1, jquery@>=1.8.0: version "2.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f" @@ -2644,7 +2646,7 @@ js-tokens@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" -js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@3.x: +js-yaml@3.x, js-yaml@^3.5.1, js-yaml@^3.7.0: version "3.8.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628" dependencies: @@ -2681,7 +2683,7 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json3@^3.3.2, json3@3.3.2: +json3@3.3.2, json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -2721,6 +2723,12 @@ karma-jasmine@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf" +karma-mocha-reporter@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.2.tgz#876de9a287244e54a608591732a98e66611f6abe" + dependencies: + chalk "1.1.3" + karma-phantomjs-launcher@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.2.tgz#19e1041498fd75563ed86730a22c1fe579fa8fb1" @@ -2823,7 +2831,7 @@ loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" -loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@0.2.x: +loader-utils@0.2.x, loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5: version "0.2.16" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" dependencies: @@ -2985,7 +2993,7 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.13, mime-types@~2.1.7: dependencies: mime-db "~1.26.0" -mime@^1.3.4, mime@1.3.4: +mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -2993,25 +3001,19 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, "minimatch@2 || 3": +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1, minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.x: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" mkdirp@0.5.0: version "0.5.0" @@ -3019,6 +3021,12 @@ mkdirp@0.5.0: dependencies: minimist "0.0.8" +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + moment@2.x: version "2.17.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" @@ -3130,7 +3138,7 @@ node-zopfli@^2.0.0: nan "^2.0.0" node-pre-gyp "^0.6.4" -nopt@~3.0.6, nopt@3.x: +nopt@3.x, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -3166,14 +3174,14 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" @@ -3199,7 +3207,7 @@ on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" -once@^1.3.0, once@^1.4.0, once@1.x: +once@1.x, once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3480,22 +3488,18 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" -qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" - qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" @@ -3504,6 +3508,10 @@ qs@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.1.tgz#ce03c5ff0935bc1d9d69a9f14cbd18e568d67625" +qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -3743,11 +3751,11 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@^1.1.6, resolve@1.1.x: +resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" -resolve@^1.2.0: +resolve@^1.1.6, resolve@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" @@ -3764,7 +3772,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -3796,7 +3804,7 @@ select2@3.5.2-browserify: version "3.5.2-browserify" resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d" -semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -3963,7 +3971,7 @@ source-map-support@^0.4.2: dependencies: source-map "^0.5.3" -source-map@^0.1.41, source-map@0.1.x: +source-map@0.1.x, source-map@^0.1.41: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" dependencies: @@ -4063,10 +4071,6 @@ stream-http@^2.3.1: to-arraybuffer "^1.0.0" xtend "^4.0.0" -string_decoder@^0.10.25, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4082,6 +4086,10 @@ string-width@^2.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^3.0.0" +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -4292,20 +4300,20 @@ underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" -url-parse@^1.1.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" dependencies: querystringify "0.0.x" requires-port "1.0.x" -url-parse@1.0.x: - version "1.0.5" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" dependencies: querystringify "0.0.x" requires-port "1.0.x" @@ -4334,7 +4342,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@^0.10.3, util@0.10.3: +util@0.10.3, util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -4494,6 +4502,10 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -4502,10 +4514,6 @@ wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -4588,4 +4596,3 @@ yauzl@2.4.1: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - -- cgit v1.2.1 From 23e6cd6f1d9a6379491ba80b90e6003ff0480260 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 15 Feb 2017 08:48:46 -0600 Subject: only load istanbul plugin in development mode --- config/webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 5d5e4bb570a..7782fe393ea 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -54,7 +54,7 @@ var config = { exclude: /(node_modules|vendor\/assets)/, loader: 'babel-loader', options: { - plugins: ['istanbul'], + plugins: IS_PRODUCTION ? [] : ['istanbul'], presets: [ ["es2015", {"modules": false}], 'stage-2' -- cgit v1.2.1 From ba374a5a35b5fbe3c5c2e05b3e8a3a20d40fa331 Mon Sep 17 00:00:00 2001 From: Mark Fletcher Date: Mon, 16 Jan 2017 09:17:02 +0000 Subject: Specify that only project owners can transfer a project [skip ci] --- changelogs/unreleased/26651-cannot-move-project-into-group.yml | 4 ++++ doc/workflow/groups.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/26651-cannot-move-project-into-group.yml diff --git a/changelogs/unreleased/26651-cannot-move-project-into-group.yml b/changelogs/unreleased/26651-cannot-move-project-into-group.yml new file mode 100644 index 00000000000..244a19a627d --- /dev/null +++ b/changelogs/unreleased/26651-cannot-move-project-into-group.yml @@ -0,0 +1,4 @@ +--- +title: Specify in the documentation that only projects owners can transfer projects +merge_request: +author: diff --git a/doc/workflow/groups.md b/doc/workflow/groups.md index a693cc3d0fd..6237a5d5e18 100644 --- a/doc/workflow/groups.md +++ b/doc/workflow/groups.md @@ -23,7 +23,7 @@ You can use the 'New project' button to add a project to the new group. ## Transferring an existing project into a group -You can transfer an existing project into a group you own from the project settings page. +You can transfer an existing project into a group you own from the project settings page. The option to transfer a project is only available if you are the Owner of the project. First scroll down to the 'Dangerous settings' and click 'Show them to me'. Now you can pick any of the groups you manage as the new namespace for the group. -- cgit v1.2.1 From 33c8d413d2b42bd7b823228a2739eddcd4dfbe51 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 8 Feb 2017 20:33:29 +0000 Subject: Merge branch 'asciidoctor-xss-patch' into 'security' Add sanitization filter to asciidocs output to prevent XSS See https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2057 --- changelogs/unreleased/asciidocs-xss-patch.yml | 4 ++++ lib/gitlab/asciidoc.rb | 3 +++ spec/lib/gitlab/asciidoc_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 changelogs/unreleased/asciidocs-xss-patch.yml diff --git a/changelogs/unreleased/asciidocs-xss-patch.yml b/changelogs/unreleased/asciidocs-xss-patch.yml new file mode 100644 index 00000000000..f70a4b81b82 --- /dev/null +++ b/changelogs/unreleased/asciidocs-xss-patch.yml @@ -0,0 +1,4 @@ +--- +title: Patch Asciidocs rendering to block XSS +merge_request: +author: diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb index 0618107e2c3..d575367d81a 100644 --- a/lib/gitlab/asciidoc.rb +++ b/lib/gitlab/asciidoc.rb @@ -36,6 +36,9 @@ module Gitlab html = Banzai.post_process(html, context) + filter = Banzai::Filter::SanitizationFilter.new(html) + html = filter.call.to_s + html.html_safe end diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb index ba199917f5c..bca57105d1d 100644 --- a/spec/lib/gitlab/asciidoc_spec.rb +++ b/spec/lib/gitlab/asciidoc_spec.rb @@ -41,6 +41,29 @@ module Gitlab render(input, context, asciidoc_opts) end end + + context "XSS" do + links = { + 'links' => { + input: 'link:mylink"onmouseover="alert(1)[Click Here]', + output: "
    " + }, + 'images' => { + input: 'image:https://localhost.com/image.png[Alt text" onerror="alert(7)]', + output: "
    \n

    \"Alt

    \n
    " + }, + 'pre' => { + input: '```mypre">', + output: "
    \n
    \n
    \">
    \n
    \n
    " + } + } + + links.each do |name, data| + it "does not convert dangerous #{name} into HTML" do + expect(render(data[:input], context)).to eql data[:output] + end + end + end end def render(*args) -- cgit v1.2.1 From 7e1f7a02dbe3ebb6688005a4d966670bea12beb1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 9 Feb 2017 17:30:06 +0000 Subject: Merge branch 'fix-rdoc-xss' into 'security' Fix XSS in rdoc and other markups See https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2058 --- changelogs/unreleased/patch-rdoc-xss.yml | 4 ++++ lib/gitlab/other_markup.rb | 3 +++ spec/lib/gitlab/other_markup.rb | 22 ++++++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 changelogs/unreleased/patch-rdoc-xss.yml create mode 100644 spec/lib/gitlab/other_markup.rb diff --git a/changelogs/unreleased/patch-rdoc-xss.yml b/changelogs/unreleased/patch-rdoc-xss.yml new file mode 100644 index 00000000000..b428f5435e3 --- /dev/null +++ b/changelogs/unreleased/patch-rdoc-xss.yml @@ -0,0 +1,4 @@ +--- +title: Patch XSS vulnerability in RDOC support +merge_request: +author: diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb index 4e2f8ed5587..e67acf28c94 100644 --- a/lib/gitlab/other_markup.rb +++ b/lib/gitlab/other_markup.rb @@ -17,6 +17,9 @@ module Gitlab html = Banzai.post_process(html, context) + filter = Banzai::Filter::SanitizationFilter.new(html) + html = filter.call.to_s + html.html_safe end end diff --git a/spec/lib/gitlab/other_markup.rb b/spec/lib/gitlab/other_markup.rb new file mode 100644 index 00000000000..8f5a353b381 --- /dev/null +++ b/spec/lib/gitlab/other_markup.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe Gitlab::OtherMarkup, lib: true do + context "XSS Checks" do + links = { + 'links' => { + file: 'file.rdoc', + input: 'XSS[JaVaScriPt:alert(1)]', + output: '

    XSS

    ' + } + } + links.each do |name, data| + it "does not convert dangerous #{name} into HTML" do + expect(render(data[:file], data[:input], context)).to eql data[:output] + end + end + end + + def render(*args) + described_class.render(*args) + end +end -- cgit v1.2.1 From dd944bf14f4a0fd555db32d5833325fa459d9565 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Mon, 13 Feb 2017 22:42:46 +0000 Subject: Merge branch 'svg-xss-fix' into 'security' Fix for XSS vulnerability in SVG attachments See https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2059 --- app/uploaders/file_uploader.rb | 2 +- app/uploaders/uploader_helper.rb | 9 ++++++++- changelogs/unreleased/fix-xss-svg.yml | 4 ++++ spec/controllers/uploads_controller_spec.rb | 22 ++++++++++++++++++++++ spec/factories/notes.rb | 6 +++++- 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/fix-xss-svg.yml diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 47bef7cd1e4..23b7318827c 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -36,7 +36,7 @@ class FileUploader < GitlabUploader escaped_filename = filename.gsub("]", "\\]") markdown = "[#{escaped_filename}](#{self.secure_url})" - markdown.prepend("!") if image_or_video? + markdown.prepend("!") if image_or_video? || dangerous? { alt: filename, diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb index fbaea2744a3..35fd1ed23f8 100644 --- a/app/uploaders/uploader_helper.rb +++ b/app/uploaders/uploader_helper.rb @@ -1,12 +1,15 @@ # Extra methods for uploader module UploaderHelper - IMAGE_EXT = %w[png jpg jpeg gif bmp tiff svg] + IMAGE_EXT = %w[png jpg jpeg gif bmp tiff] # We recommend using the .mp4 format over .mov. Videos in .mov format can # still be used but you really need to make sure they are served with the # proper MIME type video/mp4 and not video/quicktime or your videos won't play # on IE >= 9. # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html VIDEO_EXT = %w[mp4 m4v mov webm ogv] + # These extension types can contain dangerous code and should only be embedded inline with + # proper filtering. They should always be tagged as "Content-Disposition: attachment", not "inline". + DANGEROUS_EXT = %w[svg] def image? extension_match?(IMAGE_EXT) @@ -20,6 +23,10 @@ module UploaderHelper image? || video? end + def dangerous? + extension_match?(DANGEROUS_EXT) + end + def extension_match?(extensions) return false unless file diff --git a/changelogs/unreleased/fix-xss-svg.yml b/changelogs/unreleased/fix-xss-svg.yml new file mode 100644 index 00000000000..dbb956c3910 --- /dev/null +++ b/changelogs/unreleased/fix-xss-svg.yml @@ -0,0 +1,4 @@ +--- +title: Fix XSS vulnerability in SVG attachments +merge_request: +author: diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 570d9fa43f8..c9584ddf18c 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -4,6 +4,28 @@ describe UploadsController do let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) } describe "GET show" do + context 'Content-Disposition security measures' do + let(:project) { create(:empty_project, :public) } + + context 'for PNG files' do + it 'returns Content-Disposition: inline' do + note = create(:note, :with_attachment, project: project) + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png' + + expect(response['Content-Disposition']).to start_with('inline;') + end + end + + context 'for SVG files' do + it 'returns Content-Disposition: attachment' do + note = create(:note, :with_svg_attachment, project: project) + get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.svg' + + expect(response['Content-Disposition']).to start_with('attachment;') + end + end + end + context "when viewing a user avatar" do context "when signed in" do before do diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index a21da7074f9..5c50cd7f4ad 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -97,7 +97,11 @@ FactoryGirl.define do end trait :with_attachment do - attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") } + attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") } + end + + trait :with_svg_attachment do + attachment { fixture_file_upload(Rails.root + "spec/fixtures/unsanitized.svg", "image/svg+xml") } end end end -- cgit v1.2.1 From 414d695db74e1a237aac7e0ca6a2543e7be1510e Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 9 Feb 2017 21:25:30 +0000 Subject: Merge branch 'fix-github-import-MR-wrong-project' into 'security' Fix labels being applied to wrong merge requests on GitHub import See https://dev.gitlab.org/gitlab/gitlabhq/merge_requests/2064 --- changelogs/unreleased/labels-assigned-to-wrong-project.yml | 4 ++++ lib/gitlab/github_import/importer.rb | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/labels-assigned-to-wrong-project.yml diff --git a/changelogs/unreleased/labels-assigned-to-wrong-project.yml b/changelogs/unreleased/labels-assigned-to-wrong-project.yml new file mode 100644 index 00000000000..0f4a88075a4 --- /dev/null +++ b/changelogs/unreleased/labels-assigned-to-wrong-project.yml @@ -0,0 +1,4 @@ +--- +title: Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. +merge_request: +author: diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index ec1318ab33c..9a4ffd28438 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -115,7 +115,7 @@ module Gitlab begin issuable = if gh_issue.pull_request? - MergeRequest.find_by_iid(gh_issue.number) + MergeRequest.find_by(target_project_id: project.id, iid: gh_issue.number) else gh_issue.create! end @@ -212,8 +212,12 @@ module Gitlab comment = CommentFormatter.new(project, raw) # GH does not return info about comment's parent, so we guess it by checking its URL! *_, parent, iid = URI(raw.html_url).path.split('/') - issuable_class = parent == 'issues' ? Issue : MergeRequest - issuable = issuable_class.find_by_iid(iid) + if parent == 'issues' + issuable = Issue.find_by(project_id: project.id, iid: iid) + else + issuable = MergeRequest.find_by(target_project_id: project.id, iid: iid) + end + next unless issuable issuable.notes.create!(comment.attributes) -- cgit v1.2.1 From cec1e3ebc8fe03955c3c1c27f10a7d924f2917ed Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 15 Feb 2017 16:45:41 +0100 Subject: create lighter version of JSON and reuse initial restore in spec to speed up run --- spec/lib/gitlab/import_export/project.light.json | 48 ++++++ .../import_export/project_tree_restorer_spec.rb | 164 +++++++++------------ 2 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 spec/lib/gitlab/import_export/project.light.json diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json new file mode 100644 index 00000000000..a78836c3c34 --- /dev/null +++ b/spec/lib/gitlab/import_export/project.light.json @@ -0,0 +1,48 @@ +{ + "description": "Nisi et repellendus ut enim quo accusamus vel magnam.", + "visibility_level": 10, + "archived": false, + "labels": [ + { + "id": 2, + "title": "test2", + "color": "#428bca", + "project_id": 8, + "created_at": "2016-07-22T08:55:44.161Z", + "updated_at": "2016-07-22T08:55:44.161Z", + "template": false, + "description": "", + "type": "ProjectLabel", + "priorities": [ + ] + }, + { + "id": 3, + "title": "test3", + "color": "#428bca", + "group_id": 8, + "created_at": "2016-07-22T08:55:44.161Z", + "updated_at": "2016-07-22T08:55:44.161Z", + "template": false, + "description": "", + "project_id": null, + "type": "GroupLabel", + "priorities": [ + { + "id": 1, + "project_id": 5, + "label_id": 1, + "priority": 1, + "created_at": "2016-10-18T09:35:43.338Z", + "updated_at": "2016-10-18T09:35:43.338Z" + } + ] + } + ], + "snippets": [ + + ], + "hooks": [ + + ] +} \ No newline at end of file diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index 0af13ba8e47..0eefb450f37 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -3,24 +3,27 @@ include ImportExport::CommonUtil describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do describe 'restore project tree' do - let(:user) { create(:user) } - let(:namespace) { create(:namespace, owner: user) } - let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } - let!(:project) { create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') } - let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) } - let(:restored_project_json) { project_tree_restorer.restore } + before(:all) do + user = create(:user) + + RSpec::Mocks.with_temporary_scope do + @shared = Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') + allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') + project = create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') + project_tree_restorer = described_class.new(user: user, shared: @shared, project: project) + @restored_project_json = project_tree_restorer.restore + end + end before do - allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') end context 'JSON' do it 'restores models based on JSON' do - expect(restored_project_json).to be true + expect(@restored_project_json).to be true end it 'restore correct project features' do - restored_project_json project = Project.find_by_path('project') expect(project.project_feature.issues_access_level).to eq(ProjectFeature::DISABLED) @@ -31,62 +34,42 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end it 'has the same label associated to two issues' do - restored_project_json - expect(ProjectLabel.find_by_title('test2').issues.count).to eq(2) end it 'has milestones associated to two separate issues' do - restored_project_json - expect(Milestone.find_by_description('test milestone').issues.count).to eq(2) end it 'creates a valid pipeline note' do - restored_project_json - expect(Ci::Pipeline.first.notes).not_to be_empty end it 'restores pipelines with missing ref' do - restored_project_json - expect(Ci::Pipeline.where(ref: nil)).not_to be_empty end it 'restores the correct event with symbolised data' do - restored_project_json - expect(Event.where.not(data: nil).first.data[:ref]).not_to be_empty end it 'preserves updated_at on issues' do - restored_project_json - issue = Issue.where(description: 'Aliquam enim illo et possimus.').first expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC') end it 'contains the merge access levels on a protected branch' do - restored_project_json - expect(ProtectedBranch.first.merge_access_levels).not_to be_empty end it 'contains the push access levels on a protected branch' do - restored_project_json - expect(ProtectedBranch.first.push_access_levels).not_to be_empty end context 'event at forth level of the tree' do let(:event) { Event.where(title: 'test levels').first } - before do - restored_project_json - end - it 'restores the event' do expect(event).not_to be_nil end @@ -99,77 +82,44 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do it 'has the correct data for merge request st_diffs' do # makes sure we are renaming the custom method +utf8_st_diffs+ into +st_diffs+ - expect { restored_project_json }.to change(MergeRequestDiff.where.not(st_diffs: nil), :count).by(9) + expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9) end it 'has labels associated to label links, associated to issues' do - restored_project_json - expect(Label.first.label_links.first.target).not_to be_nil end it 'has project labels' do - restored_project_json - expect(ProjectLabel.count).to eq(2) end it 'has no group labels' do - restored_project_json - expect(GroupLabel.count).to eq(0) end - context 'with group' do - let!(:project) do - create(:empty_project, - :builds_disabled, - :issues_disabled, - name: 'project', - path: 'project', - group: create(:group)) - end - - it 'has group labels' do - restored_project_json - - expect(GroupLabel.count).to eq(1) - end - - it 'has label priorities' do - restored_project_json - - expect(GroupLabel.first.priorities).not_to be_empty - end - end - it 'has a project feature' do - restored_project_json - - expect(project.project_feature).not_to be_nil + expect(Project.first.project_feature).not_to be_nil end it 'restores the correct service' do - restored_project_json - expect(CustomIssueTrackerService.first).not_to be_nil end context 'Merge requests' do before do - restored_project_json + @restored_project_json end it 'always has the new project as a target' do - expect(MergeRequest.find_by_title('MR1').target_project).to eq(project) + expect(MergeRequest.find_by_title('MR1').target_project).to eq(Project.first) end it 'has the same source project as originally if source/target are the same' do - expect(MergeRequest.find_by_title('MR1').source_project).to eq(project) + expect(MergeRequest.find_by_title('MR1').source_project).to eq(Project.first) end it 'has the new project as target if source/target differ' do - expect(MergeRequest.find_by_title('MR2').target_project).to eq(project) + expect(MergeRequest.find_by_title('MR2').target_project).to eq(Project.first) end it 'has no source if source/target differ' do @@ -177,39 +127,71 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do end end - context 'project.json file access check' do - it 'does not read a symlink' do - Dir.mktmpdir do |tmpdir| - setup_symlink(tmpdir, 'project.json') - allow(shared).to receive(:export_path).and_call_original - - restored_project_json + context 'tokens are regenerated' do + it 'has a new CI trigger token' do + expect(Ci::Trigger.where(token: 'cdbfasdf44a5958c83654733449e585')).to be_empty + end - expect(shared.errors.first).not_to include('test') - end + it 'has a new CI build token' do + expect(Ci::Build.where(token: 'abcd')).to be_empty end end + end + end - context 'when there is an existing build with build token' do - it 'restores project json correctly' do - create(:ci_build, token: 'abcd') + context 'Light JSON' do + let(:user) { create(:user) } + let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: "", project_path: 'path') } + let!(:project) { create(:empty_project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') } + let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) } + let(:restored_project_json) { project_tree_restorer.restore } - expect(restored_project_json).to be true - end - end + before do + allow(ImportExport).to receive(:project_filename).and_return('project.light.json') + allow(shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') + end + + context 'project.json file access check' do + it 'does not read a symlink' do + Dir.mktmpdir do |tmpdir| + setup_symlink(tmpdir, 'project.json') + allow(shared).to receive(:export_path).and_call_original - context 'tokens are regenerated' do - before do restored_project_json - end - it 'has a new CI trigger token' do - expect(Ci::Trigger.where(token: 'cdbfasdf44a5958c83654733449e585')).to be_empty + expect(shared.errors.first).not_to include('test') end + end + end - it 'has a new CI build token' do - expect(Ci::Build.where(token: 'abcd')).to be_empty - end + context 'when there is an existing build with build token' do + it 'restores project json correctly' do + create(:ci_build, token: 'abcd') + + expect(restored_project_json).to be true + end + end + + context 'with group' do + let!(:project) do + create(:empty_project, + :builds_disabled, + :issues_disabled, + name: 'project', + path: 'project', + group: create(:group)) + end + + before do + restored_project_json + end + + it 'has group labels' do + expect(GroupLabel.count).to eq(1) + end + + it 'has label priorities' do + expect(GroupLabel.first.priorities).not_to be_empty end end end -- cgit v1.2.1 From ea9e0372a0b85fd24f72ea1f8aefcfe057e431b1 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Wed, 15 Feb 2017 10:48:55 -0500 Subject: Remove changelog entries for 8.16.5 release --- changelogs/unreleased/asciidocs-xss-patch.yml | 4 ---- changelogs/unreleased/fix-xss-svg.yml | 4 ---- changelogs/unreleased/labels-assigned-to-wrong-project.yml | 4 ---- changelogs/unreleased/patch-rdoc-xss.yml | 4 ---- 4 files changed, 16 deletions(-) delete mode 100644 changelogs/unreleased/asciidocs-xss-patch.yml delete mode 100644 changelogs/unreleased/fix-xss-svg.yml delete mode 100644 changelogs/unreleased/labels-assigned-to-wrong-project.yml delete mode 100644 changelogs/unreleased/patch-rdoc-xss.yml diff --git a/changelogs/unreleased/asciidocs-xss-patch.yml b/changelogs/unreleased/asciidocs-xss-patch.yml deleted file mode 100644 index f70a4b81b82..00000000000 --- a/changelogs/unreleased/asciidocs-xss-patch.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Patch Asciidocs rendering to block XSS -merge_request: -author: diff --git a/changelogs/unreleased/fix-xss-svg.yml b/changelogs/unreleased/fix-xss-svg.yml deleted file mode 100644 index dbb956c3910..00000000000 --- a/changelogs/unreleased/fix-xss-svg.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix XSS vulnerability in SVG attachments -merge_request: -author: diff --git a/changelogs/unreleased/labels-assigned-to-wrong-project.yml b/changelogs/unreleased/labels-assigned-to-wrong-project.yml deleted file mode 100644 index 0f4a88075a4..00000000000 --- a/changelogs/unreleased/labels-assigned-to-wrong-project.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Prevent the GitHub importer from assigning labels and comments to merge requests or issues belonging to other projects. -merge_request: -author: diff --git a/changelogs/unreleased/patch-rdoc-xss.yml b/changelogs/unreleased/patch-rdoc-xss.yml deleted file mode 100644 index b428f5435e3..00000000000 --- a/changelogs/unreleased/patch-rdoc-xss.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Patch XSS vulnerability in RDOC support -merge_request: -author: -- cgit v1.2.1 From 25fec0f882300ab4f917a71a5650ab0d29c7b939 Mon Sep 17 00:00:00 2001 From: Clement Ho Date: Thu, 9 Feb 2017 16:19:12 -0600 Subject: Fix regression where cmd-click stopped working for todos and merge request tabs --- app/assets/javascripts/merge_request_tabs.js.es6 | 5 +- app/assets/javascripts/todos.js.es6 | 17 +++--- .../27922-cmd-click-todo-doesn-t-work.yml | 5 ++ spec/javascripts/merge_request_tabs_spec.js | 40 +++++++++++--- spec/javascripts/todos_spec.js | 63 ++++++++++++++++++++++ 5 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml create mode 100644 spec/javascripts/todos_spec.js diff --git a/app/assets/javascripts/merge_request_tabs.js.es6 b/app/assets/javascripts/merge_request_tabs.js.es6 index cc049e00477..5ac3da574b5 100644 --- a/app/assets/javascripts/merge_request_tabs.js.es6 +++ b/app/assets/javascripts/merge_request_tabs.js.es6 @@ -102,9 +102,10 @@ require('./flash'); } clickTab(e) { - if (e.target && gl.utils.isMetaClick(e)) { - const targetLink = e.target.getAttribute('href'); + if (e.currentTarget && gl.utils.isMetaClick(e)) { + const targetLink = e.currentTarget.getAttribute('href'); e.stopImmediatePropagation(); + e.preventDefault(); window.open(targetLink, '_blank'); } } diff --git a/app/assets/javascripts/todos.js.es6 b/app/assets/javascripts/todos.js.es6 index b07e62a8c30..ded683f2ca1 100644 --- a/app/assets/javascripts/todos.js.es6 +++ b/app/assets/javascripts/todos.js.es6 @@ -147,24 +147,21 @@ goToTodoUrl(e) { const todoLink = this.dataset.url; - let targetLink = e.target.getAttribute('href'); - - if (e.target.tagName === 'IMG') { // See if clicked target was Avatar - targetLink = e.target.parentElement.getAttribute('href'); // Parent of Avatar is link - } if (!todoLink) { return; } if (gl.utils.isMetaClick(e)) { + const windowTarget = '_blank'; + const selected = e.target; e.preventDefault(); - // Meta-Click on username leads to different URL than todoLink. - // Turbolinks can resolve that URL, but window.open requires URL manually. - if (targetLink !== todoLink) { - return window.open(targetLink, '_blank'); + + if (selected.tagName === 'IMG') { + const avatarUrl = selected.parentElement.getAttribute('href'); + return window.open(avatarUrl, windowTarget); } else { - return window.open(todoLink, '_blank'); + return window.open(todoLink, windowTarget); } } else { return gl.utils.visitUrl(todoLink); diff --git a/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml b/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml new file mode 100644 index 00000000000..79a54429ee8 --- /dev/null +++ b/changelogs/unreleased/27922-cmd-click-todo-doesn-t-work.yml @@ -0,0 +1,5 @@ +--- +title: Fix regression where cmd-click stopped working for todos and merge request + tabs +merge_request: +author: diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js index 3810991f104..5b0c124962c 100644 --- a/spec/javascripts/merge_request_tabs_spec.js +++ b/spec/javascripts/merge_request_tabs_spec.js @@ -62,19 +62,47 @@ require('vendor/jquery.scrollTo'); }); }); describe('#opensInNewTab', function () { - var commitsLink; var tabUrl; + var windowTarget = '_blank'; beforeEach(function () { - commitsLink = '.commits-tab li a'; - tabUrl = $(commitsLink).attr('href'); + loadFixtures('merge_requests/merge_request_with_task_list.html.raw'); + + tabUrl = $('.commits-tab a').attr('href'); spyOn($.fn, 'attr').and.returnValue(tabUrl); }); + + describe('meta click', () => { + beforeEach(function () { + spyOn(gl.utils, 'isMetaClick').and.returnValue(true); + }); + + it('opens page when commits link is clicked', function () { + spyOn(window, 'open').and.callFake(function (url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); + }); + + this.class.bindEvents(); + document.querySelector('.merge-request-tabs .commits-tab a').click(); + }); + + it('opens page when commits badge is clicked', function () { + spyOn(window, 'open').and.callFake(function (url, name) { + expect(url).toEqual(tabUrl); + expect(name).toEqual(windowTarget); + }); + + this.class.bindEvents(); + document.querySelector('.merge-request-tabs .commits-tab a .badge').click(); + }); + }); + it('opens page tab in a new browser tab with Ctrl+Click - Windows/Linux', function () { spyOn(window, 'open').and.callFake(function (url, name) { expect(url).toEqual(tabUrl); - expect(name).toEqual('_blank'); + expect(name).toEqual(windowTarget); }); this.class.clickTab({ @@ -87,7 +115,7 @@ require('vendor/jquery.scrollTo'); it('opens page tab in a new browser tab with Cmd+Click - Mac', function () { spyOn(window, 'open').and.callFake(function (url, name) { expect(url).toEqual(tabUrl); - expect(name).toEqual('_blank'); + expect(name).toEqual(windowTarget); }); this.class.clickTab({ @@ -100,7 +128,7 @@ require('vendor/jquery.scrollTo'); it('opens page tab in a new browser tab with Middle-click - Mac/PC', function () { spyOn(window, 'open').and.callFake(function (url, name) { expect(url).toEqual(tabUrl); - expect(name).toEqual('_blank'); + expect(name).toEqual(windowTarget); }); this.class.clickTab({ diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js new file mode 100644 index 00000000000..66e4fbd6304 --- /dev/null +++ b/spec/javascripts/todos_spec.js @@ -0,0 +1,63 @@ +require('~/todos'); +require('~/lib/utils/common_utils'); + +describe('Todos', () => { + preloadFixtures('todos/todos.html.raw'); + let todoItem; + + beforeEach(() => { + loadFixtures('todos/todos.html.raw'); + todoItem = document.querySelector('.todos-list .todo'); + + return new gl.Todos(); + }); + + describe('goToTodoUrl', () => { + it('opens the todo url', (done) => { + const todoLink = todoItem.dataset.url; + + spyOn(gl.utils, 'visitUrl').and.callFake((url) => { + expect(url).toEqual(todoLink); + done(); + }); + + todoItem.click(); + }); + + describe('meta click', () => { + let visitUrlSpy; + + beforeEach(() => { + spyOn(gl.utils, 'isMetaClick').and.returnValue(true); + visitUrlSpy = spyOn(gl.utils, 'visitUrl').and.callFake(() => {}); + }); + + it('opens the todo url in another tab', (done) => { + const todoLink = todoItem.dataset.url; + + spyOn(window, 'open').and.callFake((url, target) => { + expect(todoLink).toEqual(url); + expect(target).toEqual('_blank'); + done(); + }); + + todoItem.click(); + expect(visitUrlSpy).not.toHaveBeenCalled(); + }); + + it('opens the avatar\'s url in another tab when the avatar is clicked', (done) => { + const avatarImage = todoItem.querySelector('img'); + const avatarUrl = avatarImage.parentElement.getAttribute('href'); + + spyOn(window, 'open').and.callFake((url, target) => { + expect(avatarUrl).toEqual(url); + expect(target).toEqual('_blank'); + done(); + }); + + avatarImage.click(); + expect(visitUrlSpy).not.toHaveBeenCalled(); + }); + }); + }); +}); -- cgit v1.2.1 From 79ef221e4e4998809a3fa1ac888c39868baa589e Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Wed, 15 Feb 2017 12:55:41 -0500 Subject: Fix Rubocop offense --- spec/views/projects/_home_panel.html.haml_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index 5af57cdf3b7..f5381a48207 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -4,7 +4,7 @@ describe 'projects/_home_panel', :view do let(:project) { create(:empty_project, :public) } let(:notification_settings) do - user.notification_settings_for(project) if user + user&.notification_settings_for(project) end before do -- cgit v1.2.1 From a89e9736266cbf9ccde3a49eccd8ab04f72bf38f Mon Sep 17 00:00:00 2001 From: Semyon Pupkov Date: Sat, 11 Feb 2017 22:30:15 +0500 Subject: Set `Auto-Submitted: auto-generated` header to emails Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/20305 --- changelogs/unreleased/add-auto-submited-header.yml | 4 ++++ config/initializers/additional_headers_interceptor.rb | 1 + lib/additional_email_headers_interceptor.rb | 8 ++++++++ spec/lib/additional_email_headers_interceptor_spec.rb | 12 ++++++++++++ 4 files changed, 25 insertions(+) create mode 100644 changelogs/unreleased/add-auto-submited-header.yml create mode 100644 config/initializers/additional_headers_interceptor.rb create mode 100644 lib/additional_email_headers_interceptor.rb create mode 100644 spec/lib/additional_email_headers_interceptor_spec.rb diff --git a/changelogs/unreleased/add-auto-submited-header.yml b/changelogs/unreleased/add-auto-submited-header.yml new file mode 100644 index 00000000000..93481613b39 --- /dev/null +++ b/changelogs/unreleased/add-auto-submited-header.yml @@ -0,0 +1,4 @@ +--- +title: Set Auto-Submitted header to mails +merge_request: +author: Semyon Pupkov diff --git a/config/initializers/additional_headers_interceptor.rb b/config/initializers/additional_headers_interceptor.rb new file mode 100644 index 00000000000..b9159e7c06c --- /dev/null +++ b/config/initializers/additional_headers_interceptor.rb @@ -0,0 +1 @@ +ActionMailer::Base.register_interceptor(AdditionalEmailHeadersInterceptor) diff --git a/lib/additional_email_headers_interceptor.rb b/lib/additional_email_headers_interceptor.rb new file mode 100644 index 00000000000..2358fa6bbfd --- /dev/null +++ b/lib/additional_email_headers_interceptor.rb @@ -0,0 +1,8 @@ +class AdditionalEmailHeadersInterceptor + def self.delivering_email(message) + message.headers( + 'Auto-Submitted' => 'auto-generated', + 'X-Auto-Response-Suppress' => 'All' + ) + end +end diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/additional_email_headers_interceptor_spec.rb new file mode 100644 index 00000000000..580450eef1e --- /dev/null +++ b/spec/lib/additional_email_headers_interceptor_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe AdditionalEmailHeadersInterceptor do + it 'adds Auto-Submitted header' do + mail = ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello').deliver + + expect(mail.header['To'].value).to eq('test@mail.com') + expect(mail.header['From'].value).to eq('info@mail.com') + expect(mail.header['Auto-Submitted'].value).to eq('auto-generated') + expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All') + end +end -- cgit v1.2.1 From f2ed14458c539e3c8276799620c4e6a29a500f76 Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Wed, 15 Feb 2017 19:27:40 +0100 Subject: Fix most of broken docs links [ci skip] --- doc/administration/build_artifacts.md | 2 +- doc/administration/pages/index.md | 8 +++--- doc/administration/pages/source.md | 6 ++--- doc/ci/build_artifacts/README.md | 2 +- doc/ci/pipelines.md | 3 ++- doc/ci/runners/README.md | 19 +++++++------ doc/update/7.0-to-7.1.md | 2 ++ doc/update/7.1-to-7.2.md | 2 ++ doc/update/7.10-to-7.11.md | 4 ++- doc/update/7.11-to-7.12.md | 4 ++- doc/update/7.12-to-7.13.md | 4 ++- doc/update/7.13-to-7.14.md | 4 ++- doc/update/7.14-to-8.0.md | 4 ++- doc/update/7.2-to-7.3.md | 2 ++ doc/update/7.3-to-7.4.md | 7 +++-- doc/update/7.4-to-7.5.md | 8 ++++-- doc/update/7.5-to-7.6.md | 13 ++++++--- doc/update/7.6-to-7.7.md | 15 +++++++---- doc/update/7.7-to-7.8.md | 13 ++++++--- doc/update/7.8-to-7.9.md | 13 ++++++--- doc/update/7.9-to-7.10.md | 13 ++++++--- doc/update/8.0-to-8.1.md | 4 ++- doc/update/8.1-to-8.2.md | 4 ++- doc/update/8.10-to-8.11.md | 4 ++- doc/update/8.11-to-8.12.md | 4 ++- doc/update/8.12-to-8.13.md | 4 ++- doc/update/8.13-to-8.14.md | 4 ++- doc/update/8.14-to-8.15.md | 4 ++- doc/update/8.15-to-8.16.md | 4 ++- doc/update/8.16-to-8.17.md | 4 ++- doc/update/8.2-to-8.3.md | 4 ++- doc/update/8.3-to-8.4.md | 6 +++-- doc/update/8.4-to-8.5.md | 6 +++-- doc/update/8.5-to-8.6.md | 4 ++- doc/update/8.6-to-8.7.md | 4 ++- doc/update/8.7-to-8.8.md | 4 ++- doc/update/8.8-to-8.9.md | 4 ++- doc/update/8.9-to-8.10.md | 4 ++- doc/user/account/security.md | 2 +- doc/user/markdown.md | 31 +++------------------- doc/user/project/merge_requests/index.md | 2 +- doc/user/project/pages/index.md | 24 +++++++++-------- .../importing/import_projects_from_github.md | 4 +-- 43 files changed, 169 insertions(+), 114 deletions(-) diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md index 5a892a35fcc..623a5321f32 100644 --- a/doc/administration/build_artifacts.md +++ b/doc/administration/build_artifacts.md @@ -1 +1 @@ -This document was moved to [jobs_artifacts](jobs_artifacts.md). +This document was moved to [job_artifacts](job_artifacts.md). diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index c352caf1115..19316458d49 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -54,7 +54,7 @@ Before proceeding with the Pages configuration, you will need to: 1. Configure a **wildcard DNS record**. 1. (Optional) Have a **wildcard certificate** for that domain if you decide to serve Pages under HTTPS. -1. (Optional but recommended) Enable [Shared runners](../ci/runners/README.md) +1. (Optional but recommended) Enable [Shared runners](../../ci/runners/README.md) so that your users don't have to bring their own. ### DNS configuration @@ -236,7 +236,7 @@ latest previous version. [8-3-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-3-stable-ee/doc/pages/administration.md [8-5-docs]: https://gitlab.com/gitlab-org/gitlab-ee/blob/8-5-stable-ee/doc/pages/administration.md [8-17-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable-ce/doc/administration/pages/index.md -[backup]: ../raketasks/backup_restore.md +[backup]: ../../raketasks/backup_restore.md [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 [ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80 [ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173 @@ -244,6 +244,6 @@ latest previous version. [NGINX configs]: https://gitlab.com/gitlab-org/gitlab-ee/tree/8-5-stable-ee/lib/support/nginx [pages-readme]: https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md [pages-userguide]: ../../user/project/pages/index.md -[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure -[restart]: ../administration/restart_gitlab.md#installations-from-source +[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure +[restart]: ../restart_gitlab.md#installations-from-source [gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4 diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md index d4468b99992..0718c05a6f4 100644 --- a/doc/administration/pages/source.md +++ b/doc/administration/pages/source.md @@ -311,13 +311,13 @@ Pages are part of the [regular backup][backup] so there is nothing to configure. You should strongly consider running GitLab pages under a different hostname than GitLab to prevent XSS attacks. -[backup]: ../raketasks/backup_restore.md +[backup]: ../../raketasks/backup_restore.md [ee-80]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/80 [ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173 [gitlab pages daemon]: https://gitlab.com/gitlab-org/gitlab-pages [NGINX configs]: https://gitlab.com/gitlab-org/gitlab-ee/tree/8-5-stable-ee/lib/support/nginx [pages-readme]: https://gitlab.com/gitlab-org/gitlab-pages/blob/master/README.md [pages-userguide]: ../../user/project/pages/index.md -[reconfigure]: ../administration/restart_gitlab.md#omnibus-gitlab-reconfigure -[restart]: ../administration/restart_gitlab.md#installations-from-source +[reconfigure]: ../restart_gitlab.md#omnibus-gitlab-reconfigure +[restart]: ../restart_gitlab.md#installations-from-source [gitlab-pages]: https://gitlab.com/gitlab-org/gitlab-pages/tree/v0.2.4 diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md index e7692b8b9a2..22b3872025f 100644 --- a/doc/ci/build_artifacts/README.md +++ b/doc/ci/build_artifacts/README.md @@ -1 +1 @@ -This document was moved to [user/project/job_artifacts.md](../../user/project/job_artifacts.md). +This document was moved to [pipelines/job_artifacts.md](../../user/project/pipelines/job_artifacts.md). diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 3134405e10b..9d294240d9d 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -21,7 +21,7 @@ There are three types of pipelines that often use the single shorthand of "pipel 1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml` 2. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production -3. **Project Pipeline**: Cross-project CI dependencies [triggered via API]((triggers)), particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. +3. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus. ## Development Workflows @@ -99,3 +99,4 @@ respective link in the [Pipelines settings] page. [stages]: yaml/README.md#stages [runners]: runners/README.html [pipelines settings]: ../user/project/pipelines/settings.md +[triggers]: triggers/README.md diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index e15b6891334..1bd1ee93ac5 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -8,14 +8,13 @@ A Runner can be specific to a certain project or serve any project in GitLab CI. A Runner that serves all projects is called a shared Runner. Ideally, GitLab Runner should not be installed on the same machine as GitLab. -Read the [requirements documentation](../../install/requirements.md#gitlab-Runner) +Read the [requirements documentation](../../install/requirements.md#gitlab-runner) for more information. ## Shared vs. Specific Runners A Runner that is specific only runs for the specified project. A shared Runner -can run jobs for every project that has enabled the option -`Allow shared Runners`. +can run jobs for every project that has enabled the option **Allow shared Runners**. **Shared Runners** are useful for jobs that have similar requirements, between multiple projects. Rather than having multiple Runners idling for @@ -44,7 +43,7 @@ A fork does copy the CI settings (jobs, allow shared, etc) of the cloned reposit There are several ways to create a Runner. Only after creation, upon registration its status as Shared or Specific is determined. -[See the documentation for](https://gitlab.com/gitlab-org/gitlab-ci-multi-Runner/#installation) +[See the documentation for](https://docs.gitlab.com/runner/install) the different methods of installing a Runner instance. After installing the Runner, you can either register it as `Shared` or as `Specific`. @@ -55,19 +54,19 @@ You can only register a Shared Runner if you have admin access to the GitLab ins You can only register a shared Runner if you are an admin on the linked GitLab instance. -Grab the shared-Runner token on the `admin/Runners` page of your GitLab CI +Grab the shared-Runner token on the `admin/runners` page of your GitLab CI instance. -![shared token](shared_Runner.png) +![shared token](shared_runner.png) Now simply register the Runner as any Runner: ``` -sudo gitlab-ci-multi-Runner register +sudo gitlab-ci-multi-runner register ``` Shared Runners are enabled by default as of GitLab 8.2, but can be disabled with the -`DISABLE SHARED RunnerS` button. Previous versions of GitLab defaulted shared Runners to +`DISABLE SHARED RUNNERS` button. Previous versions of GitLab defaulted shared Runners to disabled. ## Registering a Specific Runner @@ -93,7 +92,7 @@ setup a specific Runner for this project. To register the Runner, run the command below and follow instructions: ``` -sudo gitlab-ci-multi-Runner register +sudo gitlab-ci-multi-runner register ``` ### Lock a specific Runner from being enabled for other projects @@ -108,7 +107,7 @@ If you are an admin on your GitLab instance, you can make any shared Runner a specific Runner, _but you can not make a specific Runner a shared Runner_. -To make a shared Runner specific, go to the Runner page (`/admin/Runners`) +To make a shared Runner specific, go to the Runner page (`/admin/runners`) and find your Runner. Add any projects on the left to make this Runner run exclusively for these projects, therefore making it a specific Runner. diff --git a/doc/update/7.0-to-7.1.md b/doc/update/7.0-to-7.1.md index c717affebd3..2e9457aa142 100644 --- a/doc/update/7.0-to-7.1.md +++ b/doc/update/7.0-to-7.1.md @@ -136,3 +136,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-1-stable/config/gitlab.yml.example diff --git a/doc/update/7.1-to-7.2.md b/doc/update/7.1-to-7.2.md index d01f8528e14..e5045b5570f 100644 --- a/doc/update/7.1-to-7.2.md +++ b/doc/update/7.1-to-7.2.md @@ -135,3 +135,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/gitlab.yml.example diff --git a/doc/update/7.10-to-7.11.md b/doc/update/7.10-to-7.11.md index 79bc6de1e46..89213ba7178 100644 --- a/doc/update/7.10-to-7.11.md +++ b/doc/update/7.10-to-7.11.md @@ -65,7 +65,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-10-stable:config/gitlab.yml.example origin/7-11-stable:config/gitlab.yml.example @@ -101,3 +101,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-11-stable/config/gitlab.yml.example diff --git a/doc/update/7.11-to-7.12.md b/doc/update/7.11-to-7.12.md index cc14a135926..3865186918c 100644 --- a/doc/update/7.11-to-7.12.md +++ b/doc/update/7.11-to-7.12.md @@ -91,7 +91,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-11-stable:config/gitlab.yml.example origin/7-12-stable:config/gitlab.yml.example @@ -127,3 +127,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-12-stable/config/gitlab.yml.example diff --git a/doc/update/7.12-to-7.13.md b/doc/update/7.12-to-7.13.md index 57ebe3261b6..4c8d8f1f741 100644 --- a/doc/update/7.12-to-7.13.md +++ b/doc/update/7.12-to-7.13.md @@ -91,7 +91,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-12-stable:config/gitlab.yml.example origin/7-13-stable:config/gitlab.yml.example @@ -127,3 +127,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-13-stable/config/gitlab.yml.example diff --git a/doc/update/7.13-to-7.14.md b/doc/update/7.13-to-7.14.md index 6dd9727fb49..934898da5a1 100644 --- a/doc/update/7.13-to-7.14.md +++ b/doc/update/7.13-to-7.14.md @@ -91,7 +91,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-13-stable:config/gitlab.yml.example origin/7-14-stable:config/gitlab.yml.example @@ -127,3 +127,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-14-stable/config/gitlab.yml.example diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md index 117e2afaaa0..25fa6d93f06 100644 --- a/doc/update/7.14-to-8.0.md +++ b/doc/update/7.14-to-8.0.md @@ -143,7 +143,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/7-14-stable:config/gitlab.yml.example origin/8-0-stable:config/gitlab.yml.example @@ -227,3 +227,5 @@ this is likely due to an outdated Nginx or Apache configuration, or a missing or misconfigured `gitlab-git-http-server` instance. Double-check that you correctly completed [Step 5](#5-install-gitlab-git-http-server) to install the daemon and [Step 8](#new-nginx-configuration) to reconfigure Nginx. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-0-stable/config/gitlab.yml.example diff --git a/doc/update/7.2-to-7.3.md b/doc/update/7.2-to-7.3.md index 0e91e682175..d3391ddd225 100644 --- a/doc/update/7.2-to-7.3.md +++ b/doc/update/7.2-to-7.3.md @@ -143,3 +143,5 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-3-stable/config/gitlab.yml.example diff --git a/doc/update/7.3-to-7.4.md b/doc/update/7.3-to-7.4.md index 4df9127dd5f..6d632dc3c8e 100644 --- a/doc/update/7.3-to-7.4.md +++ b/doc/update/7.3-to-7.4.md @@ -75,7 +75,7 @@ sudo -u git -H editor config/unicorn.rb #### MySQL Databases: Update database.yml config file -* Add `collation: utf8_general_ci` to `config/database.yml` as seen in [config/database.yml.mysql](/config/database.yml.mysql) +* Add `collation: utf8_general_ci` to `config/database.yml` as seen in [config/database.yml.mysql][mysql]: ``` sudo -u git -H editor config/database.yml @@ -192,6 +192,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. - - - +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/gitlab.yml.example +[mysql]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-4-stable/config/database.yml.mysql diff --git a/doc/update/7.4-to-7.5.md b/doc/update/7.4-to-7.5.md index 673eab3c56e..ec50706d421 100644 --- a/doc/update/7.4-to-7.5.md +++ b/doc/update/7.4-to-7.5.md @@ -73,8 +73,8 @@ git diff origin/7-4-stable:config/gitlab.yml.example origin/7-5-stable:config/gi #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your setting ### 6. Start application @@ -106,3 +106,7 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/config/gitlab.yml.example +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/7.5-to-7.6.md b/doc/update/7.5-to-7.6.md index 35cd437fdc4..331f5de080e 100644 --- a/doc/update/7.5-to-7.6.md +++ b/doc/update/7.5-to-7.6.md @@ -67,7 +67,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-5-stable:config/gitlab.yml.example origin/7-6-stable:config/gitlab.yml.example @@ -75,12 +75,12 @@ git diff origin/7-5-stable:config/gitlab.yml.example origin/7-6-stable:config/gi #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your setting #### Setup time zone (optional) -Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it. ### 6. Start application @@ -112,3 +112,8 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/config/gitlab.yml.example +[app]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/config/application.rb +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/7.6-to-7.7.md b/doc/update/7.6-to-7.7.md index 910c7dcdd3c..918b10fbd95 100644 --- a/doc/update/7.6-to-7.7.md +++ b/doc/update/7.6-to-7.7.md @@ -67,7 +67,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](/config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-6-stable:config/gitlab.yml.example origin/7-7-stable:config/gitlab.yml.example @@ -75,12 +75,12 @@ git diff origin/7-6-stable:config/gitlab.yml.example origin/7-7-stable:config/gi #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your setting #### Setup time zone (optional) -Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it. ### 6. Start application @@ -101,7 +101,7 @@ If all items are green, then congratulations upgrade is complete! ### 8. GitHub settings (if applicable) -If you are using GitHub as an OAuth provider for authentication, you should change the callback URL so that it +If you are using GitHub as an OAuth provider for authentication, you should change the callback URL so that it only contains a root URL (ex. `https://gitlab.example.com/`) ## Things went south? Revert to previous version (7.6) @@ -117,3 +117,8 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/config/gitlab.yml.example +[app]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/config/application.rb +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-7-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/7.7-to-7.8.md b/doc/update/7.7-to-7.8.md index 46ca163c1bb..84e0464a824 100644 --- a/doc/update/7.7-to-7.8.md +++ b/doc/update/7.7-to-7.8.md @@ -67,7 +67,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-7-stable:config/gitlab.yml.example origin/7-8-stable:config/gitlab.yml.example @@ -75,13 +75,13 @@ git diff origin/7-7-stable:config/gitlab.yml.example origin/7-8-stable:config/gi #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings. * A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. #### Setup time zone (optional) -Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it. ### 6. Start application @@ -118,3 +118,8 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/gitlab.yml.example +[app]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/application.rb +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/7.8-to-7.9.md b/doc/update/7.8-to-7.9.md index 6ffa21c6141..b0dc2ba1dbb 100644 --- a/doc/update/7.8-to-7.9.md +++ b/doc/update/7.8-to-7.9.md @@ -69,7 +69,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-8-stable:config/gitlab.yml.example origin/7-9-stable:config/gitlab.yml.example @@ -77,13 +77,13 @@ git diff origin/7-8-stable:config/gitlab.yml.example origin/7-9-stable:config/gi #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings. * A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. #### Setup time zone (optional) -Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it. ### 6. Start application @@ -120,3 +120,8 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-9-stable/config/gitlab.yml.example +[app]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-9-stable/config/application.rb +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-9-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-9-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/7.9-to-7.10.md b/doc/update/7.9-to-7.10.md index d1179dc2ec7..8f7f84b41ba 100644 --- a/doc/update/7.9-to-7.10.md +++ b/doc/update/7.9-to-7.10.md @@ -65,7 +65,7 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them to your current `gitlab.yml`. +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them to your current `gitlab.yml`. ``` git diff origin/7-9-stable:config/gitlab.yml.example origin/7-10-stable:config/gitlab.yml.example @@ -73,13 +73,13 @@ git diff origin/7-9-stable:config/gitlab.yml.example origin/7-10-stable:config/g #### Change Nginx settings -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`][nginx] but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`][nginx-ssl] but with your settings. * A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section. #### Setup time zone (optional) -Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`](config/application.rb) (unlikely), unset it. +Consider setting the time zone in `gitlab.yml` otherwise GitLab will default to UTC. If you set a time zone previously in [`application.rb`][app] (unlikely), unset it. ### 6. Start application @@ -116,3 +116,8 @@ cd /home/git/gitlab sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup *.tar file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-10-stable/config/gitlab.yml.example +[app]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-10-stable/config/application.rb +[nginx]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-10-stable/lib/support/nginx/gitlab +[nginx-ssl]: https://gitlab.com/gitlab-org/gitlab-ce/blob/7-10-stable/lib/support/nginx/gitlab-ssl diff --git a/doc/update/8.0-to-8.1.md b/doc/update/8.0-to-8.1.md index bfb83cf79b1..6ee0c0656ee 100644 --- a/doc/update/8.0-to-8.1.md +++ b/doc/update/8.0-to-8.1.md @@ -108,7 +108,7 @@ For Ubuntu 16.04.1 LTS: #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-0-stable:config/gitlab.yml.example origin/8-1-stable:config/gitlab.yml.example @@ -173,3 +173,5 @@ If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of ### "You appear to have cloned an empty repository." See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting). + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-1-stable/config/gitlab.yml.example diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md index 7f36ce00e96..4c9ff5c5c0a 100644 --- a/doc/update/8.1-to-8.2.md +++ b/doc/update/8.1-to-8.2.md @@ -125,7 +125,7 @@ For Ubuntu 16.04.1 LTS: #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-1-stable:config/gitlab.yml.example origin/8-2-stable:config/gitlab.yml.example @@ -190,3 +190,5 @@ If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of ### "You appear to have cloned an empty repository." See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting). + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-2-stable/config/gitlab.yml.example diff --git a/doc/update/8.10-to-8.11.md b/doc/update/8.10-to-8.11.md index 119c5f475e4..e5e3cd395df 100644 --- a/doc/update/8.10-to-8.11.md +++ b/doc/update/8.10-to-8.11.md @@ -114,7 +114,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-10-stable:config/gitlab.yml.example origin/8-11-stable:config/gitlab.yml.example @@ -195,3 +195,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-11-stable/config/gitlab.yml.example diff --git a/doc/update/8.11-to-8.12.md b/doc/update/8.11-to-8.12.md index cddfa7e3e01..d6b3b0ffa5a 100644 --- a/doc/update/8.11-to-8.12.md +++ b/doc/update/8.11-to-8.12.md @@ -113,7 +113,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-11-stable:config/gitlab.yml.example origin/8-12-stable:config/gitlab.yml.example @@ -203,3 +203,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-12-stable/config/gitlab.yml.example diff --git a/doc/update/8.12-to-8.13.md b/doc/update/8.12-to-8.13.md index 8c0d3f78b55..75956aeb360 100644 --- a/doc/update/8.12-to-8.13.md +++ b/doc/update/8.12-to-8.13.md @@ -113,7 +113,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-12-stable:config/gitlab.yml.example origin/8-13-stable:config/gitlab.yml.example @@ -203,3 +203,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-13-stable/config/gitlab.yml.example diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md index c64d3407461..327ecb7cdc2 100644 --- a/doc/update/8.13-to-8.14.md +++ b/doc/update/8.13-to-8.14.md @@ -113,7 +113,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-13-stable:config/gitlab.yml.example origin/8-14-stable:config/gitlab.yml.example @@ -203,3 +203,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-14-stable/config/gitlab.yml.example diff --git a/doc/update/8.14-to-8.15.md b/doc/update/8.14-to-8.15.md index b1e3b116548..a68fe3bb605 100644 --- a/doc/update/8.14-to-8.15.md +++ b/doc/update/8.14-to-8.15.md @@ -122,7 +122,7 @@ sudo -u git -H git checkout v4.1.1 #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh cd /home/git/gitlab @@ -235,3 +235,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-15-stable/config/gitlab.yml.example diff --git a/doc/update/8.15-to-8.16.md b/doc/update/8.15-to-8.16.md index 2695a16ac0b..9f8f0f714d4 100644 --- a/doc/update/8.15-to-8.16.md +++ b/doc/update/8.15-to-8.16.md @@ -124,7 +124,7 @@ sudo -u git -H git checkout v4.1.1 #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh cd /home/git/gitlab @@ -237,3 +237,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-16-stable/config/gitlab.yml.example diff --git a/doc/update/8.16-to-8.17.md b/doc/update/8.16-to-8.17.md index 1808232c59a..53c2bc560e8 100644 --- a/doc/update/8.16-to-8.17.md +++ b/doc/update/8.16-to-8.17.md @@ -124,7 +124,7 @@ sudo -u git -H git checkout v4.1.1 #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh cd /home/git/gitlab @@ -237,3 +237,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-17-stable/config/gitlab.yml.example diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md index dd3fdafd8d1..f28896c2227 100644 --- a/doc/update/8.2-to-8.3.md +++ b/doc/update/8.2-to-8.3.md @@ -114,7 +114,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gitlab.yml.example @@ -211,3 +211,5 @@ If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of ### "You appear to have cloned an empty repository." See the [7.14 to 8.0 update guide](7.14-to-8.0.md#troubleshooting). + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-3-stable/config/gitlab.yml.example diff --git a/doc/update/8.3-to-8.4.md b/doc/update/8.3-to-8.4.md index e62d894609a..8b89455ca87 100644 --- a/doc/update/8.3-to-8.4.md +++ b/doc/update/8.3-to-8.4.md @@ -84,7 +84,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-3-stable:config/gitlab.yml.example origin/8-4-stable:config/gitlab.yml.example @@ -98,7 +98,7 @@ We updated the init script for GitLab in order to set a specific PATH for gitlab cd /home/git/gitlab sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ``` - + For Ubuntu 16.04.1 LTS: sudo systemctl daemon-reload @@ -135,3 +135,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/config/gitlab.yml.example diff --git a/doc/update/8.4-to-8.5.md b/doc/update/8.4-to-8.5.md index 678cc69d773..0eedfaee2db 100644 --- a/doc/update/8.4-to-8.5.md +++ b/doc/update/8.4-to-8.5.md @@ -88,7 +88,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-4-stable:config/gitlab.yml.example origin/8-5-stable:config/gitlab.yml.example @@ -119,7 +119,7 @@ via [/etc/default/gitlab]. Ensure you're still up-to-date with the latest init script changes: sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab - + For Ubuntu 16.04.1 LTS: sudo systemctl daemon-reload @@ -156,3 +156,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-5-stable/config/gitlab.yml.example diff --git a/doc/update/8.5-to-8.6.md b/doc/update/8.5-to-8.6.md index a76346516b9..851056161bb 100644 --- a/doc/update/8.5-to-8.6.md +++ b/doc/update/8.5-to-8.6.md @@ -107,7 +107,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-5-stable:config/gitlab.yml.example origin/8-6-stable:config/gitlab.yml.example @@ -175,3 +175,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-6-stable/config/gitlab.yml.example diff --git a/doc/update/8.6-to-8.7.md b/doc/update/8.6-to-8.7.md index 05ef4e61759..34c727260aa 100644 --- a/doc/update/8.6-to-8.7.md +++ b/doc/update/8.6-to-8.7.md @@ -88,7 +88,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-6-stable:config/gitlab.yml.example origin/8-7-stable:config/gitlab.yml.example @@ -164,3 +164,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-7-stable/config/gitlab.yml.example diff --git a/doc/update/8.7-to-8.8.md b/doc/update/8.7-to-8.8.md index 8ce434e5f78..6feeb1919de 100644 --- a/doc/update/8.7-to-8.8.md +++ b/doc/update/8.7-to-8.8.md @@ -88,7 +88,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-7-stable:config/gitlab.yml.example origin/8-8-stable:config/gitlab.yml.example @@ -164,3 +164,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-8-stable/config/gitlab.yml.example diff --git a/doc/update/8.8-to-8.9.md b/doc/update/8.8-to-8.9.md index aa077316bbe..61cdf8854d4 100644 --- a/doc/update/8.8-to-8.9.md +++ b/doc/update/8.8-to-8.9.md @@ -104,7 +104,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-8-stable:config/gitlab.yml.example origin/8-9-stable:config/gitlab.yml.example @@ -193,3 +193,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-9-stable/config/gitlab.yml.example diff --git a/doc/update/8.9-to-8.10.md b/doc/update/8.9-to-8.10.md index bb2c79fbb84..d6b2f11d49a 100644 --- a/doc/update/8.9-to-8.10.md +++ b/doc/update/8.9-to-8.10.md @@ -104,7 +104,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS #### New configuration options for `gitlab.yml` -There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: +There are new configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`: ```sh git diff origin/8-9-stable:config/gitlab.yml.example origin/8-10-stable:config/gitlab.yml.example @@ -193,3 +193,5 @@ sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production ``` If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above. + +[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-10-stable/config/gitlab.yml.example diff --git a/doc/user/account/security.md b/doc/user/account/security.md index 2459f913583..f4078876fab 100644 --- a/doc/user/account/security.md +++ b/doc/user/account/security.md @@ -1 +1 @@ -This document was moved to [profile](../profile/index.md). +This document was moved to [profile](../profile/account/index.md). diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 699318e2479..c14db17b0e6 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -524,35 +524,12 @@ There are two ways to create links, inline-style and reference-style. [1]: http://slashdot.org [link text itself]: https://www.reddit.com -[I'm an inline-style link](https://www.google.com) - -[I'm a reference-style link][Arbitrary case-insensitive reference text] - -[I'm a relative reference to a repository file](LICENSE)[^1] - -[I am an absolute reference within the repository](/doc/user/markdown.md) - -[I link to the Milestones page](/../milestones) - -[You can use numbers for reference-style link definitions][1] - -Or leave it empty and use the [link text itself][] - -Some text to show that the reference links can follow later. - -[arbitrary case-insensitive reference text]: https://www.mozilla.org -[1]: http://slashdot.org -[link text itself]: https://www.reddit.com - -**Note** - -Relative links do not allow referencing project files in a wiki page or wiki page in a project file. The reason for this is that, in GitLab, wiki is always a separate git repository. For example: - -`[I'm a reference-style link](style)` - +>**Note:** +Relative links do not allow referencing project files in a wiki page or wiki +page in a project file. The reason for this is that, in GitLab, wiki is always +a separate Git repository. For example, `[I'm a reference-style link](style)` will point the link to `wikis/style` when the link is inside of a wiki markdown file. - ### Images Here's our logo (hover to see the title text): diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index abd3740ef20..c759b7aaa4a 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -166,4 +166,4 @@ And to check out a particular merge request: git checkout origin/merge-requests/1 ``` -[protected branches]: protected_branches.md +[protected branches]: ../protected_branches.md diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md index 044c89a03c0..4c4f15aad40 100644 --- a/doc/user/project/pages/index.md +++ b/doc/user/project/pages/index.md @@ -50,13 +50,13 @@ In brief, this is what you need to upload your website in GitLab Pages: (ask your administrator). This is very important, so you should first make sure you get that right. 1. Create a project -1. Push a [`.gitlab-ci.yml` file](../ci/yaml/README.md) in the root directory +1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory of your repository with a specific job named [`pages`][pages] 1. Set up a GitLab Runner to build your website > **Note:** -> If [shared runners](../ci/runners/README.md) are enabled by your GitLab -> administrator, you should be able to use them instead of bringing your own. +If [shared runners](../../../ci/runners/README.md) are enabled by your GitLab +administrator, you should be able to use them instead of bringing your own. ### User or group Pages @@ -89,7 +89,7 @@ GitLab Pages for projects can be created by both user and group accounts. The steps to create a project page for a user or a group are identical: 1. Create a new project -1. Push a [`.gitlab-ci.yml` file](../ci/yaml/README.md) in the root directory +1. Push a [`.gitlab-ci.yml` file][yaml] in the root directory of your repository with a specific job named [`pages`][pages]. 1. Set up a GitLab Runner to build your website @@ -104,8 +104,8 @@ website being built live by following the CI job traces. > **Note:** > Before reading this section, make sure you familiarize yourself with GitLab CI -> and the specific syntax of[`.gitlab-ci.yml`](../ci/yaml/README.md) by -> following our [quick start guide](../ci/quick_start/README.md). +> and the specific syntax of[`.gitlab-ci.yml`][yaml] by +> following our [quick start guide]. To make use of GitLab Pages, the contents of `.gitlab-ci.yml` must follow the rules below: @@ -132,11 +132,11 @@ exit status, it then uploads the `public/` directory to GitLab Pages. The `public/` directory should contain all the static content of your website. Depending on how you plan to publish your website, the steps defined in the -[`script` parameter](../ci/yaml/README.md#script) may differ. +[`script` parameter](../../../ci/yaml/README.md#script) may differ. Be aware that Pages are by default branch/tag agnostic and their deployment relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the -`pages` job with the [`only` parameter](../ci/yaml/README.md#only-and-except), +`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except), whenever a new commit is pushed to whatever branch or tag, the Pages will be overwritten. In the example below, we limit the Pages to be deployed whenever a commit is pushed only on the `master` branch: @@ -237,7 +237,7 @@ get you started. Remember that GitLab Pages are by default branch/tag agnostic and their deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit -the `pages` job with the [`only` parameter](../ci/yaml/README.md#only-and-except), +the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except), whenever a new commit is pushed to a branch that will be used specifically for your pages. @@ -426,10 +426,12 @@ For a list of known issues, visit GitLab's [public issue tracker]. [ee-173]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/173 [pages-daemon]: https://gitlab.com/gitlab-org/gitlab-pages [gitlab ci]: https://about.gitlab.com/gitlab-ci -[gitlab runner]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner -[pages]: ../ci/yaml/README.md#pages +[gitlab runner]: https://docs.gitlab.com/runner +[pages]: ../../../ci/yaml/README.md#pages +[yaml]: ../../../ci/yaml/README.md [staticgen]: https://www.staticgen.com/ [pages-jekyll]: https://gitlab.com/pages/jekyll [metarefresh]: https://en.wikipedia.org/wiki/Meta_refresh [public issue tracker]: https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name=Pages [ce-14605]: https://gitlab.com/gitlab-org/gitlab-ce/issues/14605 +[quick start guide]: ../../../ci/quick_start/README.md diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md index cdacef9832f..aece4ab34ba 100644 --- a/doc/workflow/importing/import_projects_from_github.md +++ b/doc/workflow/importing/import_projects_from_github.md @@ -60,8 +60,7 @@ If the [GitHub integration][gh-import] is enabled by your GitLab administrator, you can use it instead of the personal access token. 1. First you may want to connect your GitHub account to GitLab in order for - the username mapping to be correct. Follow the [social sign-in] documentation - on how to do so. + the username mapping to be correct. 1. Once you connect GitHub, click the **List your GitHub repositories** button and you will be redirected to GitHub for permission to access your projects. 1. After accepting, you'll be automatically redirected to the importer. @@ -115,4 +114,3 @@ if you have the privileges to do so. [gh-import]: ../../integration/github.md "GitHub integration" [gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration [gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token -[social sign-in]: ../../profile/account/social_sign_in.md -- cgit v1.2.1 From a254dcf0edfb6aa4ea93fd0bfdb992565d6e8422 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 15 Feb 2017 19:59:30 +0100 Subject: Add count keys to response JSON --- app/controllers/projects/environments_controller.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 3b7240d8469..fed75396d6e 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -29,9 +29,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController end def folder - @environments = project.environments - .where(environment_type: params[:id]) - .with_state(params[:scope] || :available) + folder_environments = project.environments.where(environment_type: params[:id]) + @environments = folder_environments.with_state(params[:scope] || :available) respond_to do |format| format.html @@ -41,6 +40,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController .new(project: @project, user: @current_user) .with_pagination(request, response) .represent(@environments), + available_count: folder_environments.available.count, + stopped_count: folder_environments.stopped.count } end end -- cgit v1.2.1 From b29f6c5176b699de2f6efb012f9646e0d3243727 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Wed, 15 Feb 2017 16:36:13 +0000 Subject: Use `page` query parameter instead of `p` to keep consistency with all URLs Fix rubocop error --- .../vue_pipelines_index/pipelines.js.es6 | 4 ++-- spec/features/projects/pipelines/pipelines_spec.rb | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 index e47dc6935d6..6bc2d6ec312 100644 --- a/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 +++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js.es6 @@ -28,7 +28,7 @@ require('../vue_shared/components/pipelines_table'); }, props: ['scope', 'store', 'svgs'], created() { - const pagenum = gl.utils.getParameterByName('p'); + const pagenum = gl.utils.getParameterByName('page'); const scope = gl.utils.getParameterByName('scope'); if (pagenum) this.pagenum = pagenum; if (scope) this.apiScope = scope; @@ -36,7 +36,7 @@ require('../vue_shared/components/pipelines_table'); }, methods: { change(pagenum, apiScope) { - gl.utils.visitUrl(`?scope=${apiScope}&p=${pagenum}`); + gl.utils.visitUrl(`?scope=${apiScope}&page=${pagenum}`); }, }, template: ` diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 6555b2fc6c1..5c896a051a4 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -277,6 +277,28 @@ describe 'Pipelines', :feature, :js do end end end + + context 'with pagination' do + before do + create_list(:ci_empty_pipeline, 40, project: project) + end + + it 'should render pagination' do + visit namespace_project_pipelines_path(project.namespace, project) + wait_for_vue_resource + + expect(page).to have_css('.gl-pagination') + expect(page.find_all('tbody tr').length).to eq(20) + end + + it "should render second page of pipelines" do + visit namespace_project_pipelines_path(project.namespace, project, page: '2') + wait_for_vue_resource + + expect(page).to have_css('.gl-pagination') + expect(page.find_all('tbody tr').length).to eq(20) + end + end end describe 'POST /:project/pipelines' do -- cgit v1.2.1 From a8bc272237590efe43b83d5294f7d572c1b205c0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 09:21:52 -0600 Subject: Add dropdown toggle button --- app/views/layouts/header/_default.html.haml | 11 +++++ app/views/layouts/nav/_dashboard.html.haml | 77 ++++++++++++++--------------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 59082ce5fd5..14d7a75c34a 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -58,6 +58,17 @@ %h1.title= title + .dropdown + %button.global-dropdown-toggle{ href: '#', 'data-toggle' => 'dropdown' } + %span.sr-only Toggle navigation + = icon('bars') + .dropdown-menu-nav + %ul + - if current_user + = render 'layouts/nav/dashboard' + - else + = render 'layouts/nav/explore' + .header-logo = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = brand_header_logo diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 205d23178d2..efcb60addc0 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,41 +1,40 @@ -.nav-sidebar - %ul.nav - = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do - = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do - %span - Projects - = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do - %span - Activity - - if koding_enabled? - = nav_link(controller: :koding) do - = link_to koding_path, title: 'Koding' do - %span - Koding - = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do - = link_to dashboard_groups_path, title: 'Groups' do - %span - Groups - = nav_link(controller: 'dashboard/milestones') do - = link_to dashboard_milestones_path, title: 'Milestones' do - %span - Milestones - = nav_link(path: 'dashboard#issues') do - = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do - %span - Issues - %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) - = nav_link(path: 'dashboard#merge_requests') do - = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do - %span - Merge Requests - %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) - = nav_link(controller: 'dashboard/snippets') do - = link_to dashboard_snippets_path, title: 'Snippets' do +%ul.nav + = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do + = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do + %span + Projects + = nav_link(path: 'dashboard#activity') do + = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do + %span + Activity + - if koding_enabled? + = nav_link(controller: :koding) do + = link_to koding_path, title: 'Koding' do %span - Snippets - - = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do + Koding + = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do + = link_to dashboard_groups_path, title: 'Groups' do + %span + Groups + = nav_link(controller: 'dashboard/milestones') do + = link_to dashboard_milestones_path, title: 'Milestones' do + %span + Milestones + = nav_link(path: 'dashboard#issues') do + = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do %span - About GitLab CE + Issues + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) + = nav_link(path: 'dashboard#merge_requests') do + = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do + %span + Merge Requests + %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) + = nav_link(controller: 'dashboard/snippets') do + = link_to dashboard_snippets_path, title: 'Snippets' do + %span + Snippets + + = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do + %span + About GitLab CE -- cgit v1.2.1 From 93c57201b224696f8cd11b6fa2cd85e0c49f92be Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 09:28:15 -0600 Subject: Remove pinned nav functionality and side_navbar_class --- app/helpers/nav_helper.rb | 14 -------------- app/views/layouts/_page.html.haml | 19 +------------------ 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index e21178c7377..c1523b4dabf 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -1,10 +1,4 @@ module NavHelper - def page_sidebar_class - if pinned_nav? - "page-sidebar-expanded page-sidebar-pinned" - end - end - def page_gutter_class if current_path?('merge_requests#show') || current_path?('merge_requests#diffs') || @@ -32,10 +26,6 @@ module NavHelper class_name = '' class_name << " with-horizontal-nav" if defined?(nav) && nav - if pinned_nav? - class_name << " header-sidebar-expanded header-sidebar-pinned" - end - class_name end @@ -46,8 +36,4 @@ module NavHelper def nav_control_class "nav-control" if current_user end - - def pinned_nav? - cookies[:pin_nav] == 'true' - end end diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 54d02ee8e4b..5d227d1d52a 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,21 +1,4 @@ -.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" } - .sidebar-wrapper.nicescroll - .sidebar-action-buttons - .nav-header-btn.toggle-nav-collapse{ title: "Open/Close" } - %span.sr-only Toggle navigation - = icon('bars') - - %div{ class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: { placement: 'right', container: 'body' } } - %span.sr-only Toggle navigation pinning - = icon('fw thumb-tack') - - - if defined?(sidebar) && sidebar - = render "layouts/nav/#{sidebar}" - - elsif current_user - = render 'layouts/nav/dashboard' - - else - = render 'layouts/nav/explore' - +.page-with-sidebar{ class: "#{page_gutter_class}" } - if defined?(nav) && nav .layout-nav .container-fluid -- cgit v1.2.1 From 109953885cdf6686d929623cf8f2fc5b55c90cb4 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 09:48:04 -0600 Subject: Remove all unnecessary sidebar.js --- app/assets/javascripts/sidebar.js.es6 | 91 ----------------------------------- 1 file changed, 91 deletions(-) diff --git a/app/assets/javascripts/sidebar.js.es6 b/app/assets/javascripts/sidebar.js.es6 index 33e4b7db681..cd0aa9be6e5 100644 --- a/app/assets/javascripts/sidebar.js.es6 +++ b/app/assets/javascripts/sidebar.js.es6 @@ -2,70 +2,7 @@ /* global Cookies */ (() => { - const pinnedStateCookie = 'pin_nav'; - const sidebarBreakpoint = 1024; - - const pageSelector = '.page-with-sidebar'; - const navbarSelector = '.navbar-gitlab'; - const sidebarWrapperSelector = '.sidebar-wrapper'; - const sidebarContentSelector = '.nav-sidebar'; - - const pinnedToggleSelector = '.js-nav-pin'; - const sidebarToggleSelector = '.toggle-nav-collapse, .side-nav-toggle'; - - const pinnedPageClass = 'page-sidebar-pinned'; - const expandedPageClass = 'page-sidebar-expanded'; - - const pinnedNavbarClass = 'header-sidebar-pinned'; - const expandedNavbarClass = 'header-sidebar-expanded'; - class Sidebar { - constructor() { - if (!Sidebar.singleton) { - Sidebar.singleton = this; - Sidebar.singleton.init(); - } - - return Sidebar.singleton; - } - - init() { - this.isPinned = Cookies.get(pinnedStateCookie) === 'true'; - this.isExpanded = ( - window.innerWidth >= sidebarBreakpoint && - $(pageSelector).hasClass(expandedPageClass) - ); - $(window).on('resize', () => this.setSidebarHeight()); - $(document) - .on('click', sidebarToggleSelector, () => this.toggleSidebar()) - .on('click', pinnedToggleSelector, () => this.togglePinnedState()) - .on('click', 'html, body, a, button', (e) => this.handleClickEvent(e)) - .on('DOMContentLoaded', () => this.renderState()) - .on('scroll', () => this.setSidebarHeight()) - .on('todo:toggle', (e, count) => this.updateTodoCount(count)); - this.renderState(); - this.setSidebarHeight(); - } - - handleClickEvent(e) { - if (this.isExpanded && (!this.isPinned || window.innerWidth < sidebarBreakpoint)) { - const $target = $(e.target); - const targetIsToggle = $target.closest(sidebarToggleSelector).length > 0; - const targetIsSidebar = $target.closest(sidebarWrapperSelector).length > 0; - if (!targetIsToggle && (!targetIsSidebar || $target.closest('a'))) { - this.toggleSidebar(); - } - } - } - - updateTodoCount(count) { - $('.js-todos-count').text(gl.text.addDelimiter(count)); - } - - toggleSidebar() { - this.isExpanded = !this.isExpanded; - this.renderState(); - } setSidebarHeight() { const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight(); @@ -76,34 +13,6 @@ $('.js-right-sidebar').outerHeight('100%'); } } - - togglePinnedState() { - this.isPinned = !this.isPinned; - if (!this.isPinned) { - this.isExpanded = false; - } - Cookies.set(pinnedStateCookie, this.isPinned ? 'true' : 'false', { expires: 3650 }); - this.renderState(); - } - - renderState() { - $(pageSelector) - .toggleClass(pinnedPageClass, this.isPinned && this.isExpanded) - .toggleClass(expandedPageClass, this.isExpanded); - $(navbarSelector) - .toggleClass(pinnedNavbarClass, this.isPinned && this.isExpanded) - .toggleClass(expandedNavbarClass, this.isExpanded); - - const $pinnedToggle = $(pinnedToggleSelector); - const tooltipText = this.isPinned ? 'Unpin navigation' : 'Pin navigation'; - const tooltipState = $pinnedToggle.attr('aria-describedby') && this.isExpanded ? 'show' : 'hide'; - $pinnedToggle.attr('title', tooltipText).tooltip('fixTitle').tooltip(tooltipState); - - if (this.isExpanded) { - const sidebarContent = $(sidebarContentSelector); - setTimeout(() => { sidebarContent.niceScroll().updateScrollBar(); }, 200); - } - } } window.gl = window.gl || {}; -- cgit v1.2.1 From 4b7f0b327cceedf7b107db5fcfa7af75e4eb9e34 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 09:56:27 -0600 Subject: Remove all gitlab theme related code --- app/assets/javascripts/application.js | 5 - app/assets/stylesheets/framework.scss | 1 - app/assets/stylesheets/framework/animations.scss | 1 - app/assets/stylesheets/framework/gitlab-theme.scss | 144 --------------------- app/assets/stylesheets/framework/sidebar.scss | 67 ---------- .../stylesheets/pages/profiles/preferences.scss | 39 ------ app/helpers/preferences_helper.rb | 4 - app/views/layouts/application.html.haml | 2 +- app/views/layouts/nav/_dashboard.html.haml | 2 +- app/views/layouts/nav/_explore.html.haml | 2 +- app/views/profiles/preferences/show.html.haml | 6 - app/views/profiles/preferences/update.js.erb | 4 - config/initializers/1_settings.rb | 1 - lib/gitlab/themes.rb | 87 ------------- spec/features/profiles/preferences_spec.rb | 29 ----- spec/helpers/preferences_helper_spec.rb | 26 ---- spec/lib/gitlab/themes_spec.rb | 48 ------- 17 files changed, 3 insertions(+), 465 deletions(-) delete mode 100644 app/assets/stylesheets/framework/gitlab-theme.scss delete mode 100644 lib/gitlab/themes.rb delete mode 100644 spec/lib/gitlab/themes_spec.rb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4b5c9686cab..3c1d65b8e67 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -101,11 +101,6 @@ require('es6-promise').polyfill(); } }); - $('.nav-sidebar').niceScroll({ - cursoropacitymax: '0.4', - cursorcolor: '#FFF', - cursorborder: '1px solid #FFF' - }); $('.js-select-on-focus').on('focusin', function () { return $(this).select().one('mouseup', function (e) { return e.preventDefault(); diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 08f203a1bf6..39cf3b5f8ae 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -19,7 +19,6 @@ @import "framework/flash.scss"; @import "framework/forms.scss"; @import "framework/gfm.scss"; -@import "framework/gitlab-theme.scss"; @import "framework/header.scss"; @import "framework/highlight.scss"; @import "framework/issue_box.scss"; diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 0ca5a9343f7..49ae84b7a70 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -140,7 +140,6 @@ a { @include transition(background-color, box-shadow); } -.nav-sidebar a, .dropdown-menu a, .dropdown-menu button, .dropdown-menu-nav a { diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss deleted file mode 100644 index d6566dc4ec9..00000000000 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Styles the GitLab application with a specific color theme - * - * $color-light - - * $color - - * $color-darker - - * $color-dark - - */ -@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { - .page-with-sidebar { - .toggle-nav-collapse, - .pin-nav-btn { - color: $color-light; - - &:hover { - color: $white-light; - } - } - - .sidebar-wrapper { - background: $color-darker; - } - - .sidebar-action-buttons { - color: $color-light; - background-color: lighten($color-darker, 5%); - } - - .nav-sidebar { - li { - a { - color: $color-light; - - &:hover, - &:focus, - &:active { - background: $color-dark; - } - - i { - color: $color-light; - } - - path, - polygon { - fill: $color-light; - } - - .count { - color: $color-light; - background: $color-dark; - } - - svg { - position: relative; - top: 3px; - } - } - - &.separate-item { - border-top: 1px solid $color; - } - - &.active a { - color: $white-light; - background: $color-dark; - - &.no-highlight { - border: none; - } - - i { - color: $white-light; - } - - path, - polygon { - fill: $white-light; - } - } - } - - .about-gitlab { - color: $color-light; - } - } - } -} - -$theme-charcoal-light: #b9bbbe; -$theme-charcoal: #485157; -$theme-charcoal-dark: #3d454d; -$theme-charcoal-darker: #383f45; - -$theme-blue-light: #becde9; -$theme-blue: #2980b9; -$theme-blue-dark: #1970a9; -$theme-blue-darker: #096099; - -$theme-graphite-light: #ccc; -$theme-graphite: #777; -$theme-graphite-dark: #666; -$theme-graphite-darker: #555; - -$theme-black-light: #979797; -$theme-black: #373737; -$theme-black-dark: #272727; -$theme-black-darker: #222; - -$theme-green-light: #adc; -$theme-green: #019875; -$theme-green-dark: #018865; -$theme-green-darker: #017855; - -$theme-violet-light: #98c; -$theme-violet: #548; -$theme-violet-dark: #436; -$theme-violet-darker: #325; - -body { - &.ui_blue { - @include gitlab-theme($theme-blue-light, $theme-blue, $theme-blue-dark, $theme-blue-darker); - } - - &.ui_charcoal { - @include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker); - } - - &.ui_graphite { - @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker); - } - - &.ui_black { - @include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker); - } - - &.ui_green { - @include gitlab-theme($theme-green-light, $theme-green, $theme-green-dark, $theme-green-darker); - } - - &.ui_violet { - @include gitlab-theme($theme-violet-light, $theme-violet, $theme-violet-dark, $theme-violet-darker); - } -} diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 20bcb1eeb23..390d2589ab2 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -47,73 +47,6 @@ } } -.nav-sidebar { - position: absolute; - top: 50px; - bottom: 0; - width: $sidebar_width; - overflow-y: auto; - overflow-x: hidden; - - &.navbar-collapse { - padding: 0 !important; - } - - li { - &.separate-item { - padding-top: 10px; - margin-top: 10px; - } - - .icon-container { - width: 34px; - display: inline-block; - text-align: center; - } - - a { - padding: 7px $gl-sidebar-padding; - font-size: $gl-font-size; - line-height: 24px; - display: block; - text-decoration: none; - font-weight: normal; - - &:hover, - &:active, - &:focus { - text-decoration: none; - } - - i { - font-size: 16px; - } - - i, - svg { - margin-right: 13px; - } - } - } - - .count { - float: right; - padding: 0 8px; - border-radius: 6px; - } - - .about-gitlab { - padding: 7px $gl-sidebar-padding; - font-size: $gl-font-size; - line-height: 24px; - display: block; - text-decoration: none; - font-weight: normal; - position: absolute; - bottom: 10px; - } -} - .sidebar-action-buttons { width: $sidebar_width; position: absolute; diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index 100ace41f2a..305feaacaa1 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -1,42 +1,3 @@ -.application-theme { - label { - margin-right: 20px; - text-align: center; - - .preview { - border-radius: 4px; - - height: 80px; - margin-bottom: 10px; - width: 160px; - - &.ui_blue { - background: $theme-blue; - } - - &.ui_charcoal { - background: $theme-charcoal; - } - - &.ui_graphite { - background: $theme-graphite; - } - - &.ui_black { - background: $theme-black; - } - - &.ui_green { - background: $theme-green; - } - - &.ui_violet { - background: $theme-violet; - } - } - } -} - .syntax-theme { label { margin-right: 20px; diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index dd0a4ea03f0..c3a08d76318 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -41,10 +41,6 @@ module PreferencesHelper ] end - def user_application_theme - Gitlab::Themes.for_user(current_user).css_class - end - def user_color_scheme Gitlab::ColorSchemes.for_user(current_user).css_class end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 248d439cd05..19bd9b6d5c9 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en", class: "#{page_class}" } = render "layouts/head" - %body{ class: "#{user_application_theme}", data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } + %body{ data: { page: body_data_page, project: "#{@project.path if @project}", group: "#{@group.path if @group}" } } = Gon::Base.render_data = render "layouts/header/default", title: header_title diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index efcb60addc0..4f2547d0838 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,4 +1,4 @@ -%ul.nav +%ul = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do %span diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index e5bda7b3a6f..3a1fcd00e9c 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,4 +1,4 @@ -%ul.nav.nav-sidebar +%ul = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = link_to explore_root_path, title: 'Projects' do %span diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index feadd863b00..787339e0fe2 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -7,12 +7,6 @@ Application theme %p This setting allows you to customize the appearance of the site, e.g. the sidebar. - .col-lg-9.application-theme - - Gitlab::Themes.each do |theme| - = label_tag do - .preview{ class: theme.css_class } - = f.radio_button :theme_id, theme.id - = theme.name .col-sm-12 %hr .col-lg-3.profile-settings-sidebar diff --git a/app/views/profiles/preferences/update.js.erb b/app/views/profiles/preferences/update.js.erb index 8966dd3fd86..431ab9d052b 100644 --- a/app/views/profiles/preferences/update.js.erb +++ b/app/views/profiles/preferences/update.js.erb @@ -1,7 +1,3 @@ -// Remove body class for any previous theme, re-add current one -$('body').removeClass('<%= Gitlab::Themes.body_classes %>') -$('body').addClass('<%= user_application_theme %>') - // Toggle container-fluid class if ('<%= current_user.layout %>' === 'fluid') { $('.content-wrapper .container-fluid').removeClass('container-limited') diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index ab59394cb0c..3f716dd8833 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -183,7 +183,6 @@ Settings['gitlab'] ||= Settingslogic.new({}) Settings.gitlab['default_projects_limit'] ||= 10 Settings.gitlab['default_branch_protection'] ||= 2 Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil? -Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil? Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost' Settings.gitlab['ssh_host'] ||= Settings.gitlab.host Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb deleted file mode 100644 index 19ab76ae80f..00000000000 --- a/lib/gitlab/themes.rb +++ /dev/null @@ -1,87 +0,0 @@ -module Gitlab - # Module containing GitLab's application theme definitions and helper methods - # for accessing them. - module Themes - extend self - - # Theme ID used when no `default_theme` configuration setting is provided. - APPLICATION_DEFAULT = 2 - - # Struct class representing a single Theme - Theme = Struct.new(:id, :name, :css_class) - - # All available Themes - THEMES = [ - Theme.new(1, 'Graphite', 'ui_graphite'), - Theme.new(2, 'Charcoal', 'ui_charcoal'), - Theme.new(3, 'Green', 'ui_green'), - Theme.new(4, 'Black', 'ui_black'), - Theme.new(5, 'Violet', 'ui_violet'), - Theme.new(6, 'Blue', 'ui_blue') - ].freeze - - # Convenience method to get a space-separated String of all the theme - # classes that might be applied to the `body` element - # - # Returns a String - def body_classes - THEMES.collect(&:css_class).uniq.join(' ') - end - - # Get a Theme by its ID - # - # If the ID is invalid, returns the default Theme. - # - # id - Integer ID - # - # Returns a Theme - def by_id(id) - THEMES.detect { |t| t.id == id } || default - end - - # Returns the number of defined Themes - def count - THEMES.size - end - - # Get the default Theme - # - # Returns a Theme - def default - by_id(default_id) - end - - # Iterate through each Theme - # - # Yields the Theme object - def each(&block) - THEMES.each(&block) - end - - # Get the Theme for the specified user, or the default - # - # user - User record - # - # Returns a Theme - def for_user(user) - if user - by_id(user.theme_id) - else - default - end - end - - private - - def default_id - id = Gitlab.config.gitlab.default_theme.to_i - - # Prevent an invalid configuration setting from causing an infinite loop - if id < THEMES.first.id || id > THEMES.last.id - APPLICATION_DEFAULT - else - id - end - end - end -end diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb index a6b841c0210..15c8677fcd3 100644 --- a/spec/features/profiles/preferences_spec.rb +++ b/spec/features/profiles/preferences_spec.rb @@ -8,35 +8,6 @@ describe 'Profile > Preferences', feature: true do visit profile_preferences_path end - describe 'User changes their application theme', js: true do - let(:default) { Gitlab::Themes.default } - let(:theme) { Gitlab::Themes.by_id(5) } - - it 'creates a flash message' do - choose "user_theme_id_#{theme.id}" - - expect_preferences_saved_message - end - - it 'updates their preference' do - choose "user_theme_id_#{theme.id}" - - allowing_for_delay do - visit page.current_path - expect(page).to have_checked_field("user_theme_id_#{theme.id}") - end - end - - it 'reflects the changes immediately' do - expect(page).to have_selector("body.#{default.css_class}") - - choose "user_theme_id_#{theme.id}" - - expect(page).not_to have_selector("body.#{default.css_class}") - expect(page).to have_selector("body.#{theme.css_class}") - end - end - describe 'User changes their syntax highlighting theme', js: true do it 'creates a flash message' do choose 'user_color_scheme_id_5' diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 1f02e06e312..f3e79cc7290 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -26,32 +26,6 @@ describe PreferencesHelper do end end - describe 'user_application_theme' do - context 'with a user' do - it "returns user's theme's css_class" do - stub_user(theme_id: 3) - - expect(helper.user_application_theme).to eq 'ui_green' - end - - it 'returns the default when id is invalid' do - stub_user(theme_id: Gitlab::Themes.count + 5) - - allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(2) - - expect(helper.user_application_theme).to eq 'ui_charcoal' - end - end - - context 'without a user' do - it 'returns the default theme' do - stub_user - - expect(helper.user_application_theme).to eq Gitlab::Themes.default.css_class - end - end - end - describe 'user_color_scheme' do context 'with a user' do it "returns user's scheme's css_class" do diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb deleted file mode 100644 index 7a140518dd2..00000000000 --- a/spec/lib/gitlab/themes_spec.rb +++ /dev/null @@ -1,48 +0,0 @@ -require 'spec_helper' - -describe Gitlab::Themes, lib: true do - describe '.body_classes' do - it 'returns a space-separated list of class names' do - css = described_class.body_classes - - expect(css).to include('ui_graphite') - expect(css).to include(' ui_charcoal ') - expect(css).to include(' ui_blue') - end - end - - describe '.by_id' do - it 'returns a Theme by its ID' do - expect(described_class.by_id(1).name).to eq 'Graphite' - expect(described_class.by_id(6).name).to eq 'Blue' - end - end - - describe '.default' do - it 'returns the default application theme' do - allow(described_class).to receive(:default_id).and_return(2) - expect(described_class.default.id).to eq 2 - end - - it 'prevents an infinite loop when configuration default is invalid' do - default = described_class::APPLICATION_DEFAULT - themes = described_class::THEMES - - config = double(default_theme: 0).as_null_object - allow(Gitlab).to receive(:config).and_return(config) - expect(described_class.default.id).to eq default - - config = double(default_theme: themes.size + 5).as_null_object - allow(Gitlab).to receive(:config).and_return(config) - expect(described_class.default.id).to eq default - end - end - - describe '.each' do - it 'passes the block to the THEMES Array' do - ids = [] - described_class.each { |theme| ids << theme.id } - expect(ids).not_to be_empty - end - end -end -- cgit v1.2.1 From 572bc7bc3771d76afe5b7524369ed069b66fa1f2 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 10:34:01 -0600 Subject: Remove global sidebar styles --- app/assets/stylesheets/framework/animations.scss | 2 +- app/assets/stylesheets/framework/header.scss | 2 +- app/assets/stylesheets/framework/sidebar.scss | 118 +---------------------- app/assets/stylesheets/framework/variables.scss | 4 +- app/assets/stylesheets/pages/issuable.scss | 4 +- app/assets/stylesheets/print.scss | 1 - 6 files changed, 10 insertions(+), 121 deletions(-) diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss index 49ae84b7a70..90935b9616b 100644 --- a/app/assets/stylesheets/framework/animations.scss +++ b/app/assets/stylesheets/framework/animations.scss @@ -116,7 +116,7 @@ } .btn, -.side-nav-toggle { +.global-dropdown-toggle { @include transition(background-color, border-color, color, box-shadow); } diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 34e010e0e8a..be689bd252b 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -101,7 +101,7 @@ header { } } - .side-nav-toggle { + .global-dropdown-toggle { position: absolute; left: -10px; margin: 7px 0; diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 390d2589ab2..d09b1c9d7f5 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,36 +1,3 @@ -.page-with-sidebar { - padding-bottom: 25px; - transition: padding $sidebar-transition-duration; - - &.page-sidebar-pinned { - .sidebar-wrapper { - box-shadow: none; - } - } - - .sidebar-wrapper { - position: fixed; - top: 0; - bottom: 0; - left: 0; - height: 100%; - width: 0; - overflow: hidden; - transition: width $sidebar-transition-duration; - box-shadow: 2px 0 16px 0 $black-transparent; - } -} - -.sidebar-wrapper { - z-index: 1000; - background: $gray-light; - - .nicescroll-rails-hr { - // TODO: Figure out why nicescroll doesn't hide horizontal bar - display: none!important; - } -} - .content-wrapper { width: 100%; transition: padding $sidebar-transition-duration; @@ -47,38 +14,6 @@ } } -.sidebar-action-buttons { - width: $sidebar_width; - position: absolute; - top: 0; - left: 0; - min-height: 50px; - padding: 5px 0; - font-size: 18px; - line-height: 30px; - - .toggle-nav-collapse { - left: 0; - } - - .pin-nav-btn { - right: 0; - display: none; - - @media (min-width: $sidebar-breakpoint) { - display: block; - } - - .fa { - transition: transform .15s; - - .page-sidebar-pinned & { - transform: rotate(90deg); - } - } - } -} - .nav-header-btn { padding: 10px $gl-sidebar-padding; color: inherit; @@ -94,59 +29,16 @@ } } -.page-sidebar-expanded { - .sidebar-wrapper { - width: $sidebar_width; - } -} - -.page-sidebar-pinned { - .content-wrapper, - .layout-nav { - @media (min-width: $sidebar-breakpoint) { - padding-left: $sidebar_width; - } - } - - .merge-request-tabs-holder.affix { - @media (min-width: $sidebar-breakpoint) { - left: $sidebar_width; - } - } - - &.right-sidebar-expanded { - .line-resolve-all-container { - @media (min-width: $sidebar-breakpoint) { - display: none; - } - } - } -} - -header.header-sidebar-pinned { - @media (min-width: $sidebar-breakpoint) { - padding-left: ($sidebar_width + $gl-padding); - - .side-nav-toggle { - display: none; - } - - .header-content { - padding-left: 0; - } - } -} - .right-sidebar-collapsed { padding-right: 0; @media (min-width: $screen-sm-min) { .content-wrapper { - padding-right: $sidebar_collapsed_width; + padding-right: $gutter_collapsed_width; } .merge-request-tabs-holder.affix { - right: $sidebar_collapsed_width; + right: $gutter_collapsed_width; } } @@ -164,7 +56,7 @@ header.header-sidebar-pinned { @media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) { &:not(.build-sidebar):not(.wiki-sidebar) { - padding-right: $sidebar_collapsed_width; + padding-right: $gutter_collapsed_width; } } @@ -178,12 +70,12 @@ header.header-sidebar-pinned { } &.with-overlay .merge-request-tabs-holder.affix { - right: $sidebar_collapsed_width; + right: $gutter_collapsed_width; } } &.with-overlay { - padding-right: $sidebar_collapsed_width; + padding-right: $gutter_collapsed_width; } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 7809d4866f1..ba0af072716 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -1,8 +1,6 @@ /* * Layout */ -$sidebar_collapsed_width: 62px; -$sidebar_width: 220px; $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 250px; @@ -541,4 +539,4 @@ Pipeline Graph */ $stage-hover-bg: #eaf3fc; $stage-hover-border: #d1e7fc; -$action-icon-color: #d6d6d6; \ No newline at end of file +$action-icon-color: #d6d6d6; diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index a53cc27fac9..4426169ef5a 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -253,11 +253,11 @@ display: block; } - width: $sidebar_collapsed_width; + width: $gutter_collapsed_width; padding-top: 0; .block { - width: $sidebar_collapsed_width - 2px; + width: $gutter_collapsed_width - 2px; margin-left: -19px; padding: 15px 0 0; border-bottom: none; diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss index 0ff3c3f5472..6cc1cc8e263 100644 --- a/app/assets/stylesheets/print.scss +++ b/app/assets/stylesheets/print.scss @@ -31,7 +31,6 @@ nav.navbar-collapse.collapse, .blob-commit-info, .file-title, .file-holder, -.sidebar-wrapper, .nav, .btn, ul.notes-form, -- cgit v1.2.1 From a1a0a0350747d656281564ae1cee157171668516 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 10:57:15 -0600 Subject: Remove sidebar toggle button; replace with dropdown --- app/assets/stylesheets/framework/header.scss | 5 ++++- app/views/layouts/header/_default.html.haml | 25 ++++++++++--------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index be689bd252b..2a6273f1301 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -101,9 +101,12 @@ header { } } - .global-dropdown-toggle { + .global-dropdown { position: absolute; left: -10px; + } + + .global-dropdown-toggle { margin: 7px 0; font-size: 18px; padding: 6px 10px; diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 14d7a75c34a..ea56d92cf34 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -2,9 +2,16 @@ %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content .container-fluid .header-content - %button.side-nav-toggle{ type: 'button', "aria-label" => "Toggle global navigation" } - %span.sr-only Toggle navigation - = icon('bars') + .dropdown.global-dropdown + %button.global-dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.sr-only Toggle navigation + = icon('bars') + .dropdown-menu-nav + %ul + - if current_user + = render 'layouts/nav/dashboard' + - else + = render 'layouts/nav/explore' %button.navbar-toggle{ type: 'button' } %span.sr-only Toggle navigation = icon('ellipsis-v') @@ -55,20 +62,8 @@ %div = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' - %h1.title= title - .dropdown - %button.global-dropdown-toggle{ href: '#', 'data-toggle' => 'dropdown' } - %span.sr-only Toggle navigation - = icon('bars') - .dropdown-menu-nav - %ul - - if current_user - = render 'layouts/nav/dashboard' - - else - = render 'layouts/nav/explore' - .header-logo = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = brand_header_logo -- cgit v1.2.1 From b0dff3c83edcc2b48a0457ad93da4e8d4324ed06 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 11:11:31 -0600 Subject: Fix dropdown alignment --- app/views/layouts/nav/_dashboard.html.haml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 4f2547d0838..bb611d39be5 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -24,17 +24,16 @@ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do %span Issues - %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) + %span.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened)) = nav_link(path: 'dashboard#merge_requests') do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do %span Merge Requests - %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) + %span.badge= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened)) = nav_link(controller: 'dashboard/snippets') do = link_to dashboard_snippets_path, title: 'Snippets' do %span Snippets - - = link_to help_path, title: 'About GitLab CE', class: 'about-gitlab' do - %span - About GitLab CE + %li.divider + %li + = link_to "About GitLab CE", help_path, title: 'About GitLab CE', class: 'about-gitlab' -- cgit v1.2.1 From 0667d1aa7f251f5bf11dd121fe2f696107d8103e Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 11:38:53 -0600 Subject: Drop theme ID from users table --- app/controllers/admin/users_controller.rb | 1 - app/controllers/profiles/preferences_controller.rb | 1 - app/models/user.rb | 1 - config/gitlab.yml.example | 8 ------ .../20170213172852_remove_theme_id_from_users.rb | 29 ++++++++++++++++++++++ db/schema.rb | 7 +++--- spec/models/user_spec.rb | 1 - 7 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 db/migrate/20170213172852_remove_theme_id_from_users.rb diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 1cd50852e89..7ffde71c3b1 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -194,7 +194,6 @@ class Admin::UsersController < Admin::ApplicationController :provider, :remember_me, :skype, - :theme_id, :twitter, :username, :website_url diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index a9a06ecc808..0d891ef4004 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -34,7 +34,6 @@ class Profiles::PreferencesController < Profiles::ApplicationController :layout, :dashboard, :project_view, - :theme_id ) end end diff --git a/app/models/user.rb b/app/models/user.rb index ad997ce2b13..f614eb66e1f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,7 +21,6 @@ class User < ActiveRecord::Base default_value_for :can_create_team, false default_value_for :hide_no_ssh_key, false default_value_for :hide_no_password, false - default_value_for :theme_id, gitlab_config.default_theme attr_encrypted :otp_secret, key: Gitlab::Application.secrets.otp_key_base, diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index cc1af77a1de..560be67a70f 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -76,14 +76,6 @@ production: &base # default_can_create_group: false # default: true # username_changing_enabled: false # default: true - User can change her username/namespace - ## Default theme ID - ## 1 - Graphite - ## 2 - Charcoal - ## 3 - Green - ## 4 - Gray - ## 5 - Violet - ## 6 - Blue - # default_theme: 2 # default: 2 ## Automatic issue closing # If a commit message matches this regular expression, all issues referenced from the matched text will be closed. diff --git a/db/migrate/20170213172852_remove_theme_id_from_users.rb b/db/migrate/20170213172852_remove_theme_id_from_users.rb new file mode 100644 index 00000000000..857f678ad0e --- /dev/null +++ b/db/migrate/20170213172852_remove_theme_id_from_users.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveThemeIdFromUsers < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + remove_column :users, :theme_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 52672406ec6..df065520835 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170214111112) do +ActiveRecord::Schema.define(version: 20170213172852) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -109,8 +109,8 @@ ActiveRecord::Schema.define(version: 20170214111112) do t.boolean "html_emails_enabled", default: true t.string "plantuml_url" t.boolean "plantuml_enabled" - t.integer "max_pages_size", default: 100, null: false t.integer "terminal_max_session_time", default: 0, null: false + t.integer "max_pages_size", default: 100, null: false end create_table "audit_events", force: :cascade do |t| @@ -1238,7 +1238,6 @@ ActiveRecord::Schema.define(version: 20170214111112) do t.string "linkedin", default: "", null: false t.string "twitter", default: "", null: false t.string "authentication_token" - t.integer "theme_id", default: 1, null: false t.string "bio" t.integer "failed_attempts", default: 0 t.datetime "locked_at" @@ -1351,4 +1350,4 @@ ActiveRecord::Schema.define(version: 20170214111112) do add_foreign_key "timelogs", "merge_requests", name: "fk_timelogs_merge_requests_merge_request_id", on_delete: :cascade add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "u2f_registrations", "users" -end \ No newline at end of file +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 89cef7ab978..546efcfce97 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -582,7 +582,6 @@ describe User, models: true do it "applies defaults to user" do expect(user.projects_limit).to eq(Gitlab.config.gitlab.default_projects_limit) expect(user.can_create_group).to eq(Gitlab.config.gitlab.default_can_create_group) - expect(user.theme_id).to eq(Gitlab.config.gitlab.default_theme) expect(user.external).to be_falsey end end -- cgit v1.2.1 From c780ad0e926f15ed47acd744df99f89081c8a340 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 11:50:13 -0600 Subject: Remove all instances of theme_id; require downtime --- config/gitlab.yml.example | 2 +- db/migrate/20170213172852_remove_theme_id_from_users.rb | 2 +- db/schema.rb | 2 +- doc/api/keys.md | 1 - doc/api/session.md | 1 - doc/api/users.md | 5 ----- lib/api/entities.rb | 2 +- spec/controllers/profiles/preferences_controller_spec.rb | 6 ++---- spec/fixtures/api/schemas/user/login.json | 1 - spec/fixtures/api/schemas/user/public.json | 16 +++++++--------- spec/models/user_spec.rb | 3 +-- spec/support/gitlab_stubs/session.json | 4 ++-- spec/support/gitlab_stubs/user.json | 4 ++-- 13 files changed, 18 insertions(+), 31 deletions(-) diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 560be67a70f..a82ff605a70 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -603,4 +603,4 @@ test: admin_group: '' staging: - <<: *base \ No newline at end of file + <<: *base diff --git a/db/migrate/20170213172852_remove_theme_id_from_users.rb b/db/migrate/20170213172852_remove_theme_id_from_users.rb index 857f678ad0e..cd6388ab7d2 100644 --- a/db/migrate/20170213172852_remove_theme_id_from_users.rb +++ b/db/migrate/20170213172852_remove_theme_id_from_users.rb @@ -5,7 +5,7 @@ class RemoveThemeIdFromUsers < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers # Set this constant to true if this migration requires downtime. - DOWNTIME = false + DOWNTIME = true # When a migration requires downtime you **must** uncomment the following # constant and define a short and easy to understand explanation as to why the diff --git a/db/schema.rb b/db/schema.rb index df065520835..32cbefc2566 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -109,8 +109,8 @@ ActiveRecord::Schema.define(version: 20170213172852) do t.boolean "html_emails_enabled", default: true t.string "plantuml_url" t.boolean "plantuml_enabled" - t.integer "terminal_max_session_time", default: 0, null: false t.integer "max_pages_size", default: 100, null: false + t.integer "terminal_max_session_time", default: 0, null: false end create_table "audit_events", force: :cascade do |t| diff --git a/doc/api/keys.md b/doc/api/keys.md index b68f08a007d..3b55c2baf56 100644 --- a/doc/api/keys.md +++ b/doc/api/keys.md @@ -33,7 +33,6 @@ Parameters: "twitter": "", "website_url": "", "email": "john@example.com", - "theme_id": 2, "color_scheme_id": 1, "projects_limit": 10, "current_sign_in_at": null, diff --git a/doc/api/session.md b/doc/api/session.md index f776424023e..d7809716fbe 100644 --- a/doc/api/session.md +++ b/doc/api/session.md @@ -41,7 +41,6 @@ Example response: "twitter": "", "website_url": "", "email": "john@example.com", - "theme_id": 1, "color_scheme_id": 1, "projects_limit": 10, "current_sign_in_at": "2015-07-07T07:10:58.392Z", diff --git a/doc/api/users.md b/doc/api/users.md index ed3469521fc..626f7e63e3e 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -72,7 +72,6 @@ GET /users "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", - "theme_id": 1, "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -105,7 +104,6 @@ GET /users "organization": "", "last_sign_in_at": null, "confirmed_at": "2012-05-30T16:53:06.148Z", - "theme_id": 1, "color_scheme_id": 3, "projects_limit": 100, "current_sign_in_at": "2014-03-19T17:54:13Z", @@ -198,7 +196,6 @@ Parameters: "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", - "theme_id": 1, "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -323,7 +320,6 @@ GET /user "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", - "theme_id": 1, "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", @@ -369,7 +365,6 @@ GET /user "organization": "", "last_sign_in_at": "2012-06-01T11:41:01Z", "confirmed_at": "2012-05-23T09:05:22Z", - "theme_id": 1, "color_scheme_id": 2, "projects_limit": 100, "current_sign_in_at": "2012-06-02T06:36:55Z", diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 232f231ddd2..400ee7c92aa 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -26,7 +26,7 @@ module API expose :last_sign_in_at expose :confirmed_at expose :email - expose :theme_id, :color_scheme_id, :projects_limit, :current_sign_in_at + expose :color_scheme_id, :projects_limit, :current_sign_in_at expose :identities, using: Entities::Identity expose :can_create_group?, as: :can_create_group expose :can_create_project?, as: :can_create_project diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb index 8f02003992a..7b3aa0491c7 100644 --- a/spec/controllers/profiles/preferences_controller_spec.rb +++ b/spec/controllers/profiles/preferences_controller_spec.rb @@ -25,8 +25,7 @@ describe Profiles::PreferencesController do def go(params: {}, format: :js) params.reverse_merge!( color_scheme_id: '1', - dashboard: 'stars', - theme_id: '1' + dashboard: 'stars' ) patch :update, user: params, format: format @@ -41,8 +40,7 @@ describe Profiles::PreferencesController do it "changes the user's preferences" do prefs = { color_scheme_id: '1', - dashboard: 'stars', - theme_id: '2' + dashboard: 'stars' }.with_indifferent_access expect(user).to receive(:update_attributes).with(prefs) diff --git a/spec/fixtures/api/schemas/user/login.json b/spec/fixtures/api/schemas/user/login.json index e6c1d9c9d84..6181b3ccc86 100644 --- a/spec/fixtures/api/schemas/user/login.json +++ b/spec/fixtures/api/schemas/user/login.json @@ -19,7 +19,6 @@ "organization", "last_sign_in_at", "confirmed_at", - "theme_id", "color_scheme_id", "projects_limit", "current_sign_in_at", diff --git a/spec/fixtures/api/schemas/user/public.json b/spec/fixtures/api/schemas/user/public.json index dbd5d32e89c..5587cfec61a 100644 --- a/spec/fixtures/api/schemas/user/public.json +++ b/spec/fixtures/api/schemas/user/public.json @@ -19,7 +19,6 @@ "organization", "last_sign_in_at", "confirmed_at", - "theme_id", "color_scheme_id", "projects_limit", "current_sign_in_at", @@ -32,14 +31,14 @@ "properties": { "id": { "type": "integer" }, "username": { "type": "string" }, - "email": { + "email": { "type": "string", "pattern": "^[^@]+@[^@]+$" }, "name": { "type": "string" }, - "state": { + "state": { "type": "string", - "enum": ["active", "blocked"] + "enum": ["active", "blocked"] }, "avatar_url": { "type": "string" }, "web_url": { "type": "string" }, @@ -54,18 +53,17 @@ "organization": { "type": ["string", "null"] }, "last_sign_in_at": { "type": "date" }, "confirmed_at": { "type": ["date", "null"] }, - "theme_id": { "type": "integer" }, "color_scheme_id": { "type": "integer" }, "projects_limit": { "type": "integer" }, "current_sign_in_at": { "type": "date" }, - "identities": { + "identities": { "type": "array", "items": { "type": "object", "properties": { - "provider": { + "provider": { "type": "string", - "enum": ["github", "bitbucket", "google_oauth2"] + "enum": ["github", "bitbucket", "google_oauth2"] }, "extern_uid": { "type": ["number", "string"] } } @@ -74,6 +72,6 @@ "can_create_group": { "type": "boolean" }, "can_create_project": { "type": "boolean" }, "two_factor_enabled": { "type": "boolean" }, - "external": { "type": "boolean" } + "external": { "type": "boolean" } } } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 546efcfce97..584a4facd94 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -587,12 +587,11 @@ describe User, models: true do end describe 'with default overrides' do - let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true, theme_id: 1) } + let(:user) { User.new(projects_limit: 123, can_create_group: false, can_create_team: true) } it "applies defaults to user" do expect(user.projects_limit).to eq(123) expect(user.can_create_group).to be_falsey - expect(user.theme_id).to eq(1) end end diff --git a/spec/support/gitlab_stubs/session.json b/spec/support/gitlab_stubs/session.json index ce8dfe5ae75..cd55d63125e 100644 --- a/spec/support/gitlab_stubs/session.json +++ b/spec/support/gitlab_stubs/session.json @@ -7,7 +7,7 @@ "skype":"aertert", "linkedin":"", "twitter":"", - "theme_id":2,"color_scheme_id":2, + "color_scheme_id":2, "state":"active", "created_at":"2012-12-21T13:02:20Z", "extern_uid":null, @@ -17,4 +17,4 @@ "can_create_project":false, "private_token":"Wvjy2Krpb7y8xi93owUz", "access_token":"Wvjy2Krpb7y8xi93owUz" -} \ No newline at end of file +} diff --git a/spec/support/gitlab_stubs/user.json b/spec/support/gitlab_stubs/user.json index ce8dfe5ae75..cd55d63125e 100644 --- a/spec/support/gitlab_stubs/user.json +++ b/spec/support/gitlab_stubs/user.json @@ -7,7 +7,7 @@ "skype":"aertert", "linkedin":"", "twitter":"", - "theme_id":2,"color_scheme_id":2, + "color_scheme_id":2, "state":"active", "created_at":"2012-12-21T13:02:20Z", "extern_uid":null, @@ -17,4 +17,4 @@ "can_create_project":false, "private_token":"Wvjy2Krpb7y8xi93owUz", "access_token":"Wvjy2Krpb7y8xi93owUz" -} \ No newline at end of file +} -- cgit v1.2.1 From 23035d90b708216e75fb993a1e48a0426ddacb89 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 16:16:03 -0600 Subject: Make active links bold; fix badge styling --- app/assets/stylesheets/framework/header.scss | 40 +++++++++++++++++++--------- app/views/layouts/header/_default.html.haml | 11 ++++---- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 2a6273f1301..044d3517650 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -100,26 +100,42 @@ header { } } } + } - .global-dropdown { - position: absolute; - left: -10px; + .global-dropdown { + position: absolute; + left: -10px; + + .badge { + font-size: 11px; } - .global-dropdown-toggle { - margin: 7px 0; - font-size: 18px; - padding: 6px 10px; - border: none; - background-color: $gray-light; + li.active { + a { + font-weight: bold; + } + } - &:hover { - background-color: $white-normal; - color: $gl-header-nav-hover-color; + li:hover { + .badge { + background-color: $white-light; } } } + .global-dropdown-toggle { + margin: 7px 0; + font-size: 18px; + padding: 6px 10px; + border: none; + background-color: $gray-light; + + &:hover { + background-color: $white-normal; + color: $gl-header-nav-hover-color; + } + } + .header-content { position: relative; height: $header-height; diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index ea56d92cf34..ddf50d6667f 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -6,12 +6,11 @@ %button.global-dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %span.sr-only Toggle navigation = icon('bars') - .dropdown-menu-nav - %ul - - if current_user - = render 'layouts/nav/dashboard' - - else - = render 'layouts/nav/explore' + .dropdown-menu-nav.global-dropdown-menu + - if current_user + = render 'layouts/nav/dashboard' + - else + = render 'layouts/nav/explore' %button.navbar-toggle{ type: 'button' } %span.sr-only Toggle navigation = icon('ellipsis-v') -- cgit v1.2.1 From 2fc559142e00cc15a47c49c190666284221cc9b0 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Mon, 13 Feb 2017 18:37:26 -0600 Subject: Fix active_tab and issuables_counter specs --- spec/features/dashboard/active_tab_spec.rb | 7 ++++--- spec/features/dashboard/issuables_counter_spec.rb | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb index 7d59fcac517..ae750be4d4a 100644 --- a/spec/features/dashboard/active_tab_spec.rb +++ b/spec/features/dashboard/active_tab_spec.rb @@ -1,14 +1,15 @@ require 'spec_helper' -RSpec.describe 'Dashboard Active Tab', feature: true do +RSpec.describe 'Dashboard Active Tab', js: true, feature: true do before do login_as :user end shared_examples 'page has active tab' do |title| it "#{title} tab" do - expect(page).to have_selector('.nav-sidebar li.active', count: 1) - expect(find('.nav-sidebar li.active')).to have_content(title) + find('.global-dropdown-toggle').trigger('click') + expect(page).to have_selector('.global-dropdown-menu li.active', count: 1) + expect(find('.global-dropdown-menu li.active')).to have_content(title) end end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 41dcfe439c2..603076d7d37 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -36,7 +36,8 @@ describe 'Navigation bar counter', feature: true, js: true, caching: true do def expect_counters(issuable_type, count) dashboard_count = find('li.active span.badge') - nav_count = find(".dashboard-shortcuts-#{issuable_type} span.count") + find('.global-dropdown-toggle').click + nav_count = find(".dashboard-shortcuts-#{issuable_type} span.badge") expect(nav_count).to have_content(count) expect(dashboard_count).to have_content(count) -- cgit v1.2.1 From 8c1b41f12b5ec8dd3dd2547d3ad729e71c4ebadd Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 14 Feb 2017 12:27:47 -0600 Subject: Move migration to post_migrate; fix shortcuts_spec --- .../20170213172852_remove_theme_id_from_users.rb | 29 ---------------------- .../20170213172852_remove_theme_id_from_users.rb | 29 ++++++++++++++++++++++ spec/features/dashboard/shortcuts_spec.rb | 3 ++- 3 files changed, 31 insertions(+), 30 deletions(-) delete mode 100644 db/migrate/20170213172852_remove_theme_id_from_users.rb create mode 100644 db/post_migrate/20170213172852_remove_theme_id_from_users.rb diff --git a/db/migrate/20170213172852_remove_theme_id_from_users.rb b/db/migrate/20170213172852_remove_theme_id_from_users.rb deleted file mode 100644 index cd6388ab7d2..00000000000 --- a/db/migrate/20170213172852_remove_theme_id_from_users.rb +++ /dev/null @@ -1,29 +0,0 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. - -class RemoveThemeIdFromUsers < ActiveRecord::Migration - include Gitlab::Database::MigrationHelpers - - # Set this constant to true if this migration requires downtime. - DOWNTIME = true - - # When a migration requires downtime you **must** uncomment the following - # constant and define a short and easy to understand explanation as to why the - # migration requires downtime. - # DOWNTIME_REASON = '' - - # When using the methods "add_concurrent_index" or "add_column_with_default" - # you must disable the use of transactions as these methods can not run in an - # existing transaction. When using "add_concurrent_index" make sure that this - # method is the _only_ method called in the migration, any other changes - # should go in a separate migration. This ensures that upon failure _only_ the - # index creation fails and can be retried or reverted easily. - # - # To disable transactions uncomment the following line and remove these - # comments: - # disable_ddl_transaction! - - def change - remove_column :users, :theme_id, :integer - end -end diff --git a/db/post_migrate/20170213172852_remove_theme_id_from_users.rb b/db/post_migrate/20170213172852_remove_theme_id_from_users.rb new file mode 100644 index 00000000000..857f678ad0e --- /dev/null +++ b/db/post_migrate/20170213172852_remove_theme_id_from_users.rb @@ -0,0 +1,29 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveThemeIdFromUsers < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + # disable_ddl_transaction! + + def change + remove_column :users, :theme_id, :integer + end +end diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index d9be4e5dbdd..c5a0d1c6d99 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -24,6 +24,7 @@ feature 'Dashboard shortcuts', feature: true, js: true do end def ensure_active_main_tab(content) - expect(find('.nav-sidebar li.active')).to have_content(content) + find('.global-dropdown-toggle').trigger('click') + expect(find('.global-dropdown-menu li.active')).to have_content(content) end end -- cgit v1.2.1 From f1710bd131a040ef9104a1ff1a804b35c9568550 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Tue, 14 Feb 2017 17:43:03 -0600 Subject: Delete sidebar specs and fixtures; update shortcuts_spec to check page title --- spec/features/dashboard/shortcuts_spec.rb | 11 ++-- spec/javascripts/dashboard_spec.js.es6 | 37 ----------- spec/javascripts/fixtures/dashboard.html.haml | 45 -------------- spec/javascripts/project_dashboard_spec.js.es6 | 86 -------------------------- 4 files changed, 5 insertions(+), 174 deletions(-) delete mode 100644 spec/javascripts/dashboard_spec.js.es6 delete mode 100644 spec/javascripts/fixtures/dashboard.html.haml delete mode 100644 spec/javascripts/project_dashboard_spec.js.es6 diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb index c5a0d1c6d99..62a2c54c94c 100644 --- a/spec/features/dashboard/shortcuts_spec.rb +++ b/spec/features/dashboard/shortcuts_spec.rb @@ -10,21 +10,20 @@ feature 'Dashboard shortcuts', feature: true, js: true do find('body').native.send_key('g') find('body').native.send_key('p') - ensure_active_main_tab('Projects') + check_page_title('Projects') find('body').native.send_key('g') find('body').native.send_key('i') - ensure_active_main_tab('Issues') + check_page_title('Issues') find('body').native.send_key('g') find('body').native.send_key('m') - ensure_active_main_tab('Merge Requests') + check_page_title('Merge Requests') end - def ensure_active_main_tab(content) - find('.global-dropdown-toggle').trigger('click') - expect(find('.global-dropdown-menu li.active')).to have_content(content) + def check_page_title(title) + expect(find('.header-content .title')).to have_content(title) end end diff --git a/spec/javascripts/dashboard_spec.js.es6 b/spec/javascripts/dashboard_spec.js.es6 deleted file mode 100644 index c0bdb89ed63..00000000000 --- a/spec/javascripts/dashboard_spec.js.es6 +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable no-new */ - -require('~/sidebar'); -require('~/lib/utils/text_utility'); - -((global) => { - describe('Dashboard', () => { - const fixtureTemplate = 'static/dashboard.html.raw'; - - function todosCountText() { - return $('.js-todos-count').text(); - } - - function triggerToggle(newCount) { - $(document).trigger('todo:toggle', newCount); - } - - preloadFixtures(fixtureTemplate); - beforeEach(() => { - loadFixtures(fixtureTemplate); - new global.Sidebar(); - }); - - it('should update todos-count after receiving the todo:toggle event', () => { - triggerToggle(5); - expect(todosCountText()).toEqual('5'); - }); - - it('should display todos-count with delimiter', () => { - triggerToggle(1000); - expect(todosCountText()).toEqual('1,000'); - - triggerToggle(1000000); - expect(todosCountText()).toEqual('1,000,000'); - }); - }); -})(window.gl); diff --git a/spec/javascripts/fixtures/dashboard.html.haml b/spec/javascripts/fixtures/dashboard.html.haml deleted file mode 100644 index 32446acfd60..00000000000 --- a/spec/javascripts/fixtures/dashboard.html.haml +++ /dev/null @@ -1,45 +0,0 @@ -%ul.nav.nav-sidebar - %li.home.active - %a.dashboard-shortcuts-projects - %span - Projects - %li - %a - %span - Todos - %span.count.js-todos-count - 1 - %li - %a.dashboard-shortcuts-activity - %span - Activity - %li - %a - %span - Groups - %li - %a - %span - Milestones - %li - %a.dashboard-shortcuts-issues - %span - Issues - %span - 1 - %li - %a.dashboard-shortcuts-merge_requests - %span - Merge Requests - %li - %a - %span - Snippets - %li - %a - %span - Help - %li - %a - %span - Profile Settings diff --git a/spec/javascripts/project_dashboard_spec.js.es6 b/spec/javascripts/project_dashboard_spec.js.es6 deleted file mode 100644 index 24833b4eb57..00000000000 --- a/spec/javascripts/project_dashboard_spec.js.es6 +++ /dev/null @@ -1,86 +0,0 @@ -require('~/sidebar'); - -(() => { - describe('Project dashboard page', () => { - let $pageWithSidebar = null; - let $sidebarToggle = null; - let sidebar = null; - const fixtureTemplate = 'projects/dashboard.html.raw'; - - const assertSidebarStateExpanded = (shouldBeExpanded) => { - expect(sidebar.isExpanded).toBe(shouldBeExpanded); - expect($pageWithSidebar.hasClass('page-sidebar-expanded')).toBe(shouldBeExpanded); - }; - - preloadFixtures(fixtureTemplate); - beforeEach(() => { - loadFixtures(fixtureTemplate); - - $pageWithSidebar = $('.page-with-sidebar'); - $sidebarToggle = $('.toggle-nav-collapse'); - - // otherwise instantiating the Sidebar for the second time - // won't do anything, as the Sidebar is a singleton class - gl.Sidebar.singleton = null; - sidebar = new gl.Sidebar(); - }); - - it('can show the sidebar when the toggler is clicked', () => { - assertSidebarStateExpanded(false); - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - }); - - it('should dismiss the sidebar when clone button clicked', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - const cloneButton = $('.project-clone-holder a.clone-dropdown-btn'); - cloneButton.click(); - assertSidebarStateExpanded(false); - }); - - it('should dismiss the sidebar when download button clicked', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - const downloadButton = $('.project-action-button .btn:has(i.fa-download)'); - downloadButton.click(); - assertSidebarStateExpanded(false); - }); - - it('should dismiss the sidebar when add button clicked', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - const addButton = $('.project-action-button .btn:has(i.fa-plus)'); - addButton.click(); - assertSidebarStateExpanded(false); - }); - - it('should dismiss the sidebar when notification button clicked', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - const notifButton = $('.js-notification-toggle-btns .notifications-btn'); - notifButton.click(); - assertSidebarStateExpanded(false); - }); - - it('should dismiss the sidebar when clicking on the body', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - $('body').click(); - assertSidebarStateExpanded(false); - }); - - it('should dismiss the sidebar when clicking on the project description header', () => { - $sidebarToggle.click(); - assertSidebarStateExpanded(true); - - $('.project-home-panel').click(); - assertSidebarStateExpanded(false); - }); - }); -})(); -- cgit v1.2.1 From ee63415c741b7c5f061f79212c74564b2ddb01a6 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Gray Date: Wed, 15 Feb 2017 13:17:14 -0600 Subject: Delete labels spinach test bc covered by rspec --- features/project/labels.feature | 15 --------------- features/steps/project/labels.rb | 32 -------------------------------- 2 files changed, 47 deletions(-) delete mode 100644 features/project/labels.feature delete mode 100644 features/steps/project/labels.rb diff --git a/features/project/labels.feature b/features/project/labels.feature deleted file mode 100644 index 955bc3d8b1b..00000000000 --- a/features/project/labels.feature +++ /dev/null @@ -1,15 +0,0 @@ -@labels -Feature: Labels - Background: - Given I sign in as a user - And I own project "Shop" - And project "Shop" has labels: "bug", "feature", "enhancement" - When I visit project "Shop" labels page - - @javascript - Scenario: I can subscribe to a label - Then I should see that I am not subscribed to the "bug" label - When I click button "Subscribe" for the "bug" label - Then I should see that I am subscribed to the "bug" label - When I click button "Unsubscribe" for the "bug" label - Then I should see that I am not subscribed to the "bug" label diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb deleted file mode 100644 index dbeb07c78db..00000000000 --- a/features/steps/project/labels.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Spinach::Features::Labels < Spinach::FeatureSteps - include SharedAuthentication - include SharedIssuable - include SharedProject - include SharedPaths - - step 'And I visit project "Shop" labels page' do - visit namespace_project_labels_path(project.namespace, project) - end - - step 'I should see that I am subscribed to the "bug" label' do - expect(subscribe_button).to have_content 'Unsubscribe' - end - - step 'I should see that I am not subscribed to the "bug" label' do - expect(subscribe_button).to have_content 'Subscribe' - end - - step 'I click button "Unsubscribe" for the "bug" label' do - subscribe_button.click - end - - step 'I click button "Subscribe" for the "bug" label' do - subscribe_button.click - end - - private - - def subscribe_button - first('.js-subscribe-button', visible: true) - end -end -- cgit v1.2.1 From 7d0bd32ec75615ba75f1f1e15dc229064c4a0ee2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 15 Feb 2017 20:32:18 +0100 Subject: Remove legacy GitlabCi module from initializers --- config/initializers/4_ci_app.rb | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 config/initializers/4_ci_app.rb diff --git a/config/initializers/4_ci_app.rb b/config/initializers/4_ci_app.rb deleted file mode 100644 index d252e403102..00000000000 --- a/config/initializers/4_ci_app.rb +++ /dev/null @@ -1,8 +0,0 @@ -module GitlabCi - VERSION = Gitlab::VERSION - REVISION = Gitlab::REVISION - - def self.config - Settings - end -end -- cgit v1.2.1 From 679ce9dbb35021bec3bc048948e471ba3989e518 Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Thu, 16 Feb 2017 06:55:52 +1100 Subject: dev favicon is blue, not purple --- app/assets/images/favicon-blue.ico | Bin 0 -> 5430 bytes app/assets/images/favicon-purple.ico | Bin 5430 -> 0 bytes app/helpers/page_layout_helper.rb | 2 +- spec/helpers/page_layout_helper_spec.rb | 4 ++-- 4 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 app/assets/images/favicon-blue.ico delete mode 100644 app/assets/images/favicon-purple.ico diff --git a/app/assets/images/favicon-blue.ico b/app/assets/images/favicon-blue.ico new file mode 100644 index 00000000000..71acdf670ab Binary files /dev/null and b/app/assets/images/favicon-blue.ico differ diff --git a/app/assets/images/favicon-purple.ico b/app/assets/images/favicon-purple.ico deleted file mode 100644 index 71acdf670ab..00000000000 Binary files a/app/assets/images/favicon-purple.ico and /dev/null differ diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index aee8099aeb2..3286a92a8a7 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -35,7 +35,7 @@ module PageLayoutHelper end def favicon - Rails.env.development? ? 'favicon-purple.ico' : 'favicon.ico' + Rails.env.development? ? 'favicon-blue.ico' : 'favicon.ico' end def page_image diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb index 872679a6ce3..2cc0b40b2d0 100644 --- a/spec/helpers/page_layout_helper_spec.rb +++ b/spec/helpers/page_layout_helper_spec.rb @@ -46,9 +46,9 @@ describe PageLayoutHelper do expect(helper.favicon).to eq 'favicon.ico' end - it 'has purple favicon for development' do + it 'has blue favicon for development' do allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development')) - expect(helper.favicon).to eq 'favicon-purple.ico' + expect(helper.favicon).to eq 'favicon-blue.ico' end end -- cgit v1.2.1 From 7af6982e5fcf2b76906f33d5046dd9b2298ac32c Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 14:40:11 +0000 Subject: Extracts table into a reusable component --- .../environments/components/environment.js.es6 | 37 ++++------- .../components/environments_table.js.es6 | 74 ++++++++++++++++++++++ .../folder/environments_folder_bundle.js.es6 | 0 .../folder/environments_folder_view.js.es6 | 0 app/views/projects/environments/folder.html.haml | 0 5 files changed, 85 insertions(+), 26 deletions(-) create mode 100644 app/assets/javascripts/environments/components/environments_table.js.es6 create mode 100644 app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 create mode 100644 app/assets/javascripts/environments/folder/environments_folder_view.js.es6 create mode 100644 app/views/projects/environments/folder.html.haml diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 6d9599e7645..42f74e114c9 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -4,14 +4,14 @@ const Vue = require('vue'); Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -const EnvironmentItem = require('./environment_item'); +const EnvironmentTable = require('./environments_table'); const Store = require('../stores/environments_store'); require('../../vue_shared/components/table_pagination'); module.exports = Vue.component('environment-component', { components: { - 'environment-item': EnvironmentItem, + 'environment-table': EnvironmentTable, 'table-pagination': gl.VueGlPagination, }, @@ -209,30 +209,15 @@ module.exports = Vue.component('environment-component', {
    - - - - - - - - - - - - - - -
    EnvironmentLast deploymentJobCommitUpdated
    + + + ([]), + }, + + canReadEnvironment: { + type: Boolean, + required: false, + default: false, + }, + + canCreateDeployment: { + type: Boolean, + required: false, + default: false, + }, + + commitIconSvg: { + type: String, + required: false, + }, + + playIconSvg: { + type: String, + required: false, + }, + + terminalIconSvg: { + type: String, + required: false, + }, + }, + + template: ` + + + + + + + + + + + + + + +
    EnvironmentLast deploymentJobCommitUpdated
    + `, +}); diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.1 From 26d18387dea786ded85df3a429542c5d1e4f1ac1 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 14:55:18 +0000 Subject: First iteration --- .../folder/environments_folder_bundle.js.es6 | 14 ++ .../folder/environments_folder_view.js.es6 | 196 +++++++++++++++++++++ .../stores/environments_folder_store.js.es6 | 49 ++++++ app/views/projects/environments/folder.html.haml | 8 + config/webpack.config.js | 1 + 5 files changed, 268 insertions(+) create mode 100644 app/assets/javascripts/environments/stores/environments_folder_store.js.es6 diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 index e69de29bb2d..9cc1c2f4f18 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 @@ -0,0 +1,14 @@ +const EnvironmentsFolderComponent = require('./environments_folder_view'); +require('../vue_shared/vue_resource_interceptor'); + +$(() => { + window.gl = window.gl || {}; + + if (gl.EnvironmentsListFolderApp) { + gl.EnvironmentsListFolderApp.$destroy(true); + } + + gl.EnvironmentsListFolderApp = new EnvironmentsFolderComponent({ + el: document.querySelector('#environments-folder-list-view'), + }); +}); diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 index e69de29bb2d..070bc84bbe3 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 @@ -0,0 +1,196 @@ +/* eslint-disable no-param-reassign, no-new */ +/* global Flash */ + +const Vue = require('vue'); +Vue.use(require('vue-resource')); +const EnvironmentsService = require('../services/environments_service'); +const EnvironmentTable = require('./environments_table'); +const Store = require('../stores/environments_folder_store'); +require('../../vue_shared/components/table_pagination'); + +module.exports = Vue.component('environment-folder-view', { + + components: { + 'environment-table': EnvironmentTable, + 'table-pagination': gl.VueGlPagination, + }, + + props: { + endpoint: { + type: String, + required: true, + default: '', + }, + + folderName: { + type: String, + required: true, + default: '', + }, + }, + + data() { + const store = new Store(); + + return { + store, + state: store.state, + isLoading: false, + + // Pagination Properties, + paginationInformation: {}, + pageNumber: 1, + }; + }, + + /** + * Fetches all the environments and stores them. + * Toggles loading property. + */ + created() { + const scope = this.$options.getQueryParameter('scope') || this.visibility; + const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + + const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; + + const service = new EnvironmentsService(endpoint); + + this.isLoading = true; + + return service.all() + .then(resp => ({ + headers: resp.headers, + body: resp.json(), + })) + .then((response) => { + this.store.storeEnvironments(response.body.environments); + this.store.storePagination(response.headers); + }) + .then(() => { + this.isLoading = false; + }) + .catch(() => { + this.isLoading = false; + new Flash('An error occurred while fetching the environments.', 'alert'); + }); + }, + + /** + * Transforms the url parameter into an object and + * returns the one requested. + * + * @param {String} param + * @returns {String} The value of the requested parameter. + */ + getQueryParameter(parameter) { + return window.location.search.substring(1).split('&').reduce((acc, param) => { + const paramSplited = param.split('='); + acc[paramSplited[0]] = paramSplited[1]; + return acc; + }, {})[parameter]; + }, + + methods: { + /** + * Will change the page number and update the URL. + * + * If no search params are present, we'll add param for page + * If param for page is already present, we'll update it + * If there are params but none for page, we'll add it at the end. + * + * @param {Number} pageNumber desired page to go to. + */ + changePage(pageNumber) { + let param; + if (window.location.search.length === 0) { + param = `?page=${pageNumber}`; + } + + if (window.location.search.indexOf('page') !== -1) { + param = window.location.search.replace(/page=\d/g, `page=${pageNumber}`); + } + + if (window.location.search.length && + window.location.search.indexOf('page') === -1) { + param = `${window.location.search}&page=${pageNumber}`; + } + + gl.utils.visitUrl(param); + return param; + }, + }, + + template: ` +
    + + +
    +
    + +
    + +
    +

    + You don't have any environments right now. +

    +

    + Environments are places where code gets deployed, such as staging or production. +
    + + Read more about environments + +

    + + + New Environment + +
    + +
    + + + + + + +
    +
    +
    + `, +}); diff --git a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 new file mode 100644 index 00000000000..005ed52d9a1 --- /dev/null +++ b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 @@ -0,0 +1,49 @@ +require('~/lib/utils/common_utils'); +/** + * Environments Folder Store. + * + * Stores received environments that belong to a parent store. + */ +class EnvironmentsFolderStore { + constructor() { + this.state = {}; + this.state.environments = []; + this.state.paginationInformation = {}; + + return this; + } + + /** + * + * Stores the received environments. + * + * Each environment has the following schema + * { name: String, size: Number, latest: Object } + * + * + * @param {Array} environments + * @returns {Array} + */ + storeEnvironments(environments = []) { + this.state.environments = environments; + + return environments; + } + + storePagination(pagination = {}) { + const normalizedHeaders = gl.utils.normalizeHeaders(pagination); + const paginationInformation = { + perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), + page: parseInt(normalizedHeaders['X-PAGE'], 10), + total: parseInt(normalizedHeaders['X-TOTAL'], 10), + totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), + nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), + previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), + }; + + this.state.paginationInformation = paginationInformation; + return paginationInformation; + } +} + +module.exports = EnvironmentsFolderStore; diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index e69de29bb2d..452d32fc8b6 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -0,0 +1,8 @@ +- @no_container = true +- page_title "Environments" += render "projects/pipelines/head" + +- content_for :page_specific_javascripts do + = page_specific_javascript_bundle_tag("environments_folder") + +#environments-folder-list-view diff --git a/config/webpack.config.js b/config/webpack.config.js index 00f448c1fbb..7fda5405ea2 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -22,6 +22,7 @@ var config = { commit_pipelines: './commit/pipelines/pipelines_bundle.js', diff_notes: './diff_notes/diff_notes_bundle.js', environments: './environments/environments_bundle.js', + environments_folder: './environments/folder/environments_folder_bundle.js', filtered_search: './filtered_search/filtered_search_bundle.js', graphs: './graphs/graphs_bundle.js', issuable: './issuable/issuable_bundle.js', -- cgit v1.2.1 From 082348491360d51ffaa737e3f266c4d1d6bdd946 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Sun, 12 Feb 2017 16:58:53 +0000 Subject: Adds url for folder; Creates new subview to show envirnoments that belong to a folder --- .../components/environment_item.js.es6 | 12 ++- .../folder/environments_folder_bundle.js.es6 | 1 - .../folder/environments_folder_view.js.es6 | 86 +++++++++++----------- .../stores/environments_folder_store.js.es6 | 49 ------------ app/views/projects/environments/folder.html.haml | 7 +- 5 files changed, 59 insertions(+), 96 deletions(-) delete mode 100644 app/assets/javascripts/environments/stores/environments_folder_store.js.es6 diff --git a/app/assets/javascripts/environments/components/environment_item.js.es6 b/app/assets/javascripts/environments/components/environment_item.js.es6 index fc45c3c5f53..e40c97130ad 100644 --- a/app/assets/javascripts/environments/components/environment_item.js.es6 +++ b/app/assets/javascripts/environments/components/environment_item.js.es6 @@ -407,6 +407,16 @@ module.exports = Vue.component('environment-item', { return ''; }, + + /** + * Constructs folder URL based on the current location and the folder id. + * + * @return {String} + */ + folderUrl() { + return `${window.location.pathname}/folders/${this.model.latest.id}`; + }, + }, /** @@ -432,7 +442,7 @@ module.exports = Vue.component('environment-item', { :href="environmentPath"> {{model.name}} - + diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 index 9cc1c2f4f18..d2ca465351a 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js.es6 @@ -1,5 +1,4 @@ const EnvironmentsFolderComponent = require('./environments_folder_view'); -require('../vue_shared/vue_resource_interceptor'); $(() => { window.gl = window.gl || {}; diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 index 070bc84bbe3..83643161056 100644 --- a/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 +++ b/app/assets/javascripts/environments/folder/environments_folder_view.js.es6 @@ -4,9 +4,8 @@ const Vue = require('vue'); Vue.use(require('vue-resource')); const EnvironmentsService = require('../services/environments_service'); -const EnvironmentTable = require('./environments_table'); -const Store = require('../stores/environments_folder_store'); -require('../../vue_shared/components/table_pagination'); +const EnvironmentTable = require('../components/environments_table'); +const Store = require('../stores/environments_store'); module.exports = Vue.component('environment-folder-view', { @@ -15,27 +14,25 @@ module.exports = Vue.component('environment-folder-view', { 'table-pagination': gl.VueGlPagination, }, - props: { - endpoint: { - type: String, - required: true, - default: '', - }, - - folderName: { - type: String, - required: true, - default: '', - }, - }, - data() { + const environmentsData = document.querySelector('#environments-folder-list-view').dataset; const store = new Store(); + const endpoint = `${window.location.pathname}.json`; return { store, + endpoint, state: store.state, + visibility: 'available', isLoading: false, + cssContainerClass: environmentsData.cssClass, + canCreateDeployment: environmentsData.canCreateDeployment, + canReadEnvironment: environmentsData.canReadEnvironment, + + // svgs + commitIconSvg: environmentsData.commitIconSvg, + playIconSvg: environmentsData.playIconSvg, + terminalIconSvg: environmentsData.terminalIconSvg, // Pagination Properties, paginationInformation: {}, @@ -43,6 +40,29 @@ module.exports = Vue.component('environment-folder-view', { }; }, + computed: { + scope() { + return this.$options.getQueryParameter('scope'); + }, + + canReadEnvironmentParsed() { + return this.$options.convertPermissionToBoolean(this.canReadEnvironment); + }, + + canCreateDeploymentParsed() { + return this.$options.convertPermissionToBoolean(this.canCreateDeployment); + }, + + stoppedPath() { + return `${window.location.pathname}?scope=stopped`; + }, + + availablePath() { + return window.location.pathname; + }, + + }, + /** * Fetches all the environments and stores them. * Toggles loading property. @@ -123,9 +143,12 @@ module.exports = Vue.component('environment-folder-view', { template: `
    @@ -153,26 +171,6 @@ module.exports = Vue.component('environment-folder-view', {
    -
    -

    - You don't have any environments right now. -

    -

    - Environments are places where code gets deployed, such as staging or production. -
    - - Read more about environments - -

    - - - New Environment - -
    -
    diff --git a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 b/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 deleted file mode 100644 index 005ed52d9a1..00000000000 --- a/app/assets/javascripts/environments/stores/environments_folder_store.js.es6 +++ /dev/null @@ -1,49 +0,0 @@ -require('~/lib/utils/common_utils'); -/** - * Environments Folder Store. - * - * Stores received environments that belong to a parent store. - */ -class EnvironmentsFolderStore { - constructor() { - this.state = {}; - this.state.environments = []; - this.state.paginationInformation = {}; - - return this; - } - - /** - * - * Stores the received environments. - * - * Each environment has the following schema - * { name: String, size: Number, latest: Object } - * - * - * @param {Array} environments - * @returns {Array} - */ - storeEnvironments(environments = []) { - this.state.environments = environments; - - return environments; - } - - storePagination(pagination = {}) { - const normalizedHeaders = gl.utils.normalizeHeaders(pagination); - const paginationInformation = { - perPage: parseInt(normalizedHeaders['X-PER-PAGE'], 10), - page: parseInt(normalizedHeaders['X-PAGE'], 10), - total: parseInt(normalizedHeaders['X-TOTAL'], 10), - totalPages: parseInt(normalizedHeaders['X-TOTAL-PAGES'], 10), - nextPage: parseInt(normalizedHeaders['X-NEXT-PAGE'], 10), - previousPage: parseInt(normalizedHeaders['X-PREV-PAGE'], 10), - }; - - this.state.paginationInformation = paginationInformation; - return paginationInformation; - } -} - -module.exports = EnvironmentsFolderStore; diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml index 452d32fc8b6..d9cb7bc0331 100644 --- a/app/views/projects/environments/folder.html.haml +++ b/app/views/projects/environments/folder.html.haml @@ -5,4 +5,9 @@ - content_for :page_specific_javascripts do = page_specific_javascript_bundle_tag("environments_folder") -#environments-folder-list-view +#environments-folder-list-view{ data: { "can-create-deployment" => can?(current_user, :create_deployment, @project).to_s, + "can-read-environment" => can?(current_user, :read_environment, @project).to_s, + "css-class" => container_class, + "commit-icon-svg" => custom_icon("icon_commit"), + "terminal-icon-svg" => custom_icon("icon_terminal"), + "play-icon-svg" => custom_icon("icon_play") } } -- cgit v1.2.1 From 17897c37f806e593359239ba09667081b88cb24b Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 13 Feb 2017 14:41:50 +0000 Subject: Fix underline style --- app/assets/stylesheets/pages/environments.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss index 606cf501b82..2f4a3c80aeb 100644 --- a/app/assets/stylesheets/pages/environments.scss +++ b/app/assets/stylesheets/pages/environments.scss @@ -122,6 +122,7 @@ .folder-name { cursor: pointer; color: $gl-text-color-secondary; + display: inline-block; } } -- cgit v1.2.1 From 73accafe430f56cd3065774c6118de3db0a45734 Mon Sep 17 00:00:00 2001 From: Filipa Lacerda Date: Mon, 13 Feb 2017 14:49:19 +0000 Subject: Use common util to get parameter name --- .../environments/components/environment.js.es6 | 24 +++++----------------- .../folder/environments_folder_view.js.es6 | 7 ++++--- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6 index 42f74e114c9..2cbfbcad023 100644 --- a/app/assets/javascripts/environments/components/environment.js.es6 +++ b/app/assets/javascripts/environments/components/environment.js.es6 @@ -7,6 +7,7 @@ const EnvironmentsService = require('../services/environments_service'); const EnvironmentTable = require('./environments_table'); const Store = require('../stores/environments_store'); require('../../vue_shared/components/table_pagination'); +require('../../lib/utils/common_utils'); module.exports = Vue.component('environment-component', { @@ -45,7 +46,7 @@ module.exports = Vue.component('environment-component', { computed: { scope() { - return this.$options.getQueryParameter('scope'); + return gl.utils.getParameterByName('scope'); }, canReadEnvironmentParsed() { @@ -67,8 +68,8 @@ module.exports = Vue.component('environment-component', { * Toggles loading property. */ created() { - const scope = this.$options.getQueryParameter('scope') || this.visibility; - const pageNumber = this.$options.getQueryParameter('page') || this.pageNumber; + const scope = gl.utils.getParameterByName('scope') || this.visibility; + const pageNumber = gl.utils.getParameterByName('page') || this.pageNumber; const endpoint = `${this.endpoint}?scope=${scope}&page=${pageNumber}`; @@ -96,21 +97,6 @@ module.exports = Vue.component('environment-component', { }); }, - /** - * Transforms the url parameter into an object and - * returns the one requested. - * - * @param {String} param - * @returns {String} The value of the requested parameter. - */ - getQueryParameter(parameter) { - return window.location.search.substring(1).split('&').reduce((acc, param) => { - const paramSplited = param.split('='); - acc[paramSplited[0]] = paramSplited[1]; - return acc; - }, {})[parameter]; - }, - /** * Converts permission provided as strings to booleans. * @param {String} string @@ -158,7 +144,7 @@ module.exports = Vue.component('environment-component', {