Commit 25fb4798 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2018-03-06' into 'master'

CE upstream - 2018-03-06 15:24 UTC

Closes gitaly#1056, gitaly#1059, and gitlab-com/gitlab-docs#154

See merge request gitlab-org/gitlab-ee!4858
parents 2ed6b0cd f61c6cdd
......@@ -152,14 +152,14 @@ export default {
showLeaveGroupModal(group, parentGroup) {
this.targetGroup = group;
this.targetParentGroup = parentGroup;
this.updateModal = true;
this.showModal = true;
this.groupLeaveConfirmationMessage = s__(`GroupsTree|Are you sure you want to leave the "${group.fullName}" group?`);
},
hideLeaveGroupModal() {
this.updateModal = false;
this.showModal = false;
},
leaveGroup() {
this.updateModal = false;
this.showModal = false;
this.targetGroup.isBeingRemoved = true;
this.service.leaveGroup(this.targetGroup.leavePath)
.then(res => res.json())
......@@ -208,9 +208,9 @@ export default {
:page-info="pageInfo"
/>
<modal
v-show="showModal"
:primary-button-label="__('Leave')"
v-if="showModal"
kind="warning"
:primary-button-label="__('Leave')"
:title="__('Are you sure?')"
:text="groupLeaveConfirmationMessage"
@cancel="hideLeaveGroupModal"
......
......@@ -7,13 +7,19 @@ class Projects::BranchesController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
def index
@sort = params[:sort].presence || sort_value_recently_updated
@branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
@branches = Kaminari.paginate_array(@branches).page(params[:page])
# Support legacy URLs
before_action :redirect_for_legacy_index_sort_or_search, only: [:index]
def index
respond_to do |format|
format.html do
@sort = params[:sort].presence || sort_value_recently_updated
@mode = params[:state].presence || 'overview'
@overview_max_branches = 5
# Fetch branches for the specified mode
fetch_branches_by_mode
@refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names =
repository.merged_branch_names(@branches.map(&:name))
......@@ -28,7 +34,9 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
format.json do
render json: @branches.map(&:name)
branches = BranchesFinder.new(@repository, params).execute
branches = Kaminari.paginate_array(branches).page(params[:page])
render json: branches.map(&:name)
end
end
end
......@@ -123,4 +131,27 @@ class Projects::BranchesController < Projects::ApplicationController
context: 'autodeploy'
)
end
def redirect_for_legacy_index_sort_or_search
# Normalize a legacy URL with redirect
if request.format != :json && !params[:state].presence && [:sort, :search, :page].any? { |key| params[key].presence }
redirect_to project_branches_filtered_path(@project, state: 'all'), notice: 'Update your bookmarked URLs as filtered/sorted branches URL has been changed.'
end
end
def fetch_branches_by_mode
if @mode == 'overview'
# overview mode
@active_branches, @stale_branches = BranchesFinder.new(@repository, sort: sort_value_recently_updated).execute.partition(&:active?)
# Here we get one more branch to indicate if there are more data we're not showing
@active_branches = @active_branches.first(@overview_max_branches + 1)
@stale_branches = @stale_branches.first(@overview_max_branches + 1)
@branches = @active_branches + @stale_branches
else
# active/stale/all view mode
@branches = BranchesFinder.new(@repository, params.merge(sort: @sort)).execute
@branches = @branches.select { |b| b.state.to_s == @mode } if %w[active stale].include?(@mode)
@branches = Kaminari.paginate_array(@branches).page(params[:page])
end
end
end
......@@ -9,25 +9,22 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :assign_commit
def show
# n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37602
Gitlab::GitalyClient.allow_n_plus_1_calls do
@url = project_network_path(@project, @ref, @options.merge(format: :json))
@commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
respond_to do |format|
format.html do
if @options[:extended_sha1] && !@commit
flash.now[:alert] = "Git revision '#{@options[:extended_sha1]}' does not exist."
end
end
@url = project_network_path(@project, @ref, @options.merge(format: :json))
@commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
format.json do
@graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref])
respond_to do |format|
format.html do
if @options[:extended_sha1] && !@commit
flash.now[:alert] = "Git revision '#{@options[:extended_sha1]}' does not exist."
end
end
render
format.json do
@graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref])
end
end
render
end
def assign_commit
......
class BranchesFinder
def initialize(repository, params)
def initialize(repository, params = {})
@repository = repository
@params = params
end
......
......@@ -150,9 +150,7 @@ class TodosFinder
if project?
items.where(project: project)
else
projects = Project
.public_or_visible_to_user(current_user)
.order_id_desc
projects = Project.public_or_visible_to_user(current_user)
items.joins(:project).merge(projects)
end
......
module BranchesHelper
prepend EE::BranchesHelper
def filter_branches_path(options = {})
exist_opts = {
search: params[:search],
sort: params[:sort]
}
options = exist_opts.merge(options)
project_branches_path(@project, @id, options)
end
def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch)
end
......
......@@ -56,12 +56,13 @@ module Clusters
def specification
{
"gitlabUrl" => gitlab_url,
"runnerToken" => ensure_runner.token
"runnerToken" => ensure_runner.token,
"runners" => { "privileged" => privileged }
}
end
def content_values
specification.merge(YAML.load_file(chart_values_file))
YAML.load_file(chart_values_file).deep_merge!(specification)
end
end
end
......
......@@ -24,12 +24,7 @@ module Network
end
def parents(map)
@commit.parents.map do |p|
if map.include?(p.id)
map[p.id]
end
end
.compact
map.values_at(*@commit.parent_ids).compact
end
end
end
......@@ -32,8 +32,6 @@ class Todo < ActiveRecord::Base
validates :target_id, presence: true, unless: :for_commit?
validates :commit_id, presence: true, if: :for_commit?
default_scope { reorder(id: :desc) }
scope :pending, -> { with_state(:pending) }
scope :done, -> { with_state(:done) }
......@@ -53,10 +51,14 @@ class Todo < ActiveRecord::Base
# milestones, but still show something if the user has a URL with that
# selected.
def sort(method)
case method.to_s
when 'priority', 'label_priority' then order_by_labels_priority
else order_by(method)
end
sorted =
case method.to_s
when 'priority', 'label_priority' then order_by_labels_priority
else order_by(method)
end
# Break ties with the ID column for pagination
sorted.order(id: :desc)
end
# Order by priority depending on which issue/merge request the Todo belongs to
......
- branches = local_assigns.fetch(:branches)
- state = local_assigns.fetch(:state)
- panel_title = local_assigns.fetch(:panel_title)
- show_more_text = local_assigns.fetch(:show_more_text)
- project = local_assigns.fetch(:project)
- overview_max_branches = local_assigns.fetch(:overview_max_branches)
- return unless branches.any?
.panel.panel-default.prepend-top-10
.panel-heading
%h4.panel-title
= panel_title
%ul.content-list.all-branches
- branches.first(overview_max_branches).each do |branch|
= render "projects/branches/branch", branch: branch, merged: project.repository.merged_to_root_ref?(branch)
- if branches.size > overview_max_branches
.panel-footer.text-center
= link_to show_more_text, project_branches_filtered_path(project, state: state), id: "state-#{state}", data: { state: state }
......@@ -4,26 +4,35 @@
%div{ class: container_class }
.top-area.adjust
- if can?(current_user, :admin_project, @project)
.nav-text
- project_settings_link = link_to s_('Branches|project settings'), project_protected_branches_path(@project)
= s_('Branches|Protected branches can be managed in %{project_settings_link}').html_safe % { project_settings_link: project_settings_link }
%ul.nav-links.issues-state-filters
%li{ class: active_when(@mode == 'overview') }>
= link_to s_('Branches|Overview'), project_branches_path(@project), title: s_('Branches|Show overview of the branches')
%li{ class: active_when(@mode == 'active') }>
= link_to s_('Branches|Active'), project_branches_filtered_path(@project, state: 'active'), title: s_('Branches|Show active branches')
%li{ class: active_when(@mode == 'stale') }>
= link_to s_('Branches|Stale'), project_branches_filtered_path(@project, state: 'stale'), title: s_('Branches|Show stale branches')
%li{ class: active_when(!%w[overview active stale].include?(@mode)) }>
= link_to s_('Branches|All'), project_branches_filtered_path(@project, state: 'all'), title: s_('Branches|Show all branches')
.nav-controls
= form_tag(filter_branches_path, method: :get) do
= form_tag(project_branches_filtered_path(@project, state: 'all'), method: :get) do
= search_field_tag :search, params[:search], { placeholder: s_('Branches|Filter by branch name'), id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline>
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
= branches_sort_options_hash[@sort]
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
= s_('Branches|Sort by')
- branches_sort_options_hash.each do |value, title|
%li
= link_to title, filter_branches_path(sort: value), class: ("is-active" if @sort == value)
- unless @mode == 'overview'
.dropdown.inline>
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light
= branches_sort_options_hash[@sort]
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-selectable
%li.dropdown-header
= s_('Branches|Sort by')
- branches_sort_options_hash.each do |value, title|
%li
= link_to title, project_branches_filtered_path(@project, state: 'all', search: params[:search], sort: value), class: ("is-active" if @sort == value)
- if can? current_user, :push_code, @project
= link_to project_merged_branches_path(@project),
......@@ -38,7 +47,17 @@
= render 'projects/commits/mirror_status'
- if @branches.any?
- if can?(current_user, :admin_project, @project)
- project_settings_link = link_to s_('Branches|project settings'), project_protected_branches_path(@project)
.row-content-block
%h5
= s_('Branches|Protected branches can be managed in %{project_settings_link}.').html_safe % { project_settings_link: project_settings_link }
- if @mode == 'overview' && (@active_branches.any? || @stale_branches.any?)
= render "projects/branches/panel", branches: @active_branches, state: 'active', panel_title: s_('Branches|Active branches'), show_more_text: s_('Branches|Show more active branches'), project: @project, overview_max_branches: @overview_max_branches
= render "projects/branches/panel", branches: @stale_branches, state: 'stale', panel_title: s_('Branches|Stale branches'), show_more_text: s_('Branches|Show more stale branches'), project: @project, overview_max_branches: @overview_max_branches
- elsif @branches.any?
%ul.content-list.all-branches
- @branches.each do |branch|
= render "projects/branches/branch", branch: branch, merged: @merged_branch_names.include?(branch.name)
......
---
title: Add overview of branches and a filter for active/stale branches
merge_request: 15402
author: Takuya Noguchi
type: added
---
title: Enable privileged mode for GitLab Runner
merge_request: 17528
author:
type: added
---
title: Prevent the graphs page from generating unnecessary Gitaly requests
merge_request: 37602
author:
type: performance
---
title: Store sha256 checksum to job artifacts
merge_request: 17354
author:
type: performance
......@@ -49,6 +49,7 @@ scope format: false do
end
end
get '/branches/:state', to: 'branches#index', as: :branches_filtered, constraints: { state: /active|stale|all/ }
resources :branches, only: [:index, :new, :create, :destroy]
delete :merged_branches, controller: 'branches', action: :destroy_all_merged
resources :tags, only: [:index, :show, :new, :create, :destroy] do
......
class AddChecksumToCiJobArtifacts < ActiveRecord::Migration
DOWNTIME = false
def change
add_column :ci_job_artifacts, :file_sha256, :binary
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPrivilegedToRunner < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default :clusters_applications_runners, :privileged, :boolean, default: true, allow_null: false
end
def down
remove_column :clusters_applications_runners, :privileged
end
end
......@@ -427,6 +427,7 @@ ActiveRecord::Schema.define(version: 20180306074045) do
t.datetime_with_timezone "expire_at"
t.string "file"
t.integer "file_store"
t.binary "file_sha256"
end
add_index "ci_job_artifacts", ["expire_at", "job_id"], name: "index_ci_job_artifacts_on_expire_at_and_job_id", using: :btree
......@@ -698,6 +699,7 @@ ActiveRecord::Schema.define(version: 20180306074045) do
t.datetime_with_timezone "updated_at", null: false
t.string "version", null: false
t.text "status_reason"
t.boolean "privileged", default: true, null: false
end
add_index "clusters_applications_runners", ["cluster_id"], name: "index_clusters_applications_runners_on_cluster_id", unique: true, using: :btree
......
......@@ -48,3 +48,9 @@ Here's a few links to get you started:
- [git-p4 manual page](https://www.kernel.org/pub/software/scm/git/docs/git-p4.html)
- [git-p4 example usage](https://git.wiki.kernel.org/index.php/Git-p4_Usage)
- [Git book migration guide](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git#_perforce_import)
Note that `git p4` and `git filter-branch` are not very good at
creating small and efficient Git pack files. So it might be a good
idea to spend time and CPU to properly repack your repository before
sending it for the first time to your GitLab server. See
[this StackOverflow question](https://stackoverflow.com/questions/28720151/git-gc-aggressive-vs-git-repack/).
......@@ -204,6 +204,7 @@ module API
optional 'file.path', type: String, desc: %q(path to locally stored body (generated by Workhorse))
optional 'file.name', type: String, desc: %q(real filename as send in Content-Disposition (generated by Workhorse))
optional 'file.type', type: String, desc: %q(real content type as send in Content-Type (generated by Workhorse))
optional 'file.sha256', type: String, desc: %q(sha256 checksum of the file)
optional 'metadata.path', type: String, desc: %q(path to locally stored body (generated by Workhorse))
optional 'metadata.name', type: String, desc: %q(filename (generated by Workhorse))
end
......@@ -224,7 +225,7 @@ module API
expire_in = params['expire_in'] ||
Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in
job.build_job_artifacts_archive(project: job.project, file_type: :archive, file: artifacts, expire_in: expire_in)
job.build_job_artifacts_archive(project: job.project, file_type: :archive, file: artifacts, file_sha256: params['file.sha256'], expire_in: expire_in)
job.build_job_artifacts_metadata(project: job.project, file_type: :metadata, file: metadata, expire_in: expire_in) if metadata
job.artifacts_expire_in = expire_in
......
module Gitlab
module Git
class Branch < Ref
STALE_BRANCH_THRESHOLD = 3.months
def self.find(repo, branch_name)
if branch_name.is_a?(Gitlab::Git::Branch)
branch_name
......@@ -12,6 +14,18 @@ module Gitlab
def initialize(repository, name, target, target_commit)
super(repository, name, target, target_commit)
end
def active?
self.dereferenced_target.committed_date >= STALE_BRANCH_THRESHOLD.ago
end
def stale?
!active?
end
def state
active? ? :active : :stale
end
end
end
end
......@@ -7,7 +7,7 @@ require 'gitlab'
#
Gitlab.configure do |config|
config.endpoint = 'https://gitlab.com/api/v4'
config.private_token = ENV["DOCS_API_TOKEN"] # GitLab Docs bot access token which has only Developer access to gitlab-docs
config.private_token = ENV["DOCS_API_TOKEN"] # GitLab Docs bot access token with Developer access to gitlab-docs
end
#
......@@ -31,13 +31,24 @@ def docs_branch
end
#
# Create a remote branch in gitlab-docs
# Create a remote branch in gitlab-docs and immediately cancel the pipeline
# to avoid race conditions, since a triggered pipeline will also run right
# after the branch creation. This only happens the very first time a branch
# is created and will be skipped in subsequent runs. Read more in
# https://gitlab.com/gitlab-com/gitlab-docs/issues/154.
#
def create_remote_branch
Gitlab.create_branch(GITLAB_DOCS_REPO, docs_branch, 'master')
puts "Remote branch '#{docs_branch}' created"
puts "=> Remote branch '#{docs_branch}' created"
# Get the latest pipeline ID which is also the first
pipeline_id = Gitlab.pipelines(GITLAB_DOCS_REPO, { ref: docs_branch }).last.id
# Cancel the pipeline
Gitlab.cancel_pipeline(GITLAB_DOCS_REPO, pipeline_id)
puts "=> Canceled uneeded pipeline #{pipeline_id} for '#{docs_branch}'"
rescue Gitlab::Error::BadRequest
puts "Remote branch '#{docs_branch}' already exists"
puts "=> Remote branch '#{docs_branch}' already exists"
end
#
......@@ -45,7 +56,7 @@ end
#
def remove_remote_branch
Gitlab.delete_branch(GITLAB_DOCS_REPO, docs_branch)
puts "Remote branch '#{docs_branch}' deleted"
puts "=> Remote branch '#{docs_branch}' deleted"
end
#
......@@ -78,18 +89,22 @@ def trigger_pipeline
# The review app URL
app_url = "http://#{docs_branch}.#{ENV["DOCS_REVIEW_APPS_DOMAIN"]}/#{slug}"
# Create the pipeline
puts "=> Triggering a pipeline..."
# Create the cross project pipeline using CI_JOB_TOKEN
pipeline = Gitlab.run_trigger(GITLAB_DOCS_REPO, ENV["CI_JOB_TOKEN"], docs_branch, { param_name => ENV["CI_COMMIT_REF_NAME"] })
puts "=> Pipeline created:"
puts "=> Follow the status of the triggered pipeline:"
puts ""
puts "https://gitlab.com/gitlab-com/gitlab-docs/pipelines/#{pipeline.id}"
puts ""
puts "=> Preview your changes live at:"
puts "=> In a few minutes, you will be able to preview your changes under the following URL:"
puts ""
puts app_url
puts ""
puts "=> For more information, read the documentation"
puts "=> https://docs.gitlab.com/ee/development/writing_documentation.html#previewing-the-changes-live"
puts ""
puts "=> If something doesn't work, drop a line in the #docs chat channel."
puts ""
end
#
......
......@@ -425,10 +425,43 @@ describe Projects::BranchesController do
get :index,
namespace_id: project.namespace,
project_id: project,
state: 'all',
format: :html
expect(response).to have_gitlab_http_status(200)
end
end
context 'when depreated sort/search/page parameters are specified' do
it 'returns with a status 301 when sort specified' do
get :index,
namespace_id: project.namespace,
project_id: project,
sort: 'updated_asc',
format: :html
expect(response).to redirect_to project_branches_filtered_path(project, state: 'all')
end
it 'returns with a status 301 when search specified' do
get :index,
namespace_id: project.namespace,
project_id: project,
search: 'feature',
format: :html
expect(response).to redirect_to project_branches_filtered_path(project, state: 'all')
end
it 'returns with a status 301 when page specified' do
get :index,
namespace_id: project.namespace,
project_id: project,
page: 2,
format: :html
expect(response).to redirect_to project_branches_filtered_path(project, state: 'all')
end
end
end
end
......@@ -29,7 +29,7 @@ feature 'Download buttons in branches page' do
describe 'when checking branches' do
context 'with artifacts' do
before do
visit project_branches_path(project, search: 'binary-encoding')
visit project_branches_filtered_path(project, state: 'all', search: 'binary-encoding')
end
scenario 'shows download artifacts button' do
......
......@@ -13,15 +13,109 @@ describe 'Branches' do
project.add_developer(user)
end
describe 'Initial branches page' do
it 'shows all the branches sorted by last updated by default' do
context 'on the projects with 6 active branches and 4 stale branches' do
let(:project) { create(:project, :public, :empty_repo) }
let(:repository) { project.repository }
let(:threshold) { Gitlab::Git::Branch::STALE_BRANCH_THRESHOLD }
before do
# Add 4 stale branches
(1..4).reverse_each do |i|
Timecop.freeze((threshold + i).ago) { create_file(message: "a commit in stale-#{i}", branch_name: "stale-#{i}") }
end
# Add 6 active branches
(1..6).each do |i|
Timecop.freeze((threshold - i).ago) { create_file(message: "a commit in active-#{i}", branch_name: "active-#{i}") }
end
end
describe 'Overview page of the branches' do
it 'shows the first 5 active branches and the first 4 stale branches sorted by last updated' do
visit project_branches_path(project)
expect(page).to have_content(sorted_branches(repository, count: 5, sort_by: :updated_desc, state: 'active'))
expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
expect(page).to have_link('Show more active branches', href: project_branches_filtered_path(project, state: 'active'))
expect(page).not_to have_content('Show more stale branches')
end
end
describe 'Active branches page' do
it 'shows 6 active branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'active')
expect(page).to have_content(sorted_branches(repository, count: 6, sort_by: :updated_desc, state: 'active'))
end
end
describe 'Stale branches page' do
it 'shows 4 active branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'stale')
expect(page).to have_content(sorted_branches(repository, count: 4, sort_by: :updated_desc, state: 'stale'))
end
end
describe 'All branches page' do
it 'shows 10 branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'all')
expect(page).to have_content(sorted_branches(repository, count: 10, sort_by: :updated_desc))
end
end
context 'with branches over more than one page' do
before do
allow(Kaminari.config).to receive(:default_per_page).and_return(5)
end
it 'shows only default_per_page active branches sorted by last updated' do
visit project_branches_filtered_path(project, state: 'active')
expect(page).to have_content(sorted_branches(repository, count: Kaminari.config.default_per_page, sort_by: :updated_desc, state: 'active'))
end
it 'shows only default_per_page branches sorted by last updated on All branches' do
visit project_branches_filtered_path(project, state: 'all')
expect(page).to have_content(sorted_branches(repository, count: Kaminari.config.default_per_page, sort_by: :updated_desc))
end
end
end
describe 'Find branches' do
it 'shows filtered branches', :js do
visit project_branches_path(project)
fill_in 'branch-search', with: 'fix'
find('#branch-search').native.send_keys(:enter)
expect(page).to have_content('fix')
expect(find('.all-branches')).to have_selector('li', count: 1)
end
end
describe 'Delete unprotected branch on Overview' do
it 'removes branch after confirmation', :js do
visit project_branches_filtered_path(project, state: 'all')
expect(all('.all-branches').last).to have_selector('li', count: 20)
accept_confirm { find('.js-branch-add-pdf-text-binary .btn-remove').click }
expect(all('.all-branches').last).to have_selector('li', count: 19)
end
end
describe 'All branches page' do
it 'shows all the branches sorted by last updated by default' do
visit project_branches_filtered_path(project, state: 'all')
expect(page).to have_content(sorted_branches(repository, count: 20, sort_by: :updated_desc))
end
it 'sorts the branches by name' do
visit project_branches_path(project)
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
click_link "Name"
......@@ -30,7 +124,7 @@ describe 'Branches' do
end
it 'sorts the branches by oldest updated' do
visit project_branches_path(project)
visit project_branches_filtered_path(project, state: 'all')
click_button "Last updated" # Open sorting dropdown
click_link "Oldest updated"
......@@ -43,13 +137,13 @@ describe 'Branches' do
%w(one two three four five).each { |ref| repository.add_branch(user, ref, 'master') }
expect { visit project_branches_path(project) }.not_to exceed_query_limit(control_count)
expect { visit project_branches_filtered_path(project, state: 'all') }.not_to exceed_query_limit(control_count)
end
end
describe 'Find branches' do
describe 'Find branches on All branches' do
it 'shows filtered branches', :js do
visit project_branches_path(project)
visit project_branches_filtered_path(project, state: 'all')
fill_in 'branch-search', with: 'fix'
find('#branch-search').native.send_keys(:enter)
......@@ -59,9 +153,9 @@ describe 'Branches' do
end
end
describe 'Delete unprotected branch' do
describe 'Delete unprotected branch on All branches' do
it 'removes branch after confirmation', :js do
visit project_branches_path(project)
visit project_branches_filtered_path(project, state: 'all')
fill_in 'branch-search', with: 'fix'
......@@ -75,6 +169,19 @@ describe 'Branches' do
expect(find('.all-branches')).to have_selector('li', count: 0)
end
end
context 'on project with 0 branch' do
let(:project) { create(:project, :public, :empty_repo) }
let(:repository) { project.repository }
describe '0 branches on Overview' do
it 'shows warning' do
visit project_branches_path(project)
expect(page).not_to have_selector('.all-branches')
end
end
end
end
context 'logged in as master' do
......@@ -85,7 +192,7 @@ describe 'Branches' do
describe 'Initial branches page' do
it 'shows description for admin' do
visit project_branches_path(project)
visit project_branches_filtered_path(project, state: 'all')
expect(page).to have_content("Protected branches can be managed in project settings")
end
......@@ -104,12 +211,18 @@ describe 'Branches' do
end
end
def sorted_branches(repository, count:, sort_by:)
def sorted_branches(repository, count:, sort_by:, state: nil)
branches = repository.branches_sorted_by(sort_by)
branches = branches.select { |b| state == 'active' ? b.active? : b.stale? } if state
sorted_branches =
repository.branches_sorted_by(sort_by).first(count).map do |branch|
branches.first(count).map do |branch|
Regexp.escape(branch.name)
end
Regexp.new(sorted_branches.join('.*'))
end
def create_file(message: 'message', branch_name:)
repository.create_file(user, generate(:branch), 'content', message: message, branch_name: branch_name)
end
end
......@@ -234,7 +234,7 @@ feature 'Environment' do
end
scenario 'user deletes the branch with running environment' do
visit project_branches_path(project, search: 'feature')
visit project_branches_filtered_path(project, state: 'all', search: 'feature')
remove_branch_with_hooks(project, user, 'feature') do
page.within('.js-branch-feature') { find('a.btn-remove').click }
......
......@@ -81,8 +81,8 @@ feature 'Merge Request button' do
context 'on branches page' do
it_behaves_like 'Merge request button only shown when allowed' do
let(:label) { 'Merge request' }
let(:url) { project_branches_path(project, search: 'feature') }
let(:fork_url) { project_branches_path(forked_project, search: 'feature') }
let(:url) { project_branches_filtered_path(project, state: 'all', search: 'feature') }
let(:fork_url) { project_branches_filtered_path(forked_project, state: 'all', search: 'feature') }
end
end
......
......@@ -2,12 +2,13 @@ require 'spec_helper'
describe TodosFinder do
describe '#execute' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:finder) { described_class }
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:finder) { described_class }
before do
project.add_developer(user)
group.add_developer(user)
end
describe '#sort' do
......@@ -34,17 +35,20 @@ describe TodosFinder do
end
it "sorts by priority" do
project_2 = create(:project)
label_1 = create(:label, title: 'label_1', project: project, priority: 1)
label_2 = create(:label, title: 'label_2', project: project, priority: 2)
label_3 = create(:label, title: 'label_3', project: project, priority: 3)
label_1_2 = create(:label, title: 'label_1', project: project_2, priority: 1)
issue_1 = create(:issue, title: 'issue_1', project: project)
issue_2 = create(:issue, title: 'issue_2', project: project)
issue_3 = create(:issue, title: 'issue_3', project: project)
issue_4 = create(:issue, title: 'issue_4', project: project)
merge_request_1 = create(:merge_request, source_project: project)
merge_request_1 = create(:merge_request, source_project: project_2)
merge_request_1.labels << label_1
merge_request_1.labels << label_1_2
# Covers the case where Todo has more than one label
issue_3.labels << label_1
......@@ -57,15 +61,14 @@ describe TodosFinder do
todo_2 = create(:todo, user: user, project: project, target: issue_2)
todo_3 = create(:todo, user: user, project: project, target: issue_3, created_at: 2.hours.ago)
todo_4 = create(:todo, user: user, project: project, target: issue_1)
todo_5 = create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago)
todo_5 = create(:todo, user: user, project: project_2, target: merge_request_1, created_at: 1.hour.ago)
project_2.add_developer(user)
todos = finder.new(user, { sort: 'priority' }).execute
expect(todos.first).to eq(todo_3)
expect(todos.second).to eq(todo_5)
expect(todos.third).to eq(todo_4)
expect(todos.fourth).to eq(todo_2)
expect(todos.fifth).to eq(todo_1)
puts todos.to_sql
expect(todos).to eq([todo_3, todo_5, todo_4, todo_2, todo_1])
end
end
end
......
......@@ -129,7 +129,7 @@ describe('AppComponent', () => {
vm.fetchGroups({});
setTimeout(() => {
expect(vm.isLoading).toBeFalsy();
expect(vm.isLoading).toBe(false);
expect($.scrollTo).toHaveBeenCalledWith(0);
expect(window.Flash).toHaveBeenCalledWith('An error occurred. Please try again.');
done();
......@@ -144,10 +144,10 @@ describe('AppComponent', () => {
spyOn(vm, 'updateGroups').and.callThrough();
vm.fetchAllGroups();
expect(vm.isLoading).toBeTruthy();
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalled();
setTimeout(() => {
expect(vm.isLoading).toBeFalsy();
expect(vm.isLoading).toBe(false);
expect(vm.updateGroups).toHaveBeenCalled();
done();
}, 0);
......@@ -181,7 +181,7 @@ describe('AppComponent', () => {
spyOn($, 'scrollTo');
vm.fetchPage(2, null, null, true);
expect(vm.isLoading).toBeTruthy();
expect(vm.isLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
page: 2,
filterGroupsBy: null,
......@@ -190,7 +190,7 @@ describe('AppComponent', () => {
archived: true,
});
setTimeout(() => {
expect(vm.isLoading).toBeFalsy();
expect(vm.isLoading).toBe(false);
expect($.scrollTo).toHaveBeenCalledWith(0);
expect(utils.mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
expect(window.history.replaceState).toHaveBeenCalledWith({
......@@ -216,7 +216,7 @@ describe('AppComponent', () => {
spyOn(vm.store, 'setGroupChildren');
vm.toggleChildren(groupItem);
expect(groupItem.isChildrenLoading).toBeTruthy();
expect(groupItem.isChildrenLoading).toBe(true);
expect(vm.fetchGroups).toHaveBeenCalledWith({
parentId: groupItem.id,
});
......@@ -232,7 +232,7 @@ describe('AppComponent', () => {
vm.toggleChildren(groupItem);
expect(vm.fetchGroups).not.toHaveBeenCalled();
expect(groupItem.isOpen).toBeTruthy();
expect(groupItem.isOpen).toBe(true);
});
it('should collapse group if it is already expanded', () => {
......@@ -241,16 +241,16 @@ describe('AppComponent', () => {
vm.toggleChildren(groupItem);
expect(vm.fetchGroups).not.toHaveBeenCalled();
expect(groupItem.isOpen).toBeFalsy();
expect(groupItem.isOpen).toBe(false);
});
it('should set `isChildrenLoading` back to `false` if load request fails', (done) => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise({}, true));
vm.toggleChildren(groupItem);
expect(groupItem.isChildrenLoading).toBeTruthy();
expect(groupItem.isChildrenLoading).toBe(true);
setTimeout(() => {
expect(groupItem.isChildrenLoading).toBeFalsy();
expect(groupItem.isChildrenLoading).toBe(false);
done();
}, 0);
});
......@@ -268,10 +268,10 @@ describe('AppComponent', () => {
it('updates props which show modal confirmation dialog', () => {
const group = Object.assign({}, mockParentGroupItem);
expect(vm.updateModal).toBeFalsy();
expect(vm.showModal).toBe(false);
expect(vm.groupLeaveConfirmationMessage).toBe('');
vm.showLeaveGroupModal(group, mockParentGroupItem);
expect(vm.updateModal).toBeTruthy();
expect(vm.showModal).toBe(true);
expect(vm.groupLeaveConfirmationMessage).toBe(`Are you sure you want to leave the "${group.fullName}" group?`);
});
});
......@@ -280,9 +280,9 @@ describe('AppComponent', () => {
it('hides modal confirmation which is shown before leaving the group', () => {
const group = Object.assign({}, mockParentGroupItem);
vm.showLeaveGroupModal(group, mockParentGroupItem);
expect(vm.updateModal).toBeTruthy();
expect(vm.showModal).toBe(true);
vm.hideLeaveGroupModal();
expect(vm.updateModal).toBeFalsy();
expect(vm.showModal).toBe(false);
});
});
......@@ -307,8 +307,8 @@ describe('AppComponent', () => {
spyOn($, 'scrollTo');
vm.leaveGroup();
expect(vm.updateModal).toBeFalsy();
expect(vm.targetGroup.isBeingRemoved).toBeTruthy();
expect(vm.showModal).toBe(false);
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(vm.targetGroup.leavePath);
setTimeout(() => {
expect($.scrollTo).toHaveBeenCalledWith(0);
......@@ -325,12 +325,12 @@ describe('AppComponent', () => {
spyOn(window, 'Flash');
vm.leaveGroup();
expect(vm.targetGroup.isBeingRemoved).toBeTruthy();
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath);
setTimeout(() => {
expect(vm.store.removeGroup).not.toHaveBeenCalled();
expect(window.Flash).toHaveBeenCalledWith(message);
expect(vm.targetGroup.isBeingRemoved).toBeFalsy();
expect(vm.targetGroup.isBeingRemoved).toBe(false);
done();
}, 0);
});
......@@ -342,12 +342,12 @@ describe('AppComponent', () => {
spyOn(window, 'Flash');
vm.leaveGroup(childGroupItem, groupItem);
expect(vm.targetGroup.isBeingRemoved).toBeTruthy();
expect(vm.targetGroup.isBeingRemoved).toBe(true);
expect(vm.service.leaveGroup).toHaveBeenCalledWith(childGroupItem.leavePath);
setTimeout(() => {
expect(vm.store.removeGroup).not.toHaveBeenCalled();
expect(window.Flash).toHaveBeenCalledWith(message);
expect(vm.targetGroup.isBeingRemoved).toBeFalsy();
expect(vm.targetGroup.isBeingRemoved).toBe(false);
done();
}, 0);
});
......@@ -379,10 +379,10 @@ describe('AppComponent', () => {
it('should set `isSearchEmpty` prop based on groups count', () => {
vm.updateGroups(mockGroups);
expect(vm.isSearchEmpty).toBeFalsy();
expect(vm.isSearchEmpty).toBe(false);
vm.updateGroups([]);
expect(vm.isSearchEmpty).toBeTruthy();
expect(vm.isSearchEmpty).toBe(true);
});
});
});
......@@ -473,13 +473,16 @@ describe('AppComponent', () => {
});
});
it('renders modal confirmation dialog', () => {
it('renders modal confirmation dialog', (done) => {
vm.groupLeaveConfirmationMessage = 'Are you sure you want to leave the "foo" group?';
vm.updateModal = true;
const modalDialogEl = vm.$el.querySelector('.modal');
expect(modalDialogEl).not.toBe(null);
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
vm.showModal = true;
Vue.nextTick(() => {
const modalDialogEl = vm.$el.querySelector('.modal');
expect(modalDialogEl).not.toBe(null);
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
done();
});
});
});
});
......@@ -59,5 +59,69 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { expect(branch.dereferenced_target.sha).to eq(SeedRepo::LastCommit::ID) }
end
context 'with active, stale and future branches' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
end
let(:user) { create(:user) }
let(:committer) do
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
let(:params) do
parents = [repository.rugged.head.target]
tree = parents.first.tree
{
message: 'commit message',
author: committer,
committer: committer,
tree: tree,
parents: parents
}
end
let(:stale_sha) { Timecop.freeze(Gitlab::Git::Branch::STALE_BRANCH_THRESHOLD.ago - 5.days) { create_commit } }
let(:active_sha) { Timecop.freeze(Gitlab::Git::Branch::STALE_BRANCH_THRESHOLD.ago + 5.days) { create_commit } }
let(:future_sha) { Timecop.freeze(100.days.since) { create_commit } }
before do
repository.create_branch('stale-1', stale_sha)
repository.create_branch('active-1', active_sha)
repository.create_branch('future-1', future_sha)
end
after do
ensure_seeds
end
describe 'examine if the branch is active or stale' do
let(:stale_branch) { repository.find_branch('stale-1') }
let(:active_branch) { repository.find_branch('active-1') }
let(:future_branch) { repository.find_branch('future-1') }
describe '#active?' do
it { expect(stale_branch.active?).to be_falsey }
it { expect(active_branch.active?).to be_truthy }
it { expect(future_branch.active?).to be_truthy }
end
describe '#stale?' do
it { expect(stale_branch.stale?).to be_truthy }
it { expect(active_branch.stale?).to be_falsey }
it { expect(future_branch.stale?).to be_falsey }
end
describe '#state' do
it { expect(stale_branch.state).to eq(:stale) }
it { expect(active_branch.state).to eq(:active) }
it { expect(future_branch.state).to eq(:active) }
end
end
end
it { expect(repository.branches.size).to eq(SeedRepo::Repo::BRANCHES.size) }
def create_commit
repository.create_commit(params.merge(committer: committer.merge(time: Time.now)))
end
end
......@@ -34,6 +34,8 @@ describe Clusters::Applications::Runner do
is_expected.to include('checkInterval')
is_expected.to include('rbac')
is_expected.to include('runners')
is_expected.to include('privileged: true')
is_expected.to include('image: ubuntu:16.04')
is_expected.to include('resources')
is_expected.to include("runnerToken: #{ci_runner.token}")
is_expected.to include("gitlabUrl: #{Gitlab::Routing.url_helpers.root_url}")
......@@ -61,5 +63,33 @@ describe Clusters::Applications::Runner do
expect(gitlab_runner.runner).not_to be_nil
end
end
context 'with duplicated values on vendor/runner/values.yaml' do
let(:values) do
{
"concurrent" => 4,
"checkInterval" => 3,
"rbac" => {
"create" => false
},
"clusterWideAccess" => false,
"runners" => {
"privileged" => false,
"image" => "ubuntu:16.04",
"builds" => {},
"services" => {},
"helpers" => {}
}
}
end
before do
allow(gitlab_runner).to receive(:chart_values).and_return(values)
end
it 'should overwrite values.yaml' do
is_expected.to include("privileged: #{gitlab_runner.privileged}")
end
end
end
end
......@@ -1103,11 +1103,13 @@ describe API::Runner do
context 'posts artifacts file and metadata file' do
let!(:artifacts) { file_upload }
let!(:artifacts_sha256) { Digest::SHA256.file(artifacts.path).hexdigest }
let!(:metadata) { file_upload2 }
let(:stored_artifacts_file) { job.reload.artifacts_file.file }
let(:stored_metadata_file) { job.reload.artifacts_metadata.file }
let(:stored_artifacts_size) { job.reload.artifacts_size }
let(:stored_artifacts_sha256) { job.reload.job_artifacts_archive.file_sha256 }
before do
post(api("/jobs/#{job.id}/artifacts"), post_data, headers_with_token)
......@@ -1117,6 +1119,7 @@ describe API::Runner do
let(:post_data) do
{ 'file.path' => artifacts.path,
'file.name' => artifacts.original_filename,
'file.sha256' => artifacts_sha256,
'metadata.path' => metadata.path,
'metadata.name' => metadata.original_filename }
end
......@@ -1126,6 +1129,7 @@ describe API::Runner do
expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename)
expect(stored_metadata_file.original_filename).to eq(metadata.original_filename)
expect(stored_artifacts_size).to eq(72821)
expect(stored_artifacts_sha256).to eq(artifacts_sha256)
end
end
......
......@@ -15,10 +15,8 @@ rbac:
clusterWideAccess: false
## Configuration for the Pods that that the runner launches for each new job
##
runners:
image: ubuntu:16.04
privileged: false
builds: {}
services: {}
helpers: {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment