summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/deployments.rb82
-rw-r--r--lib/api/members.rb21
-rw-r--r--lib/api/settings.rb1
-rw-r--r--lib/gitlab.rb6
-rw-r--r--lib/gitlab/ci/ansi2json.rb12
-rw-r--r--lib/gitlab/ci/ansi2json/converter.rb131
-rw-r--r--lib/gitlab/ci/ansi2json/line.rb93
-rw-r--r--lib/gitlab/ci/ansi2json/parser.rb200
-rw-r--r--lib/gitlab/ci/ansi2json/state.rb98
-rw-r--r--lib/gitlab/ci/ansi2json/style.rb84
-rw-r--r--lib/gitlab/cycle_analytics/summary/deploy.rb2
-rw-r--r--lib/gitlab/diff/position_collection.rb18
-rw-r--r--lib/gitlab/graphql/docs/renderer.rb11
-rw-r--r--lib/gitlab/graphql/docs/templates/default.md.haml3
-rw-r--r--lib/gitlab/health_checks/checks.rb14
-rw-r--r--lib/gitlab/health_checks/probes/collection.rb (renamed from lib/gitlab/health_checks/probes/readiness.rb)7
-rw-r--r--lib/gitlab/health_checks/probes/liveness.rb13
-rw-r--r--lib/gitlab/metrics/exporter/base_exporter.rb6
-rw-r--r--lib/gitlab/metrics/exporter/web_exporter.rb2
-rw-r--r--lib/tasks/gitlab/graphql.rake20
20 files changed, 771 insertions, 53 deletions
diff --git a/lib/api/deployments.rb b/lib/api/deployments.rb
index eb45df31ff9..da882547071 100644
--- a/lib/api/deployments.rb
+++ b/lib/api/deployments.rb
@@ -42,6 +42,88 @@ module API
present deployment, with: Entities::Deployment
end
+
+ desc 'Creates a new deployment' do
+ detail 'This feature was introduced in GitLab 12.4'
+ success Entities::Deployment
+ end
+ params do
+ requires :environment,
+ type: String,
+ desc: 'The name of the environment to deploy to'
+
+ requires :sha,
+ type: String,
+ desc: 'The SHA of the commit that was deployed'
+
+ requires :ref,
+ type: String,
+ desc: 'The name of the branch or tag that was deployed'
+
+ requires :tag,
+ type: Boolean,
+ desc: 'A boolean indicating if the deployment ran for a tag'
+
+ requires :status,
+ type: String,
+ desc: 'The status of the deployment',
+ values: %w[running success failed canceled]
+ end
+ post ':id/deployments' do
+ authorize!(:create_deployment, user_project)
+ authorize!(:create_environment, user_project)
+
+ environment = user_project
+ .environments
+ .find_or_create_by_name(params[:environment])
+
+ unless environment.persisted?
+ render_validation_error!(deployment)
+ end
+
+ authorize!(:create_deployment, environment)
+
+ service = ::Deployments::CreateService
+ .new(environment, current_user, declared_params)
+
+ deployment = service.execute
+
+ if deployment.persisted?
+ present(deployment, with: Entities::Deployment, current_user: current_user)
+ else
+ render_validation_error!(deployment)
+ end
+ end
+
+ desc 'Updates an existing deployment' do
+ detail 'This feature was introduced in GitLab 12.4'
+ success Entities::Deployment
+ end
+ params do
+ requires :status,
+ type: String,
+ desc: 'The new status of the deployment',
+ values: %w[running success failed canceled]
+ end
+ put ':id/deployments/:deployment_id' do
+ authorize!(:read_deployment, user_project)
+
+ deployment = user_project.deployments.find(params[:deployment_id])
+
+ authorize!(:update_deployment, deployment)
+
+ if deployment.deployable
+ forbidden!('Deployments created using GitLab CI can not be updated using the API')
+ end
+
+ service = ::Deployments::UpdateService.new(deployment, declared_params)
+
+ if service.execute
+ present(deployment, with: Entities::Deployment, current_user: current_user)
+ else
+ render_validation_error!(deployment)
+ end
+ end
end
end
end
diff --git a/lib/api/members.rb b/lib/api/members.rb
index 461ffe71a62..1d4616fed52 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -18,6 +18,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
+ optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -26,6 +27,7 @@ module API
members = source.members.where.not(user_id: nil).includes(:user)
members = members.joins(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
members = paginate(members)
present members, with: Entities::Member
@@ -37,6 +39,7 @@ module API
end
params do
optional :query, type: String, desc: 'A query string to search for members'
+ optional :user_ids, type: Array[Integer], desc: 'Array of user ids to look up for membership'
use :pagination
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -45,6 +48,7 @@ module API
members = find_all_members(source_type, source)
members = members.includes(:user).references(:user).merge(User.search(params[:query])) if params[:query].present?
+ members = members.where(user_id: params[:user_ids]) if params[:user_ids].present?
members = paginate(members)
present members, with: Entities::Member
@@ -68,6 +72,23 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ desc 'Gets a member of a group or project, including those who gained membership through ancestor group' do
+ success Entities::Member
+ end
+ params do
+ requires :user_id, type: Integer, desc: 'The user ID of the member'
+ end
+ # rubocop: disable CodeReuse/ActiveRecord
+ get ":id/members/all/:user_id" do
+ source = find_source(source_type, params[:id])
+
+ members = find_all_members(source_type, source)
+ member = members.find_by!(user_id: params[:user_id])
+
+ present member, with: Entities::Member
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
desc 'Adds a member to a group or project.' do
success Entities::Member
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index e4ef507228b..b7a471f14fe 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -101,6 +101,7 @@ module API
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
optional :project_export_enabled, type: Boolean, desc: 'Enable project export'
optional :prometheus_metrics_enabled, type: Boolean, desc: 'Enable Prometheus metrics'
+ optional :push_event_hooks_limit, type: Integer, desc: "Number of changes (branches or tags) in a single push to determine whether webhooks and services will be fired or not. Webhooks and services won't be submitted if it surpasses that value."
optional :recaptcha_enabled, type: Boolean, desc: 'Helps prevent bots from creating accounts'
given recaptcha_enabled: ->(val) { val } do
requires :recaptcha_site_key, type: String, desc: 'Generate site key at http://www.google.com/recaptcha'
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index 0cc9a6a5fb1..ad8e693ccbc 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -69,14 +69,14 @@ module Gitlab
# means that checking the presence of the License class could result in
# this method returning `false`, even for an EE installation.
#
- # The `IS_GITLAB_EE` is always `string` or `nil`
+ # The `FOSS_ONLY` is always `string` or `nil`
# Thus the nil or empty string will result
- # in using default value: true
+ # in using default value: false
#
# The behavior needs to be synchronised with
# config/helpers/is_ee_env.js
root.join('ee/app/models/license.rb').exist? &&
- (ENV['IS_GITLAB_EE'].to_s.empty? || Gitlab::Utils.to_boolean(ENV['IS_GITLAB_EE']))
+ !%w[true 1].include?(ENV['FOSS_ONLY'].to_s)
end
def self.ee
diff --git a/lib/gitlab/ci/ansi2json.rb b/lib/gitlab/ci/ansi2json.rb
new file mode 100644
index 00000000000..79114d35916
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+# Convert terminal stream to JSON
+module Gitlab
+ module Ci
+ module Ansi2json
+ def self.convert(ansi, state = nil)
+ Converter.new.convert(ansi, state)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2json/converter.rb b/lib/gitlab/ci/ansi2json/converter.rb
new file mode 100644
index 00000000000..53adaf38b87
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json/converter.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Ansi2json
+ class Converter
+ def convert(stream, new_state)
+ @lines = []
+ @state = State.new(new_state, stream.size)
+
+ append = false
+ truncated = false
+
+ cur_offset = stream.tell
+ if cur_offset > @state.offset
+ @state.offset = cur_offset
+ truncated = true
+ else
+ stream.seek(@state.offset)
+ append = @state.offset > 0
+ end
+
+ start_offset = @state.offset
+
+ @state.set_current_line!(style: Style.new(@state.inherited_style))
+
+ stream.each_line do |line|
+ s = StringScanner.new(line)
+ convert_line(s)
+ end
+
+ # This must be assigned before flushing the current line
+ # or the @current_line.offset will advance to the very end
+ # of the trace. Instead we want @last_line_offset to always
+ # point to the beginning of last line.
+ @state.set_last_line_offset
+
+ flush_current_line
+
+ OpenStruct.new(
+ lines: @lines,
+ state: @state.encode,
+ append: append,
+ truncated: truncated,
+ offset: start_offset,
+ size: stream.tell - start_offset,
+ total: stream.size
+ )
+ end
+
+ private
+
+ def convert_line(scanner)
+ until scanner.eos?
+
+ if scanner.scan(Gitlab::Regex.build_trace_section_regex)
+ handle_section(scanner)
+ elsif scanner.scan(/\e([@-_])(.*?)([@-~])/)
+ handle_sequence(scanner)
+ elsif scanner.scan(/\e(([@-_])(.*?)?)?$/)
+ break
+ elsif scanner.scan(/</)
+ @state.current_line << '&lt;'
+ elsif scanner.scan(/\r?\n/)
+ # we advance the offset of the next current line
+ # so it does not start from \n
+ flush_current_line(advance_offset: scanner.matched_size)
+ else
+ @state.current_line << scanner.scan(/./m)
+ end
+
+ @state.offset += scanner.matched_size
+ end
+ end
+
+ def handle_sequence(scanner)
+ indicator = scanner[1]
+ commands = scanner[2].split ';'
+ terminator = scanner[3]
+
+ # We are only interested in color and text style changes - triggered by
+ # sequences starting with '\e[' and ending with 'm'. Any other control
+ # sequence gets stripped (including stuff like "delete last line")
+ return unless indicator == '[' && terminator == 'm'
+
+ @state.update_style(commands)
+ end
+
+ def handle_section(scanner)
+ action = scanner[1]
+ timestamp = scanner[2]
+ section = scanner[3]
+
+ section_name = sanitize_section_name(section)
+
+ if action == "start"
+ handle_section_start(section_name, timestamp)
+ elsif action == "end"
+ handle_section_end(section_name, timestamp)
+ end
+ end
+
+ def handle_section_start(section, timestamp)
+ flush_current_line unless @state.current_line.empty?
+ @state.open_section(section, timestamp)
+ end
+
+ def handle_section_end(section, timestamp)
+ return unless @state.section_open?(section)
+
+ flush_current_line unless @state.current_line.empty?
+ @state.close_section(section, timestamp)
+
+ # ensure that section end is detached from the last
+ # line in the section
+ flush_current_line
+ end
+
+ def flush_current_line(advance_offset: 0)
+ @lines << @state.current_line.to_h
+
+ @state.set_current_line!(advance_offset: advance_offset)
+ end
+
+ def sanitize_section_name(section)
+ section.to_s.downcase.gsub(/[^a-z0-9]/, '-')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2json/line.rb b/lib/gitlab/ci/ansi2json/line.rb
new file mode 100644
index 00000000000..173fb1df88e
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json/line.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Ansi2json
+ # Line class is responsible for keeping the internal state of
+ # a log line and to finally serialize it as Hash.
+ class Line
+ # Line::Segment is a portion of a line that has its own style
+ # and text. Multiple segments make the line content.
+ class Segment
+ attr_accessor :text, :style
+
+ def initialize(style:)
+ @text = +''
+ @style = style
+ end
+
+ def empty?
+ text.empty?
+ end
+
+ def to_h
+ # Without force encoding to UTF-8 we could get an error
+ # when serializing the Hash to JSON.
+ # Encoding::UndefinedConversionError:
+ # "\xE2" from ASCII-8BIT to UTF-8
+ { text: text.force_encoding('UTF-8') }.tap do |result|
+ result[:style] = style.to_s if style.set?
+ end
+ end
+ end
+
+ attr_reader :offset, :sections, :segments, :current_segment,
+ :section_header, :section_duration
+
+ def initialize(offset:, style:, sections: [])
+ @offset = offset
+ @segments = []
+ @sections = sections
+ @section_header = false
+ @duration = nil
+ @current_segment = Segment.new(style: style)
+ end
+
+ def <<(data)
+ @current_segment.text << data
+ end
+
+ def style
+ @current_segment.style
+ end
+
+ def empty?
+ @segments.empty? && @current_segment.empty?
+ end
+
+ def update_style(ansi_commands)
+ @current_segment.style.update(ansi_commands)
+ end
+
+ def add_section(section)
+ @sections << section
+ end
+
+ def set_as_section_header
+ @section_header = true
+ end
+
+ def set_section_duration(duration)
+ @section_duration = Time.at(duration.to_i).strftime('%M:%S')
+ end
+
+ def flush_current_segment!
+ return if @current_segment.empty?
+
+ @segments << @current_segment.to_h
+ @current_segment = Segment.new(style: @current_segment.style)
+ end
+
+ def to_h
+ flush_current_segment!
+
+ { offset: offset, content: @segments }.tap do |result|
+ result[:section] = sections.last if sections.any?
+ result[:section_header] = true if @section_header
+ result[:section_duration] = @section_duration if @section_duration
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2json/parser.rb b/lib/gitlab/ci/ansi2json/parser.rb
new file mode 100644
index 00000000000..d428680fb2a
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json/parser.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+# This Parser translates ANSI escape codes into human readable format.
+# It considers color and format changes.
+# Inspired by http://en.wikipedia.org/wiki/ANSI_escape_code
+module Gitlab
+ module Ci
+ module Ansi2json
+ class Parser
+ # keys represent the trailing digit in color changing command (30-37, 40-47, 90-97. 100-107)
+ COLOR = {
+ 0 => 'black', # not that this is gray in the intense color table
+ 1 => 'red',
+ 2 => 'green',
+ 3 => 'yellow',
+ 4 => 'blue',
+ 5 => 'magenta',
+ 6 => 'cyan',
+ 7 => 'white' # not that this is gray in the dark (aka default) color table
+ }.freeze
+
+ STYLE_SWITCHES = {
+ bold: 0x01,
+ italic: 0x02,
+ underline: 0x04,
+ conceal: 0x08,
+ cross: 0x10
+ }.freeze
+
+ def self.bold?(mask)
+ mask & STYLE_SWITCHES[:bold] != 0
+ end
+
+ def self.matching_formats(mask)
+ formats = []
+ STYLE_SWITCHES.each do |text_format, flag|
+ formats << "term-#{text_format}" if mask & flag != 0
+ end
+
+ formats
+ end
+
+ def initialize(command, ansi_stack = nil)
+ @command = command
+ @ansi_stack = ansi_stack
+ end
+
+ def changes
+ if self.respond_to?("on_#{@command}")
+ send("on_#{@command}", @ansi_stack) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+
+ # rubocop:disable Style/SingleLineMethods
+ def on_0(_) { reset: true } end
+
+ def on_1(_) { enable: STYLE_SWITCHES[:bold] } end
+
+ def on_3(_) { enable: STYLE_SWITCHES[:italic] } end
+
+ def on_4(_) { enable: STYLE_SWITCHES[:underline] } end
+
+ def on_8(_) { enable: STYLE_SWITCHES[:conceal] } end
+
+ def on_9(_) { enable: STYLE_SWITCHES[:cross] } end
+
+ def on_21(_) { disable: STYLE_SWITCHES[:bold] } end
+
+ def on_22(_) { disable: STYLE_SWITCHES[:bold] } end
+
+ def on_23(_) { disable: STYLE_SWITCHES[:italic] } end
+
+ def on_24(_) { disable: STYLE_SWITCHES[:underline] } end
+
+ def on_28(_) { disable: STYLE_SWITCHES[:conceal] } end
+
+ def on_29(_) { disable: STYLE_SWITCHES[:cross] } end
+
+ def on_30(_) { fg: fg_color(0) } end
+
+ def on_31(_) { fg: fg_color(1) } end
+
+ def on_32(_) { fg: fg_color(2) } end
+
+ def on_33(_) { fg: fg_color(3) } end
+
+ def on_34(_) { fg: fg_color(4) } end
+
+ def on_35(_) { fg: fg_color(5) } end
+
+ def on_36(_) { fg: fg_color(6) } end
+
+ def on_37(_) { fg: fg_color(7) } end
+
+ def on_38(stack) { fg: fg_color_256(stack) } end
+
+ def on_39(_) { fg: fg_color(9) } end
+
+ def on_40(_) { bg: bg_color(0) } end
+
+ def on_41(_) { bg: bg_color(1) } end
+
+ def on_42(_) { bg: bg_color(2) } end
+
+ def on_43(_) { bg: bg_color(3) } end
+
+ def on_44(_) { bg: bg_color(4) } end
+
+ def on_45(_) { bg: bg_color(5) } end
+
+ def on_46(_) { bg: bg_color(6) } end
+
+ def on_47(_) { bg: bg_color(7) } end
+
+ def on_48(stack) { bg: bg_color_256(stack) } end
+
+ # TODO: all the x9 never get called?
+ def on_49(_) { fg: fg_color(9) } end
+
+ def on_90(_) { fg: fg_color(0, 'l') } end
+
+ def on_91(_) { fg: fg_color(1, 'l') } end
+
+ def on_92(_) { fg: fg_color(2, 'l') } end
+
+ def on_93(_) { fg: fg_color(3, 'l') } end
+
+ def on_94(_) { fg: fg_color(4, 'l') } end
+
+ def on_95(_) { fg: fg_color(5, 'l') } end
+
+ def on_96(_) { fg: fg_color(6, 'l') } end
+
+ def on_97(_) { fg: fg_color(7, 'l') } end
+
+ def on_99(_) { fg: fg_color(9, 'l') } end
+
+ def on_100(_) { fg: bg_color(0, 'l') } end
+
+ def on_101(_) { fg: bg_color(1, 'l') } end
+
+ def on_102(_) { fg: bg_color(2, 'l') } end
+
+ def on_103(_) { fg: bg_color(3, 'l') } end
+
+ def on_104(_) { fg: bg_color(4, 'l') } end
+
+ def on_105(_) { fg: bg_color(5, 'l') } end
+
+ def on_106(_) { fg: bg_color(6, 'l') } end
+
+ def on_107(_) { fg: bg_color(7, 'l') } end
+
+ def on_109(_) { fg: bg_color(9, 'l') } end
+ # rubocop:enable Style/SingleLineMethods
+
+ def fg_color(color_index, prefix = nil)
+ term_color_class(color_index, ['fg', prefix])
+ end
+
+ def fg_color_256(command_stack)
+ xterm_color_class(command_stack, 'fg')
+ end
+
+ def bg_color(color_index, prefix = nil)
+ term_color_class(color_index, ['bg', prefix])
+ end
+
+ def bg_color_256(command_stack)
+ xterm_color_class(command_stack, 'bg')
+ end
+
+ def term_color_class(color_index, prefix)
+ color_name = COLOR[color_index]
+ return if color_name.nil?
+
+ color_class(['term', prefix, color_name])
+ end
+
+ def xterm_color_class(command_stack, prefix)
+ # the 38 and 48 commands have to be followed by "5" and the color index
+ return unless command_stack.length >= 2
+ return unless command_stack[0] == "5"
+
+ command_stack.shift # ignore the "5" command
+ color_index = command_stack.shift.to_i
+
+ return unless color_index >= 0
+ return unless color_index <= 255
+
+ color_class(["xterm", prefix, color_index])
+ end
+
+ def color_class(segments)
+ [segments].flatten.compact.join('-')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2json/state.rb b/lib/gitlab/ci/ansi2json/state.rb
new file mode 100644
index 00000000000..db7a9035b8b
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json/state.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+# In this class we keep track of the state changes that the
+# Converter makes as it scans through the log stream.
+module Gitlab
+ module Ci
+ module Ansi2json
+ class State
+ attr_accessor :offset, :current_line, :inherited_style, :open_sections, :last_line_offset
+
+ def initialize(new_state, stream_size)
+ @offset = 0
+ @inherited_style = {}
+ @open_sections = {}
+ @stream_size = stream_size
+
+ restore_state!(new_state)
+ end
+
+ def encode
+ state = {
+ offset: @last_line_offset,
+ style: @current_line.style.to_h,
+ open_sections: @open_sections
+ }
+ Base64.urlsafe_encode64(state.to_json)
+ end
+
+ def open_section(section, timestamp)
+ @open_sections[section] = timestamp
+
+ @current_line.add_section(section)
+ @current_line.set_as_section_header
+ end
+
+ def close_section(section, timestamp)
+ return unless section_open?(section)
+
+ duration = timestamp.to_i - @open_sections[section].to_i
+ @current_line.set_section_duration(duration)
+
+ @open_sections.delete(section)
+ end
+
+ def section_open?(section)
+ @open_sections.key?(section)
+ end
+
+ def set_current_line!(style: nil, advance_offset: 0)
+ new_line = Line.new(
+ offset: @offset + advance_offset,
+ style: style || @current_line.style,
+ sections: @open_sections.keys
+ )
+ @current_line = new_line
+ end
+
+ def set_last_line_offset
+ @last_line_offset = @current_line.offset
+ end
+
+ def update_style(commands)
+ @current_line.flush_current_segment!
+ @current_line.update_style(commands)
+ end
+
+ private
+
+ def restore_state!(encoded_state)
+ state = decode_state(encoded_state)
+
+ return unless state
+ return if state['offset'].to_i > @stream_size
+
+ @offset = state['offset'].to_i if state['offset']
+ @open_sections = state['open_sections'] if state['open_sections']
+
+ if state['style']
+ @inherited_style = {
+ fg: state.dig('style', 'fg'),
+ bg: state.dig('style', 'bg'),
+ mask: state.dig('style', 'mask')
+ }
+ end
+ end
+
+ def decode_state(state)
+ return unless state.present?
+
+ decoded_state = Base64.urlsafe_decode64(state)
+ return unless decoded_state.present?
+
+ JSON.parse(decoded_state)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/ansi2json/style.rb b/lib/gitlab/ci/ansi2json/style.rb
new file mode 100644
index 00000000000..2739ffdfa5d
--- /dev/null
+++ b/lib/gitlab/ci/ansi2json/style.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Ansi2json
+ class Style
+ attr_reader :fg, :bg, :mask
+
+ def initialize(fg: nil, bg: nil, mask: 0)
+ @fg = fg
+ @bg = bg
+ @mask = mask
+
+ update_formats
+ end
+
+ def update(ansi_commands)
+ command = ansi_commands.shift
+ return unless command
+
+ if changes = Gitlab::Ci::Ansi2json::Parser.new(command, ansi_commands).changes
+ apply_changes(changes)
+ end
+
+ update(ansi_commands)
+ end
+
+ def set?
+ @fg || @bg || @formats.any?
+ end
+
+ def reset!
+ @fg = nil
+ @bg = nil
+ @mask = 0
+ @formats = []
+ end
+
+ def ==(other)
+ self.to_h == other.to_h
+ end
+
+ def to_s
+ [@fg, @bg, @formats].flatten.compact.join(' ')
+ end
+
+ def to_h
+ { fg: @fg, bg: @bg, mask: @mask }
+ end
+
+ private
+
+ def apply_changes(changes)
+ case
+ when changes[:reset]
+ reset!
+ when changes[:fg]
+ @fg = changes[:fg]
+ when changes[:bg]
+ @bg = changes[:bg]
+ when changes[:enable]
+ @mask |= changes[:enable]
+ when changes[:disable]
+ @mask &= ~changes[:disable]
+ else
+ return
+ end
+
+ update_formats
+ end
+
+ def update_formats
+ # Most terminals show bold colored text in the light color variant
+ # Let's mimic that here
+ if @fg.present? && Gitlab::Ci::Ansi2json::Parser.bold?(@mask)
+ @fg = @fg.sub(/fg-([a-z]{2,}+)/, 'fg-l-\1')
+ end
+
+ @formats = Gitlab::Ci::Ansi2json::Parser.matching_formats(@mask)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cycle_analytics/summary/deploy.rb b/lib/gitlab/cycle_analytics/summary/deploy.rb
index 0691f3cd131..5ff8d881143 100644
--- a/lib/gitlab/cycle_analytics/summary/deploy.rb
+++ b/lib/gitlab/cycle_analytics/summary/deploy.rb
@@ -12,7 +12,7 @@ module Gitlab
def value
strong_memoize(:value) do
- query = @project.deployments.where("created_at >= ?", @from)
+ query = @project.deployments.success.where("created_at >= ?", @from)
query = query.where("created_at <= ?", @to) if @to
query.count
end
diff --git a/lib/gitlab/diff/position_collection.rb b/lib/gitlab/diff/position_collection.rb
index 59c60f77aaa..2112d347678 100644
--- a/lib/gitlab/diff/position_collection.rb
+++ b/lib/gitlab/diff/position_collection.rb
@@ -6,13 +6,13 @@ module Gitlab
include Enumerable
# collection - An array of Gitlab::Diff::Position
- def initialize(collection, diff_head_sha)
+ def initialize(collection, diff_head_sha = nil)
@collection = collection
@diff_head_sha = diff_head_sha
end
def each(&block)
- @collection.each(&block)
+ filtered_positions.each(&block)
end
def concat(positions)
@@ -23,9 +23,21 @@ module Gitlab
# positions (https://gitlab.com/gitlab-org/gitlab/issues/33271).
def unfoldable
select do |position|
- position.unfoldable? && position.head_sha == @diff_head_sha
+ position.unfoldable? && valid_head_sha?(position)
end
end
+
+ private
+
+ def filtered_positions
+ @collection.select { |item| item.is_a?(Position) }
+ end
+
+ def valid_head_sha?(position)
+ return true unless @diff_head_sha
+
+ position.head_sha == @diff_head_sha
+ end
end
end
end
diff --git a/lib/gitlab/graphql/docs/renderer.rb b/lib/gitlab/graphql/docs/renderer.rb
index f47a372aa19..41aef64f683 100644
--- a/lib/gitlab/graphql/docs/renderer.rb
+++ b/lib/gitlab/graphql/docs/renderer.rb
@@ -23,15 +23,12 @@ module Gitlab
@parsed_schema = GraphQLDocs::Parser.new(schema, {}).parse
end
- def render
- contents = @layout.render(self)
-
- write_file(contents)
+ def contents
+ # Render and remove an extra trailing new line
+ @contents ||= @layout.render(self).sub!(/\n(?=\Z)/, '')
end
- private
-
- def write_file(contents)
+ def write
filename = File.join(@output_dir, 'index.md')
FileUtils.mkdir_p(@output_dir)
diff --git a/lib/gitlab/graphql/docs/templates/default.md.haml b/lib/gitlab/graphql/docs/templates/default.md.haml
index cc22d43ab4f..33acff38ef4 100644
--- a/lib/gitlab/graphql/docs/templates/default.md.haml
+++ b/lib/gitlab/graphql/docs/templates/default.md.haml
@@ -20,6 +20,3 @@
- type[:fields].each do |field|
= "| `#{field[:name]}` | #{render_field_type(field[:type][:info])} | #{field[:description]} |"
\
-
-
-
diff --git a/lib/gitlab/health_checks/checks.rb b/lib/gitlab/health_checks/checks.rb
deleted file mode 100644
index c4016c5fffd..00000000000
--- a/lib/gitlab/health_checks/checks.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- CHECKS = [
- Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::GitalyCheck
- ].freeze
- end
-end
diff --git a/lib/gitlab/health_checks/probes/readiness.rb b/lib/gitlab/health_checks/probes/collection.rb
index 28abf490ffc..db3ef4834c2 100644
--- a/lib/gitlab/health_checks/probes/readiness.rb
+++ b/lib/gitlab/health_checks/probes/collection.rb
@@ -3,14 +3,13 @@
module Gitlab
module HealthChecks
module Probes
- class Readiness
+ class Collection
attr_reader :checks
# This accepts an array of objects implementing `:readiness`
# that returns `::Gitlab::HealthChecks::Result`
- def initialize(*additional_checks)
- @checks = ::Gitlab::HealthChecks::CHECKS
- @checks += additional_checks
+ def initialize(*checks)
+ @checks = checks
end
def execute
diff --git a/lib/gitlab/health_checks/probes/liveness.rb b/lib/gitlab/health_checks/probes/liveness.rb
deleted file mode 100644
index b4d346e945e..00000000000
--- a/lib/gitlab/health_checks/probes/liveness.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Probes
- class Liveness
- def execute
- Probes::Status.new(200, status: 'ok')
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/metrics/exporter/base_exporter.rb b/lib/gitlab/metrics/exporter/base_exporter.rb
index 01d1ec9305e..7111835c85a 100644
--- a/lib/gitlab/metrics/exporter/base_exporter.rb
+++ b/lib/gitlab/metrics/exporter/base_exporter.rb
@@ -6,7 +6,7 @@ module Gitlab
class BaseExporter < Daemon
attr_reader :server
- attr_accessor :additional_checks
+ attr_accessor :readiness_checks
def enabled?
settings.enabled
@@ -73,11 +73,11 @@ module Gitlab
end
def readiness_probe
- ::Gitlab::HealthChecks::Probes::Readiness.new(*additional_checks)
+ ::Gitlab::HealthChecks::Probes::Collection.new(*readiness_checks)
end
def liveness_probe
- ::Gitlab::HealthChecks::Probes::Liveness.new
+ ::Gitlab::HealthChecks::Probes::Collection.new
end
def render_probe(probe, req, res)
diff --git a/lib/gitlab/metrics/exporter/web_exporter.rb b/lib/gitlab/metrics/exporter/web_exporter.rb
index 597ac289193..3940f6fa155 100644
--- a/lib/gitlab/metrics/exporter/web_exporter.rb
+++ b/lib/gitlab/metrics/exporter/web_exporter.rb
@@ -20,7 +20,7 @@ module Gitlab
def initialize
super
- self.additional_checks = [
+ self.readiness_checks = [
WebExporter::ExporterCheck.new(self),
Gitlab::HealthChecks::PumaCheck,
Gitlab::HealthChecks::UnicornCheck
diff --git a/lib/tasks/gitlab/graphql.rake b/lib/tasks/gitlab/graphql.rake
index fd8df015903..902f22684ee 100644
--- a/lib/tasks/gitlab/graphql.rake
+++ b/lib/tasks/gitlab/graphql.rake
@@ -11,10 +11,28 @@ namespace :gitlab do
task compile_docs: :environment do
renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema.graphql_definition, render_options)
- renderer.render
+ renderer.write
puts "Documentation compiled."
end
+
+ desc 'GitLab | Check if GraphQL docs are up to date'
+ task check_docs: :environment do
+ renderer = Gitlab::Graphql::Docs::Renderer.new(GitlabSchema.graphql_definition, render_options)
+
+ doc = File.read(Rails.root.join(OUTPUT_DIR, 'index.md'))
+
+ if doc == renderer.contents
+ puts "GraphQL documentation is up to date"
+ else
+ puts '#' * 10
+ puts '#'
+ puts '# GraphQL documentation is outdated! Please update it by running `bundle exec rake gitlab:graphql:compile_docs`.'
+ puts '#'
+ puts '#' * 10
+ abort
+ end
+ end
end
end