summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--app/assets/javascripts/importer_status.js.coffee39
-rw-r--r--app/assets/stylesheets/framework/timeline.scss3
-rw-r--r--app/assets/stylesheets/pages/import.scss21
-rw-r--r--app/assets/stylesheets/pages/notes.scss17
-rw-r--r--app/assets/stylesheets/print.scss44
-rw-r--r--app/models/ci/build.rb18
-rw-r--r--app/views/import/base/create.js.haml4
-rw-r--r--app/views/import/bitbucket/status.html.haml20
-rw-r--r--app/views/import/fogbugz/status.html.haml15
-rw-r--r--app/views/import/github/status.html.haml15
-rw-r--r--app/views/import/gitlab/status.html.haml15
-rw-r--r--app/views/import/gitorious/status.html.haml15
-rw-r--r--app/views/import/google_code/status.html.haml19
-rw-r--r--app/views/projects/issues/update.js.haml3
-rw-r--r--app/views/projects/merge_requests/update.js.haml3
-rw-r--r--app/views/projects/notes/_discussion.html.haml2
-rw-r--r--app/views/projects/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/projects/notes/discussions/_active.html.haml6
-rw-r--r--app/views/projects/notes/discussions/_commit.html.haml9
-rw-r--r--app/views/projects/notes/discussions/_outdated.html.haml6
-rw-r--r--doc/ci/variables/README.md22
-rw-r--r--doc/ci/yaml/README.md16
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb40
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb88
-rw-r--r--spec/models/build_spec.rb16
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml17
27 files changed, 363 insertions, 114 deletions
diff --git a/CHANGELOG b/CHANGELOG
index eb03305adfa..dd94ada123f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,6 +3,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.7.0 (unreleased)
- Method instrumentation now uses Module#prepend instead of aliasing methods
- Repository.clean_old_archives is now instrumented
+ - Add support for environment variables on a job level in CI configuration file
- The Projects::HousekeepingService class has extra instrumentation
- All service classes (those residing in app/services) are now instrumented
- Developers can now add custom tags to transactions
@@ -80,6 +81,7 @@ v 8.7.0 (unreleased)
- Add encrypted credentials for imported projects and migrate old ones
- Author and participants are displayed first on users autocompletion
- Show number sign on external issue reference text (Florent Baldino)
+ - Updated print style for issues
v 8.6.6
- Expire the exists cache before deletion to ensure project dir actually exists (Stan Hu). !3413
diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee
index be8d225e73b..b0edc895649 100644
--- a/app/assets/javascripts/importer_status.js.coffee
+++ b/app/assets/javascripts/importer_status.js.coffee
@@ -4,18 +4,33 @@ class @ImporterStatus
this.setAutoUpdate()
initStatusPage: ->
- $(".js-add-to-import").click (event) =>
- new_namespace = null
- tr = $(event.currentTarget).closest("tr")
- id = tr.attr("id").replace("repo_", "")
- if tr.find(".import-target input").length > 0
- new_namespace = tr.find(".import-target input").prop("value")
- tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name"))
- $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
-
- $(".js-import-all").click (event) =>
- $(".js-add-to-import").each ->
- $(this).click()
+ $('.js-add-to-import')
+ .off 'click'
+ .on 'click', (e) =>
+ new_namespace = null
+ $btn = $(e.currentTarget)
+ $tr = $btn.closest('tr')
+ id = $tr.attr('id').replace('repo_', '')
+ if $tr.find('.import-target input').length > 0
+ new_namespace = $tr.find('.import-target input').prop('value')
+ $tr.find('.import-target').empty().append("#{new_namespace} / #{$tr.find('.import-target').data('project_name')}")
+
+ $btn
+ .disable()
+ .addClass 'is-loading'
+
+ $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
+
+ $('.js-import-all')
+ .off 'click'
+ .on 'click', (e) ->
+ $btn = $(@)
+ $btn
+ .disable()
+ .addClass 'is-loading'
+
+ $('.js-add-to-import').each ->
+ $(this).trigger('click')
setAutoUpdate: ->
setInterval (=>
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index b91f2f6f898..f0ec250de2b 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -39,8 +39,7 @@
.diff-file {
border: 1px solid $border-color;
border-bottom: none;
- margin-left: 0;
- margin-right: 0;
+ margin: 0;
}
}
diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss
index 6a99cd9cb94..84cc35239f9 100644
--- a/app/assets/stylesheets/pages/import.scss
+++ b/app/assets/stylesheets/pages/import.scss
@@ -16,3 +16,24 @@ i.icon-gitorious-big {
width: 18px;
height: 18px;
}
+
+.import-jobs-from-col,
+.import-jobs-to-col {
+ width: 40%;
+}
+
+.import-jobs-status-col {
+ width: 20%;
+}
+
+.btn-import {
+ .loading-icon {
+ display: none;
+ }
+
+ &.is-loading {
+ .loading-icon {
+ display: inline-block;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index ce44f5aa13b..a0fbf7d67c5 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -198,6 +198,12 @@ ul.notes {
color: $notes-light-color;
}
+.discussion-headline-light {
+ a {
+ color: $gl-link-color;
+ }
+}
+
/**
* Actions for Discussions/Notes
*/
@@ -209,6 +215,17 @@ ul.notes {
color: $notes-action-color;
}
+.discussion-actions {
+ @media (max-width: $screen-sm-max) {
+ float: none;
+ margin-left: 0;
+
+ .note-action-button {
+ margin-left: 0;
+ }
+ }
+}
+
.note-action-button,
.discussion-action-button {
display: inline-block;
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index 1be0551ad3b..a30b6492572 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -1,17 +1,37 @@
-/* Generic print styles */
-header, nav, nav.main-nav, nav.navbar-collapse, nav.navbar-collapse.collapse {display: none!important;}
-.profiler-results {display: none;}
-
-/* Styles targeted specifically at printing files */
-.tree-ref-holder, .tree-holder .breadcrumb, .blob-commit-info {display: none;}
-.file-title {display: none;}
-.file-holder {border: none;}
-
.wiki h1, .wiki h2, .wiki h3, .wiki h4, .wiki h5, .wiki h6 {margin-top: 17px; }
.wiki h1 {font-size: 30px;}
.wiki h2 {font-size: 22px;}
.wiki h3 {font-size: 18px; font-weight: bold; }
-.sidebar-wrapper { display: none; }
-.nav { display: none; }
-.btn { display: none; }
+header,
+nav,
+nav.main-nav,
+nav.navbar-collapse,
+nav.navbar-collapse.collapse,
+.profiler-results,
+.tree-ref-holder,
+.tree-holder .breadcrumb,
+.blob-commit-info,
+.file-title,
+.file-holder,
+.sidebar-wrapper,
+.nav,
+.btn,
+ul.notes-form,
+.merge-request-ci-status .ci-status-link:after,
+.issuable-gutter-toggle,
+.gutter-toggle,
+.issuable-details .content-block-small,
+.edit-link,
+.note-action-button {
+ display: none!important;
+}
+
+.page-gutter {
+ padding-top: 0;
+ padding-left: 0;
+}
+
+.right-sidebar {
+ top: 0;
+}
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 7d33838044b..85ef0523b31 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -365,11 +365,23 @@ module Ci
self.update(erased_by: user, erased_at: Time.now)
end
- private
-
def yaml_variables
+ global_yaml_variables + job_yaml_variables
+ end
+
+ def global_yaml_variables
+ if commit.config_processor
+ commit.config_processor.global_variables.map do |key, value|
+ { key: key, value: value, public: true }
+ end
+ else
+ []
+ end
+ end
+
+ def job_yaml_variables
if commit.config_processor
- commit.config_processor.variables.map do |key, value|
+ commit.config_processor.job_variables(name).map do |key, value|
{ key: key, value: value, public: true }
end
else
diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml
index d8af0295b2d..dfebf7768d9 100644
--- a/app/views/import/base/create.js.haml
+++ b/app/views/import/base/create.js.haml
@@ -20,10 +20,10 @@
job.attr("id", "project_#{@project.id}")
target_field = job.find(".import-target")
target_field.empty()
- target_field.append('<strong>#{link_to @project.path_with_namespace, namespace_project_path(@project.namespace, @project)}</strong>')
+ target_field.append('#{link_to @project.path_with_namespace, namespace_project_path(@project.namespace, @project)}')
$("table.import-jobs tbody").prepend(job)
job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started")
- else
:plain
job = $("tr#repo_#{@repo_id}")
- job.find(".import-actions").html("<i class='fa fa-exclamation-circle'> Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}</i>")
+ job.find(".import-actions").html("<i class='fa fa-exclamation-circle'></i> Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}")
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index aec2e836c9f..6e993e58f0d 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -10,13 +10,19 @@
%hr
%p
- if @incompatible_repos.any?
- = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all compatible projects
+ = icon("spinner spin", class: "loading-icon")
- else
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-success js-import-all" do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From Bitbucket
@@ -28,7 +34,7 @@
%td
= link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank"
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -47,7 +53,9 @@
%td.import-target
= "#{repo["owner"]}/#{repo["slug"]}"
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn btn-import js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
%tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
%td
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index 6ee16c8be4b..d3d3c595c17 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -13,10 +13,15 @@
how FogBugz email addresses and usernames are imported into GitLab.
%hr
%p
- = button_tag 'Import all projects', class: 'btn btn-success js-import-all'
+ = button_tag class: 'btn btn-import btn-success js-import-all' do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From FogBugz
@@ -28,7 +33,7 @@
%td
= project.import_source
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -47,7 +52,9 @@
%td.import-target
= "#{current_user.username}/#{repo.name}"
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn btn-import js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
: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 1416ee5bd5a..9639da4cb58 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -8,10 +8,15 @@
Select projects you want to import.
%hr
%p
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From GitHub
@@ -23,7 +28,7 @@
%td
= link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank"
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -42,7 +47,9 @@
%td.import-target
= repo.full_name
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn btn-import js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
: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 911a55eb85d..e3a356b5379 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -8,10 +8,15 @@
Select projects you want to import.
%hr
%p
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From GitLab.com
@@ -23,7 +28,7 @@
%td
= link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -42,7 +47,9 @@
%td.import-target
= repo["path_with_namespace"]
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
: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 6b0fa1edf8c..267eee4f262 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -8,10 +8,15 @@
Select projects you want to import.
%hr
%p
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From Gitorious.org
@@ -23,7 +28,7 @@
%td
= link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank"
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -42,7 +47,9 @@
%td.import-target
= repo.full_name
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn btn-import js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
: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 175ef6921cd..5ada6b174eb 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -14,12 +14,19 @@
%hr
%p
- if @incompatible_repos.any?
- = button_tag 'Import all compatible projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all compatible projects
+ = icon("spinner spin", class: "loading-icon")
- else
- = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ Import all projects
+ = icon("spinner spin", class: "loading-icon")
-.table-holder
+.table-responsive
%table.table.import-jobs
+ %colgroup.import-jobs-from-col
+ %colgroup.import-jobs-to-col
+ %colgroup.import-jobs-status-col
%thead
%tr
%th From Google Code
@@ -31,7 +38,7 @@
%td
= link_to project.import_source, "https://code.google.com/p/#{project.import_source}", target: "_blank"
%td
- %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
%td.job-status
- if project.import_status == 'finished'
%span
@@ -50,7 +57,9 @@
%td.import-target
= "#{current_user.username}/#{repo.name}"
%td.import-actions.job-status
- = button_tag "Import", class: "btn js-add-to-import"
+ = button_tag class: "btn btn-import js-add-to-import" do
+ Import
+ = icon("spinner spin", class: "loading-icon")
- @incompatible_repos.each do |repo|
%tr{id: "repo_#{repo.id}"}
%td
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index 986d8c220db..e69de29bb2d 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -1,3 +0,0 @@
-$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @issue)}";
-$('aside.right-sidebar').effect('highlight');
-new IssuableContext();
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index 9cce5660e1c..e69de29bb2d 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -1,3 +0,0 @@
-$('aside.right-sidebar')[0].outerHTML = "#{escape_javascript(render 'shared/issuable/sidebar', issuable: @merge_request)}";
-$('aside.right-sidebar').effect('highlight');
-new IssuableContext();
diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml
index b8068835b3a..572b00a38c7 100644
--- a/app/views/projects/notes/_discussion.html.haml
+++ b/app/views/projects/notes/_discussion.html.haml
@@ -1,5 +1,5 @@
- note = discussion_notes.first
-.timeline-entry
+%li.note.note-discussion.timeline-entry
.timeline-entry-inner
.timeline-icon
= link_to user_path(note.author) do
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index cc42aab5c52..1c39ce897a3 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -1,6 +1,6 @@
%ul#notes-list.notes.main-notes-list.timeline
= render "projects/notes/notes"
-%ul.notes.timeline
+%ul.notes.notes-form.timeline
%li.timeline-entry
- if can? current_user, :create_note, @project
.timeline-icon.hidden-xs.hidden-sm
diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml
index cd8a5f0bd02..0ea8862a684 100644
--- a/app/views/projects/notes/discussions/_active.html.haml
+++ b/app/views/projects/notes/discussions/_active.html.haml
@@ -6,15 +6,11 @@
= "#{note.author.to_reference} started a discussion"
= link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
on the diff
+ = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "discussion-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
- .last-update.hide.js-toggle-content
- - last_note = discussion_notes.last
- last updated by
- = link_to_member(@project, last_note.author, avatar: false)
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml
index 46f2ba4bbcf..2a2ead58eeb 100644
--- a/app/views/projects/notes/discussions/_commit.html.haml
+++ b/app/views/projects/notes/discussions/_commit.html.haml
@@ -8,21 +8,18 @@
= "#{note.author.to_reference} started a discussion on #{commit_description}"
- if commit
= link_to(commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
+ = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
- .last-update.hide.js-toggle-content
- - last_note = discussion_notes.last
- last updated by
- = link_to_member(@project, last_note.author, avatar: false)
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
- else
.panel.panel-default
.notes{ data: { discussion_id: discussion_notes.first.discussion_id } }
- = render discussion_notes
+ %ul.notes.timeline
+ = render discussion_notes
.discussion-reply-holder
= link_to_reply_diff(discussion_notes.first)
diff --git a/app/views/projects/notes/discussions/_outdated.html.haml b/app/views/projects/notes/discussions/_outdated.html.haml
index f8e000b424f..45141bcd1df 100644
--- a/app/views/projects/notes/discussions/_outdated.html.haml
+++ b/app/views/projects/notes/discussions/_outdated.html.haml
@@ -5,14 +5,10 @@
.inline.discussion-headline-light
= "#{note.author.to_reference} started a discussion"
on the outdated diff
+ = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-down
Show/hide discussion
- .last-update.hide.js-toggle-content
- - last_note = discussion_notes.last
- last updated by
- = link_to_member(@project, last_note.author, avatar: false)
- #{time_ago_with_tooltip(last_note.updated_at, placement: 'bottom', html_class: 'discussion_updated_ago')}
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index b0e53cbc261..70fb81492d6 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -1,17 +1,20 @@
## Variables
+
When receiving a build from GitLab CI, the runner prepares the build environment.
It starts by setting a list of **predefined variables** (Environment Variables) and a list of **user-defined variables**
The variables can be overwritten. They take precedence over each other in this order:
+1. Trigger variables
1. Secure variables
-1. YAML-defined variables
+1. YAML-defined job-level variables
+1. YAML-defined global variables
1. Predefined variables
For example, if you define:
-1. API_TOKEN=SECURE as Secure Variable
-1. API_TOKEN=YAML as YAML-defined variable
+1. `API_TOKEN=SECURE` as Secure Variable
+1. `API_TOKEN=YAML` as YAML-defined variable
-The API_TOKEN will take the Secure Variable value: `SECURE`.
+The `API_TOKEN` will take the Secure Variable value: `SECURE`.
### Predefined variables (Environment Variables)
@@ -70,15 +73,20 @@ These variables can be later used in all executed commands and scripts.
The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them.
+Variables can be defined at a global level, but also at a job level.
+
More information about Docker integration can be found in [Using Docker Images](../docker/using_docker_images.md).
### User-defined variables (Secure Variables)
**This feature requires GitLab Runner 0.4.0 or higher**
-GitLab CI allows you to define per-project **Secure Variables** that are set in build environment.
+GitLab CI allows you to define per-project **Secure Variables** that are set in
+the build environment.
The secure variables are stored out of the repository (the `.gitlab-ci.yml`).
-The variables are securely passed to GitLab Runner and are available in build environment.
-It's desired method to use them for storing passwords, secret keys or whatever you want.
+The variables are securely passed to GitLab Runner and are available in the
+build environment.
+It's desired method to use them for storing passwords, secret keys or whatever
+you want.
**The value of the variable can be shown in build log if explicitly asked to do so.**
If your project is public or internal you can make the builds private.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 697445bceea..bd3ec9e4688 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -24,6 +24,7 @@ If you want a quick introduction to GitLab CI, follow our
- [Jobs](#jobs)
- [script](#script)
- [stage](#stage)
+ - [job variables](#job-variables)
- [only and except](#only-and-except)
- [tags](#tags)
- [when](#when)
@@ -187,6 +188,8 @@ These variables can be later used in all executed commands and scripts.
The YAML-defined variables are also set to all created service containers,
thus allowing to fine tune them.
+Variables can be also defined on [job level](#job-variables).
+
### cache
>**Note:**
@@ -337,6 +340,7 @@ job_name:
| services | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
| stage | no | Defines a build stage (default: `test`) |
| type | no | Alias for `stage` |
+| variables | no | Define build variables on a job level |
| only | no | Defines a list of git refs for which build is created |
| except | no | Defines a list of git refs for which build is not created |
| tags | no | Defines a list of tags which are used to select Runner |
@@ -427,6 +431,18 @@ job:
The above example will run `job` for all branches on `gitlab-org/gitlab-ce`,
except master.
+### job variables
+
+It is possible to define build variables using a `variables` keyword on a job
+level. It works basically the same way as its global-level equivalent but
+allows you to define job-specific build variables.
+
+When the `variables` keyword is used on a job level, it overrides global YAML
+build variables and predefined variables.
+
+Build variables priority is defined in
+[variables documentation](../variables/README.md).
+
### tags
`tags` is used to select specific Runners from the list of all Runners that are
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index f1d6f2be1e0..2a228845ec0 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -7,9 +7,9 @@ module Ci
ALLOWED_YAML_KEYS = [:before_script, :after_script, :image, :services, :types, :stages, :variables, :cache]
ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services,
:allow_failure, :type, :stage, :when, :artifacts, :cache,
- :dependencies]
+ :dependencies, :variables]
- attr_reader :before_script, :after_script, :image, :services, :variables, :path, :cache
+ attr_reader :before_script, :after_script, :image, :services, :path, :cache
def initialize(config, path = nil)
@config = YAML.safe_load(config, [Symbol], [], true)
@@ -40,6 +40,17 @@ module Ci
@stages || DEFAULT_STAGES
end
+ def global_variables
+ @variables
+ end
+
+ def job_variables(name)
+ job = @jobs[name.to_sym]
+ return [] unless job
+
+ job.fetch(:variables, [])
+ end
+
private
def initial_parsing
@@ -144,7 +155,7 @@ module Ci
end
unless @variables.nil? || validate_variables(@variables)
- raise ValidationError, "variables should be a map of key-valued strings"
+ raise ValidationError, "variables should be a map of key-value strings"
end
validate_global_cache! if @cache
@@ -159,9 +170,25 @@ module Ci
raise ValidationError, "cache:untracked parameter should be an boolean"
end
+<<<<<<< HEAD
if @cache[:paths] && !validate_array_of_strings(@cache[:paths])
raise ValidationError, "cache:paths parameter should be an array of strings"
end
+=======
+ true
+ end
+
+ def validate_job!(name, job)
+ validate_job_name!(name)
+ validate_job_keys!(name, job)
+ validate_job_types!(name, job)
+
+ validate_job_stage!(name, job) if job[:stage]
+ validate_job_variables!(name, job) if job[:variables]
+ validate_job_cache!(name, job) if job[:cache]
+ validate_job_artifacts!(name, job) if job[:artifacts]
+ validate_job_dependencies!(name, job) if job[:dependencies]
+>>>>>>> origin/master
end
def validate_job_name!(name)
@@ -218,6 +245,13 @@ module Ci
end
end
+ def validate_job_variables!(name, job)
+ unless validate_variables(job[:variables])
+ raise ValidationError,
+ "#{name} job: variables should be a map of key-value strings"
+ end
+ end
+
def validate_job_cache!(name, job)
if job[:cache][:key] && !validate_string(job[:cache][:key])
raise ValidationError, "#{name} job: cache:key parameter should be a string"
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index b94fec2ddfd..d317870f496 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -367,20 +367,76 @@ module Ci
end
end
- describe "Variables" do
- it "returns variables when defined" do
- variables = {
- var1: "value1",
- var2: "value2",
- }
- config = YAML.dump({
- variables: variables,
- before_script: ["pwd"],
- rspec: { script: "rspec" }
- })
+ describe 'Variables' do
+ context 'when global variables are defined' do
+ it 'returns global variables' do
+ variables = {
+ VAR1: 'value1',
+ VAR2: 'value2',
+ }
- config_processor = GitlabCiYamlProcessor.new(config, path)
- expect(config_processor.variables).to eq(variables)
+ config = YAML.dump({
+ variables: variables,
+ before_script: ['pwd'],
+ rspec: { script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.global_variables).to eq(variables)
+ end
+ end
+
+ context 'when job variables are defined' do
+ context 'when syntax is correct' do
+ it 'returns job variables' do
+ variables = {
+ KEY1: 'value1',
+ SOME_KEY_2: 'value2'
+ }
+
+ config = YAML.dump(
+ { before_script: ['pwd'],
+ rspec: {
+ variables: variables,
+ script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.job_variables(:rspec)).to eq variables
+ end
+ end
+
+ context 'when syntax is incorrect' do
+ it 'raises error' do
+ variables = [:KEY1, 'value1', :KEY2, 'value2']
+
+ config = YAML.dump(
+ { before_script: ['pwd'],
+ rspec: {
+ variables: variables,
+ script: 'rspec' }
+ })
+
+ expect { GitlabCiYamlProcessor.new(config, path) }
+ .to raise_error(GitlabCiYamlProcessor::ValidationError,
+ /job: variables should be a map/)
+ end
+ end
+ end
+
+ context 'when job variables are not defined' do
+ it 'returns empty array' do
+ config = YAML.dump({
+ before_script: ['pwd'],
+ rspec: { script: 'rspec' }
+ })
+
+ config_processor = GitlabCiYamlProcessor.new(config, path)
+
+ expect(config_processor.job_variables(:rspec)).to eq []
+ end
end
end
@@ -759,14 +815,14 @@ EOT
config = YAML.dump({ variables: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
end
- it "returns errors if variables is not a map of key-valued strings" do
+ it "returns errors if variables is not a map of key-value strings" do
config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
- end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
+ end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings")
end
it "returns errors if job when is not on_success, on_failure or always" do
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index b7457808040..b5d356aa066 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -238,6 +238,22 @@ describe Ci::Build, models: true do
it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
end
+
+ context 'when job variables are defined' do
+ ##
+ # Job-level variables are defined in gitlab_ci.yml fixture
+ #
+ context 'when job variables are unique' do
+ let(:build) { create(:ci_build, name: 'staging') }
+
+ it 'includes job variables' do
+ expect(subject).to include(
+ { key: :KEY1, value: 'value1', public: true },
+ { key: :KEY2, value: 'value2', public: true }
+ )
+ end
+ end
+ end
end
end
end
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index a5b256bd3ec..e55a61b2b94 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -4,7 +4,7 @@ services:
before_script:
- gem install bundler
- - bundle install
+ - bundle install
- bundle exec rake db:create
variables:
@@ -17,7 +17,7 @@ types:
rspec:
script: "rake spec"
- tags:
+ tags:
- ruby
- postgres
only:
@@ -26,27 +26,32 @@ rspec:
spinach:
script: "rake spinach"
allow_failure: true
- tags:
+ tags:
- ruby
- mysql
except:
- tags
staging:
+ variables:
+ KEY1: value1
+ KEY2: value2
script: "cap deploy stating"
type: deploy
- tags:
+ tags:
- ruby
- mysql
except:
- stable
production:
+ variables:
+ DB_NAME: mysql
type: deploy
- script:
+ script:
- cap deploy production
- cap notify
- tags:
+ tags:
- ruby
- mysql
only: