Commit 5f79494d authored by Rémy Coutable's avatar Rémy Coutable 🏖

Merge remote-tracking branch 'origin/master' into 8-10-stable

parents 119368fb 4e898b9b
...@@ -16,6 +16,7 @@ v 8.10.0 (unreleased) ...@@ -16,6 +16,7 @@ v 8.10.0 (unreleased)
- Align flash messages with left side of page content !4959 (winniehell) - Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell) - Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
- Use default cursor for table header of project files !5165 (winniehell) - Use default cursor for table header of project files !5165 (winniehell)
- Store when and yaml variables in builds table
- Display last commit of deleted branch in push events !4699 (winniehell) - Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell) - Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack - Apply the trusted_proxies config to the rack request object for use with rack_attack
...@@ -91,6 +92,7 @@ v 8.10.0 (unreleased) ...@@ -91,6 +92,7 @@ v 8.10.0 (unreleased)
- Handle custom Git hook result in GitLab UI - Handle custom Git hook result in GitLab UI
- Allow to access Container Registry for Public and Internal projects - Allow to access Container Registry for Public and Internal projects
- Allow '?', or '&' for label names - Allow '?', or '&' for label names
- Support redirected blobs for Container Registry integration
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page - Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project - Fix 404 redirect after validation fails importing a GitLab project
...@@ -111,6 +113,7 @@ v 8.10.0 (unreleased) ...@@ -111,6 +113,7 @@ v 8.10.0 (unreleased)
- Fix creating group with space in group path - Fix creating group with space in group path
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska) - Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- Limit the number of retries on error to 3 for exporting projects - Limit the number of retries on error to 3 for exporting projects
- Allow empty repositories on project import/export
v 8.9.6 v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154 - Fix importing of events under notes for GitLab projects. !5154
...@@ -119,6 +122,7 @@ v 8.9.6 ...@@ -119,6 +122,7 @@ v 8.9.6
- Fix broken migration in MySQL. !5005 - Fix broken migration in MySQL. !5005
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213 - Overwrite Host and X-Forwarded-Host headers in NGINX !5213
- Keeps issue number when importing from Gitlab.com - Keeps issue number when importing from Gitlab.com
- Add Pending tab for Builds (Katarzyna Kobierska, Urszula Budziszewska)
v 8.9.7 (unreleased) v 8.9.7 (unreleased)
- Fix import_data wrongly saved as a result of an invalid import_url - Fix import_data wrongly saved as a result of an invalid import_url
......
...@@ -5,8 +5,10 @@ class Admin::BuildsController < Admin::ApplicationController ...@@ -5,8 +5,10 @@ class Admin::BuildsController < Admin::ApplicationController
@builds = @all_builds.order('created_at DESC') @builds = @all_builds.order('created_at DESC')
@builds = @builds =
case @scope case @scope
when 'pending'
@builds.pending.reverse_order
when 'running' when 'running'
@builds.running_or_pending.reverse_order @builds.running.reverse_order
when 'finished' when 'finished'
@builds.finished @builds.finished
else else
......
...@@ -10,8 +10,10 @@ class Projects::BuildsController < Projects::ApplicationController ...@@ -10,8 +10,10 @@ class Projects::BuildsController < Projects::ApplicationController
@builds = @all_builds.order('created_at DESC') @builds = @all_builds.order('created_at DESC')
@builds = @builds =
case @scope case @scope
when 'pending'
@builds.pending.reverse_order
when 'running' when 'running'
@builds.running_or_pending.reverse_order @builds.running.reverse_order
when 'finished' when 'finished'
@builds.finished @builds.finished
else else
......
...@@ -5,6 +5,7 @@ module Ci ...@@ -5,6 +5,7 @@ module Ci
belongs_to :erased_by, class_name: 'User' belongs_to :erased_by, class_name: 'User'
serialize :options serialize :options
serialize :yaml_variables
validates :coverage, numericality: true, allow_blank: true validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref validates_presence_of :ref
...@@ -52,6 +53,8 @@ module Ci ...@@ -52,6 +53,8 @@ module Ci
new_build.stage = build.stage new_build.stage = build.stage
new_build.stage_idx = build.stage_idx new_build.stage_idx = build.stage_idx
new_build.trigger_request = build.trigger_request new_build.trigger_request = build.trigger_request
new_build.yaml_variables = build.yaml_variables
new_build.when = build.when
new_build.user = user new_build.user = user
new_build.environment = build.environment new_build.environment = build.environment
new_build.save new_build.save
...@@ -118,7 +121,12 @@ module Ci ...@@ -118,7 +121,12 @@ module Ci
end end
def variables def variables
predefined_variables + yaml_variables + project_variables + trigger_variables variables = []
variables += predefined_variables
variables += yaml_variables if yaml_variables
variables += project_variables
variables += trigger_variables
variables
end end
def merge_request def merge_request
...@@ -395,30 +403,6 @@ module Ci ...@@ -395,30 +403,6 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil) self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
end end
def yaml_variables
global_yaml_variables + job_yaml_variables
end
def global_yaml_variables
if pipeline.config_processor
pipeline.config_processor.global_variables.map do |key, value|
{ key: key, value: value, public: true }
end
else
[]
end
end
def job_yaml_variables
if pipeline.config_processor
pipeline.config_processor.job_variables(name).map do |key, value|
{ key: key, value: value, public: true }
end
else
[]
end
end
def project_variables def project_variables
project.variables.map do |variable| project.variables.map do |variable|
{ key: variable.key, value: variable.value, public: false } { key: variable.key, value: variable.value, public: false }
......
...@@ -36,7 +36,9 @@ module Ci ...@@ -36,7 +36,9 @@ module Ci
:allow_failure, :allow_failure,
:stage, :stage,
:stage_idx, :stage_idx,
:environment) :environment,
:when,
:yaml_variables)
build_attrs.merge!(pipeline: @pipeline, build_attrs.merge!(pipeline: @pipeline,
ref: @pipeline.ref, ref: @pipeline.ref,
......
...@@ -10,15 +10,20 @@ ...@@ -10,15 +10,20 @@
All All
%span.badge.js-totalbuilds-count= @all_builds.count(:id) %span.badge.js-totalbuilds-count= @all_builds.count(:id)
%li{class: ('active' if @scope == 'pending')}
= link_to admin_builds_path(scope: :pending) do
Pending
%span.badge= number_with_delimiter(@all_builds.pending.count(:id))
%li{class: ('active' if @scope == 'running')} %li{class: ('active' if @scope == 'running')}
= link_to admin_builds_path(scope: :running) do = link_to admin_builds_path(scope: :running) do
Running Running
%span.badge.js-running-count= number_with_delimiter(@all_builds.running_or_pending.count(:id)) %span.badge= number_with_delimiter(@all_builds.running.count(:id))
%li{class: ('active' if @scope == 'finished')} %li{class: ('active' if @scope == 'finished')}
= link_to admin_builds_path(scope: :finished) do = link_to admin_builds_path(scope: :finished) do
Finished Finished
%span.badge.js-running-count= number_with_delimiter(@all_builds.finished.count(:id)) %span.badge= number_with_delimiter(@all_builds.finished.count(:id))
.nav-controls .nav-controls
- if @all_builds.running_or_pending.any? - if @all_builds.running_or_pending.any?
......
...@@ -11,17 +11,22 @@ ...@@ -11,17 +11,22 @@
%span.badge.js-totalbuilds-count %span.badge.js-totalbuilds-count
= number_with_delimiter(@all_builds.count(:id)) = number_with_delimiter(@all_builds.count(:id))
%li{class: ('active' if @scope == 'pending')}
= link_to project_builds_path(@project, scope: :pending) do
Pending
%span.badge
= number_with_delimiter(@all_builds.pending.count(:id))
%li{class: ('active' if @scope == 'running')} %li{class: ('active' if @scope == 'running')}
= link_to project_builds_path(@project, scope: :running) do = link_to project_builds_path(@project, scope: :running) do
Running Running
%span.badge.js-running-count %span.badge
= number_with_delimiter(@all_builds.running_or_pending.count(:id)) = number_with_delimiter(@all_builds.running.count(:id))
%li{class: ('active' if @scope == 'finished')} %li{class: ('active' if @scope == 'finished')}
= link_to project_builds_path(@project, scope: :finished) do = link_to project_builds_path(@project, scope: :finished) do
Finished Finished
%span.badge.js-running-count %span.badge
= number_with_delimiter(@all_builds.finished.count(:id)) = number_with_delimiter(@all_builds.finished.count(:id))
.nav-controls .nav-controls
......
class AddIndexForPipelineUserId < ActiveRecord::Migration class AddIndexForPipelineUserId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def change def change
add_concurrent_index :ci_commits, :user_id add_concurrent_index :ci_commits, :user_id
end end
......
class AddWhenAndYamlVariablesToCiBuilds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column :ci_builds, :when, :string
add_column :ci_builds, :yaml_variables, :text
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160715134306) do ActiveRecord::Schema.define(version: 20160716115710) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -168,6 +168,8 @@ ActiveRecord::Schema.define(version: 20160715134306) do ...@@ -168,6 +168,8 @@ ActiveRecord::Schema.define(version: 20160715134306) do
t.string "environment" t.string "environment"
t.datetime "artifacts_expire_at" t.datetime "artifacts_expire_at"
t.integer "artifacts_size" t.integer "artifacts_size"
t.string "when"
t.text "yaml_variables"
end end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
......
...@@ -31,28 +31,34 @@ module Ci ...@@ -31,28 +31,34 @@ module Ci
raise ValidationError, e.message raise ValidationError, e.message
end end
def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil) def jobs_for_ref(ref, tag = false, trigger_request = nil)
builds.select do |build| @jobs.select do |_, job|
build[:stage] == stage && process?(job[:only], job[:except], ref, tag, trigger_request)
process?(build[:only], build[:except], ref, tag, trigger_request)
end end
end end
def builds def jobs_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
@jobs.map do |name, job| jobs_for_ref(ref, tag, trigger_request).select do |_, job|
build_job(name, job) job[:stage] == stage
end end
end end
def global_variables def builds_for_ref(ref, tag = false, trigger_request = nil)
@variables jobs_for_ref(ref, tag, trigger_request).map do |name, job|
build_job(name, job)
end
end end
def job_variables(name) def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
job = @jobs[name.to_sym] jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, job|
return [] unless job build_job(name, job)
end
end
job[:variables] || [] def builds
@jobs.map do |name, job|
build_job(name, job)
end
end end
private private
...@@ -95,11 +101,10 @@ module Ci ...@@ -95,11 +101,10 @@ module Ci
commands: [job[:before_script] || @before_script, job[:script]].flatten.compact.join("\n"), commands: [job[:before_script] || @before_script, job[:script]].flatten.compact.join("\n"),
tag_list: job[:tags] || [], tag_list: job[:tags] || [],
name: name, name: name,
only: job[:only],
except: job[:except],
allow_failure: job[:allow_failure] || false, allow_failure: job[:allow_failure] || false,
when: job[:when] || 'on_success', when: job[:when] || 'on_success',
environment: job[:environment], environment: job[:environment],
yaml_variables: yaml_variables(name),
options: { options: {
image: job[:image] || @image, image: job[:image] || @image,
services: job[:services] || @services, services: job[:services] || @services,
...@@ -111,6 +116,24 @@ module Ci ...@@ -111,6 +116,24 @@ module Ci
} }
end end
def yaml_variables(name)
variables = global_variables.merge(job_variables(name))
variables.map do |key, value|
{ key: key, value: value, public: true }
end
end
def global_variables
@variables || {}
end
def job_variables(name)
job = @jobs[name.to_sym]
return {} unless job
job[:variables] || {}
end
def validate! def validate!
@jobs.each do |name, job| @jobs.each do |name, job|
validate_job!(name, job) validate_job!(name, job)
......
...@@ -7,62 +7,91 @@ module ContainerRegistry ...@@ -7,62 +7,91 @@ module ContainerRegistry
MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json' MANIFEST_VERSION = 'application/vnd.docker.distribution.manifest.v2+json'
# Taken from: FaradayMiddleware::FollowRedirects
REDIRECT_CODES = Set.new [301, 302, 303, 307]
def initialize(base_uri, options = {}) def initialize(base_uri, options = {})
@base_uri = base_uri @base_uri = base_uri
@faraday = Faraday.new(@base_uri) do |conn| @options = options
initialize_connection(conn, options)
end
end end
def repository_tags(name) def repository_tags(name)
response_body @faraday.get("/v2/#{name}/tags/list") response_body faraday.get("/v2/#{name}/tags/list")
end end
def repository_manifest(name, reference) def repository_manifest(name, reference)
response_body @faraday.get("/v2/#{name}/manifests/#{reference}") response_body faraday.get("/v2/#{name}/manifests/#{reference}")
end end
def repository_tag_digest(name, reference) def repository_tag_digest(name, reference)
response = @faraday.head("/v2/#{name}/manifests/#{reference}") response = faraday.head("/v2/#{name}/manifests/#{reference}")
response.headers['docker-content-digest'] if response.success? response.headers['docker-content-digest'] if response.success?
end end
def delete_repository_tag(name, reference) def delete_repository_tag(name, reference)
@faraday.delete("/v2/#{name}/manifests/#{reference}").success? faraday.delete("/v2/#{name}/manifests/#{reference}").success?
end end
def blob(name, digest, type = nil) def blob(name, digest, type = nil)
headers = {} type ||= 'application/octet-stream'
headers['Accept'] = type if type response_body faraday_blob.get("/v2/#{name}/blobs/#{digest}", nil, 'Accept' => type), allow_redirect: true
response_body @faraday.get("/v2/#{name}/blobs/#{digest}", nil, headers)
end end
def delete_blob(name, digest) def delete_blob(name, digest)
@faraday.delete("/v2/#{name}/blobs/#{digest}").success? faraday.delete("/v2/#{name}/blobs/#{digest}").success?
end end
private private
def initialize_connection(conn, options) def initialize_connection(conn, options)
conn.request :json conn.request :json
if options[:user] && options[:password]
conn.request(:basic_auth, options[:user].to_s, options[:password].to_s)
elsif options[:token]
conn.request(:authorization, :bearer, options[:token].to_s)
end
conn.adapter :net_http
end
def accept_manifest(conn)
conn.headers['Accept'] = MANIFEST_VERSION conn.headers['Accept'] = MANIFEST_VERSION
conn.response :json, content_type: 'application/json' conn.response :json, content_type: 'application/json'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws' conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+prettyjws'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json' conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v1+json'
conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v2+json' conn.response :json, content_type: 'application/vnd.docker.distribution.manifest.v2+json'
end
if options[:user] && options[:password] def response_body(response, allow_redirect: false)
conn.request(:basic_auth, options[:user].to_s, options[:password].to_s) if allow_redirect && REDIRECT_CODES.include?(response.status)
elsif options[:token] response = redirect_response(response.headers['location'])
conn.request(:authorization, :bearer, options[:token].to_s)
end end
conn.adapter :net_http response.body if response && response.success?
end
def redirect_response(location)
return unless location
# We explicitly remove authorization token
faraday_blob.get(location) do |req|
req['Authorization'] = ''
end
end end
def response_body(response) def faraday
response.body if response.success? @faraday ||= Faraday.new(@base_uri) do |conn|
initialize_connection(conn, @options)
accept_manifest(conn)
end
end
def faraday_blob
@faraday_blob ||= Faraday.new(@base_uri) do |conn|
initialize_connection(conn, @options)
end
end end
end end
end end
...@@ -53,7 +53,7 @@ module ContainerRegistry ...@@ -53,7 +53,7 @@ module ContainerRegistry
def config def config
return unless config_blob return unless config_blob
@config ||= ContainerRegistry::Config.new(self, config_blob) @config ||= ContainerRegistry::Config.new(self, config_blob) if config_blob.data
end end
def created_at def created_at
......
...@@ -44,8 +44,7 @@ module Gitlab ...@@ -44,8 +44,7 @@ module Gitlab
def wiki_restorer def wiki_restorer
Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path, Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
shared: @shared, shared: @shared,
project: ProjectWiki.new(project_tree.restored_project), project: ProjectWiki.new(project_tree.restored_project))
wiki: true)
end end
def uploads_restorer def uploads_restorer
......
...@@ -3,15 +3,14 @@ module Gitlab ...@@ -3,15 +3,14 @@ module Gitlab
class RepoRestorer class RepoRestorer
include Gitlab::ImportExport::CommandLineUtil include Gitlab::ImportExport::CommandLineUtil
def initialize(project:, shared:, path_to_bundle:, wiki: false) def initialize(project:, shared:, path_to_bundle:)
@project = project @project = project
@path_to_bundle = path_to_bundle @path_to_bundle = path_to_bundle
@shared = shared @shared = shared
@wiki = wiki
end end
def restore def restore
return wiki? unless File.exist?(@path_to_bundle) return true unless File.exist?(@path_to_bundle)
FileUtils.mkdir_p(path_to_repo) FileUtils.mkdir_p(path_to_repo)
...@@ -30,10 +29,6 @@ module Gitlab ...@@ -30,10 +29,6 @@ module Gitlab
def path_to_repo def path_to_repo
@project.repository.path_to_repo @project.repository.path_to_repo
end end
def wiki?
@wiki
end
end end
end end
end end
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
end end
def save def save
return false if @project.empty_repo? return true if @project.empty_repo? # it's ok to have no repo
@full_path = File.join(@shared.export_path, ImportExport.project_bundle_filename) @full_path = File.join(@shared.export_path, ImportExport.project_bundle_filename)
bundle_to_disk bundle_to_disk
......
...@@ -4,6 +4,7 @@ module Gitlab ...@@ -4,6 +4,7 @@ module Gitlab
def save def save
@wiki = ProjectWiki.new(@project) @wiki = ProjectWiki.new(@project)
return true unless wiki_repository_exists? # it's okay to have no Wiki return true unless wiki_repository_exists? # it's okay to have no Wiki
bundle_to_disk(File.join(@shared.export_path, project_filename)) bundle_to_disk(File.join(@shared.export_path, project_filename))
end end
......
...@@ -15,6 +15,11 @@ FactoryGirl.define do ...@@ -15,6 +15,11 @@ FactoryGirl.define do
services: ["postgres"] services: ["postgres"]
} }
end end
yaml_variables do
[
{ key: :DB_NAME, value: 'postgres', public: true }
]
end
pipeline factory: :ci_pipeline pipeline factory: :ci_pipeline
......
...@@ -36,12 +36,45 @@ describe 'Admin Builds' do ...@@ -36,12 +36,45 @@ describe 'Admin Builds' do
end end
end end
context 'Pending tab' do
context 'when have pending builds' do
it 'shows pending builds' do
build1 = create(:ci_build, pipeline: pipeline, status: :pending)
build2 = create(:ci_build, pipeline: pipeline, status: :running)
build3 = create(:ci_build, pipeline: pipeline, status: :success)
build4 = create(:ci_build, pipeline: pipeline, status: :failed)
visit admin_builds_path(scope: :pending)
expect(page).to have_selector('.nav-links li.active', text: 'Pending')
expect(page.find('.build-link')).to have_content(build1.id)
expect(page.find('.build-link')).not_to have_content(build2.id)
expect(page.find('.build-link')).not_to have_content(build3.id)
expect(page.find('.build-link')).not_to have_content(build4.id)
expect(page).to have_link 'Cancel all'
end
end
context 'when have no builds pending' do
it 'shows a message' do
create(:ci_build, pipeline: pipeline, status: :success)
visit admin_builds_path(scope: :pending)
expect(page).to have_selector('.nav-links li.active', text: 'Pending')
expect(page).to have_content 'No builds to show'
expect(page).not_to have_link 'Cancel all'
end
end
end
context 'Running tab' do context 'Running tab' do
context 'when have running builds' do context 'when have running builds' do
it 'shows running builds' do it 'shows running builds' do
build1 = create(:ci_build, pipeline: pipeline, status: :pending) build1 = create(:ci_build, pipeline: pipeline, status: :running)
build2 = create(:ci_build, pipeline: pipeline, status: :success) build2 = create(:ci_build, pipeline: pipeline, status: :success)