Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CHANGELOG19
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/stylesheets/framework/layout.scss1
-rw-r--r--app/assets/stylesheets/framework/typography.scss1
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/projects/builds_controller.rb27
-rw-r--r--app/models/ability.rb1
-rw-r--r--app/models/application_setting.rb1
-rw-r--r--app/models/ci/build.rb17
-rw-r--r--app/models/commit_status.rb4
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/project_services/drone_ci_service.rb1
-rw-r--r--app/uploaders/artifact_uploader.rb50
-rw-r--r--app/views/admin/application_settings/_form.html.haml5
-rw-r--r--app/views/admin/labels/_form.html.haml4
-rw-r--r--app/views/ci/lints/show.html.haml16
-rw-r--r--app/views/ci/notify/build_fail_email.html.haml4
-rw-r--r--app/views/ci/notify/build_fail_email.text.erb2
-rw-r--r--app/views/ci/notify/build_success_email.html.haml4
-rw-r--r--app/views/ci/notify/build_success_email.text.erb2
-rw-r--r--app/views/groups/group_members/index.html.haml9
-rw-r--r--app/views/help/_shortcuts.html.haml8
-rw-r--r--app/views/import/bitbucket/status.html.haml4
-rw-r--r--app/views/import/fogbugz/new_user_map.html.haml6
-rw-r--r--app/views/import/fogbugz/status.html.haml4
-rw-r--r--app/views/import/github/status.html.haml4
-rw-r--r--app/views/import/gitlab/status.html.haml4
-rw-r--r--app/views/import/gitorious/status.html.haml4
-rw-r--r--app/views/import/google_code/status.html.haml4
-rw-r--r--app/views/layouts/_search.html.haml4
-rw-r--r--app/views/layouts/notify.html.haml3
-rw-r--r--app/views/projects/_activity.html.haml4
-rw-r--r--app/views/projects/blob/_new_dir.html.haml2
-rw-r--r--app/views/projects/blob/_upload.html.haml6
-rw-r--r--app/views/projects/builds/show.html.haml3
-rw-r--r--app/views/projects/buttons/_star.html.haml12
-rw-r--r--app/views/projects/commit/_commit_box.html.haml4
-rw-r--r--app/views/projects/commit_statuses/_commit_status.html.haml3
-rw-r--r--app/views/projects/graphs/ci.html.haml16
-rw-r--r--app/views/projects/graphs/ci/_build_times.haml17
-rw-r--r--app/views/projects/graphs/ci/_builds.haml50
-rw-r--r--app/views/projects/graphs/ci/_overall.haml16
-rw-r--r--app/views/projects/graphs/commits.html.haml54
-rw-r--r--app/views/projects/graphs/show.html.haml23
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml17
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml7
-rw-r--r--app/views/projects/merge_requests/widget/_merged.html.haml31
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml11
-rw-r--r--app/views/projects/merge_requests/widget/open/_check.html.haml8
-rw-r--r--app/views/projects/new.html.haml14
-rw-r--r--app/views/projects/project_members/index.html.haml9
-rw-r--r--app/views/shared/issuable/_context.html.haml6
-rw-r--r--app/views/shared/issuable/_filter.html.haml12
-rw-r--r--app/views/shared/projects/_list.html.haml4
-rw-r--r--app/views/users/show.html.haml4
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/routes.rb1
-rw-r--r--db/migrate/20151013092124_add_artifacts_file_to_builds.rb5
-rw-r--r--db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb5
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/commits.md6
-rw-r--r--doc/ci/yaml/README.md30
-rw-r--r--doc/install/installation.md3
-rw-r--r--doc/raketasks/backup_restore.md3
-rw-r--r--features/steps/project/graph.rb6
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/helpers.rb48
-rw-r--r--lib/backup/artifacts.rb13
-rw-r--r--lib/backup/manager.rb2
-rw-r--r--lib/ci/api/api.rb1
-rw-r--r--lib/ci/api/builds.rb100
-rw-r--r--lib/ci/api/entities.rb7
-rw-r--r--lib/ci/api/helpers.rb11
-rw-r--r--lib/ci/charts.rb3
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb21
-rw-r--r--lib/file_streamer.rb16
-rw-r--r--lib/gitlab/current_settings.rb1
-rw-r--r--lib/support/nginx/gitlab16
-rw-r--r--lib/support/nginx/gitlab-ssl16
-rw-r--r--lib/tasks/gitlab/backup.rake21
-rw-r--r--lib/uploaded_file.rb37
-rw-r--r--shared/artifacts/.gitkeep0
-rw-r--r--shared/artifacts/tmp/cache/.gitkeep0
-rw-r--r--shared/artifacts/tmp/uploads/.gitkeep0
-rw-r--r--spec/features/builds_spec.rb21
-rw-r--r--spec/features/commits_spec.rb16
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb51
-rw-r--r--spec/models/build_spec.rb15
-rw-r--r--spec/requests/api/commit_status_spec.rb4
-rw-r--r--spec/requests/api/users_spec.rb15
-rw-r--r--spec/requests/ci/api/builds_spec.rb195
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb14
95 files changed, 1043 insertions, 227 deletions
diff --git a/.gitignore b/.gitignore
index 73bde4cc761..39ff95c50ee 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ nohup.out
public/assets/
public/uploads.*
public/uploads/
+shared/artifacts/
rails_best_practices_output.html
/tags
tmp/
diff --git a/CHANGELOG b/CHANGELOG
index 668ea87b28f..71300769fe6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,11 +1,12 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.2.0 (unreleased)
+ - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
+ - Fix Drone CI service template not saving properly (Stan Hu)
- Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
- Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
- - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- - Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
- Improved performance of finding users by one of their Email addresses
+ - Add allow_failure field to commit status API (Stan Hu)
- Improved performance of replacing references in comments
- Show last project commit to default branch on project home page
- Highlight comment based on anchor in URL
@@ -16,6 +17,7 @@ v 8.2.0 (unreleased)
- Fix: Inability to reply to code comments in the MR view, if the MR comes from a fork
- Use git follow flag for commits page when retrieve history for file or directory
- Show merge request CI status on merge requests index page
+ - Send build name and stage in CI notification e-mail
- Extend yml syntax for only and except to support specifying repository path
- Enable shared runners to all new projects
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
@@ -29,11 +31,14 @@ v 8.2.0 (unreleased)
- New design for project graphs page
- Fix incoming email config defaults
- MR target branch is now visible on a list view when it is different from project's default one
+ - Improve Continuous Integration graphs page
+ - Make color of "Accept Merge Request" button consistent with current build status
v 8.1.4
- Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
- Prevent redirect loop when home_page_url is set to the root URL
- - Ability to add release notes (markdown text and attachments) to git tags
+ - Fix incoming email config defaults
+ - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
v 8.1.3
- Force update refs/merge-requests/X/head upon a push to the source branch of a merge request (Stan Hu)
@@ -41,7 +46,7 @@ v 8.1.3
- Use issue editor as cross reference comment author when issue is edited with a new mention
- Add Facebook authentication
-v 8.1.1
+v 8.1.2
- Fix cloning Wiki repositories via HTTP (Stan Hu)
- Add migration to remove satellites directory
- Fix specific runners visibility
@@ -51,14 +56,15 @@ v 8.1.1
- Fix CI badge
- Allow developer to manage builds
+v 8.1.1
+ - Removed, see 8.1.2
+
v 8.1.0
- Ensure MySQL CI limits DB migrations occur after the fields have been created (Stan Hu)
- Fix duplicate repositories in GitHub import page (Stan Hu)
- Redirect to a default path if HTTP_REFERER is not set (Stan Hu)
- Adds ability to create directories using the web editor (Ben Ford)
- Cleanup stuck CI builds
-
-v 8.1.0 (unreleased)
- Send an email to admin email when a user is reported for spam (Jonathan Rochkind)
- Show notifications button when user is member of group rather than project (Grzegorz Bizon)
- Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge.
@@ -96,6 +102,7 @@ v 8.1.0 (unreleased)
- Show CI status on Your projects page and Starred projects page
- Remove "Continuous Integration" page from dashboard
- Add notes and SSL verification entries to hook APIs (Ben Boeckel)
+ - Added build artifacts
- Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
- Move CI runners page to project settings area
- Move CI variables page to project settings area
diff --git a/Gemfile b/Gemfile
index 67aef0c3a8d..8a19885bcb1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -54,7 +54,7 @@ gem 'gollum-lib', '~> 4.0.2'
gem "github-linguist", "~> 4.7.0", require: "linguist"
# API
-gem 'grape', '~> 0.6.1'
+gem 'grape', '~> 0.13.0'
gem 'grape-entity', '~> 0.4.2'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5f95637966e..99cdc2a50ae 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -312,10 +312,10 @@ GEM
gon (5.0.4)
actionpack (>= 2.3.0)
json
- grape (0.6.1)
+ grape (0.13.0)
activesupport
builder
- hashie (>= 1.2.0)
+ hashie (>= 2.1.0)
multi_json (>= 1.3.2)
multi_xml (>= 0.5.2)
rack (>= 1.3.0)
@@ -843,7 +843,7 @@ DEPENDENCIES
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.0.2)
gon (~> 5.0.0)
- grape (~> 0.6.1)
+ grape (~> 0.13.0)
grape-entity (~> 0.4.2)
haml-rails (~> 0.9.0)
hipchat (~> 1.5.0)
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index c7b3b60e769..b91c15d8910 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -5,7 +5,6 @@ html {
body {
padding-top: $header-height;
- text-rendering: geometricPrecision;
}
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index e6558a23858..ba0312ba0db 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -173,7 +173,6 @@
*
*/
body {
- text-rendering:optimizeLegibility;
-webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 3d9c59050ff..a9bcfc7456a 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -58,6 +58,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:admin_notification_email,
:user_oauth_applications,
:shared_runners_enabled,
+ :max_artifacts_size,
restricted_visibility_levels: [],
import_sources: []
)
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 953f30e7c03..4638f77b887 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -3,6 +3,7 @@ class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_manage_builds!, except: [:index, :show, :status]
+ before_action :authorize_download_build_artifacts!, only: [:download]
layout "project"
@@ -51,6 +52,18 @@ class Projects::BuildsController < Projects::ApplicationController
redirect_to build_path(build)
end
+ def download
+ unless artifacts_file.file_storage?
+ return redirect_to artifacts_file.url
+ end
+
+ unless artifacts_file.exists?
+ return not_found!
+ end
+
+ send_file artifacts_file.path, disposition: 'attachment'
+ end
+
def status
render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
end
@@ -67,6 +80,10 @@ class Projects::BuildsController < Projects::ApplicationController
@build ||= ci_project.builds.unscoped.find_by!(id: params[:id])
end
+ def artifacts_file
+ build.artifacts_file
+ end
+
def build_path(build)
namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
end
@@ -76,4 +93,14 @@ class Projects::BuildsController < Projects::ApplicationController
return page_404
end
end
+
+ def authorize_download_build_artifacts!
+ unless can?(current_user, :download_build_artifacts, @project)
+ if current_user.nil?
+ return authenticate_user!
+ else
+ return render_404
+ end
+ end
+ end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index b72178fa126..5ae28d5133e 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -154,6 +154,7 @@ class Ability
:create_merge_request,
:create_wiki,
:manage_builds,
+ :download_build_artifacts,
:push_code
]
end
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 266045f7afa..fa7cf2464ad 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -89,6 +89,7 @@ class ApplicationSetting < ActiveRecord::Base
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
+ max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'],
)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 7f185ae7cc3..0ec7e210321 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -39,6 +39,8 @@ module Ci
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
+ mount_uploader :artifacts_file, ArtifactUploader
+
acts_as_taggable
# To prevent db load megabytes of data from trace
@@ -217,6 +219,14 @@ module Ci
"#{dir_to_trace}/#{id}.log"
end
+ def token
+ project.token
+ end
+
+ def valid_token? token
+ project.valid_token? token
+ end
+
def target_url
Gitlab::Application.routes.url_helpers.
namespace_project_build_url(gl_project.namespace, gl_project, self)
@@ -248,6 +258,13 @@ module Ci
pending? && !any_runners_online?
end
+ def download_url
+ if artifacts_file.exists?
+ Gitlab::Application.routes.url_helpers.
+ download_namespace_project_build_path(gl_project.namespace, gl_project, self)
+ end
+ end
+
private
def yaml_variables
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7d54d83974a..d346c5d35d2 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -92,4 +92,8 @@ class CommitStatus < ActiveRecord::Base
def show_warning?
false
end
+
+ def download_url
+ nil
+ end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 5e964f04ef5..492a026add9 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -24,7 +24,7 @@ module Issuable
scope :authored, ->(user) { where(author_id: user) }
scope :assigned_to, ->(u) { where(assignee_id: u.id)}
- scope :recent, -> { order("created_at DESC") }
+ scope :recent, -> { reorder(id: :desc) }
scope :assigned, -> { where("assignee_id IS NOT NULL") }
scope :unassigned, -> { where("assignee_id IS NULL") }
scope :of_projects, ->(ids) { where(project_id: ids) }
diff --git a/app/models/event.rb b/app/models/event.rb
index 47600c57e35..bf64ac29d32 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -45,7 +45,7 @@ class Event < ActiveRecord::Base
after_create :reset_project_activity
# Scopes
- scope :recent, -> { order(created_at: :desc) }
+ scope :recent, -> { reorder(id: :desc) }
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :with_associations, -> { includes(project: :namespace) }
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index c73c4b058a1..c240213200d 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -32,7 +32,6 @@ class DroneCiService < CiService
def compose_service_hook
hook = service_hook || build_service_hook
- hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join
hook.enable_ssl_verification = enable_ssl_verification
hook.save
end
diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
new file mode 100644
index 00000000000..b4e0fc5772d
--- /dev/null
+++ b/app/uploaders/artifact_uploader.rb
@@ -0,0 +1,50 @@
+# encoding: utf-8
+class ArtifactUploader < CarrierWave::Uploader::Base
+ storage :file
+
+ attr_accessor :build, :field
+
+ def self.artifacts_path
+ File.expand_path('shared/artifacts/', Rails.root)
+ end
+
+ def self.artifacts_upload_path
+ File.expand_path('shared/artifacts/tmp/uploads/', Rails.root)
+ end
+
+ def self.artifacts_cache_path
+ File.expand_path('shared/artifacts/tmp/cache/', Rails.root)
+ end
+
+ def initialize(build, field)
+ @build, @field = build, field
+ end
+
+ def artifacts_path
+ File.join(build.created_at.utc.strftime('%Y_%m'), build.project.id.to_s, build.id.to_s)
+ end
+
+ def store_dir
+ File.join(ArtifactUploader.artifacts_path, artifacts_path)
+ end
+
+ def cache_dir
+ File.join(ArtifactUploader.artifacts_cache_path, artifacts_path)
+ end
+
+ def file_storage?
+ self.class.storage == CarrierWave::Storage::File
+ end
+
+ def exists?
+ file.try(:exists?)
+ end
+
+ def move_to_cache
+ true
+ end
+
+ def move_to_store
+ true
+ end
+end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 7253218c2e9..ddaf0e0e8ff 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -139,5 +139,10 @@
= f.check_box :shared_runners_enabled
Enable shared runners for a new projects
+ .form-group
+ = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'control-label col-sm-2'
+ .col-sm-10
+ = f.number_field :max_artifacts_size, class: 'form-control'
+
.form-actions
= f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/admin/labels/_form.html.haml b/app/views/admin/labels/_form.html.haml
index ad58a3837f6..a5ace4e7a3b 100644
--- a/app/views/admin/labels/_form.html.haml
+++ b/app/views/admin/labels/_form.html.haml
@@ -31,5 +31,5 @@
= f.submit 'Save', class: 'btn btn-save js-save-button'
= link_to "Cancel", admin_labels_path, class: 'btn btn-cancel'
-:coffeescript
- new Labels
+:javascript
+ new Labels();
diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml
index a9b954771c5..fb9057e4882 100644
--- a/app/views/ci/lints/show.html.haml
+++ b/app/views/ci/lints/show.html.haml
@@ -11,15 +11,17 @@
.controls.pull-left.prepend-top-10
= submit_tag "Validate", class: 'btn btn-success submit-yml'
-
+
%p.text-center.loading
%i.fa.fa-refresh.fa-spin
.results.prepend-top-20
-:coffeescript
- $(".loading").hide()
- $('form').bind 'ajax:beforeSend', ->
- $(".loading").show()
- $('form').bind 'ajax:complete', ->
- $(".loading").hide()
+:javascript
+ $(".loading").hide();
+ $('form').bind('ajax:beforeSend', function() {
+ $(".loading").show();
+ });
+ $('form').bind('ajax:complete', function() {
+ $(".loading").hide();
+ });
diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml
index cefb75040e9..b0aaea89075 100644
--- a/app/views/ci/notify/build_fail_email.html.haml
+++ b/app/views/ci/notify/build_fail_email.html.haml
@@ -13,6 +13,10 @@
%p
Branch: #{@build.ref}
%p
+ Stage: #{@build.stage}
+%p
+ Job: #{@build.name}
+%p
Message: #{@build.commit.git_commit_message}
%p
diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb
index 6de5dc10f17..17a3b9b1d33 100644
--- a/app/views/ci/notify/build_fail_email.text.erb
+++ b/app/views/ci/notify/build_fail_email.text.erb
@@ -4,6 +4,8 @@ Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
Branch: <%= @build.ref %>
+Stage: <%= @build.stage %>
+Job: <%= @build.name %>
Message: <%= @build.commit.git_commit_message %>
Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml
index 617b88f7345..24c439e50eb 100644
--- a/app/views/ci/notify/build_success_email.html.haml
+++ b/app/views/ci/notify/build_success_email.html.haml
@@ -14,6 +14,10 @@
%p
Branch: #{@build.ref}
%p
+ Stage: #{@build.stage}
+%p
+ Job: #{@build.name}
+%p
Message: #{@build.commit.git_commit_message}
%p
diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb
index d0a43ae1c12..bc8b978c3d7 100644
--- a/app/views/ci/notify/build_success_email.text.erb
+++ b/app/views/ci/notify/build_success_email.text.erb
@@ -4,6 +4,8 @@ Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
Branch: <%= @build.ref %>
+Stage: <%= @build.stage %>
+Job: <%= @build.name %>
Message: <%= @build.commit.git_commit_message %>
Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index fee4b0052b5..15d289471c9 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -36,7 +36,8 @@
= paginate @members, theme: 'gitlab'
-:coffeescript
- $('form.member-search-form').on 'submit', (event) ->
- event.preventDefault()
- Turbolinks.visit @.action + '?' + $(@).serialize()
+:javascript
+ $('form.member-search-form').on('submit', function(event) {
+ event.preventDefault();
+ Turbolinks.visit(this.action + '?' + $(this).serialize());
+ });
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 67349fcbd78..7e801b5332d 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -222,8 +222,8 @@
:javascript
- $('.js-more-help-button').click(function(e){
- $(this).remove()
- $('.hidden-shortcut').show()
- e.preventDefault()
+ $('.js-more-help-button').click(function (e) {
+ $(this).remove()l
+ $('.hidden-shortcut').show();
+ e.preventDefault();
});
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 30bcdb86827..1f09a27e2d6 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -66,5 +66,5 @@
again.
-:coffeescript
- new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}");
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index a701e49ac56..bc3c90294e3 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -22,7 +22,7 @@
%strong Map a FogBugz account ID to a GitLab user
%p
Selecting a GitLab user will add a link to the GitLab user in the descriptions
- of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
+ of issues and comments (e.g. "By <a href="#">@johnsmith</a>"). It will also
associate and/or assign these issues and comments with the selected user.
.table-holder
@@ -46,5 +46,5 @@
.form-actions
= submit_tag 'Continue to the next step', class: 'btn btn-create'
-:coffeescript
- new UsersSelect()
+:javascript
+ new UsersSelect();
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index beca6ab1423..b902006597b 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -48,5 +48,5 @@
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
-:coffeescript
- new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_fogbugz_path}", "#{import_fogbugz_path}");
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 0669b05adca..0699321c8c0 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -43,5 +43,5 @@
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
-:coffeescript
- new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}");
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index 3bc85059e7d..f4a2b33af21 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -43,5 +43,5 @@
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
-:coffeescript
- new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}");
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index 2e3a535737f..71752d21efa 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -43,5 +43,5 @@
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
-:coffeescript
- new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}");
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index c5af06edf87..8c64fd27e60 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -67,5 +67,5 @@
= link_to "import flow", new_import_google_code_path
again.
-:coffeescript
- new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}")
+:javascript
+ new ImporterStatus("#{jobs_import_google_code_path}", "#{import_google_code_path}");
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index d1aa8f62463..a44f5762a6b 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -25,6 +25,6 @@
:javascript
$('.search-input').on('keyup', function(e) {
if (e.keyCode == 27) {
- $('.search-input').blur()
+ $('.search-input').blur();
}
- })
+ });
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index f58b9bd6ba6..3ca4c340406 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -42,7 +42,8 @@
- else
#{link_to "View it on GitLab", @target_url}.
%br
+ -# Don't link the host is the line below, one link in the email is easier to quickly click than two.
You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
If you'd like to receive fewer emails, you can adjust your notification settings.
- = email_action @target_url
+ = email_action @target_url \ No newline at end of file
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 012858f70b4..101880bd105 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -8,5 +8,5 @@
.content_list{:"data-href" => activity_project_path(@project)}
= spinner
-:coffeescript
- new Activities()
+:javascript
+ new Activities();
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index cb1567a2e68..a0fc8bbd752 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -21,5 +21,5 @@
= submit_tag "Create directory", class: 'btn btn-primary btn-create'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
-:coffeescript
+:javascript
disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create");
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index e27f1707527..a1c54e731f0 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -26,6 +26,6 @@
= button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
= link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
-:coffeescript
- disableButtonIfEmptyField $('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file'
- new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}')
+:javascript
+ disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file');
+ new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}');
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 3374d5432a5..7661452e6ec 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -87,6 +87,9 @@
Test coverage
%h1 #{@build.coverage}%
+ - if current_user && can?(current_user, :download_build_artifacts, @project) && @build.download_url
+ .build-widget.center
+ = link_to "Download artifacts", @build.download_url, class: 'btn btn-sm btn-primary'
.build-widget
%h4.title
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 3501dddefbe..06583902035 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -4,11 +4,13 @@
%span.count
= @project.star_count
- :coffeescript
- $('.project-home-panel .toggle-star').on 'ajax:success', (e, data, status, xhr) ->
- $(@).replaceWith(data.html)
- .on 'ajax:error', (e, xhr, status, error) ->
- new Flash('Star toggle failed. Try again later.', 'alert')
+ :javascript
+ $('.project-home-panel .toggle-star').on('ajax:success', function (e, data, status, xhr) {
+ $(this).replaceWith(data.html);
+ })
+ .on('ajax:error', function (e, xhr, status, error) {
+ new Flash('Star toggle failed. Try again later.', 'alert');
+ });
- else
= link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index a6458b84860..776768537d0 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -55,5 +55,5 @@
%pre.commit-description
= preserve(gfm(escape_once(@commit.description)))
-:coffeescript
- $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
+:javascript
+ $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}");
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index c255559b88c..9a0e7bff3f1 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -61,6 +61,9 @@
%td
.pull-right
+ - if current_user && can?(current_user, :download_build_artifacts, @project) && commit_status.download_url
+ = link_to commit_status.download_url, title: 'Download artifacts' do
+ %i.fa.fa-download
- if current_user && can?(current_user, :manage_builds, commit_status.gl_project)
- if commit_status.active?
- if commit_status.cancel_url
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index b2dfe97938a..6fa77cc10c6 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,10 +1,16 @@
- page_title "Continuous Integration", "Graphs"
= render "header_title"
= render 'head'
-.gray-content-block
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
+.gray-content-block.append-bottom-default
+ .oneline
+ A collection of graphs for Continuous Integration
+
#charts.ci-charts
+ .row
+ .col-md-6
+ = render 'projects/graphs/ci/overall'
+ .col-md-6
+ = render 'projects/graphs/ci/build_times'
+
+ %hr
= render 'projects/graphs/ci/builds'
- = render 'projects/graphs/ci/build_times'
-= render 'projects/graphs/ci/overall'
diff --git a/app/views/projects/graphs/ci/_build_times.haml b/app/views/projects/graphs/ci/_build_times.haml
index c3c2f572414..c58223fd39e 100644
--- a/app/views/projects/graphs/ci/_build_times.haml
+++ b/app/views/projects/graphs/ci/_build_times.haml
@@ -1,21 +1,22 @@
-%fieldset
- %legend
+%div
+ %p.light
Commit duration in minutes for last 30 commits
- %canvas#build_timesChart.padded{width: 800, height: 300}
+ %canvas#build_timesChart{height: 200}
:javascript
var data = {
labels : #{@charts[:build_times].labels.to_json},
datasets : [
{
- fillColor : "#4A3",
- strokeColor : "rgba(151,187,205,1)",
- pointColor : "rgba(151,187,205,1)",
- pointStrokeColor : "#fff",
+ fillColor : "rgba(220,220,220,0.5)",
+ strokeColor : "rgba(220,220,220,1)",
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
data : #{@charts[:build_times].build_times.to_json}
}
]
}
var ctx = $("#build_timesChart").get(0).getContext("2d");
- new Chart(ctx).Line(data,{"scaleOverlay": true});
+ new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_builds.haml b/app/views/projects/graphs/ci/_builds.haml
index 1b0039fb834..8fca07114fa 100644
--- a/app/views/projects/graphs/ci/_builds.haml
+++ b/app/views/projects/graphs/ci/_builds.haml
@@ -1,20 +1,30 @@
-%fieldset
- %legend
- Builds chart for last week
- (#{date_from_to(Date.today - 7.days, Date.today)})
+%h4 Build charts
+%p
+ &nbsp;
+ %span.cgreen
+ = icon("circle")
+ success
+ &nbsp;
+ %span.cgray
+ = icon("circle")
+ all
- %canvas#weekChart.padded{width: 800, height: 200}
+.prepend-top-default
+ %p.light
+ Builds for last week
+ (#{date_from_to(Date.today - 7.days, Date.today)})
+ %canvas#weekChart{height: 200}
-%fieldset
- %legend
- Builds chart for last month
+.prepend-top-default
+ %p.light
+ Builds for last month
(#{date_from_to(Date.today - 30.days, Date.today)})
+ %canvas#monthChart{height: 200}
- %canvas#monthChart.padded{width: 800, height: 300}
-
-%fieldset
- %legend Builds chart for last year
- %canvas#yearChart.padded{width: 800, height: 400}
+.prepend-top-default
+ %p.light
+ Builds for last year
+ %canvas#yearChart.padded{height: 250}
- [:week, :month, :year].each do |scope|
:javascript
@@ -22,20 +32,20 @@
labels : #{@charts[scope].labels.to_json},
datasets : [
{
- fillColor : "rgba(220,220,220,0.5)",
- strokeColor : "rgba(220,220,220,1)",
- pointColor : "rgba(220,220,220,1)",
+ fillColor : "#7f8fa4",
+ strokeColor : "#7f8fa4",
+ pointColor : "#7f8fa4",
pointStrokeColor : "#EEE",
data : #{@charts[scope].total.to_json}
},
{
- fillColor : "#4A3",
- strokeColor : "rgba(151,187,205,1)",
- pointColor : "rgba(151,187,205,1)",
+ fillColor : "#44aa22",
+ strokeColor : "#44aa22",
+ pointColor : "#44aa22",
pointStrokeColor : "#fff",
data : #{@charts[scope].success.to_json}
}
]
}
var ctx = $("##{scope}Chart").get(0).getContext("2d");
- new Chart(ctx).Line(data,{"scaleOverlay": true});
+ new Chart(ctx).Line(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
diff --git a/app/views/projects/graphs/ci/_overall.haml b/app/views/projects/graphs/ci/_overall.haml
index 9550d719471..cf4285a2671 100644
--- a/app/views/projects/graphs/ci/_overall.haml
+++ b/app/views/projects/graphs/ci/_overall.haml
@@ -1,22 +1,20 @@
- ci_project = @project.gitlab_ci_project
-%fieldset
- %legend Overall
- %p
+%h4 Overall stats
+%ul
+ %li
Total:
%strong= pluralize ci_project.builds.count(:all), 'build'
- %p
+ %li
Successful:
%strong= pluralize ci_project.builds.success.count(:all), 'build'
- %p
+ %li
Failed:
%strong= pluralize ci_project.builds.failed.count(:all), 'build'
-
- %p
+ %li
Success ratio:
%strong
#{success_ratio(ci_project.builds.success, ci_project.builds.failed)}%
-
- %p
+ %li
Commits covered:
%strong
= ci_project.commits.count(:all)
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index 4e0c3e5b3de..fc465ab273b 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -2,7 +2,7 @@
= render "header_title"
= render 'head'
-.gray-content-block
+.gray-content-block.append-bottom-default
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs_commits'
%ul.breadcrumb.repo-breadcrumb
@@ -49,26 +49,24 @@
Commits per weekday
%canvas#weekday-chart
-:coffeescript
- responsiveChart = (selector, data) ->
- options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false }
+:javascript
+ var responsiveChart = function (selector, data) {
+ var options = { "scaleOverlay": true, responsive: true, pointHitDetectionRadius: 2, maintainAspectRatio: false };
+ // get selector by context
+ var ctx = selector.get(0).getContext("2d");
+ // pointing parent container to make chart.js inherit its width
+ var container = $(selector).parent();
+ var generateChart = function() {
+ selector.attr('width', $(container).width());
+ return new Chart(ctx).Bar(data, options);
+ };
+ // enabling auto-resizing
+ $(window).resize(generateChart);
+ return generateChart();
+ };
- # get selector by context
- ctx = selector.get(0).getContext("2d")
- # pointing parent container to make chart.js inherit its width
- container = $(selector).parent()
-
- generateChart = ->
- selector.attr('width', $(container).width())
- new Chart(ctx).Bar(data, options)
-
- # enabling auto-resizing
- $(window).resize( generateChart )
-
- generateChart()
-
- chartData = (keys, values) ->
- data = {
+ var chartData = function (keys, values) {
+ var data = {
labels : keys,
datasets : [{
fillColor : "rgba(220,220,220,0.5)",
@@ -78,13 +76,15 @@
barDatasetSpacing: 1,
data : values
}]
- }
+ };
+ return data;
+ };
- hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json})
- responsiveChart($('#hour-chart'), hourData)
+ var hourData = chartData(#{@commits_per_time.keys.to_json}, #{@commits_per_time.values.to_json});
+ responsiveChart($('#hour-chart'), hourData);
- dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json})
- responsiveChart($('#weekday-chart'), dayData)
+ var dayData = chartData(#{@commits_per_week_days.keys.to_json}, #{@commits_per_week_days.values.to_json});
+ responsiveChart($('#weekday-chart'), dayData);
- monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json})
- responsiveChart($('#month-chart'), monthData)
+ var monthData = chartData(#{@commits_per_month.keys.to_json}, #{@commits_per_month.values.to_json});
+ responsiveChart($('#month-chart'), monthData);
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 6bbf15d05a2..882e7d6b6ee 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -2,7 +2,7 @@
= render "header_title"
= render 'head'
-.gray-content-block
+.gray-content-block.append-bottom-default
.tree-ref-holder
= render 'shared/ref_switcher', destination: 'graphs'
%ul.breadcrumb.repo-breadcrumb
@@ -28,18 +28,21 @@
-:coffeescript
- $.ajax
+:javascript
+ $.ajax({
type: "GET",
url: location.href,
- success: (data) ->
- graph = new ContributorsStatGraph()
- graph.init(data)
+ dataType: "json",
+ success: function (data) {
+ var graph = new ContributorsStatGraph();
+ graph.init(data);
- $("#brush_change").change ->
- graph.change_date_header()
- graph.redraw_authors()
+ $("#brush_change").change(function(){
+ graph.change_date_header();
+ graph.redraw_authors();
+ });
$(".stat-graph").fadeIn();
$(".loading-graph").hide();
- dataType: "json"
+ }
+ });
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 452006162db..d9eff1f9320 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -77,12 +77,13 @@
});
-:coffeescript
-
- $(".merge-request-form").on 'submit', ->
- if $("#merge_request_source_branch").val() is "" or $('#merge_request_target_branch').val() is ""
- $(".mr-compare-errors").html("You must select source and target branch to proceed")
- $(".mr-compare-errors").fadeIn()
- event.preventDefault()
- return
+:javascript
+ $(".merge-request-form").on('submit', function () {
+ if ($("#merge_request_source_branch").val() === "" || $('#merge_request_target_branch').val() === "") {
+ $(".mr-compare-errors").html("You must select source and target branch to proceed");
+ $(".mr-compare-errors").fadeIn();
+ event.preventDefault();
+ return;
+ }
+ });
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index a3551516bfe..ba5ad22bca7 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -38,6 +38,7 @@
= icon("times-circle")
Could not connect to the CI server. Please check your settings and try again.
- :coffeescript
- $ ->
- merge_request_widget.getCiStatus()
+ :javascript
+ $(function() {
+ merge_request_widget.getCiStatus();
+ });
diff --git a/app/views/projects/merge_requests/widget/_merged.html.haml b/app/views/projects/merge_requests/widget/_merged.html.haml
index f223f687def..a788fcea23f 100644
--- a/app/views/projects/merge_requests/widget/_merged.html.haml
+++ b/app/views/projects/merge_requests/widget/_merged.html.haml
@@ -15,7 +15,7 @@
- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch)
.remove_source_branch_widget
- %p
+ %p
= succeed '.' do
The changes were merged into
%span.label-branch= @merge_request.target_branch
@@ -25,7 +25,7 @@
Remove Source Branch
.remove_source_branch_widget.failed.hide
- %p
+ %p
Failed to remove source branch '#{@merge_request.source_branch}'.
.remove_source_branch_in_progress.hide
@@ -33,17 +33,20 @@
= icon('spinner spin')
Removing source branch '#{@merge_request.source_branch}'. Please wait. This page will be automatically reload.
- :coffeescript
- $('.remove_source_branch').on 'click', ->
- $('.remove_source_branch_widget').hide()
- $('.remove_source_branch_in_progress').show()
-
- $(".remove_source_branch").on "ajax:success", (e, data, status, xhr) ->
- location.reload()
-
- $(".remove_source_branch").on "ajax:error", (e, data, status, xhr) ->
- $('.remove_source_branch_widget').hide()
- $('.remove_source_branch_in_progress').hide()
- $('.remove_source_branch_widget.failed').show()
+ :javascript
+ $('.remove_source_branch').on('click', function() {
+ $('.remove_source_branch_widget').hide();
+ $('.remove_source_branch_in_progress').show();
+ });
+
+ $(".remove_source_branch").on("ajax:success", function (e, data, status, xhr) {
+ location.reload();
+ });
+
+ $(".remove_source_branch").on("ajax:error", function (e, data, status, xhr) {
+ $('.remove_source_branch_widget').hide();
+ $('.remove_source_branch_in_progress').hide();
+ $('.remove_source_branch_widget.failed').show();
+ });
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index 689247f3186..9b31014b581 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -20,8 +20,9 @@
text: @merge_request.merge_commit_message,
rows: 14, hint: true
- :coffeescript
- $('.accept-mr-form').on 'ajax:before', ->
- btn = $('.accept_merge_request')
- btn.disable()
- btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress")
+ :javascript
+ $('.accept-mr-form').on('ajax:before', function() {
+ var btn = $('.accept_merge_request');
+ btn.disable();
+ btn.html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
+ });
diff --git a/app/views/projects/merge_requests/widget/open/_check.html.haml b/app/views/projects/merge_requests/widget/open/_check.html.haml
index b6b8974297e..e16878ba513 100644
--- a/app/views/projects/merge_requests/widget/open/_check.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_check.html.haml
@@ -2,6 +2,8 @@
= icon("spinner spin")
Checking ability to merge automatically&hellip;
-:coffeescript
- $ ->
- merge_request_widget.getMergeStatus()
+:javascript
+ $(function() {
+ merge_request_widget.getMergeStatus();
+ });
+
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index daab2326bc7..a02c12f06a8 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -124,9 +124,11 @@
Creating project &amp; repository.
%p Please wait a moment, this page will automatically refresh when ready.
-:coffeescript
- $('.how_to_import_link').bind 'click', (e) ->
- e.preventDefault()
- import_modal = $(this).next(".modal").show()
- $('.modal-header .close').bind 'click', ->
- $(".modal").hide()
+:javascript
+ $('.how_to_import_link').bind('click', function (e) {
+ e.preventDefault();
+ var import_modal = $(this).next(".modal").show();
+ });
+ $('.modal-header .close').bind('click', function() {
+ $(".modal").hide();
+ });
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 82809bec5b8..9fc4be583cc 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -29,7 +29,8 @@
- if @group
= render "group_members", members: @group_members
-:coffeescript
- $('form.member-search-form').on 'submit', (event) ->
- event.preventDefault()
- Turbolinks.visit @.action + '?' + $(@).serialize()
+:javascript
+ $('form.member-search-form').on('submit', function (event) {
+ event.preventDefault();
+ Turbolinks.visit(this.action + '?' + $(this).serialize());
+ });
diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml
index cba18c14568..be66256c7b0 100644
--- a/app/views/shared/issuable/_context.html.haml
+++ b/app/views/shared/issuable/_context.html.haml
@@ -45,6 +45,6 @@
.description-block.subscribed{class: ( 'hidden' unless subscribed )}
You're receiving notifications because you're subscribed to this thread.
-:coffeescript
- new Subscription("#{toggle_subscription_path(issuable)}")
- new IssuableContext()
+:javascript
+ new Subscription("#{toggle_subscription_path(issuable)}");
+ new IssuableContext();
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 0e4e9c0987a..d1231438ee4 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -60,9 +60,9 @@
= hidden_field_tag :state_event, params[:state_event]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
-:coffeescript
- new UsersSelect()
-
- $('form.filter-form').on 'submit', (event) ->
- event.preventDefault()
- Turbolinks.visit @.action + '&' + $(@).serialize()
+:javascript
+ new UsersSelect();
+ $('form.filter-form').on('submit', function (event) {
+ event.preventDefault();
+ Turbolinks.visit(this.action + '&' + $(this).serialize());
+ });
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 357cfd6a370..e5ffe1e29ae 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -17,5 +17,5 @@
= link_to '#', class: 'js-expand' do
Show all
-:coffeescript
- new ProjectsList()
+:javascript
+ new ProjectsList();
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 5a15c6c244a..30992412184 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -115,5 +115,5 @@
projects: @projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: true
-:coffeescript
- $(".user-calendar").load("#{user_calendar_path}")
+:javascript
+ $(".user-calendar").load("#{user_calendar_path}");
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 8192d727f2a..302124bd977 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -186,6 +186,7 @@ Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_br
Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil?
Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
+Settings.gitlab_ci['max_artifacts_size'] ||= 100 # in megabytes
#
# Reply by email
diff --git a/config/routes.rb b/config/routes.rb
index 2028ea938e4..c892c034f01 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -611,6 +611,7 @@ Gitlab::Application.routes.draw do
member do
get :status
post :cancel
+ get :download
post :retry
end
end
diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
new file mode 100644
index 00000000000..5a299f7b26d
--- /dev/null
+++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb
@@ -0,0 +1,5 @@
+class AddArtifactsFileToBuilds < ActiveRecord::Migration
+ def change
+ add_column :ci_builds, :artifacts_file, :text
+ end
+end
diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
new file mode 100644
index 00000000000..01d8c0f043e
--- /dev/null
+++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 116c0c8d97d..f631d73f334 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: 20151105094515) do
+ActiveRecord::Schema.define(version: 20151109100728) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -48,6 +48,7 @@ ActiveRecord::Schema.define(version: 20151105094515) do
t.text "help_page_text"
t.string "admin_notification_email"
t.boolean "shared_runners_enabled", default: true, null: false
+ t.integer "max_artifacts_size", default: 100, null: false
end
create_table "audit_events", force: true do |t|
@@ -108,6 +109,7 @@ ActiveRecord::Schema.define(version: 20151105094515) do
t.string "type"
t.string "target_url"
t.string "description"
+ t.text "artifacts_file"
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
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 9f72adc6ed9..8e4a0ee1b82 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -22,7 +22,8 @@ Parameters:
"author_name": "Dmitriy Zaporozhets",
"author_email": "dzaporozhets@sphereconsultinginc.com",
"created_at": "2012-09-20T11:50:22+03:00",
- "message": "Replace sanitize with escape once"
+ "message": "Replace sanitize with escape once",
+ "allow_failure": false
},
{
"id": "6104942438c14ec7bd21c6cd5bd995272b3faff6",
@@ -31,7 +32,8 @@ Parameters:
"author_name": "randx",
"author_email": "dmitriy.zaporozhets@gmail.com",
"created_at": "2012-09-20T09:06:12+03:00",
- "message": "Sanitize for network graph"
+ "message": "Sanitize for network graph",
+ "allow_failure": false
}
]
```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index d117a2969be..5d35d1da4ee 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -141,6 +141,7 @@ job_name:
| tags | optional | Defines a list of tags which are used to select runner |
| allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status |
| when | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` |
+| artifacts | optional | Define list build artifacts |
### script
`script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
@@ -258,6 +259,35 @@ The above script will:
1. Execute `cleanup_build` only when the `build` failed,
2. Always execute `cleanup` as the last step in pipeline.
+### artifacts
+`artifacts` is used to specify list of files and directories which should be attached to build after success.
+
+1. Send all files in `binaries` and `.config`:
+```
+artifacts:
+ paths:
+ - binaries/
+ - .config
+```
+
+2. Send all git untracked files:
+```
+artifacts:
+ untracked: true
+```
+
+3. Send all git untracked files and files in `binaries`:
+```
+artifacts:
+ untracked: true
+ paths:
+ - binaries/
+```
+
+The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download.
+
+This feature requires GitLab Runner v0.7.0 or higher.
+
## Validate the .gitlab-ci.yml
Each instance of GitLab CI has an embedded debug tool called Lint.
You can find the link to the Lint in the project's settings page or use short url `/lint`.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index f17477a3218..8028e51dbcd 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -246,6 +246,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
# Change the permissions of the directory where CI build traces are stored
sudo chmod -R u+rwX builds/
+ # Change the permissions of the directory where CI artifacts are stored
+ sudo chmod -R u+rwX shared/artifacts/
+
# Copy the example Unicorn config
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 606532a6fbe..1a5442cdac7 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -29,7 +29,8 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
-uploads (attachments), repositories, builds(CI build output logs). Use a comma to specify several options at the same time.
+uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts).
+Use a comma to specify several options at the same time.
```
sudo gitlab-rake gitlab:backup:create SKIP=db,uploads
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
index 4abd5288d51..98f31f3b76a 100644
--- a/features/steps/project/graph.rb
+++ b/features/steps/project/graph.rb
@@ -25,9 +25,9 @@ class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
step 'page should have CI graphs' do
expect(page).to have_content 'Overall'
- expect(page).to have_content 'Builds chart for last week'
- expect(page).to have_content 'Builds chart for last month'
- expect(page).to have_content 'Builds chart for last year'
+ expect(page).to have_content 'Builds for last week'
+ expect(page).to have_content 'Builds for last month'
+ expect(page).to have_content 'Builds for last year'
expect(page).to have_content 'Commit duration in minutes for last 30 commits'
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 883a5e14b17..20cadae2291 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -231,7 +231,7 @@ module API
class CommitStatus < Grape::Entity
expose :id, :sha, :ref, :status, :name, :target_url, :description,
- :created_at, :started_at, :finished_at
+ :created_at, :started_at, :finished_at, :allow_failure
expose :author, using: Entities::UserBasic
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 652bdf9b278..92540ccf2b1 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -133,6 +133,12 @@ module API
authorize! :admin_project, user_project
end
+ def require_gitlab_workhorse!
+ unless env['HTTP_GITLAB_WORKHORSE'].present?
+ forbidden!('Request should be executed via GitLab Workhorse')
+ end
+ end
+
def can?(object, action, subject)
abilities.allowed?(object, action, subject)
end
@@ -234,6 +240,10 @@ module API
render_api_error!(message || '409 Conflict', 409)
end
+ def file_to_large!
+ render_api_error!('413 Request Entity Too Large', 413)
+ end
+
def render_validation_error!(model)
if model.errors.any?
render_api_error!(model.errors.messages || '400 Bad Request', 400)
@@ -282,6 +292,44 @@ module API
end
end
+ # file helpers
+
+ def uploaded_file!(field, uploads_path)
+ if params[field]
+ bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename)
+ return params[field]
+ end
+
+ # sanitize file paths
+ # this requires all paths to exist
+ required_attributes! %W(#{field}.path)
+ uploads_path = File.realpath(uploads_path)
+ file_path = File.realpath(params["#{field}.path"])
+ bad_request!('Bad file path') unless file_path.start_with?(uploads_path)
+
+ UploadedFile.new(
+ file_path,
+ params["#{field}.name"],
+ params["#{field}.type"] || 'application/octet-stream',
+ )
+ end
+
+ def present_file!(path, filename, content_type = 'application/octet-stream')
+ filename ||= File.basename(path)
+ header['Content-Disposition'] = "attachment; filename=#{filename}"
+ header['Content-Transfer-Encoding'] = 'binary'
+ content_type content_type
+
+ # Support download acceleration
+ case headers['X-Sendfile-Type']
+ when 'X-Sendfile'
+ header['X-Sendfile'] = path
+ body
+ else
+ file FileStreamer.new(path)
+ end
+ end
+
private
def add_pagination_headers(paginated, per_page)
diff --git a/lib/backup/artifacts.rb b/lib/backup/artifacts.rb
new file mode 100644
index 00000000000..51fa3867e67
--- /dev/null
+++ b/lib/backup/artifacts.rb
@@ -0,0 +1,13 @@
+require 'backup/files'
+
+module Backup
+ class Artifacts < Files
+ def initialize
+ super('artifacts', ArtifactUploader.artifacts_path)
+ end
+
+ def create_files_dir
+ Dir.mkdir(app_files_dir, 0700)
+ end
+ end
+end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index f011fd03de0..9e15d5411a1 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -150,7 +150,7 @@ module Backup
private
def backup_contents
- folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "backup_information.yml"]
+ folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"]
end
def folders_to_backup
diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb
index 0a4cbf69b63..07e68216d7f 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -27,6 +27,7 @@ module Ci
helpers Helpers
helpers ::API::Helpers
+ helpers Gitlab::CurrentSettings
mount Builds
mount Commits
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 83ca1e6481c..0a586672807 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -47,6 +47,106 @@ module Ci
build.drop
end
end
+
+ # Authorize artifacts uploading for build - Runners only
+ #
+ # Parameters:
+ # id (required) - The ID of a build
+ # token (required) - The build authorization token
+ # filesize (optional) - the size of uploaded file
+ # Example Request:
+ # POST /builds/:id/artifacts/authorize
+ post ":id/artifacts/authorize" do
+ require_gitlab_workhorse!
+ build = Ci::Build.find_by_id(params[:id])
+ not_found! unless build
+ authenticate_build_token!(build)
+ forbidden!('build is not running') unless build.running?
+
+ if params[:filesize]
+ file_size = params[:filesize].to_i
+ file_to_large! unless file_size < max_artifacts_size
+ end
+
+ status 200
+ { TempPath: ArtifactUploader.artifacts_upload_path }
+ end
+
+ # Upload artifacts to build - Runners only
+ #
+ # Parameters:
+ # id (required) - The ID of a build
+ # token (required) - The build authorization token
+ # file (required) - The uploaded file
+ # Parameters (accelerated by GitLab Workhorse):
+ # file.path - path to locally stored body (generated by Workhorse)
+ # file.name - real filename as send in Content-Disposition
+ # file.type - real content type as send in Content-Type
+ # Headers:
+ # BUILD-TOKEN (required) - The build authorization token, the same as token
+ # Body:
+ # The file content
+ #
+ # Example Request:
+ # POST /builds/:id/artifacts
+ post ":id/artifacts" do
+ require_gitlab_workhorse!
+ build = Ci::Build.find_by_id(params[:id])
+ not_found! unless build
+ authenticate_build_token!(build)
+ forbidden!('build is not running') unless build.running?
+
+ file = uploaded_file!(:file, ArtifactUploader.artifacts_upload_path)
+ file_to_large! unless file.size < max_artifacts_size
+
+ if build.update_attributes(artifacts_file: file)
+ present build, with: Entities::Build
+ else
+ render_validation_error!(build)
+ end
+ end
+
+ # Download the artifacts file from build - Runners only
+ #
+ # Parameters:
+ # id (required) - The ID of a build
+ # token (required) - The build authorization token
+ # Headers:
+ # BUILD-TOKEN (required) - The build authorization token, the same as token
+ # Example Request:
+ # GET /builds/:id/artifacts
+ get ":id/artifacts" do
+ build = Ci::Build.find_by_id(params[:id])
+ not_found! unless build
+ authenticate_build_token!(build)
+ artifacts_file = build.artifacts_file
+
+ unless artifacts_file.file_storage?
+ return redirect_to build.artifacts_file.url
+ end
+
+ unless artifacts_file.exists?
+ not_found!
+ end
+
+ present_file!(artifacts_file.path, artifacts_file.filename)
+ end
+
+ # Remove the artifacts file from build
+ #
+ # Parameters:
+ # id (required) - The ID of a build
+ # token (required) - The build authorization token
+ # Headers:
+ # BUILD-TOKEN (required) - The build authorization token, the same as token
+ # Example Request:
+ # DELETE /builds/:id/artifacts
+ delete ":id/artifacts" do
+ build = Ci::Build.find_by_id(params[:id])
+ not_found! unless build
+ authenticate_build_token!(build)
+ build.remove_artifacts_file!
+ end
end
end
end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index b80c0b8b273..750f421872d 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -11,10 +11,16 @@ module Ci
expose :builds
end
+ class ArtifactFile < Grape::Entity
+ expose :filename, :size
+ end
+
class Build < Grape::Entity
expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
:before_sha, :allow_git_fetch, :project_name
+ expose :name, :token, :stage
+
expose :options do |model|
model.options
end
@@ -24,6 +30,7 @@ module Ci
end
expose :variables
+ expose :artifacts_file, using: ArtifactFile
end
class Runner < Grape::Entity
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index 7e4986b6af3..02502333756 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -1,6 +1,8 @@
module Ci
module API
module Helpers
+ BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN"
+ BUILD_TOKEN_PARAM = :token
UPDATE_RUNNER_EVERY = 60
def authenticate_runners!
@@ -15,6 +17,11 @@ module Ci
forbidden! unless project.valid_token?(params[:project_token])
end
+ def authenticate_build_token!(build)
+ token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s
+ forbidden! unless token && build.valid_token?(token)
+ end
+
def update_runner_last_contact
# Use a random threshold to prevent beating DB updates
contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY)
@@ -32,6 +39,10 @@ module Ci
info = attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"])
current_runner.update(info)
end
+
+ def max_artifacts_size
+ current_application_settings.max_artifacts_size.megabytes.to_i
+ end
end
end
end
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index 915a4f526a6..5ff7407c6fe 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -60,7 +60,8 @@ module Ci
class BuildTime < Chart
def collect
- commits = project.commits.joins(:builds).where("#{Ci::Build.table_name}.finished_at is NOT NULL AND #{Ci::Build.table_name}.started_at is NOT NULL").last(30)
+ commits = project.commits.last(30)
+
commits.each do |commit|
@labels << commit.short_sha
@build_times << (commit.duration / 60)
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 0f57a4f53ab..2e2209031ee 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -5,7 +5,7 @@ module Ci
DEFAULT_STAGES = %w(build test deploy)
DEFAULT_STAGE = 'test'
ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables]
- ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when]
+ ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts]
attr_reader :before_script, :image, :services, :variables, :path
@@ -77,7 +77,8 @@ module Ci
when: job[:when] || 'on_success',
options: {
image: job[:image] || @image,
- services: job[:services] || @services
+ services: job[:services] || @services,
+ artifacts: job[:artifacts]
}.compact
}
end
@@ -159,7 +160,17 @@ module Ci
raise ValidationError, "#{name} job: except parameter should be an array of strings"
end
- if job[:allow_failure] && !job[:allow_failure].in?([true, false])
+ if job[:artifacts]
+ if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked])
+ raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean"
+ end
+
+ if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths])
+ raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings"
+ end
+ end
+
+ if job[:allow_failure] && !validate_boolean(job[:allow_failure])
raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
end
@@ -182,6 +193,10 @@ module Ci
value.is_a?(String) || value.is_a?(Symbol)
end
+ def validate_boolean(value)
+ value.in?([true, false])
+ end
+
def process?(only_params, except_params, ref, tag)
if only_params.present?
return false unless matching?(only_params, ref, tag)
diff --git a/lib/file_streamer.rb b/lib/file_streamer.rb
new file mode 100644
index 00000000000..4e3c6d3c773
--- /dev/null
+++ b/lib/file_streamer.rb
@@ -0,0 +1,16 @@
+class FileStreamer #:nodoc:
+ attr_reader :to_path
+
+ def initialize(path)
+ @to_path = path
+ end
+
+ # Stream the file's contents if Rack::Sendfile isn't present.
+ def each
+ File.open(to_path, 'rb') do |file|
+ while chunk = file.read(16384)
+ yield chunk
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index cd84afa31d5..2d3e32d9539 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -25,6 +25,7 @@ module Gitlab
session_expire_delay: Settings.gitlab['session_expire_delay'],
import_sources: Settings.gitlab['import_sources'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
+ max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'],
)
end
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index e767027dc29..0a7a4118077 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -131,6 +131,22 @@ server {
return 418;
}
+ # Build artifacts should be submitted to this location
+ location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
+ client_max_body_size 0;
+ # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+ error_page 418 = @gitlab-workhorse;
+ return 418;
+ }
+
+ # Build artifacts should be submitted to this location
+ location ~ /ci/api/v1/builds/[0-9]+/artifacts {
+ client_max_body_size 0;
+ # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+ error_page 418 = @gitlab-workhorse;
+ return 418;
+ }
+
location @gitlab-workhorse {
## If you use HTTPS make sure you disable gzip compression
## to be safe against BREACH attack.
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 4d31e31f8d5..b463d5b6aa9 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -178,6 +178,22 @@ server {
return 418;
}
+ # Build artifacts should be submitted to this location
+ location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
+ client_max_body_size 0;
+ # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+ error_page 418 = @gitlab-workhorse;
+ return 418;
+ }
+
+ # Build artifacts should be submitted to this location
+ location ~ /ci/api/v1/builds/[0-9]+/artifacts {
+ client_max_body_size 0;
+ # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+ error_page 418 = @gitlab-workhorse;
+ return 418;
+ }
+
location @gitlab-workhorse {
## If you use HTTPS make sure you disable gzip compression
## to be safe against BREACH attack.
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index f20c7f71ba5..3c46bcea40e 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -12,6 +12,7 @@ namespace :gitlab do
Rake::Task["gitlab:backup:repo:create"].invoke
Rake::Task["gitlab:backup:uploads:create"].invoke
Rake::Task["gitlab:backup:builds:create"].invoke
+ Rake::Task["gitlab:backup:artifacts:create"].invoke
backup = Backup::Manager.new
backup.pack
@@ -32,6 +33,7 @@ namespace :gitlab do
Rake::Task["gitlab:backup:repo:restore"].invoke unless backup.skipped?("repositories")
Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads")
Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds")
+ Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts")
Rake::Task["gitlab:shell:setup"].invoke
backup.cleanup
@@ -113,6 +115,25 @@ namespace :gitlab do
end
end
+ namespace :artifacts do
+ task create: :environment do
+ $progress.puts "Dumping artifacts ... ".blue
+
+ if ENV["SKIP"] && ENV["SKIP"].include?("artifacts")
+ $progress.puts "[SKIPPED]".cyan
+ else
+ Backup::Artifacts.new.dump
+ $progress.puts "done".green
+ end
+ end
+
+ task restore: :environment do
+ $progress.puts "Restoring artifacts ... ".blue
+ Backup::Artifacts.new.restore
+ $progress.puts "done".green
+ end
+ end
+
def configure_cron_mode
if ENV['CRON']
# We need an object we can say 'puts' and 'print' to; let's use a
diff --git a/lib/uploaded_file.rb b/lib/uploaded_file.rb
new file mode 100644
index 00000000000..d4291f012d3
--- /dev/null
+++ b/lib/uploaded_file.rb
@@ -0,0 +1,37 @@
+require "tempfile"
+require "fileutils"
+
+# Taken from: Rack::Test::UploadedFile
+class UploadedFile
+
+ # The filename, *not* including the path, of the "uploaded" file
+ attr_reader :original_filename
+
+ # The tempfile
+ attr_reader :tempfile
+
+ # The content type of the "uploaded" file
+ attr_accessor :content_type
+
+ def initialize(path, filename, content_type = "text/plain")
+ raise "#{path} file does not exist" unless ::File.exist?(path)
+
+ @content_type = content_type
+ @original_filename = filename || ::File.basename(path)
+ @tempfile = File.new(path, 'rb')
+ end
+
+ def path
+ @tempfile.path
+ end
+
+ alias_method :local_path, :path
+
+ def method_missing(method_name, *args, &block) #:nodoc:
+ @tempfile.__send__(method_name, *args, &block)
+ end
+
+ def respond_to?(method_name, include_private = false) #:nodoc:
+ @tempfile.respond_to?(method_name, include_private) || super
+ end
+end
diff --git a/shared/artifacts/.gitkeep b/shared/artifacts/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/shared/artifacts/.gitkeep
diff --git a/shared/artifacts/tmp/cache/.gitkeep b/shared/artifacts/tmp/cache/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/shared/artifacts/tmp/cache/.gitkeep
diff --git a/shared/artifacts/tmp/uploads/.gitkeep b/shared/artifacts/tmp/uploads/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/shared/artifacts/tmp/uploads/.gitkeep
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 158e85e598f..5213ce1099f 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe "Builds" do
+ let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
before do
login_as(:user)
@commit = FactoryGirl.create :ci_commit
@@ -66,6 +68,15 @@ describe "Builds" do
it { expect(page).to have_content @commit.sha[0..7] }
it { expect(page).to have_content @commit.git_commit_message }
it { expect(page).to have_content @commit.git_author_name }
+
+ context "Download artifacts" do
+ before do
+ @build.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+ end
+
+ it { expect(page).to have_content 'Download artifacts' }
+ end
end
describe "POST /:project/builds/:id/cancel" do
@@ -90,4 +101,14 @@ describe "Builds" do
it { expect(page).to have_content 'pending' }
it { expect(page).to have_content 'Cancel' }
end
+
+ describe "GET /:project/builds/:id/download" do
+ before do
+ @build.update_attributes(artifacts_file: artifacts_file)
+ visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+ click_link 'Download artifacts'
+ end
+
+ it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
+ end
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 340924fafe7..90739cd6a28 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -19,7 +19,7 @@ describe "Commits" do
stub_ci_commit_to_return_yaml_file
end
- describe "GET /:project/commits/:sha" do
+ describe "GET /:project/commits/:sha/ci" do
before do
visit ci_status_path(@commit)
end
@@ -29,6 +29,20 @@ describe "Commits" do
it { expect(page).to have_content @commit.git_author_name }
end
+ context "Download artifacts" do
+ let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
+ before do
+ @build.update_attributes(artifacts_file: artifacts_file)
+ end
+
+ it do
+ visit ci_status_path(@commit)
+ click_on "Download artifacts"
+ expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type)
+ end
+ end
+
describe "Cancel all builds" do
it "cancels commit" do
visit ci_status_path(@commit)
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 9963f76f993..29fc2713821 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -333,6 +333,43 @@ module Ci
end
end
+ describe "Artifacts" do
+ it "returns artifacts when defined" do
+ config = YAML.dump({
+ image: "ruby:2.1",
+ services: ["mysql"],
+ before_script: ["pwd"],
+ rspec: {
+ artifacts: { paths: ["logs/", "binaries/"], untracked: true },
+ script: "rspec"
+ }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config)
+
+ expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+ expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
+ except: nil,
+ stage: "test",
+ stage_idx: 1,
+ name: :rspec,
+ only: nil,
+ commands: "pwd\nrspec",
+ tag_list: [],
+ options: {
+ image: "ruby:2.1",
+ services: ["mysql"],
+ artifacts: {
+ paths: ["logs/", "binaries/"],
+ untracked: true
+ }
+ },
+ when: "on_success",
+ allow_failure: false
+ })
+ end
+ end
+
describe "Error handling" do
it "indicates that object is invalid" do
expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
@@ -491,6 +528,20 @@ module Ci
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always")
end
+
+ it "returns errors if job artifacts:untracked is not an array of strings" do
+ config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { untracked: "string" } } })
+ expect do
+ GitlabCiYamlProcessor.new(config)
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:untracked parameter should be an boolean")
+ end
+
+ it "returns errors if job artifacts:paths is not an array of strings" do
+ config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", artifacts: { paths: "string" } } })
+ expect do
+ GitlabCiYamlProcessor.new(config)
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings")
+ end
end
end
end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 7f5abb83ac2..839b4c6b16e 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -400,4 +400,19 @@ describe Ci::Build do
end
end
end
+
+ describe :download_url do
+ subject { build.download_url }
+
+ it "should be nil if artifact doesn't exist" do
+ build.update_attributes(artifacts_file: nil)
+ is_expected.to be_nil
+ end
+
+ it 'should be nil if artifact exist' do
+ gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+ build.update_attributes(artifacts_file: gif)
+ is_expected.to_not be_nil
+ end
+ end
end
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
index b9e6dfc15a7..a28607bd240 100644
--- a/spec/requests/api/commit_status_spec.rb
+++ b/spec/requests/api/commit_status_spec.rb
@@ -18,7 +18,7 @@ describe API::API, api: true do
before do
@status1 = create(:commit_status, commit: ci_commit, status: 'running')
@status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending')
- @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running')
+ @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running', allow_failure: true)
@status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success')
@status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success')
@status6 = create(:commit_status, commit: ci_commit, status: 'success')
@@ -30,6 +30,8 @@ describe API::API, api: true do
expect(json_response).to be_an Array
expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id)
+ json_response.sort_by!{ |status| status['id'] }
+ expect(json_response.map{ |status| status['allow_failure'] }).to eq([true, false, false, false])
end
it "should return all commit statuses" do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index d26a300ed82..a9ef2fe5885 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -343,8 +343,9 @@ describe API::API, api: true do
end.to change{ user.keys.count }.by(1)
end
- it "should raise error for invalid ID" do
- expect{post api("/users/ASDF/keys", admin) }.to raise_error(ActionController::RoutingError)
+ it "should return 405 for invalid ID" do
+ post api("/users/ASDF/keys", admin)
+ expect(response.status).to eq(405)
end
end
@@ -374,9 +375,9 @@ describe API::API, api: true do
expect(json_response.first['title']).to eq(key.title)
end
- it "should return 404 for invalid ID" do
+ it "should return 405 for invalid ID" do
get api("/users/ASDF/keys", admin)
- expect(response.status).to eq(404)
+ expect(response.status).to eq(405)
end
end
end
@@ -434,7 +435,8 @@ describe API::API, api: true do
end
it "should raise error for invalid ID" do
- expect{post api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError)
+ post api("/users/ASDF/emails", admin)
+ expect(response.status).to eq(405)
end
end
@@ -465,7 +467,8 @@ describe API::API, api: true do
end
it "should raise error for invalid ID" do
- expect{put api("/users/ASDF/emails", admin) }.to raise_error(ActionController::RoutingError)
+ put api("/users/ASDF/emails", admin)
+ expect(response.status).to eq(405)
end
end
end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 88218a93e1f..7886a6feca2 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -41,7 +41,7 @@ describe Ci::API::API do
it "should return 404 error if no builds for specific runner" do
commit = FactoryGirl.create(:ci_commit, gl_project: shared_gl_project)
- FactoryGirl.create(:ci_build, commit: commit, status: 'pending' )
+ FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
post ci_api("/builds/register"), token: runner.token
@@ -50,7 +50,7 @@ describe Ci::API::API do
it "should return 404 error if no builds for shared runner" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
- FactoryGirl.create(:ci_build, commit: commit, status: 'pending' )
+ FactoryGirl.create(:ci_build, commit: commit, status: 'pending')
post ci_api("/builds/register"), token: shared_runner.token
@@ -79,7 +79,7 @@ describe Ci::API::API do
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true },
- { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
+ { "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }
])
end
@@ -122,5 +122,194 @@ describe Ci::API::API do
expect(build.reload.trace).to eq 'hello_world'
end
end
+
+ context "Artifacts" do
+ let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+ let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
+ let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
+ let(:build) { FactoryGirl.create(:ci_build, commit: commit, runner_id: runner.id) }
+ let(:authorize_url) { ci_api("/builds/#{build.id}/artifacts/authorize") }
+ let(:post_url) { ci_api("/builds/#{build.id}/artifacts") }
+ let(:delete_url) { ci_api("/builds/#{build.id}/artifacts") }
+ let(:get_url) { ci_api("/builds/#{build.id}/artifacts") }
+ let(:headers) { { "GitLab-Workhorse" => "1.0" } }
+ let(:headers_with_token) { headers.merge(Ci::API::Helpers::BUILD_TOKEN_HEADER => build.project.token) }
+
+ describe "POST /builds/:id/artifacts/authorize" do
+ context "should authorize posting artifact to running build" do
+ before do
+ build.run!
+ end
+
+ it "using token as parameter" do
+ post authorize_url, { token: build.project.token }, headers
+ expect(response.status).to eq(200)
+ expect(json_response["TempPath"]).to_not be_nil
+ end
+
+ it "using token as header" do
+ post authorize_url, {}, headers_with_token
+ expect(response.status).to eq(200)
+ expect(json_response["TempPath"]).to_not be_nil
+ end
+ end
+
+ context "should fail to post too large artifact" do
+ before do
+ build.run!
+ end
+
+ it "using token as parameter" do
+ settings = Gitlab::CurrentSettings::current_application_settings
+ settings.update_attributes(max_artifacts_size: 0)
+ post authorize_url, { token: build.project.token, filesize: 100 }, headers
+ expect(response.status).to eq(413)
+ end
+
+ it "using token as header" do
+ settings = Gitlab::CurrentSettings::current_application_settings
+ settings.update_attributes(max_artifacts_size: 0)
+ post authorize_url, { filesize: 100 }, headers_with_token
+ expect(response.status).to eq(413)
+ end
+ end
+
+ context "should get denied" do
+ it do
+ post authorize_url, { token: 'invalid', filesize: 100 }
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
+ describe "POST /builds/:id/artifacts" do
+ context "Disable sanitizer" do
+ before do
+ # by configuring this path we allow to pass temp file from any path
+ allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return('/')
+ end
+
+ context "should post artifact to running build" do
+ before do
+ build.run!
+ end
+
+ it "uses regual file post" do
+ upload_artifacts(file_upload, headers_with_token, false)
+ expect(response.status).to eq(201)
+ expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
+ end
+
+ it "uses accelerated file post" do
+ upload_artifacts(file_upload, headers_with_token, true)
+ expect(response.status).to eq(201)
+ expect(json_response["artifacts_file"]["filename"]).to eq(file_upload.original_filename)
+ end
+
+ it "updates artifact" do
+ upload_artifacts(file_upload, headers_with_token)
+ upload_artifacts(file_upload2, headers_with_token)
+ expect(response.status).to eq(201)
+ expect(json_response["artifacts_file"]["filename"]).to eq(file_upload2.original_filename)
+ end
+ end
+
+ context "should fail to post too large artifact" do
+ before do
+ build.run!
+ end
+
+ it do
+ settings = Gitlab::CurrentSettings::current_application_settings
+ settings.update_attributes(max_artifacts_size: 0)
+ upload_artifacts(file_upload, headers_with_token)
+ expect(response.status).to eq(413)
+ end
+ end
+
+ context "should fail to post artifacts without file" do
+ before do
+ build.run!
+ end
+
+ it do
+ post post_url, {}, headers_with_token
+ expect(response.status).to eq(400)
+ end
+ end
+
+ context "should fail to post artifacts without GitLab-Workhorse" do
+ before do
+ build.run!
+ end
+
+ it do
+ post post_url, { token: build.project.token }, {}
+ expect(response.status).to eq(403)
+ end
+ end
+ end
+
+ context "should fail to post artifacts for outside of tmp path" do
+ before do
+ # by configuring this path we allow to pass file from @tmpdir only
+ # but all temporary files are stored in system tmp directory
+ @tmpdir = Dir.mktmpdir
+ allow(ArtifactUploader).to receive(:artifacts_upload_path).and_return(@tmpdir)
+ build.run!
+ end
+
+ after do
+ FileUtils.remove_entry @tmpdir
+ end
+
+ it do
+ upload_artifacts(file_upload, headers_with_token)
+ expect(response.status).to eq(400)
+ end
+ end
+
+ def upload_artifacts(file, headers = {}, accelerated = true)
+ if accelerated
+ post post_url, {
+ 'file.path' => file.path,
+ 'file.name' => file.original_filename
+ }, headers
+ else
+ post post_url, { file: file }, headers
+ end
+ end
+ end
+
+ describe "DELETE /builds/:id/artifacts" do
+ before do
+ build.run!
+ post delete_url, token: build.project.token, file: file_upload
+ end
+
+ it "should delete artifact build" do
+ build.success
+ delete delete_url, token: build.project.token
+ expect(response.status).to eq(200)
+ end
+ end
+
+ describe "GET /builds/:id/artifacts" do
+ before do
+ build.run!
+ end
+
+ it "should download artifact" do
+ build.update_attributes(artifacts_file: file_upload)
+ get get_url, token: build.project.token
+ expect(response.status).to eq(200)
+ end
+
+ it "should fail to download if no artifact uploaded" do
+ get get_url, token: build.project.token
+ expect(response.status).to eq(404)
+ end
+ end
+ end
end
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 386ac9c8372..06559c3925d 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do
end
def reenable_backup_sub_tasks
- %w{db repo uploads builds}.each do |subtask|
+ %w{db repo uploads builds artifacts}.each do |subtask|
Rake::Task["gitlab:backup:#{subtask}:create"].reenable
end
end
@@ -56,6 +56,7 @@ describe 'gitlab:app namespace rake task' do
expect(Rake::Task["gitlab:backup:repo:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke)
+ expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive(:invoke)
expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke)
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
@@ -113,19 +114,20 @@ describe 'gitlab:app namespace rake task' do
it 'should set correct permissions on the tar contents' do
tar_contents, exit_status = Gitlab::Popen.popen(
- %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz}
+ %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz}
)
expect(exit_status).to eq(0)
expect(tar_contents).to match('db/')
expect(tar_contents).to match('uploads.tar.gz')
expect(tar_contents).to match('repositories/')
expect(tar_contents).to match('builds.tar.gz')
- expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz)\/$/)
+ expect(tar_contents).to match('artifacts.tar.gz')
+ expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/)
end
it 'should delete temp directories' do
temp_dirs = Dir.glob(
- File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds}')
+ File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}')
)
expect(temp_dirs).to be_empty
@@ -161,12 +163,13 @@ describe 'gitlab:app namespace rake task' do
it "does not contain skipped item" do
tar_contents, _exit_status = Gitlab::Popen.popen(
- %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz}
+ %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz}
)
expect(tar_contents).to match('db/')
expect(tar_contents).to match('uploads.tar.gz')
expect(tar_contents).to match('builds.tar.gz')
+ expect(tar_contents).to match('artifacts.tar.gz')
expect(tar_contents).not_to match('repositories/')
end
@@ -178,6 +181,7 @@ describe 'gitlab:app namespace rake task' do
expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke
expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke
expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke
+ expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke
expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end