Merge remote-tracking branch 'upstream/pipeline-hooks-without-slack' into wall-clock-time-for-showing-pipeline
* upstream/pipeline-hooks-without-slack: Make explicit call for all event types for ProjectHook factory Capitalise URL on web_hooks/form Remove changes not related to this MR Added documentation for pipeline hooks Rename queue to enqueue in tests Instrument Project.visible_to_user Fix build play failure Update ruby 2.3.1 Improve transition between states for event `enqueue` Use event `enqueue` instead of `queue` Fix test failures Fix bug where destroying a namespace would not always destroy projects Remove unused SpamReport model; this was renamed to SpamLog Corrected links/usernames in performance guide Add gravatars to build history Add deployment ID and gravatar to environments page Format environment history page Add avatar to commit message; environment style updates to match pipelines page Style deploy button
This commit is contained in:
commit
0ea81ae50a
37 changed files with 466 additions and 108 deletions
|
@ -1,7 +1,7 @@
|
|||
image: "ruby:2.3"
|
||||
image: "ruby:2.3.1"
|
||||
|
||||
cache:
|
||||
key: "ruby-23"
|
||||
key: "ruby-231"
|
||||
paths:
|
||||
- vendor/apt
|
||||
- vendor/ruby
|
||||
|
|
|
@ -26,6 +26,7 @@ v 8.11.0 (unreleased)
|
|||
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
|
||||
- Show wall clock time when showing a pipeline. !5734
|
||||
- Show member roles to all users on members page
|
||||
- Project.visible_to_user is instrumented again
|
||||
- Fix awardable button mutuality loading spinners (ClemMakesApps)
|
||||
- Add support for using RequestStore within Sidekiq tasks via SIDEKIQ_REQUEST_STORE env variable
|
||||
- Optimize maximum user access level lookup in loading of notes
|
||||
|
@ -94,6 +95,7 @@ v 8.11.0 (unreleased)
|
|||
- Bump gitlab_git to lazy load compare commits
|
||||
- Reduce number of queries made for merge_requests/:id/diffs
|
||||
- Sensible state specific default sort order for issues and merge requests !5453 (tomb0y)
|
||||
- Fix bug where destroying a namespace would not always destroy projects
|
||||
- Fix RequestProfiler::Middleware error when code is reloaded in development
|
||||
- Catch what warden might throw when profiling requests to re-throw it
|
||||
- Avoid commit lookup on diff_helper passing existing local variable to the helper method
|
||||
|
|
|
@ -1,5 +1,35 @@
|
|||
.environments {
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.fa-play {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-new {
|
||||
color: $table-text-gray;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
||||
.fa {
|
||||
margin-right: 6px;
|
||||
color: $table-text-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.branch-name {
|
||||
color: $gl-dark-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.table.builds.environments {
|
||||
min-width: 500px;
|
||||
|
||||
.icon-container {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,9 +48,9 @@ class Admin::GroupsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DestroyGroupService.new(@group, current_user).execute
|
||||
DestroyGroupService.new(@group, current_user).async_execute
|
||||
|
||||
redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
|
||||
redirect_to admin_groups_path, alert: "Group '#{@group.name}' was scheduled for deletion."
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -87,9 +87,9 @@ class GroupsController < Groups::ApplicationController
|
|||
end
|
||||
|
||||
def destroy
|
||||
DestroyGroupService.new(@group, current_user).execute
|
||||
DestroyGroupService.new(@group, current_user).async_execute
|
||||
|
||||
redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted."
|
||||
redirect_to root_path, alert: "Group '#{@group.name}' was scheduled for deletion."
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -7,8 +7,6 @@ module AvatarsHelper
|
|||
}))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_avatar(options = {})
|
||||
avatar_size = options[:size] || 16
|
||||
user_name = options[:user].try(:name) || options[:user_name]
|
||||
|
|
|
@ -59,7 +59,7 @@ module Ci
|
|||
when: build.when,
|
||||
user: user,
|
||||
environment: build.environment,
|
||||
status_event: 'queue'
|
||||
status_event: 'enqueue'
|
||||
)
|
||||
MergeRequests::AddTodoWhenBuildFailsService.new(build.project, nil).close(new_build)
|
||||
new_build
|
||||
|
@ -102,7 +102,7 @@ module Ci
|
|||
|
||||
def play(current_user = nil)
|
||||
# Try to queue a current build
|
||||
if self.queue
|
||||
if self.enqueue
|
||||
self.update(user: current_user)
|
||||
self
|
||||
else
|
||||
|
|
|
@ -22,9 +22,9 @@ module Ci
|
|||
delegate :stages, to: :statuses
|
||||
|
||||
state_machine :status, initial: :created do
|
||||
event :queue do
|
||||
event :enqueue do
|
||||
transition created: :pending
|
||||
transition any - [:created, :pending] => :running
|
||||
transition [:success, :failed, :canceled, :skipped] => :running
|
||||
end
|
||||
|
||||
event :run do
|
||||
|
@ -234,18 +234,12 @@ module Ci
|
|||
|
||||
def build_updated
|
||||
case latest_builds_status
|
||||
when 'pending'
|
||||
queue
|
||||
when 'running'
|
||||
run
|
||||
when 'success'
|
||||
succeed
|
||||
when 'failed'
|
||||
drop
|
||||
when 'canceled'
|
||||
cancel
|
||||
when 'skipped'
|
||||
skip
|
||||
when 'pending' then enqueue
|
||||
when 'running' then run
|
||||
when 'success' then succeed
|
||||
when 'failed' then drop
|
||||
when 'canceled' then cancel
|
||||
when 'skipped' then skip
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class CommitStatus < ActiveRecord::Base
|
|||
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
|
||||
|
||||
state_machine :status do
|
||||
event :queue do
|
||||
event :enqueue do
|
||||
transition [:created, :skipped] => :pending
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
class Namespace < ActiveRecord::Base
|
||||
acts_as_paranoid
|
||||
|
||||
include Sortable
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
class SpamReport < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
|
||||
validates :user, presence: true
|
||||
end
|
|
@ -37,7 +37,7 @@ module Ci
|
|||
return false unless Statuseable::COMPLETED_STATUSES.include?(current_status)
|
||||
|
||||
if valid_statuses_for_when(build.when).include?(current_status)
|
||||
build.queue
|
||||
build.enqueue
|
||||
true
|
||||
else
|
||||
build.skip
|
||||
|
|
|
@ -21,6 +21,11 @@ class DeleteUserService
|
|||
::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
|
||||
end
|
||||
|
||||
user.destroy
|
||||
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
|
||||
namespace = user.namespace
|
||||
user_data = user.destroy
|
||||
namespace.really_destroy!
|
||||
|
||||
user_data
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,13 +5,23 @@ class DestroyGroupService
|
|||
@group, @current_user = group, user
|
||||
end
|
||||
|
||||
def execute
|
||||
group.projects.each do |project|
|
||||
# Skip repository removal because we remove directory with namespace
|
||||
# that contain all this repositories
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute
|
||||
def async_execute
|
||||
group.transaction do
|
||||
# Soft delete via paranoia gem
|
||||
group.destroy
|
||||
job_id = GroupDestroyWorker.perform_async(group.id, current_user.id)
|
||||
Rails.logger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}")
|
||||
end
|
||||
end
|
||||
|
||||
group.destroy
|
||||
def execute
|
||||
group.projects.each do |project|
|
||||
# Execute the destruction of the models immediately to ensure atomic cleanup.
|
||||
# Skip repository removal because we remove directory with namespace
|
||||
# that contain all these repositories
|
||||
::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
|
||||
end
|
||||
|
||||
group.really_destroy!
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
.pull-right
|
||||
- actions = deployment.manual_actions
|
||||
- if actions.present?
|
||||
.btn-group.inline
|
||||
.btn-group
|
||||
%a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
.inline
|
||||
.dropdown
|
||||
%a.dropdown-new.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
= icon("play")
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
%div.branch-commit
|
||||
- if deployment.ref
|
||||
= link_to deployment.ref, namespace_project_commits_path(@project.namespace, @project, deployment.ref), class: "monospace"
|
||||
·
|
||||
.icon-container
|
||||
= deployment.tag? ? icon('tag') : icon('code-fork')
|
||||
= link_to deployment.ref, namespace_project_commits_path(@project.namespace, @project, deployment.ref), class: "monospace branch-name"
|
||||
.icon-container
|
||||
= custom_icon("icon_commit")
|
||||
= link_to deployment.short_sha, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-id monospace"
|
||||
|
||||
%p.commit-title
|
||||
%span
|
||||
- if commit_title = deployment.commit_title
|
||||
= author_avatar(deployment.commit, size: 20)
|
||||
= link_to_gfm commit_title, namespace_project_commit_path(@project.namespace, @project, deployment.sha), class: "commit-row-message"
|
||||
- else
|
||||
Cant find HEAD commit for this branch
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
%td
|
||||
- if deployment.deployable
|
||||
= link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable] do
|
||||
= user_avatar(user: deployment.user, size: 20)
|
||||
= "#{deployment.deployable.name} (##{deployment.deployable.id})"
|
||||
|
||||
%td
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
%tr.environment
|
||||
%td
|
||||
%strong
|
||||
= link_to environment.name, namespace_project_environment_path(@project.namespace, @project, environment)
|
||||
|
||||
%td
|
||||
- if last_deployment
|
||||
= user_avatar(user: last_deployment.user, size: 20)
|
||||
%strong ##{last_deployment.id}
|
||||
|
||||
%td
|
||||
- if last_deployment
|
||||
= render 'projects/deployments/commit', deployment: last_deployment
|
||||
|
|
|
@ -23,10 +23,11 @@
|
|||
New environment
|
||||
- else
|
||||
.table-holder
|
||||
%table.table.environments
|
||||
%table.table.builds.environments
|
||||
%tbody
|
||||
%th Environment
|
||||
%th Last deployment
|
||||
%th Date
|
||||
%th Last Deployment
|
||||
%th Commit
|
||||
%th
|
||||
%th
|
||||
= render @environments
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
= link_to "Read more", help_page_path("ci/environments"), class: "btn btn-success"
|
||||
- else
|
||||
.table-holder
|
||||
%table.table.environments
|
||||
%table.table.builds.environments
|
||||
%thead
|
||||
%tr
|
||||
%th ID
|
||||
%th Commit
|
||||
%th Build
|
||||
%th Date
|
||||
%th
|
||||
%th
|
||||
|
||||
= render @deployments
|
||||
|
|
|
@ -29,56 +29,56 @@
|
|||
= f.label :push_events, class: 'list-label' do
|
||||
%strong Push events
|
||||
%p.light
|
||||
This url will be triggered by a push to the repository
|
||||
This URL will be triggered by a push to the repository
|
||||
%li
|
||||
= f.check_box :tag_push_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :tag_push_events, class: 'list-label' do
|
||||
%strong Tag push events
|
||||
%p.light
|
||||
This url will be triggered when a new tag is pushed to the repository
|
||||
This URL will be triggered when a new tag is pushed to the repository
|
||||
%li
|
||||
= f.check_box :note_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :note_events, class: 'list-label' do
|
||||
%strong Comments
|
||||
%p.light
|
||||
This url will be triggered when someone adds a comment
|
||||
This URL will be triggered when someone adds a comment
|
||||
%li
|
||||
= f.check_box :issues_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :issues_events, class: 'list-label' do
|
||||
%strong Issues events
|
||||
%p.light
|
||||
This url will be triggered when an issue is created/updated/merged
|
||||
This URL will be triggered when an issue is created/updated/merged
|
||||
%li
|
||||
= f.check_box :merge_requests_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :merge_requests_events, class: 'list-label' do
|
||||
%strong Merge Request events
|
||||
%p.light
|
||||
This url will be triggered when a merge request is created/updated/merged
|
||||
This URL will be triggered when a merge request is created/updated/merged
|
||||
%li
|
||||
= f.check_box :build_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :build_events, class: 'list-label' do
|
||||
%strong Build events
|
||||
%p.light
|
||||
This url will be triggered when the build status changes
|
||||
This URL will be triggered when the build status changes
|
||||
%li
|
||||
= f.check_box :pipeline_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :pipeline_events, class: 'list-label' do
|
||||
%strong Pipeline events
|
||||
%p.light
|
||||
This url will be triggered when the pipeline status changes
|
||||
This URL will be triggered when the pipeline status changes
|
||||
%li
|
||||
= f.check_box :wiki_page_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :wiki_page_events, class: 'list-label' do
|
||||
%strong Wiki Page events
|
||||
%p.light
|
||||
This url will be triggered when a wiki page is created/updated
|
||||
This URL will be triggered when a wiki page is created/updated
|
||||
.form-group
|
||||
= f.label :enable_ssl_verification, "SSL verification", class: 'label-light checkbox'
|
||||
.checkbox
|
||||
|
|
17
app/workers/group_destroy_worker.rb
Normal file
17
app/workers/group_destroy_worker.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
class GroupDestroyWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options queue: :default
|
||||
|
||||
def perform(group_id, user_id)
|
||||
begin
|
||||
group = Group.with_deleted.find(group_id)
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
return
|
||||
end
|
||||
|
||||
user = User.find(user_id)
|
||||
|
||||
DestroyGroupService.new(group, user).execute
|
||||
end
|
||||
end
|
|
@ -148,6 +148,9 @@ if Gitlab::Metrics.enabled?
|
|||
|
||||
config.instrument_methods(Gitlab::Highlight)
|
||||
config.instrument_instance_methods(Gitlab::Highlight)
|
||||
|
||||
# This is a Rails scope so we have to instrument it manually.
|
||||
config.instrument_method(Project, :visible_to_user)
|
||||
end
|
||||
|
||||
GC::Profiler.enable
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# rubocop:disable all
|
||||
class FixNamespaces < ActiveRecord::Migration
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
Namespace.where('name <> path and type is null').each do |namespace|
|
||||
namespace.update_attribute(:name, namespace.path)
|
||||
namespaces = exec_query('SELECT id, path FROM namespaces WHERE name <> path and type is null')
|
||||
|
||||
namespaces.each do |row|
|
||||
id = row['id']
|
||||
path = row['path']
|
||||
exec_query("UPDATE namespaces SET name = '#{path}' WHERE id = #{id}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
12
db/migrate/20160805041956_add_deleted_at_to_namespaces.rb
Normal file
12
db/migrate/20160805041956_add_deleted_at_to_namespaces.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class AddDeletedAtToNamespaces < ActiveRecord::Migration
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_column :namespaces, :deleted_at, :datetime
|
||||
add_concurrent_index :namespaces, :deleted_at
|
||||
end
|
||||
end
|
|
@ -640,9 +640,11 @@ ActiveRecord::Schema.define(version: 20160810142633) do
|
|||
t.boolean "share_with_group_lock", default: false
|
||||
t.integer "visibility_level", default: 20, null: false
|
||||
t.boolean "request_access_enabled", default: true, null: false
|
||||
t.datetime "deleted_at"
|
||||
end
|
||||
|
||||
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
|
||||
add_index "namespaces", ["deleted_at"], name: "index_namespaces_on_deleted_at", using: :btree
|
||||
add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree
|
||||
add_index "namespaces", ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
|
||||
add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
|
||||
|
|
|
@ -15,8 +15,8 @@ The process of solving performance problems is roughly as follows:
|
|||
3. Add your findings based on the measurement period (screenshots of graphs,
|
||||
timings, etc) to the issue mentioned in step 1.
|
||||
4. Solve the problem.
|
||||
5. Create a merge request, assign the "performance" label and ping the right
|
||||
people (e.g. [@yorickpeterse][yorickpeterse] and [@joshfng][joshfng]).
|
||||
5. Create a merge request, assign the "Performance" label and assign it to
|
||||
[@yorickpeterse][yorickpeterse] for reviewing.
|
||||
6. Once a change has been deployed make sure to _again_ measure for at least 24
|
||||
hours to see if your changes have any impact on the production environment.
|
||||
7. Repeat until you're done.
|
||||
|
@ -36,8 +36,8 @@ graphs/dashboards.
|
|||
|
||||
GitLab provides two built-in tools to aid the process of improving performance:
|
||||
|
||||
* [Sherlock](doc/development/profiling.md#sherlock)
|
||||
* [GitLab Performance Monitoring](doc/monitoring/performance/monitoring.md)
|
||||
* [Sherlock](profiling.md#sherlock)
|
||||
* [GitLab Performance Monitoring](../monitoring/performance/monitoring.md)
|
||||
|
||||
GitLab employees can use GitLab.com's performance monitoring systems located at
|
||||
<http://performance.gitlab.net>, this requires you to log in using your
|
||||
|
@ -254,5 +254,4 @@ referencing an object directly may even slow code down.
|
|||
|
||||
[#15607]: https://gitlab.com/gitlab-org/gitlab-ce/issues/15607
|
||||
[yorickpeterse]: https://gitlab.com/u/yorickpeterse
|
||||
[joshfng]: https://gitlab.com/u/joshfng
|
||||
[anti-pattern]: https://en.wikipedia.org/wiki/Anti-pattern
|
||||
|
|
|
@ -754,6 +754,174 @@ X-Gitlab-Event: Wiki Page Hook
|
|||
}
|
||||
```
|
||||
|
||||
## Pipeline events
|
||||
|
||||
Triggered on status change of Pipeline.
|
||||
|
||||
**Request Header**:
|
||||
|
||||
```
|
||||
X-Gitlab-Event: Pipeline Hook
|
||||
```
|
||||
|
||||
**Request Body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"object_kind": "pipeline",
|
||||
"object_attributes":{
|
||||
"id": 31,
|
||||
"ref": "master",
|
||||
"tag": false,
|
||||
"sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
|
||||
"before_sha": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
|
||||
"status": "success",
|
||||
"stages":[
|
||||
"build",
|
||||
"test",
|
||||
"deploy"
|
||||
],
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"finished_at": "2016-08-12 15:26:29 UTC",
|
||||
"duration": 63
|
||||
},
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"project":{
|
||||
"name": "Gitlab Test",
|
||||
"description": "Atque in sunt eos similique dolores voluptatem.",
|
||||
"web_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test",
|
||||
"avatar_url": null,
|
||||
"git_ssh_url": "git@192.168.64.1:gitlab-org/gitlab-test.git",
|
||||
"git_http_url": "http://192.168.64.1:3005/gitlab-org/gitlab-test.git",
|
||||
"namespace": "Gitlab Org",
|
||||
"visibility_level": 20,
|
||||
"path_with_namespace": "gitlab-org/gitlab-test",
|
||||
"default_branch": "master"
|
||||
},
|
||||
"commit":{
|
||||
"id": "bcbb5ec396a2c0f828686f14fac9b80b780504f2",
|
||||
"message": "test\n",
|
||||
"timestamp": "2016-08-12T17:23:21+02:00",
|
||||
"url": "http://example.com/gitlab-org/gitlab-test/commit/bcbb5ec396a2c0f828686f14fac9b80b780504f2",
|
||||
"author":{
|
||||
"name": "User",
|
||||
"email": "user@gitlab.com"
|
||||
}
|
||||
},
|
||||
"builds":[
|
||||
{
|
||||
"id": 380,
|
||||
"stage": "deploy",
|
||||
"name": "production",
|
||||
"status": "skipped",
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"started_at": null,
|
||||
"finished_at": null,
|
||||
"when": "manual",
|
||||
"manual": true,
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"runner": null,
|
||||
"artifacts_file":{
|
||||
"filename": null,
|
||||
"size": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 377,
|
||||
"stage": "test",
|
||||
"name": "test-image",
|
||||
"status": "success",
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"started_at": "2016-08-12 15:26:12 UTC",
|
||||
"finished_at": null,
|
||||
"when": "on_success",
|
||||
"manual": false,
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"runner": null,
|
||||
"artifacts_file":{
|
||||
"filename": null,
|
||||
"size": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 378,
|
||||
"stage": "test",
|
||||
"name": "test-build",
|
||||
"status": "success",
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"started_at": "2016-08-12 15:26:12 UTC",
|
||||
"finished_at": "2016-08-12 15:26:29 UTC",
|
||||
"when": "on_success",
|
||||
"manual": false,
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"runner": null,
|
||||
"artifacts_file":{
|
||||
"filename": null,
|
||||
"size": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 376,
|
||||
"stage": "build",
|
||||
"name": "build-image",
|
||||
"status": "success",
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"started_at": "2016-08-12 15:24:56 UTC",
|
||||
"finished_at": "2016-08-12 15:25:26 UTC",
|
||||
"when": "on_success",
|
||||
"manual": false,
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"runner": null,
|
||||
"artifacts_file":{
|
||||
"filename": null,
|
||||
"size": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 379,
|
||||
"stage": "deploy",
|
||||
"name": "staging",
|
||||
"status": "created",
|
||||
"created_at": "2016-08-12 15:23:28 UTC",
|
||||
"started_at": null,
|
||||
"finished_at": null,
|
||||
"when": "on_success",
|
||||
"manual": false,
|
||||
"user":{
|
||||
"name": "Administrator",
|
||||
"username": "root",
|
||||
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
|
||||
},
|
||||
"runner": null,
|
||||
"artifacts_file":{
|
||||
"filename": null,
|
||||
"size": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Example webhook receiver
|
||||
|
||||
If you want to see GitLab's webhooks in action for testing purposes you can use
|
||||
|
|
24
spec/controllers/admin/groups_controller_spec.rb
Normal file
24
spec/controllers/admin/groups_controller_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Admin::GroupsController do
|
||||
let(:group) { create(:group) }
|
||||
let(:project) { create(:project, namespace: group) }
|
||||
let(:admin) { create(:admin) }
|
||||
|
||||
before do
|
||||
sign_in(admin)
|
||||
Sidekiq::Testing.fake!
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
it 'schedules a group destroy' do
|
||||
expect { delete :destroy, id: project.group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
|
||||
end
|
||||
|
||||
it 'redirects to the admin group path' do
|
||||
delete :destroy, id: project.group.path
|
||||
|
||||
expect(response).to redirect_to(admin_groups_path)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -75,4 +75,33 @@ describe GroupsController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
context 'as another user' do
|
||||
it 'returns 404' do
|
||||
sign_in(create(:user))
|
||||
|
||||
delete :destroy, id: group.path
|
||||
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
context 'as the group owner' do
|
||||
before do
|
||||
Sidekiq::Testing.fake!
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
it 'schedules a group destroy' do
|
||||
expect { delete :destroy, id: group.path }.to change(GroupDestroyWorker.jobs, :size).by(1)
|
||||
end
|
||||
|
||||
it 'redirects to the root path' do
|
||||
delete :destroy, id: group.path
|
||||
|
||||
expect(response).to redirect_to(root_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,15 +7,13 @@ FactoryGirl.define do
|
|||
end
|
||||
|
||||
trait :all_events_enabled do
|
||||
%w[push_events
|
||||
merge_requests_events
|
||||
tag_push_events
|
||||
issues_events
|
||||
note_events
|
||||
build_events
|
||||
pipeline_events].each do |event|
|
||||
send(event, true)
|
||||
end
|
||||
push_events true
|
||||
merge_requests_events true
|
||||
tag_push_events true
|
||||
issues_events true
|
||||
note_events true
|
||||
build_events true
|
||||
pipeline_events true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -887,8 +887,10 @@ describe Ci::Build, models: true do
|
|||
is_expected.to eq(build)
|
||||
end
|
||||
|
||||
context 'for success build' do
|
||||
before { build.queue }
|
||||
context 'for successful build' do
|
||||
before do
|
||||
build.update(status: 'success')
|
||||
end
|
||||
|
||||
it 'creates a new build' do
|
||||
is_expected.to be_pending
|
||||
|
|
|
@ -145,7 +145,7 @@ describe Ci::Pipeline, models: true do
|
|||
expect(pipeline.reload.started_at).not_to be_nil
|
||||
end
|
||||
|
||||
it 'do not update on transitioning to success' do
|
||||
it 'does not update on transitioning to success' do
|
||||
build.success
|
||||
|
||||
expect(pipeline.reload.started_at).to be_nil
|
||||
|
@ -159,7 +159,7 @@ describe Ci::Pipeline, models: true do
|
|||
expect(pipeline.reload.finished_at).not_to be_nil
|
||||
end
|
||||
|
||||
it 'do not update on transitioning to running' do
|
||||
it 'does not update on transitioning to running' do
|
||||
build.run
|
||||
|
||||
expect(pipeline.reload.finished_at).to be_nil
|
||||
|
@ -259,14 +259,16 @@ describe Ci::Pipeline, models: true do
|
|||
subject { pipeline.reload.status }
|
||||
|
||||
context 'on queuing' do
|
||||
before { build.queue }
|
||||
before do
|
||||
build.enqueue
|
||||
end
|
||||
|
||||
it { is_expected.to eq('pending') }
|
||||
end
|
||||
|
||||
context 'on run' do
|
||||
before do
|
||||
build.queue
|
||||
build.enqueue
|
||||
build.run
|
||||
end
|
||||
|
||||
|
@ -296,6 +298,19 @@ describe Ci::Pipeline, models: true do
|
|||
|
||||
it { is_expected.to eq('canceled') }
|
||||
end
|
||||
|
||||
context 'on failure and build retry' do
|
||||
before do
|
||||
build.drop
|
||||
Ci::Build.retry(build)
|
||||
end
|
||||
|
||||
# We are changing a state: created > failed > running
|
||||
# Instead of: created > failed > pending
|
||||
# Since the pipeline already run, so it should not be pending anymore
|
||||
|
||||
it { is_expected.to eq('running') }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#execute_hooks' do
|
||||
|
@ -320,8 +335,8 @@ describe Ci::Pipeline, models: true do
|
|||
context 'with multiple builds' do
|
||||
context 'when build is queued' do
|
||||
before do
|
||||
build_a.queue
|
||||
build_b.queue
|
||||
build_a.enqueue
|
||||
build_b.enqueue
|
||||
end
|
||||
|
||||
it 'receive a pending event once' do
|
||||
|
@ -331,9 +346,9 @@ describe Ci::Pipeline, models: true do
|
|||
|
||||
context 'when build is run' do
|
||||
before do
|
||||
build_a.queue
|
||||
build_a.enqueue
|
||||
build_a.run
|
||||
build_b.queue
|
||||
build_b.enqueue
|
||||
build_b.run
|
||||
end
|
||||
|
||||
|
@ -367,8 +382,8 @@ describe Ci::Pipeline, models: true do
|
|||
let(:enabled) { false }
|
||||
|
||||
before do
|
||||
build_a.queue
|
||||
build_b.queue
|
||||
build_a.enqueue
|
||||
build_b.enqueue
|
||||
end
|
||||
|
||||
it 'did not execute pipeline_hook after touched' do
|
||||
|
|
|
@ -564,12 +564,14 @@ describe API::API, api: true do
|
|||
end
|
||||
|
||||
describe "DELETE /users/:id" do
|
||||
let!(:namespace) { user.namespace }
|
||||
before { admin }
|
||||
|
||||
it "deletes user" do
|
||||
delete api("/users/#{user.id}", admin)
|
||||
expect(response).to have_http_status(200)
|
||||
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
|
||||
expect { Namespace.find(namespace.id) }.to raise_error ActiveRecord::RecordNotFound
|
||||
expect(json_response['email']).to eq(user.email)
|
||||
end
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@ describe DeleteUserService, services: true do
|
|||
|
||||
context 'no options are given' do
|
||||
it 'deletes the user' do
|
||||
DeleteUserService.new(current_user).execute(user)
|
||||
user_data = DeleteUserService.new(current_user).execute(user)
|
||||
|
||||
expect { user_data['email'].to eq(user.email) }
|
||||
expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
expect { Namespace.with_deleted.find(user.namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
it 'will delete the project in the near future' do
|
||||
|
|
|
@ -7,9 +7,10 @@ describe DestroyGroupService, services: true do
|
|||
let!(:gitlab_shell) { Gitlab::Shell.new }
|
||||
let!(:remove_path) { group.path + "+#{group.id}+deleted" }
|
||||
|
||||
shared_examples 'group destruction' do |async|
|
||||
context 'database records' do
|
||||
before do
|
||||
destroy_group(group, user)
|
||||
destroy_group(group, user, async)
|
||||
end
|
||||
|
||||
it { expect(Group.all).not_to include(group) }
|
||||
|
@ -20,7 +21,7 @@ describe DestroyGroupService, services: true do
|
|||
context 'Sidekiq inline' do
|
||||
before do
|
||||
# Run sidekiq immediatly to check that renamed dir will be removed
|
||||
Sidekiq::Testing.inline! { destroy_group(group, user) }
|
||||
Sidekiq::Testing.inline! { destroy_group(group, user, async) }
|
||||
end
|
||||
|
||||
it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey }
|
||||
|
@ -30,7 +31,7 @@ describe DestroyGroupService, services: true do
|
|||
context 'Sidekiq fake' do
|
||||
before do
|
||||
# Dont run sidekiq to check if renamed repository exists
|
||||
Sidekiq::Testing.fake! { destroy_group(group, user) }
|
||||
Sidekiq::Testing.fake! { destroy_group(group, user, async) }
|
||||
end
|
||||
|
||||
it { expect(gitlab_shell.exists?(project.repository_storage_path, group.path)).to be_falsey }
|
||||
|
@ -38,7 +39,20 @@ describe DestroyGroupService, services: true do
|
|||
end
|
||||
end
|
||||
|
||||
def destroy_group(group, user)
|
||||
def destroy_group(group, user, async)
|
||||
if async
|
||||
DestroyGroupService.new(group, user).async_execute
|
||||
else
|
||||
DestroyGroupService.new(group, user).execute
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'asynchronous delete' do
|
||||
it_behaves_like 'group destruction', true
|
||||
end
|
||||
|
||||
describe 'synchronous delete' do
|
||||
it_behaves_like 'group destruction', false
|
||||
end
|
||||
end
|
||||
|
|
19
spec/workers/group_destroy_worker_spec.rb
Normal file
19
spec/workers/group_destroy_worker_spec.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe GroupDestroyWorker do
|
||||
let(:group) { create(:group) }
|
||||
let(:user) { create(:admin) }
|
||||
let!(:project) { create(:project, namespace: group) }
|
||||
|
||||
subject { GroupDestroyWorker.new }
|
||||
|
||||
describe "#perform" do
|
||||
it "deletes the project" do
|
||||
subject.perform(group.id, user.id)
|
||||
|
||||
expect(Group.all).not_to include(group)
|
||||
expect(Project.all).not_to include(project)
|
||||
expect(Dir.exist?(project.path)).to be_falsey
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue