Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorKushal Pandya <kushalspandya@gmail.com>2017-04-07 00:12:16 +0300
committerKushal Pandya <kushalspandya@gmail.com>2017-04-07 00:12:16 +0300
commit8a5ca1121b090fe813144adf4428e7cb656b65d3 (patch)
tree3e6777ed81c3c08bdd4abba0d00826c68e51cba9 /lib
parent26a672dabc6b5217a58a2ec9b3c88e869c87c6dd (diff)
parent69bf7bfa7b1ff33a66e8b4531ce2302cebf6678b (diff)
Merge branch 'master' into '18471-restrict-tag-pushes-protected-tags'
# Conflicts: # spec/lib/gitlab/import_export/all_models.yml
Diffstat (limited to 'lib')
-rw-r--r--lib/api/jobs.rb2
-rw-r--r--lib/api/runner.rb12
-rw-r--r--lib/api/services.rb9
-rw-r--r--lib/api/v3/builds.rb2
-rw-r--r--lib/api/v3/services.rb6
-rw-r--r--lib/ci/ansi2html.rb62
-rw-r--r--lib/ci/api/builds.rb12
-rw-r--r--lib/container_registry/blob.rb4
-rw-r--r--lib/container_registry/path.rb70
-rw-r--r--lib/container_registry/registry.rb4
-rw-r--r--lib/container_registry/repository.rb48
-rw-r--r--lib/container_registry/tag.rb10
-rw-r--r--lib/gitlab/ci/cron_parser.rb34
-rw-r--r--lib/gitlab/ci/trace.rb136
-rw-r--r--lib/gitlab/ci/trace/stream.rb119
-rw-r--r--lib/gitlab/ci/trace_reader.rb50
-rw-r--r--lib/gitlab/git/repository.rb15
-rw-r--r--lib/gitlab/gitaly_client/ref.rb10
-rw-r--r--lib/gitlab/import_export/import_export.yml3
-rw-r--r--lib/gitlab/import_export/relation_factory.rb1
-rw-r--r--lib/gitlab/regex.rb7
-rw-r--r--lib/microsoft_teams/activity.rb19
-rw-r--r--lib/microsoft_teams/notifier.rb46
-rwxr-xr-xlib/support/init.d/gitlab3
24 files changed, 533 insertions, 151 deletions
diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb
index ffab0aafe59..288b03d940c 100644
--- a/lib/api/jobs.rb
+++ b/lib/api/jobs.rb
@@ -118,7 +118,7 @@ module API
content_type 'text/plain'
env['api.format'] = :binary
- trace = build.trace
+ trace = build.trace.raw
body trace
end
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index d288369e362..6fbb02cb3aa 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -115,7 +115,7 @@ module API
put '/:id' do
job = authenticate_job!
- job.update_attributes(trace: params[:trace]) if params[:trace]
+ job.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build,
project: job.project.path_with_namespace)
@@ -145,16 +145,14 @@ module API
content_range = request.headers['Content-Range']
content_range = content_range.split('-')
- current_length = job.trace_length
- unless current_length == content_range[0].to_i
- return error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{current_length}" })
+ stream_size = job.trace.append(request.body.read, content_range[0].to_i)
+ if stream_size < 0
+ return error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{-stream_size}" })
end
- job.append_trace(request.body.read, content_range[0].to_i)
-
status 202
header 'Job-Status', job.status
- header 'Range', "0-#{job.trace_length}"
+ header 'Range', "0-#{stream_size}"
end
desc 'Authorize artifacts uploading for job' do
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 6802a99311e..65f86caaa51 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -488,6 +488,14 @@ module API
desc: 'The channel name'
}
],
+ 'microsoft-teams' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
+ }
+ ],
'mattermost' => [
{
required: true,
@@ -550,6 +558,7 @@ module API
RedmineService,
SlackService,
MattermostService,
+ MicrosoftTeamsService,
TeamcityService,
]
diff --git a/lib/api/v3/builds.rb b/lib/api/v3/builds.rb
index 6f97102c6ef..4dd03cdf24b 100644
--- a/lib/api/v3/builds.rb
+++ b/lib/api/v3/builds.rb
@@ -120,7 +120,7 @@ module API
content_type 'text/plain'
env['api.format'] = :binary
- trace = build.trace
+ trace = build.trace.raw
body trace
end
diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb
index 3bacaeee032..bbe07ed4212 100644
--- a/lib/api/v3/services.rb
+++ b/lib/api/v3/services.rb
@@ -501,6 +501,12 @@ module API
desc: 'The channel name'
}
],
+ 'microsoft-teams' => [
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
+ ],
'mattermost' => [
{
required: true,
diff --git a/lib/ci/ansi2html.rb b/lib/ci/ansi2html.rb
index b3ccad7b28d..1020452480a 100644
--- a/lib/ci/ansi2html.rb
+++ b/lib/ci/ansi2html.rb
@@ -132,34 +132,54 @@ module Ci
STATE_PARAMS = [:offset, :n_open_tags, :fg_color, :bg_color, :style_mask].freeze
- def convert(raw, new_state)
+ def convert(stream, new_state)
reset_state
- restore_state(raw, new_state) if new_state.present?
-
- start = @offset
- ansi = raw[@offset..-1]
+ restore_state(new_state, stream) if new_state.present?
+
+ append = false
+ truncated = false
+
+ cur_offset = stream.tell
+ if cur_offset > @offset
+ @offset = cur_offset
+ truncated = true
+ else
+ stream.seek(@offset)
+ append = @offset > 0
+ end
+ start_offset = @offset
open_new_tag
- s = StringScanner.new(ansi)
- until s.eos?
- if s.scan(/\e([@-_])(.*?)([@-~])/)
- handle_sequence(s)
- elsif s.scan(/\e(([@-_])(.*?)?)?$/)
- break
- elsif s.scan(/</)
- @out << '&lt;'
- elsif s.scan(/\r?\n/)
- @out << '<br>'
- else
- @out << s.scan(/./m)
+ stream.each_line do |line|
+ s = StringScanner.new(line)
+ until s.eos?
+ if s.scan(/\e([@-_])(.*?)([@-~])/)
+ handle_sequence(s)
+ elsif s.scan(/\e(([@-_])(.*?)?)?$/)
+ break
+ elsif s.scan(/</)
+ @out << '&lt;'
+ elsif s.scan(/\r?\n/)
+ @out << '<br>'
+ else
+ @out << s.scan(/./m)
+ end
+ @offset += s.matched_size
end
- @offset += s.matched_size
end
close_open_tags()
- { state: state, html: @out, text: ansi[0, @offset - start], append: start > 0 }
+ OpenStruct.new(
+ html: @out,
+ state: state,
+ append: append,
+ truncated: truncated,
+ offset: start_offset,
+ size: stream.tell - start_offset,
+ total: stream.size
+ )
end
def handle_sequence(s)
@@ -240,10 +260,10 @@ module Ci
Base64.urlsafe_encode64(state.to_json)
end
- def restore_state(raw, new_state)
+ def restore_state(new_state, stream)
state = Base64.urlsafe_decode64(new_state)
state = JSON.parse(state, symbolize_names: true)
- return if state[:offset].to_i > raw.length
+ return if state[:offset].to_i > stream.size
STATE_PARAMS.each do |param|
send("#{param}=".to_sym, state[param])
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 95cc6308c3b..67b269b330c 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -61,7 +61,7 @@ module Ci
update_runner_info
- build.update_attributes(trace: params[:trace]) if params[:trace]
+ build.trace.set(params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build,
project: build.project.path_with_namespace)
@@ -92,16 +92,14 @@ module Ci
content_range = request.headers['Content-Range']
content_range = content_range.split('-')
- current_length = build.trace_length
- unless current_length == content_range[0].to_i
- return error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{current_length}" })
+ stream_size = build.trace.append(request.body.read, content_range[0].to_i)
+ if stream_size < 0
+ return error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{-stream_size}" })
end
- build.append_trace(request.body.read, content_range[0].to_i)
-
status 202
header 'Build-Status', build.status
- header 'Range', "0-#{build.trace_length}"
+ header 'Range', "0-#{stream_size}"
end
# Authorize artifacts uploading for build - Runners only
diff --git a/lib/container_registry/blob.rb b/lib/container_registry/blob.rb
index eb5a2596177..d5f85f9fcad 100644
--- a/lib/container_registry/blob.rb
+++ b/lib/container_registry/blob.rb
@@ -38,11 +38,11 @@ module ContainerRegistry
end
def delete
- client.delete_blob(repository.name, digest)
+ client.delete_blob(repository.path, digest)
end
def data
- @data ||= client.blob(repository.name, digest, type)
+ @data ||= client.blob(repository.path, digest, type)
end
end
end
diff --git a/lib/container_registry/path.rb b/lib/container_registry/path.rb
new file mode 100644
index 00000000000..a4b5f2aba6c
--- /dev/null
+++ b/lib/container_registry/path.rb
@@ -0,0 +1,70 @@
+module ContainerRegistry
+ ##
+ # Class responsible for extracting project and repository name from
+ # image repository path provided by a containers registry API response.
+ #
+ # Example:
+ #
+ # some/group/my_project/my/image ->
+ # project: some/group/my_project
+ # repository: my/image
+ #
+ class Path
+ InvalidRegistryPathError = Class.new(StandardError)
+
+ LEVELS_SUPPORTED = 3
+
+ def initialize(path)
+ @path = path
+ end
+
+ def valid?
+ @path =~ Gitlab::Regex.container_repository_name_regex &&
+ components.size > 1 &&
+ components.size < Namespace::NUMBER_OF_ANCESTORS_ALLOWED
+ end
+
+ def components
+ @components ||= @path.to_s.split('/')
+ end
+
+ def nodes
+ raise InvalidRegistryPathError unless valid?
+
+ @nodes ||= components.size.downto(2).map do |length|
+ components.take(length).join('/')
+ end
+ end
+
+ def has_project?
+ repository_project.present?
+ end
+
+ def has_repository?
+ return false unless has_project?
+
+ repository_project.container_repositories
+ .where(name: repository_name).any?
+ end
+
+ def root_repository?
+ @path == repository_project.full_path
+ end
+
+ def repository_project
+ @project ||= Project
+ .where_full_path_in(nodes.first(LEVELS_SUPPORTED))
+ .first
+ end
+
+ def repository_name
+ return unless has_project?
+
+ @path.remove(%r(^#{Regexp.escape(repository_project.full_path)}/?))
+ end
+
+ def to_s
+ @path
+ end
+ end
+end
diff --git a/lib/container_registry/registry.rb b/lib/container_registry/registry.rb
index 0e634f6b6ef..63bce655f57 100644
--- a/lib/container_registry/registry.rb
+++ b/lib/container_registry/registry.rb
@@ -8,10 +8,6 @@ module ContainerRegistry
@client = ContainerRegistry::Client.new(uri, options)
end
- def repository(name)
- ContainerRegistry::Repository.new(self, name)
- end
-
private
def default_path
diff --git a/lib/container_registry/repository.rb b/lib/container_registry/repository.rb
deleted file mode 100644
index 0e4a7cb3cc9..00000000000
--- a/lib/container_registry/repository.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-module ContainerRegistry
- class Repository
- attr_reader :registry, :name
-
- delegate :client, to: :registry
-
- def initialize(registry, name)
- @registry, @name = registry, name
- end
-
- def path
- [registry.path, name].compact.join('/')
- end
-
- def tag(tag)
- ContainerRegistry::Tag.new(self, tag)
- end
-
- def manifest
- return @manifest if defined?(@manifest)
-
- @manifest = client.repository_tags(name)
- end
-
- def valid?
- manifest.present?
- end
-
- def tags
- return @tags if defined?(@tags)
- return [] unless manifest && manifest['tags']
-
- @tags = manifest['tags'].map do |tag|
- ContainerRegistry::Tag.new(self, tag)
- end
- end
-
- def blob(config)
- ContainerRegistry::Blob.new(self, config)
- end
-
- def delete_tags
- return unless tags
-
- tags.all?(&:delete)
- end
- end
-end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 59040199920..d00e6191e7e 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -22,9 +22,7 @@ module ContainerRegistry
end
def manifest
- return @manifest if defined?(@manifest)
-
- @manifest = client.repository_manifest(repository.name, name)
+ @manifest ||= client.repository_manifest(repository.path, name)
end
def path
@@ -38,9 +36,7 @@ module ContainerRegistry
end
def digest
- return @digest if defined?(@digest)
-
- @digest = client.repository_tag_digest(repository.name, name)
+ @digest ||= client.repository_tag_digest(repository.path, name)
end
def config_blob
@@ -82,7 +78,7 @@ module ContainerRegistry
def delete
return unless digest
- client.delete_repository_tag(repository.name, digest)
+ client.delete_repository_tag(repository.path, digest)
end
end
end
diff --git a/lib/gitlab/ci/cron_parser.rb b/lib/gitlab/ci/cron_parser.rb
new file mode 100644
index 00000000000..a3cc350ef22
--- /dev/null
+++ b/lib/gitlab/ci/cron_parser.rb
@@ -0,0 +1,34 @@
+module Gitlab
+ module Ci
+ class CronParser
+ VALID_SYNTAX_SAMPLE_TIME_ZONE = 'UTC'.freeze
+ VALID_SYNTAX_SAMPLE_CRON = '* * * * *'.freeze
+
+ def initialize(cron, cron_timezone = 'UTC')
+ @cron = cron
+ @cron_timezone = cron_timezone
+ end
+
+ def next_time_from(time)
+ @cron_line ||= try_parse_cron(@cron, @cron_timezone)
+ @cron_line.next_time(time).in_time_zone(Time.zone) if @cron_line.present?
+ end
+
+ def cron_valid?
+ try_parse_cron(@cron, VALID_SYNTAX_SAMPLE_TIME_ZONE).present?
+ end
+
+ def cron_timezone_valid?
+ try_parse_cron(VALID_SYNTAX_SAMPLE_CRON, @cron_timezone).present?
+ end
+
+ private
+
+ def try_parse_cron(cron, cron_timezone)
+ Rufus::Scheduler.parse("#{cron} #{cron_timezone}")
+ rescue
+ # noop
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
new file mode 100644
index 00000000000..5b835bb669a
--- /dev/null
+++ b/lib/gitlab/ci/trace.rb
@@ -0,0 +1,136 @@
+module Gitlab
+ module Ci
+ class Trace
+ attr_reader :job
+
+ delegate :old_trace, to: :job
+
+ def initialize(job)
+ @job = job
+ end
+
+ def html(last_lines: nil)
+ read do |stream|
+ stream.html(last_lines: last_lines)
+ end
+ end
+
+ def raw(last_lines: nil)
+ read do |stream|
+ stream.raw(last_lines: last_lines)
+ end
+ end
+
+ def extract_coverage(regex)
+ read do |stream|
+ stream.extract_coverage(regex)
+ end
+ end
+
+ def set(data)
+ write do |stream|
+ data = job.hide_secrets(data)
+ stream.set(data)
+ end
+ end
+
+ def append(data, offset)
+ write do |stream|
+ current_length = stream.size
+ return -current_length unless current_length == offset
+
+ data = job.hide_secrets(data)
+ stream.append(data, offset)
+ stream.size
+ end
+ end
+
+ def exist?
+ current_path.present? || old_trace.present?
+ end
+
+ def read
+ stream = Gitlab::Ci::Trace::Stream.new do
+ if current_path
+ File.open(current_path, "rb")
+ elsif old_trace
+ StringIO.new(old_trace)
+ end
+ end
+
+ yield stream
+ ensure
+ stream&.close
+ end
+
+ def write
+ stream = Gitlab::Ci::Trace::Stream.new do
+ File.open(ensure_path, "a+b")
+ end
+
+ yield(stream).tap do
+ job.touch if job.needs_touch?
+ end
+ ensure
+ stream&.close
+ end
+
+ def erase!
+ paths.each do |trace_path|
+ FileUtils.rm(trace_path, force: true)
+ end
+
+ job.erase_old_trace!
+ end
+
+ private
+
+ def ensure_path
+ return current_path if current_path
+
+ ensure_directory
+ default_path
+ end
+
+ def ensure_directory
+ unless Dir.exist?(default_directory)
+ FileUtils.mkdir_p(default_directory)
+ end
+ end
+
+ def current_path
+ @current_path ||= paths.find do |trace_path|
+ File.exist?(trace_path)
+ end
+ end
+
+ def paths
+ [
+ default_path,
+ deprecated_path
+ ].compact
+ end
+
+ def default_directory
+ File.join(
+ Settings.gitlab_ci.builds_path,
+ job.created_at.utc.strftime("%Y_%m"),
+ job.project_id.to_s
+ )
+ end
+
+ def default_path
+ File.join(default_directory, "#{job.id}.log")
+ end
+
+ def deprecated_path
+ File.join(
+ Settings.gitlab_ci.builds_path,
+ job.created_at.utc.strftime("%Y_%m"),
+ job.project.ci_id.to_s,
+ "#{job.id}.log"
+ ) if job.project&.ci_id
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
new file mode 100644
index 00000000000..2af94e2c60e
--- /dev/null
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -0,0 +1,119 @@
+module Gitlab
+ module Ci
+ class Trace
+ # This was inspired from: http://stackoverflow.com/a/10219411/1520132
+ class Stream
+ BUFFER_SIZE = 4096
+ LIMIT_SIZE = 50.kilobytes
+
+ attr_reader :stream
+
+ delegate :close, :tell, :seek, :size, :path, :truncate, to: :stream, allow_nil: true
+
+ delegate :valid?, to: :stream, as: :present?, allow_nil: true
+
+ def initialize
+ @stream = yield
+ end
+
+ def valid?
+ self.stream.present?
+ end
+
+ def file?
+ self.path.present?
+ end
+
+ def limit(last_bytes = LIMIT_SIZE)
+ stream_size = size
+ if stream_size < last_bytes
+ last_bytes = stream_size
+ end
+ stream.seek(-last_bytes, IO::SEEK_END)
+ end
+
+ def append(data, offset)
+ stream.truncate(offset)
+ stream.seek(0, IO::SEEK_END)
+ stream.write(data)
+ stream.flush()
+ end
+
+ def set(data)
+ truncate(0)
+ stream.write(data)
+ stream.flush()
+ end
+
+ def raw(last_lines: nil)
+ return unless valid?
+
+ if last_lines.to_i > 0
+ read_last_lines(last_lines)
+ else
+ stream.read
+ end
+ end
+
+ def html_with_state(state = nil)
+ ::Ci::Ansi2html.convert(stream, state)
+ end
+
+ def html(last_lines: nil)
+ text = raw(last_lines: last_lines)
+ stream = StringIO.new(text)
+ ::Ci::Ansi2html.convert(stream).html
+ end
+
+ def extract_coverage(regex)
+ return unless valid?
+ return unless regex
+
+ regex = Regexp.new(regex)
+
+ match = ""
+
+ stream.each_line do |line|
+ matches = line.scan(regex)
+ next unless matches.is_a?(Array)
+
+ match = matches.flatten.last
+ coverage = match.gsub(/\d+(\.\d+)?/).first
+ return coverage.to_f if coverage.present?
+ end
+ rescue
+ # if bad regex or something goes wrong we dont want to interrupt transition
+ # so we just silentrly ignore error for now
+ end
+
+ private
+
+ def read_last_lines(last_lines)
+ chunks = []
+ pos = lines = 0
+ max = stream.size
+
+ # We want an extra line to make sure fist line has full contents
+ while lines <= last_lines && pos < max
+ pos += BUFFER_SIZE
+
+ buf =
+ if pos <= max
+ stream.seek(-pos, IO::SEEK_END)
+ stream.read(BUFFER_SIZE)
+ else # Reached the head, read only left
+ stream.seek(0)
+ stream.read(BUFFER_SIZE - (pos - max))
+ end
+
+ lines += buf.count("\n")
+ chunks.unshift(buf)
+ end
+
+ chunks.join.lines.last(last_lines).join
+ .force_encoding(Encoding.default_external)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/trace_reader.rb b/lib/gitlab/ci/trace_reader.rb
deleted file mode 100644
index 1d7ddeb3e0f..00000000000
--- a/lib/gitlab/ci/trace_reader.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module Gitlab
- module Ci
- # This was inspired from: http://stackoverflow.com/a/10219411/1520132
- class TraceReader
- BUFFER_SIZE = 4096
-
- attr_accessor :path, :buffer_size
-
- def initialize(new_path, buffer_size: BUFFER_SIZE)
- self.path = new_path
- self.buffer_size = Integer(buffer_size)
- end
-
- def read(last_lines: nil)
- if last_lines
- read_last_lines(last_lines)
- else
- File.read(path)
- end
- end
-
- def read_last_lines(max_lines)
- File.open(path) do |file|
- chunks = []
- pos = lines = 0
- max = file.size
-
- # We want an extra line to make sure fist line has full contents
- while lines <= max_lines && pos < max
- pos += buffer_size
-
- buf = if pos <= max
- file.seek(-pos, IO::SEEK_END)
- file.read(buffer_size)
- else # Reached the head, read only left
- file.seek(0)
- file.read(buffer_size - (pos - max))
- end
-
- lines += buf.count("\n")
- chunks.unshift(buf)
- end
-
- chunks.join.lines.last(max_lines).join
- .force_encoding(Encoding.default_external)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 2e4314932c8..9e338282e96 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -452,6 +452,21 @@ module Gitlab
Gitlab::Git::DiffCollection.new(diff_patches(from, to, options, *paths), options)
end
+ # Returns a RefName for a given SHA
+ def ref_name_for_sha(ref_path, sha)
+ Gitlab::GitalyClient.migrate(:find_ref_name) do |is_enabled|
+ if is_enabled
+ gitaly_ref_client.find_ref_name(sha, ref_path)
+ else
+ args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
+
+ # Not found -> ["", 0]
+ # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
+ Gitlab::Popen.popen(args, @path).first.split.last
+ end
+ end
+ end
+
# Returns commits collection
#
# Ex.
diff --git a/lib/gitlab/gitaly_client/ref.rb b/lib/gitlab/gitaly_client/ref.rb
index bfc5fa573c7..fcdf452d567 100644
--- a/lib/gitlab/gitaly_client/ref.rb
+++ b/lib/gitlab/gitaly_client/ref.rb
@@ -23,6 +23,16 @@ module Gitlab
consume_refs_response(stub.find_all_tag_names(request), prefix: 'refs/tags/')
end
+ def find_ref_name(commit_id, ref_prefix)
+ request = Gitaly::FindRefNameRequest.new(
+ repository: @repository,
+ commit_id: commit_id,
+ prefix: ref_prefix
+ )
+
+ stub.find_ref_name(request).name
+ end
+
private
def consume_refs_response(response, prefix:)
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 745f9a1cfbd..899a6567768 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -39,7 +39,8 @@ project_tree:
- :author
- :events
- :statuses
- - :triggers
+ - triggers:
+ - :trigger_schedule
- :deploy_keys
- :services
- :hooks
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 0545ca10862..71811be6f50 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -5,6 +5,7 @@ module Gitlab
pipelines: 'Ci::Pipeline',
statuses: 'commit_status',
triggers: 'Ci::Trigger',
+ trigger_schedule: 'Ci::TriggerSchedule',
builds: 'Ci::Build',
hooks: 'ProjectHook',
merge_access_levels: 'ProtectedBranch::MergeAccessLevel',
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 5e5f5ff1589..e599dd4a656 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -121,6 +121,13 @@ module Gitlab
git_reference_regex
end
+ ##
+ # Docker Distribution Registry 2.4.1 repository name rules
+ #
+ def container_repository_name_regex
+ @container_repository_regex ||= %r{\A[a-z0-9]+(?:[-._/][a-z0-9]+)*\Z}
+ end
+
def environment_name_regex
@environment_name_regex ||= /\A[a-zA-Z0-9_\\\/\${}. -]+\z/.freeze
end
diff --git a/lib/microsoft_teams/activity.rb b/lib/microsoft_teams/activity.rb
new file mode 100644
index 00000000000..d2c420efdaf
--- /dev/null
+++ b/lib/microsoft_teams/activity.rb
@@ -0,0 +1,19 @@
+module MicrosoftTeams
+ class Activity
+ def initialize(title:, subtitle:, text:, image:)
+ @title = title
+ @subtitle = subtitle
+ @text = text
+ @image = image
+ end
+
+ def prepare
+ {
+ 'activityTitle' => @title,
+ 'activitySubtitle' => @subtitle,
+ 'activityText' => @text,
+ 'activityImage' => @image
+ }
+ end
+ end
+end
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
new file mode 100644
index 00000000000..3bef68a1bcb
--- /dev/null
+++ b/lib/microsoft_teams/notifier.rb
@@ -0,0 +1,46 @@
+module MicrosoftTeams
+ class Notifier
+ def initialize(webhook)
+ @webhook = webhook
+ @header = { 'Content-type' => 'application/json' }
+ end
+
+ def ping(options = {})
+ result = false
+
+ begin
+ response = HTTParty.post(
+ @webhook.to_str,
+ headers: @header,
+ body: body(options)
+ )
+
+ result = true if response
+ rescue HTTParty::Error, StandardError => error
+ Rails.logger.info("#{self.class.name}: Error while connecting to #{@webhook}: #{error.message}")
+ end
+
+ result
+ end
+
+ private
+
+ def body(options = {})
+ result = { 'sections' => [] }
+
+ result['title'] = options[:title]
+ result['summary'] = options[:pretext]
+ result['sections'] << MicrosoftTeams::Activity.new(options[:activity]).prepare
+
+ attachments = options[:attachments]
+ unless attachments.blank?
+ result['sections'] << {
+ 'title' => 'Details',
+ 'facts' => [{ 'name' => 'Attachments', 'value' => attachments }]
+ }
+ end
+
+ result.to_json
+ end
+ end
+end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 09e121e5120..6e351365de0 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -326,8 +326,7 @@ start_gitlab() {
echo "Gitaly is already running with pid $gapid, not restarting"
else
$app_root/bin/daemon_with_pidfile $gitaly_pid_path \
- $app_root/bin/with_env $gitaly_dir/env \
- $gitaly_dir/gitaly >> $gitaly_log 2>&1 &
+ $gitaly_dir/gitaly $gitaly_dir/config.toml >> $gitaly_log 2>&1 &
fi
fi