Commit f2885bed authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'master' into fix-personal-snippet-access-workflow

parents 48c3bfe5 482a1708
......@@ -13,11 +13,20 @@ 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
- Extend yml syntax for only and except to support specifying repository path
- Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
- Remove deprecated CI events from project settings page
- Use issue editor as cross reference comment author when issue is edited with a new mention.
- Improve personal snippet access workflow
- [API] Add ability to fetch the commit ID of the last commit that actually touched a file
- Add "New file" link to dropdown on project page
- Include commit logs in project search
- Add "added", "modified" and "removed" properties to commit object in webhook
- Rename "Back to" links to "Go to" because its not always a case it point to place user come from
v 8.1.3
- Spread out runner contacted_at updates
- New design for user profile page
v 8.1.1
- Fix cloning Wiki repositories via HTTP (Stan Hu)
......@@ -34,6 +43,7 @@ v 8.1.0
- 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)
......
......@@ -25,7 +25,7 @@ class @Calendar
30
]
legendCellPadding: 3
cellSize: $('.user-calendar').width() / 80
cellSize: $('.user-calendar').width() / 76
onClick: (date, count) ->
formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
$.ajax
......
......@@ -100,7 +100,7 @@
}
.cover-desc {
padding: 0 $gl-padding;
padding: 0 $gl-padding 3px;
color: $gl-text-color;
}
......
......@@ -180,3 +180,7 @@
}
}
}
.btn-clipboard {
border: none;
}
......@@ -387,6 +387,16 @@ table {
}
}
.center-middle-menu {
@include nav-menu;
text-align: center;
margin: -$gl-padding;
height: auto;
margin-top: 0;
margin-bottom: 0;
border-bottom: 1px solid $border-color;
}
.dropzone .dz-preview .dz-progress {
border-color: $border-color !important;
}
......
......@@ -33,6 +33,8 @@
}
li.commit {
list-style: none;
.commit-row-title {
font-size: $list-font-size;
line-height: 20px;
......
......@@ -53,3 +53,29 @@
float: right;
font-size: 12px;
}
.profile-link-holder {
display: inline;
&:after {
content: "\00B7";
padding: 0px 6px;
font-weight: bold;
}
&:last-child {
&:after {
content: "";
padding: 0;
}
}
a {
color: $blue-dark;
text-decoration: none;
}
}
.cal-heatmap-container {
margin: 0 auto;
}
class ProjectsController < ApplicationController
include ExtractsPath
prepend_before_filter :render_go_import, only: [:show]
prepend_before_action :render_go_import, only: [:show]
skip_before_action :authenticate_user!, only: [:show, :activity]
before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create]
......
......@@ -23,8 +23,8 @@ class SearchController < ApplicationController
@search_results =
if @project
unless %w(blobs notes issues merge_requests milestones wiki_blobs).
include?(@scope)
unless %w(blobs notes issues merge_requests milestones wiki_blobs
commits).include?(@scope)
@scope = 'blobs'
end
......
......@@ -187,7 +187,7 @@ module Ci
end
def config_processor
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
rescue Ci::GitlabCiYamlProcessor::ValidationError => e
save_yaml_error(e.message)
nil
......
......@@ -87,6 +87,15 @@ class Repository
commits
end
def find_commits_by_message(query)
# Limited to 1000 commits for now, could be parameterized?
args = %W(git log --pretty=%H --max-count 1000 --grep=#{query})
git_log_results = Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:chomp)
commits = git_log_results.map { |c| commit(c) }
commits
end
def find_branch(name)
branches.find { |branch| branch.name == name }
end
......
......@@ -11,6 +11,8 @@
= hidden_field_tag :scope, 'merge_requests'
- elsif current_controller?(:wikis)
= hidden_field_tag :scope, 'wiki_blobs'
- elsif current_controller?(:commits)
= hidden_field_tag :scope, 'commits'
- else
= hidden_field_tag :search_code, true
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
= link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to dashboard
Go to dashboard
%li.separate-item
......
%ul.nav.nav-sidebar
= nav_link do
= link_to group_path(@group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do
= link_to group_path(@group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to group
Go to group
%li.separate-item
......
%ul.nav.nav-sidebar
= nav_link do
= link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
= link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to dashboard
Go to dashboard
%li.separate-item
......
%ul.nav.nav-sidebar
- if @project.group
= nav_link do
= link_to group_path(@project.group), title: 'Back to group', data: {placement: 'right'}, class: 'back-link' do
= link_to group_path(@project.group), title: 'Go to group', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to group
Go to group
- else
= nav_link do
= link_to root_path, title: 'Back to dashboard', data: {placement: 'right'}, class: 'back-link' do
= link_to root_path, title: 'Go to dashboard', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to dashboard
Go to dashboard
%li.separate-item
......
%ul.nav.nav-sidebar
= nav_link do
= link_to project_path(@project), title: 'Back to project', data: {placement: 'right'}, class: 'back-link' do
= link_to project_path(@project), title: 'Go to project', data: {placement: 'right'}, class: 'back-link' do
= icon('caret-square-o-left fw')
%span
Back to project
Go to project
%li.separate-item
......
......@@ -20,6 +20,10 @@
New snippet
- if can?(current_user, :push_code, @project)
%li.divider
%li
= link_to namespace_project_new_blob_path(@project.namespace, @project, @project.default_branch || 'master'), title: 'New file' do
= icon('file fw')
New file
%li
= link_to new_namespace_project_branch_path(@project.namespace, @project) do
= icon('code-fork fw')
......
......@@ -42,6 +42,13 @@
Wiki
%span.badge
= @search_results.wiki_blobs_count
%li{class: ("active" if @scope == 'commits')}
= link_to search_filter_path(scope: 'commits') do
= icon('history fw')
%span
Commits
%span.badge
= @search_results.commits_count
- elsif @show_snippets
%li{class: ("active" if @scope == 'snippet_blobs')}
......
.search-result-row
= render 'projects/commits/commit', project: @project, commit: commit
- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
.panel.panel-default.contributed-projects
.panel-heading Projects contributed to
= render 'shared/projects/list',
projects: contributed_projects.sort_by(&:star_count).reverse,
projects_limit: 5, stars: true, avatar: false
- if local_assigns.has_key?(:projects) && projects.present?
.panel.panel-default
.panel-heading Personal projects
= render 'shared/projects/list',
projects: projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: false
......@@ -24,21 +24,26 @@
.cover-desc
- unless @user.public_email.blank?
.profile-link-holder
= link_to @user.public_email, "mailto:#{@user.public_email}"
- unless @user.skype.blank?
&middot;
= link_to "Skype", "skype:#{@user.skype}"
.profile-link-holder
= link_to "skype:#{@user.skype}", title: "Skype" do
= icon('skype')
- unless @user.linkedin.blank?
&middot;
= link_to "LinkedIn", "http://www.linkedin.com/in/#{@user.linkedin}"
.profile-link-holder
= link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
= icon('linkedin-square')
- unless @user.twitter.blank?
&middot;
= link_to "Twitter", "http://www.twitter.com/#{@user.twitter}"
.profile-link-holder
= link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do
= icon('twitter-square')
- unless @user.website_url.blank?
&middot;
.profile-link-holder
= link_to @user.short_website_url, @user.full_website_url
- unless @user.location.blank?
&middot;
.profile-link-holder
= icon('map-marker')
= @user.location
......@@ -47,7 +52,7 @@
= link_to profile_path, class: 'btn btn-gray' do
= icon('pencil')
- elsif current_user
.report-abuse
%span.report-abuse
- if @user.abuse_report
%button.btn.btn-danger{ title: 'Already reported for abuse',
data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
......@@ -56,6 +61,10 @@
= link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray',
title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
= icon('exclamation-circle')
- if current_user
&nbsp;
= link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
= icon('rss')
.gray-content-block.second-block
.user-calendar
......@@ -64,27 +73,47 @@
.user-calendar-activities
.row.prepend-top-20
%section.col-md-7
%ul.nav.center-middle-menu
%li.active
= link_to "#activity", 'data-toggle' => 'tab' do
Activity
- if @groups.any?
.prepend-top-20
%h4 Groups
= render 'groups', groups: @groups
%hr
%h4
User Activity
- if current_user
%span.rss-icon.pull-right
= link_to user_path(@user, :atom, { private_token: current_user.private_token }) do
%strong
%i.fa.fa-rss
%li
= link_to "#groups", 'data-toggle' => 'tab' do
Groups
- if @contributed_projects.present?
%li
= link_to "#contributed", 'data-toggle' => 'tab' do
Contributed projects
- if @projects.present?
%li
= link_to "#personal", 'data-toggle' => 'tab' do
Personal projects
.tab-content
.tab-pane.active#activity
.content_list
= spinner
%aside.col-md-5
= render 'projects', projects: @projects, contributed_projects: @contributed_projects
- if @groups.any?
.tab-pane#groups
%ul.content-list
- @groups.each do |group|
= render 'shared/groups/group', group: group
- if @contributed_projects.present?
.tab-pane#contributed
.contributed-projects
= render 'shared/projects/list',
projects: @contributed_projects.sort_by(&:star_count).reverse,
projects_limit: 5, stars: true, avatar: false
- if @projects.present?
.tab-pane#personal
.personal-projects
= render 'shared/projects/list',
projects: @projects.sort_by(&:star_count).reverse,
projects_limit: 10, stars: true, avatar: false
:coffeescript
$(".user-calendar").load("#{user_calendar_path}")
class StuckCiBuildsWorker
include Sidekiq::Worker
include Sidetiq::Schedulable
BUILD_STUCK_TIMEOUT = 1.day
recurrence { daily }
def perform
Rails.logger.info 'Cleaning stuck builds'
builds = Ci::Build.running_or_pending.where('updated_at < ?', BUILD_STUCK_TIMEOUT.ago)
builds.find_each(batch_size: 50).each do |build|
Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}"
build.drop
end
end
end
......@@ -8,24 +8,3 @@
# inflect.irregular 'person', 'people'
# inflect.uncountable %w( fish sheep )
# end
# Mark "commits" as uncountable.
#
# Without this change, the routes
#
# resources :commit, only: [:show], constraints: {id: /[[:alnum:]]{6,40}/}
# resources :commits, only: [:show], constraints: {id: /.+/}
#
# would generate identical route helper methods (`project_commit_path`), resulting
# in one of them not getting a helper method at all.
#
# After this change, the helper methods are:
#
# project_commit_path(@project, @project.commit)
# # => "/gitlabhq/commit/bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a
#
# project_commits_path(@project, 'stable/README.md')
# # => "/gitlabhq/commits/stable/README.md"
ActiveSupport::Inflector.inflections do |inflect|
inflect.uncountable %w(commits)
end
# Build script examples
+ [Test and deploy Ruby applications to Heroku](test-and-deploy-ruby-application-to-heroku.md)
+ [Test and deploy Python applications to Heroku](test-and-deploy-python-application-to-heroku.md)
+ [Test Clojure applications](test-clojure-application.md)
+ [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
+ [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
+ [Test a Clojure application](test-clojure-application.md)
## Test and Deploy a python application
This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application.
You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://ci.gitlab.com/projects/4080).
You can checkout the example [source](https://gitlab.com/ayufan/python-getting-started) and check [CI status](https://gitlab.com/ayufan/python-getting-started/builds?scope=all).
### Configure project
This is what the `.gitlab-ci.yml` file looks like for this project:
......
## Test and Deploy a ruby application
This example will guide you how to run tests in your Ruby application and deploy it automatiacally as Heroku application.
You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://ci.gitlab.com/projects/4050).
You can checkout the example [source](https://gitlab.com/ayufan/ruby-getting-started) and check [CI status](https://gitlab.com/ayufan/ruby-getting-started/builds?scope=all).
### Configure project
This is what the `.gitlab-ci.yml` file looks like for this project:
......
## Test Clojure applications
## Test a Clojure application
This example will guide you how to run tests in your Clojure application.
You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://ci.gitlab.com/projects/6306).
You can checkout the example [source](https://gitlab.com/dzaporozhets/clojure-web-application) and check [CI status](https://gitlab.com/dzaporozhets/clojure-web-application/builds?scope=all).
### Configure project
......
......@@ -169,7 +169,7 @@ This are two parameters that allow for setting a refs policy to limit when jobs
There are a few rules that apply to usage of refs policy:
1. `only` and `except` are exclusive. If both `only` and `except` are defined in job specification only `only` is taken into account.
1. `only` and `except` are inclusive. If both `only` and `except` are defined in job specification the ref is filtered by `only` and `except`.
1. `only` and `except` allow for using the regexp expressions.
1. `only` and `except` allow for using special keywords: `branches` and `tags`.
These names can be used for example to exclude all tags and all branches.
......@@ -182,6 +182,18 @@ job:
- branches # use special keyword
```
1. `only` and `except` allow for specify repository path to filter jobs for forks.
The repository path can be used to have jobs executed only for parent repository.
```yaml
job:
only:
- branches@gitlab-org/gitlab-ce
except:
- master@gitlab-org/gitlab-ce
```
The above will run `job` for all branches on `gitlab-org/gitlab-ce`, except master .
### tags
`tags` is used to select specific runners from the list of all runners that are allowed to run this project.
......
......@@ -69,7 +69,10 @@ X-Gitlab-Event: Push Hook
}
}
],
"total_commits_count": 4
"total_commits_count": 4,
"added": ["CHANGELOG"],
"modified": ["app/controller/application.rb"],
"removed": []
}
```
......
......@@ -7,6 +7,7 @@ Feature: Profile
Given I visit profile page
Then I should see my profile info
@javascript
Scenario: I can see groups I belong to
Given I have group with projects
When I visit profile page
......
......@@ -6,7 +6,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
include Select2Helper
step 'I should see back to dashboard button' do
expect(page).to have_content 'Back to dashboard'
expect(page).to have_content 'Go to dashboard'
end
step 'gitlab user "Mike"' do
......
......@@ -159,10 +159,9 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see my user page' do
expect(page).to have_content "User Activity"
page.within '.navbar-gitlab' do
page.within ".cover-block" do
expect(page).to have_content current_user.name
expect(page).to have_content current_user.username
end
end
......@@ -176,7 +175,13 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see groups I belong to' do
expect(page).to have_css('.profile-groups-avatars', visible: true)
page.within ".content" do
click_link "Groups"
end
page.within "#groups" do
expect(page).to have_content @group.name
end
end
step 'I click on new application button' do
......
......@@ -124,11 +124,11 @@ class Spinach::Features::Project < Spinach::FeatureSteps
end
step 'I should see back to dashboard button' do
expect(page).to have_content 'Back to dashboard'
expect(page).to have_content 'Go to dashboard'
end
step 'I should see back to group button' do
expect(page).to have_content 'Back to group'
expect(page).to have_content 'Go to group'
end
step 'I click notifications drop down button' do
......
......@@ -46,7 +46,7 @@ module SharedProjectTab
step 'the active main tab should be Settings' do
page.within '.nav-sidebar' do
expect(page).to have_content('Back to project')
expect(page).to have_content('Go to project')
end
end
......
require 'backup/files'
module Backup
class Builds < Files
def initialize
......
require 'backup/files'
module Backup
class Uploads < Files
......
......@@ -16,7 +16,9 @@ module Ci
end
def update_runner_last_contact
if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= UPDATE_RUNNER_EVERY
# Use a random threshold to prevent beating DB updates
contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY)
if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= contacted_at_max_age
current_runner.update_attributes(contacted_at: Time.now)
end
end
......
......@@ -7,10 +7,11 @@ module Ci
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]
attr_reader :before_script, :image, :services, :variables
attr_reader :before_script, :image, :services, :variables, :path
def initialize(config)
def initialize(config, path = nil)
@config = YAML.load(config)
@path = path
unless @config.is_a? Hash
raise ValidationError, "YAML should be a hash"
......@@ -63,26 +64,6 @@ module Ci
end
end
def process?(only_params, except_params, ref, tag)
return true if only_params.nil? && except_params.nil?
if only_params
return true if tag && only_params.include?("tags")
return true if !tag && only_params.include?("branches")
only_params.find do |pattern|
match_ref?(pattern, ref)
end
else
return false if tag && except_params.include?("tags")
return false if !tag && except_params.include?("branches")
except_params.each do |pattern|
return false if match_ref?(pattern, ref)
end
end
end
def build_job(name, job)
{
stage_idx: stages.index(job[:stage]),
......@@ -101,14 +82,6 @@ module Ci
}
end
def match_ref?(pattern, ref)
if pattern.first == "/" && pattern.last == "/"
Regexp.new(pattern[1...-1]) =~ ref
else
pattern == ref
end
end
def normalize_script(script)
if script.is_a? Array
script.join("\n")
......@@ -208,5 +181,36 @@ module Ci
def validate_string(value)
value.is_a?(String) || value.is_a?(Symbol)
end
def process?(only_params, except_params, ref, tag)
if only_params.present?
return false unless matching?(only_params, ref, tag)
end
if except_params.present?
return false if matching?(except_params, ref, tag)
end
true
end
def matching?(patterns, ref, tag)
patterns.any? do |pattern|
match_ref?(pattern, ref, tag)
end
end
def match_ref?(pattern, ref, tag)
pattern, path = pattern.split('@', 2)
return false if path && path != self.path
return true if tag && pattern == 'tags'
return true if !tag && pattern == 'branches'
if pattern.first == "/" && pattern.last == "/"
Regexp.new(pattern[1...-1]) =~ ref
else
pattern == ref
end
end
end
end
......@@ -20,6 +20,8 @@ module Gitlab
Kaminari.paginate_array(blobs).page(page).per(per_page)
when 'wiki_blobs'
Kaminari.paginate_array(wiki_blobs).page(page).per(per_page)
when 'commits'
Kaminari.paginate_array(commits).page(page).per(per_page)
else
super
end
......@@ -27,7 +29,7 @@ module Gitlab
def total_count
@total_count ||= issues_count + merge_requests_count + blobs_count +
notes_count + wiki_blobs_count
notes_count + wiki_blobs_count + commits_count
end
def blobs_count
......@@ -42,6 +44,10 @@ module Gitlab
@wiki_blobs_count ||= wiki_blobs.count
end
def commits_count
@commits_count ||= commits.count
end
private
def blobs
......@@ -70,6 +76,14 @@ module Gitlab
Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC')
end
def commits
if project.empty_repo? || query.blank?
[]
else
project.repository.find_commits_by_message(query).compact
end
end
def limit_project_ids
[project.id]
end
......
......@@ -18,7 +18,10 @@ module Gitlab
# homepage: String,
# },
# commits: Array,
# total_commits_count: Fixnum
# total_commits_count: Fixnum,
# added: ["CHANGELOG"],
# modified: [],
# removed: ["tmp/file.txt"]
# }
#
def build(project, user, oldrev, newrev, ref, commits = [], message = nil)
......@@ -33,6 +36,8 @@ module Gitlab
commit_attrs = commits_limited.map(&:hook_attrs)
type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push"
repo_changes = repo_changes(project, newrev, oldrev)
# Hash to be passed as post_receive_data
data = {
object_kind: type,
......@@ -55,7 +60,10 @@ module Gitlab
visibility_level: project.visibility_level
},
commits: commit_attrs,
total_commits_count: commits_count
total_commits_count: commits_count,
added: repo_changes[:added],
modified: repo_changes[:modified],
removed: repo_changes[:removed]
}
data
......@@ -86,6 +94,27 @@ module Gitlab
newrev
end
end
def repo_changes(project, newrev, oldrev)
changes = { added: [], modified: [], removed: [] }
compare_result = CompareService.new.
execute(project, newrev, project, oldrev)
if compare_result
compare_result.diffs.each do |diff|
case true
when diff.deleted_file
changes[:removed] << diff.old_path
when diff.renamed_file, diff.new_file
changes[:added] << diff.new_path
else
changes[:modified] << diff.new_path
end
end
end
changes
end
end
end
end
......@@ -5,7 +5,7 @@ namespace :spinach do
task :project do
cmds = [
%W(rake gitlab:setup),
%W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets),
%W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits),
]
run_commands(cmds)
end
......@@ -14,7 +14,7 @@ namespace :spinach do
task :other do
cmds = [
%W(rake gitlab:setup),
%W(spinach --tags @admin,@dashboard,@profile,@public,@snippets),
%W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits),
]
run_commands(cmds)
end
......
......@@ -2,6 +2,7 @@ require 'spec_helper'
module Ci
describe GitlabCiYamlProcessor do
let(:path) { 'path' }
describe "#builds_for_ref" do
let(:type) { 'test' }
......@@ -12,7 +13,7 @@ module Ci
rspec: { script: "rspec" }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
......@@ -29,13 +30,14 @@ module Ci
})
end
describe :only do
it "does not return builds if only has another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", only: ["deploy"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
end
......@@ -46,7 +48,7 @@ module Ci
rspec: { script: "rspec", only: ["/^deploy$/"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
end
......@@ -57,49 +59,188 @@ module Ci
rspec: { script: "rspec", only: ["master"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
end
it "does not build tags" do
it "returns builds if only has a list of branches including specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["tags"] }
rspec: { script: "rspec", type: type, only: ["master", "deploy"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "0-1", true).size).to eq(0)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
end
it "returns builds if only has a list of branches including specified" do
it "returns builds if only has a branches keyword specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["master", "deploy"] }
rspec: { script: "rspec", type: type, only: ["branches"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
end
it "does not return builds if only has a tags keyword" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["tags"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
end
it "returns builds if only has current repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["branches@path"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
end
it "does not return builds if only has different repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, only: ["branches@fork"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
end
it "returns build only for specified type" do
config = YAML.dump({
before_script: ["pwd"],
build: { script: "build", type: "build", only: ["master", "deploy"] },
rspec: { script: "rspec", type: type, only: ["master", "deploy"] },
rspec: { script: "rspec", type: "test", only: ["master", "deploy"] },
staging: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
production: { script: "deploy", type: "deploy", only: ["master", "deploy"] },
production: { script: "deploy", type: "deploy", only: ["master@path", "deploy"] },
})
config_processor = GitlabCiYamlProcessor.new(config, 'fork')
expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
expect(config_processor.builds_for_stage_and_ref("test", "deploy").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(1)
end
end
describe :except do
it "returns builds if except has another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["deploy"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
end
it "returns builds if except has regexp with another branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["/^deploy$/"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
end
it "does not return builds if except has specified this branch" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", except: ["master"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(0)
end
it "does not return builds if except has a list of branches including specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["master", "deploy"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
end
it "does not return builds if except has a branches keyword specified" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["branches"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
end
it "returns builds if except has a tags keyword" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["tags"] }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref("production", "deploy").size).to eq(0)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
end
it "does not return builds if except has current repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["branches@path"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(0)
end
it "returns builds if except has different repository path" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: type, except: ["branches@fork"] }
})
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.builds_for_stage_and_ref(type, "deploy").size).to eq(1)
end
it "returns build except specified type" do
config = YAML.dump({
before_script: ["pwd"],
rspec: { script: "rspec", type: "test", except: ["master", "deploy", "test@fork"] },
staging: { script: "deploy", type: "deploy", except: ["master"] },
production: { script: "deploy", type: "deploy", except: ["master@fork"] },
})
config_processor = GitlabCiYamlProcessor.new(config, 'fork')
expect(config_processor.builds_for_stage_and_ref("deploy", "deploy").size).to eq(2)
expect(config_processor.builds_for_stage_and_ref("test", "test").size).to eq(0)
expect(config_processor.builds_for_stage_and_ref("deploy", "master").size).to eq(0)
end
end
end
describe "Image and service handling" do
......@@ -111,7 +252,7 @@ module Ci
rspec: { script: "rspec" }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
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({
......@@ -139,7 +280,7 @@ module Ci
rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
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({
......@@ -172,7 +313,7 @@ module Ci
rspec: { script: "rspec" }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
expect(config_processor.variables).to eq(variables)
end
end
......@@ -184,7 +325,7 @@ module Ci
rspec: { script: "rspec", when: when_state }
})
config_processor = GitlabCiYamlProcessor.new(config)
config_processor = GitlabCiYamlProcessor.new(config, path)
builds = config_processor.builds_for_stage_and_ref("test", "master")
expect(builds.size).to eq(1)
expect(builds.first[:when]).to eq(when_state)
......@@ -200,154 +341,154 @@ module Ci
it "returns errors if tags parameter is invalid" do
config = YAML.dump({ rspec: { script: "test", tags: "mysql" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: tags parameter should be an array of strings")
end
it "returns errors if before_script parameter is invalid" do
config = YAML.dump({ before_script: "bundle update", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings")
end
it "returns errors if image parameter is invalid" do
config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image should be a string")
end
it "returns errors if job name is blank" do
config = YAML.dump({ '' => { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
end
it "returns errors if job name is non-string" do
config = YAML.dump({ 10 => { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "job name should be non-empty string")
end
it "returns errors if job image parameter is invalid" do
config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: image should be a string")
end
it "returns errors if services parameter is not an array" do
config = YAML.dump({ services: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
end
it "returns errors if services parameter is not an array of strings" do
config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services should be an array of strings")
end
it "returns errors if job services parameter is not an array" do
config = YAML.dump({ rspec: { script: "test", services: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
end
it "returns errors if job services parameter is not an array of strings" do
config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: services should be an array of strings")
end
it "returns errors if there are unknown parameters" do
config = YAML.dump({ extra: "bundle update" })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
end
it "returns errors if there are unknown parameters that are hashes, but doesn't have a script" do
config = YAML.dump({ extra: { services: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Unknown parameter: extra")
end
it "returns errors if there is no any jobs defined" do
config = YAML.dump({ before_script: ["bundle update"] })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Please define at least one job")
end
it "returns errors if job allow_failure parameter is not an boolean" do
config = YAML.dump({ rspec: { script: "test", allow_failure: "string" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: allow_failure parameter should be an boolean")
end
it "returns errors if job stage is not a string" do
config = YAML.dump({ rspec: { script: "test", type: 1, allow_failure: "string" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
end
it "returns errors if job stage is not a pre-defined stage" do
config = YAML.dump({ rspec: { script: "test", type: "acceptance", allow_failure: "string" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test, deploy")
end
it "returns errors if job stage is not a defined stage" do
config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", type: "acceptance", allow_failure: "string" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: stage parameter should be build, test")
end
it "returns errors if stages is not an array" do
config = YAML.dump({ types: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
end
it "returns errors if stages is not an array of strings" do
config = YAML.dump({ types: [true, "test"], rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "stages should be an array of strings")
end
it "returns errors if variables is not a map" do
config = YAML.dump({ variables: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
end
it "returns errors if variables is not a map of key-valued strings" do
config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings")
end
it "returns errors if job when is not on_success, on_failure or always" do
config = YAML.dump({ rspec: { script: "test", when: 1 } })
expect do
GitlabCiYamlProcessor.new(config)
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: when parameter should be on_success, on_failure or always")
end
end
......
......@@ -17,6 +17,9 @@ describe 'Gitlab::PushDataBuilder' do
it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) }
it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) }
it { expect(data[:total_commits_count]).to eq(3) }
it { expect(data[:added]).to eq(["gitlab-grack"]) }
it { expect(data[:modified]).to eq([".gitmodules", "files/ruby/popen.rb", "files/ruby/regex.rb"]) }
it { expect(data[:removed]).to eq([]) }
end
describe :build do
......@@ -35,5 +38,8 @@ describe 'Gitlab::PushDataBuilder' do
it { expect(data[:ref]).to eq('refs/tags/v1.1.0') }
it { expect(data[:commits]).to be_empty }
it { expect(data[:total_commits_count]).to be_zero }
it { expect(data[:added]).to eq([]) }
it { expect(data[:modified]).to eq([]) }
it { expect(data[:removed]).to eq([]) }
end
end
......@@ -468,7 +468,7 @@ describe Notify do
subject { Notify.note_commit_email(recipient.id, note.id) }
it_behaves_like 'a note email'
it_behaves_like 'an answer to an existing thread', 'commits'
it_behaves_like 'an answer to an existing thread', 'commit'
it 'has the correct subject' do
is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
......
......@@ -26,6 +26,15 @@ describe Repository do
it { is_expected.to eq('c1acaa58bbcbc3eafe538cb8274ba387047b69f8') }
end
describe :find_commits_by_message do
subject { repository.find_commits_by_message('submodule').map{ |k| k.id } }
it { is_expected.to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { is_expected.to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
it { is_expected.to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660') }
it { is_expected.not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e') }
end
describe :blob_at do
context 'blank sha' do
subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
......
require "spec_helper"
describe StuckCiBuildsWorker do
let!(:build) { create :ci_build }
subject do
build.reload
build.status
end
%w(pending running).each do |status|
context "#{status} build" do
before do
build.update!(status: status)
end
it 'gets dropped if it was updated over 2 days ago' do
build.update!(updated_at: 2.day.ago)
StuckCiBuildsWorker.new.perform
is_expected.to eq('failed')
end
it "is still #{status}" do
build.update!(updated_at: 1.minute.ago)
StuckCiBuildsWorker.new.perform
is_expected.to eq(status)
end
end
end
%w(success failed canceled).each do |status|
context "#{status} build" do
before do
build.update!(status: status)
end
it "is still #{status}" do
build.update!(updated_at: 2.day.ago)
StuckCiBuildsWorker.new.perform
is_expected.to eq(status)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment