From f78257cbddd711e18cbce93ad740a4aa0acac347 Mon Sep 17 00:00:00 2001
From: GitLab Bot <gitlab-bot@gitlab.com>
Date: Fri, 6 Mar 2020 15:08:05 +0000
Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master

---
 CHANGELOG.md                                  |   1 -
 .../javascripts/diffs/components/app.vue      |   2 +-
 app/models/ci/bridge.rb                       |   4 +
 app/models/ci/pipeline.rb                     |   4 +
 app/models/snippet.rb                         |   2 +
 .../update_repository_storage_service.rb      |  47 +++--
 app/workers/post_receive.rb                   |   5 +
 ...nt-editing-weight-to-scroll-to-the-top.yml |   5 +
 ...-retiresjs-vars-to-dependency-scanning.yml |   5 +
 .../add-trigger-include-artifact.yml          |   5 +
 ...ecting-all-queues-with-sidekiq-cluster.yml |   5 +
 .../unreleased/allow-to-disable-defaults.yml  |   5 +
 .../fj-update-snippet-from-git-action.yml     |   5 +
 .../remove-cs-kubernetes-workaround.yml       |   5 +
 .../update_repo_storage_checksum.yml          |   5 +
 config/environments/development.rb            |  10 ++
 .../gitlab_self_monitoring_project/index.md   |   7 +-
 .../operations/extra_sidekiq_processes.md     |  24 ++-
 doc/administration/troubleshooting/sidekiq.md |  20 +++
 doc/ci/yaml/README.md                         |  36 ++++
 doc/development/profiling.md                  |  13 ++
 doc/development/sidekiq_debugging.md          |  19 +-
 lib/gitlab/ci/artifact_file_reader.rb         |  71 ++++++++
 lib/gitlab/ci/config.rb                       |  14 +-
 lib/gitlab/ci/config/entry/bridge.rb          |   7 +-
 lib/gitlab/ci/config/entry/include.rb         |   2 +-
 lib/gitlab/ci/config/entry/inherit.rb         |  30 ++++
 lib/gitlab/ci/config/entry/job.rb             |   7 +-
 lib/gitlab/ci/config/entry/processable.rb     |  30 +++-
 lib/gitlab/ci/config/entry/root.rb            |  13 +-
 lib/gitlab/ci/config/entry/workflow.rb        |   1 -
 lib/gitlab/ci/config/external/context.rb      |   5 +-
 .../ci/config/external/file/artifact.rb       |  93 ++++++++++
 lib/gitlab/ci/config/external/file/local.rb   |   3 +-
 lib/gitlab/ci/config/external/file/project.rb |   3 +-
 lib/gitlab/ci/config/external/mapper.rb       |   3 +-
 lib/gitlab/ci/pipeline/chain/base.rb          |   2 +-
 lib/gitlab/ci/pipeline/chain/command.rb       |   4 +
 .../ci/pipeline/chain/config/process.rb       |   3 +-
 .../Security/Container-Scanning.gitlab-ci.yml |   9 +-
 .../Dependency-Scanning.gitlab-ci.yml         |   2 +
 lib/gitlab/ci/yaml_processor.rb               |   9 +-
 lib/gitlab/config/entry/attributable.rb       |   2 +-
 lib/gitlab/config/entry/configurable.rb       |  15 +-
 locale/gitlab.pot                             |  12 ++
 scripts/trigger-build                         |   6 +-
 spec/fixtures/ci_build_artifacts.zip          | Bin 106365 -> 107464 bytes
 spec/lib/backup/manager_spec.rb               |  17 +-
 .../gitlab/ci/artifact_file_reader_spec.rb    | 100 +++++++++++
 .../lib/gitlab/ci/config/entry/bridge_spec.rb |   2 +
 spec/lib/gitlab/ci/config/entry/job_spec.rb   |  16 +-
 spec/lib/gitlab/ci/config/entry/jobs_spec.rb  |   1 +
 .../ci/config/entry/processable_spec.rb       | 105 ++++++++++-
 spec/lib/gitlab/ci/config/entry/root_spec.rb  |  17 +-
 .../ci/config/external/file/artifact_spec.rb  | 167 ++++++++++++++++++
 .../ci/config/external/file/local_spec.rb     |  17 +-
 .../ci/config/external/file/project_spec.rb   |  17 +-
 spec/lib/gitlab/ci/config_spec.rb             |  88 +++++++--
 spec/lib/gitlab/ci/yaml_processor_spec.rb     |  76 +++++---
 .../gitlab/config/entry/attributable_spec.rb  |   2 +-
 spec/models/ci/job_artifact_spec.rb           |   6 +-
 spec/models/ci/pipeline_spec.rb               |  13 ++
 spec/models/snippet_spec.rb                   |  26 +++
 spec/requests/api/runner_spec.rb              |   2 +-
 .../custom_config_content_spec.rb             |  67 +++++--
 spec/services/projects/fork_service_spec.rb   |   1 +
 .../update_repository_storage_service_spec.rb |  68 +++++--
 .../config/inheritable_shared_examples.rb     |   8 +-
 ...ository_storage_service_shared_examples.rb |  46 ++++-
 spec/workers/post_receive_spec.rb             |  22 ++-
 70 files changed, 1242 insertions(+), 222 deletions(-)
 create mode 100644 changelogs/unreleased/207249-prevent-editing-weight-to-scroll-to-the-top.yml
 create mode 100644 changelogs/unreleased/add-retiresjs-vars-to-dependency-scanning.yml
 create mode 100644 changelogs/unreleased/add-trigger-include-artifact.yml
 create mode 100644 changelogs/unreleased/allow-selecting-all-queues-with-sidekiq-cluster.yml
 create mode 100644 changelogs/unreleased/allow-to-disable-defaults.yml
 create mode 100644 changelogs/unreleased/fj-update-snippet-from-git-action.yml
 create mode 100644 changelogs/unreleased/remove-cs-kubernetes-workaround.yml
 create mode 100644 changelogs/unreleased/update_repo_storage_checksum.yml
 create mode 100644 lib/gitlab/ci/artifact_file_reader.rb
 create mode 100644 lib/gitlab/ci/config/entry/inherit.rb
 create mode 100644 lib/gitlab/ci/config/external/file/artifact.rb
 create mode 100644 spec/lib/gitlab/ci/artifact_file_reader_spec.rb
 create mode 100644 spec/lib/gitlab/ci/config/external/file/artifact_spec.rb

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9c320e9e96..f257df035f2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,6 @@ entry.
 
 ## 12.8.4
 
-- No changes.
 ### Fixed (8 changes)
 
 - Fix Group Import API file upload when object storage is disabled. !25715
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index f9d3d31e152..77cd2afc106 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -398,7 +398,7 @@ export default {
           }"
         >
           <commit-widget v-if="commit" :commit="commit" />
-          <div v-if="isBatchLoading" class="loading"><gl-loading-icon /></div>
+          <div v-if="isBatchLoading" class="loading"><gl-loading-icon size="lg" /></div>
           <template v-else-if="renderDiffFiles">
             <diff-file
               v-for="file in diffFiles"
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index a28e44d44c1..d62aa09e432 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -89,6 +89,10 @@ module Ci
       end
     end
 
+    def parent_pipeline
+      pipeline if triggers_child_pipeline?
+    end
+
     def triggers_child_pipeline?
       yaml_for_downstream.present?
     end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index ff257c6cd68..bc0528bdbe0 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -787,6 +787,10 @@ module Ci
         .fabricate!
     end
 
+    def find_job_with_archive_artifacts(name)
+      builds.latest.with_artifacts_archive.find_by_name(name)
+    end
+
     def latest_builds_with_artifacts
       # We purposely cast the builds to an Array here. Because we always use the
       # rows if there are more than 0 this prevents us from having to run two
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 770d9b5205c..27e1778e9b6 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -197,6 +197,8 @@ class Snippet < ApplicationRecord
   end
 
   def blobs
+    return [] unless repository_exists?
+
     repository.ls_files(repository.root_ref).map { |file| Blob.lazy(self, repository.root_ref, file) }
   end
 
diff --git a/app/services/projects/update_repository_storage_service.rb b/app/services/projects/update_repository_storage_service.rb
index 30b99e85304..1565b534b19 100644
--- a/app/services/projects/update_repository_storage_service.rb
+++ b/app/services/projects/update_repository_storage_service.rb
@@ -4,6 +4,7 @@ module Projects
   class UpdateRepositoryStorageService < BaseService
     include Gitlab::ShellAdapter
 
+    Error = Class.new(StandardError)
     RepositoryAlreadyMoved = Class.new(StandardError)
 
     def initialize(project)
@@ -17,37 +18,45 @@ module Projects
       # exception.
       raise RepositoryAlreadyMoved if project.repository_storage == new_repository_storage_key
 
-      if mirror_repositories(new_repository_storage_key)
-        mark_old_paths_for_archive
+      mirror_repositories(new_repository_storage_key)
 
-        project.update(repository_storage: new_repository_storage_key, repository_read_only: false)
-        project.leave_pool_repository
-        project.track_project_repository
+      mark_old_paths_for_archive
 
-        enqueue_housekeeping
-      else
-        project.update(repository_read_only: false)
-      end
+      project.update(repository_storage: new_repository_storage_key, repository_read_only: false)
+      project.leave_pool_repository
+      project.track_project_repository
+
+      enqueue_housekeeping
+
+      success
+
+    rescue Error => e
+      project.update(repository_read_only: false)
+
+      Gitlab::ErrorTracking.track_exception(e, project_path: project.full_path)
+
+      error(s_("UpdateRepositoryStorage|Error moving repository storage for %{project_full_path} - %{message}") % { project_full_path: project.full_path, message: e.message })
     end
 
     private
 
     def mirror_repositories(new_repository_storage_key)
-      result = mirror_repository(new_repository_storage_key)
+      mirror_repository(new_repository_storage_key)
 
       if project.wiki.repository_exists?
-        result &&= mirror_repository(new_repository_storage_key, type: Gitlab::GlRepository::WIKI)
+        mirror_repository(new_repository_storage_key, type: Gitlab::GlRepository::WIKI)
       end
-
-      result
     end
 
     def mirror_repository(new_storage_key, type: Gitlab::GlRepository::PROJECT)
-      return false unless wait_for_pushes(type)
+      unless wait_for_pushes(type)
+        raise Error, s_('UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes') % { type: type.name }
+      end
 
       repository = type.repository_for(project)
       full_path = repository.full_path
       raw_repository = repository.raw
+      checksum = repository.checksum
 
       # Initialize a git repository on the target path
       gitlab_shell.create_repository(new_storage_key, raw_repository.relative_path, full_path)
@@ -56,7 +65,15 @@ module Projects
                                                    raw_repository.gl_repository,
                                                    full_path)
 
-      new_repository.fetch_repository_as_mirror(raw_repository)
+      unless new_repository.fetch_repository_as_mirror(raw_repository)
+        raise Error, s_('UpdateRepositoryStorage|Failed to fetch %{type} repository as mirror') % { type: type.name }
+      end
+
+      new_checksum = new_repository.checksum
+
+      if checksum != new_checksum
+        raise Error, s_('UpdateRepositoryStorage|Failed to verify %{type} repository checksum from %{old} to %{new}') % { type: type.name, old: checksum, new: new_checksum }
+      end
     end
 
     def mark_old_paths_for_archive
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 5178fabb2d8..05b6d6d570c 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -77,6 +77,11 @@ class PostReceive # rubocop:disable Scalability/IdempotentWorker
 
     return false unless user
 
+    # We can remove once we implement multi-file snippets
+    # https://gitlab.com/gitlab-org/gitlab/-/issues/39269
+    blob = snippet.blobs.first
+    snippet.update(file_name: blob.path, content: blob.data) if blob
+
     # At the moment, we only expires the repository caches.
     # In the future we might need to call ProjectCacheWorker
     # (or the custom class we create) to update the snippet
diff --git a/changelogs/unreleased/207249-prevent-editing-weight-to-scroll-to-the-top.yml b/changelogs/unreleased/207249-prevent-editing-weight-to-scroll-to-the-top.yml
new file mode 100644
index 00000000000..59382ad677e
--- /dev/null
+++ b/changelogs/unreleased/207249-prevent-editing-weight-to-scroll-to-the-top.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent editing weight to scroll to the top.
+merge_request: 26613
+author: Gilang Gumilar
+type: fixed
diff --git a/changelogs/unreleased/add-retiresjs-vars-to-dependency-scanning.yml b/changelogs/unreleased/add-retiresjs-vars-to-dependency-scanning.yml
new file mode 100644
index 00000000000..5c9f312f44e
--- /dev/null
+++ b/changelogs/unreleased/add-retiresjs-vars-to-dependency-scanning.yml
@@ -0,0 +1,5 @@
+---
+title: Add vars to allow air-gapped usage of Retire.js (Dependency Scanning)
+merge_request: 26463
+author:
+type: added
diff --git a/changelogs/unreleased/add-trigger-include-artifact.yml b/changelogs/unreleased/add-trigger-include-artifact.yml
new file mode 100644
index 00000000000..acfad941751
--- /dev/null
+++ b/changelogs/unreleased/add-trigger-include-artifact.yml
@@ -0,0 +1,5 @@
+---
+title: 'Create child pipelines dynamically using content from artifact as CI configuration'
+merge_request: 23790
+author:
+type: fixed
diff --git a/changelogs/unreleased/allow-selecting-all-queues-with-sidekiq-cluster.yml b/changelogs/unreleased/allow-selecting-all-queues-with-sidekiq-cluster.yml
new file mode 100644
index 00000000000..7dba322b07e
--- /dev/null
+++ b/changelogs/unreleased/allow-selecting-all-queues-with-sidekiq-cluster.yml
@@ -0,0 +1,5 @@
+---
+title: Allow selecting all queues with sidekiq-cluster
+merge_request: 26594
+author:
+type: added
diff --git a/changelogs/unreleased/allow-to-disable-defaults.yml b/changelogs/unreleased/allow-to-disable-defaults.yml
new file mode 100644
index 00000000000..c5c6616a8c2
--- /dev/null
+++ b/changelogs/unreleased/allow-to-disable-defaults.yml
@@ -0,0 +1,5 @@
+---
+title: Allow to disable inheritance of default job settings
+merge_request: 25690
+author:
+type: added
diff --git a/changelogs/unreleased/fj-update-snippet-from-git-action.yml b/changelogs/unreleased/fj-update-snippet-from-git-action.yml
new file mode 100644
index 00000000000..bf210ba1a14
--- /dev/null
+++ b/changelogs/unreleased/fj-update-snippet-from-git-action.yml
@@ -0,0 +1,5 @@
+---
+title: Sync snippet after Git action
+merge_request: 26565
+author:
+type: changed
diff --git a/changelogs/unreleased/remove-cs-kubernetes-workaround.yml b/changelogs/unreleased/remove-cs-kubernetes-workaround.yml
new file mode 100644
index 00000000000..879da58aa3c
--- /dev/null
+++ b/changelogs/unreleased/remove-cs-kubernetes-workaround.yml
@@ -0,0 +1,5 @@
+---
+title: Remove kubernetes workaround in container scanning
+merge_request: 21188
+author:
+type: changed
diff --git a/changelogs/unreleased/update_repo_storage_checksum.yml b/changelogs/unreleased/update_repo_storage_checksum.yml
new file mode 100644
index 00000000000..b2f8b673730
--- /dev/null
+++ b/changelogs/unreleased/update_repo_storage_checksum.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure checksums match when updating repository storage
+merge_request: 26334
+author:
+type: changed
diff --git a/config/environments/development.rb b/config/environments/development.rb
index dc804197fef..b6b025112fe 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -50,4 +50,14 @@ Rails.application.configure do
 
   # BetterErrors live shell (REPL) on every stack frame
   BetterErrors::Middleware.allow_ip!("127.0.0.1/0")
+
+  # Reassign some performance related settings when we profile the app
+  if Gitlab::Utils.to_boolean(ENV['RAILS_PROFILE'].to_s)
+    warn "Hot-reloading is disabled as you are running with RAILS_PROFILE enabled"
+    config.cache_classes = true
+    config.eager_load = true
+    config.active_record.migration_error = false
+    config.active_record.verbose_query_logs = false
+    config.action_view.cache_template_loading = true
+  end
 end
diff --git a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
index 1946e55603c..6f9821f4ace 100644
--- a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
+++ b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
@@ -17,7 +17,7 @@ members to the group in order to give them maintainer access to the project.
 
 This project will be used for self monitoring your GitLab instance.
 
-## Activating the self monitoring project
+## Creating the self monitoring project
 
 1. Navigate to **Admin Area > Settings > Metrics and profiling**, and expand the **Self monitoring** section.
 1. Toggle the **Create Project** button on.
@@ -26,10 +26,11 @@ created, GitLab displays a message with a link to the project. The project
 will also be linked in the help text above the **Create Project** button. You can also
 find the project under **Projects > Your projects**.
 
-## Deactivating the self monitoring project
+## Deleting the self monitoring project
 
 CAUTION: **Warning:**
-If you deactivate the self monitoring project, it will be permanently deleted.
+If you delete the self monitoring project, you will lose any changes made to the
+project. If you create the project again, it will be created in its default state.
 
 1. Navigate to **Admin Area > Settings > Metrics and profiling**, and expand the **Self monitoring** section.
 1. Toggle the **Create Project** button off.
diff --git a/doc/administration/operations/extra_sidekiq_processes.md b/doc/administration/operations/extra_sidekiq_processes.md
index 3ad411f6f5a..f859db5caff 100644
--- a/doc/administration/operations/extra_sidekiq_processes.md
+++ b/doc/administration/operations/extra_sidekiq_processes.md
@@ -53,6 +53,20 @@ To start extra Sidekiq processes, you must enable `sidekiq-cluster`:
    ]
    ```
 
+   [In GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26594) and
+   later, the special queue name `*` means all queues. This starts two
+   processes, each handling all queues:
+
+   ```ruby
+   sidekiq_cluster['queue_groups'] = [
+     "*",
+     "*"
+   ]
+   ```
+
+   `*` cannot be combined with concrete queue names - `*, mailers` will
+   just handle the `mailers` queue.
+
 1. Save the file and reconfigure GitLab for the changes to take effect:
 
    ```shell
@@ -154,6 +168,10 @@ from highest to lowest precedence:
 The operator precedence for this syntax is fixed: it's not possible to make AND
 have higher precedence than OR.
 
+[In GitLab 12.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/26594) and
+later, as with the standard queue group syntax above, a single `*` as the
+entire queue group selects all queues.
+
 ### Example queries
 
 In `/etc/gitlab/gitlab.rb`:
@@ -163,9 +181,11 @@ sidekiq_cluster['enable'] = true
 sidekiq_cluster['experimental_queue_selector'] = true
 sidekiq_cluster['queue_groups'] = [
   # Run all non-CPU-bound queues that are high urgency
-  'resource_boundary!=cpu&urgency=high,
+  'resource_boundary!=cpu&urgency=high',
   # Run all continuous integration and pages queues that are not high urgency
-  'feature_category=continuous_integration,pages&urgency!=high
+  'feature_category=continuous_integration,pages&urgency!=high',
+  # Run all queues
+  '*'
 ]
 ```
 
diff --git a/doc/administration/troubleshooting/sidekiq.md b/doc/administration/troubleshooting/sidekiq.md
index b72bce5b3c6..8176ebab2e8 100644
--- a/doc/administration/troubleshooting/sidekiq.md
+++ b/doc/administration/troubleshooting/sidekiq.md
@@ -18,6 +18,26 @@ troubleshooting steps that will help you diagnose the bottleneck.
 > may be using all available CPU, or have a Ruby Global Interpreter Lock,
 > preventing other threads from continuing.
 
+## Log arguments to Sidekiq jobs
+
+If you want to see what arguments are being passed to Sidekiq jobs you can set
+the `SIDEKIQ_LOG_ARGUMENTS` [environment variable](https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
+
+Example:
+
+```
+gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "1"}
+```
+
+Please note: It is not recommend to enable this setting in production because some
+Sidekiq jobs (such as sending a password reset email) take secret arguments (for
+example the password reset token).
+
+When using [Sidekiq JSON logging](../logs.md#sidekiqlog),
+arguments logs are limited to a maximum size of 10 kilobytes of text;
+any arguments after this limit will be discarded and replaced with a
+single argument containing the string `"..."`.
+
 ## Thread dump
 
 Send the Sidekiq process ID the `TTIN` signal and it will output thread
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 00b5d5d0d6d..9976a423996 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -158,6 +158,42 @@ rspec 2.6:
   script: bundle exec rspec
 ```
 
+### `inherit`
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/207484) in GitLab 12.9.
+
+You can disable inheritance of globally defined defaults
+and variables with the `inherit:` parameter.
+
+In the example below:
+
+- `rubocop` **will** inherit both the `before_script` and the variable `DOMAIN`.
+- `rspec` **will not** inherit the `before_script` or the variable `DOMAIN`.
+- `capybara` **will** inherit the `before_script`, but **will not** inherit the variable `DOMAIN`.
+
+```yaml
+default:
+  before_script:
+    - echo Hello World
+
+variables:
+  DOMAIN: example.com
+
+rubocop:
+  script: bundle exec rubocop
+
+rspec:
+  inherit:
+    default: false
+    variables: false
+  script: bundle exec rspec
+
+capybara:
+  inherit:
+    variables: false
+  script: bundle exec capybara
+```
+
 ## Parameter details
 
 The following are detailed explanations for parameters used to configure CI/CD pipelines.
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 316273f37b8..04713055117 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -120,3 +120,16 @@ Bullet will log query problems to both the Rails log as well as the Chrome
 console.
 
 As a follow up to finding `N+1` queries with Bullet, consider writing a [QueryRecoder test](query_recorder.md) to prevent a regression.
+
+## Settings that impact performance
+
+1. `development` environment by default works with hot-reloading enabled, this makes Rails to check file changes every request, and create a potential contention lock, as hot reload is single threaded.
+1. `development` environment can load code lazily once the request is fired which results in first request to always be slow.
+
+To disable those features for profiling/benchmarking set the `RAILS_PROFILE` environment variable to `true` before starting GitLab. For example when using GDK:
+
+- create a file [`env.runit`](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/runit.md#modifying-environment-configuration-for-services) in the root GDK directory
+- add `export RAILS_PROFILE=true` to your `env.runit` file
+- restart GDK with `gdk restart`
+
+*This environment variable is only applicable for the development mode.*
diff --git a/doc/development/sidekiq_debugging.md b/doc/development/sidekiq_debugging.md
index 2b3a9481b93..b6a11dd813d 100644
--- a/doc/development/sidekiq_debugging.md
+++ b/doc/development/sidekiq_debugging.md
@@ -2,20 +2,5 @@
 
 ## Log arguments to Sidekiq jobs
 
-If you want to see what arguments are being passed to Sidekiq jobs you can set
-the `SIDEKIQ_LOG_ARGUMENTS` [environment variable](https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
-
-Example:
-
-```
-gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "1"}
-```
-
-Please note: It is not recommend to enable this setting in production because some
-Sidekiq jobs (such as sending a password reset email) take secret arguments (for
-example the password reset token).
-
-When using [Sidekiq JSON logging](../administration/logs.md#sidekiqlog),
-arguments logs are limited to a maximum size of 10 kilobytes of text;
-any arguments after this limit will be discarded and replaced with a
-single argument containing the string `"..."`.
+This content has been moved to the
+[Troubleshooting Sidekiq docs](../administration/troubleshooting/sidekiq.md).
diff --git a/lib/gitlab/ci/artifact_file_reader.rb b/lib/gitlab/ci/artifact_file_reader.rb
new file mode 100644
index 00000000000..c2d17cc176e
--- /dev/null
+++ b/lib/gitlab/ci/artifact_file_reader.rb
@@ -0,0 +1,71 @@
+# frozen_string_literal: true
+
+# This class takes in input a Ci::Build object and an artifact path to read.
+# It downloads and extracts the artifacts archive, then returns the content
+# of the artifact, if found.
+module Gitlab
+  module Ci
+    class ArtifactFileReader
+      Error = Class.new(StandardError)
+
+      MAX_ARCHIVE_SIZE = 5.megabytes
+
+      def initialize(job)
+        @job = job
+
+        raise ArgumentError, 'Job does not have artifacts' unless @job.artifacts?
+
+        validate!
+      end
+
+      def read(path)
+        return unless job.artifacts_metadata
+
+        metadata_entry = job.artifacts_metadata_entry(path)
+
+        if metadata_entry.total_size > MAX_ARCHIVE_SIZE
+          raise Error, "Artifacts archive for job `#{job.name}` is too large: max #{max_archive_size_in_mb}"
+        end
+
+        read_zip_file!(path)
+      end
+
+      private
+
+      attr_reader :job
+
+      def validate!
+        if job.job_artifacts_archive.size > MAX_ARCHIVE_SIZE
+          raise Error, "Artifacts archive for job `#{job.name}` is too large: max #{max_archive_size_in_mb}"
+        end
+
+        unless job.artifacts_metadata?
+          raise Error, "Job `#{job.name}` has missing artifacts metadata and cannot be extracted!"
+        end
+      end
+
+      def read_zip_file!(file_path)
+        job.artifacts_file.use_file do |archive_path|
+          Zip::File.open(archive_path) do |zip_file|
+            entry = zip_file.find_entry(file_path)
+            unless entry
+              raise Error, "Path `#{file_path}` does not exist inside the `#{job.name}` artifacts archive!"
+            end
+
+            if entry.name_is_directory?
+              raise Error, "Path `#{file_path}` was expected to be a file but it was a directory!"
+            end
+
+            zip_file.get_input_stream(entry) do |is|
+              is.read
+            end
+          end
+        end
+      end
+
+      def max_archive_size_in_mb
+        ActiveSupport::NumberHelper.number_to_human_size(MAX_ARCHIVE_SIZE)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index 38ab3475d01..e069f734e32 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -18,12 +18,9 @@ module Gitlab
 
       attr_reader :root
 
-      def initialize(config, project: nil, sha: nil, user: nil)
-        @context = build_context(project: project, sha: sha, user: user)
-
-        if Feature.enabled?(:ci_limit_yaml_expansion, project, default_enabled: true)
-          @context.set_deadline(TIMEOUT_SECONDS)
-        end
+      def initialize(config, project: nil, sha: nil, user: nil, parent_pipeline: nil)
+        @context = build_context(project: project, sha: sha, user: user, parent_pipeline: parent_pipeline)
+        @context.set_deadline(TIMEOUT_SECONDS)
 
         @config = expand_config(config)
 
@@ -87,11 +84,12 @@ module Gitlab
         initial_config
       end
 
-      def build_context(project:, sha:, user:)
+      def build_context(project:, sha:, user:, parent_pipeline:)
         Config::External::Context.new(
           project: project,
           sha: sha || project&.repository&.root_ref_sha,
-          user: user)
+          user: user,
+          parent_pipeline: parent_pipeline)
       end
 
       def track_and_raise_for_dev_exception(error)
diff --git a/lib/gitlab/ci/config/entry/bridge.rb b/lib/gitlab/ci/config/entry/bridge.rb
index 6fdaa373170..f4362d3b0ce 100644
--- a/lib/gitlab/ci/config/entry/bridge.rb
+++ b/lib/gitlab/ci/config/entry/bridge.rb
@@ -11,7 +11,7 @@ module Gitlab
         class Bridge < ::Gitlab::Config::Entry::Node
           include ::Gitlab::Ci::Config::Entry::Processable
 
-          ALLOWED_KEYS = %i[trigger allow_failure when variables needs].freeze
+          ALLOWED_KEYS = %i[trigger allow_failure when needs].freeze
 
           validations do
             validates :config, allowed_keys: ALLOWED_KEYS + PROCESSABLE_ALLOWED_KEYS
@@ -45,10 +45,6 @@ module Gitlab
             inherit: false,
             metadata: { allowed_needs: %i[job bridge] }
 
-          entry :variables, ::Gitlab::Ci::Config::Entry::Variables,
-            description: 'Environment variables available for this job.',
-            inherit: false
-
           attributes :when, :allow_failure
 
           def self.matching?(name, config)
@@ -67,7 +63,6 @@ module Gitlab
               needs: (needs_value if needs_defined?),
               ignore: !!allow_failure,
               when: self.when,
-              variables: (variables_value if variables_defined?),
               scheduling_type: needs_defined? && !bridge_needs ? :dag : :stage
             ).compact
           end
diff --git a/lib/gitlab/ci/config/entry/include.rb b/lib/gitlab/ci/config/entry/include.rb
index f2f3dd84eda..cd09d83b728 100644
--- a/lib/gitlab/ci/config/entry/include.rb
+++ b/lib/gitlab/ci/config/entry/include.rb
@@ -10,7 +10,7 @@ module Gitlab
         class Include < ::Gitlab::Config::Entry::Node
           include ::Gitlab::Config::Entry::Validatable
 
-          ALLOWED_KEYS = %i[local file remote template].freeze
+          ALLOWED_KEYS = %i[local file remote template artifact job].freeze
 
           validations do
             validates :config, hash_or_string: true
diff --git a/lib/gitlab/ci/config/entry/inherit.rb b/lib/gitlab/ci/config/entry/inherit.rb
new file mode 100644
index 00000000000..540f1e62c6c
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/inherit.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Ci
+    class Config
+      module Entry
+        ##
+        # This class represents a inherit entry
+        #
+        class Inherit < ::Gitlab::Config::Entry::Node
+          include ::Gitlab::Config::Entry::Configurable
+
+          ALLOWED_KEYS = %i[default variables].freeze
+
+          validations do
+            validates :config, allowed_keys: ALLOWED_KEYS
+          end
+
+          entry :default, ::Gitlab::Config::Entry::Boolean,
+            description: 'Indicates whether to inherit `default:`.',
+            default: true
+
+          entry :variables, ::Gitlab::Config::Entry::Boolean,
+            description: 'Indicates whether to inherit `variables:`.',
+            default: true
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index 8db21b116eb..1ea59491378 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -13,7 +13,7 @@ module Gitlab
           ALLOWED_WHEN = %w[on_success on_failure always manual delayed].freeze
           ALLOWED_KEYS = %i[tags script type image services
                             allow_failure type when start_in artifacts cache
-                            dependencies before_script needs after_script variables
+                            dependencies before_script needs after_script
                             environment coverage retry parallel interruptible timeout
                             resource_group release].freeze
 
@@ -112,10 +112,6 @@ module Gitlab
             metadata: { allowed_needs: %i[job cross_dependency] },
             inherit: false
 
-          entry :variables, Entry::Variables,
-            description: 'Environment variables available for this job.',
-            inherit: false
-
           entry :environment, Entry::Environment,
             description: 'Environment configuration for this job.',
             inherit: false
@@ -174,7 +170,6 @@ module Gitlab
               when: self.when,
               start_in: self.start_in,
               dependencies: dependencies,
-              variables: variables_defined? ? variables_value : {},
               environment: environment_defined? ? environment_value : nil,
               environment_name: environment_defined? ? environment_value[:name] : nil,
               coverage: coverage_defined? ? coverage_value : nil,
diff --git a/lib/gitlab/ci/config/entry/processable.rb b/lib/gitlab/ci/config/entry/processable.rb
index bfa2905ed77..b4da48957b0 100644
--- a/lib/gitlab/ci/config/entry/processable.rb
+++ b/lib/gitlab/ci/config/entry/processable.rb
@@ -14,7 +14,7 @@ module Gitlab
           include ::Gitlab::Config::Entry::Attributable
           include ::Gitlab::Config::Entry::Inheritable
 
-          PROCESSABLE_ALLOWED_KEYS = %i[extends stage only except rules].freeze
+          PROCESSABLE_ALLOWED_KEYS = %i[extends stage only except rules variables inherit].freeze
 
           included do
             validations do
@@ -54,12 +54,21 @@ module Gitlab
                 allowed_when: %w[on_success on_failure always never manual delayed].freeze
               }
 
+            entry :variables, ::Gitlab::Ci::Config::Entry::Variables,
+              description: 'Environment variables available for this job.',
+              inherit: false
+
+            entry :inherit, ::Gitlab::Ci::Config::Entry::Inherit,
+              description: 'Indicates whether to inherit defaults or not.',
+              inherit: false,
+              default: {}
+
             attributes :extends, :rules
           end
 
           def compose!(deps = nil)
             super do
-              has_workflow_rules = deps&.workflow&.has_rules?
+              has_workflow_rules = deps&.workflow_entry&.has_rules?
 
               # If workflow:rules: or rules: are used
               # they are considered not compatible
@@ -73,6 +82,9 @@ module Gitlab
                 @entries.delete(:except) unless except_defined? # rubocop:disable Gitlab/ModuleWithInstanceVariables
               end
 
+              # inherit root variables
+              @root_variables_value = deps&.variables_value # rubocop:disable Gitlab/ModuleWithInstanceVariables
+
               yield if block_given?
             end
           end
@@ -82,7 +94,10 @@ module Gitlab
           end
 
           def overwrite_entry(deps, key, current_entry)
-            deps.default[key] unless current_entry.specified?
+            return unless inherit_entry&.default_value
+            return unless deps.default_entry
+
+            deps.default_entry[key] unless current_entry.specified?
           end
 
           def value
@@ -90,9 +105,18 @@ module Gitlab
               stage: stage_value,
               extends: extends,
               rules: rules_value,
+              variables: root_and_job_variables_value,
               only: only_value,
               except: except_value }.compact
           end
+
+          def root_and_job_variables_value
+            if inherit_entry&.variables_value
+              @root_variables_value.to_h.merge(variables_value.to_h) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+            else
+              variables_value.to_h
+            end
+          end
         end
       end
     end
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index caa0725c4bd..19d6a470941 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -65,7 +65,8 @@ module Gitlab
             reserved: true
 
           entry :workflow, Entry::Workflow,
-            description: 'List of evaluable rules to determine Pipeline status'
+            description: 'List of evaluable rules to determine Pipeline status',
+            default: {}
 
           dynamic_helpers :jobs
 
@@ -73,7 +74,7 @@ module Gitlab
                    :image_value,
                    :services_value,
                    :after_script_value,
-                   :cache_value, to: :default
+                   :cache_value, to: :default_entry
 
           attr_reader :jobs_config
 
@@ -102,14 +103,6 @@ module Gitlab
             end
           end
 
-          def default
-            self[:default]
-          end
-
-          def workflow
-            self[:workflow] if workflow_defined?
-          end
-
           private
 
           # rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/ci/config/entry/workflow.rb b/lib/gitlab/ci/config/entry/workflow.rb
index 1d9007c9b9b..5bc992a38a0 100644
--- a/lib/gitlab/ci/config/entry/workflow.rb
+++ b/lib/gitlab/ci/config/entry/workflow.rb
@@ -12,7 +12,6 @@ module Gitlab
           validations do
             validates :config, type: Hash
             validates :config, allowed_keys: ALLOWED_KEYS
-            validates :config, presence: true
           end
 
           entry :rules, Entry::Rules,
diff --git a/lib/gitlab/ci/config/external/context.rb b/lib/gitlab/ci/config/external/context.rb
index bb4439cd069..814dcc66362 100644
--- a/lib/gitlab/ci/config/external/context.rb
+++ b/lib/gitlab/ci/config/external/context.rb
@@ -7,13 +7,14 @@ module Gitlab
         class Context
           TimeoutError = Class.new(StandardError)
 
-          attr_reader :project, :sha, :user
+          attr_reader :project, :sha, :user, :parent_pipeline
           attr_reader :expandset, :execution_deadline
 
-          def initialize(project: nil, sha: nil, user: nil)
+          def initialize(project: nil, sha: nil, user: nil, parent_pipeline: nil)
             @project = project
             @sha = sha
             @user = user
+            @parent_pipeline = parent_pipeline
             @expandset = Set.new
             @execution_deadline = 0
 
diff --git a/lib/gitlab/ci/config/external/file/artifact.rb b/lib/gitlab/ci/config/external/file/artifact.rb
new file mode 100644
index 00000000000..fcfdda21c08
--- /dev/null
+++ b/lib/gitlab/ci/config/external/file/artifact.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Gitlab
+  module Ci
+    class Config
+      module External
+        module File
+          class Artifact < Base
+            extend ::Gitlab::Utils::Override
+            include Gitlab::Utils::StrongMemoize
+
+            attr_reader :job_name
+
+            def initialize(params, context)
+              @location = params[:artifact]
+              @job_name = params[:job]
+
+              super
+            end
+
+            def content
+              strong_memoize(:content) do
+                next unless artifact_job
+
+                Gitlab::Ci::ArtifactFileReader.new(artifact_job).read(location)
+              rescue Gitlab::Ci::ArtifactFileReader::Error => error
+                errors.push(error.message)
+              end
+            end
+
+            def matching?
+              super &&
+                Feature.enabled?(:ci_dynamic_child_pipeline, project)
+            end
+
+            private
+
+            def project
+              context&.parent_pipeline&.project
+            end
+
+            def validate_content!
+              return unless ensure_preconditions_satisfied!
+
+              errors.push("File `#{location}` is empty!") unless content.present?
+            end
+
+            def ensure_preconditions_satisfied!
+              unless creating_child_pipeline?
+                errors.push('Including configs from artifacts is only allowed when triggering child pipelines')
+                return false
+              end
+
+              unless job_name.present?
+                errors.push("Job must be provided when including configs from artifacts")
+                return false
+              end
+
+              unless artifact_job.present?
+                errors.push("Job `#{job_name}` not found in parent pipeline or does not have artifacts!")
+                return false
+              end
+
+              true
+            end
+
+            def artifact_job
+              strong_memoize(:artifact_job) do
+                next unless creating_child_pipeline?
+
+                context.parent_pipeline.find_job_with_archive_artifacts(job_name)
+              end
+            end
+
+            def creating_child_pipeline?
+              context.parent_pipeline.present?
+            end
+
+            override :expand_context_attrs
+            def expand_context_attrs
+              {
+                project: context.project,
+                sha: context.sha,
+                user: context.user,
+                parent_pipeline: context.parent_pipeline
+              }
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/config/external/file/local.rb b/lib/gitlab/ci/config/external/file/local.rb
index 8cb1575a3e1..e74f5b33de7 100644
--- a/lib/gitlab/ci/config/external/file/local.rb
+++ b/lib/gitlab/ci/config/external/file/local.rb
@@ -40,7 +40,8 @@ module Gitlab
               {
                 project: context.project,
                 sha: context.sha,
-                user: context.user
+                user: context.user,
+                parent_pipeline: context.parent_pipeline
               }
             end
           end
diff --git a/lib/gitlab/ci/config/external/file/project.rb b/lib/gitlab/ci/config/external/file/project.rb
index c7b49b495fa..be479741784 100644
--- a/lib/gitlab/ci/config/external/file/project.rb
+++ b/lib/gitlab/ci/config/external/file/project.rb
@@ -71,7 +71,8 @@ module Gitlab
               {
                 project: project,
                 sha: sha,
-                user: context.user
+                user: context.user,
+                parent_pipeline: context.parent_pipeline
               }
             end
           end
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index 0143d7784fa..97ae6c4ceba 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -13,7 +13,8 @@ module Gitlab
             External::File::Remote,
             External::File::Template,
             External::File::Local,
-            External::File::Project
+            External::File::Project,
+            External::File::Artifact
           ].freeze
 
           Error = Class.new(StandardError)
diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb
index aabdf7ce47d..9b494f3a7ec 100644
--- a/lib/gitlab/ci/pipeline/chain/base.rb
+++ b/lib/gitlab/ci/pipeline/chain/base.rb
@@ -7,7 +7,7 @@ module Gitlab
         class Base
           attr_reader :pipeline, :command, :config
 
-          delegate :project, :current_user, to: :command
+          delegate :project, :current_user, :parent_pipeline, to: :command
 
           def initialize(pipeline, command)
             @pipeline = pipeline
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index 6a16e6df23d..fa46114615c 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -72,6 +72,10 @@ module Gitlab
               project.repository.ambiguous_ref?(origin_ref)
             end
           end
+
+          def parent_pipeline
+            bridge&.parent_pipeline
+          end
         end
       end
     end
diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb
index 09d1b0edc93..1e47be21b93 100644
--- a/lib/gitlab/ci/pipeline/chain/config/process.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/process.rb
@@ -15,7 +15,8 @@ module Gitlab
                 @command.config_content, {
                   project: project,
                   sha: @pipeline.sha,
-                  user: current_user
+                  user: current_user,
+                  parent_pipeline: parent_pipeline
                 }
               )
             rescue Gitlab::Ci::YamlProcessor::ValidationError => ex
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
index f708e95c2cf..6efb6b4e273 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -5,9 +5,7 @@ variables:
 
 container_scanning:
   stage: test
-  image:
-    name: registry.gitlab.com/gitlab-org/security-products/analyzers/klar:$CS_MAJOR_VERSION
-    entrypoint: []
+  image: registry.gitlab.com/gitlab-org/security-products/analyzers/klar:$CS_MAJOR_VERSION
   variables:
     # By default, use the latest clair vulnerabilities database, however, allow it to be overridden here with a specific image
     # to enable container scanning to run offline, or to provide a consistent list of vulnerabilities for integration testing purposes
@@ -22,10 +20,7 @@ container_scanning:
     - name: $CLAIR_DB_IMAGE
       alias: clair-vulnerabilities-db
   script:
-    # the kubernetes executor currently ignores the Docker image entrypoint value, so the start.sh script must
-    # be explicitly executed here in order for this to work with both the kubernetes and docker executors
-    # see this issue for more details https://gitlab.com/gitlab-org/gitlab-runner/issues/4125
-    - /container-scanner/start.sh
+    - /analyzer run
   artifacts:
     reports:
       container_scanning: gl-container-scanning-report.json
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index 5ff6413898f..4a9fa3091be 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -59,6 +59,8 @@ dependency_scanning:
           BUNDLER_AUDIT_UPDATE_DISABLED \
           BUNDLER_AUDIT_ADVISORY_DB_URL \
           BUNDLER_AUDIT_ADVISORY_DB_REF_NAME \
+          RETIREJS_JS_ADVISORY_DB \
+          RETIREJS_NODE_ADVISORY_DB \
         ) \
         --volume "$PWD:/code" \
         --volume /var/run/docker.sock:/var/run/docker.sock \
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index ae3ff4a51e2..764047dae6d 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -57,7 +57,7 @@ module Gitlab
           when: job[:when] || 'on_success',
           environment: job[:environment_name],
           coverage_regex: job[:coverage],
-          yaml_variables: transform_to_yaml_variables(job_variables(name)),
+          yaml_variables: transform_to_yaml_variables(job[:variables]),
           needs_attributes: job.dig(:needs, :job),
           interruptible: job[:interruptible],
           only: job[:only],
@@ -146,13 +146,6 @@ module Gitlab
         end
       end
 
-      def job_variables(name)
-        job_variables = @jobs.dig(name.to_sym, :variables)
-
-        @variables.to_h
-          .merge(job_variables.to_h)
-      end
-
       def transform_to_yaml_variables(variables)
         variables.to_h.map do |key, value|
           { key: key.to_s, value: value, public: true }
diff --git a/lib/gitlab/config/entry/attributable.rb b/lib/gitlab/config/entry/attributable.rb
index 4deb233d10e..d266d5218de 100644
--- a/lib/gitlab/config/entry/attributable.rb
+++ b/lib/gitlab/config/entry/attributable.rb
@@ -10,7 +10,7 @@ module Gitlab
           def attributes(*attributes)
             attributes.flatten.each do |attribute|
               if method_defined?(attribute)
-                raise ArgumentError, "Method already defined: #{attribute}"
+                raise ArgumentError, "Method '#{attribute}' already defined in '#{name}'"
               end
 
               define_method(attribute) do
diff --git a/lib/gitlab/config/entry/configurable.rb b/lib/gitlab/config/entry/configurable.rb
index 3fd562c2904..571e7a5127e 100644
--- a/lib/gitlab/config/entry/configurable.rb
+++ b/lib/gitlab/config/entry/configurable.rb
@@ -76,7 +76,7 @@ module Gitlab
           # rubocop: disable CodeReuse/ActiveRecord
           def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil, metadata: {})
             entry_name = key.to_sym
-            raise ArgumentError, "Entry #{key} already defined" if @nodes.to_h[entry_name]
+            raise ArgumentError, "Entry '#{key}' already defined in '#{name}'" if @nodes.to_h[entry_name]
 
             factory = ::Gitlab::Config::Entry::Factory.new(entry)
               .with(description: description)
@@ -98,8 +98,8 @@ module Gitlab
 
           def helpers(*nodes, dynamic: false)
             nodes.each do |symbol|
-              if method_defined?("#{symbol}_defined?") || method_defined?("#{symbol}_value")
-                raise ArgumentError, "Method #{symbol}_defined? or #{symbol}_value already defined"
+              if method_defined?("#{symbol}_defined?") || method_defined?("#{symbol}_entry") || method_defined?("#{symbol}_value")
+                raise ArgumentError, "Method '#{symbol}_defined?', '#{symbol}_entry' or '#{symbol}_value' already defined in '#{name}'"
               end
 
               unless @nodes.to_h[symbol]
@@ -110,10 +110,13 @@ module Gitlab
                 entries[symbol]&.specified?
               end
 
-              define_method("#{symbol}_value") do
-                return unless entries[symbol] && entries[symbol].valid?
+              define_method("#{symbol}_entry") do
+                entries[symbol]
+              end
 
-                entries[symbol].value
+              define_method("#{symbol}_value") do
+                entry = entries[symbol]
+                entry.value if entry&.valid?
               end
             end
           end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e93052df7e0..18cfcea9335 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -21221,6 +21221,18 @@ msgstr ""
 msgid "UpdateProject|Project could not be updated!"
 msgstr ""
 
+msgid "UpdateRepositoryStorage|Error moving repository storage for %{project_full_path} - %{message}"
+msgstr ""
+
+msgid "UpdateRepositoryStorage|Failed to fetch %{type} repository as mirror"
+msgstr ""
+
+msgid "UpdateRepositoryStorage|Failed to verify %{type} repository checksum from %{old} to %{new}"
+msgstr ""
+
+msgid "UpdateRepositoryStorage|Timeout waiting for %{type} repository pushes"
+msgstr ""
+
 msgid "Updated"
 msgstr ""
 
diff --git a/scripts/trigger-build b/scripts/trigger-build
index 6359446ecd1..c7b45480bf3 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -176,11 +176,9 @@ module Trigger
       edition = Trigger.ee? ? 'EE' : 'CE'
 
       {
-        # Back-compatibility until https://gitlab.com/gitlab-org/build/CNG/merge_requests/189 is merged
-        "GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
-        "GITLAB_VERSION" => ENV['CI_COMMIT_REF_NAME'],
+        "GITLAB_VERSION" => ENV['CI_COMMIT_SHA'],
         "GITLAB_TAG" => ENV['CI_COMMIT_TAG'],
-        "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
+        "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_SHA'],
         "FORCE_RAILS_IMAGE_BUILDS" => 'true',
         "#{edition}_PIPELINE" => 'true'
       }
diff --git a/spec/fixtures/ci_build_artifacts.zip b/spec/fixtures/ci_build_artifacts.zip
index dae976d918e998171438058d8801d2ddd85ab4e1..91ec9f7dcd0fbcbd04e39dd54e34870308d03669 100644
GIT binary patch
delta 2034
zcmeynkL|>9wh6|fr-T~AT$uIaN-MY-7+JnDGBB`+FiiANtQTQmVBlc*UTPiibA$bD
zEg;Voh<O=g7}8VoQi~ExQd9IQb8|vNI2o7)f9S<w7{b7yt)=JTyX{d>fQFymDIJZC
zK^jb)1yfU=EmP`l`Vn(-=~306J5L-E5Y%G`@MdP=0vgZ2P*7tX0HQfT1PF*t50qw<
zGzZ%QvLhYcu?%3{5a-SSI(O;5ABG=+8d)S5805j4>-AiM<AY1`i&DX+#DCX|17Q>|
zGcdfJVViZ>L7?@%h89caHjjrz`V*AQBU*}?;uSWgdN@P~+;+7JwVU4mO!W7kb{!9`
z)*rPSYJ{vePntDXmT^N-^j-!&hsKmQ1(TZ=ySqh8754J$d~8i$vvKd<`mlbz@0{-=
z79M=2Ro}-o|6^XK>zvF-SIf7?UHQCh<=ggdZp9XXMeL_DK3crX=1}~naQEMXmk!%c
z9{bsIPQOUzEttKd=iJMtvMtM*@2VAFHZqexrY>zQQoLpP;q`Wr9lIYYo-F<PU$pt}
zet}2w$v;cXbPKaMs!FzAJO1&Q_>8}Xg%1=aER}B)p7Yp7_JvtwJEP*U2NSn=i1zfj
zW5)kxLB=E|7En}7E@yTyA}ZLGLyDzeK!e=`6zqCL2YcQjP_SF*=>(>no>I-4x525P
zNv1(#1{3F#j`>aJLeEWIyz$zeKMKrET^g11xBSYfOv^FfZOwhaZFdz13xnvLisPPI
zeR8b1c169m%T{bWAocn3)~b^Fo&A<ot>!uXpVEKsI~o4F$~bzT+1XF0x|}!^TXwYU
zTy?P4-kIYg_seJP9_qVFrRR&6{8U!IFww8%wb?Pz63_6G1<G?@Ts!M|`bLe!{hT)e
zIzTz|zncX9x3P!6xb~m(+_(Qu9ris_A3y)d5zp(`pSQNRa*x5Y$HpJexGng=%zs$D
z=D4fX#kJA185Q3hWSo4CIUaj>H#@OxcVb}-5*ODHZVbbc!l$>$G8$voyZsC>_0=#j
zvrHG5%P2AZxHO}fc+0UVSWVe4!zjh5F#VPcqrCN_&IS75>=1Vxt71f!eDAH}!E}~S
zgdG^WAgl*Az!YRaQEGZ=PGV7fT4qjaydl`QPe9|alm#Fa3Wi+MzeqE>h^Aq)a=Nb!
zV~E@nJlc)8rdQ2kbgl=LS!kIzz?+dtju}^(1uO}G09dv$ENM&yl4vCuE2IR&Y9wl@
z1TuzU`pa32f{fRu|DMGtXOG3J$Zo+XkQg9t0hzO;QH^o>pIM9&@<`<q&@C8c({!!b
zj5<sOOkl;+3uZG4+k>(#vF-(0r46@MfZ?s93M<&8$#a>-oq+Zc;RTe;4L1wqg-*Cx
zq6EFcItS>D`)n}FMW-jsVH6X@@XGXdpgLxEImYRKXEBOTKRbs}OAwSVQ2aZce?Cyr
z9-yLyvW&taKyP50!!egpM|0C`Muw8q;*#R{)V$>Ul+3(zV6q1)2jvk|6D{X4$_XmK
zeFqAu-?JH~-;rUIm4f6Jpf*r$LDe=Fq)iX14U)z}=RmY+V9O&wLqK^1)sR@AQ<;wd
zwF$~Fs(^e0$|9&rj)0WN&jl+{n%*bFD4+vW2g)xf>Oj>YD;p?%@dKeUkkvmI<SGC^
CSXgTS

delta 1037
zcmZ{iOK1~e5XZmSM}ko#vCTs_*{X=NWU<tMAR?Gk15sLRvPa`<ErHTXn}SFcf}W)z
zg#nvr6ZIqkK_n_Z@T3RLsV5P%;=xN1K@b$dnQcHfyWM3z_?Z9vhHw7!=C$w73*TZY
z{8TBM9P^j93Q)evLeNpk><I(}f3Nbj(aM)(<UL6^95PXzkuu?htD`1|BlYmDbXE#|
zrfM70J(890JFCyta6F&eBIKA_HaQ%HLR3Hc3f|Cb6aMyuA5KQKO-jeJvVyG!#IUmr
z5v}xi$>iv1V5>efN%g4)gJJW@l9_4us4!87oW4paPWJ#V6sN4+2Lt$f8Fqn=7i%zZ
zxHW9Q=>(}aX}K}bZ&@QFz1!aJr;%_Nc6q8bJZnwPPo0@9FuX8g!`2AKK(F>A0eq%I
z%6m|4WdI&5V5JV}V=i-r7|(*F%l{1i-=c_!b+19Op0>!2v>J`%bE=SC{^QSEV91X)
zG<ftj^o3lDfSnc4LrDj<C^PjyKQUeY<M9<@c5H=F9rX9H%9CoyhcqCLfr~oq@P^%M
zfH8Dm15vW(N^=Ek`rOR<;`AhWMO2qhxZ0%NKQzF#8th}D%X~a?ha_WhTe25V)gj0_
zUKEE*_^SbWP<K50WgKrbL67mOP>su3JWbi4(X9cp;chvauxs|a_k#$$AI`30^j_(D
b0<Sb6I^>!fJFoiK-fi@+D?(hp$!-1u`dtjn

diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index cee299522ce..b86e92d5969 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -319,18 +319,21 @@ describe Backup::Manager do
 
     context 'when there is a non-tarred backup in the directory' do
       before do
-        allow(Dir).to receieve(:glob).and_return(
+        allow(Dir).to receive(:glob).and_return(
           [
             'backup_information.yml'
           ]
         )
+        allow(File).to receive(:exist?).and_return(true)
+      end
+
+      it 'selects the non-tarred backup to restore from' do
+        expect(Kernel).not_to receive(:system)
 
-        it 'selects the non-tarred backup to restore from' do
-          expect { subject.unpack }.to output.to_stdout
-          expect(progress).to have_received(:puts)
-            .with(a_string_matching('Non tarred backup found '))
-          expect(Kernel).not_to receive(:system)
-        end
+        subject.unpack
+
+        expect(progress).to have_received(:puts)
+          .with(a_string_matching('Non tarred backup found '))
       end
     end
   end
diff --git a/spec/lib/gitlab/ci/artifact_file_reader_spec.rb b/spec/lib/gitlab/ci/artifact_file_reader_spec.rb
new file mode 100644
index 00000000000..04017b9ae3e
--- /dev/null
+++ b/spec/lib/gitlab/ci/artifact_file_reader_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::ArtifactFileReader do
+  let(:job) { create(:ci_build) }
+  let(:path) { 'generated.yml' } # included in the ci_build_artifacts.zip
+
+  describe '#read' do
+    subject { described_class.new(job).read(path) }
+
+    context 'when job has artifacts and metadata' do
+      let!(:artifacts) { create(:ci_job_artifact, :archive, job: job) }
+      let!(:metadata) { create(:ci_job_artifact, :metadata, job: job) }
+
+      it 'returns the content at the path' do
+        is_expected.to be_present
+        expect(YAML.safe_load(subject).keys).to contain_exactly('rspec', 'time', 'custom')
+      end
+
+      context 'when path does not exist' do
+        let(:path) { 'file/does/not/exist.txt' }
+        let(:expected_error) do
+          "Path `#{path}` does not exist inside the `#{job.name}` artifacts archive!"
+        end
+
+        it 'raises an error' do
+          expect { subject }.to raise_error(described_class::Error, expected_error)
+        end
+      end
+
+      context 'when path points to a directory' do
+        let(:path) { 'other_artifacts_0.1.2' }
+        let(:expected_error) do
+          "Path `#{path}` was expected to be a file but it was a directory!"
+        end
+
+        it 'raises an error' do
+          expect { subject }.to raise_error(described_class::Error, expected_error)
+        end
+      end
+
+      context 'when path is nested' do
+        # path exists in ci_build_artifacts.zip
+        let(:path) { 'other_artifacts_0.1.2/doc_sample.txt' }
+
+        it 'returns the content at the nested path' do
+          is_expected.to be_present
+        end
+      end
+
+      context 'when artifact archive size is greater than the limit' do
+        let(:expected_error) do
+          "Artifacts archive for job `#{job.name}` is too large: max 1 KB"
+        end
+
+        before do
+          stub_const("#{described_class}::MAX_ARCHIVE_SIZE", 1.kilobyte)
+        end
+
+        it 'raises an error' do
+          expect { subject }.to raise_error(described_class::Error, expected_error)
+        end
+      end
+
+      context 'when metadata entry shows size greater than the limit' do
+        let(:expected_error) do
+          "Artifacts archive for job `#{job.name}` is too large: max 5 MB"
+        end
+
+        before do
+          expect_next_instance_of(Gitlab::Ci::Build::Artifacts::Metadata::Entry) do |entry|
+            expect(entry).to receive(:total_size).and_return(10.megabytes)
+          end
+        end
+
+        it 'raises an error' do
+          expect { subject }.to raise_error(described_class::Error, expected_error)
+        end
+      end
+    end
+
+    context 'when job does not have metadata artifacts' do
+      let!(:artifacts) { create(:ci_job_artifact, :archive, job: job) }
+      let(:expected_error) do
+        "Job `#{job.name}` has missing artifacts metadata and cannot be extracted!"
+      end
+
+      it 'raises an error' do
+        expect { subject }.to raise_error(described_class::Error, expected_error)
+      end
+    end
+
+    context 'when job does not have artifacts' do
+      it 'raises ArgumentError' do
+        expect { subject }.to raise_error(ArgumentError, 'Job does not have artifacts')
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
index ad388886681..d08ce30618d 100644
--- a/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/bridge_spec.rb
@@ -106,6 +106,7 @@ describe Gitlab::Ci::Config::Entry::Bridge do
                                       ignore: false,
                                       stage: 'test',
                                       only: { refs: %w[branches tags] },
+                                      variables: {},
                                       scheduling_type: :stage)
         end
       end
@@ -128,6 +129,7 @@ describe Gitlab::Ci::Config::Entry::Bridge do
                                       ignore: false,
                                       stage: 'test',
                                       only: { refs: %w[branches tags] },
+                                      variables: {},
                                       scheduling_type: :stage)
         end
       end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 313b504ab59..7df0eccb3ed 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -6,6 +6,7 @@ describe Gitlab::Ci::Config::Entry::Job do
   let(:entry) { described_class.new(config, name: :rspec) }
 
   it_behaves_like 'with inheritable CI config' do
+    let(:config) { { script: 'echo' } }
     let(:inheritable_key) { 'default' }
     let(:inheritable_class) { Gitlab::Ci::Config::Entry::Default }
 
@@ -15,6 +16,10 @@ describe Gitlab::Ci::Config::Entry::Job do
     let(:ignored_inheritable_columns) do
       %i[]
     end
+
+    before do
+      allow(entry).to receive_message_chain(:inherit_entry, :default_value).and_return(true)
+    end
   end
 
   describe '.nodes' do
@@ -24,7 +29,8 @@ describe Gitlab::Ci::Config::Entry::Job do
       let(:result) do
         %i[before_script script stage type after_script cache
            image services only except rules needs variables artifacts
-           environment coverage retry interruptible timeout release tags]
+           environment coverage retry interruptible timeout release tags
+           inherit]
       end
 
       it { is_expected.to match_array result }
@@ -500,7 +506,13 @@ describe Gitlab::Ci::Config::Entry::Job do
     let(:unspecified) { double('unspecified', 'specified?' => false) }
     let(:default) { double('default', '[]' => unspecified) }
     let(:workflow) { double('workflow', 'has_rules?' => false) }
-    let(:deps) { double('deps', 'default' => default, '[]' => unspecified, 'workflow' => workflow) }
+
+    let(:deps) do
+      double('deps',
+        'default_entry' => default,
+        'workflow_entry' => workflow,
+        'variables_value' => nil)
+    end
 
     context 'when job config overrides default config' do
       before do
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index c8c188d71bf..203342ab620 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -99,6 +99,7 @@ describe Gitlab::Ci::Config::Entry::Jobs do
               only: { refs: %w[branches tags] },
               stage: 'test',
               trigger: { project: 'my/project' },
+              variables: {},
               scheduling_type: :stage
             },
             regular_job: {
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 410aef1cd53..5c2c6520f25 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -7,6 +7,10 @@ describe Gitlab::Ci::Config::Entry::Processable do
     Class.new(::Gitlab::Config::Entry::Node) do
       include Gitlab::Ci::Config::Entry::Processable
 
+      entry :tags, ::Gitlab::Config::Entry::ArrayOfStrings,
+        description: 'Set the default tags.',
+        inherit: true
+
       def self.name
         'job'
       end
@@ -189,14 +193,17 @@ describe Gitlab::Ci::Config::Entry::Processable do
   end
 
   describe '#compose!' do
-    let(:specified) do
-      double('specified', 'specified?' => true, value: 'specified')
-    end
-
     let(:unspecified) { double('unspecified', 'specified?' => false) }
     let(:default) { double('default', '[]' => unspecified) }
     let(:workflow) { double('workflow', 'has_rules?' => false) }
-    let(:deps) { double('deps', 'default' => default, '[]' => unspecified, 'workflow' => workflow) }
+    let(:variables) { }
+
+    let(:deps) do
+      double('deps',
+        default_entry: default,
+        workflow_entry: workflow,
+        variables_value: variables)
+    end
 
     context 'with workflow rules' do
       using RSpec::Parameterized::TableSyntax
@@ -240,6 +247,84 @@ describe Gitlab::Ci::Config::Entry::Processable do
         end
       end
     end
+
+    context 'with inheritance' do
+      context 'of variables' do
+        let(:config) do
+          { variables: { A: 'job', B: 'job' } }
+        end
+
+        before do
+          entry.compose!(deps)
+        end
+
+        context 'with only job variables' do
+          it 'does return defined variables' do
+            expect(entry.value).to include(
+              variables: { 'A' => 'job', 'B' => 'job' }
+            )
+          end
+        end
+
+        context 'when root yaml variables are used' do
+          let(:variables) do
+            Gitlab::Ci::Config::Entry::Variables.new(
+              A: 'root', C: 'root'
+            ).value
+          end
+
+          it 'does return all variables and overwrite them' do
+            expect(entry.value).to include(
+              variables: { 'A' => 'job', 'B' => 'job', 'C' => 'root' }
+            )
+          end
+
+          context 'when inherit of defaults is disabled' do
+            let(:config) do
+              {
+                variables: { A: 'job', B: 'job' },
+                inherit: { variables: false }
+              }
+            end
+
+            it 'does return only job variables' do
+              expect(entry.value).to include(
+                variables: { 'A' => 'job', 'B' => 'job' }
+              )
+            end
+          end
+        end
+      end
+
+      context 'of default:tags' do
+        using RSpec::Parameterized::TableSyntax
+
+        where(:default_tags, :tags, :inherit_default, :result) do
+          nil | %w[a b] | nil | %w[a b]
+          nil | %w[a b] | true | %w[a b]
+          nil | %w[a b] | false | %w[a b]
+          %w[b c] | %w[a b] | nil | %w[a b]
+          %w[b c] | %w[a b] | true | %w[a b]
+          %w[b c] | %w[a b] | false | %w[a b]
+          %w[b c] | nil | nil | %w[b c]
+          %w[b c] | nil | true | %w[b c]
+          %w[b c] | nil | false | nil
+        end
+
+        with_them do
+          let(:config) { { tags: tags, inherit: { default: inherit_default } } }
+          let(:default_specified_tags) { double('tags', 'specified?' => true, 'valid?' => true, 'value' => default_tags) }
+
+          before do
+            allow(default).to receive('[]').with(:tags).and_return(default_specified_tags)
+
+            entry.compose!(deps)
+          end
+
+          it { expect(entry.tags_value).to eq(result) }
+        end
+      end
+    end
   end
 
   context 'when composed' do
@@ -254,10 +339,12 @@ describe Gitlab::Ci::Config::Entry::Processable do
         end
 
         it 'returns correct value' do
-          expect(entry.value)
-            .to eq(name: :rspec,
-                   stage: 'test',
-                   only: { refs: %w[branches tags] })
+          expect(entry.value).to eq(
+            name: :rspec,
+            stage: 'test',
+            only: { refs: %w[branches tags] },
+            variables: {}
+          )
         end
       end
     end
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index cf0a3cfa963..c3871b6b3cf 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -32,7 +32,7 @@ describe Gitlab::Ci::Config::Entry::Root do
           image: 'ruby:2.2',
           default: {},
           services: ['postgres:9.1', 'mysql:5.5'],
-          variables: { VAR: 'value' },
+          variables: { VAR: 'root' },
           after_script: ['make clean'],
           stages: %w(build pages release),
           cache: { key: 'k', untracked: true, paths: ['public/'] },
@@ -42,6 +42,7 @@ describe Gitlab::Ci::Config::Entry::Root do
             stage: 'release',
             before_script: [],
             after_script: [],
+            variables: { 'VAR' => 'job' },
             script: ["make changelog | tee release_changelog.txt"],
             release: {
               tag_name: 'v0.06',
@@ -127,7 +128,7 @@ describe Gitlab::Ci::Config::Entry::Root do
                        services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
                        stage: 'test',
                        cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push' },
-                       variables: {},
+                       variables: { 'VAR' => 'root' },
                        ignore: false,
                        after_script: ['make clean'],
                        only: { refs: %w[branches tags] },
@@ -141,7 +142,7 @@ describe Gitlab::Ci::Config::Entry::Root do
                          services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
                          stage: 'test',
                          cache: { key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push' },
-                         variables: {},
+                         variables: { 'VAR' => 'root' },
                          ignore: false,
                          after_script: ['make clean'],
                          only: { refs: %w[branches tags] },
@@ -157,7 +158,7 @@ describe Gitlab::Ci::Config::Entry::Root do
                          services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
                          cache: { key: "k", untracked: true, paths: ["public/"], policy: "pull-push" },
                          only: { refs: %w(branches tags) },
-                         variables: {},
+                         variables: { 'VAR' => 'job' },
                          after_script: [],
                          ignore: false,
                          scheduling_type: :stage }
@@ -175,11 +176,11 @@ describe Gitlab::Ci::Config::Entry::Root do
             image: 'ruby:2.1',
             services: ['postgres:9.1', 'mysql:5.5']
           },
-          variables: { VAR: 'value' },
+          variables: { VAR: 'root' },
           stages: %w(build pages),
           cache: { key: 'k', untracked: true, paths: ['public/'] },
           rspec: { script: %w[rspec ls] },
-          spinach: { before_script: [], variables: { VAR: 'AA' }, script: 'spinach' } }
+          spinach: { before_script: [], variables: { VAR: 'job' }, script: 'spinach' } }
       end
 
       context 'when composed' do
@@ -203,7 +204,7 @@ describe Gitlab::Ci::Config::Entry::Root do
                        services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
                        stage: 'test',
                        cache: { key: 'k', untracked: true, paths: ['public/'], policy: "pull-push" },
-                       variables: {},
+                       variables: { 'VAR' => 'root' },
                        ignore: false,
                        after_script: ['make clean'],
                        only: { refs: %w[branches tags] },
@@ -215,7 +216,7 @@ describe Gitlab::Ci::Config::Entry::Root do
                          services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
                          stage: 'test',
                          cache: { key: 'k', untracked: true, paths: ['public/'], policy: "pull-push" },
-                         variables: { 'VAR' => 'AA' },
+                         variables: { 'VAR' => 'job' },
                          ignore: false,
                          after_script: ['make clean'],
                          only: { refs: %w[branches tags] },
diff --git a/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
new file mode 100644
index 00000000000..a8eb13c47bc
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/external/file/artifact_spec.rb
@@ -0,0 +1,167 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::External::File::Artifact do
+  let(:parent_pipeline) { create(:ci_pipeline) }
+  let(:context) do
+    Gitlab::Ci::Config::External::Context.new(parent_pipeline: parent_pipeline)
+  end
+
+  let(:external_file) { described_class.new(params, context) }
+
+  describe '#matching?' do
+    context 'when params contain artifact location' do
+      let(:params) { { artifact: 'generated.yml' } }
+
+      it 'returns true' do
+        expect(external_file).to be_matching
+      end
+    end
+
+    context 'when params does not contain artifact location' do
+      let(:params) { {} }
+
+      it 'returns false' do
+        expect(external_file).not_to be_matching
+      end
+    end
+  end
+
+  describe '#valid?' do
+    shared_examples 'is invalid' do
+      it 'is not valid' do
+        expect(external_file).not_to be_valid
+      end
+
+      it 'sets the expected error' do
+        expect(external_file.errors)
+          .to contain_exactly(expected_error)
+      end
+    end
+
+    describe 'when used in non child pipeline context' do
+      let(:parent_pipeline) { nil }
+      let(:params) { { artifact: 'generated.yml' } }
+
+      let(:expected_error) do
+        'Including configs from artifacts is only allowed when triggering child pipelines'
+      end
+
+      it_behaves_like 'is invalid'
+    end
+
+    context 'when used in child pipeline context' do
+      let(:parent_pipeline) { create(:ci_pipeline) }
+
+      context 'when job is not provided' do
+        let(:params) { { artifact: 'generated.yml' } }
+
+        let(:expected_error) do
+          'Job must be provided when including configs from artifacts'
+        end
+
+        it_behaves_like 'is invalid'
+      end
+
+      context 'when job is provided' do
+        let(:params) { { artifact: 'generated.yml', job: 'generator' } }
+
+        context 'when job does not exist in the parent pipeline' do
+          let(:expected_error) do
+            'Job `generator` not found in parent pipeline or does not have artifacts!'
+          end
+
+          it_behaves_like 'is invalid'
+        end
+
+        context 'when job exists in the parent pipeline' do
+          let!(:generator_job) { create(:ci_build, name: 'generator', pipeline: parent_pipeline) }
+
+          context 'when job does not have artifacts' do
+            let(:expected_error) do
+              'Job `generator` not found in parent pipeline or does not have artifacts!'
+            end
+
+            it_behaves_like 'is invalid'
+          end
+
+          context 'when job has archive artifacts' do
+            let!(:artifacts) do
+              create(:ci_job_artifact, :archive,
+                job: generator_job,
+                file: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip'), 'application/zip'))
+            end
+
+            let(:expected_error) do
+              'Job `generator` has missing artifacts metadata and cannot be extracted!'
+            end
+
+            it_behaves_like 'is invalid'
+
+            context 'when job has artifacts exceeding the max allowed size' do
+              let(:expected_error) do
+                "Artifacts archive for job `generator` is too large: max 1 KB"
+              end
+
+              before do
+                stub_const("#{Gitlab::Ci::ArtifactFileReader}::MAX_ARCHIVE_SIZE", 1.kilobyte)
+              end
+
+              it_behaves_like 'is invalid'
+            end
+
+            context 'when job has artifacts metadata' do
+              let!(:metadata) do
+                create(:ci_job_artifact, :metadata, job: generator_job)
+              end
+
+              let(:expected_error) do
+                'Path `generated.yml` does not exist inside the `generator` artifacts archive!'
+              end
+
+              it_behaves_like 'is invalid'
+
+              context 'when file is found in metadata' do
+                let!(:artifacts) { create(:ci_job_artifact, :archive, job: generator_job) }
+                let!(:metadata) { create(:ci_job_artifact, :metadata, job: generator_job) }
+
+                context 'when file is empty' do
+                  before do
+                    allow_next_instance_of(Gitlab::Ci::ArtifactFileReader) do |reader|
+                      allow(reader).to receive(:read).and_return('')
+                    end
+                  end
+
+                  let(:expected_error) do
+                    'File `generated.yml` is empty!'
+                  end
+
+                  it_behaves_like 'is invalid'
+                end
+
+                context 'when file is not empty' do
+                  it 'is valid' do
+                    expect(external_file).to be_valid
+                    expect(external_file.content).to be_present
+                  end
+
+                  it 'propagates parent_pipeline to nested includes' do
+                    expected_attrs = {
+                      parent_pipeline: parent_pipeline,
+                      project: anything,
+                      sha: anything,
+                      user: anything
+                    }
+                    expect(context).to receive(:mutate).with(expected_attrs).and_call_original
+                    external_file.content
+                  end
+                end
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index 53205a18762..c9851239859 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -6,10 +6,19 @@ describe Gitlab::Ci::Config::External::File::Local do
   let_it_be(:project) { create(:project, :repository) }
   let_it_be(:user) { create(:user) }
   let(:sha) { '12345' }
-  let(:context_params) { { project: project, sha: sha, user: user } }
   let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
   let(:params) { { local: location } }
   let(:local_file) { described_class.new(params, context) }
+  let(:parent_pipeline) { double(:parent_pipeline) }
+
+  let(:context_params) do
+    {
+      project: project,
+      sha: sha,
+      user: user,
+      parent_pipeline: parent_pipeline
+    }
+  end
 
   before do
     allow_any_instance_of(Gitlab::Ci::Config::External::Context)
@@ -117,7 +126,11 @@ describe Gitlab::Ci::Config::External::File::Local do
     subject { local_file.send(:expand_context_attrs) }
 
     it 'inherits project, user and sha' do
-      is_expected.to include(user: user, project: project, sha: sha)
+      is_expected.to include(
+        user: user,
+        project: project,
+        sha: sha,
+        parent_pipeline: parent_pipeline)
     end
   end
 
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index 77a71f9972b..b2924ae9d91 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -7,10 +7,19 @@ describe Gitlab::Ci::Config::External::File::Project do
   let_it_be(:project) { create(:project, :repository) }
   let_it_be(:user) { create(:user) }
   let(:context_user) { user }
-  let(:context_params) { { project: context_project, sha: '12345', user: context_user } }
+  let(:parent_pipeline) { double(:parent_pipeline) }
   let(:context) { Gitlab::Ci::Config::External::Context.new(**context_params) }
   let(:project_file) { described_class.new(params, context) }
 
+  let(:context_params) do
+    {
+      project: context_project,
+      sha: '12345',
+      user: context_user,
+      parent_pipeline: parent_pipeline
+    }
+  end
+
   before do
     project.add_developer(user)
 
@@ -152,7 +161,11 @@ describe Gitlab::Ci::Config::External::File::Project do
     subject { project_file.send(:expand_context_attrs) }
 
     it 'inherits user, and target project and sha' do
-      is_expected.to include(user: user, project: project, sha: project.commit('master').id)
+      is_expected.to include(
+        user: user,
+        project: project,
+        sha: project.commit('master').id,
+        parent_pipeline: parent_pipeline)
     end
   end
 
diff --git a/spec/lib/gitlab/ci/config_spec.rb b/spec/lib/gitlab/ci/config_spec.rb
index 525335cfea9..82ca8a29c5a 100644
--- a/spec/lib/gitlab/ci/config_spec.rb
+++ b/spec/lib/gitlab/ci/config_spec.rb
@@ -376,23 +376,6 @@ describe Gitlab::Ci::Config do
       end
     end
 
-    context 'when context expansion timeout is disabled' do
-      before do
-        allow_next_instance_of(Gitlab::Ci::Config::External::Context) do |instance|
-          allow(instance).to receive(:check_execution_time!).and_call_original
-        end
-
-        allow(Feature)
-          .to receive(:enabled?)
-          .with(:ci_limit_yaml_expansion, project, default_enabled: true)
-          .and_return(false)
-      end
-
-      it 'does not raises errors' do
-        expect { config }.not_to raise_error
-      end
-    end
-
     describe 'external file version' do
       context 'when external local file SHA is defined' do
         it 'is using a defined value' do
@@ -541,5 +524,76 @@ describe Gitlab::Ci::Config do
         end
       end
     end
+
+    context 'when including file from artifact' do
+      let(:config) do
+        described_class.new(
+          gitlab_ci_yml,
+          project: nil,
+          sha: nil,
+          user: nil,
+          parent_pipeline: parent_pipeline)
+      end
+
+      let(:gitlab_ci_yml) do
+        <<~HEREDOC
+        include:
+          - artifact: generated.yml
+            job: rspec
+        HEREDOC
+      end
+
+      let(:parent_pipeline) { nil }
+
+      context 'when used in the context of a child pipeline' do
+        # This job has ci_build_artifacts.zip artifact archive which
+        # contains generated.yml
+        let!(:job) { create(:ci_build, :artifacts, name: 'rspec', pipeline: parent_pipeline) }
+        let(:parent_pipeline) { create(:ci_pipeline) }
+
+        it 'returns valid config' do
+          expect(config).to be_valid
+        end
+
+        context 'when job key is missing' do
+          let(:gitlab_ci_yml) do
+            <<~HEREDOC
+            include:
+              - artifact: generated.yml
+            HEREDOC
+          end
+
+          it 'raises an error' do
+            expect { config }.to raise_error(
+              described_class::ConfigError,
+              'Job must be provided when including configs from artifacts'
+            )
+          end
+        end
+
+        context 'when artifact key is missing' do
+          let(:gitlab_ci_yml) do
+            <<~HEREDOC
+            include:
+              - job: rspec
+            HEREDOC
+          end
+
+          it 'raises an error' do
+            expect { config }.to raise_error(
+              described_class::ConfigError,
+              /needs to match exactly one accessor!/
+            )
+          end
+        end
+      end
+
+      it 'disallows the use in parent pipelines' do
+        expect { config }.to raise_error(
+          described_class::ConfigError,
+          'Including configs from artifacts is only allowed when triggering child pipelines'
+        )
+      end
+    end
   end
 end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index e303557bd00..5c85a136972 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -509,28 +509,44 @@ module Gitlab
 
         describe "before_script" do
           context "in global context" do
-            let(:config) do
-              {
-                before_script: ["global script"],
-                test: { script: ["script"] }
-              }
+            using RSpec::Parameterized::TableSyntax
+
+            where(:inherit, :result) do
+              nil | ["global script"]
+              { default: false } | nil
+              { default: true } | ["global script"]
             end
 
-            it "return commands with scripts concatenated" do
-              expect(subject[:options][:before_script]).to eq(["global script"])
+            with_them do
+              let(:config) do
+                {
+                  before_script: ["global script"],
+                  test: { script: ["script"], inherit: inherit }
+                }
+              end
+
+              it { expect(subject[:options][:before_script]).to eq(result) }
             end
           end
 
           context "in default context" do
-            let(:config) do
-              {
-                default: { before_script: ["global script"] },
-                test: { script: ["script"] }
-              }
+            using RSpec::Parameterized::TableSyntax
+
+            where(:inherit, :result) do
+              nil | ["global script"]
+              { default: false } | nil
+              { default: true } | ["global script"]
             end
 
-            it "return commands with scripts concatenated" do
-              expect(subject[:options][:before_script]).to eq(["global script"])
+            with_them do
+              let(:config) do
+                {
+                  default: { before_script: ["global script"] },
+                  test: { script: ["script"], inherit: inherit }
+                }
+              end
+
+              it { expect(subject[:options][:before_script]).to eq(result) }
             end
           end
 
@@ -793,7 +809,7 @@ module Gitlab
 
         context 'when job and global variables are defined' do
           let(:global_variables) do
-            { 'VAR1' => 'global1', 'VAR3' => 'global3' }
+            { 'VAR1' => 'global1', 'VAR3' => 'global3', 'VAR4' => 'global4' }
           end
           let(:job_variables) do
             { 'VAR1' => 'value1', 'VAR2' => 'value2' }
@@ -802,16 +818,32 @@ module Gitlab
             {
               before_script: ['pwd'],
               variables: global_variables,
-              rspec: { script: 'rspec', variables: job_variables }
+              rspec: { script: 'rspec', variables: job_variables, inherit: inherit }
             }
           end
 
-          it 'returns all unique variables' do
-            expect(subject).to contain_exactly(
-              { key: 'VAR3', value: 'global3', public: true },
-              { key: 'VAR1', value: 'value1', public: true },
-              { key: 'VAR2', value: 'value2', public: true }
-            )
+          context 'when no inheritance is specified' do
+            let(:inherit) { }
+
+            it 'returns all unique variables' do
+              expect(subject).to contain_exactly(
+                { key: 'VAR4', value: 'global4', public: true },
+                { key: 'VAR3', value: 'global3', public: true },
+                { key: 'VAR1', value: 'value1', public: true },
+                { key: 'VAR2', value: 'value2', public: true }
+              )
+            end
+          end
+
+          context 'when inheritance is disabled' do
+            let(:inherit) { { variables: false } }
+
+            it 'does not inherit variables' do
+              expect(subject).to contain_exactly(
+                { key: 'VAR1', value: 'value1', public: true },
+                { key: 'VAR2', value: 'value2', public: true }
+              )
+            end
           end
         end
 
diff --git a/spec/lib/gitlab/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb
index bc29a194181..64a4670f483 100644
--- a/spec/lib/gitlab/config/entry/attributable_spec.rb
+++ b/spec/lib/gitlab/config/entry/attributable_spec.rb
@@ -59,7 +59,7 @@ describe Gitlab::Config::Entry::Attributable do
         end
       end
 
-      expectation.to raise_error(ArgumentError, 'Method already defined: length')
+      expectation.to raise_error(ArgumentError, /Method 'length' already defined in/)
     end
   end
 end
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 8f56d735f36..0a7a44b225c 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -25,7 +25,7 @@ describe Ci::JobArtifact do
     end
 
     it_behaves_like 'UpdateProjectStatistics' do
-      subject { build(:ci_job_artifact, :archive, size: 106365) }
+      subject { build(:ci_job_artifact, :archive, size: 107464) }
     end
   end
 
@@ -35,7 +35,7 @@ describe Ci::JobArtifact do
     end
 
     it_behaves_like 'UpdateProjectStatistics' do
-      subject { build(:ci_job_artifact, :archive, size: 106365) }
+      subject { build(:ci_job_artifact, :archive, size: 107464) }
     end
   end
 
@@ -173,7 +173,7 @@ describe Ci::JobArtifact do
     let(:artifact) { create(:ci_job_artifact, :archive, project: project) }
 
     it 'sets the size from the file size' do
-      expect(artifact.size).to eq(106365)
+      expect(artifact.size).to eq(107464)
     end
   end
 
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 76051ecb177..f775906a545 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -2553,6 +2553,19 @@ describe Ci::Pipeline, :mailer do
     end
   end
 
+  describe '#find_job_with_archive_artifacts' do
+    let!(:old_job) { create(:ci_build, name: 'rspec', retried: true, pipeline: pipeline) }
+    let!(:job_without_artifacts) { create(:ci_build, name: 'rspec', pipeline: pipeline) }
+    let!(:expected_job) { create(:ci_build, :artifacts, name: 'rspec', pipeline: pipeline ) }
+    let!(:different_job) { create(:ci_build, name: 'deploy', pipeline: pipeline) }
+
+    subject { pipeline.find_job_with_archive_artifacts('rspec') }
+
+    it 'finds the expected job' do
+      expect(subject).to eq(expected_job)
+    end
+  end
+
   describe '#latest_builds_with_artifacts' do
     let!(:fresh_build) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
     let!(:stale_build) { create(:ci_build, :success, :expired, :artifacts, pipeline: pipeline) }
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 87ace7b51f4..95807f5f0c1 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -511,6 +511,32 @@ describe Snippet do
     end
   end
 
+  describe '#blobs' do
+    let(:snippet) { create(:snippet) }
+
+    context 'when repository does not exist' do
+      it 'returns empty array' do
+        expect(snippet.blobs).to be_empty
+      end
+    end
+
+    context 'when repository exists' do
+      let(:snippet) { create(:snippet, :repository) }
+
+      it 'returns array of blobs' do
+        expect(snippet.blobs).to all(be_a(Blob))
+      end
+    end
+
+    it 'returns a blob representing the snippet data' do
+      blob = snippet.blob
+
+      expect(blob).to be_a(Blob)
+      expect(blob.path).to eq(snippet.file_name)
+      expect(blob.data).to eq(snippet.content)
+    end
+  end
+
   describe '#to_json' do
     let(:snippet) { build(:snippet) }
 
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index e76ab8d409b..6e5c2088ee7 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -756,7 +756,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
               expect(json_response['dependencies'].count).to eq(1)
               expect(json_response['dependencies']).to include(
                 { 'id' => job.id, 'name' => job.name, 'token' => job.token,
-                  'artifacts_file' => { 'filename' => 'ci_build_artifacts.zip', 'size' => 106365 } })
+                  'artifacts_file' => { 'filename' => 'ci_build_artifacts.zip', 'size' => 107464 } })
             end
           end
 
diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
index 2657f1d300a..112b19fcbc5 100644
--- a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
+++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
@@ -4,30 +4,77 @@ require 'spec_helper'
 describe Ci::CreatePipelineService do
   let_it_be(:project) { create(:project, :repository) }
   let_it_be(:user) { create(:admin) }
-  let(:upstream_pipeline) { create(:ci_pipeline) }
   let(:ref) { 'refs/heads/master' }
   let(:service) { described_class.new(project, user, { ref: ref }) }
 
+  let(:upstream_pipeline) { create(:ci_pipeline, project: project) }
+  let(:bridge) { create(:ci_bridge, pipeline: upstream_pipeline) }
+
+  subject { service.execute(:push, bridge: bridge) }
+
   context 'custom config content' do
     let(:bridge) do
       create(:ci_bridge, status: 'running', pipeline: upstream_pipeline, project: upstream_pipeline.project).tap do |bridge|
-        allow(bridge).to receive(:yaml_for_downstream).and_return(
-          <<~YML
-            rspec:
-              script: rspec
-            custom:
-              script: custom
-          YML
-        )
+        allow(bridge).to receive(:yaml_for_downstream).and_return(config_from_bridge)
       end
     end
 
-    subject { service.execute(:push, bridge: bridge) }
+    let(:config_from_bridge) do
+      <<~YML
+        rspec:
+          script: rspec
+        custom:
+          script: custom
+      YML
+    end
+
+    before do
+      allow(bridge).to receive(:yaml_for_downstream).and_return config_from_bridge
+    end
 
     it 'creates a pipeline using the content passed in as param' do
       expect(subject).to be_persisted
       expect(subject.builds.map(&:name)).to eq %w[rspec custom]
       expect(subject.config_source).to eq 'bridge_source'
     end
