Commit 50076ab9 authored by Kamil Trzcinski's avatar Kamil Trzcinski

Merge remote-tracking branch 'origin/master' into per-build-token

# Conflicts:
#	db/schema.rb
parents 11f87700 4768521a
*.erb
lib/gitlab/sanitizers/svg/whitelist.rb
......@@ -206,10 +206,15 @@ spinach 9 10 ruby21: *spinach-knapsack-ruby21
- bundle exec $CI_BUILD_NAME
rubocop: *exec
rake haml_lint: *exec
rake scss_lint: *exec
rake brakeman: *exec
rake flog: *exec
rake flay: *exec
rake flog:
<<: *exec
allow_failure: yes
rake flay:
<<: *exec
allow_failure: yes
license_finder: *exec
rake downtime_check: *exec
......
# Whether to ignore frontmatter at the beginning of HAML documents for
# frameworks such as Jekyll/Middleman
skip_frontmatter: false
exclude:
- 'vendor/**/*'
- 'spec/**/*'
linters:
AltText:
enabled: false
ClassAttributeWithStaticValue:
enabled: false
ClassesBeforeIds:
enabled: false
ConsecutiveComments:
enabled: false
ConsecutiveSilentScripts:
enabled: false
max_consecutive: 2
EmptyObjectReference:
enabled: true
EmptyScript:
enabled: true
FinalNewline:
enabled: false
present: true
HtmlAttributes:
enabled: false
ImplicitDiv:
enabled: false
LeadingCommentSpace:
enabled: false
LineLength:
enabled: false
max: 80
MultilinePipe:
enabled: false
MultilineScript:
enabled: true
ObjectReferenceAttributes:
enabled: true
RuboCop:
enabled: false
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- Lint/BlockAlignment
- Lint/EndAlignment
- Lint/Void
- Metrics/LineLength
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
- Style/FileName
- Style/FinalNewline
- Style/FrozenStringLiteralComment
- Style/IfUnlessModifier
- Style/IndentationWidth
- Style/Next
- Style/TrailingBlankLines
- Style/TrailingWhitespace
- Style/WhileUntilModifier
RubyComments:
enabled: false
SpaceBeforeScript:
enabled: false
SpaceInsideHashAttributes:
enabled: false
style: space
Indentation:
enabled: true
character: space # or tab
TagName:
enabled: true
TrailingWhitespace:
enabled: false
UnnecessaryInterpolation:
enabled: false
UnnecessaryStringOutput:
enabled: false
This diff is collapsed.
......@@ -2,24 +2,29 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- Only check :can_resolve permission if the note is resolvable
- Add ability to fork to a specific namespace using API. (ritave)
- Cleanup misalignments in Issue list view !6206
- Prune events older than 12 months. (ritave)
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Filter tags by name !6121
- Update gitlab shell secret file also when it is empty. !3774 (glensc)
- Give project selection dropdowns responsive width, make non-wrapping.
- Make push events have equal vertical spacing.
- Add two-factor recovery endpoint to internal API !5510
- Pass the "Remember me" value to the U2F authentication form
- Remove vendor prefixes for linear-gradient CSS (ClemMakesApps)
- Move pushes_since_gc from the database to Redis
- Add font color contrast to external label in admin area (ClemMakesApps)
- Change logo animation to CSS (ClemMakesApps)
- Instructions for enabling Git packfile bitmaps !6104
- Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
- Fix pagination on user snippets page
- Fix sorting of issues in API
- Ensure specs on sorting of issues in API are deterministic on MySQL
- Escape search term before passing it to Regexp.new !6241 (winniehell)
- Fix pinned sidebar behavior in smaller viewports !6169
- Fix file permissions change when updating a file on the Gitlab UI !5979
- Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
......@@ -76,6 +81,7 @@ v 8.12.0 (unreleased)
- Remove inconsistent font weight for sidebar's labels (ClemMakesApps)
- Align add button on repository view (ClemMakesApps)
- Fix contributions calendar month label truncation (ClemMakesApps)
- Import release note descriptions from GitHub (EspadaV8)
- Added tests for diff notes
- Add pipeline events to Slack integration !5525
- Add a button to download latest successful artifacts for branches and tags !5142
......@@ -87,12 +93,14 @@ v 8.12.0 (unreleased)
- Fix repo title alignment (ClemMakesApps)
- Change update interval of contacted_at
- Fix branch title trailing space on hover (ClemMakesApps)
- Don't include 'Created By' tag line when importing from GitHub if there is a linked GitLab account (EspadaV8)
- Award emoji tooltips containing more than 10 usernames are now truncated !4780 (jlogandavison)
- Fix duplicate "me" in award emoji tooltip !5218 (jlogandavison)
- Order award emoji tooltips in order they were added (EspadaV8)
- Fix spacing and vertical alignment on build status icon on commits page (ClemMakesApps)
- Update merge_requests.md with a simpler way to check out a merge request. !5944
- Fix button missing type (ClemMakesApps)
- Gitlab::Checks is now instrumented
- Move to project dropdown with infinite scroll for better performance
- Fix leaking of submit buttons outside the width of a main container !18731 (originally by @pavelloz)
- Load branches asynchronously in Cherry Pick and Revert dialogs.
......@@ -111,6 +119,8 @@ v 8.12.0 (unreleased)
- Avoid conflict with admin labels when importing GitHub labels
- User can edit closed MR with deleted fork (Katarzyna Kobierska Ula Budziszewska) !5496
- Fix repository page ui issues
- Avoid protected branches checks when verifying access without branch name
- Add information about user and manual build start to runner as variables !6201 (Sergey Gnuskov)
- Fixed invisible scroll controls on build page on iPhone
- Fix error on raw build trace download for old builds stored in database !4822
- Refactor the triggers page and documentation !6217
......@@ -119,8 +129,23 @@ v 8.12.0 (unreleased)
- API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
- Allow bulk update merge requests from merge requests index page
- Add notification_settings API calls !5632 (mahcsig)
v 8.11.6 (unreleased)
- Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
- Fix URLs with anchors in wiki !6300 (houqp)
- Deleting source project with existing fork link will close all related merge requests !6177 (Katarzyna Kobierska Ula Budziszeska)
- Return 204 instead of 404 for /ci/api/v1/builds/register.json if no builds are scheduled for a runner !6225
- Fix Gitlab::Popen.popen thread-safety issue
- Add specs to removing project (Katarzyna Kobierska Ula Budziszewska)
- Clean environment variables when running git hooks
v 8.11.6
- Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
- Make merge conflict file size limit 200 KB, to match the docs. !6052
- Fix an error where we were unable to create a CommitStatus for running state. !6107
- Optimize discussion notes resolving and unresolving. !6141
- Fix GitLab import button. !6167
- Restore SSH Key title auto-population behavior. !6186
- Fix DB schema to match latest migration. !6256
- Exclude some pending or inactivated rows in Member scopes.
v 8.11.5
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
......@@ -325,6 +350,13 @@ v 8.11.0
- Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done
v 8.10.9
- Exclude some pending or inactivated rows in Member scopes
v 8.10.8
- Fix information disclosure in issue boards.
- Fix privilege escalation in project import.
v 8.10.7
- Upgrade Hamlit to 2.6.1. !5873
- Upgrade Doorkeeper to 4.2.0. !5881
......@@ -550,6 +582,9 @@ v 8.10.0
- Fix migration corrupting import data for old version upgrades
- Show tooltip on GitLab export link in new project page
v 8.9.9
- Exclude some pending or inactivated rows in Member scopes
v 8.9.8
- Upgrade Doorkeeper to 4.2.0. !5881
......
......@@ -91,19 +91,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
## Implement design & UI elements
### Design reference
The GitLab design reference can be found in the [gitlab-design] project.
The designs are made using Antetype (`.atype` files). You can use the
[free Antetype viewer (Mac OSX only)] or grab an exported PNG from the design
(the PNG is 1:1).
The current designs can be found in the [`gitlab8.atype` file].
### UI development kit
Implemented UI elements can also be found at https://gitlab.com/help/ui. Please
note that this page isn't comprehensive at this time.
Please see the [UI Guide for building GitLab].
## Issue tracker
......@@ -489,7 +477,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab8.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/current/
[UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md
[license-finder-doc]: doc/development/licensing.md
......@@ -26,7 +26,7 @@ gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6'
gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 3.0.0'
gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0'
gem 'omniauth-google-oauth2', '~> 0.4.1'
......@@ -53,7 +53,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.6.3'
gem 'gitlab_git', '~> 10.6.6'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......@@ -295,9 +295,10 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.41.2', require: false
gem 'rubocop', '~> 0.42.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false
gem 'simplecov', '0.12.0', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false
......
......@@ -279,7 +279,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3)
gitlab_git (10.6.3)
gitlab_git (10.6.6)
activesupport (~> 4.0)
charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0)
......@@ -322,11 +322,18 @@ GEM
grape-entity (0.4.8)
activesupport
multi_json (>= 1.3.2)
haml (4.0.7)
tilt
haml_lint (0.18.2)
haml (~> 4.0)
rake (>= 10, < 12)
rubocop (>= 0.36.0)
sysexits (~> 1.1)
hamlit (2.6.1)
temple (~> 0.7.6)
thor
tilt
hashie (3.4.3)
hashie (3.4.4)
health_check (2.1.0)
rails (>= 4.0)
hipchat (1.5.2)
......@@ -394,7 +401,7 @@ GEM
mime-types (>= 1.16, < 4)
mail_room (0.8.0)
method_source (0.8.2)
mime-types (2.99.2)
mime-types (2.99.3)
mimemagic (0.3.0)
mini_portile2 (2.1.0)
minitest (5.7.0)
......@@ -437,7 +444,7 @@ GEM
addressable (~> 2.3)
nokogiri (~> 1.6.6)
omniauth (~> 1.2)
omniauth-facebook (3.0.0)
omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2)
omniauth-github (1.1.2)
omniauth (~> 1.0)
......@@ -612,7 +619,7 @@ GEM
rspec-retry (0.4.5)
rspec-core
rspec-support (3.5.0)
rubocop (0.41.2)
rubocop (0.42.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
......@@ -723,6 +730,7 @@ GEM
stringex (2.5.2)
sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
systemu (2.6.5)
task_list (1.0.2)
html-pipeline
......@@ -754,7 +762,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.2)
unicode-display_width (1.1.0)
unicode-display_width (1.1.1)
unicorn (4.9.0)
kgio (~> 2.6)
rack
......@@ -858,7 +866,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0)
github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.6.3)
gitlab_git (~> 10.6.6)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2)
......@@ -866,6 +874,7 @@ DEPENDENCIES
gon (~> 6.1.0)
grape (~> 0.15.0)
grape-entity (~> 0.4.2)
haml_lint (~> 0.18.2)
hamlit (~> 2.6.1)
health_check (~> 2.1.0)
hipchat (~> 1.5.0)
......@@ -900,7 +909,7 @@ DEPENDENCIES
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-bitbucket (~> 0.0.2)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 3.0.0)
omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0)
omniauth-google-oauth2 (~> 0.4.1)
......@@ -935,7 +944,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rubocop (~> 0.41.2)
rubocop (~> 0.42.0)
rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9)
......
......@@ -79,10 +79,6 @@
padding-left: 15px !important;
}
.issue-info, .merge-request-info {
display: none;
}
.nav-links, .nav-links {
li a {
font-size: 14px;
......
......@@ -206,7 +206,7 @@
padding-top: 0;
.block {
width: $sidebar_collapsed_width - 1px;
width: $sidebar_collapsed_width - 2px;
margin-left: -19px;
padding: 15px 0 0;
border-bottom: none;
......
......@@ -37,6 +37,15 @@ form.edit-issue {
margin: 0;
}
ul.related-merge-requests > li {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
.merge-request-id {
flex-shrink: 0;
}
}
.merge-requests-title, .related-branches-title {
font-size: 16px;
font-weight: 600;
......
......@@ -7,8 +7,7 @@ module CreatesCommit
commit_params = @commit_params.merge(
source_project: @project,
source_branch: @ref,
target_branch: @target_branch,
previous_path: @previous_path
target_branch: @target_branch
)
result = service.new(@tree_edit_project, current_user, commit_params).execute
......
......@@ -38,12 +38,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
if params[:file_path].present?
@previous_path = @path
@path = params[:file_path]
@commit_params[:file_path] = @path
end
@path = params[:file_path] if params[:file_path].present?
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
......@@ -143,6 +138,8 @@ class Projects::BlobController < Projects::ApplicationController
params[:file_name] = params[:file].original_filename
end
File.join(@path, params[:file_name])
elsif params[:file_path].present?
params[:file_path]
else
@path
end
......@@ -155,6 +152,7 @@ class Projects::BlobController < Projects::ApplicationController
@commit_params = {
file_path: @file_path,
commit_message: params[:commit_message],
previous_path: @path,
file_content: params[:content],
file_content_encoding: params[:encoding],
last_commit_sha: params[:last_commit_sha]
......
......@@ -428,17 +428,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
# If source project was removed (Ex. mr from fork to origin)
return invalid_mr unless @merge_request.source_project
# Show git not found page
# if there is no saved commits between source & target branch
if @merge_request.commits.blank?
# and if target branch doesn't exist
return invalid_mr unless @merge_request.target_branch_exists?
# or if source branch doesn't exist
return invalid_mr unless @merge_request.source_branch_exists?
end
end
......
......@@ -30,6 +30,37 @@ module SearchHelper
"Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\""
end
def parse_search_result(result)
ref = nil
filename = nil
basename = nil
startline = 0
result.each_line.each_with_index do |line, index|
if line =~ /^.*:.*:\d+:/
ref, filename, startline = line.split(':')
startline = startline.to_i - index
extname = Regexp.escape(File.extname(filename))
basename = filename.sub(/#{extname}$/, '')
break
end
end
data = ""
result.each_line do |line|
data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
OpenStruct.new(
filename: filename,
basename: basename,
ref: ref,
startline: startline,
data: data
)
end
private
# Autocomplete results for various settings pages
......
......@@ -22,6 +22,18 @@ class Blob < SimpleDelegator
new(blob)
end
# Returns the data of the blob.
#
# If the blob is a text based blob the content is converted to UTF-8 and any
# invalid byte sequences are replaced.
def data
if binary?
super
else
@data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
end
end
def no_highlighting?
size && size > 1.megabyte
end
......
......@@ -153,6 +153,7 @@ module Ci
variables += runner.predefined_variables if runner
variables += project.container_registry_variables
variables += yaml_variables
variables += user_variables
variables += project.secret_variables
variables += trigger_request.user_variables if trigger_request
variables
......@@ -435,6 +436,15 @@ module Ci
read_attribute(:yaml_variables) || build_attributes_from_config[:yaml_variables] || []
end
def user_variables
return [] if user.blank?
[
{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true }
]
end
private
def update_artifacts_size
......@@ -470,6 +480,7 @@ module Ci
]
variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request
variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if manual?
variables
end
......
......@@ -28,17 +28,34 @@ class Member < ActiveRecord::Base
allow_nil: true
}
# This scope encapsulates (most of) the conditions a row in the member table
# must satisfy if it is a valid permission. Of particular note:
#
# * Access requests must be excluded
# * Blocked users must be excluded
# * Invitations take effect immediately
# * expires_at is not implemented. A background worker purges expired rows
scope :active, -> do
is_external_invite = arel_table[:user_id].eq(nil).and(arel_table[:invite_token].not_eq(nil))
user_is_active = User.arel_table[:state].eq(:active)
includes(:user).references(:users)
.where(is_external_invite.or(user_is_active))
.where(requested_at: nil)
end
scope :invite, -> { where.not(invite_token: nil) }
scope :non_invite, -> { where(invite_token: nil) }
scope :request, -> { where.not(requested_at: nil) }
scope :has_access, -> { where('access_level > 0') }
scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) }
scope :developers, -> { where(access_level: DEVELOPER) }
scope :masters, -> { where(access_level: MASTER) }
scope :owners, -> { where(access_level: OWNER) }
scope :owners_and_masters, -> { where(access_level: [OWNER, MASTER]) }
scope :has_access, -> { active.where('access_level > 0') }
scope :guests, -> { active.where(access_level: GUEST) }
scope :reporters, -> { active.where(access_level: REPORTER) }
scope :developers, -> { active.where(access_level: DEVELOPER) }
scope :masters, -> { active.where(access_level: MASTER) }
scope :owners, -> { active.where(access_level: OWNER) }
scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) }
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
......
......@@ -316,6 +316,10 @@ class MergeRequest < ActiveRecord::Base
closed? && forked_source_project_missing?
end
def closed_without_source_project?
closed? && !source_project
end
def forked_source_project_missing?
return false unless for_fork?
return true unless source_project
......@@ -323,6 +327,12 @@ class MergeRequest < ActiveRecord::Base
!source_project.forked_from?(target_project)
end
def reopenable?
return false if closed_without_fork? || closed_without_source_project? || merged?
closed?
end
def ensure_merge_request_diff
merge_request_diff || create_merge_request_diff
end
......
......@@ -58,7 +58,7 @@ class Project < ActiveRecord::Base
# Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
belongs_to :group, -> { where(type: 'Group') }, foreign_key: 'namespace_id'
belongs_to :namespace
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
......@@ -1282,8 +1282,24 @@ class Project < ActiveRecord::Base
end
end
def pushes_since_gc
Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i }
end
def increment_pushes_since_gc
Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) }
end
def reset_pushes_since_gc
Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) }
end
private
def pushes_since_gc_redis_key
"projects/#{id}/pushes_since_gc"
end
# Prevents the creation of project_feature record for every project
def setup_project_feature
build_project_feature unless project_feature
......
......@@ -813,7 +813,7 @@ class Repository
update: true
}
if previous_path
if previous_path && previous_path != path
options[:file][:previous_path] = previous_path
Gitlab::Git::Blob.rename(raw_repository, options)
else
......@@ -990,37 +990,6 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end
def parse_search_result(result)
ref = nil
filename = nil
basename = nil
startline = 0
result.each_line.each_with_index do |line, index|
if line =~ /^.*:.*:\d+:/
ref, filename, startline = line.split(':')
startline = startline.to_i - index
extname = Regexp.escape(File.extname(filename))
basename = filename.sub(/#{extname}$/, '')
break
end
end
data = ""
result.each_line do |line|
data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
OpenStruct.new(
filename: filename,
basename: basename,
ref: ref,
startline: startline,
data: data
)
end
def fetch_ref(source_path, source_ref, target_ref)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
......@@ -1048,7 +1017,7 @@ class Repository
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
update_ref!(ref, newrev, oldrev)
if was_empty || !target_branch
# If repo was empty expire cache
after_create if was_empty
......
......@@ -31,13 +31,13 @@ module Ci
current_status = status_for_prior_stages(index)
created_builds_in_stage(index).select do |build|
process_build(build, current_status)
if HasStatus::COMPLETED_STATUSES.include?(current_status)
process_build(build, current_status)
end
end
end
def process_build(build, current_status)
return false unless HasStatus::COMPLETED_STATUSES.include?(current_status)
if valid_statuses_for_when(build.when).include?(current_status)
build.enqueue
true
......
......@@ -27,6 +27,8 @@ module Projects
# Git data (e.g. a list of branch names).
flush_caches(project, wiki_path)
Projects::UnlinkForkService.new(project, current_user).execute
Project.transaction do
project.destroy!
......
......@@ -30,10 +30,8 @@ module Projects
end
def increment!
if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
update_pushes_since_gc(@project.pushes_since_gc + 1)
end
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
@project.increment_pushes_since_gc
end
end
......@@ -43,14 +41,10 @@ module Projects
GitGarbageCollectWorker.perform_async(@project.id)
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
update_pushes_since_gc(0)
@project.reset_pushes_since_gc
end
end
def update_pushes_since_gc(new_value)
@project.update_column(:pushes_since_gc, new_value)
end
def try_obtain_lease
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
......
- project = build.project
%tr.build.commit
%td.status
= ci_status_with_icon(build.status)
%td
.branch-commit
- if can?(current_user, :read_build, build.project)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%span.build-link ##{build.id}
- else
%span.build-link ##{build.id}
- if build.ref
.icon-container
= build.tag? ? icon('tag') : icon('code-fork')
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- else
.light none
.icon-container
= custom_icon("icon_commit")
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
- if build.stuck?
%i.fa.fa-warning.text-warning
.label-container
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
= tag
- if build.try(:trigger_request)
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
%td
- if project
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
- if build.try(:runner)
= runner_link(build.runner)
- else
.light none
%td
#{build.stage} / #{build.name}
%td
- if build.duration
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.duration)
- if build.finished_at
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
- if build.try(:coverage)
#{build.coverage}%
%td
.pull-right
- if can?(current_user, :read_build, project) && build.artifacts?
= link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do
%i.fa.fa-download
- if can?(current_user, :update_build, build.project)
- if build.active?
= link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
%i.fa.fa-remove.cred
- elsif defined?(allow_retry) && allow_retry && build.retryable?
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
%i.fa.fa-refresh
......@@ -4,26 +4,8 @@
%div{ class: container_class }
.top-area
%ul.nav-links
%li{class: ('active' if @scope.nil?)}
= link_to admin_builds_path do
All
%span.badge.js-totalbuilds-count= @all_builds.count(:id)
%li{class: ('active' if @scope == 'pending')}
= link_to admin_builds_path(scope: :pending) do
Pending
%span.badge= number_with_delimiter(@all_builds.pending.count(:id))
%li{class: ('active' if @scope == 'running')}
= link_to admin_builds_path(scope: :running) do
Running
%span.badge= number_with_delimiter(@all_builds.running.count(:id))
%li{class: ('active' if @scope == 'finished')}
= link_to admin_builds_path(scope: :finished) do
Finished
%span.badge= number_with_delimiter(@all_builds.finished.count(:id))
- build_path_proc = ->(scope) { admin_builds_path(scope: scope) }
= render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if @all_builds.running_or_pending.any?
......@@ -33,23 +15,4 @@
#{(@scope || 'all').capitalize} builds
%ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
- else
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Commit
%th Project
%th Runner
%th Name
%th
%th
- @builds.each do |build|
= render "admin/builds/build", build: build
= paginate @builds, theme: 'gitlab'
= render "projects/builds/table", builds: @builds, admin: true
- page_title "SSH Keys"
= render 'profiles/head'
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
......
- admin = local_assigns.fetch(:admin, false)
- if builds.blank?
%li
.nothing-here-block No builds to show
- else
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Commit
- if admin
%th Project
%th Runner
%th Stage
%th Name
%th
%th Coverage
%th
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= paginate builds, theme: 'gitlab'
......@@ -4,30 +4,8 @@
%div{ class: container_class }
.top-area
%ul.nav-links
%li{class: ('active' if @scope.nil?)}
= link_to project_builds_path(@project) do
All
%span.badge.js-totalbuilds-count
= number_with_delimiter(@all_builds.count(:id))
%li{class: ('active' if @scope == 'pending')}
= link_to project_builds_path(@project, scope: :pending) do
Pending
%span.badge
= number_with_delimiter(@all_builds.pending.count(:id))
%li{class: ('active' if @scope == 'running')}
= link_to project_builds_path(@project, scope: :running) do
Running
%span.badge
= number_with_delimiter(@all_builds.running.count(:id))
%li{class: ('active' if @scope == 'finished')}
= link_to project_builds_path(@project, scope: :finished) do
Finished
%span.badge
= number_with_delimiter(@all_builds.finished.count(:id))
- build_path_proc = ->(scope) { project_builds_path(@project, scope: scope) }
= render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
.nav-controls
- if can?(current_user, :update_build, @project)
......@@ -42,23 +20,4 @@
%span CI Lint
%ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
- else
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Commit
%th Stage
%th Name
%th
- if @project.build_coverage_enabled?
%th Coverage
%th
= render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
= paginate @builds, theme: 'gitlab'
= render "table", builds: @builds, project: @project
- admin = local_assigns.fetch(:admin, false)
- ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false)
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
- allow_retry = local_assigns.fetch(:allow_retry, false)
%tr.build.commit
%td.status
- if can?(current_user, :read_build, build)
......@@ -9,11 +17,11 @@
.branch-commit
- if can?(current_user, :read_build, build)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%span ##{build.id}
%span.build-link ##{build.id}
- else
%span ##{build.id}
%span.build-link ##{build.id}
- if defined?(ref) && ref
- if ref
- if build.ref
.icon-container
= build.tag? ? icon('tag') : icon('code-fork')
......@@ -23,12 +31,12 @@
.icon-container
= custom_icon("icon_commit")
- if defined?(commit_sha) && commit_sha
- if commit_sha
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
- if build.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
- if defined?(retried) && retried
- if retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
.label-container
......@@ -40,19 +48,24 @@
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
- if defined?(retried) && retried
- if retried
%span.label.label-warning retried
- if build.manual?
%span.label.label-info manual
- if defined?(runner) && runner
- if admin
%td
- if build.project
= link_to build.project.name_with_namespace, admin_namespace_project_path(build.project.namespace, build.project)
- if admin
%td
- if build.try(:runner)
= runner_link(build.runner)
- else
.light none
- if defined?(stage) && stage
- if stage
%td
= build.stage
......@@ -64,13 +77,14 @@
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.duration)
- if build.finished_at
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
%td.coverage
- if coverage
- if build.try(:coverage)
#{build.coverage}%
......@@ -83,10 +97,10 @@
- if build.active?
= link_to cancel_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Cancel', class: 'btn btn-build' do
= icon('remove', class: 'cred')
- elsif defined?(allow_retry) && allow_retry
- elsif allow_retry
- if build.retryable?
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
= icon('repeat')
- elsif build.playable?
- elsif build.playable? && !admin
= link_to play_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do
= custom_icon('icon_play')
- if @merge_requests.any?
%h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list
%ul.unstyled-list.related-merge-requests
- has_any_ci = @merge_requests.any?(&:pipeline)
- @merge_requests.each do |merge_request|
%li
......
- if @related_branches.any?
%h2.related-branches-title
= pluralize(@related_branches.count, 'Related Branch')
%ul.unstyled-list
%ul.unstyled-list.related-merge-requests
- @related_branches.each do |branch|
%li
- target = @project.repository.find_branch(branch).target
......
......@@ -2,7 +2,7 @@
- if can?(current_user, :update_merge_request, @merge_request)
- if @merge_request.open?
= link_to 'Close merge request', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-close close-mr-link js-note-target-close", title: "Close merge request", data: {original_text: "Close merge request", alternative_text: "Comment & close merge request"}
- if @merge_request.closed?
- if @merge_request.reopenable?
= link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request", data: {original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
%comment-and-resolve-btn{ "inline-template" => true, ":discussion-id" => "" }
%button.btn.btn-nr.btn-default.append-right-10.js-comment-resolve-button{ "v-if" => "showButton", type: "submit", data: { namespace_path: "#{@merge_request.project.namespace.path}", project_path: "#{@merge_request.project.path}" } }
......
......@@ -29,17 +29,19 @@
%ul.dropdown-menu.dropdown-menu-align-right
%li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
.normal
%span Request to merge
%span.label-branch= source_branch_with_namespace(@merge_request)
%span into
%span.label-branch
= link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch)
- if @merge_request.open? && @merge_request.diverged_from_target_branch?
%span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind)
- unless @merge_request.closed_without_fork?
.normal
%span Request to merge
%span.label-branch= source_branch_with_namespace(@merge_request)
%span into
%span.label-branch
= link_to @merge_request.target_branch, namespace_project_commits_path(@project.namespace, @project, @merge_request.target_branch)
- if @merge_request.open? && @merge_request.diverged_from_target_branch?
%span (#{pluralize(@merge_request.diverged_commits_count, 'commit')} behind)
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/widget/show.html.haml"
- unless @merge_request.closed_without_source_project?
= render "projects/merge_requests/show/how_to_merge"
= render "projects/merge_requests/widget/show.html.haml"
- if @merge_request.source_branch_exists? && @merge_request.mergeable? && @merge_request.can_be_merged_by?(current_user)
.light.prepend-top-default.append-bottom-default
......@@ -53,10 +55,11 @@
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
Discussion
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- unless @merge_request.closed_without_source_project?
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- if @pipeline
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
......
- return unless note.author
- return if note.cross_reference_not_visible_for?(current_user)
- can_resolve = can?(current_user, :resolve_note, note)
- note_editable = note_editable?(note)
%li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} }
......@@ -24,6 +23,8 @@
%span.note-role.hidden-xs= access
- if note.resolvable?
- can_resolve = can?(current_user, :resolve_note, note)
%resolve-btn{ ":namespace-path" => "'#{note.project.namespace.path}'",
":project-path" => "'#{note.project.path}'",
":discussion-id" => "'#{note.discussion_id}'",
......
- blob = @project.repository.parse_search_result(blob)
- blob = parse_search_result(blob)
.blob-result
.file-holder
.file-title
......
- wiki_blob = @project.repository.parse_search_result(wiki_blob)
- wiki_blob = parse_search_result(wiki_blob)
.blob-result
.file-holder
.file-title
......
%ul.nav-links
%li{ class: ('active' if scope.nil?) }
= link_to build_path_proc.call(nil) do
All
%span.badge.js-totalbuilds-count
= number_with_delimiter(all_builds.count(:id))
%li{ class: ('active' if scope == 'pending') }
= link_to build_path_proc.call('pending') do
Pending
%span.badge
= number_with_delimiter(all_builds.pending.count(:id))
%li{ class: ('active' if scope == 'running') }
= link_to build_path_proc.call('running') do
Running
%span.badge
= number_with_delimiter(all_builds.running.count(:id))
%li{ class: ('active' if scope == 'finished') }
= link_to build_path_proc.call('finished') do
Finished
%span.badge
= number_with_delimiter(all_builds.finished.count(:id))
......@@ -68,7 +68,8 @@ if Gitlab::Metrics.enabled?
['app', 'mailers', 'emails'] => ['app', 'mailers'],
['app', 'services', '**'] => ['app', 'services'],
['lib', 'gitlab', 'diff'] => ['lib'],
['lib', 'gitlab', 'email', 'message'] => ['lib']
['lib', 'gitlab', 'email', 'message'] => ['lib'],
['lib', 'gitlab', 'checks'] => ['lib']
}
paths_to_instrument.each do |(path, prefix)|
......
......@@ -13,9 +13,5 @@ Mime::Type.register "video/mp4", :mp4, [], [:m4v, :mov]
Mime::Type.register "video/webm", :webm
Mime::Type.register "video/ogg", :ogv
middlewares = Gitlab::Application.config.middleware
middlewares.swap(ActionDispatch::ParamsParser, ActionDispatch::ParamsParser, {
Mime::Type.lookup('application/vnd.git-lfs+json') => lambda do |body|
ActiveSupport::JSON.decode(body)
end
})
Mime::Type.unregister :json
Mime::Type.register 'application/json', :json, %w(application/vnd.git-lfs+json application/json)
......@@ -8,14 +8,28 @@ class MergeRequestDiffRemoveUniq < ActiveRecord::Migration
DOWNTIME = false
def up
if index_exists?(:merge_request_diffs, :merge_request_id)
remove_index :merge_request_diffs, :merge_request_id
constraint_name = 'merge_request_diffs_merge_request_id_key'
transaction do
if index_exists?(:merge_request_diffs, :merge_request_id)
remove_index(:merge_request_diffs, :merge_request_id)
end
# In some bizarre cases PostgreSQL might have a separate unique constraint
# that we'll need to drop.
if constraint_exists?(constraint_name) && Gitlab::Database.postgresql?
execute("ALTER TABLE merge_request_diffs DROP CONSTRAINT IF EXISTS #{constraint_name};")
end
end
end
def down
unless index_exists?(:merge_request_diffs, :merge_request_id)
add_concurrent_index :merge_request_diffs, :merge_request_id, unique: true
add_concurrent_index(:merge_request_diffs, :merge_request_id, unique: true)
end
end
def constraint_exists?(name)
indexes(:merge_request_diffs).map(&:name).include?(name)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveProjectsPushesSinceGc < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'This migration removes an existing column'
disable_ddl_transaction!
def up
remove_column :projects, :pushes_since_gc
end
def down
add_column_with_default! :projects, :pushes_since_gc, :integer, default: 0
end
end
......@@ -11,7 +11,8 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160907131111) do
ActiveRecord::Schema.define(version: 20160913162434) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -826,7 +827,6 @@ ActiveRecord::Schema.define(version: 20160907131111) do
t.integer "build_timeout", default: 3600, null: false
t.boolean "pending_delete", default: false
t.boolean "public_builds", default: true, null: false
t.integer "pushes_since_gc", default: 0
t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
......
......@@ -406,7 +406,8 @@ To configure the storage driver in Omnibus:
's3' => {
'accesskey' => 's3-access-key',
'secretkey' => 's3-secret-key-for-access-key',
'bucket' => 'your-s3-bucket'
'bucket' => 'your-s3-bucket',
'region' => 'your-s3-region'
}
}
```
......@@ -428,6 +429,7 @@ storage:
accesskey: 'AKIAKIAKI'
secretkey: 'secret123'
bucket: 'gitlab-registry-bucket-AKIAKIAKI'
region: 'your-s3-region'
cache:
blobdescriptor: inmemory
delete:
......
......@@ -38,6 +38,15 @@ POST /ci/api/v1/builds/register
curl --request POST "https://gitlab.example.com/ci/api/v1/builds/register" --form "token=t0k3n"
```
**Responses:**
| Status | Data |Description |
|--------|------|---------------------------------------------------------------------------|
| `201` | yes | When a build is scheduled for a runner |
| `204` | no | When no builds are scheduled for a runner (for GitLab Runner >= `v1.3.0`) |
| `403` | no | When invalid token is used or no token is sent |
| `404` | no | When no builds are scheduled for a runner (for GitLab Runner < `v1.3.0`) **or** when the runner is set to `paused` in GitLab runner's configuration page |
### Update details of an existing build
```
......
......@@ -34,6 +34,7 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
| **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built |
| **CI_BUILD_REPO** | all | all | The URL to clone the Git repository |
| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that build was [triggered] |
| **CI_BUILD_MANUAL** | 8.12 | all | The flag to indicate that build was manually started |
| **CI_BUILD_TOKEN** | all | 1.2 | Token used for authenticating with the GitLab Container Registry |
| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally |
......@@ -47,6 +48,8 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the build |
| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the build |
**Some of the variables are only available when using runner with at least defined version.**
......@@ -60,6 +63,7 @@ export CI_BUILD_REPO="https://gitab-ci-token:abcde-1234ABCD5678ef@gitlab.com/git
export CI_BUILD_TAG="1.0.0"
export CI_BUILD_NAME="spec:other"
export CI_BUILD_STAGE="test"
export CI_BUILD_MANUAL="true"
export CI_BUILD_TRIGGERED="true"
export CI_BUILD_TOKEN="abcde-1234ABCD5678ef"
export CI_PIPELINE_ID="1000"
......@@ -78,6 +82,8 @@ export CI_SERVER="yes"
export CI_SERVER_NAME="GitLab"
export CI_SERVER_REVISION="70606bf"
export CI_SERVER_VERSION="8.9.0"
export GITLAB_USER_ID="42"
export GITLAB_USER_EMAIL="alexzander@sporer.com"
```
### YAML-defined variables
......
......@@ -137,3 +137,18 @@ end
```
Here the final value of `sleep_real_time` will be `3`, _not_ `1`.
## Tracking Custom Events
Besides instrumenting code GitLab Performance Monitoring also supports tracking
of custom events. This is primarily intended to be used for tracking business
metrics such as the number of Git pushes, repository imports, and so on.
To track a custom event simply call `Gitlab::Metrics.add_event` passing it an
event name and a custom set of (optional) tags. For example:
```ruby
Gitlab::Metrics.add_event(:user_login, email: current_user.email)
```
Event names should be verbs such as `push_repository` and `remove_branch`.
......@@ -70,7 +70,7 @@ sudo -u git -H git checkout 8-12-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v3.4.0
sudo -u git -H git checkout v3.5.0
```
### 6. Update gitlab-workhorse
......
......@@ -93,6 +93,9 @@ A merge request contains all the history from a repository, plus the additional
commits added to the branch associated with the merge request. Here's a few
tricks to checkout a merge request locally.
Please note that you can checkout a merge request locally even if the source
project is a fork (even a private fork) of the target project.
#### Checkout locally by adding a git alias
Add the following alias to your `~/.gitconfig`:
......
......@@ -15,6 +15,7 @@ At its current state, GitHub importer can import:
- the wiki pages (introduced in GitLab 8.4)
- the milestones (introduced in GitLab 8.7)
- the labels (introduced in GitLab 8.7)
- the release note descriptions (introduced in GitLab 8.12)
With GitLab 8.7+, references to pull requests and issues are preserved.
......
......@@ -37,7 +37,7 @@ module API
# id (required) - The ID of a project
# sha (required) - The commit hash
# ref (optional) - The ref
# state (required) - The state of the status. Can be: pending, running, success, error or failure
# state (required) - The state of the status. Can be: pending, running, success, failed or canceled
# target_url (optional) - The target URL to associate with this status
# description (optional) - A short description of the status
# name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default"
......@@ -46,7 +46,7 @@ module API
post ':id/statuses/:sha' do
authorize! :create_commit_status, user_project
required_attributes! [:state]
attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name]
attrs = attributes_for_keys [:target_url, :description]
commit = @project.commit(params[:sha])
not_found! 'Commit' unless commit
......@@ -58,36 +58,38 @@ module API
# the first found branch on that commit
ref = params[:ref]
unless ref
branches = @project.repository.branch_names_contains(commit.sha)
not_found! 'References for commit' if branches.none?
ref = branches.first
end
ref ||= @project.repository.branch_names_contains(commit.sha).first
not_found! 'References for commit' unless ref
name = params[:name] || params[:context] || 'default'
pipeline = @project.ensure_pipeline(ref, commit.sha, current_user)
name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(pipeline: pipeline, name: name, ref: params[:ref])
status ||= GenericCommitStatus.new(project: @project, pipeline: pipeline, user: current_user)
status.update(attrs)
status = GenericCommitStatus.running_or_pending.find_or_initialize_by(
project: @project, pipeline: pipeline,
user: current_user, name: name, ref: ref)
status.attributes = attrs
case params[:state].to_s
when 'running'
status.run
when 'success'
status.success
when 'failed'
status.drop
when 'canceled'
status.cancel
else
status.status = params[:state].to_s
end
begin
case params[:state].to_s
when 'pending'
status.enqueue!
when 'running'
status.enqueue
status.run!
when 'success'
status.success!
when 'failed'
status.drop!
when 'canceled'
status.cancel!
else
render_api_error!('invalid state', 400)
end
if status.save
present status, with: Entities::CommitStatus
else
render_validation_error!(status)
rescue StateMachines::InvalidTransition => e
render_api_error!(e.message, 400)
end
end
end
......
......@@ -269,6 +269,10 @@ module API
render_api_error!('304 Not Modified', 304)
end
def no_content!
render_api_error!('204 No Content', 204)
end
def render_validation_error!(model)
if model.errors.any?
render_api_error!(model.errors.messages || '400 Bad Request', 400)
......
......@@ -31,6 +31,7 @@ module Banzai
def apply_relative_link_rules!
if @uri.relative? && @uri.path.present?
link = ::File.join(@wiki_base_path, @uri.path)
link = "#{link}##{@uri.fragment}" if @uri.fragment
@uri = Addressable::URI.parse(link)
end
end
......
......@@ -27,7 +27,7 @@ module Ci
else
Gitlab::Metrics.add_event(:build_not_found)
not_found!
build_not_found!
end
end
......
......@@ -40,6 +40,14 @@ module Ci
end
end
def build_not_found!
if headers['User-Agent'].match(/gitlab-ci-multi-runner \d+\.\d+\.\d+(~beta\.\d+\.g[0-9a-f]+)? /)
no_content!
else
not_found!
end
end
def current_runner
@runner ||= Runner.find_by_token(params[:token].to_s)
end
......
......@@ -195,7 +195,7 @@ module Gitlab
# Create (if necessary) and link the secret token file
def generate_and_link_secret_token
secret_file = Gitlab.config.gitlab_shell.secret_file
unless File.exist? secret_file
unless File.size?(secret_file)
# Generate a new token of 16 random hexadecimal characters and store it in secret_file.
token = SecureRandom.hex(16)
File.write(secret_file, token)
......
......@@ -23,6 +23,7 @@ module Gitlab
protected
def protected_branch_checks
return unless @branch_name
return unless project.protected_branch?(@branch_name)
if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
......
......@@ -17,11 +17,13 @@ module Gitlab
def trigger(gl_id, oldrev, newrev, ref)
return [true, nil] unless exists?
case name
when "pre-receive", "post-receive"
call_receive_hook(gl_id, oldrev, newrev, ref)
when "update"
call_update_hook(gl_id, oldrev, newrev, ref)
Bundler.with_clean_env do
case name
when "pre-receive", "post-receive"
call_receive_hook(gl_id, oldrev, newrev, ref)
when "update"
call_update_hook(gl_id, oldrev, newrev, ref)
end
end
end
......
......@@ -20,6 +20,11 @@ module Gitlab
find_by("identities.extern_uid = ? AND identities.provider = 'github'", github_id.to_s).
try(:id)
end
def gitlab_author_id
return @gitlab_author_id if defined?(@gitlab_author_id)
@gitlab_author_id = gitlab_user_id(raw_data.user.id)
end
end
end
end
......@@ -21,7 +21,7 @@ module Gitlab
end
def author_id
gitlab_user_id(raw_data.user.id) || project.creator_id
gitlab_author_id || project.creator_id
end
def body
......@@ -52,7 +52,11 @@ module Gitlab
end
def note
formatter.author_line(author) + body
if gitlab_author_id
body
else
formatter.author_line(author) + body
end
end
def type
......
......@@ -24,6 +24,7 @@ module Gitlab
import_issues
import_pull_requests
import_wiki
import_releases
handle_errors
true
......@@ -177,6 +178,18 @@ module Gitlab
errors << { type: :wiki, errors: e.message }
end
end
def import_releases
releases = client.releases(repo, per_page: 100)
releases.each do |raw|
begin
gh_release = ReleaseFormatter.new(project, raw)
gh_release.create! if gh_release.valid?
rescue => e
errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
end
end
end
......@@ -49,7 +49,7 @@ module Gitlab
end
def author_id
gitlab_user_id(raw_data.user.id) || project.creator_id
gitlab_author_id || project.creator_id
end
def body
......@@ -57,7 +57,11 @@ module Gitlab
end
def description
@formatter.author_line(author) + body
if gitlab_author_id
body
else
formatter.author_line(author) + body
end
end
def milestone
......
......@@ -77,7 +77,7 @@ module Gitlab
end
def author_id
gitlab_user_id(raw_data.user.id) || project.creator_id
gitlab_author_id || project.creator_id
end
def body
......@@ -85,7 +85,11 @@ module Gitlab
end
def description
formatter.author_line(author) + body
if gitlab_author_id
body
else
formatter.author_line(author) + body
end
end
def milestone
......
module Gitlab
module GithubImport
class ReleaseFormatter < BaseFormatter
def attributes
{
project: project,
tag: raw_data.tag_name,
description: raw_data.body,
created_at: raw_data.created_at,
updated_at: raw_data.created_at
}
end
def klass
Release
end
def valid?
!raw_data.draft
end
end
end
end
......@@ -18,18 +18,18 @@ module Gitlab
FileUtils.mkdir_p(path)
end
@cmd_output = ""
@cmd_status = 0
cmd_output = ""
cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
yield(stdin) if block_given?
stdin.close
@cmd_output << stdout.read
@cmd_output << stderr.read
@cmd_status = wait_thr.value.exitstatus
cmd_output << stdout.read
cmd_output << stderr.read
cmd_status = wait_thr.value.exitstatus
end
[@cmd_output, @cmd_status]
[cmd_output, cmd_status]
end
end
end
......@@ -102,7 +102,7 @@ module Gitlab
def secret
@secret ||= begin
bytes = Base64.strict_decode64(File.read(secret_path))
bytes = Base64.strict_decode64(File.read(secret_path).chomp)
raise "#{secret_path} does not contain #{SECRET_LENGTH} bytes" if bytes.length != SECRET_LENGTH
bytes
end
......
unless Rails.env.production?
require 'haml_lint/rake_task'
HamlLint::RakeTask.new
end
......@@ -181,6 +181,25 @@ describe ProjectsController do
expect(response).to have_http_status(302)
expect(response).to redirect_to(dashboard_projects_path)
end
context "when the project is forked" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:merge_request) do
create(:merge_request,
source_project: fork_project,
target_project: project)
end
it "closes all related merge requests" do
project.merge_requests << merge_request
sign_in(admin)
delete :destroy, namespace_id: fork_project.namespace.path, id: fork_project.path
expect(merge_request.reload.state).to eq('closed')
end
end
end
describe "POST #toggle_star" do
......
......@@ -30,5 +30,9 @@ FactoryGirl.define do
trait :shared do
is_shared true
end
trait :inactive do
active false
end
end
end
FactoryGirl.define do
sequence :issue_created_at do |n|
4.hours.ago + ( 2 * n ).seconds
end
factory :issue do
title
author
......
require 'rails_helper'
describe 'Profile > SSH Keys', feature: true do
let(:user) { create(:user) }
before do
login_as(user)
visit profile_keys_path
end
describe 'User adds an SSH key' do
it 'auto-populates the title', js: true do
fill_in('Key', with: attributes_for(:key).fetch(:key))
expect(find_field('Title').value).to eq 'dummy@gitlab.com'
end
end
end
......@@ -57,7 +57,7 @@ feature 'Project', feature: true do
describe 'removal', js: true do
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:project) { create(:project, namespace: user.namespace, name: 'project1') }
before do
login_with(user)
......@@ -65,8 +65,12 @@ feature 'Project', feature: true do
visit edit_namespace_project_path(project.namespace, project)
end
it 'removes project' do
it 'removes a project' do
expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1)
expect(page).to have_content "Project 'project1' will be deleted."
expect(Project.all.count).to be_zero
expect(project.issues).to be_empty
expect(project.merge_requests).to be_empty
end
end
......
......@@ -6,6 +6,38 @@ describe SearchHelper do
str
end
describe 'parsing result' do
let(:project) { create(:project) }
let(:repository) { project.repository }
let(:results) { repository.search_files('feature', 'master') }
let(:search_result) { results.first }
subject { helper.parse_search_result(search_result) }
it "returns a valid OpenStruct object" do
is_expected.to be_an OpenStruct
expect(subject.filename).to eq('CHANGELOG')
expect(subject.basename).to eq('CHANGELOG')
expect(subject.ref).to eq('master')
expect(subject.startline).to eq(186)
expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n")
end
context "when filename has extension" do
let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
it { expect(subject.filename).to eq('CONTRIBUTE.md') }
it { expect(subject.basename).to eq('CONTRIBUTE') }
end
context "when file under directory" do
let(:search_result) { "master:a/b/c.md:5:a b c\n" }
it { expect(subject.filename).to eq('a/b/c.md') }
it { expect(subject.basename).to eq('a/b/c') }
end
end
describe 'search_autocomplete_source' do
context "with no current user" do
before do
......
......@@ -127,6 +127,13 @@ describe Banzai::Pipeline::WikiPipeline do
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/nested/twice/page.md\"")
end
it 'rewrites links with anchor' do
markdown = '[Link to Header](start-page#title)'
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include("href=\"#{relative_url_root}/wiki_link_ns/wiki_link_project/wikis/start-page#title\"")
end
end
describe "when creating root links" do
......
......@@ -73,6 +73,12 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do
gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(comment.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns note without created at tag line' do
create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(comment.attributes.fetch(:note)).to eq("I'm having a problem with this.")
end
end
end
end
......@@ -98,6 +98,30 @@ describe Gitlab::GithubImport::Importer, lib: true do
)
end
let(:release1) do
double(
tag_name: 'v1.0.0',
name: 'First release',
body: 'Release v1.0.0',
draft: false,
created_at: created_at,
updated_at: updated_at,
url: 'https://api.github.com/repos/octocat/Hello-World/releases/1'
)
end
let(:release2) do
double(
tag_name: 'v2.0.0',
name: 'Second release',
body: nil,
draft: false,
created_at: created_at,
updated_at: updated_at,
url: 'https://api.github.com/repos/octocat/Hello-World/releases/2'
)
end
before do
allow(project).to receive(:import_data).and_return(double.as_null_object)
allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound)
......@@ -106,6 +130,7 @@ describe Gitlab::GithubImport::Importer, lib: true do
allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2])
allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request])
allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil }))
allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2])
allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error)
end
......@@ -127,8 +152,9 @@ describe Gitlab::GithubImport::Importer, lib: true do
{ type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank, Title is too short (minimum is 0 characters)" },
{ type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Invalid Repository. Use user/repo format." },
{ type: :pull_request, url: "https://api.github.com/repos/octocat/Hello-World/pulls/1347", errors: "Validation failed: Validate branches Cannot Create: This merge request already exists: [\"New feature\"]" },
{ type: :wiki, errors: "Gitlab::Shell::Error" }
]
{ type: :wiki, errors: "Gitlab::Shell::Error" },
{ type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" }
]
}
described_class.new(project).execute
......
......@@ -109,6 +109,12 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do
expect(issue.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(issue.attributes.fetch(:description)).to eq("I'm having a problem with this.")
end
end
end
......
......@@ -140,6 +140,12 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id
end
it 'returns description without created at tag line' do
create(:omniauth_user, extern_uid: octocat.id, provider: 'github')
expect(pull_request.attributes.fetch(:description)).to eq('Please pull these awesome changes')
end
end
context 'when it has a milestone' do
......
require 'spec_helper'
describe Gitlab::GithubImport::ReleaseFormatter, lib: true do
let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:base_data) do
{
tag_name: 'v1.0.0',
name: 'First release',
draft: false,
created_at: created_at,
published_at: created_at,
body: 'Release v1.0.0'
}
end
subject(:release) { described_class.new(project, raw_data) }
describe '#attributes' do
let(:raw_data) { double(base_data) }
it 'returns formatted attributes' do
expected = {
project: project,
tag: 'v1.0.0',
description: 'Release v1.0.0',
created_at: created_at,
updated_at: created_at
}
expect(release.attributes).to eq(expected)
end
end
describe '#valid' do
context 'when release is not a draft' do
let(:raw_data) { double(base_data) }
it 'returns true' do
expect(release.valid?).to eq true
end
end
context 'when release is draft' do
let(:raw_data) { double(base_data.merge(draft: true)) }
it 'returns false' do
expect(release.valid?).to eq false
end
end
end
end
......@@ -30,6 +30,11 @@ describe Gitlab::Workhorse, lib: true do
expect(subject.encoding).to eq(Encoding::ASCII_8BIT)
end
it 'accepts a trailing newline' do
open(described_class.secret_path, 'a') { |f| f.write "\n" }
expect(subject.length).to eq(32)
end
it 'raises an exception if the secret file cannot be read' do
File.delete(described_class.secret_path)
expect { subject }.to raise_exception(Errno::ENOENT)
......
# encoding: utf-8
require 'rails_helper'
describe Blob do
......@@ -7,6 +8,25 @@ describe Blob do
end
end
describe '#data' do
context 'using a binary blob' do
it 'returns the data as-is' do
data = "\n\xFF\xB9\xC3"
blob = described_class.new(double(binary?: true, data: data))
expect(blob.data).to eq(data)
end
end
context 'using a text blob' do
it 'converts the data to UTF-8' do
blob = described_class.new(double(binary?: false, data: "\n\xFF\xB9\xC3"))
expect(blob.data).to eq("\n���")
end
end
end
describe '#svg?' do
it 'is falsey when not text' do
git_blob = double(text?: false)
......
......@@ -231,6 +231,34 @@ describe Ci::Build, models: true do
it { is_expected.to eq(predefined_variables) }
end
context 'when build has user' do
let(:user) { create(:user, username: 'starter') }
let(:user_variables) do
[
{ key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
{ key: 'GITLAB_USER_EMAIL', value: user.email, public: true }
]
end
before do
build.update_attributes(user: user)
end
it { user_variables.each { |v| is_expected.to include(v) } }
end
context 'when build started manually' do
before do
build.update_attributes(when: :manual)
end
let(:manual_variable) do
{ key: 'CI_BUILD_MANUAL', value: 'true', public: true }
end
it { is_expected.to include(manual_variable) }
end
context 'when build is for tag' do
let(:tag_variable) do
{ key: 'CI_BUILD_TAG', value: 'master', public: true }
......
......@@ -57,7 +57,7 @@ describe Member, models: true do
describe 'Scopes & finders' do
before do
project = create(:project)
project = create(:empty_project)
group = create(:group)
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
......@@ -65,6 +65,15 @@ describe Member, models: true do
@master_user = create(:user).tap { |u| project.team << [u, :master] }
@master = project.members.find_by(user_id: @master_user.id)
@blocked_user = create(:user).tap do |u|
project.team << [u, :master]
project.team << [u, :developer]
u.block!
end
@blocked_master = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::MASTER)
@blocked_developer = project.members.find_by(user_id: @blocked_user.id, access_level: Gitlab::Access::DEVELOPER)
Member.add_user(
project.members,
'toto1@example.com',
......@@ -73,7 +82,7 @@ describe Member, models: true do
)
@invited_member = project.members.invite.find_by_invite_email('toto1@example.com')
accepted_invite_user = build(:user)
accepted_invite_user = build(:user, state: :active)
Member.add_user(
project.members,
'toto2@example.com',
......@@ -91,7 +100,7 @@ describe Member, models: true do
describe '.access_for_user_ids' do
it 'returns the right access levels' do
users = [@owner_user.id, @master_user.id]
users = [@owner_user.id, @master_user.id, @blocked_user.id]
expected = {
@owner_user.id => Gitlab::Access::OWNER,
@master_user.id => Gitlab::Access::MASTER
......@@ -125,6 +134,19 @@ describe Member, models: true do
it { expect(described_class.request).not_to include @accepted_request_member }
end
describe '.developers' do
subject { described_class.developers.to_a }
it { is_expected.not_to include @owner }
it { is_expected.not_to include @master }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
it { is_expected.not_to include @blocked_master }
it { is_expected.not_to include @blocked_developer }
end
describe '.owners_and_masters' do
it { expect(described_class.owners_and_masters).to include @owner }
it { expect(described_class.owners_and_masters).to include @master }
......@@ -132,6 +154,20 @@ describe Member, models: true do
it { expect(described_class.owners_and_masters).not_to include @accepted_invite_member }
it { expect(described_class.owners_and_masters).not_to include @requested_member }
it { expect(described_class.owners_and_masters).not_to include @accepted_request_member }
it { expect(described_class.owners_and_masters).not_to include @blocked_master }
end
describe '.has_access' do
subject { described_class.has_access.to_a }
it { is_expected.to include @owner }
it { is_expected.to include @master }
it { is_expected.to include @invited_member }
it { is_expected.to include @accepted_invite_member }
it { is_expected.not_to include @requested_member }
it { is_expected.to include @accepted_request_member }
it { is_expected.not_to include @blocked_master }
it { is_expected.not_to include @blocked_developer }
end
end
......
......@@ -1038,4 +1038,81 @@ describe MergeRequest, models: true do
end
end
end
describe '#closed_without_source_project?' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
let(:destroy_service) { Projects::DestroyService.new(fork_project, user) }
context 'when the merge request is closed' do
let(:closed_merge_request) do
create(:closed_merge_request,
source_project: fork_project,
target_project: project)
end
it 'returns false if the source project exists' do
expect(closed_merge_request.closed_without_source_project?).to be_falsey
end
it 'returns true if the source project does not exist' do
destroy_service.execute
closed_merge_request.reload
expect(closed_merge_request.closed_without_source_project?).to be_truthy
end
end
context 'when the merge request is open' do
it 'returns false' do
expect(subject.closed_without_source_project?).to be_falsey
end
end
end
describe '#reopenable?' do
context 'when the merge request is closed' do
it 'returns true' do
subject.close
expect(subject.reopenable?).to be_truthy
end
context 'forked project' do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) }
let(:merge_request) do
create(:closed_merge_request,
source_project: fork_project,
target_project: project)
end
it 'returns false if unforked' do
Projects::UnlinkForkService.new(fork_project, user).execute
expect(merge_request.reload.reopenable?).to be_falsey
end
it 'returns false if the source project is deleted' do
Projects::DestroyService.new(fork_project, user).execute
expect(merge_request.reload.reopenable?).to be_falsey
end
it 'returns false if the merge request is merged' do
merge_request.update_attributes(state: 'merged')
expect(merge_request.reload.reopenable?).to be_falsey
end
end
end
context 'when the merge request is opened' do
it 'returns false' do
expect(subject.reopenable?).to be_falsey
end
end
end
end
......@@ -6,6 +6,7 @@ describe Project, models: true do
it { is_expected.to belong_to(:namespace) }
it { is_expected.to belong_to(:creator).class_name('User') }
it { is_expected.to have_many(:users) }
it { is_expected.to have_many(:services) }
it { is_expected.to have_many(:events).dependent(:destroy) }
it { is_expected.to have_many(:merge_requests).dependent(:destroy) }
it { is_expected.to have_many(:issues).dependent(:destroy) }
......@@ -24,6 +25,30 @@ describe Project, models: true do
it { is_expected.to have_one(:pushover_service).dependent(:destroy) }
it { is_expected.to have_one(:asana_service).dependent(:destroy) }
it { is_expected.to have_one(:board).dependent(:destroy) }
it { is_expected.to have_one(:campfire_service).dependent(:destroy) }
it { is_expected.to have_one(:drone_ci_service).dependent(:destroy) }
it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
it { is_expected.to have_one(:builds_email_service).dependent(:destroy) }
it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
it { is_expected.to have_one(:irker_service).dependent(:destroy) }
it { is_expected.to have_one(:pivotaltracker_service).dependent(:destroy) }
it { is_expected.to have_one(:hipchat_service).dependent(:destroy) }
it { is_expected.to have_one(:flowdock_service).dependent(:destroy) }
it { is_expected.to have_one(:assembla_service).dependent(:destroy) }
it { is_expected.to have_one(:gemnasium_service).dependent(:destroy) }
it { is_expected.to have_one(:buildkite_service).dependent(:destroy) }
it { is_expected.to have_one(:bamboo_service).dependent(:destroy) }
it { is_expected.to have_one(:teamcity_service).dependent(:destroy) }
it { is_expected.to have_one(:jira_service).dependent(:destroy) }
it { is_expected.to have_one(:redmine_service).dependent(:destroy) }
it { is_expected.to have_one(:custom_issue_tracker_service).dependent(:destroy) }
it { is_expected.to have_one(:bugzilla_service).dependent(:destroy) }
it { is_expected.to have_one(:gitlab_issue_tracker_service).dependent(:destroy) }
it { is_expected.to have_one(:external_wiki_service).dependent(:destroy) }
it { is_expected.to have_one(:project_feature).dependent(:destroy) }
it { is_expected.to have_one(:import_data).class_name('ProjectImportData').dependent(:destroy) }
it { is_expected.to have_one(:last_event).class_name('Event') }
it { is_expected.to have_one(:forked_from_project).through(:forked_project_link) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:pipelines) }
it { is_expected.to have_many(:builds) }
......@@ -31,9 +56,16 @@ describe Project, models: true do
it { is_expected.to have_many(:runners) }
it { is_expected.to have_many(:variables) }
it { is_expected.to have_many(:triggers) }
it { is_expected.to have_many(:labels).dependent(:destroy) }
it { is_expected.to have_many(:users_star_projects).dependent(:destroy) }
it { is_expected.to have_many(:environments).dependent(:destroy) }
it { is_expected.to have_many(:deployments).dependent(:destroy) }
it { is_expected.to have_many(:todos).dependent(:destroy) }
it { is_expected.to have_many(:releases).dependent(:destroy) }
it { is_expected.to have_many(:lfs_objects_projects).dependent(:destroy) }
it { is_expected.to have_many(:project_group_links).dependent(:destroy) }
it { is_expected.to have_many(:notification_settings).dependent(:destroy) }
it { is_expected.to have_many(:forks).through(:forked_project_links) }
describe '#members & #requesters' do
let(:project) { create(:project) }
......@@ -178,7 +210,7 @@ describe Project, models: true do
expect(project.runners_token).not_to eq('')
end
it 'does not set an random toke if one provided' do
it 'does not set an random token if one provided' do
project = FactoryGirl.create :empty_project, runners_token: 'my-token'
expect(project.runners_token).to eq('my-token')
end
......@@ -1497,4 +1529,56 @@ describe Project, models: true do
project.change_head(project.default_branch)
end
end
describe '#pushes_since_gc' do
let(:project) { create(:project) }
after do
project.reset_pushes_since_gc
end
context 'without any pushes' do
it 'returns 0' do
expect(project.pushes_since_gc).to eq(0)
end
end
context 'with a number of pushes' do
it 'returns the number of pushes' do
3.times { project.increment_pushes_since_gc }
expect(project.pushes_since_gc).to eq(3)
end
end
end
describe '#increment_pushes_since_gc' do
let(:project) { create(:project) }
after do
project.reset_pushes_since_gc
end
it 'increments the number of pushes since the last GC' do
3.times { project.increment_pushes_since_gc }
expect(project.pushes_since_gc).to eq(3)
end
end
describe '#reset_pushes_since_gc' do
let(:project) { create(:project) }
after do
project.reset_pushes_since_gc
end
it 'resets the number of pushes since the last GC' do
3.times { project.increment_pushes_since_gc }
project.reset_pushes_since_gc
expect(project.pushes_since_gc).to eq(0)
end
end
end
......@@ -186,32 +186,6 @@ describe Repository, models: true do
it { is_expected.to be_an String }
it { expect(subject.lines[2]).to eq("master:CHANGELOG:188: - Feature: Replace teams with group membership\n") }
end
describe 'parsing result' do
subject { repository.parse_search_result(search_result) }
let(:search_result) { results.first }
it { is_expected.to be_an OpenStruct }
it { expect(subject.filename).to eq('CHANGELOG') }
it { expect(subject.basename).to eq('CHANGELOG') }
it { expect(subject.ref).to eq('master') }
it { expect(subject.startline).to eq(186) }
it { expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") }
context "when filename has extension" do
let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" }
it { expect(subject.filename).to eq('CONTRIBUTE.md') }
it { expect(subject.basename).to eq('CONTRIBUTE') }
end
context "when file under directory" do
let(:search_result) { "master:a/b/c.md:5:a b c\n" }
it { expect(subject.filename).to eq('a/b/c.md') }
it { expect(subject.basename).to eq('a/b/c') }
end
end
end
describe "#changelog" do
......
......@@ -117,17 +117,36 @@ describe API::CommitStatuses, api: true do
let(:post_url) { "/projects/#{project.id}/statuses/#{sha}" }
context 'developer user' do
context 'only required parameters' do
before { post api(post_url, developer), state: 'success' }
%w[pending running success failed canceled].each do |status|
context "for #{status}" do
context 'uses only required parameters' do
it 'creates commit status' do
post api(post_url, developer), state: status
expect(response).to have_http_status(201)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq(status)
expect(json_response['name']).to eq('default')
expect(json_response['ref']).not_to be_empty
expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil
end
end
end
end
it 'creates commit status' do
expect(response).to have_http_status(201)
expect(json_response['sha']).to eq(commit.id)
expect(json_response['status']).to eq('success')
expect(json_response['name']).to eq('default')
expect(json_response['ref']).to be_nil
expect(json_response['target_url']).to be_nil
expect(json_response['description']).to be_nil
context 'transitions status from pending' do
before do
post api(post_url, developer), state: 'pending'
end
%w[running success failed canceled].each do |status|
it "to #{status}" do
expect { post api(post_url, developer), state: status }.not_to change { CommitStatus.count }
expect(response).to have_http_status(201)
expect(json_response['status']).to eq(status)
end
end
end
......
......@@ -18,6 +18,7 @@ describe API::API, api: true do
project: project,
state: :closed,
milestone: milestone,
created_at: generate(:issue_created_at),
updated_at: 3.hours.ago
end
let!(:confidential_issue) do
......@@ -26,6 +27,7 @@ describe API::API, api: true do
project: project,
author: author,
assignee: assignee,
created_at: generate(:issue_created_at),
updated_at: 2.hours.ago
end
let!(:issue) do
......@@ -34,6 +36,7 @@ describe API::API, api: true do
assignee: user,
project: project,
milestone: milestone,
created_at: generate(:issue_created_at),
updated_at: 1.hour.ago
end
let!(:label) do
......
......@@ -15,6 +15,25 @@ describe Ci::API::API do
describe "POST /builds/register" do
let!(:build) { create(:ci_build, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
let(:user_agent) { 'gitlab-ci-multi-runner 1.5.2 (1-5-stable; go1.6.3; linux/amd64)' }
shared_examples 'no builds available' do
context 'when runner sends version in User-Agent' do
context 'for stable version' do
it { expect(response).to have_http_status(204) }
end
context 'for beta version' do
let(:user_agent) { 'gitlab-ci-multi-runner 1.6.0~beta.167.g2b2bacc (1-5-stable; go1.6.3; linux/amd64)' }
it { expect(response).to have_http_status(204) }
end
end
context "when runner doesn't send version in User-Agent" do
let(:user_agent) { 'Go-http-client/1.1' }
it { expect(response).to have_http_status(404) }
end
end
it "starts a build" do
register_builds info: { platform: :darwin }
......@@ -33,36 +52,30 @@ describe Ci::API::API do
context 'when builds are finished' do
before do
build.success
end
it "returns 404 error if no builds for specific runner" do
register_builds
expect(response).to have_http_status(404)
end
it_behaves_like 'no builds available'
end
context 'for other project with builds' do
before do
build.success
create(:ci_build, :pending)
end
it "returns 404 error if no builds for shared runner" do
register_builds
expect(response).to have_http_status(404)
end
it_behaves_like 'no builds available'
end
context 'for shared runner' do
let(:shared_runner) { create(:ci_runner, token: "SharedRunner") }
it "should return 404 error if no builds for shared runner" do
before do
register_builds shared_runner.token
expect(response).to have_http_status(404)
end
it_behaves_like 'no builds available'
end
context 'for triggered build' do
......@@ -136,18 +149,27 @@ describe Ci::API::API do
end
context 'when runner is not allowed to pick untagged builds' do
before { runner.update_column(:run_untagged, false) }
it 'does not pick build' do
before do
runner.update_column(:run_untagged, false)
register_builds
expect(response).to have_http_status 404
end
it_behaves_like 'no builds available'
end
end
context 'when runner is paused' do
let(:inactive_runner) { create(:ci_runner, :inactive, token: "InactiveRunner") }
before do
register_builds inactive_runner.token
end
it { expect(response).to have_http_status 404 }
end
def register_builds(token = runner.token, **params)
post ci_api("/builds/register"), params.merge(token: token)
post ci_api("/builds/register"), params.merge(token: token), { 'User-Agent' => user_agent }
end
end
......
......@@ -4,12 +4,11 @@ describe Projects::HousekeepingService do
subject { Projects::HousekeepingService.new(project) }
let(:project) { create :project }
describe 'execute' do
before do
project.pushes_since_gc = 3
project.save!
end
after do
project.reset_pushes_since_gc
end
describe '#execute' do
it 'enqueues a sidekiq job' do
expect(subject).to receive(:try_obtain_lease).and_return(true)
expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id)
......@@ -32,12 +31,12 @@ describe Projects::HousekeepingService do
it 'does not reset pushes_since_gc' do
expect do
expect { subject.execute }.to raise_error(Projects::HousekeepingService::LeaseTaken)
end.not_to change { project.pushes_since_gc }.from(3)
end.not_to change { project.pushes_since_gc }
end
end
end
describe 'needed?' do
describe '#needed?' do
it 'when the count is low enough' do
expect(subject.needed?).to eq(false)
end
......@@ -48,25 +47,11 @@ describe Projects::HousekeepingService do
end
end
describe 'increment!' do
let(:lease_key) { "project_housekeeping:increment!:#{project.id}" }
describe '#increment!' do
it 'increments the pushes_since_gc counter' do
lease = double(:lease, try_obtain: true)
expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
expect do
subject.increment!
end.to change { project.pushes_since_gc }.from(0).to(1)
end
it 'does not increment when no lease can be obtained' do
lease = double(:lease, try_obtain: false)
expect(Gitlab::ExclusiveLease).to receive(:new).with(lease_key, anything).and_return(lease)
expect do
subject.increment!
end.not_to change { project.pushes_since_gc }
end
end
end
......@@ -3,5 +3,4 @@ build/
nbbuild/
dist/
nbdist/
nbactions.xml
.nb-gradle/
......@@ -9,6 +9,7 @@ gtags.files
GTAGS
GRTAGS
GPATH
GSYMS
cscope.files
cscope.out
cscope.in.out
......
......@@ -3,7 +3,8 @@
.LSOverride
# Icon must end with two \r
Icon
Icon
# Thumbnails
._*
......
......@@ -17,3 +17,4 @@ cabal.sandbox.config
*.eventlog
.stack-work/
cabal.project.local
.HTF/
......@@ -52,6 +52,7 @@
/administrator/language/en-GB/en-GB.plg_content_contact.sys.ini
/administrator/language/en-GB/en-GB.plg_content_finder.ini
/administrator/language/en-GB/en-GB.plg_content_finder.sys.ini
/administrator/language/en-GB/en-GB.plg_editors-xtd_module*
/administrator/language/en-GB/en-GB.plg_finder_categories.ini
/administrator/language/en-GB/en-GB.plg_finder_categories.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_contacts.ini
......@@ -64,6 +65,10 @@
/administrator/language/en-GB/en-GB.plg_finder_tags.sys.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.ini
/administrator/language/en-GB/en-GB.plg_finder_weblinks.sys.ini
/administrator/language/en-GB/en-GB.plg_installer_folderinstaller*
/administrator/language/en-GB/en-GB.plg_installer_packageinstaller*
/administrator/language/en-GB/en-GB.plg_installer_packageinstaller
/administrator/language/en-GB/en-GB.plg_installer_urlinstaller*
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.ini
/administrator/language/en-GB/en-GB.plg_installer_webinstaller.sys.ini
/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.ini
......@@ -72,6 +77,8 @@
/administrator/language/en-GB/en-GB.plg_search_tags.sys.ini
/administrator/language/en-GB/en-GB.plg_system_languagecode.ini
/administrator/language/en-GB/en-GB.plg_system_languagecode.sys.ini
/administrator/language/en-GB/en-GB.plg_system_stats*
/administrator/language/en-GB/en-GB.plg_system_updatenotification*
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.ini
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.sys.ini
/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.ini
......@@ -249,8 +256,10 @@
/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini
/administrator/language/en-GB/en-GB.xml
/administrator/language/en-GB/index.html
/administrator/language/ru-RU/index.html
/administrator/language/overrides/*
/administrator/language/index.html
/administrator/logs/index.html
/administrator/manifests/*
/administrator/modules/mod_custom/*
/administrator/modules/mod_feed/*
......@@ -289,6 +298,7 @@
/components/com_finder/*
/components/com_mailto/*
/components/com_media/*
/components/com_modules/*
/components/com_newsfeeds/*
/components/com_search/*
/components/com_users/*
......@@ -407,6 +417,7 @@
/libraries/idna_convert/*
/libraries/joomla/*
/libraries/legacy/*
/libraries/php-encryption/*
/libraries/phpass/*
/libraries/phpmailer/*
/libraries/phputf8/*
......@@ -431,9 +442,11 @@
/media/media/*
/media/mod_languages/*
/media/overrider/*
/media/plg_captcha_recaptcha/*
/media/plg_quickicon_extensionupdate/*
/media/plg_quickicon_joomlaupdate/*
/media/plg_system_highlight/*
/media/plg_system_stats/*
/media/system/*
/media/index.html
/modules/mod_articles_archive/*
......@@ -486,6 +499,7 @@
/plugins/editors/none/*
/plugins/editors/tinymce/*
/plugins/editors/index.html
/plugins/editors-xtd/module/*
/plugins/editors-xtd/article/*
/plugins/editors-xtd/image/*
/plugins/editors-xtd/pagebreak/*
......@@ -523,6 +537,8 @@
/plugins/system/redirect/*
/plugins/system/remember/*
/plugins/system/sef/*
/plugins/system/stats/*
/plugins/system/updatenotification/*
/plugins/system/index.html
/plugins/twofactorauth/*
/plugins/user/contactcreator/*
......
......@@ -34,5 +34,8 @@ jspm_packages
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
......@@ -50,7 +50,9 @@ Carthage/Build
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Code Injection
#
......
......@@ -79,6 +79,7 @@ celerybeat-schedule
.env
# virtualenv
.venv/
venv/
ENV/
......
......@@ -12,9 +12,11 @@ capybara-*.html
rerun.txt
pickle-email-*.html
# TODO Comment out these rules if you are OK with secrets being uploaded to the repo
# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb
config/secrets.yml
# Only include if you have production secrets in this file, which is no longer a Rails default
# config/secrets.yml
# dotenv
# TODO Comment out this rule if environment variables can be committed
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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