Commit fd40843a authored by Robert Speicher's avatar Robert Speicher

Merge branch 'ce-to-ee-2018-09-05' into 'master'

CE upstream - 2018-09-05 15:23 UTC

See merge request gitlab-org/gitlab-ee!7252
parents e952f958 cb677592
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
</script> </script>
<template> <template>
<div class="groups-list-tree-container"> <div class="groups-list-tree-container qa-groups-list-tree-container">
<div <div
v-if="searchEmpty" v-if="searchEmpty"
class="has-no-search-results" class="has-no-search-results"
......
...@@ -66,6 +66,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -66,6 +66,7 @@ class Projects::HooksController < Projects::ApplicationController
:enable_ssl_verification, :enable_ssl_verification,
:token, :token,
:url, :url,
:push_events_branch_filter,
*ProjectHook.triggers.values *ProjectHook.triggers.values
) )
end end
......
...@@ -110,10 +110,12 @@ module EventsHelper ...@@ -110,10 +110,12 @@ module EventsHelper
event.note_target) event.note_target)
elsif event.note? elsif event.note?
if event.note_target if event.note_target
event_note_target_path(event) event_note_target_url(event)
end end
elsif event.push? elsif event.push?
push_event_feed_url(event) push_event_feed_url(event)
elsif event.created_project?
project_url(event.project)
end end
end end
...@@ -145,14 +147,14 @@ module EventsHelper ...@@ -145,14 +147,14 @@ module EventsHelper
end end
end end
def event_note_target_path(event) def event_note_target_url(event)
if event.commit_note? if event.commit_note?
project_commit_path(event.project, event.note_target, anchor: dom_id(event.target)) project_commit_url(event.project, event.note_target, anchor: dom_id(event.target))
elsif event.project_snippet_note? elsif event.project_snippet_note?
project_snippet_path(event.project, event.note_target, anchor: dom_id(event.target)) project_snippet_url(event.project, event.note_target, anchor: dom_id(event.target))
else else
polymorphic_path([event.project.namespace.becomes(Namespace), polymorphic_url([event.project.namespace.becomes(Namespace),
event.project, event.note_target], event.project, event.note_target],
anchor: dom_id(event.target)) anchor: dom_id(event.target))
end end
end end
...@@ -166,7 +168,7 @@ module EventsHelper ...@@ -166,7 +168,7 @@ module EventsHelper
event.note_target_reference event.note_target_reference
end end
link_to(text, event_note_target_path(event), title: event.target_title, class: 'has-tooltip') link_to(text, event_note_target_url(event), title: event.target_title, class: 'has-tooltip')
else else
content_tag(:strong, '(deleted)') content_tag(:strong, '(deleted)')
end end
......
...@@ -90,7 +90,7 @@ module Ci ...@@ -90,7 +90,7 @@ module Ci
end end
def hashed_path? def hashed_path?
super || self.file_location.nil? super || self.try(:file_location).nil?
end end
def expire_in def expire_in
......
...@@ -50,14 +50,20 @@ module ProtectedRef ...@@ -50,14 +50,20 @@ module ProtectedRef
.map(&:"#{action}_access_levels").flatten .map(&:"#{action}_access_levels").flatten
end end
# Returns all protected refs that match the given ref name.
# This checks all records from the scope built up so far, and does
# _not_ return a relation.
#
# This method optionally takes in a list of `protected_refs` to search
# through, to avoid calling out to the database.
def matching(ref_name, protected_refs: nil) def matching(ref_name, protected_refs: nil)
ProtectedRefMatcher.matching(self, ref_name, protected_refs: protected_refs) (protected_refs || self.all).select { |protected_ref| protected_ref.matches?(ref_name) }
end end
end end
private private
def ref_matcher def ref_matcher
@ref_matcher ||= ProtectedRefMatcher.new(self) @ref_matcher ||= RefMatcher.new(self.name)
end end
end end
...@@ -29,6 +29,12 @@ module TriggerableHooks ...@@ -29,6 +29,12 @@ module TriggerableHooks
public_send(trigger) # rubocop:disable GitlabSecurity/PublicSend public_send(trigger) # rubocop:disable GitlabSecurity/PublicSend
end end
def select_active(hooks_scope, data)
select do |hook|
ActiveHookFilter.new(hook).matches?(hooks_scope, data)
end
end
private private
def triggerable_hooks(hooks) def triggerable_hooks(hooks)
......
class ActiveHookFilter
def initialize(hook)
@hook = hook
@push_events_filter_matcher = RefMatcher.new(@hook.push_events_branch_filter)
end
def matches?(hooks_scope, data)
return true if hooks_scope != :push_hooks
return true if @hook.push_events_branch_filter.blank?
branch_name = Gitlab::Git.branch_name(data[:ref])
@push_events_filter_matcher.matches?(branch_name)
end
end
...@@ -9,6 +9,7 @@ class WebHook < ActiveRecord::Base ...@@ -9,6 +9,7 @@ class WebHook < ActiveRecord::Base
allow_local_network: lambda(&:allow_local_requests?) } allow_local_network: lambda(&:allow_local_requests?) }
validates :token, format: { without: /\n/ } validates :token, format: { without: /\n/ }
validates :push_events_branch_filter, branch_filter: true
def execute(data, hook_name) def execute(data, hook_name)
WebHookService.new(self, data, hook_name).execute WebHookService.new(self, data, hook_name).execute
......
...@@ -1201,10 +1201,9 @@ class Project < ActiveRecord::Base ...@@ -1201,10 +1201,9 @@ class Project < ActiveRecord::Base
def execute_hooks(data, hooks_scope = :push_hooks) def execute_hooks(data, hooks_scope = :push_hooks)
run_after_commit_or_now do run_after_commit_or_now do
hooks.hooks_for(hooks_scope).each do |hook| hooks.hooks_for(hooks_scope).select_active(hooks_scope, data).each do |hook|
hook.async_execute(data, hooks_scope.to_s) hook.async_execute(data, hooks_scope.to_s)
end end
SystemHooksService.new.execute_hooks(data, hooks_scope) SystemHooksService.new.execute_hooks(data, hooks_scope)
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
class ProtectedRefMatcher class RefMatcher
def initialize(protected_ref) def initialize(ref_name_or_pattern)
@protected_ref = protected_ref @ref_name_or_pattern = ref_name_or_pattern
end
# Returns all protected refs that match the given ref name.
# This checks all records from the scope built up so far, and does
# _not_ return a relation.
#
# This method optionally takes in a list of `protected_refs` to search
# through, to avoid calling out to the database.
def self.matching(type, ref_name, protected_refs: nil)
(protected_refs || type.all).select { |protected_ref| protected_ref.matches?(ref_name) }
end end
# Returns all branches/tags (among the given list of refs [`Gitlab::Git::Branch`]) # Returns all branches/tags (among the given list of refs [`Gitlab::Git::Branch`])
# that match the current protected ref. # that match the current protected ref.
def matching(refs) def matching(refs)
refs.select { |ref| @protected_ref.matches?(ref.name) } refs.select { |ref| matches?(ref.name) }
end end
# Checks if the protected ref matches the given ref name. # Checks if the protected ref matches the given ref name.
def matches?(ref_name) def matches?(ref_name)
return false if @protected_ref.name.blank? return false if @ref_name_or_pattern.blank?
exact_match?(ref_name) || wildcard_match?(ref_name) exact_match?(ref_name) || wildcard_match?(ref_name)
end end
# Checks if this protected ref contains a wildcard # Checks if this protected ref contains a wildcard
def wildcard? def wildcard?
@protected_ref.name && @protected_ref.name.include?('*') @ref_name_or_pattern && @ref_name_or_pattern.include?('*')
end end
protected protected
def exact_match?(ref_name) def exact_match?(ref_name)
@protected_ref.name == ref_name @ref_name_or_pattern == ref_name
end end
def wildcard_match?(ref_name) def wildcard_match?(ref_name)
...@@ -47,7 +37,7 @@ class ProtectedRefMatcher ...@@ -47,7 +37,7 @@ class ProtectedRefMatcher
def wildcard_regex def wildcard_regex
@wildcard_regex ||= begin @wildcard_regex ||= begin
name = @protected_ref.name.gsub('*', 'STAR_DONT_ESCAPE') name = @ref_name_or_pattern.gsub('*', 'STAR_DONT_ESCAPE')
quoted_name = Regexp.quote(name) quoted_name = Regexp.quote(name)
regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?') regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
/\A#{regex_string}\z/ /\A#{regex_string}\z/
......
# BranchFilterValidator
#
# Custom validator for branch names. Squishes whitespace and ignores empty
# string. This only checks that a string is a valid git branch name. It does
# not check whether a branch already exists.
#
# Example:
#
# class Webhook < ActiveRecord::Base
# validates :push_events_branch_filter, branch_name: true
# end
#
class BranchFilterValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
value.squish! unless value.nil?
if value.present?
value_without_wildcards = value.tr('*', 'x')
unless Gitlab::GitRefValidator.validate(value_without_wildcards)
record.errors[attribute] << "is not a valid branch name"
end
unless value.length <= 4000
record.errors[attribute] << "is longer than the allowed length of 4000 characters."
end
end
end
private
def contains_wildcard?(value)
value.include?('*')
end
end
%div{ xmlns: "http://www.w3.org/1999/xhtml" } %div{ xmlns: "http://www.w3.org/1999/xhtml" }
%p %p
%strong= event.author_name %strong= event.author_name
= link_to "(#{truncate_sha(event.commit_id)})", project_commit_path(event.project, event.commit_id) = link_to "(#{truncate_sha(event.commit_id)})", event_feed_url(event)
%i %i
at at
= event.created_at.to_s(:short) = event.created_at.to_s(:short)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- else - else
- search_path_url = search_path - search_path_url = search_path
%header.navbar.navbar-gitlab.navbar-gitlab-new.qa-navbar.navbar-expand-sm %header.navbar.navbar-gitlab.qa-navbar.navbar-expand-sm.navbar-gitlab-new
%a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content %a.sr-only.gl-accessibility{ href: "#content-body", tabindex: "1" } Skip to content
.container-fluid .container-fluid
.header-content .header-content
......
...@@ -3,7 +3,10 @@ ...@@ -3,7 +3,10 @@
- @content_class = "limit-container-width" unless fluid_layout - @content_class = "limit-container-width" unless fluid_layout
= render "projects/default_branch/show" = render "projects/default_branch/show"
<<<<<<< HEAD
= render "projects/push_rules/index" = render "projects/push_rules/index"
=======
>>>>>>> upstream/master
= render "projects/mirrors/show" = render "projects/mirrors/show"
-# Protected branches & tags use a lot of nested partials. -# Protected branches & tags use a lot of nested partials.
......
.groups-empty-state .groups-empty-state.qa-groups-empty-state
= custom_icon("icon_empty_groups") = custom_icon("icon_empty_groups")
.text-content .text-content
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
%strong Push events %strong Push events
%p.light.ml-1 %p.light.ml-1
This URL will be triggered by a push to the repository This URL will be triggered by a push to the repository
= form.text_field :push_events_branch_filter, class: 'form-control', placeholder: 'Branch name or wildcard pattern to trigger on (leave blank for all)'
%li %li
= form.check_box :tag_push_events, class: 'form-check-input' = form.check_box :tag_push_events, class: 'form-check-input'
= form.label :tag_push_events, class: 'list-label form-check-label ml-1' do = form.label :tag_push_events, class: 'list-label form-check-label ml-1' do
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
= event_action_name(event) = event_action_name(event)
%strong %strong
- if event.note? - if event.note?
= link_to event.note_target.to_reference, event_note_target_path(event), class: 'has-tooltip', title: event.target_title = link_to event.note_target.to_reference, event_note_target_url(event), class: 'has-tooltip', title: event.target_title
- elsif event.target - elsif event.target
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title = link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
......
---
title: Adds diverged_commits_count field to GET api/v4/projects/:project_id/merge_requests/:merge_request_iid
merge_request: 21405
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: Fix links in RSS feed elements
merge_request: 21424
author: Marc Schwede
type: fixed
---
title: Add branch filter to project webhooks
merge_request: 20338
author: Duana Saskia
type: added
---
title: 'Rails 5: fix hashed_path? method that looks up file_location that doesn''t
exist when running certain migration specs'
merge_request: 21510
author: Jasper Maes
type: other
---
title: Ignore irrelevant sql commands in metrics
merge_request: 21498
author:
type: other
...@@ -235,8 +235,9 @@ ...@@ -235,8 +235,9 @@
:why: https://github.com/component/inherit/blob/master/LICENSE :why: https://github.com/component/inherit/blob/master/LICENSE
:versions: [] :versions: []
:when: 2017-01-14 20:10:41.804804000 Z :when: 2017-01-14 20:10:41.804804000 Z
- - :approve - - :license
- fsevents - fsevents
- MIT
- :who: Matt Lee - :who: Matt Lee
:why: https://github.com/strongloop/fsevents/blob/master/LICENSE :why: https://github.com/strongloop/fsevents/blob/master/LICENSE
:versions: [] :versions: []
...@@ -380,8 +381,9 @@ ...@@ -380,8 +381,9 @@
:why: https://github.com/Tjatse/ansi-html/blob/master/LICENSE :why: https://github.com/Tjatse/ansi-html/blob/master/LICENSE
:versions: [] :versions: []
:when: 2017-04-10 05:42:12.898178000 Z :when: 2017-04-10 05:42:12.898178000 Z
- - :approve - - :license
- map-stream - map-stream
- MIT
- :who: Mike Greiling - :who: Mike Greiling
:why: https://github.com/dominictarr/map-stream/blob/master/LICENCE :why: https://github.com/dominictarr/map-stream/blob/master/LICENCE
:versions: [] :versions: []
...@@ -458,8 +460,9 @@ ...@@ -458,8 +460,9 @@
:why: CC0 1.0 - https://github.com/jonathantneal/svg4everybody/blob/master/LICENSE.md :why: CC0 1.0 - https://github.com/jonathantneal/svg4everybody/blob/master/LICENSE.md
:versions: [] :versions: []
:when: 2017-09-13 17:31:16.425819400 Z :when: 2017-09-13 17:31:16.425819400 Z
- - :approve - - :license
- "@gitlab-org/gitlab-svgs" - "@gitlab-org/gitlab-svgs"
- MIT
- :who: Tim Zallmann - :who: Tim Zallmann
:why: Our own library - GitLab License https://gitlab.com/gitlab-org/gitlab-svgs :why: Our own library - GitLab License https://gitlab.com/gitlab-org/gitlab-svgs
:versions: [] :versions: []
...@@ -528,8 +531,9 @@ ...@@ -528,8 +531,9 @@
:why: https://github.com/mafintosh/cyclist/blob/master/LICENSE :why: https://github.com/mafintosh/cyclist/blob/master/LICENSE
:versions: [] :versions: []
:when: 2018-02-20 21:37:43.774978000 Z :when: 2018-02-20 21:37:43.774978000 Z
- - :approve - - :license
- bitsyntax - bitsyntax
- MIT
- :who: Mike Greiling - :who: Mike Greiling
:why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT :why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
:versions: [] :versions: []
...@@ -540,8 +544,9 @@ ...@@ -540,8 +544,9 @@
:why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE :why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
:versions: [] :versions: []
:when: 2018-06-08 05:30:56.764116000 Z :when: 2018-06-08 05:30:56.764116000 Z
- - :approve - - :license
- "@gitlab-org/gitlab-ui" - "@gitlab-org/gitlab-ui"
- MIT
- :who: Clement Ho - :who: Clement Ho
:why: Our own library :why: Our own library
:versions: [] :versions: []
...@@ -552,20 +557,23 @@ ...@@ -552,20 +557,23 @@
:why: https://github.com/pieroxy/lz-string/blob/master/LICENSE.txt :why: https://github.com/pieroxy/lz-string/blob/master/LICENSE.txt
:versions: [] :versions: []
:when: 2018-08-03 08:22:44.973457000 Z :when: 2018-08-03 08:22:44.973457000 Z
- - :approve - - :license
- smooshpack - smooshpack
- LGPL
- :who: Phil Hughes - :who: Phil Hughes
:why: https://github.com/CompuIves/codesandbox-client/blob/master/packages/sandpack/LICENSE.md :why: https://github.com/CompuIves/codesandbox-client/blob/master/packages/sandpack/LICENSE.md
:versions: [] :versions: []
:when: 2018-08-03 08:24:29.578991000 Z :when: 2018-08-03 08:24:29.578991000 Z
- - :approve - - :license
- codesandbox-import-util-types - codesandbox-import-util-types
- LGPL
- :who: Phil Hughes - :who: Phil Hughes
:why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/types/LICENSE :why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/types/LICENSE
:versions: [] :versions: []
:when: 2018-08-03 12:22:47.574421000 Z :when: 2018-08-03 12:22:47.574421000 Z
- - :approve - - :license
- codesandbox-import-utils - codesandbox-import-utils
- LGPL
- :who: Phil Hughes - :who: Phil Hughes
:why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/import-utils/LICENSE :why: https://github.com/codesandbox-app/codesandbox-importers/blob/master/packages/import-utils/LICENSE
:versions: [] :versions: []
...@@ -573,7 +581,7 @@ ...@@ -573,7 +581,7 @@
- - :ignore_group - - :ignore_group
- devDependencies - devDependencies
- :who: Winnie Hellmann - :who: Winnie Hellmann
:why: NPM packages used for development are not distributed with the final product and are therefore :why: NPM packages used for development are not distributed with the final product
exempt. and are therefore exempt.
:versions: [] :versions: []
:when: 2018-08-30 12:06:35.668181000 Z :when: 2018-08-30 12:06:35.668181000 Z
...@@ -7,11 +7,12 @@ ...@@ -7,11 +7,12 @@
# the old Rails 4 schema layout is still used # the old Rails 4 schema layout is still used
module MysqlSetLengthForBinaryIndex module MysqlSetLengthForBinaryIndex
def add_index(table_name, column_names, options = {}) def add_index(table_name, column_names, options = {})
options[:length] ||= {}
Array(column_names).each do |column_name| Array(column_names).each do |column_name|
column = ActiveRecord::Base.connection.columns(table_name).find { |c| c.name == column_name } column = ActiveRecord::Base.connection.columns(table_name).find { |c| c.name == column_name }
if column&.type == :binary if column&.type == :binary
options[:length] = 20 options[:length][column_name] = 20
end end
end end
...@@ -27,11 +28,12 @@ if Gitlab.rails5? ...@@ -27,11 +28,12 @@ if Gitlab.rails5?
module MysqlSetLengthForBinaryIndexAndIgnorePostgresOptionsForSchema module MysqlSetLengthForBinaryIndexAndIgnorePostgresOptionsForSchema
# This method is used in Rails 5 schema loading as t.index # This method is used in Rails 5 schema loading as t.index
def index(column_names, options = {}) def index(column_names, options = {})
options[:length] ||= {}
Array(column_names).each do |column_name| Array(column_names).each do |column_name|
column = columns.find { |c| c.name == column_name } column = columns.find { |c| c.name == column_name }
if column&.type == :binary if column&.type == :binary
options[:length] = 20 options[:length][column_name] = 20
end end
end end
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddPushEventsBranchFilterToWebHooks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :web_hooks, :push_events_branch_filter, :text
end
end
...@@ -2965,6 +2965,7 @@ ActiveRecord::Schema.define(version: 20180831152625) do ...@@ -2965,6 +2965,7 @@ ActiveRecord::Schema.define(version: 20180831152625) do
t.boolean "repository_update_events", default: false, null: false t.boolean "repository_update_events", default: false, null: false
t.boolean "job_events", default: false, null: false t.boolean "job_events", default: false, null: false
t.boolean "confidential_note_events" t.boolean "confidential_note_events"
t.text "push_events_branch_filter"
end end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
......
...@@ -353,6 +353,7 @@ Parameters: ...@@ -353,6 +353,7 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request - `merge_request_iid` (required) - The internal ID of the merge request
- `render_html` (optional) - If `true` response includes rendered HTML for title and description - `render_html` (optional) - If `true` response includes rendered HTML for title and description
- `include_diverged_commits_count` (optional) - If `true` response includes the commits behind the target branch
```json ```json
{ {
...@@ -437,7 +438,8 @@ Parameters: ...@@ -437,7 +438,8 @@ Parameters:
"username" : "root", "username" : "root",
"id" : 1, "id" : 1,
"name" : "Administrator" "name" : "Administrator"
} },
"diverged_commits_count": 2
} }
``` ```
......
...@@ -1367,6 +1367,7 @@ GET /projects/:id/hooks/:hook_id ...@@ -1367,6 +1367,7 @@ GET /projects/:id/hooks/:hook_id
"url": "http://example.com/hook", "url": "http://example.com/hook",
"project_id": 3, "project_id": 3,
"push_events": true, "push_events": true,
"push_events_branch_filter": "",
"issues_events": true, "issues_events": true,
"confidential_issues_events": true, "confidential_issues_events": true,
"merge_requests_events": true, "merge_requests_events": true,
...@@ -1393,6 +1394,7 @@ POST /projects/:id/hooks ...@@ -1393,6 +1394,7 @@ POST /projects/:id/hooks
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `url` | string | yes | The hook URL | | `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events | | `push_events` | boolean | no | Trigger hook on push events |
| `push_events_branch_filter` | string | no | Trigger hook on push events for matching branches only |
| `issues_events` | boolean | no | Trigger hook on issues events | | `issues_events` | boolean | no | Trigger hook on issues events |
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events | | `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events | | `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
...@@ -1418,6 +1420,7 @@ PUT /projects/:id/hooks/:hook_id ...@@ -1418,6 +1420,7 @@ PUT /projects/:id/hooks/:hook_id
| `hook_id` | integer | yes | The ID of the project hook | | `hook_id` | integer | yes | The ID of the project hook |
| `url` | string | yes | The hook URL | | `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events | | `push_events` | boolean | no | Trigger hook on push events |
| `push_events_branch_filter` | string | no | Trigger hook on push events for matching branches only |
| `issues_events` | boolean | no | Trigger hook on issues events | | `issues_events` | boolean | no | Trigger hook on issues events |
| `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events | | `confidential_issues_events` | boolean | no | Trigger hook on confidential issues events |
| `merge_requests_events` | boolean | no | Trigger hook on merge requests events | | `merge_requests_events` | boolean | no | Trigger hook on merge requests events |
......
...@@ -95,6 +95,8 @@ Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCol ...@@ -95,6 +95,8 @@ Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCol
No more files will be rendered at all if 5 megabytes have already been rendered. No more files will be rendered at all if 5 megabytes have already been rendered.
*Note:* All collection limit parameters are currently sent and applied on Gitaly. That is, once the limit is surpassed,
Gitaly will only return the safe amount of data to be persisted on `merge_request_diff_files`.
### Individual diff file limits ### Individual diff file limits
...@@ -106,12 +108,17 @@ Gitlab::Git::Diff::COLLAPSE_LIMIT = 10.kilobytes ...@@ -106,12 +108,17 @@ Gitlab::Git::Diff::COLLAPSE_LIMIT = 10.kilobytes
File diff will be collapsed (but be expandable) if it is larger than 10 kilobytes. File diff will be collapsed (but be expandable) if it is larger than 10 kilobytes.
*Note:* Although this nomenclature (Collapsing) is also used on Gitaly, this limit is only used on GitLab (hardcoded - not sent to Gitaly).
Gitaly will only return `Diff.Collapsed` (RPC) when surpassing collection limits.
```ruby ```ruby
Gitlab::Git::Diff::SIZE_LIMIT = 100.kilobytes Gitlab::Git::Diff::SIZE_LIMIT = 100.kilobytes
``` ```
File diff will not be rendered if it's larger than 100 kilobytes. File diff will not be rendered if it's larger than 100 kilobytes.
*Note:* This limit is currently hardcoded and applied on Gitaly and the RPC returns `Diff.TooLarge` when this limit is surpassed.
Although we're still also applying it on GitLab, we should remove the redundancy from GitLab once we're confident with the Gitaly integration.
```ruby ```ruby
Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000 Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
...@@ -119,6 +126,8 @@ Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines ...@@ -119,6 +126,8 @@ Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines
File diff will be suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines. File diff will be suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines.
*Note:* This limit is currently hardcoded and only applied on GitLab.
## Viewers ## Viewers
Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to map metadata about each type of Diff File. It has information Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to map metadata about each type of Diff File. It has information
......
...@@ -90,8 +90,8 @@ Below are described the supported events. ...@@ -90,8 +90,8 @@ Below are described the supported events.
Triggered when you push to the repository except when pushing tags. Triggered when you push to the repository except when pushing tags.
> **Note:** When more than 20 commits are pushed at once, the `commits` web hook > **Note:** When more than 20 commits are pushed at once, the `commits` web hook
attribute will only contain the first 20 for performance reasons. Loading attribute will only contain the first 20 for performance reasons. Loading
detailed commit data is expensive. Note that despite only 20 commits being detailed commit data is expensive. Note that despite only 20 commits being
present in the `commits` attribute, the `total_commits_count` attribute will present in the `commits` attribute, the `total_commits_count` attribute will
contain the actual total. contain the actual total.
......
...@@ -105,6 +105,7 @@ module API ...@@ -105,6 +105,7 @@ module API
expose :project_id, :issues_events, :confidential_issues_events expose :project_id, :issues_events, :confidential_issues_events
expose :note_events, :confidential_note_events, :pipeline_events, :wiki_page_events expose :note_events, :confidential_note_events, :pipeline_events, :wiki_page_events
expose :job_events expose :job_events
expose :push_events_branch_filter
end end
class SharedGroup < Grape::Entity class SharedGroup < Grape::Entity
...@@ -689,6 +690,8 @@ module API ...@@ -689,6 +690,8 @@ module API
expose :diff_refs, using: Entities::DiffRefs expose :diff_refs, using: Entities::DiffRefs
expose :diverged_commits_count, as: :diverged_commits_count, if: -> (_, options) { options[:include_diverged_commits_count] }
def build_available?(options) def build_available?(options)
options[:project]&.feature_available?(:builds, options[:current_user]) options[:project]&.feature_available?(:builds, options[:current_user])
end end
......
...@@ -235,6 +235,7 @@ module API ...@@ -235,6 +235,7 @@ module API
params do params do
requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request' requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request'
optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML' optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML'
optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch'
end end
desc 'Get a single merge request' do desc 'Get a single merge request' do
success Entities::MergeRequest success Entities::MergeRequest
...@@ -242,7 +243,7 @@ module API ...@@ -242,7 +243,7 @@ module API
get ':id/merge_requests/:merge_request_iid' do get ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params[:merge_request_iid]) merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html] present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project, render_html: params[:render_html], include_diverged_commits_count: params[:include_diverged_commits_count]
end end
desc 'Get the participants of a merge request' do desc 'Get the participants of a merge request' do
......
...@@ -20,6 +20,7 @@ module API ...@@ -20,6 +20,7 @@ module API
optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events" optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook" optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response" optional :token, type: String, desc: "Secret token to validate received payloads; this will not be returned in the response"
optional :push_events_branch_filter, type: String, desc: "Trigger hook on specified branch only"
end end
end end
...@@ -63,6 +64,7 @@ module API ...@@ -63,6 +64,7 @@ module API
present hook, with: Entities::ProjectHook present hook, with: Entities::ProjectHook
else else
error!("Invalid url given", 422) if hook.errors[:url].present? error!("Invalid url given", 422) if hook.errors[:url].present?
error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present?
not_found!("Project hook #{hook.errors.messages}") not_found!("Project hook #{hook.errors.messages}")
end end
...@@ -84,6 +86,7 @@ module API ...@@ -84,6 +86,7 @@ module API
present hook, with: Entities::ProjectHook present hook, with: Entities::ProjectHook
else else
error!("Invalid url given", 422) if hook.errors[:url].present? error!("Invalid url given", 422) if hook.errors[:url].present?
error!("Invalid branch filter given", 422) if hook.errors[:push_events_branch_filter].present?
not_found!("Project hook #{hook.errors.messages}") not_found!("Project hook #{hook.errors.messages}")
end end
......
...@@ -6,9 +6,15 @@ module Gitlab ...@@ -6,9 +6,15 @@ module Gitlab
include Gitlab::Metrics::Methods include Gitlab::Metrics::Methods
attach_to :active_record attach_to :active_record
IGNORABLE_SQL = %w{BEGIN COMMIT}.freeze
def sql(event) def sql(event)
return unless current_transaction return unless current_transaction
payload = event.payload
return if payload[:name] == 'SCHEMA' || IGNORABLE_SQL.include?(payload[:sql])
self.class.gitlab_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0) self.class.gitlab_sql_duration_seconds.observe(current_transaction.labels, event.duration / 1000.0)
current_transaction.increment(:sql_duration, event.duration, false) current_transaction.increment(:sql_duration, event.duration, false)
......
...@@ -250,6 +250,7 @@ module QA ...@@ -250,6 +250,7 @@ module QA
module Component module Component
autoload :ClonePanel, 'qa/page/component/clone_panel' autoload :ClonePanel, 'qa/page/component/clone_panel'
autoload :Dropzone, 'qa/page/component/dropzone' autoload :Dropzone, 'qa/page/component/dropzone'
autoload :GroupsFilter, 'qa/page/component/groups_filter'
autoload :Select2, 'qa/page/component/select2' autoload :Select2, 'qa/page/component/select2'
end end
end end
......
...@@ -2,7 +2,7 @@ module QA ...@@ -2,7 +2,7 @@ module QA
module Factory module Factory
module Resource module Resource
class Group < Factory::Base class Group < Factory::Base
attr_writer :path, :description attr_accessor :path, :description
dependency Factory::Resource::Sandbox, as: :sandbox dependency Factory::Resource::Sandbox, as: :sandbox
...@@ -14,17 +14,23 @@ module QA ...@@ -14,17 +14,23 @@ module QA
def fabricate! def fabricate!
sandbox.visit! sandbox.visit!
Page::Group::Show.perform do |page| Page::Group::Show.perform do |group_show|
if page.has_subgroup?(@path) if group_show.has_subgroup?(path)
page.go_to_subgroup(@path) group_show.go_to_subgroup(path)
else else
page.go_to_new_subgroup group_show.go_to_new_subgroup
Page::Group::New.perform do |group| Page::Group::New.perform do |group_new|
group.set_path(@path) group_new.set_path(path)
group.set_description(@description) group_new.set_description(description)
group.set_visibility('Public') group_new.set_visibility('Public')
group.create group_new.create
end
# Ensure that the group was actually created
group_show.wait(time: 1) do
group_show.has_text?(path) &&
group_show.has_new_project_or_subgroup_dropdown?
end end
end end
end end
......
# frozen_string_literal: true
module QA
module Page
module Component
module GroupsFilter
def self.included(base)
base.view 'app/views/shared/groups/_search_form.html.haml' do
element :groups_filter, 'search_field_tag :filter'
element :groups_filter_placeholder, 'Filter by name...'
end
base.view 'app/views/shared/groups/_empty_state.html.haml' do
element :groups_empty_state
end
base.view 'app/assets/javascripts/groups/components/groups.vue' do
element :groups_list_tree_container
end
end
private
def filter_by_name(name)
wait(reload: false) do
page.has_css?(element_selector_css(:groups_empty_state)) ||
page.has_css?(element_selector_css(:groups_list_tree_container))
end
fill_in 'Filter by name...', with: name
end
end
end
end
end
...@@ -2,19 +2,12 @@ module QA ...@@ -2,19 +2,12 @@ module QA
module Page module Page
module Dashboard module Dashboard
class Groups < Page::Base class Groups < Page::Base
view 'app/views/shared/groups/_search_form.html.haml' do include Page::Component::GroupsFilter
element :groups_filter, 'search_field_tag :filter'
element :groups_filter_placeholder, 'Filter by name...'
end
view 'app/views/dashboard/_groups_head.html.haml' do view 'app/views/dashboard/_groups_head.html.haml' do
element :new_group_button, 'link_to _("New group")' element :new_group_button, 'link_to _("New group")'
end end
def filter_by_name(name)
fill_in 'Filter by name...', with: name
end
def has_group?(name) def has_group?(name)
filter_by_name(name) filter_by_name(name)
......
...@@ -5,6 +5,7 @@ module QA ...@@ -5,6 +5,7 @@ module QA
prepend QA::EE::Page::Dashboard::Projects prepend QA::EE::Page::Dashboard::Projects
view 'app/views/dashboard/projects/index.html.haml' view 'app/views/dashboard/projects/index.html.haml'
view 'app/views/shared/projects/_search_form.html.haml' do view 'app/views/shared/projects/_search_form.html.haml' do
element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/
end end
...@@ -15,6 +16,8 @@ module QA ...@@ -15,6 +16,8 @@ module QA
find_link(text: name).click find_link(text: name).click
end end
private
def filter_by_name(name) def filter_by_name(name)
page.within('form#project-filter-form') do page.within('form#project-filter-form') do
fill_in :name, with: name fill_in :name, with: name
......
...@@ -2,6 +2,8 @@ module QA ...@@ -2,6 +2,8 @@ module QA
module Page module Page
module Group module Group
class Show < Page::Base class Show < Page::Base
include Page::Component::GroupsFilter
view 'app/views/groups/show.html.haml' do view 'app/views/groups/show.html.haml' do
element :new_project_or_subgroup_dropdown, '.new-project-subgroup' element :new_project_or_subgroup_dropdown, '.new-project-subgroup'
element :new_project_or_subgroup_dropdown_toggle, '.dropdown-toggle' element :new_project_or_subgroup_dropdown_toggle, '.dropdown-toggle'
...@@ -21,8 +23,8 @@ module QA ...@@ -21,8 +23,8 @@ module QA
click_link name click_link name
end end
def filter_by_name(name) def has_new_project_or_subgroup_dropdown?
fill_in 'Filter by name...', with: name page.has_css?(element_selector_css(:new_project_or_subgroup_dropdown))
end end
def has_subgroup?(name) def has_subgroup?(name)
......
...@@ -28,12 +28,8 @@ module QA ...@@ -28,12 +28,8 @@ module QA
Specs::Runner.perform do |specs| Specs::Runner.perform do |specs|
specs.tty = true specs.tty = true
specs.options = specs.tags = self.class.focus
if rspec_options.any? specs.options = rspec_options if rspec_options.any?
rspec_options
else
['--tag', self.class.focus.join(','), '--', ::File.expand_path('../specs/features', __dir__)]
end
end end
end end
end end
......
...@@ -27,12 +27,7 @@ module QA ...@@ -27,12 +27,7 @@ module QA
Specs::Runner.perform do |specs| Specs::Runner.perform do |specs|
specs.tty = true specs.tty = true
specs.options = specs.options = rspec_options if rspec_options.any?
if rspec_options.any?
rspec_options
else
['--', ::File.expand_path('../../specs/features', __dir__)]
end
end end
end end
end end
......
...@@ -30,7 +30,7 @@ module QA ...@@ -30,7 +30,7 @@ module QA
push.project = project push.project = project
push.directory = Pathname push.directory = Pathname
.new(__dir__) .new(__dir__)
.join('../../../fixtures/auto_devops_rack') .join('../../../../../fixtures/auto_devops_rack')
push.commit_message = 'Create Auto DevOps compatible rack application' push.commit_message = 'Create Auto DevOps compatible rack application'
end end
......
...@@ -16,9 +16,9 @@ module QA ...@@ -16,9 +16,9 @@ module QA
args.push('--tty') if tty args.push('--tty') if tty
if tags.any? if tags.any?
tags.each { |tag| args.push(['-t', tag.to_s]) } tags.each { |tag| args.push(['--tag', tag.to_s]) }
else else
args.push(%w[-t ~orchestrated]) args.push(%w[--tag ~orchestrated])
end end
args.push(options) args.push(options)
......
...@@ -29,7 +29,7 @@ describe QA::Git::Repository do ...@@ -29,7 +29,7 @@ describe QA::Git::Repository do
def cd_empty_temp_directory def cd_empty_temp_directory
tmp_dir = 'tmp/git-repository-spec/' tmp_dir = 'tmp/git-repository-spec/'
FileUtils.rm_r(tmp_dir) if ::File.exist?(tmp_dir) FileUtils.rm_rf(tmp_dir) if ::File.exist?(tmp_dir)
FileUtils.mkdir_p tmp_dir FileUtils.mkdir_p tmp_dir
FileUtils.cd tmp_dir FileUtils.cd tmp_dir
end end
......
describe QA::Scenario::Test::Instance::All do describe QA::Scenario::Test::Instance::All do
subject do it_behaves_like 'a QA scenario class'
Class.new(described_class) do
tags :rspec, :foo
end
end
context '#perform' do
let(:arguments) { spy('Runtime::Scenario') }
let(:release) { spy('Runtime::Release') }
let(:runner) { spy('Specs::Runner') }
before do
stub_const('QA::Runtime::Release', release)
stub_const('QA::Runtime::Scenario', arguments)
stub_const('QA::Specs::Runner', runner)
allow(runner).to receive(:perform).and_yield(runner)
end
it 'sets an address of the subject' do
subject.perform("hello")
expect(arguments).to have_received(:define)
.with(:gitlab_address, "hello")
end
context 'no paths' do
it 'calls runner with default arguments' do
subject.perform("test")
expect(runner).to have_received(:options=)
.with(['--tag', 'rspec,foo', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)])
end
end
context 'specifying paths' do
it 'calls runner with paths' do
subject.perform('test', 'path1', 'path2')
expect(runner).to have_received(:options=).with(%w[path1 path2])
end
end
end
end end
describe QA::Scenario::Test::Instance::Smoke do describe QA::Scenario::Test::Instance::Smoke do
subject { Class.new(described_class) { tags :smoke } } it_behaves_like 'a QA scenario class' do
let(:tags) { [:smoke] }
context '#perform' do
let(:arguments) { spy('Runtime::Scenario') }
let(:release) { spy('Runtime::Release') }
let(:runner) { spy('Specs::Runner') }
before do
stub_const('QA::Runtime::Release', release)
stub_const('QA::Runtime::Scenario', arguments)
stub_const('QA::Specs::Runner', runner)
allow(runner).to receive(:perform).and_yield(runner)
end
it 'sets an address of the subject' do
subject.perform("hello")
expect(arguments).to have_received(:define)
.with(:gitlab_address, "hello")
end
it 'has a smoke tag' do
expect(subject.focus).to eq([:smoke]) # rubocop:disable Focus
end
context 'no paths' do
it 'calls runner with default arguments' do
subject.perform("test")
expect(runner).to have_received(:options=)
.with(['--tag', 'smoke', '--', ::File.expand_path('../../../../qa/specs/features', __dir__)])
end
end
context 'specifying paths' do
it 'calls runner with paths' do
subject.perform('test', 'path1', 'path2')
expect(runner).to have_received(:options=).with(%w[path1 path2])
end
end
end end
end end
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Github do
context '#perform' do
let(:env) { spy('Runtime::Env') }
before do
stub_const('QA::Runtime::Env', env)
end
it_behaves_like 'a QA scenario class' do
let(:tags) { [:github] }
it 'requires a GitHub access token' do
subject.perform('gitlab_address')
expect(env).to have_received(:require_github_access_token!)
end
end
end
end
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Kubernetes do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:kubernetes] }
end
end
end
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::LDAP do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:ldap] }
end
end
end
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::Mattermost do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:args) { %w[gitlab_address mattermost_address] }
let(:tags) { [:mattermost] }
let(:options) { ['path1']}
it 'requires a GitHub access token' do
subject.perform(*args)
expect(attributes).to have_received(:define)
.with(:mattermost_address, 'mattermost_address')
end
end
end
end
# frozen_string_literal: true
describe QA::Scenario::Test::Integration::ObjectStorage do
context '#perform' do
it_behaves_like 'a QA scenario class' do
let(:tags) { [:object_storage] }
end
end
end
# frozen_string_literal: true
describe QA::Specs::Runner do
context '#perform' do
before do
allow(QA::Runtime::Browser).to receive(:configure!)
end
it 'excludes the orchestrated tag by default' do
expect(RSpec::Core::Runner).to receive(:run)
.with(['--tag', '~orchestrated', File.expand_path('../../qa/specs/features', __dir__)], $stderr, $stdout)
.and_return(0)
subject.perform
end
context 'when tty is set' do
subject do
described_class.new.tap do |runner|
runner.tty = true
end
end
it 'sets the `--tty` flag' do
expect(RSpec::Core::Runner).to receive(:run)
.with(['--tty', '--tag', '~orchestrated', File.expand_path('../../qa/specs/features', __dir__)], $stderr, $stdout)
.and_return(0)
subject.perform
end
end
context 'when tags are set' do
subject do
described_class.new.tap do |runner|
runner.tags = %i[orchestrated github]
end
end
it 'focuses on the given tags' do
expect(RSpec::Core::Runner).to receive(:run)
.with(['--tag', 'orchestrated', '--tag', 'github', File.expand_path('../../qa/specs/features', __dir__)], $stderr, $stdout)
.and_return(0)
subject.perform
end
end
end
end
# frozen_string_literal: true
shared_examples 'a QA scenario class' do
let(:attributes) { spy('Runtime::Scenario') }
let(:release) { spy('Runtime::Release') }
let(:runner) { spy('Specs::Runner') }
let(:args) { ['gitlab_address'] }
let(:tags) { [] }
let(:options) { %w[path1 path2] }
before do
stub_const('QA::Runtime::Release', release)
stub_const('QA::Runtime::Scenario', attributes)
stub_const('QA::Specs::Runner', runner)
allow(runner).to receive(:perform).and_yield(runner)
end
it 'responds to perform' do
expect(subject).to respond_to(:perform)
end
it 'sets an address of the subject' do
subject.perform(*args)
expect(attributes).to have_received(:define).with(:gitlab_address, 'gitlab_address')
end
it 'performs before hooks' do
subject.perform(*args)
expect(release).to have_received(:perform_before_hooks)
end
it 'sets tags on runner' do
subject.perform(*args)
expect(runner).to have_received(:tags=).with(tags)
end
context 'specifying RSpec options' do
it 'sets options on runner' do
subject.perform(*args, *options)
expect(runner).to have_received(:options=).with(options)
end
end
end
...@@ -51,6 +51,7 @@ describe 'Projects > Settings > Integration settings' do ...@@ -51,6 +51,7 @@ describe 'Projects > Settings > Integration settings' do
fill_in 'hook_url', with: url fill_in 'hook_url', with: url
check 'Tag push events' check 'Tag push events'
fill_in 'hook_push_events_branch_filter', with: 'master'
check 'Enable SSL verification' check 'Enable SSL verification'
check 'Job events' check 'Job events'
......
...@@ -25,4 +25,47 @@ describe EventsHelper do ...@@ -25,4 +25,47 @@ describe EventsHelper do
expect(helper.event_commit_title("foo & bar")).to eq("foo & bar") expect(helper.event_commit_title("foo & bar")).to eq("foo & bar")
end end
end end
describe '#event_feed_url' do
let(:event) { create(:event) }
let(:project) { create(:project, :public, :repository) }
it "returns project issue url" do
event.target = create(:issue)
expect(helper.event_feed_url(event)).to eq(project_issue_url(event.project, event.issue))
end
it "returns project merge_request url" do
event.target = create(:merge_request)
expect(helper.event_feed_url(event)).to eq(project_merge_request_url(event.project, event.merge_request))
end
it "returns project commit url" do
event.target = create(:note_on_commit, project: project)
expect(helper.event_feed_url(event)).to eq(project_commit_url(event.project, event.note_target))
end
it "returns event note target url" do
event.target = create(:note)
expect(helper.event_feed_url(event)).to eq(event_note_target_url(event))
end
it "returns project url" do
event.project = project
event.action = 1
expect(helper.event_feed_url(event)).to eq(project_url(event.project))
end
it "returns push event feed url" do
event = create(:push_event)
create(:push_event_payload, event: event, action: :pushed)
expect(helper.event_feed_url(event)).to eq(push_event_feed_url(event))
end
end
end end
...@@ -419,6 +419,7 @@ ProjectHook: ...@@ -419,6 +419,7 @@ ProjectHook:
- type - type
- service_id - service_id
- push_events - push_events
- push_events_branch_filter
- issues_events - issues_events
- merge_requests_events - merge_requests_events
- tag_push_events - tag_push_events
......
...@@ -42,6 +42,65 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -42,6 +42,65 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
subscriber.sql(event) subscriber.sql(event)
end end
context 'events are internal to Rails or irrelevant' do
let(:schema_event) do
double(
:event,
name: 'sql.active_record',
payload: {
sql: "SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) WHERE cons.contype = 'p' AND cons.conrelid = '\"projects\"'::regclass",
name: 'SCHEMA',
connection_id: 135,
statement_name: nil,
binds: []
},
duration: 0.7
)
end
let(:begin_event) do
double(
:event,
name: 'sql.active_record',
payload: {
sql: "BEGIN",
name: nil,
connection_id: 231,
statement_name: nil,
binds: []
},
duration: 1.1
)
end
let(:commit_event) do
double(
:event,
name: 'sql.active_record',
payload: {
sql: "COMMIT",
name: nil,
connection_id: 212,
statement_name: nil,
binds: []
},
duration: 1.6
)
end
it 'skips schema/begin/commit sql commands' do
expect(subscriber).to receive(:current_transaction)
.at_least(:once)
.and_return(transaction)
expect(transaction).not_to receive(:increment)
subscriber.sql(schema_event)
subscriber.sql(begin_event)
subscriber.sql(commit_event)
end
end
end end
end end
end end
...@@ -40,4 +40,28 @@ RSpec.describe TriggerableHooks do ...@@ -40,4 +40,28 @@ RSpec.describe TriggerableHooks do
end end
end end
end end
describe '.select_active' do
it 'returns hooks that match the active filter' do
TestableHook.create!(url: 'http://example1.com', push_events: true)
TestableHook.create!(url: 'http://example2.com', push_events: true)
filter1 = double(:filter1)
filter2 = double(:filter2)
allow(ActiveHookFilter).to receive(:new).exactly(2).times.and_return(filter1, filter2)
expect(filter1).to receive(:matches?).and_return(true)
expect(filter2).to receive(:matches?).and_return(false)
hooks = TestableHook.push_hooks.order_id_asc
expect(hooks.select_active(:push_hooks, {})).to eq [hooks.first]
end
it 'returns empty list if no hooks match the active filter' do
TestableHook.create!(url: 'http://example1.com', push_events: true)
filter = double(:filter)
allow(ActiveHookFilter).to receive(:new).and_return(filter)
expect(filter).to receive(:matches?).and_return(false)
expect(TestableHook.push_hooks.select_active(:push_hooks, {})).to eq []
end
end
end end
require 'spec_helper'
describe ActiveHookFilter do
subject(:filter) { described_class.new(hook) }
describe '#matches?' do
context 'for push event hooks' do
let(:hook) do
create(:project_hook, push_events: true, push_events_branch_filter: branch_filter)
end
context 'branch filter is specified' do
let(:branch_filter) { 'master' }
it 'returns true if branch matches' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
end
it 'returns false if branch does not match' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/my_branch' })).to be false
end
it 'returns false if ref is nil' do
expect(filter.matches?(:push_hooks, {})).to be false
end
context 'branch filter contains wildcard' do
let(:branch_filter) { 'features/*' }
it 'returns true if branch matches' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch' })).to be true
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/features/my-branch/something' })).to be true
end
it 'returns false if branch does not match' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be false
end
end
end
context 'branch filter is not specified' do
let(:branch_filter) { nil }
it 'returns true' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
end
end
context 'branch filter is empty string' do
let(:branch_filter) { '' }
it 'acts like branch is not specified' do
expect(filter.matches?(:push_hooks, { ref: 'refs/heads/master' })).to be true
end
end
end
context 'for non-push-events hooks' do
let(:hook) do
create(:project_hook, issues_events: true, push_events: false, push_events_branch_filter: '')
end
it 'returns true as branch filters are not yet supported for these' do
expect(filter.matches?(:issues_events, { ref: 'refs/heads/master' })).to be true
end
end
end
end
...@@ -35,6 +35,26 @@ describe WebHook do ...@@ -35,6 +35,26 @@ describe WebHook do
it { is_expected.not_to allow_values("foo\nbar", "foo\r\nbar").for(:token) } it { is_expected.not_to allow_values("foo\nbar", "foo\r\nbar").for(:token) }
end end
describe 'push_events_branch_filter' do
it { is_expected.to allow_values("good_branch_name", "another/good-branch_name").for(:push_events_branch_filter) }
it { is_expected.to allow_values("").for(:push_events_branch_filter) }
it { is_expected.not_to allow_values("bad branch name", "bad~branchname").for(:push_events_branch_filter) }
it 'gets rid of whitespace' do
hook.push_events_branch_filter = ' branch '
hook.save
expect(hook.push_events_branch_filter).to eq('branch')
end
it 'stores whitespace only as empty' do
hook.push_events_branch_filter = ' '
hook.save
expect(hook.push_events_branch_filter).to eq('')
end
end
end end
describe 'execute' do describe 'execute' do
......
...@@ -4049,21 +4049,45 @@ describe Project do ...@@ -4049,21 +4049,45 @@ describe Project do
end end
describe '#execute_hooks' do describe '#execute_hooks' do
it 'executes the projects hooks with the specified scope' do let(:data) { { ref: 'refs/heads/master', data: 'data' } }
hook1 = create(:project_hook, merge_requests_events: true, tag_push_events: false) it 'executes active projects hooks with the specified scope' do
hook2 = create(:project_hook, merge_requests_events: false, tag_push_events: true) hook = create(:project_hook, merge_requests_events: false, push_events: true)
project = create(:project, hooks: [hook1, hook2]) expect(ProjectHook).to receive(:select_active)
.with(:push_hooks, data)
.and_return([hook])
project = create(:project, hooks: [hook])
expect_any_instance_of(ProjectHook).to receive(:async_execute).once expect_any_instance_of(ProjectHook).to receive(:async_execute).once
project.execute_hooks({}, :tag_push_hooks) project.execute_hooks(data, :push_hooks)
end
it 'does not execute project hooks that dont match the specified scope' do
hook = create(:project_hook, merge_requests_events: true, push_events: false)
project = create(:project, hooks: [hook])
expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once
project.execute_hooks(data, :push_hooks)
end
it 'does not execute project hooks which are not active' do
hook = create(:project_hook, push_events: true)
expect(ProjectHook).to receive(:select_active)
.with(:push_hooks, data)
.and_return([])
project = create(:project, hooks: [hook])
expect_any_instance_of(ProjectHook).not_to receive(:async_execute).once
project.execute_hooks(data, :push_hooks)
end end
it 'executes the system hooks with the specified scope' do it 'executes the system hooks with the specified scope' do
expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with({ data: 'data' }, :merge_request_hooks) expect_any_instance_of(SystemHooksService).to receive(:execute_hooks).with(data, :merge_request_hooks)
project = build(:project) project = build(:project)
project.execute_hooks({ data: 'data' }, :merge_request_hooks) project.execute_hooks(data, :merge_request_hooks)
end end
it 'executes the system hooks when inside a transaction' do it 'executes the system hooks when inside a transaction' do
...@@ -4078,7 +4102,7 @@ describe Project do ...@@ -4078,7 +4102,7 @@ describe Project do
# actually get to the `after_commit` hook that queues these jobs. # actually get to the `after_commit` hook that queues these jobs.
expect do expect do
project.transaction do project.transaction do
project.execute_hooks({ data: 'data' }, :merge_request_hooks) project.execute_hooks(data, :merge_request_hooks)
end end
end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
end end
......
...@@ -353,6 +353,15 @@ describe API::MergeRequests do ...@@ -353,6 +353,15 @@ describe API::MergeRequests do
end end
end end
it 'returns the commits behind the target branch when include_diverged_commits_count is present' do
allow_any_instance_of(merge_request.class).to receive(:diverged_commits_count).and_return(1)
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), include_diverged_commits_count: true
expect(response).to have_gitlab_http_status(200)
expect(json_response['diverged_commits_count']).to eq(1)
end
it "returns a 404 error if merge_request_iid not found" do it "returns a 404 error if merge_request_iid not found" do
get api("/projects/#{project.id}/merge_requests/999", user) get api("/projects/#{project.id}/merge_requests/999", user)
expect(response).to have_gitlab_http_status(404) expect(response).to have_gitlab_http_status(404)
......
...@@ -9,7 +9,8 @@ describe API::ProjectHooks, 'ProjectHooks' do ...@@ -9,7 +9,8 @@ describe API::ProjectHooks, 'ProjectHooks' do
:all_events_enabled, :all_events_enabled,
project: project, project: project,
url: 'http://example.com', url: 'http://example.com',
enable_ssl_verification: true) enable_ssl_verification: true,
push_events_branch_filter: 'master')
end end
before do before do
...@@ -38,6 +39,7 @@ describe API::ProjectHooks, 'ProjectHooks' do ...@@ -38,6 +39,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response.first['pipeline_events']).to eq(true) expect(json_response.first['pipeline_events']).to eq(true)
expect(json_response.first['wiki_page_events']).to eq(true) expect(json_response.first['wiki_page_events']).to eq(true)
expect(json_response.first['enable_ssl_verification']).to eq(true) expect(json_response.first['enable_ssl_verification']).to eq(true)
expect(json_response.first['push_events_branch_filter']).to eq('master')
end end
end end
...@@ -90,7 +92,7 @@ describe API::ProjectHooks, 'ProjectHooks' do ...@@ -90,7 +92,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect do expect do
post api("/projects/#{project.id}/hooks", user), post api("/projects/#{project.id}/hooks", user),
url: "http://example.com", issues_events: true, confidential_issues_events: true, wiki_page_events: true, url: "http://example.com", issues_events: true, confidential_issues_events: true, wiki_page_events: true,
job_events: true job_events: true, push_events_branch_filter: 'some-feature-branch'
end.to change {project.hooks.count}.by(1) end.to change {project.hooks.count}.by(1)
expect(response).to have_gitlab_http_status(201) expect(response).to have_gitlab_http_status(201)
...@@ -106,6 +108,7 @@ describe API::ProjectHooks, 'ProjectHooks' do ...@@ -106,6 +108,7 @@ describe API::ProjectHooks, 'ProjectHooks' do
expect(json_response['pipeline_events']).to eq(false) expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true) expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true) expect(json_response['enable_ssl_verification']).to eq(true)
expect(json_response['push_events_branch_filter']).to eq('some-feature-branch')
expect(json_response).not_to include('token') expect(json_response).not_to include('token')
end end
...@@ -132,7 +135,12 @@ describe API::ProjectHooks, 'ProjectHooks' do ...@@ -132,7 +135,12 @@ describe API::ProjectHooks, 'ProjectHooks' do
end end
it "returns a 422 error if url not valid" do it "returns a 422 error if url not valid" do
post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" post api("/projects/#{project.id}/hooks", user), url: "ftp://example.com"
expect(response).to have_gitlab_http_status(422)
end
it "returns a 422 error if branch filter is not valid" do
post api("/projects/#{project.id}/hooks", user), url: "http://example.com", push_events_branch_filter: '~badbranchname/'
expect(response).to have_gitlab_http_status(422) expect(response).to have_gitlab_http_status(422)
end end
end end
......
require 'spec_helper'
describe BranchFilterValidator do
let(:validator) { described_class.new(attributes: [:push_events_branch_filter]) }
let(:hook) { build(:project_hook) }
describe '#validates_each' do
it 'allows valid branch names' do
validator.validate_each(hook, :push_events_branch_filter, "good_branch_name")
validator.validate_each(hook, :push_events_branch_filter, "another/good_branch_name")
expect(hook.errors.empty?).to be true
end
it 'disallows bad branch names' do
validator.validate_each(hook, :push_events_branch_filter, "bad branch~name")
expect(hook.errors[:push_events_branch_filter].empty?).to be false
end
it 'allows wildcards' do
validator.validate_each(hook, :push_events_branch_filter, "features/*")
validator.validate_each(hook, :push_events_branch_filter, "features/*/bla")
validator.validate_each(hook, :push_events_branch_filter, "*-stable")
expect(hook.errors.empty?).to be true
end
it 'gets rid of whitespace' do
filter = ' master '
validator.validate_each(hook, :push_events_branch_filter, filter)
expect(filter).to eq 'master'
end
# Branch names can be quite long but in practice aren't over 255 so 4000 should
# be enough space for a list of branch names but we can increase if needed.
it 'limits length to 4000 chars' do
filter = 'a' * 4001
validator.validate_each(hook, :push_events_branch_filter, filter)
expect(hook.errors[:push_events_branch_filter].empty?).to be false
end
end
end
...@@ -7,10 +7,16 @@ ...@@ -7,10 +7,16 @@
@babel/template,7.0.0-beta.44,MIT @babel/template,7.0.0-beta.44,MIT
@babel/traverse,7.0.0-beta.44,MIT @babel/traverse,7.0.0-beta.44,MIT
@babel/types,7.0.0-beta.44,MIT @babel/types,7.0.0-beta.44,MIT
@gitlab-org/gitlab-svgs,1.27.0,SEE LICENSE IN LICENSE @gitlab-org/gitlab-svgs,1.27.0,MIT
@gitlab-org/gitlab-ui,1.0.5,UNKNOWN @gitlab-org/gitlab-svgs,1.28.0,MIT
@gitlab-org/gitlab-ui,1.0.5,MIT
@sindresorhus/is,0.7.0,MIT @sindresorhus/is,0.7.0,MIT
@types/events,1.2.0,MIT
@types/glob,5.0.35,MIT
@types/jquery,2.0.48,MIT @types/jquery,2.0.48,MIT
@types/minimatch,3.0.3,MIT
@types/node,10.5.2,MIT
@types/parse5,5.0.0,MIT
@vue/component-compiler-utils,1.2.1,MIT @vue/component-compiler-utils,1.2.1,MIT
@webassemblyjs/ast,1.5.13,MIT @webassemblyjs/ast,1.5.13,MIT
@webassemblyjs/floating-point-hex-parser,1.5.13,MIT @webassemblyjs/floating-point-hex-parser,1.5.13,MIT
...@@ -214,7 +220,7 @@ big.js,3.1.3,MIT ...@@ -214,7 +220,7 @@ big.js,3.1.3,MIT
binary-extensions,1.11.0,MIT binary-extensions,1.11.0,MIT
binaryextensions,2.1.1,MIT binaryextensions,2.1.1,MIT
bindata,2.4.3,ruby bindata,2.4.3,ruby
bitsyntax,0.0.4,UNKNOWN bitsyntax,0.0.4,MIT
bl,1.1.2,MIT bl,1.1.2,MIT
blackst0ne-mermaid,7.1.0-fixed,MIT blackst0ne-mermaid,7.1.0-fixed,MIT
blob,0.0.4,MIT* blob,0.0.4,MIT*
...@@ -300,8 +306,8 @@ clone-response,1.0.2,MIT ...@@ -300,8 +306,8 @@ clone-response,1.0.2,MIT
co,4.6.0,MIT co,4.6.0,MIT
code-point-at,1.1.0,MIT code-point-at,1.1.0,MIT
codesandbox-api,0.0.18,MIT codesandbox-api,0.0.18,MIT
codesandbox-import-util-types,1.2.11,UNKNOWN codesandbox-import-util-types,1.2.11,LGPL
codesandbox-import-utils,1.2.11,UNKNOWN codesandbox-import-utils,1.2.11,LGPL
coercible,1.0.0,MIT coercible,1.0.0,MIT
collection-visit,1.0.0,MIT collection-visit,1.0.0,MIT
color-convert,1.9.1,MIT color-convert,1.9.1,MIT
...@@ -357,6 +363,7 @@ cryptiles,3.1.2,New BSD ...@@ -357,6 +363,7 @@ cryptiles,3.1.2,New BSD
crypto-browserify,3.12.0,MIT crypto-browserify,3.12.0,MIT
crypto-random-string,1.0.0,MIT crypto-random-string,1.0.0,MIT
css-loader,1.0.0,MIT css-loader,1.0.0,MIT
css-selector-parser,1.3.0,MIT
css-selector-tokenizer,0.7.0,MIT css-selector-tokenizer,0.7.0,MIT
css_parser,1.5.0,MIT css_parser,1.5.0,MIT
cssesc,0.1.0,MIT cssesc,0.1.0,MIT
...@@ -584,7 +591,7 @@ flush-write-stream,1.0.2,MIT ...@@ -584,7 +591,7 @@ flush-write-stream,1.0.2,MIT
fog-aliyun,0.2.0,MIT fog-aliyun,0.2.0,MIT
fog-aws,2.0.1,MIT fog-aws,2.0.1,MIT
fog-core,1.45.0,MIT fog-core,1.45.0,MIT
fog-google,1.3.3,MIT fog-google,1.7.1,MIT
fog-json,1.0.2,MIT fog-json,1.0.2,MIT
fog-local,0.3.1,MIT fog-local,0.3.1,MIT
fog-openstack,0.1.21,MIT fog-openstack,0.1.21,MIT
...@@ -625,9 +632,11 @@ get-uri,2.0.2,MIT ...@@ -625,9 +632,11 @@ get-uri,2.0.2,MIT
get-value,2.0.6,MIT get-value,2.0.6,MIT
get_process_mem,0.2.0,MIT get_process_mem,0.2.0,MIT
getpass,0.1.7,MIT getpass,0.1.7,MIT
gettext-extractor,3.3.2,MIT
gettext-extractor-vue,4.0.1,MIT
gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT gettext_i18n_rails_js,1.3.0,MIT
gitaly-proto,0.112.0,MIT gitaly-proto,0.113.0,MIT
github-linguist,5.3.3,MIT github-linguist,5.3.3,MIT
github-markup,1.7.0,MIT github-markup,1.7.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT gitlab-flowdock-git-hook,1.0.1,MIT
...@@ -649,7 +658,7 @@ globby,6.1.0,MIT ...@@ -649,7 +658,7 @@ globby,6.1.0,MIT
gollum-grit_adapter,1.0.1,MIT gollum-grit_adapter,1.0.1,MIT
gon,6.2.0,MIT gon,6.2.0,MIT
good-listener,1.2.2,MIT good-listener,1.2.2,MIT
google-api-client,0.19.8,Apache 2.0 google-api-client,0.23.4,Apache 2.0
google-protobuf,3.5.1,New BSD google-protobuf,3.5.1,New BSD
googleapis-common-protos-types,1.0.1,Apache 2.0 googleapis-common-protos-types,1.0.1,Apache 2.0
googleauth,0.6.2,Apache 2.0 googleauth,0.6.2,Apache 2.0
...@@ -845,6 +854,7 @@ jquery.waitforimages,2.2.0,MIT ...@@ -845,6 +854,7 @@ jquery.waitforimages,2.2.0,MIT
js-cookie,2.1.3,MIT js-cookie,2.1.3,MIT
js-tokens,3.0.2,MIT js-tokens,3.0.2,MIT
js-yaml,3.11.0,MIT js-yaml,3.11.0,MIT
js_regex,2.2.1,MIT
jsbn,0.1.1,MIT jsbn,0.1.1,MIT
jsesc,0.5.0,MIT jsesc,0.5.0,MIT
jsesc,1.3.0,MIT jsesc,1.3.0,MIT
...@@ -939,7 +949,7 @@ make-dir,1.2.0,MIT ...@@ -939,7 +949,7 @@ make-dir,1.2.0,MIT
mamacro,0.0.3,MIT mamacro,0.0.3,MIT
map-cache,0.2.2,MIT map-cache,0.2.2,MIT
map-obj,1.0.1,MIT map-obj,1.0.1,MIT
map-stream,0.1.0,UNKNOWN map-stream,0.1.0,MIT
map-visit,1.0.0,MIT map-visit,1.0.0,MIT
marked,0.3.12,MIT marked,0.3.12,MIT
match-at,0.1.1,MIT match-at,0.1.1,MIT
...@@ -952,7 +962,7 @@ memory-fs,0.4.1,MIT ...@@ -952,7 +962,7 @@ memory-fs,0.4.1,MIT
meow,3.7.0,MIT meow,3.7.0,MIT
merge-descriptors,1.0.1,MIT merge-descriptors,1.0.1,MIT
merge-source-map,1.1.0,MIT merge-source-map,1.1.0,MIT
method_source,0.8.2,MIT method_source,0.9.0,MIT
methods,1.1.2,MIT methods,1.1.2,MIT
micromatch,3.1.10,MIT micromatch,3.1.10,MIT
miller-rabin,4.0.1,MIT miller-rabin,4.0.1,MIT
...@@ -1099,6 +1109,7 @@ pako,1.0.6,(MIT AND Zlib) ...@@ -1099,6 +1109,7 @@ pako,1.0.6,(MIT AND Zlib)
parallel-transform,1.1.0,MIT parallel-transform,1.1.0,MIT
parse-asn1,5.1.0,ISC parse-asn1,5.1.0,ISC
parse-json,2.2.0,MIT parse-json,2.2.0,MIT
parse5,5.0.0,MIT
parseqs,0.0.5,MIT parseqs,0.0.5,MIT
parseuri,0.0.5,MIT parseuri,0.0.5,MIT
parseurl,1.3.2,MIT parseurl,1.3.2,MIT
...@@ -1135,6 +1146,7 @@ pkg-dir,1.0.0,MIT ...@@ -1135,6 +1146,7 @@ pkg-dir,1.0.0,MIT
pkg-dir,2.0.0,MIT pkg-dir,2.0.0,MIT
pluralize,7.0.0,MIT pluralize,7.0.0,MIT
po_to_json,1.0.1,MIT po_to_json,1.0.1,MIT
pofile,1.0.11,MIT
popper.js,1.14.3,MIT popper.js,1.14.3,MIT
portfinder,1.0.13,MIT portfinder,1.0.13,MIT
posix-character-classes,0.1.1,MIT posix-character-classes,0.1.1,MIT
...@@ -1199,7 +1211,7 @@ rails-dom-testing,1.0.9,MIT ...@@ -1199,7 +1211,7 @@ rails-dom-testing,1.0.9,MIT
rails-html-sanitizer,1.0.4,MIT rails-html-sanitizer,1.0.4,MIT
rails-i18n,4.0.9,MIT rails-i18n,4.0.9,MIT
railties,4.2.10,MIT railties,4.2.10,MIT
rainbow,2.2.2,MIT rainbow,3.0.0,MIT
raindrops,0.18.0,LGPL-2.1+ raindrops,0.18.0,LGPL-2.1+
rake,12.3.1,MIT rake,12.3.1,MIT
randombytes,2.0.6,MIT randombytes,2.0.6,MIT
...@@ -1244,6 +1256,7 @@ regenerator-runtime,0.10.5,MIT ...@@ -1244,6 +1256,7 @@ regenerator-runtime,0.10.5,MIT
regenerator-runtime,0.11.0,MIT regenerator-runtime,0.11.0,MIT
regenerator-transform,0.10.1,BSD regenerator-transform,0.10.1,BSD
regex-not,1.0.2,MIT regex-not,1.0.2,MIT
regexp_parser,0.5.0,MIT
regexpu-core,1.0.0,MIT regexpu-core,1.0.0,MIT
regexpu-core,2.0.0,MIT regexpu-core,2.0.0,MIT
registry-auth-token,3.3.2,MIT registry-auth-token,3.3.2,MIT
...@@ -1275,13 +1288,13 @@ responselike,1.0.2,MIT ...@@ -1275,13 +1288,13 @@ responselike,1.0.2,MIT
rest-client,2.0.2,MIT rest-client,2.0.2,MIT
restore-cursor,2.0.0,MIT restore-cursor,2.0.0,MIT
ret,0.1.15,MIT ret,0.1.15,MIT
retriable,3.1.1,MIT retriable,3.1.2,MIT
right-align,0.1.3,MIT right-align,0.1.3,MIT
rimraf,2.6.2,ISC rimraf,2.6.2,ISC
rinku,2.0.0,ISC rinku,2.0.0,ISC
ripemd160,2.0.1,MIT ripemd160,2.0.1,MIT
rotp,2.1.2,MIT rotp,2.1.2,MIT
rouge,3.2.0,MIT rouge,3.2.1,MIT
rqrcode,0.7.0,MIT rqrcode,0.7.0,MIT
rqrcode-rails3,0.1.7,MIT rqrcode-rails3,0.1.7,MIT
ruby-enum,0.7.2,MIT ruby-enum,0.7.2,MIT
...@@ -1293,7 +1306,7 @@ ruby_parser,3.9.0,MIT ...@@ -1293,7 +1306,7 @@ ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT rufus-scheduler,3.4.0,MIT
rugged,0.27.2,MIT rugged,0.27.4,MIT
run-async,2.3.0,MIT run-async,2.3.0,MIT
run-queue,1.0.3,ISC run-queue,1.0.3,ISC
rw,1.3.3,New BSD rw,1.3.3,New BSD
...@@ -1352,7 +1365,7 @@ slash,1.0.0,MIT ...@@ -1352,7 +1365,7 @@ slash,1.0.0,MIT
slice-ansi,1.0.0,MIT slice-ansi,1.0.0,MIT
smart-buffer,1.1.15,MIT smart-buffer,1.1.15,MIT
smart-buffer,4.0.1,MIT smart-buffer,4.0.1,MIT
smooshpack,0.0.48,SEE LICENSE.MD IN ROOT smooshpack,0.0.48,LGPL
smtp-connection,2.12.0,MIT smtp-connection,2.12.0,MIT
snapdragon,0.8.1,MIT snapdragon,0.8.1,MIT
snapdragon-node,2.1.1,MIT snapdragon-node,2.1.1,MIT
...@@ -1485,6 +1498,7 @@ tweetnacl,0.14.5,Unlicense ...@@ -1485,6 +1498,7 @@ tweetnacl,0.14.5,Unlicense
type-check,0.3.2,MIT type-check,0.3.2,MIT
type-is,1.6.16,MIT type-is,1.6.16,MIT
typedarray,0.0.6,MIT typedarray,0.0.6,MIT
typescript,2.9.2,Apache 2.0
tzinfo,1.2.5,MIT tzinfo,1.2.5,MIT
u2f,0.2.1,MIT u2f,0.2.1,MIT
uber,0.1.0,MIT uber,0.1.0,MIT
......
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