+
+    context 'when bridge includes yaml from artifact' do
+      # the generated.yml is available inside the ci_build_artifacts.zip associated
+      # to the generator_job
+      let(:config_from_bridge) do
+        <<~YML
+          include:
+            - artifact: generated.yml
+              job: generator
+        YML
+      end
+
+      context 'when referenced job exists' do
+        let!(:generator_job) do
+          create(:ci_build, :artifacts,
+            project: project,
+            pipeline: upstream_pipeline,
+            name: 'generator')
+        end
+
+        it 'created a pipeline using the content passed in as param and download the artifact' do
+          expect(subject).to be_persisted
+          expect(subject.builds.pluck(:name)).to eq %w[rspec time custom]
+          expect(subject.config_source).to eq 'bridge_source'
+        end
+      end
+
+      context 'when referenced job does not exist' do
+        it 'creates an empty pipeline' do
+          expect(subject).to be_persisted
+          expect(subject).to be_failed
+          expect(subject.errors.full_messages)
+            .to contain_exactly(
+              'Job `generator` not found in parent pipeline or does not have artifacts!')
+          expect(subject.builds.pluck(:name)).to be_empty
+          expect(subject.config_source).to eq 'bridge_source'
+        end
+      end
+    end
   end
 end
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 12c51d01d63..cbf7a135c41 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -315,6 +315,7 @@ describe Projects::ForkService do
       # Stub everything required to move a project to a Gitaly shard that does not exist
       stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/second_storage' })
       allow_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror).and_return(true)
+      allow_any_instance_of(Gitlab::Git::Repository).to receive(:checksum).and_return(::Gitlab::Git::BLANK_SHA)
 
       Projects::UpdateRepositoryStorageService.new(project).execute('test_second_storage')
       fork_after_move = fork_project(project)
diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb
index a0917f718e6..2e9a4626abb 100644
--- a/spec/services/projects/update_repository_storage_service_spec.rb
+++ b/spec/services/projects/update_repository_storage_service_spec.rb
@@ -16,6 +16,15 @@ describe Projects::UpdateRepositoryStorageService do
 
     context 'without wiki and design repository' do
       let(:project) { create(:project, :repository, repository_read_only: true, wiki_enabled: false) }
+      let!(:checksum) { project.repository.checksum }
+      let(:project_repository_double) { double(:repository) }
+
+      before do
+        allow(Gitlab::Git::Repository).to receive(:new).and_call_original
+        allow(Gitlab::Git::Repository).to receive(:new)
+          .with('test_second_storage', project.repository.raw.relative_path, project.repository.gl_repository, project.repository.full_path)
+          .and_return(project_repository_double)
+      end
 
       context 'when the move succeeds' do
         it 'moves the repository to the new storage and unmarks the repository as read only' do
@@ -23,10 +32,14 @@ describe Projects::UpdateRepositoryStorageService do
             project.repository.path_to_repo
           end
 
-          expect_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror)
+          expect(project_repository_double).to receive(:fetch_repository_as_mirror)
             .with(project.repository.raw).and_return(true)
+          expect(project_repository_double).to receive(:checksum)
+            .and_return(checksum)
+
+          result = subject.execute('test_second_storage')
 
-          subject.execute('test_second_storage')
+          expect(result[:status]).to eq(:success)
           expect(project).not_to be_repository_read_only
           expect(project.repository_storage).to eq('test_second_storage')
           expect(gitlab_shell.repository_exists?('default', old_path)).to be(false)
@@ -44,16 +57,50 @@ describe Projects::UpdateRepositoryStorageService do
 
       context 'when the move fails' do
         it 'unmarks the repository as read-only without updating the repository storage' do
-          expect_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror)
+          expect(project_repository_double).to receive(:fetch_repository_as_mirror)
             .with(project.repository.raw).and_return(false)
           expect(GitlabShellWorker).not_to receive(:perform_async)
 
-          subject.execute('test_second_storage')
+          result = subject.execute('test_second_storage')
 
+          expect(result[:status]).to eq(:error)
           expect(project).not_to be_repository_read_only
           expect(project.repository_storage).to eq('default')
         end
       end
+
+      context 'when the checksum does not match' do
+        it 'unmarks the repository as read-only without updating the repository storage' do
+          expect(project_repository_double).to receive(:fetch_repository_as_mirror)
+            .with(project.repository.raw).and_return(true)
+          expect(project_repository_double).to receive(:checksum)
+            .and_return('not matching checksum')
+          expect(GitlabShellWorker).not_to receive(:perform_async)
+
+          result = subject.execute('test_second_storage')
+
+          expect(result[:status]).to eq(:error)
+          expect(project).not_to be_repository_read_only
+          expect(project.repository_storage).to eq('default')
+        end
+      end
+
+      context 'when a object pool was joined' do
+        let!(:pool) { create(:pool_repository, :ready, source_project: project) }
+
+        it 'leaves the pool' do
+          expect(project_repository_double).to receive(:fetch_repository_as_mirror)
+            .with(project.repository.raw).and_return(true)
+          expect(project_repository_double).to receive(:checksum)
+            .and_return(checksum)
+
+          result = subject.execute('test_second_storage')
+
+          expect(result[:status]).to eq(:success)
+          expect(project.repository_storage).to eq('test_second_storage')
+          expect(project.reload_pool_repository).to be_nil
+        end
+      end
     end
 
     context 'with wiki repository' do
@@ -66,18 +113,5 @@ describe Projects::UpdateRepositoryStorageService do
         end
       end
     end
-
-    context 'when a object pool was joined' do
-      let(:project) { create(:project, :repository, wiki_enabled: false, repository_read_only: true) }
-      let(:pool) { create(:pool_repository, :ready, source_project: project) }
-
-      it 'leaves the pool' do
-        allow_any_instance_of(Gitlab::Git::Repository).to receive(:fetch_repository_as_mirror).and_return(true)
-
-        subject.execute('test_second_storage')
-
-        expect(project.reload_pool_repository).to be_nil
-      end
-    end
   end
 end
diff --git a/spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb
index 556d81133bc..95772b1774a 100644
--- a/spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/config/inheritable_shared_examples.rb
@@ -53,7 +53,7 @@ RSpec.shared_examples 'with inheritable CI config' do
 
       let(:deps) do
         if inheritable_key
-          double('deps', inheritable_key => inheritable, '[]' => unspecified)
+          double('deps', "#{inheritable_key}_entry" => inheritable, '[]' => unspecified)
         else
           inheritable
         end
@@ -68,7 +68,7 @@ RSpec.shared_examples 'with inheritable CI config' do
         it 'does inherit value' do
           expect(inheritable).to receive('[]').with(entry_key).and_return(specified)
 
-          entry.compose!(deps)
+          entry.send(:inherit!, deps)
 
           expect(entry[entry_key]).to eq(specified)
         end
@@ -86,7 +86,7 @@ RSpec.shared_examples 'with inheritable CI config' do
             expect do
               # we ignore exceptions as `#overwrite_entry`
               # can raise exception on duplicates
-              entry.compose!(deps) rescue described_class::InheritError
+              entry.send(:inherit!, deps) rescue described_class::InheritError
             end.not_to change { entry[entry_key] }
           end
         end
@@ -94,7 +94,7 @@ RSpec.shared_examples 'with inheritable CI config' do
 
       context 'when inheritable does not specify' do
         it 'does not inherit value' do
-          entry.compose!(deps)
+          entry.send(:inherit!, deps)
 
           expect(entry[entry_key]).to be_a(
             Gitlab::Config::Entry::Undefined)
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index f222dff60ab..6f83f52d54b 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -2,7 +2,10 @@
 
 RSpec.shared_examples 'moves repository to another storage' do |repository_type|
   let(:project_repository_double) { double(:repository) }
+  let!(:project_repository_checksum) { project.repository.checksum }
+
   let(:repository_double) { double(:repository) }
+  let(:repository_checksum) { repository.checksum }
 
   before do
     # Default stub for non-specified params
@@ -19,15 +22,16 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
 
   context 'when the move succeeds', :clean_gitlab_redis_shared_state do
     before do
-      allow(project_repository_double)
-        .to receive(:fetch_repository_as_mirror)
+      allow(project_repository_double).to receive(:fetch_repository_as_mirror)
         .with(project.repository.raw)
         .and_return(true)
+      allow(project_repository_double).to receive(:checksum)
+        .and_return(project_repository_checksum)
 
-      allow(repository_double)
-        .to receive(:fetch_repository_as_mirror)
-        .with(repository.raw)
-        .and_return(true)
+      allow(repository_double).to receive(:fetch_repository_as_mirror)
+        .with(repository.raw).and_return(true)
+      allow(repository_double).to receive(:checksum)
+        .and_return(repository_checksum)
     end
 
     it "moves the project and its #{repository_type} repository to the new storage and unmarks the repository as read only" do
@@ -37,8 +41,9 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
 
       old_repository_path = repository.full_path
 
-      subject.execute('test_second_storage')
+      result = subject.execute('test_second_storage')
 
+      expect(result[:status]).to eq(:success)
       expect(project).not_to be_repository_read_only
       expect(project.repository_storage).to eq('test_second_storage')
       expect(gitlab_shell.repository_exists?('default', old_project_repository_path)).to be(false)
@@ -87,13 +92,38 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
     it 'unmarks the repository as read-only without updating the repository storage' do
       allow(project_repository_double).to receive(:fetch_repository_as_mirror)
         .with(project.repository.raw).and_return(true)
+      allow(project_repository_double).to receive(:checksum)
+        .and_return(project_repository_checksum)
       allow(repository_double).to receive(:fetch_repository_as_mirror)
         .with(repository.raw).and_return(false)
 
       expect(GitlabShellWorker).not_to receive(:perform_async)
 
-      subject.execute('test_second_storage')
+      result = subject.execute('test_second_storage')
+
+      expect(result[:status]).to eq(:error)
+      expect(project).not_to be_repository_read_only
+      expect(project.repository_storage).to eq('default')
+    end
+  end
+
+  context "when the checksum of the #{repository_type} repository does not match" do
+    it 'unmarks the repository as read-only without updating the repository storage' do
+      allow(project_repository_double).to receive(:fetch_repository_as_mirror)
+        .with(project.repository.raw).and_return(true)
+      allow(project_repository_double).to receive(:checksum)
+        .and_return(project_repository_checksum)
+
+      allow(repository_double).to receive(:fetch_repository_as_mirror)
+        .with(repository.raw).and_return(true)
+      allow(repository_double).to receive(:checksum)
+        .and_return('not matching checksum')
+
+      expect(GitlabShellWorker).not_to receive(:perform_async)
+
+      result = subject.execute('test_second_storage')
 
+      expect(result[:status]).to eq(:error)
       expect(project).not_to be_repository_read_only
       expect(project.repository_storage).to eq('default')
     end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 72e423db611..2a2d0f5a857 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -421,17 +421,35 @@ describe PostReceive do
             perform
           end
         end
+
+        it 'updates the snippet db information' do
+          blob = snippet.blobs.first
+
+          expect(snippet).to receive(:update).with(file_name: blob.path, content: blob.data)
+
+          perform
+        end
+
+        context 'when snippet does not have any blob' do
+          it 'does not update snippet db information' do
+            allow(snippet).to receive(:blobs).and_return([])
+
+            expect(snippet).not_to receive(:update)
+
+            perform
+          end
+        end
       end
     end
 
     context 'with PersonalSnippet' do
-      let!(:snippet) { create(:personal_snippet, author: project.owner) }
+      let!(:snippet) { create(:personal_snippet, :repository, author: project.owner) }
 
       it_behaves_like 'snippet changes actions'
     end
 
     context 'with ProjectSnippet' do
-      let!(:snippet) { create(:project_snippet, project: project, author: project.owner) }
+      let!(:snippet) { create(:project_snippet, :repository, project: project, author: project.owner) }
 
       it_behaves_like 'snippet changes actions'
     end
-- 
2.30.9