diff options
113 files changed, 1375 insertions, 799 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cc71ae9245a..d49b620f22c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,3 +44,4 @@ include: - local: .gitlab/ci/review.gitlab-ci.yml - local: .gitlab/ci/setup.gitlab-ci.yml - local: .gitlab/ci/test-metadata.gitlab-ci.yml + - local: .gitlab/ci/yaml.gitlab-ci.yml diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index f51982f22ab..d49fa4a49a0 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -18,3 +18,4 @@ db/ @abrandl @NikolayS /lib/gitlab/auth/ldap/ @dblessing @mkozono /lib/gitlab/ci/templates/ @nolith @zj /lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah +/lib/gitlab/ci/templates/Security/ @plafoucriere @gonzoyumo @twoodham diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index d62b24ae97d..fd5733593ef 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -33,16 +33,16 @@ gitlab:assets:compile: DOCKER_HOST: tcp://docker:2375 script: - node --version - - yarn install --frozen-lockfile --production --cache-folder .yarn-cache + - retry yarn install --frozen-lockfile --production --cache-folder .yarn-cache - free -m - - bundle exec rake gitlab:assets:compile + - retry bundle exec rake gitlab:assets:compile - time scripts/build_assets_image - scripts/clean-old-cached-assets - - rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here + - rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here # Play dependent manual jobs - install_api_client_dependencies_with_apt - - play_job "review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played - - play_job "schedule:review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played + - play_job "review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played + - play_job "schedule:review-build-cng" || true # this job might not exist so ignore the failure if it cannot be played artifacts: name: webpack-report expire_in: 31d @@ -65,9 +65,9 @@ compile-assets: stage: prepare script: - node --version - - yarn install --frozen-lockfile --cache-folder .yarn-cache + - retry yarn install --frozen-lockfile --cache-folder .yarn-cache - free -m - - bundle exec rake gitlab:assets:compile + - retry bundle exec rake gitlab:assets:compile - scripts/clean-old-cached-assets variables: # we override the max_old_space_size to prevent OOM errors @@ -141,8 +141,8 @@ jest: extends: .dedicated-no-docs-and-no-qa-pull-cache-job <<: *use-pg dependencies: - - compile-assets - - setup-test-env + - compile-assets + - setup-test-env script: - scripts/gitaly-test-spawn - date @@ -154,8 +154,8 @@ jest: expire_in: 31d when: always paths: - - coverage-frontend/ - - junit_jest.xml + - coverage-frontend/ + - junit_jest.xml reports: junit: junit_jest.xml cache: @@ -220,7 +220,7 @@ lint:javascript:report: before_script: [] script: - date - - yarn run eslint-report || true # ignore exit code + - yarn run eslint-report || true # ignore exit code artifacts: name: eslint-report expire_in: 31d @@ -235,7 +235,7 @@ jsdoc: before_script: [] script: - date - - yarn run jsdoc || true # ignore exit code + - yarn run jsdoc || true # ignore exit code artifacts: name: jsdoc expire_in: 31d diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index cf87f5eb39c..aa25bad00e8 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -1,6 +1,6 @@ .dedicated-runner: retry: - max: 2 # This is confusing but this means "3 runs at max". + max: 2 # This is confusing but this means "3 runs at max". when: - unknown_failure - api_failure diff --git a/.gitlab/ci/pages.gitlab-ci.yml b/.gitlab/ci/pages.gitlab-ci.yml index 7d9136b8213..f7b18b809b4 100644 --- a/.gitlab/ci/pages.gitlab-ci.yml +++ b/.gitlab/ci/pages.gitlab-ci.yml @@ -1,4 +1,3 @@ - pages: extends: .dedicated-no-docs-no-db-pull-cache-job before_script: [] diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml index 6b23af952e6..ee9e467886a 100644 --- a/.gitlab/ci/qa.gitlab-ci.yml +++ b/.gitlab/ci/qa.gitlab-ci.yml @@ -1,6 +1,6 @@ package-and-qa: image: ruby:2.6-alpine - stage: review # So even if review-deploy failed we can still run this + stage: review # So even if review-deploy failed we can still run this when: manual before_script: [] dependencies: [] diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 3f0c899d704..33e7b00784c 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -136,7 +136,7 @@ setup-test-env: stage: prepare script: - bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init' - - scripts/gitaly-test-build # Do not use 'bundle exec' here + - scripts/gitaly-test-build # Do not use 'bundle exec' here artifacts: expire_in: 7d paths: @@ -342,8 +342,8 @@ coverage: name: coverage expire_in: 31d paths: - - coverage/index.html - - coverage/assets/ + - coverage/index.html + - coverage/assets/ except: - /(^docs[\/-].*|.*-docs$)/ - /(^qa[\/-].*|.*-qa$)/ diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml index f5ed3e1ad9a..9b764028be9 100644 --- a/.gitlab/ci/review.gitlab-ci.yml +++ b/.gitlab/ci/review.gitlab-ci.yml @@ -76,8 +76,8 @@ schedule:review-build-cng: .review-deploy-base: &review-deploy-base <<: *review-base + allow_failure: true stage: review - retry: 2 variables: HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}" DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}" @@ -128,6 +128,7 @@ review-stop: .review-qa-base: &review-qa-base <<: *review-docker + allow_failure: true stage: qa variables: <<: *review-docker-variables @@ -157,7 +158,6 @@ review-stop: review-qa-smoke: <<: *review-qa-base - retry: 2 script: - gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}" diff --git a/.gitlab/ci/yaml.gitlab-ci.yml b/.gitlab/ci/yaml.gitlab-ci.yml new file mode 100644 index 00000000000..401318d2df2 --- /dev/null +++ b/.gitlab/ci/yaml.gitlab-ci.yml @@ -0,0 +1,9 @@ +# Yamllint of *.yml for .gitlab-ci.yml. +# This uses rules from project root `.yamllint`. +lint-ci-gitlab: + extends: .dedicated-runner + before_script: [] + dependencies: [] + image: sdesbure/yamllint:latest + script: + - yamllint .gitlab-ci.yml .gitlab/ci lib/gitlab/ci/templates diff --git a/.gitlab/route-map.yml b/.gitlab/route-map.yml index 0b37dc68f8b..cc0c2856d49 100644 --- a/.gitlab/route-map.yml +++ b/.gitlab/route-map.yml @@ -1,3 +1,3 @@ # Documentation -- source: /doc/(.+?)\.md/ # doc/administration/build_artifacts.md - public: '\1.html' # doc/administration/build_artifacts.html +- source: /doc/(.+?)\.md/ # doc/administration/build_artifacts.md + public: '\1.html' # doc/administration/build_artifacts.html diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000000..df7cdf404bc --- /dev/null +++ b/.yamllint @@ -0,0 +1,7 @@ +--- + +extends: default + +rules: + line-length: disable + document-start: disable diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 50aceaa7b71..0a3db35b241 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.45.0 +1.46.0 @@ -426,7 +426,7 @@ group :ed25519 do end # Gitaly GRPC client -gem 'gitaly-proto', '~> 1.27.0', require: 'gitaly' +gem 'gitaly-proto', '~> 1.32.0', require: 'gitaly' gem 'grpc', '~> 1.19.0' diff --git a/Gemfile.lock b/Gemfile.lock index 3b37cec3b7f..11e71cfb80e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -298,7 +298,7 @@ GEM gettext_i18n_rails (>= 0.7.1) po_to_json (>= 1.0.0) rails (>= 3.2.0) - gitaly-proto (1.27.0) + gitaly-proto (1.32.0) grpc (~> 1.0) github-markup (1.7.0) gitlab-default_value_for (3.1.1) @@ -1087,7 +1087,7 @@ DEPENDENCIES gettext (~> 3.2.2) gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails_js (~> 1.3) - gitaly-proto (~> 1.27.0) + gitaly-proto (~> 1.32.0) github-markup (~> 1.7.0) gitlab-default_value_for (~> 3.1.1) gitlab-labkit (~> 0.2.0) diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js index 18fa6265108..05f34391323 100644 --- a/app/assets/javascripts/gl_dropdown.js +++ b/app/assets/javascripts/gl_dropdown.js @@ -565,6 +565,11 @@ GitLabDropdown = (function() { !$target.data('isLink') ) { e.stopPropagation(); + + // This prevents automatic scrolling to the top + if ($target.is('a')) { + return false; + } } return true; diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index cccde1bb278..d2198bcccfe 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -134,6 +134,7 @@ export default { :current-path="path" :path="entry.flatPath" :type="entry.type" + :url="entry.webUrl" /> </template> </tbody> diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index 9a264bef87e..764882a7936 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -21,6 +21,11 @@ export default { type: String, required: true, }, + url: { + type: String, + required: false, + default: null, + }, }, computed: { routerLinkTo() { @@ -59,7 +64,7 @@ export default { <tr v-once :class="`file_${id}`" class="tree-item" @click="openRow"> <td class="tree-item-file-name"> <i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i> - <component :is="linkComponent" :to="routerLinkTo" class="str-truncated"> + <component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated"> {{ fullPath }} </component> <template v-if="isSubmodule"> diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.graphql index a9b61d28560..7d92bc46455 100644 --- a/app/assets/javascripts/repository/queries/getFiles.graphql +++ b/app/assets/javascripts/repository/queries/getFiles.graphql @@ -23,6 +23,7 @@ query getFiles( edges { node { ...TreeEntry + webUrl } } pageInfo { @@ -43,6 +44,7 @@ query getFiles( edges { node { ...TreeEntry + webUrl } } pageInfo { diff --git a/app/graphql/types/tree/blob_type.rb b/app/graphql/types/tree/blob_type.rb index 230624201b0..f2b7d5df2b2 100644 --- a/app/graphql/types/tree/blob_type.rb +++ b/app/graphql/types/tree/blob_type.rb @@ -4,7 +4,11 @@ module Types class BlobType < BaseObject implements Types::Tree::EntryType + present_using BlobPresenter + graphql_name 'Blob' + + field :web_url, GraphQL::STRING_TYPE, null: true end end end diff --git a/app/graphql/types/tree/tree_entry_type.rb b/app/graphql/types/tree/tree_entry_type.rb index d5cfb898aea..23ec2ef0ec2 100644 --- a/app/graphql/types/tree/tree_entry_type.rb +++ b/app/graphql/types/tree/tree_entry_type.rb @@ -4,8 +4,12 @@ module Types class TreeEntryType < BaseObject implements Types::Tree::EntryType + present_using TreeEntryPresenter + graphql_name 'TreeEntry' description 'Represents a directory' + + field :web_url, GraphQL::STRING_TYPE, null: true end end end diff --git a/app/graphql/types/tree/tree_type.rb b/app/graphql/types/tree/tree_type.rb index 1eb6c43972e..1ee93ed9542 100644 --- a/app/graphql/types/tree/tree_type.rb +++ b/app/graphql/types/tree/tree_type.rb @@ -4,9 +4,15 @@ module Types class TreeType < BaseObject graphql_name 'Tree' - field :trees, Types::Tree::TreeEntryType.connection_type, null: false + field :trees, Types::Tree::TreeEntryType.connection_type, null: false, resolve: -> (obj, args, ctx) do + Gitlab::Graphql::Representation::TreeEntry.decorate(obj.trees, obj.repository) + end + field :submodules, Types::Tree::SubmoduleType.connection_type, null: false - field :blobs, Types::Tree::BlobType.connection_type, null: false + + field :blobs, Types::Tree::BlobType.connection_type, null: false, resolve: -> (obj, args, ctx) do + Gitlab::Graphql::Representation::TreeEntry.decorate(obj.blobs, obj.repository) + end end end end diff --git a/app/helpers/ci_variables_helper.rb b/app/helpers/ci_variables_helper.rb index 5bfdeb9e33c..e313015c937 100644 --- a/app/helpers/ci_variables_helper.rb +++ b/app/helpers/ci_variables_helper.rb @@ -17,7 +17,7 @@ module CiVariablesHelper if variable && !only_key_value variable.masked else - true + false end end diff --git a/app/helpers/tracking_helper.rb b/app/helpers/tracking_helper.rb new file mode 100644 index 00000000000..51ea79d1ddd --- /dev/null +++ b/app/helpers/tracking_helper.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module TrackingHelper + def tracking_attrs(label, event, property) + {} # CE has no tracking features + end +end diff --git a/app/models/commit.rb b/app/models/commit.rb index f412d252e5c..fa0bf36ba49 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -13,6 +13,7 @@ class Commit include StaticModel include Presentable include ::Gitlab::Utils::StrongMemoize + include CacheMarkdownField attr_mentionable :safe_message, pipeline: :single_line @@ -37,13 +38,9 @@ class Commit # Used by GFM to match and present link extensions on node texts and hrefs. LINK_EXTENSION_PATTERN = /(patch)/.freeze - def banzai_render_context(field) - pipeline = field == :description ? :commit_description : :single_line - context = { pipeline: pipeline, project: self.project } - context[:author] = self.author if self.author - - context - end + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :full_title, pipeline: :single_line + cache_markdown_field :description, pipeline: :commit_description class << self def decorate(commits, project) diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index f90cd1ea690..42203a5f214 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -13,43 +13,9 @@ module CacheMarkdownField extend ActiveSupport::Concern - # Increment this number every time the renderer changes its output - CACHE_COMMONMARK_VERSION_START = 10 - CACHE_COMMONMARK_VERSION = 16 - # changes to these attributes cause the cache to be invalidates INVALIDATED_BY = %w[author project].freeze - # Knows about the relationship between markdown and html field names, and - # stores the rendering contexts for the latter - class FieldData - def initialize - @data = {} - end - - delegate :[], :[]=, to: :@data - - def markdown_fields - @data.keys - end - - def html_field(markdown_field) - "#{markdown_field}_html" - end - - def html_fields - markdown_fields.map { |field| html_field(field) } - end - - def html_fields_whitelisted - markdown_fields.each_with_object([]) do |field, fields| - if @data[field].fetch(:whitelisted, false) - fields << html_field(field) - end - end - end - end - def skip_project_check? false end @@ -85,24 +51,22 @@ module CacheMarkdownField end.to_h updates['cached_markdown_version'] = latest_cached_markdown_version - updates.each {|html_field, data| write_attribute(html_field, data) } + updates.each { |field, data| write_markdown_field(field, data) } end def refresh_markdown_cache! updates = refresh_markdown_cache - return unless persisted? && Gitlab::Database.read_write? - - update_columns(updates) + save_markdown(updates) end def cached_html_up_to_date?(markdown_field) - html_field = cached_markdown_fields.html_field(markdown_field) + return false if cached_html_for(markdown_field).nil? && __send__(markdown_field).present? # rubocop:disable GitlabSecurity/PublicSend - return false if cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? # rubocop:disable GitlabSecurity/PublicSend + html_field = cached_markdown_fields.html_field(markdown_field) - markdown_changed = attribute_changed?(markdown_field) || false - html_changed = attribute_changed?(html_field) || false + markdown_changed = markdown_field_changed?(markdown_field) + html_changed = markdown_field_changed?(html_field) latest_cached_markdown_version == cached_markdown_version && (html_changed || markdown_changed == html_changed) @@ -117,21 +81,21 @@ module CacheMarkdownField end def cached_html_for(markdown_field) - raise ArgumentError.new("Unknown field: #{field}") unless + raise ArgumentError.new("Unknown field: #{markdown_field}") unless cached_markdown_fields.markdown_fields.include?(markdown_field) __send__(cached_markdown_fields.html_field(markdown_field)) # rubocop:disable GitlabSecurity/PublicSend end def latest_cached_markdown_version - @latest_cached_markdown_version ||= (CacheMarkdownField::CACHE_COMMONMARK_VERSION << 16) | local_version + @latest_cached_markdown_version ||= (Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16) | local_version end def local_version # because local_markdown_version is stored in application_settings which # uses cached_markdown_version too, we check explicitly to avoid # endless loop - return local_markdown_version if has_attribute?(:local_markdown_version) + return local_markdown_version if respond_to?(:has_attribute?) && has_attribute?(:local_markdown_version) settings = Gitlab::CurrentSettings.current_application_settings @@ -150,32 +114,14 @@ module CacheMarkdownField included do cattr_reader :cached_markdown_fields do - FieldData.new + Gitlab::MarkdownCache::FieldData.new end - # Always exclude _html fields from attributes (including serialization). - # They contain unredacted HTML, which would be a security issue - alias_method :attributes_before_markdown_cache, :attributes - def attributes - attrs = attributes_before_markdown_cache - html_fields = cached_markdown_fields.html_fields - whitelisted = cached_markdown_fields.html_fields_whitelisted - exclude_fields = html_fields - whitelisted - - exclude_fields.each do |field| - attrs.delete(field) - end - - if whitelisted.empty? - attrs.delete('cached_markdown_version') - end - - attrs + if self < ActiveRecord::Base + include Gitlab::MarkdownCache::ActiveRecord::Extension + else + prepend Gitlab::MarkdownCache::Redis::Extension end - - # Using before_update here conflicts with elasticsearch-model somehow - before_create :refresh_markdown_cache, if: :invalidated_markdown_cache? - before_update :refresh_markdown_cache, if: :invalidated_markdown_cache? end class_methods do @@ -193,10 +139,8 @@ module CacheMarkdownField # The HTML becomes invalid if any dependent fields change. For now, assume # author and project invalidate the cache in all circumstances. define_method(invalidation_method) do - changed_fields = changed_attributes.keys - invalidations = changed_fields & [markdown_field.to_s, *INVALIDATED_BY] - invalidations.delete(markdown_field.to_s) if changed_fields.include?("#{markdown_field}_html") - + invalidations = changed_markdown_fields & [markdown_field.to_s, *INVALIDATED_BY] + invalidations.delete(markdown_field.to_s) if changed_markdown_fields.include?("#{markdown_field}_html") !invalidations.empty? || !cached_html_up_to_date?(markdown_field) end end diff --git a/app/presenters/blob_presenter.rb b/app/presenters/blob_presenter.rb index 6323c1b3389..c5675ef3ea3 100644 --- a/app/presenters/blob_presenter.rb +++ b/app/presenters/blob_presenter.rb @@ -13,4 +13,8 @@ class BlobPresenter < Gitlab::View::Presenter::Simple plain: plain ) end + + def web_url + Gitlab::Routing.url_helpers.project_blob_url(blob.repository.project, File.join(blob.commit_id, blob.path)) + end end diff --git a/app/presenters/tree_entry_presenter.rb b/app/presenters/tree_entry_presenter.rb new file mode 100644 index 00000000000..7bb10cd1455 --- /dev/null +++ b/app/presenters/tree_entry_presenter.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class TreeEntryPresenter < Gitlab::View::Presenter::Delegated + presents :tree + + def web_url + Gitlab::Routing.url_helpers.project_tree_url(tree.repository.project, File.join(tree.commit_id, tree.path)) + end +end diff --git a/app/views/ci/variables/_content.html.haml b/app/views/ci/variables/_content.html.haml index d07cbe4589c..0b5c1a806b2 100644 --- a/app/views/ci/variables/_content.html.haml +++ b/app/views/ci/variables/_content.html.haml @@ -1,3 +1,3 @@ -= _('Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they will be masked by default so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want.') += _('Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they can be masked so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want.') = _('You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>.').html_safe = link_to _('More information'), help_page_path('ci/variables/README', anchor: 'variables') diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml index ca2521e9bc6..ed4bd5ae19e 100644 --- a/app/views/ci/variables/_variable_row.html.haml +++ b/app/views/ci/variables/_variable_row.html.haml @@ -8,7 +8,7 @@ - value = variable&.value - is_protected_default = ci_variable_protected_by_default? - is_protected = ci_variable_protected?(variable, only_key_value) -- is_masked_default = true +- is_masked_default = false - is_masked = ci_variable_masked?(variable, only_key_value) - id_input_name = "#{form_field}[variables_attributes][][id]" diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml index b5678b56ca6..28d4f8eb201 100644 --- a/app/views/projects/_import_project_pane.html.haml +++ b/app/views/projects/_import_project_pane.html.haml @@ -8,59 +8,58 @@ .import-buttons - if gitlab_project_import_enabled? .import_gitlab_project.has-tooltip{ data: { container: 'body' } } - = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitlab_export" } do + = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit', **tracking_attrs(track_label, 'click_button', 'gitlab_export') do = icon('gitlab', text: 'GitLab export') - if github_import_enabled? %div - = link_to new_import_github_path, class: 'btn js-import-github', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "github" } do + = link_to new_import_github_path, class: 'btn js-import-github', **tracking_attrs(track_label, 'click_button', 'github') do = icon('github', text: 'GitHub') - if bitbucket_import_enabled? %div = link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}", - data: { track_label: "#{track_label}", track_event: "click_button", track_property: "bitbucket_cloud" } do + **tracking_attrs(track_label, 'click_button', 'bitbucket_cloud') do = icon('bitbucket', text: 'Bitbucket Cloud') - unless bitbucket_import_configured? = render 'bitbucket_import_modal' - if bitbucket_server_import_enabled? %div - = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", - data: { track_label: "#{track_label}", track_event: "click_button", track_property: "bitbucket_server" } do + = link_to status_import_bitbucket_server_path, class: "btn import_bitbucket", **tracking_attrs(track_label, 'click_button', 'bitbucket_server') do = icon('bitbucket-square', text: 'Bitbucket Server') %div - if gitlab_import_enabled? %div = link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}", - data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitlab_com" } do + **tracking_attrs(track_label, 'click_button', 'gitlab_com') do = icon('gitlab', text: 'GitLab.com') - unless gitlab_import_configured? = render 'gitlab_import_modal' - if google_code_import_enabled? %div - = link_to new_import_google_code_path, class: 'btn import_google_code', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "google_code" } do + = link_to new_import_google_code_path, class: 'btn import_google_code', **tracking_attrs(track_label, 'click_button', 'google_code') do = icon('google', text: 'Google Code') - if fogbugz_import_enabled? %div - = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "fogbugz" } do + = link_to new_import_fogbugz_path, class: 'btn import_fogbugz', **tracking_attrs(track_label, 'click_button', 'fogbugz') do = icon('bug', text: 'Fogbugz') - if gitea_import_enabled? %div - = link_to new_import_gitea_path, class: 'btn import_gitea', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitea" } do + = link_to new_import_gitea_path, class: 'btn import_gitea', **tracking_attrs(track_label, 'click_button', 'gitea') do = custom_icon('gitea_logo') Gitea - if git_import_enabled? %div - %button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active', data: { toggle_open_class: 'active', track_label: "#{track_label}" , track_event: "click_button", track_property: "repo_url" } } } + %button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' }, **tracking_attrs(track_label, 'click_button', 'repo_url') } = icon('git', text: 'Repo by URL') - if manifest_import_enabled? %div - = link_to new_import_manifest_path, class: 'btn import_manifest', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "manifest_file" } do + = link_to new_import_manifest_path, class: 'btn import_manifest', **tracking_attrs(track_label, 'click_button', 'manifest_file') do = icon('file-text-o', text: 'Manifest file') - if phabricator_import_enabled? diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml index e37fd7624be..065fef606d5 100644 --- a/app/views/projects/tree/_tree_commit_column.html.haml +++ b/app/views/projects/tree/_tree_commit_column.html.haml @@ -1,2 +1,3 @@ +- full_title = markdown_field(commit, :full_title) %span.str-truncated - = link_to_html commit.redacted_full_title_html, project_commit_path(@project, commit.id), title: commit.redacted_full_title_html, class: 'tree-commit-link' + = link_to_html full_title, project_commit_path(@project, commit.id), title: full_title, class: 'tree-commit-link' diff --git a/app/views/repository_check_mailer/notify.html.haml b/app/views/repository_check_mailer/notify.html.haml index d5327a2b4cc..dfcd1c6b19f 100644 --- a/app/views/repository_check_mailer/notify.html.haml +++ b/app/views/repository_check_mailer/notify.html.haml @@ -6,3 +6,5 @@ %p = _("You are receiving this message because you are a GitLab administrator for %{url}.") % { url: Gitlab.config.gitlab.url } + += render_if_exists 'repository_check_mailer/email_additional_text' diff --git a/changelogs/unreleased/11204-turn-off-mask-by-default.yml b/changelogs/unreleased/11204-turn-off-mask-by-default.yml new file mode 100644 index 00000000000..5c554e04d45 --- /dev/null +++ b/changelogs/unreleased/11204-turn-off-mask-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Default masked to false for new variables +merge_request: 28186 +author: +type: changed diff --git a/changelogs/unreleased/54140-non-ar-cache-commit-markdown.yml b/changelogs/unreleased/54140-non-ar-cache-commit-markdown.yml new file mode 100644 index 00000000000..efda07380a4 --- /dev/null +++ b/changelogs/unreleased/54140-non-ar-cache-commit-markdown.yml @@ -0,0 +1,5 @@ +--- +title: Use Redis for CacheMarkDownField on non AR models +merge_request: 29054 +author: +type: performance diff --git a/changelogs/unreleased/60819_yamllint_gitlabci.yml b/changelogs/unreleased/60819_yamllint_gitlabci.yml new file mode 100644 index 00000000000..aba3b206f7e --- /dev/null +++ b/changelogs/unreleased/60819_yamllint_gitlabci.yml @@ -0,0 +1,5 @@ +--- +title: Fix yaml linting for GitLab CI inside project (.gitlab/ci) *.yml files and CI template files +merge_request: 27576 +author: Will Hall +type: fixed diff --git a/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml b/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml new file mode 100644 index 00000000000..7fa15e1c0fd --- /dev/null +++ b/changelogs/unreleased/61246-fix-label-click-scroll-to-top.yml @@ -0,0 +1,5 @@ +--- +title: Fix label click scrolling to top +merge_request: 29202 +author: +type: fixed diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 43fc3a1ad47..c851931e85c 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -87,7 +87,7 @@ Variable types can be set via the [UI](#via-the-ui) or the [API](../../api/proje > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/13784) in GitLab 11.10 -By default, variables will be created as masked variables. +Variables can be created as masked variables. This means that the value of the variable will be hidden in job logs, though it must match certain requirements to do so: diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md index 427b474ca39..83c284dce7a 100644 --- a/doc/user/group/insights/index.md +++ b/doc/user/group/insights/index.md @@ -28,7 +28,7 @@ the project that holds your `.gitlab/insights.yml` configuration file: ![group insights configuration](img/insights_group_configuration.png) If no configuration was set, a [default configuration file]( -https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/fixtures/insights/ee/fixtures/insights/default.yml) +https://gitlab.com/gitlab-org/gitlab-ee/blob/master/ee/fixtures/insights/default.yml) will be used. See the [Project's Insights documentation](../../project/insights/index.md) for diff --git a/lib/banzai/commit_renderer.rb b/lib/banzai/commit_renderer.rb index f346151a3c1..2acc9d13f07 100644 --- a/lib/banzai/commit_renderer.rb +++ b/lib/banzai/commit_renderer.rb @@ -2,7 +2,7 @@ module Banzai module CommitRenderer - ATTRIBUTES = [:description, :title].freeze + ATTRIBUTES = [:description, :title, :full_title].freeze def self.render(commits, project, user = nil) obj_renderer = ObjectRenderer.new(user: user, default_project: project) diff --git a/lib/gitlab/ci/templates/.yamllint b/lib/gitlab/ci/templates/.yamllint new file mode 100644 index 00000000000..669c8646ff2 --- /dev/null +++ b/lib/gitlab/ci/templates/.yamllint @@ -0,0 +1,5 @@ +extends: default + +rules: + line-length: disable + document-start: disable diff --git a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml index 120272200c6..2ca6e73a803 100644 --- a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml @@ -54,7 +54,7 @@ ensureContainer: - rm -f android-signing-keystore.jks || true artifacts: paths: - - app/build/outputs + - app/build/outputs buildDebug: extends: .build_job @@ -116,6 +116,6 @@ promoteProduction: # We only allow production promotion on `master` because # it has its own production scoped secret variables only: - - master + - master script: - bundle exec fastlane promote_beta_to_production diff --git a/lib/gitlab/ci/templates/Android.gitlab-ci.yml b/lib/gitlab/ci/templates/Android.gitlab-ci.yml index 2be7f933462..b7194110002 100644 --- a/lib/gitlab/ci/templates/Android.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Android.gitlab-ci.yml @@ -6,7 +6,7 @@ image: openjdk:8-jdk variables: ANDROID_COMPILE_SDK: "28" ANDROID_BUILD_TOOLS: "28.0.2" - ANDROID_SDK_TOOLS: "4333796" + ANDROID_SDK_TOOLS: "4333796" before_script: - apt-get --quiet update --yes @@ -35,7 +35,7 @@ assembleDebug: - ./gradlew assembleDebug artifacts: paths: - - app/build/outputs/ + - app/build/outputs/ debugTests: stage: test diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index 32ab216dd55..65a6630365d 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -60,7 +60,7 @@ variables: stages: - build - test - - deploy # dummy stage to follow the template guidelines + - deploy # dummy stage to follow the template guidelines - review - dast - staging diff --git a/lib/gitlab/ci/templates/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml index 9a8fa9d7091..33a2a534508 100644 --- a/lib/gitlab/ci/templates/C++.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml @@ -14,10 +14,10 @@ build: artifacts: paths: - mybinary - # depending on your build setup it's most likely a good idea to cache outputs to reduce the build time - # cache: - # paths: - # - "*.o" + # depending on your build setup it's most likely a good idea to cache outputs to reduce the build time + # cache: + # paths: + # - "*.o" # run tests using the binary built before test: diff --git a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml index 1e14aa8bea9..5f17c93b853 100644 --- a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml @@ -23,31 +23,31 @@ stages: foodcritic: stage: lint script: - - chef exec foodcritic . + - chef exec foodcritic . cookstyle: stage: lint script: - - chef exec cookstyle . + - chef exec cookstyle . chefspec: stage: test script: - - chef exec rspec spec + - chef exec rspec spec # Set up your test matrix here. Example: -#verify-centos-6: -# stage: functional -# before_script: -# - apt-get update -# - apt-get -y install rsync -# script: -# - kitchen verify default-centos-6 --destroy=always +# verify-centos-6: +# stage: functional +# before_script: +# - apt-get update +# - apt-get -y install rsync +# script: +# - kitchen verify default-centos-6 --destroy=always # -#verify-centos-7: -# stage: functional -# before_script: -# - apt-get update -# - apt-get -y install rsync -# script: -# - kitchen verify default-centos-7 --destroy=always +# verify-centos-7: +# stage: functional +# before_script: +# - apt-get update +# - apt-get -y install rsync +# script: +# - kitchen verify default-centos-7 --destroy=always diff --git a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml index 0610cb9ccc0..c3568c0d2c8 100644 --- a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml @@ -8,7 +8,7 @@ before_script: # If you need to install any external applications, like a # postgres client, you may want to uncomment the line below: # - #- apt-get update -y + # - apt-get update -y # # Retrieve project dependencies # Do this on before_script since it'll be shared between both test and @@ -17,6 +17,6 @@ before_script: test: script: - # If you need to run any migrations or configure the database, this - # would be the point to do it. - - lein test + # If you need to run any migrations or configure the database, this + # would be the point to do it. + - lein test diff --git a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml index 36386a19fdc..e9301a2638d 100644 --- a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml @@ -22,15 +22,15 @@ cache: # services such as redis or postgres before_script: - apt-get update -qq && apt-get install -y -qq libxml2-dev - - crystal -v # Print out Crystal version for debugging + - crystal -v # Print out Crystal version for debugging - shards # If you are using built-in Crystal Spec. spec: script: - - crystal spec + - crystal spec # If you are using minitest.cr minitest: script: - - crystal test/spec_test.cr # change to the file(s) you execute for tests + - crystal test/spec_test.cr # change to the file(s) you execute for tests diff --git a/lib/gitlab/ci/templates/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml index 1d8be6f017e..d35fcb0f807 100644 --- a/lib/gitlab/ci/templates/Django.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml @@ -16,19 +16,19 @@ variables: # http://docs.gitlab.com/ce/ci/yaml/README.html#cache cache: paths: - - ~/.cache/pip/ + - ~/.cache/pip/ # This is a basic example for a gem or script which doesn't use # services such as redis or postgres before_script: - - python -V # Print out python version for debugging + - python -V # Print out python version for debugging # Uncomment next line if your Django app needs a JS runtime: # - apt-get update -q && apt-get install nodejs -yqq - pip install -r requirements.txt # To get Django tests to work you may need to create a settings file using # the following DATABASES: -# +# # DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.postgresql_psycopg2', @@ -46,4 +46,4 @@ test: variables: DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" script: - - python manage.py test + - python manage.py test diff --git a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml index cf9c731637c..4d4c6a64cd5 100644 --- a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml @@ -15,4 +15,4 @@ before_script: mix: script: - - mix test + - mix test diff --git a/lib/gitlab/ci/templates/Go.gitlab-ci.yml b/lib/gitlab/ci/templates/Go.gitlab-ci.yml index 55fda1a4799..1b686bc6cc0 100644 --- a/lib/gitlab/ci/templates/Go.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Go.gitlab-ci.yml @@ -16,21 +16,21 @@ before_script: - cd $GOPATH/src/$REPO_NAME stages: - - test - - build - - deploy + - test + - build + - deploy format: - stage: test - script: - - go fmt $(go list ./... | grep -v /vendor/) - - go vet $(go list ./... | grep -v /vendor/) - - go test -race $(go list ./... | grep -v /vendor/) + stage: test + script: + - go fmt $(go list ./... | grep -v /vendor/) + - go vet $(go list ./... | grep -v /vendor/) + - go test -race $(go list ./... | grep -v /vendor/) compile: - stage: build - script: - - go build -race -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/mybinary - artifacts: - paths: - - mybinary + stage: build + script: + - go build -race -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/mybinary + artifacts: + paths: + - mybinary diff --git a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml index dbc868238f8..efcd1d3ddc0 100644 --- a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml @@ -16,25 +16,25 @@ variables: # We use SDKMan as tool for managing versions before_script: - - apt-get update -qq && apt-get install -y -qq unzip - - curl -sSL https://get.sdkman.io | bash - - echo sdkman_auto_answer=true > /root/.sdkman/etc/config - - source /root/.sdkman/bin/sdkman-init.sh - - sdk install gradle $GRADLE_VERSION < /dev/null - - sdk use gradle $GRADLE_VERSION -# As it's not a good idea to version gradle.properties feel free to add your -# environments variable here - - echo grailsVersion=$GRAILS_VERSION > gradle.properties - - echo gradleWrapperVersion=2.14 >> gradle.properties -# refresh dependencies from your project - - ./gradlew --refresh-dependencies -# Be aware that if you are using Angular profile, -# Bower cannot be run as root if you don't allow it before. -# Feel free to remove next line if you are not using Bower - - echo {\"allow_root\":true} > /root/.bowerrc + - apt-get update -qq && apt-get install -y -qq unzip + - curl -sSL https://get.sdkman.io | bash + - echo sdkman_auto_answer=true > /root/.sdkman/etc/config + - source /root/.sdkman/bin/sdkman-init.sh + - sdk install gradle $GRADLE_VERSION < /dev/null + - sdk use gradle $GRADLE_VERSION + # As it's not a good idea to version gradle.properties feel free to add your + # environments variable here + - echo grailsVersion=$GRAILS_VERSION > gradle.properties + - echo gradleWrapperVersion=2.14 >> gradle.properties + # refresh dependencies from your project + - ./gradlew --refresh-dependencies + # Be aware that if you are using Angular profile, + # Bower cannot be run as root if you don't allow it before. + # Feel free to remove next line if you are not using Bower + - echo {\"allow_root\":true} > /root/.bowerrc # This build job does the full grails pipeline # (compile, test, integrationTest, war, assemble). build: - script: - - ./gradlew build + script: + - ./gradlew build diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml index 546c4affb4e..a09217e8cf0 100644 --- a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.gitlab-ci.yml @@ -26,8 +26,8 @@ performance: - mv sitespeed-results/data/performance.json performance.json artifacts: paths: - - performance.json - - sitespeed-results/ + - performance.json + - sitespeed-results/ only: refs: - branches diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml index 2c4683fbfbb..32d4e07d398 100644 --- a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml @@ -20,7 +20,7 @@ # want coverage results. - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage"); Pkg.test("MyPackage"; coverage = true)' # Comment out below if you do not want coverage results. - - julia -e 'using Pkg; Pkg.add("Coverage"); + - julia -e 'using Pkg; Pkg.add("Coverage"); import MyPackage; cd(joinpath(dirname(pathof(MyPackage)), "..")); using Coverage; cl, tl = get_summary(process_folder()); println("(", cl/tl*100, "%) covered")' @@ -55,17 +55,16 @@ pages: image: julia:0.7 stage: deploy script: - - apt-get update -qq && apt-get install -y git # needed by Documenter - - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage");' # rebuild Julia (can be put somewhere else I'm sure - - julia -e 'using Pkg; import MyPackage; Pkg.add("Documenter")' # install Documenter - - julia --color=yes docs/make.jl # make documentation - - mv docs/build public # move to the directory picked up by Gitlab pages + - apt-get update -qq && apt-get install -y git # needed by Documenter + - julia -e 'using Pkg; Pkg.clone(pwd()); Pkg.build("MyPackage");' # rebuild Julia (can be put somewhere else I'm sure + - julia -e 'using Pkg; import MyPackage; Pkg.add("Documenter")' # install Documenter + - julia --color=yes docs/make.jl # make documentation + - mv docs/build public # move to the directory picked up by Gitlab pages artifacts: paths: - public only: - - master - + - master # WARNING: This template is using the `julia` images from [Docker # Hub][3]. One can use custom Julia images and/or the official ones found diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml index e1cd29ecc94..9bde04dff19 100644 --- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml @@ -16,8 +16,8 @@ variables: # http://docs.gitlab.com/ce/ci/yaml/README.html#cache cache: paths: - - vendor/ - - node_modules/ + - vendor/ + - node_modules/ # This is a basic example for a gem or script which doesn't use # services such as redis or postgres @@ -37,7 +37,7 @@ before_script: - docker-php-ext-enable xdebug # Install Composer and project dependencies. - curl -sS https://getcomposer.org/installer | php - - php composer.phar install + - php composer.phar install # Install Node dependencies. # comment this out if you don't have a node dependency - npm install @@ -63,10 +63,10 @@ before_script: test: script: - # run laravel tests - - php vendor/bin/phpunit --coverage-text --colors=never - # run frontend tests - # if you have any task for testing frontend - # set it in your package.json script - # comment this out if you don't have a frontend test - - npm test + # run laravel tests + - php vendor/bin/phpunit --coverage-text --colors=never + # run frontend tests + # if you have any task for testing frontend + # set it in your package.json script + # comment this out if you don't have a frontend test + - npm test diff --git a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml index 41de1458582..b87178141a1 100644 --- a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml @@ -14,14 +14,14 @@ services: # http://docs.gitlab.com/ce/ci/yaml/README.html#cache cache: paths: - - node_modules/ + - node_modules/ test_async: script: - - npm install - - node ./specs/start.js ./specs/async.spec.js + - npm install + - node ./specs/start.js ./specs/async.spec.js test_db: script: - - npm install - - node ./specs/start.js ./specs/db-postgres.spec.js + - npm install + - node ./specs/start.js ./specs/db-postgres.spec.js diff --git a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml index 61a925e0d2d..65abee1f5eb 100644 --- a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml @@ -1,9 +1,9 @@ image: ayufan/openshift-cli stages: - - build # dummy stage to follow the template guidelines + - build # dummy stage to follow the template guidelines - test - - deploy # dummy stage to follow the template guidelines + - deploy # dummy stage to follow the template guidelines - review - staging - production diff --git a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml index 33f44ee9222..b9fee2d5731 100644 --- a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml @@ -4,19 +4,19 @@ image: php:7.1.1 # Select what we should cache between builds cache: paths: - - vendor/ + - vendor/ before_script: -- apt-get update -yqq -- apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -# Install PHP extensions -- docker-php-ext-install mbstring mcrypt pdo_pgsql curl json intl gd xml zip bz2 opcache -# Install & enable Xdebug for code coverage reports -- pecl install xdebug -- docker-php-ext-enable xdebug -# Install and run Composer -- curl -sS https://getcomposer.org/installer | php -- php composer.phar install + - apt-get update -yqq + - apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev + # Install PHP extensions + - docker-php-ext-install mbstring mcrypt pdo_pgsql curl json intl gd xml zip bz2 opcache + # Install & enable Xdebug for code coverage reports + - pecl install xdebug + - docker-php-ext-enable xdebug + # Install and run Composer + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install # Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service # See http://docs.gitlab.com/ce/ci/services/README.html for examples. @@ -33,4 +33,4 @@ variables: # If Xdebug was installed you can generate a coverage report and see code coverage metrics. test: script: - - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
\ No newline at end of file + - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never diff --git a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml index d6de8cab5d1..d2dd3fbfb75 100644 --- a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml @@ -4,12 +4,12 @@ image: node:4.2.2 pages: cache: paths: - - node_modules/ + - node_modules/ script: - - npm install -g brunch - - brunch build --production + - npm install -g brunch + - brunch build --production artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml index 791afdd23f1..ba422c08614 100644 --- a/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Doxygen.gitlab-ci.yml @@ -3,11 +3,11 @@ image: alpine pages: script: - - apk update && apk add doxygen - - doxygen doxygen/Doxyfile - - mv doxygen/documentation/html/ public/ + - apk update && apk add doxygen + - doxygen doxygen/Doxyfile + - mv doxygen/documentation/html/ public/ artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml index 9df2a4797b2..a683561a455 100644 --- a/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Gatsby.gitlab-ci.yml @@ -4,14 +4,14 @@ image: node:latest # http://docs.gitlab.com/ce/ci/yaml/README.html#cache cache: paths: - - node_modules/ + - node_modules/ pages: script: - - yarn install - - ./node_modules/.bin/gatsby build --prefix-paths + - yarn install + - ./node_modules/.bin/gatsby build --prefix-paths artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml index 249a168aa33..92f25280c6e 100644 --- a/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/HTML.gitlab-ci.yml @@ -2,11 +2,11 @@ pages: stage: deploy script: - - mkdir .public - - cp -r * .public - - mv .public public + - mkdir .public + - cp -r * .public + - mv .public public artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml index 4b58003ee10..0e206423fa5 100644 --- a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml @@ -4,12 +4,12 @@ image: node:4.2.2 pages: cache: paths: - - node_modules + - node_modules script: - - npm install -g harp - - harp compile ./ public + - npm install -g harp + - harp compile ./ public artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml index a9e195370f7..d91a8d7421f 100644 --- a/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Hexo.gitlab-ci.yml @@ -3,15 +3,15 @@ image: node:10.15.3 pages: script: - - npm install hexo-cli -g - - test -e package.json && npm install - - hexo generate + - npm install hexo-cli -g + - test -e package.json && npm install + - hexo generate artifacts: paths: - - public + - public cache: paths: - node_modules key: project only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml index f9ddcc6fb0a..9a3ecd1c34f 100644 --- a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml @@ -3,15 +3,15 @@ image: dettmering/hugo-build pages: script: - - hugo + - hugo artifacts: paths: - - public + - public only: - - master + - master test: script: - - hugo + - hugo except: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml index f5b40f2b9f1..7a441a2f70f 100644 --- a/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Hyde.gitlab-ci.yml @@ -3,7 +3,7 @@ image: python:2.7 cache: paths: - - vendor/ + - vendor/ test: stage: test @@ -20,6 +20,6 @@ pages: - hyde gen -d public artifacts: paths: - - public + - public only: - master diff --git a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml index 7a485f8d135..886b6c36249 100644 --- a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml @@ -4,28 +4,28 @@ # JBake https://jbake.org/ is a Java based, open source, static site/blog generator for developers & designers # # This yml works with jBake 2.5.1 -# Feel free to change JBAKE_VERSION version +# Feel free to change JBAKE_VERSION version # # HowTo at: https://jorge.aguilera.gitlab.io/howtojbake/ image: java:8 variables: - JBAKE_VERSION: 2.5.1 + JBAKE_VERSION: 2.5.1 # We use SDKMan as tool for managing versions before_script: - - apt-get update -qq && apt-get install -y -qq unzip zip - - curl -sSL https://get.sdkman.io | bash - - echo sdkman_auto_answer=true > /root/.sdkman/etc/config - - source /root/.sdkman/bin/sdkman-init.sh - - sdk install jbake $JBAKE_VERSION < /dev/null - - sdk use jbake $JBAKE_VERSION + - apt-get update -qq && apt-get install -y -qq unzip zip + - curl -sSL https://get.sdkman.io | bash + - echo sdkman_auto_answer=true > /root/.sdkman/etc/config + - source /root/.sdkman/bin/sdkman-init.sh + - sdk install jbake $JBAKE_VERSION < /dev/null + - sdk use jbake $JBAKE_VERSION # This build job produced the output directory of your site pages: - script: - - jbake . public - artifacts: - paths: - - public + script: + - jbake . public + artifacts: + paths: + - public diff --git a/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml index 37f50554036..0d742aa282d 100644 --- a/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Jekyll.gitlab-ci.yml @@ -6,25 +6,24 @@ variables: JEKYLL_ENV: production before_script: -- bundle install + - bundle install test: stage: test script: - - bundle exec jekyll build -d test + - bundle exec jekyll build -d test artifacts: paths: - - test + - test except: - - master + - master pages: stage: deploy script: - - bundle exec jekyll build -d public + - bundle exec jekyll build -d public artifacts: paths: - - public + - public only: - - master - + - master diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml index 5ca4619e200..2d26b86a328 100644 --- a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml @@ -7,8 +7,8 @@ image: php:7.2 # These folders are cached between builds cache: paths: - - vendor/ - - node_modules/ + - vendor/ + - node_modules/ before_script: # Update packages @@ -28,10 +28,10 @@ before_script: pages: script: - - npm run production - - mv build_production public + - npm run production + - mv build_production public artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml index c5c44a5d86c..93ab8e0be0d 100644 --- a/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Lektor.gitlab-ci.yml @@ -3,10 +3,10 @@ image: python:2.7 pages: script: - - pip install lektor - - lektor build --output-path public + - pip install lektor + - lektor build --output-path public artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml index c6ded272150..6524405133a 100644 --- a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml @@ -4,13 +4,13 @@ image: node:4.2.2 pages: cache: paths: - - node_modules/ + - node_modules/ script: - - npm install -g metalsmith - - npm install - - make build + - npm install -g metalsmith + - npm install + - make build artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml index 9f4cc0574d6..57ac323dfdf 100644 --- a/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Middleman.gitlab-ci.yml @@ -3,25 +3,25 @@ image: ruby:2.3 cache: paths: - - vendor + - vendor test: script: - - apt-get update -yqqq - - apt-get install -y nodejs - - bundle install --path vendor - - bundle exec middleman build + - apt-get update -yqqq + - apt-get install -y nodejs + - bundle install --path vendor + - bundle exec middleman build except: - master pages: script: - - apt-get update -yqqq - - apt-get install -y nodejs - - bundle install --path vendor - - bundle exec middleman build + - apt-get update -yqqq + - apt-get install -y nodejs + - bundle install --path vendor + - bundle exec middleman build artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml index b469b316ba5..7f037b5f5cf 100644 --- a/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Nanoc.gitlab-ci.yml @@ -3,10 +3,10 @@ image: ruby:2.3 pages: script: - - bundle install -j4 - - nanoc + - bundle install -j4 + - nanoc artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml index 4762ec9acfd..6d912a89bc1 100644 --- a/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Octopress.gitlab-ci.yml @@ -3,13 +3,13 @@ image: ruby:2.3 pages: script: - - apt-get update -qq && apt-get install -qq nodejs - - bundle install -j4 - - bundle exec rake generate - - mv public .public - - mv .public/octopress public + - apt-get update -qq && apt-get install -qq nodejs + - bundle install -j4 + - bundle exec rake generate + - mv public .public + - mv .public/octopress public artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml index c5f3154f587..09c6649fc13 100644 --- a/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Pages/Pelican.gitlab-ci.yml @@ -3,8 +3,8 @@ image: python:2.7-alpine pages: script: - - pip install -r requirements.txt - - pelican -s publishconf.py + - pip install -r requirements.txt + - pelican -s publishconf.py artifacts: paths: - - public/ + - public/ diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml index 3eaed4e91cd..00b8b94b574 100644 --- a/lib/gitlab/ci/templates/Python.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml @@ -18,34 +18,34 @@ cache: - venv/ before_script: - - python -V # Print out python version for debugging + - python -V # Print out python version for debugging - pip install virtualenv - virtualenv venv - source venv/bin/activate test: script: - - python setup.py test - - pip install tox flake8 # you can also use tox - - tox -e py36,flake8 + - python setup.py test + - pip install tox flake8 # you can also use tox + - tox -e py36,flake8 run: script: - - python setup.py bdist_wheel - # an alternative approach is to install and run: - - pip install dist/* - # run the command here + - python setup.py bdist_wheel + # an alternative approach is to install and run: + - pip install dist/* + # run the command here artifacts: paths: - - dist/*.whl + - dist/*.whl pages: script: - - pip install sphinx sphinx-rtd-theme - - cd doc ; make html - - mv build/html/ ../public/ + - pip install sphinx sphinx-rtd-theme + - cd doc ; make html + - mv build/html/ ../public/ artifacts: paths: - - public + - public only: - - master + - master diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml index 93196dbd475..b3cad8b858a 100644 --- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml @@ -21,7 +21,7 @@ cache: # This is a basic example for a gem or script which doesn't use # services such as redis or postgres before_script: - - ruby -v # Print out ruby version for debugging + - ruby -v # Print out ruby version for debugging # Uncomment next line if your rails app needs a JS runtime: # - apt-get update -q && apt-get install nodejs -yqq - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby @@ -29,19 +29,19 @@ before_script: # Optional - Delete if not using `rubocop` rubocop: script: - - rubocop + - rubocop rspec: script: - - rspec spec + - rspec spec rails: variables: DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" script: - - rails db:migrate - - rails db:seed - - rails test + - rails db:migrate + - rails db:seed + - rails test # This deploy job uses a simple deploy flow to Heroku, other providers, e.g. AWS Elastic Beanstalk # are supported too: https://github.com/travis-ci/dpl @@ -49,5 +49,5 @@ deploy: type: deploy environment: production script: - - gem install dpl - - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY + - gem install dpl + - dpl --provider=heroku --app=$HEROKU_APP_NAME --api-key=$HEROKU_PRODUCTION_KEY diff --git a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml index cab087c48c7..a25dc38e4e7 100644 --- a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml @@ -5,19 +5,19 @@ image: "rust:latest" # Optional: Pick zero or more services to be used on all builds. # Only needed when using a docker container to run your tests in. # Check out: http://docs.gitlab.com/ce/ci/docker/using_docker_images.html#what-is-a-service -#services: -# - mysql:latest -# - redis:latest -# - postgres:latest +# services: +# - mysql:latest +# - redis:latest +# - postgres:latest # Optional: Install a C compiler, cmake and git into the container. # You will often need this when you (or any of your dependencies) depends on C code. -#before_script: -#- apt-get update -yqq -#- apt-get install -yqq --no-install-recommends build-essential +# before_script: +# - apt-get update -yqq +# - apt-get install -yqq --no-install-recommends build-essential # Use cargo to test the project test:cargo: script: - - rustc --version && cargo --version # Print version info for debugging - - cargo test --all --verbose + - rustc --version && cargo --version # Print version info for debugging + - cargo test --all --verbose diff --git a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml index 27a498b2daf..4b55ffd3771 100644 --- a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml @@ -15,22 +15,22 @@ dast: image: name: "registry.gitlab.com/gitlab-org/security-products/dast:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable" variables: - # URL to scan: - # DAST_WEBSITE: https://example.com/ - # - # Time limit for target availability (scan is attempted even when timeout): - # DAST_TARGET_AVAILABILITY_TIMEOUT: 60 - # - # Set these variables to scan with an authenticated user: - # DAST_AUTH_URL: https://example.com/sign-in - # DAST_USERNAME: john.doe@example.com - # DAST_PASSWORD: john-doe-password - # DAST_USERNAME_FIELD: session[user] # the name of username field at the sign-in HTML form - # DAST_PASSWORD_FIELD: session[password] # the name of password field at the sign-in HTML form - # DAST_AUTH_EXCLUDE_URLS: http://example.com/sign-out,http://example.com/sign-out-2 # optional: URLs to skip during the authenticated scan; comma-separated, no spaces in between - # - # Perform ZAP Full Scan, which includes both passive and active scanning: - # DAST_FULL_SCAN_ENABLED: "true" + # URL to scan: + # DAST_WEBSITE: https://example.com/ + # + # Time limit for target availability (scan is attempted even when timeout): + # DAST_TARGET_AVAILABILITY_TIMEOUT: 60 + # + # Set these variables to scan with an authenticated user: + # DAST_AUTH_URL: https://example.com/sign-in + # DAST_USERNAME: john.doe@example.com + # DAST_PASSWORD: john-doe-password + # DAST_USERNAME_FIELD: session[user] # the name of username field at the sign-in HTML form + # DAST_PASSWORD_FIELD: session[password] # the name of password field at the sign-in HTML form + # DAST_AUTH_EXCLUDE_URLS: http://example.com/sign-out,http://example.com/sign-out-2 # optional: URLs to skip during the authenticated scan; comma-separated, no spaces in between + # + # Perform ZAP Full Scan, which includes both passive and active scanning: + # DAST_FULL_SCAN_ENABLED: "true" allow_failure: true script: - export DAST_WEBSITE=${DAST_WEBSITE:-$(cat environment_url.txt)} diff --git a/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml index 0208beb35b8..b6555e627a1 100644 --- a/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml @@ -1,7 +1,7 @@ # Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/license_management.html variables: - LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager. + LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager. license_management: stage: test diff --git a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml index 9aa4abd4791..ffed7a0fec2 100644 --- a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml @@ -25,7 +25,7 @@ archive_project: - master artifacts: paths: - - build/ProjectName.ipa + - build/ProjectName.ipa tags: - ios_11-3 - xcode_9-3 diff --git a/lib/gitlab/ci/templates/dotNET-Core.yml b/lib/gitlab/ci/templates/dotNET-Core.yml index 708b75f83e8..40ca296d7bd 100644 --- a/lib/gitlab/ci/templates/dotNET-Core.yml +++ b/lib/gitlab/ci/templates/dotNET-Core.yml @@ -1,4 +1,5 @@ -# This is a simple example illustrating how to build and test .NET Core project +--- +# This is a simple example illustrating how to build and test .NET Core project # with GitLab Continuous Integration / Continuous Delivery. # ### Specify the Docker image @@ -17,21 +18,21 @@ image: microsoft/dotnet:latest # ### Define variables # variables: - # 1) Name of directory where restore and build objects are stored. - OBJECTS_DIRECTORY: 'obj' - # 2) Name of directory used for keeping restored dependencies. - NUGET_PACKAGES_DIRECTORY: '.nuget' - # 3) A relative path to the source code from project repository root. - # NOTE: Please edit this path so it matches the structure of your project! - SOURCE_CODE_PATH: '*/*/' + # 1) Name of directory where restore and build objects are stored. + OBJECTS_DIRECTORY: 'obj' + # 2) Name of directory used for keeping restored dependencies. + NUGET_PACKAGES_DIRECTORY: '.nuget' + # 3) A relative path to the source code from project repository root. + # NOTE: Please edit this path so it matches the structure of your project! + SOURCE_CODE_PATH: '*/*/' # ### Define stage list # # In this example there are only two stages. # Initially, the project will be built and then tested. stages: - - build - - test + - build + - test # ### Define global cache rule # @@ -50,31 +51,31 @@ stages: # With global cache rule, cached dependencies will be downloaded before every job # and then unpacked to the paths as specified below. cache: - # Per-stage and per-branch caching. - key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG" - paths: - # Specify three paths that should be cached: + # Per-stage and per-branch caching. + key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG" + paths: + # Specify three paths that should be cached: + # + # 1) Main JSON file holding information about package dependency tree, packages versions, + # frameworks etc. It also holds information where to the dependencies were restored. + - '$SOURCE_CODE_PATH$OBJECTS_DIRECTORY/project.assets.json' + # 2) Other NuGet and MSBuild related files. Also needed. + - '$SOURCE_CODE_PATH$OBJECTS_DIRECTORY/*.csproj.nuget.*' + # 3) Path to the directory where restored dependencies are kept. + - '$NUGET_PACKAGES_DIRECTORY' # - # 1) Main JSON file holding information about package dependency tree, packages versions, - # frameworks etc. It also holds information where to the dependencies were restored. - - '$SOURCE_CODE_PATH$OBJECTS_DIRECTORY/project.assets.json' - # 2) Other NuGet and MSBuild related files. Also needed. - - '$SOURCE_CODE_PATH$OBJECTS_DIRECTORY/*.csproj.nuget.*' - # 3) Path to the directory where restored dependencies are kept. - - '$NUGET_PACKAGES_DIRECTORY' - # - # 'pull-push' policy means that latest cache will be downloaded (if it exists) - # before executing the job, and a newer version will be uploaded afterwards. - # Such a setting saves time when there are no changes in referenced third-party - # packages. - # - # For example, if you run a pipeline with changes in your code, - # but with no changes within third-party packages which your project is using, - # then project restore will happen quickly as all required dependencies - # will already be there — unzipped from cache. + # 'pull-push' policy means that latest cache will be downloaded (if it exists) + # before executing the job, and a newer version will be uploaded afterwards. + # Such a setting saves time when there are no changes in referenced third-party + # packages. + # + # For example, if you run a pipeline with changes in your code, + # but with no changes within third-party packages which your project is using, + # then project restore will happen quickly as all required dependencies + # will already be there — unzipped from cache. - # 'pull-push' policy is the default cache policy, you do not have to specify it explicitly. - policy: pull-push + # 'pull-push' policy is the default cache policy, you do not have to specify it explicitly. + policy: pull-push # ### Restore project dependencies # @@ -87,30 +88,30 @@ cache: # # Learn more about GitLab cache: https://docs.gitlab.com/ee/ci/caching/index.html before_script: - - 'dotnet restore --packages $NUGET_PACKAGES_DIRECTORY' + - 'dotnet restore --packages $NUGET_PACKAGES_DIRECTORY' build: - stage: build - # ### Build all projects discovered from solution file. - # - # Note: this will fail if you have any projects in your solution that are not - # .NET Core-based projects (e.g. WCF service), which is based on .NET Framework, - # not .NET Core. In this scenario, you will need to build every .NET Core-based - # project by explicitly specifying a relative path to the directory - # where it is located (e.g. 'dotnet build ./src/ConsoleApp'). - # Only one project path can be passed as a parameter to 'dotnet build' command. - script: - - 'dotnet build --no-restore' + stage: build + # ### Build all projects discovered from solution file. + # + # Note: this will fail if you have any projects in your solution that are not + # .NET Core-based projects (e.g. WCF service), which is based on .NET Framework, + # not .NET Core. In this scenario, you will need to build every .NET Core-based + # project by explicitly specifying a relative path to the directory + # where it is located (e.g. 'dotnet build ./src/ConsoleApp'). + # Only one project path can be passed as a parameter to 'dotnet build' command. + script: + - 'dotnet build --no-restore' tests: - stage: test - # ### Run the tests - # - # You can either run tests for all test projects that are defined in your solution - # with 'dotnet test' or run tests only for specific project by specifying - # a relative path to the directory where it is located (e.g. 'dotnet test ./test/UnitTests'). - # - # You may want to define separate testing jobs for different types of testing - # (e.g. integration tests, unit tests etc). - script: - - 'dotnet test --no-restore' + stage: test + # ### Run the tests + # + # You can either run tests for all test projects that are defined in your solution + # with 'dotnet test' or run tests only for specific project by specifying + # a relative path to the directory where it is located (e.g. 'dotnet test ./test/UnitTests'). + # + # You may want to define separate testing jobs for different types of testing + # (e.g. integration tests, unit tests etc). + script: + - 'dotnet test --no-restore' diff --git a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml index 5e128b793d0..b29f45323f5 100644 --- a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml @@ -3,12 +3,12 @@ # (located in gitlab-runner.exe directory): # shell = "powershell" # -# The script is composed of 3 stages: build, test and deploy. +# The script is composed of 3 stages: build, test and deploy. # # The build stage restores NuGet packages and uses msbuild to build the exe and msi # One major issue you'll find is that you can't build msi projects from command line # if you use vdproj. There are workarounds building msi via devenv, but they rarely work -# The best solution is migrating your vdproj projects to WiX, as it can be build directly +# The best solution is migrating your vdproj projects to WiX, as it can be build directly # by msbuild. # # The test stage runs nunit from command line against Test project inside your solution @@ -38,28 +38,28 @@ stages: build_job: stage: build only: - - tags # the build process will only be started by git tag commits + - tags # the build process will only be started by git tag commits script: - - '& "$env:NUGET_PATH" restore' # restore Nuget dependencies - - '& "$env:MSBUILD_PATH" /p:Configuration=Release' # build the project + - '& "$env:NUGET_PATH" restore' # restore Nuget dependencies + - '& "$env:MSBUILD_PATH" /p:Configuration=Release' # build the project artifacts: - expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on + expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on paths: - - '$env:EXE_RELEASE_FOLDER\YourApp.exe' # saving exe to copy to deploy folder - - '$env:MSI_RELEASE_FOLDER\YourApp Setup.msi' # saving msi to copy to deploy folder - - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests + - '$env:EXE_RELEASE_FOLDER\YourApp.exe' # saving exe to copy to deploy folder + - '$env:MSI_RELEASE_FOLDER\YourApp Setup.msi' # saving msi to copy to deploy folder + - '$env:TEST_FOLDER\' # saving entire Test project so NUnit can run tests test_job: stage: test only: - tags script: - - '& "$env:NUNIT_PATH" ".\$env:TEST_FOLDER\Tests.dll"' # running NUnit tests + - '& "$env:NUNIT_PATH" ".\$env:TEST_FOLDER\Tests.dll"' # running NUnit tests artifacts: - when: always # save test results even when the task fails - expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on + when: always # save test results even when the task fails + expire_in: 1 week # save gitlab server space, we copy the files we need to deploy folder later on paths: - - '.\TestResult.xml' # saving NUnit results to copy to deploy folder + - '.\TestResult.xml' # saving NUnit results to copy to deploy folder dependencies: - build_job @@ -79,7 +79,6 @@ deploy_job: - 'xcopy /y ".\$env:EXE_RELEASE_FOLDER\YourApp.exe" "$deployFolder"' - 'xcopy /y ".\$env:MSI_RELEASE_FOLDER\YourApp Setup.msi" "$deployFolder"' - 'xcopy /y ".\TestResult.xml" "$deployFolder"' - dependencies: - build_job - test_job diff --git a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml index a4a9e96c1d2..87aea8527d1 100644 --- a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml @@ -1,13 +1,13 @@ # This is a very simple template that mainly relies on FastLane to build and distribute your app. # Read more about how to use this template on the blog post https://about.gitlab.com/2019/03/06/ios-publishing-with-gitlab-and-fastlane/ -# You will also need fastlane and signing configuration for this to work, along with a MacOS runner. +# You will also need fastlane and signing configuration for this to work, along with a MacOS runner. # These details are provided in the blog post. # Note that when you're using the shell executor for MacOS builds, the # build and tests run as the identity of the runner logged in user, directly on # the build host. This is less secure than using container executors, so please -# take a look at our security implications documentation at -# https://docs.gitlab.com/runner/security/#usage-of-shell-executor for additional +# take a look at our security implications documentation at +# https://docs.gitlab.com/runner/security/#usage-of-shell-executor for additional # detail on what to keep in mind in this scenario. stages: @@ -27,4 +27,4 @@ build: - bundle exec fastlane build artifacts: paths: - - ./*.ipa + - ./*.ipa diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index dcdd3581d92..8a2e711ec4e 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -282,6 +282,11 @@ module Gitlab (size.to_f / 1024).round(2) end + # Return git object directory size in bytes + def object_directory_size + gitaly_repository_client.get_object_directory_size.to_f * 1024 + end + # Build an array of commits. # # Usage. diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb index 68b17e86608..d8e9dccb644 100644 --- a/lib/gitlab/gitaly_client/repository_service.rb +++ b/lib/gitlab/gitaly_client/repository_service.rb @@ -47,6 +47,13 @@ module Gitlab response.size end + def get_object_directory_size + request = Gitaly::GetObjectDirectorySizeRequest.new(repository: @gitaly_repo) + response = GitalyClient.call(@storage, :repository_service, :get_object_directory_size, request, timeout: GitalyClient.medium_timeout) + + response.size + end + def apply_gitattributes(revision) request = Gitaly::ApplyGitattributesRequest.new(repository: @gitaly_repo, revision: encode_binary(revision)) GitalyClient.call(@storage, :repository_service, :apply_gitattributes, request, timeout: GitalyClient.fast_timeout) diff --git a/lib/gitlab/graphql/representation/tree_entry.rb b/lib/gitlab/graphql/representation/tree_entry.rb new file mode 100644 index 00000000000..7ea83db5876 --- /dev/null +++ b/lib/gitlab/graphql/representation/tree_entry.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module Representation + class TreeEntry < SimpleDelegator + class << self + def decorate(entries, repository) + return if entries.nil? + + entries.map do |entry| + if entry.is_a?(TreeEntry) + entry + else + self.new(entry, repository) + end + end + end + end + + attr_accessor :repository + + def initialize(raw_entry, repository) + @repository = repository + + super(raw_entry) + end + end + end + end +end diff --git a/lib/gitlab/markdown_cache.rb b/lib/gitlab/markdown_cache.rb new file mode 100644 index 00000000000..0354c710a3f --- /dev/null +++ b/lib/gitlab/markdown_cache.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Gitlab + module MarkdownCache + # Increment this number every time the renderer changes its output + CACHE_COMMONMARK_VERSION_START = 10 + CACHE_COMMONMARK_VERSION = 16 + + BaseError = Class.new(StandardError) + UnsupportedClassError = Class.new(BaseError) + end +end diff --git a/lib/gitlab/markdown_cache/active_record/extension.rb b/lib/gitlab/markdown_cache/active_record/extension.rb new file mode 100644 index 00000000000..f3abe631779 --- /dev/null +++ b/lib/gitlab/markdown_cache/active_record/extension.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Gitlab + module MarkdownCache + module ActiveRecord + module Extension + extend ActiveSupport::Concern + + included do + # Using before_update here conflicts with elasticsearch-model somehow + before_create :refresh_markdown_cache, if: :invalidated_markdown_cache? + before_update :refresh_markdown_cache, if: :invalidated_markdown_cache? + end + + # Always exclude _html fields from attributes (including serialization). + # They contain unredacted HTML, which would be a security issue + def attributes + attrs = super + html_fields = cached_markdown_fields.html_fields + whitelisted = cached_markdown_fields.html_fields_whitelisted + exclude_fields = html_fields - whitelisted + + attrs.except!(*exclude_fields) + attrs.delete('cached_markdown_version') if whitelisted.empty? + + attrs + end + + def changed_markdown_fields + changed_attributes.keys.map(&:to_s) & cached_markdown_fields.markdown_fields.map(&:to_s) + end + + def write_markdown_field(field_name, value) + write_attribute(field_name, value) + end + + def markdown_field_changed?(field_name) + attribute_changed?(field_name) + end + + def save_markdown(updates) + return unless persisted? && Gitlab::Database.read_write? + + update_columns(updates) + end + end + end + end +end diff --git a/lib/gitlab/markdown_cache/field_data.rb b/lib/gitlab/markdown_cache/field_data.rb new file mode 100644 index 00000000000..14622c0f186 --- /dev/null +++ b/lib/gitlab/markdown_cache/field_data.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Gitlab + module MarkdownCache + # Knows about the relationship between markdown and html field names, and + # stores the rendering contexts for the latter + class FieldData + def initialize + @data = {} + end + + delegate :[], :[]=, to: :@data + + def markdown_fields + @data.keys + end + + def html_field(markdown_field) + "#{markdown_field}_html" + end + + def html_fields + @html_fields ||= markdown_fields.map { |field| html_field(field) } + end + + def html_fields_whitelisted + markdown_fields.each_with_object([]) do |field, fields| + if @data[field].fetch(:whitelisted, false) + fields << html_field(field) + end + end + end + end + end +end diff --git a/lib/gitlab/markdown_cache/redis/extension.rb b/lib/gitlab/markdown_cache/redis/extension.rb new file mode 100644 index 00000000000..97fc23343b4 --- /dev/null +++ b/lib/gitlab/markdown_cache/redis/extension.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Gitlab + module MarkdownCache + module Redis + module Extension + extend ActiveSupport::Concern + + attr_reader :cached_markdown_version + + class_methods do + def cache_markdown_field(markdown_field, context = {}) + super + + # define the `[field]_html` accessor + html_field = cached_markdown_fields.html_field(markdown_field) + define_method(html_field) do + load_cached_markdown unless markdown_data_loaded? + + instance_variable_get("@#{html_field}") + end + end + end + + private + + def save_markdown(updates) + markdown_store.save(updates) + end + + def write_markdown_field(field_name, value) + instance_variable_set("@#{field_name}", value) + end + + def markdown_field_changed?(field_name) + false + end + + def changed_markdown_fields + [] + end + + def cached_markdown + @cached_data ||= markdown_store.read + end + + def load_cached_markdown + cached_markdown.each do |field_name, value| + write_markdown_field(field_name, value) + end + end + + def markdown_data_loaded? + cached_markdown_version.present? || markdown_store.loaded? + end + + def markdown_store + @store ||= Gitlab::MarkdownCache::Redis::Store.new(self) + end + end + end + end +end diff --git a/lib/gitlab/markdown_cache/redis/store.rb b/lib/gitlab/markdown_cache/redis/store.rb new file mode 100644 index 00000000000..0f954404808 --- /dev/null +++ b/lib/gitlab/markdown_cache/redis/store.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +module Gitlab + module MarkdownCache + module Redis + class Store + EXPIRES_IN = 1.day + + def initialize(subject) + @subject = subject + @loaded = false + end + + def save(updates) + @loaded = false + + Gitlab::Redis::Cache.with do |r| + r.mapped_hmset(markdown_cache_key, updates) + r.expire(markdown_cache_key, EXPIRES_IN) + end + end + + def read + @loaded = true + + results = Gitlab::Redis::Cache.with do |r| + r.mapped_hmget(markdown_cache_key, *fields) + end + # The value read from redis is a string, so we're converting it back + # to an int. + results[:cached_markdown_version] = results[:cached_markdown_version].to_i + results + end + + def loaded? + @loaded + end + + private + + def fields + @fields ||= @subject.cached_markdown_fields.html_fields + [:cached_markdown_version] + end + + def markdown_cache_key + unless @subject.respond_to?(:cache_key) + raise Gitlab::MarkdownCache::UnsupportedClassError, + "This class has no cache_key to use for caching" + end + + "markdown_cache:#{@subject.cache_key}" + end + end + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6338441e141..39e18147311 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3883,7 +3883,7 @@ msgstr "" msgid "Enter the merge request title" msgstr "" -msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they will be masked by default so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want." +msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. Additionally, they can be masked so they are hidden in job logs, though they must match certain regexp requirements to do so. You can use environment variables for passwords, secret keys, or whatever you want." msgstr "" msgid "Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default" diff --git a/spec/features/markdown/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb index 6997ca48427..8fda3c7193e 100644 --- a/spec/features/markdown/gitlab_flavored_markdown_spec.rb +++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb @@ -20,8 +20,7 @@ describe "GitLab Flavored Markdown" do let(:commit) { project.commit } before do - allow_any_instance_of(Commit).to receive(:title) - .and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details") + create_commit("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details", project, user, 'master') end it "renders title in commits#index" do diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js index 6b4508c418e..a70dc7bb866 100644 --- a/spec/frontend/repository/components/table/row_spec.js +++ b/spec/frontend/repository/components/table/row_spec.js @@ -86,4 +86,16 @@ describe('Repository table row component', () => { expect(vm.find('.commit-sha').text()).toContain('1'); }); + + it('renders link with href', () => { + factory({ + id: '1', + path: 'test', + type: 'blob', + url: 'https://test.com', + currentPath: '/', + }); + + expect(vm.find('a').attributes('href')).toEqual('https://test.com'); + }); }); diff --git a/spec/graphql/types/tree/blob_type_spec.rb b/spec/graphql/types/tree/blob_type_spec.rb index fa29bb5fff7..b12e214ca84 100644 --- a/spec/graphql/types/tree/blob_type_spec.rb +++ b/spec/graphql/types/tree/blob_type_spec.rb @@ -5,5 +5,5 @@ require 'spec_helper' describe Types::Tree::BlobType do it { expect(described_class.graphql_name).to eq('Blob') } - it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path) } + it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url) } end diff --git a/spec/graphql/types/tree/tree_entry_type_spec.rb b/spec/graphql/types/tree/tree_entry_type_spec.rb index 397cabde8e5..ea1b6426034 100644 --- a/spec/graphql/types/tree/tree_entry_type_spec.rb +++ b/spec/graphql/types/tree/tree_entry_type_spec.rb @@ -5,5 +5,5 @@ require 'spec_helper' describe Types::Tree::TreeEntryType do it { expect(described_class.graphql_name).to eq('TreeEntry') } - it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path) } + it { expect(described_class).to have_graphql_fields(:id, :name, :type, :path, :flat_path, :web_url) } end diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb index c3956ba08fd..597c8f836a9 100644 --- a/spec/helpers/markup_helper_spec.rb +++ b/spec/helpers/markup_helper_spec.rb @@ -78,7 +78,8 @@ describe MarkupHelper do let(:link) { '/commits/0a1b2c3d' } let(:issues) { create_list(:issue, 2, project: project) } - it 'handles references nested in links with all the text' do + # Clean the cache to make sure the title is re-rendered from the stubbed one + it 'handles references nested in links with all the text', :clean_gitlab_redis_cache do allow(commit).to receive(:title).and_return("This should finally fix #{issues[0].to_reference} and #{issues[1].to_reference} for real") actual = helper.link_to_markdown_field(commit, :title, link) diff --git a/spec/helpers/tracking_helper_spec.rb b/spec/helpers/tracking_helper_spec.rb new file mode 100644 index 00000000000..71505e8ea69 --- /dev/null +++ b/spec/helpers/tracking_helper_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe TrackingHelper do + describe '#tracking_attrs' do + it 'returns an empty hash' do + expect(helper.tracking_attrs('a', 'b', 'c')).to eq({}) + end + end +end diff --git a/spec/lib/banzai/commit_renderer_spec.rb b/spec/lib/banzai/commit_renderer_spec.rb index 1f53657c59c..316dbf052c3 100644 --- a/spec/lib/banzai/commit_renderer_spec.rb +++ b/spec/lib/banzai/commit_renderer_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Banzai::CommitRenderer do - describe '.render' do + describe '.render', :clean_gitlab_redis_cache do it 'renders a commit description and title' do user = build(:user) project = create(:project, :repository) @@ -13,7 +13,7 @@ describe Banzai::CommitRenderer do described_class::ATTRIBUTES.each do |attr| expect_any_instance_of(Banzai::ObjectRenderer).to receive(:render).with([project.commit], attr).once.and_call_original - expect(Banzai::Renderer).to receive(:cacheless_render_field).with(project.commit, attr, {}) + expect(Banzai::Renderer).to receive(:cacheless_render_field).with(project.commit, attr, { skip_project_check: false }).and_call_original end described_class.render([project.commit], project, user) diff --git a/spec/lib/banzai/object_renderer_spec.rb b/spec/lib/banzai/object_renderer_spec.rb index 3b52f6666d0..7b855251a74 100644 --- a/spec/lib/banzai/object_renderer_spec.rb +++ b/spec/lib/banzai/object_renderer_spec.rb @@ -11,7 +11,7 @@ describe Banzai::ObjectRenderer do ) end - let(:object) { Note.new(note: 'hello', note_html: '<p dir="auto">hello</p>', cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION << 16) } + let(:object) { Note.new(note: 'hello', note_html: '<p dir="auto">hello</p>', cached_markdown_version: Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16) } describe '#render' do context 'with cache' do @@ -60,24 +60,38 @@ describe Banzai::ObjectRenderer do end context 'without cache' do - let(:commit) { project.commit } + let(:cacheless_class) do + Class.new do + attr_accessor :title, :redacted_title_html, :project + + def banzai_render_context(field) + { project: project, pipeline: :single_line } + end + end + end + let(:cacheless_thing) do + cacheless_class.new.tap do |thing| + thing.title = "Merge branch 'branch-merged' into 'master'" + thing.project = project + end + end it 'renders and redacts an Array of objects' do - renderer.render([commit], :title) + renderer.render([cacheless_thing], :title) - expect(commit.redacted_title_html).to eq("Merge branch 'branch-merged' into 'master'") + expect(cacheless_thing.redacted_title_html).to eq("Merge branch 'branch-merged' into 'master'") end it 'calls Banzai::Redactor to perform redaction' do expect_any_instance_of(Banzai::Redactor).to receive(:redact).and_call_original - renderer.render([commit], :title) + renderer.render([cacheless_thing], :title) end it 'retrieves field content using Banzai::Renderer.cacheless_render_field' do - expect(Banzai::Renderer).to receive(:cacheless_render_field).with(commit, :title, {}).and_call_original + expect(Banzai::Renderer).to receive(:cacheless_render_field).with(cacheless_thing, :title, {}).and_call_original - renderer.render([commit], :title) + renderer.render([cacheless_thing], :title) end end end diff --git a/spec/lib/banzai/renderer_spec.rb b/spec/lib/banzai/renderer_spec.rb index 650cecfc778..aa828e2f0e9 100644 --- a/spec/lib/banzai/renderer_spec.rb +++ b/spec/lib/banzai/renderer_spec.rb @@ -11,16 +11,24 @@ describe Banzai::Renderer do object end + def fake_cacheless_object + object = double('cacheless object') + + allow(object).to receive(:respond_to?).with(:cached_markdown_fields).and_return(false) + + object + end + describe '#render_field' do let(:renderer) { described_class } context 'without cache' do - let(:commit) { create(:project, :repository).commit } + let(:commit) { fake_cacheless_object } it 'returns cacheless render field' do - expect(renderer).to receive(:cacheless_render_field).with(commit, :title, {}) + expect(renderer).to receive(:cacheless_render_field).with(commit, :field, {}) - renderer.render_field(commit, :title) + renderer.render_field(commit, :field) end end diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index cb4701e8edc..e72fb9c6fbc 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -186,6 +186,18 @@ describe Gitlab::Git::Repository, :seed_helper do it { is_expected.to be < 2 } end + describe '#object_directory_size' do + before do + allow(repository.gitaly_repository_client) + .to receive(:get_object_directory_size) + .and_return(2) + end + + subject { repository.object_directory_size } + + it { is_expected.to eq 2048 } + end + describe '#empty?' do it { expect(repository).not_to be_empty } end diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb index 09de7ca6afd..a3808adb376 100644 --- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb @@ -73,6 +73,17 @@ describe Gitlab::GitalyClient::RepositoryService do end end + describe '#get_object_directory_size' do + it 'sends a get_object_directory_size message' do + expect_any_instance_of(Gitaly::RepositoryService::Stub) + .to receive(:get_object_directory_size) + .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash)) + .and_return(size: 0) + + client.get_object_directory_size + end + end + describe '#apply_gitattributes' do let(:revision) { 'master' } diff --git a/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb b/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb new file mode 100644 index 00000000000..d45e690160c --- /dev/null +++ b/spec/lib/gitlab/graphql/representation/tree_entry_spec.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Graphql::Representation::TreeEntry do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + + describe '.decorate' do + it 'returns NilClass when given nil' do + expect(described_class.decorate(nil, repository)).to be_nil + end + + it 'returns array of TreeEntry' do + entries = described_class.decorate(repository.tree.blobs, repository) + + expect(entries.first).to be_a(described_class) + end + end +end diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb new file mode 100644 index 00000000000..18052b1991c --- /dev/null +++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb @@ -0,0 +1,179 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::MarkdownCache::ActiveRecord::Extension do + let(:klass) do + Class.new(ActiveRecord::Base) do + self.table_name = 'issues' + include CacheMarkdownField + cache_markdown_field :title, whitelisted: true + cache_markdown_field :description, pipeline: :single_line + + attr_accessor :author, :project + end + end + + let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } + let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + + let(:markdown) { '`Foo`' } + let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' } + + let(:updated_markdown) { '`Bar`' } + let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' } + + context 'an unchanged markdown field' do + let(:thing) { klass.new(title: markdown) } + + before do + thing.title = thing.title + thing.save + end + + it { expect(thing.title).to eq(markdown) } + it { expect(thing.title_html).to eq(html) } + it { expect(thing.title_html_changed?).not_to be_truthy } + it { expect(thing.cached_markdown_version).to eq(cache_version) } + end + + context 'a changed markdown field' do + let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + + before do + thing.title = updated_markdown + thing.save + end + + it { expect(thing.title_html).to eq(updated_html) } + it { expect(thing.cached_markdown_version).to eq(cache_version) } + end + + context 'when a markdown field is set repeatedly to an empty string' do + it do + expect(thing).to receive(:refresh_markdown_cache).once + thing.title = '' + thing.save + thing.title = '' + thing.save + end + end + + context 'when a markdown field is set repeatedly to a string which renders as empty html' do + it do + expect(thing).to receive(:refresh_markdown_cache).once + thing.title = '[//]: # (This is also a comment.)' + thing.save + thing.title = '[//]: # (This is also a comment.)' + thing.save + end + end + + context 'a non-markdown field changed' do + let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + + before do + thing.state = 'closed' + thing.save + end + + it { expect(thing.state).to eq('closed') } + it { expect(thing.title).to eq(markdown) } + it { expect(thing.title_html).to eq(html) } + it { expect(thing.cached_markdown_version).to eq(cache_version) } + end + + context 'version is out of date' do + let(:thing) { klass.new(title: updated_markdown, title_html: html, cached_markdown_version: nil) } + + before do + thing.save + end + + it { expect(thing.title_html).to eq(updated_html) } + it { expect(thing.cached_markdown_version).to eq(cache_version) } + end + + context 'when an invalidating field is changed' do + it 'invalidates the cache when project changes' do + thing.project = :new_project + allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) + + thing.save + + expect(thing.title_html).to eq(updated_html) + expect(thing.description_html).to eq(updated_html) + expect(thing.cached_markdown_version).to eq(cache_version) + end + + it 'invalidates the cache when author changes' do + thing.author = :new_author + allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) + + thing.save + + expect(thing.title_html).to eq(updated_html) + expect(thing.description_html).to eq(updated_html) + expect(thing.cached_markdown_version).to eq(cache_version) + end + end + + describe '.attributes' do + it 'excludes cache attributes that is blacklisted by default' do + expect(thing.attributes.keys.sort).not_to include(%w[description_html]) + end + end + + describe '#cached_html_up_to_date?' do + let(:thing) { klass.create(title: updated_markdown, title_html: html, cached_markdown_version: nil) } + subject { thing.cached_html_up_to_date?(:title) } + + it 'returns false if markdown has been changed but html has not' do + thing.title = "changed!" + + is_expected.to be_falsy + end + + it 'returns true if markdown has not been changed but html has' do + thing.title_html = updated_html + + is_expected.to be_truthy + end + + it 'returns true if markdown and html have both been changed' do + thing.title = updated_markdown + thing.title_html = updated_html + + is_expected.to be_truthy + end + + it 'returns false if the markdown field is set but the html is not' do + thing.title_html = nil + + is_expected.to be_falsy + end + end + + describe '#refresh_markdown_cache!' do + before do + thing.title = updated_markdown + end + + it 'skips saving if not persisted' do + expect(thing).to receive(:persisted?).and_return(false) + expect(thing).not_to receive(:update_columns) + + thing.refresh_markdown_cache! + end + + it 'saves the changes' do + expect(thing).to receive(:persisted?).and_return(true) + + expect(thing).to receive(:update_columns) + .with("title_html" => updated_html, + "description_html" => "", + "cached_markdown_version" => cache_version) + + thing.refresh_markdown_cache! + end + end +end diff --git a/spec/lib/gitlab/markdown_cache/field_data_spec.rb b/spec/lib/gitlab/markdown_cache/field_data_spec.rb new file mode 100644 index 00000000000..393bf85aa43 --- /dev/null +++ b/spec/lib/gitlab/markdown_cache/field_data_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +describe Gitlab::MarkdownCache::FieldData do + subject(:field_data) { described_class.new } + + before do + field_data[:description] = { project: double('project in context') } + end + + it 'translates a markdown field name into a html field name' do + expect(field_data.html_field(:description)).to eq("description_html") + end +end diff --git a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb new file mode 100644 index 00000000000..b6a781de426 --- /dev/null +++ b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::MarkdownCache::Redis::Extension, :clean_gitlab_redis_cache do + let(:klass) do + Class.new do + include CacheMarkdownField + + def initialize(title: nil, description: nil) + @title, @description = title, description + end + + attr_reader :title, :description + + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description + + def cache_key + "cache-key" + end + end + end + + let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } + let(:thing) { klass.new(title: "`Hello`", description: "`World`") } + let(:expected_cache_key) { "markdown_cache:cache-key" } + + it 'defines the html attributes' do + expect(thing).to respond_to(:title_html, :description_html, :cached_markdown_version) + end + + it 'loads the markdown from the cache only once' do + expect(thing).to receive(:load_cached_markdown).once.and_call_original + + thing.title_html + thing.description_html + end + + it 'correctly loads the markdown if it was stored in redis' do + Gitlab::Redis::Cache.with do |r| + r.mapped_hmset(expected_cache_key, + title_html: 'hello', + description_html: 'world', + cached_markdown_version: cache_version) + end + + expect(thing.title_html).to eq('hello') + expect(thing.description_html).to eq('world') + expect(thing.cached_markdown_version).to eq(cache_version) + end + + describe "#refresh_markdown_cache!" do + it "stores the value in redis" do + expected_results = { "title_html" => "`Hello`", + "description_html" => "<p data-sourcepos=\"1:1-1:7\" dir=\"auto\"><code>World</code></p>", + "cached_markdown_version" => cache_version.to_s } + + thing.refresh_markdown_cache! + + results = Gitlab::Redis::Cache.with do |r| + r.mapped_hmget(expected_cache_key, + "title_html", "description_html", "cached_markdown_version") + end + + expect(results).to eq(expected_results) + end + + it "assigns the values" do + thing.refresh_markdown_cache! + + expect(thing.title_html).to eq('`Hello`') + expect(thing.description_html).to eq("<p data-sourcepos=\"1:1-1:7\" dir=\"auto\"><code>World</code></p>") + expect(thing.cached_markdown_version).to eq(cache_version) + end + end +end diff --git a/spec/lib/gitlab/markdown_cache/redis/store_spec.rb b/spec/lib/gitlab/markdown_cache/redis/store_spec.rb new file mode 100644 index 00000000000..95c68e7d491 --- /dev/null +++ b/spec/lib/gitlab/markdown_cache/redis/store_spec.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true +require 'spec_helper' + +describe Gitlab::MarkdownCache::Redis::Store, :clean_gitlab_redis_cache do + let(:storable_class) do + Class.new do + cattr_reader :cached_markdown_fields do + Gitlab::MarkdownCache::FieldData.new.tap do |field_data| + field_data[:field_1] = {} + field_data[:field_2] = {} + end + end + + attr_accessor :field_1, :field_2, :field_1_html, :field_2_html, :cached_markdown_version + + def cache_key + "cache-key" + end + end + end + let(:storable) { storable_class.new } + let(:cache_key) { "markdown_cache:#{storable.cache_key}" } + + subject(:store) { described_class.new(storable) } + + def read_values + Gitlab::Redis::Cache.with do |r| + r.mapped_hmget(cache_key, + :field_1_html, :field_2_html, :cached_markdown_version) + end + end + + def store_values(values) + Gitlab::Redis::Cache.with do |r| + r.mapped_hmset(cache_key, + values) + end + end + + describe '#save' do + it 'stores updates to html fields and version' do + values_to_store = { field_1_html: "hello", field_2_html: "world", cached_markdown_version: 1 } + + store.save(values_to_store) + + expect(read_values) + .to eq({ field_1_html: "hello", field_2_html: "world", cached_markdown_version: "1" }) + end + end + + describe '#read' do + it 'reads the html fields and version from redis if they were stored' do + stored_values = { field_1_html: "hello", field_2_html: "world", cached_markdown_version: 1 } + + store_values(stored_values) + + expect(store.read.symbolize_keys).to eq(stored_values) + end + + it 'is mared loaded after reading' do + expect(store).not_to be_loaded + + store.read + + expect(store).to be_loaded + end + end +end diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb index 78637ff10c6..0e5fb2b5153 100644 --- a/spec/models/concerns/cache_markdown_field_spec.rb +++ b/spec/models/concerns/cache_markdown_field_spec.rb @@ -2,383 +2,213 @@ require 'spec_helper' -describe CacheMarkdownField do - # The minimum necessary ActiveModel to test this concern - class ThingWithMarkdownFields - include ActiveModel::Model - include ActiveModel::Dirty - - include ActiveModel::Serialization - - class_attribute :attribute_names - self.attribute_names = [] - - def attributes - attribute_names.each_with_object({}) do |name, hsh| - hsh[name.to_s] = send(name) - end +describe CacheMarkdownField, :clean_gitlab_redis_cache do + let(:ar_class) do + Class.new(ActiveRecord::Base) do + self.table_name = 'issues' + include CacheMarkdownField + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description end + end - extend ActiveModel::Callbacks - define_model_callbacks :create, :update - - include CacheMarkdownField - cache_markdown_field :foo - cache_markdown_field :baz, pipeline: :single_line - cache_markdown_field :zoo, whitelisted: true + let(:other_class) do + Class.new do + include CacheMarkdownField - def self.add_attr(name) - self.attribute_names += [name] - define_attribute_methods(name) - attr_reader(name) - define_method("#{name}=") do |value| - write_attribute(name, value) + def initialize(args = {}) + @title, @description, @cached_markdown_version = args[:title], args[:description], args[:cached_markdown_version] + @title_html, @description_html = args[:title_html], args[:description_html] + @author, @project = args[:author], args[:project] end - end - add_attr :cached_markdown_version + attr_accessor :title, :description, :cached_markdown_version - [:foo, :foo_html, :bar, :baz, :baz_html, :zoo, :zoo_html].each do |name| - add_attr(name) - end - - def initialize(*) - super - - # Pretend new is load - clear_changes_information - end - - def read_attribute(name) - instance_variable_get("@#{name}") - end - - def write_attribute(name, value) - send("#{name}_will_change!") unless value == read_attribute(name) - instance_variable_set("@#{name}", value) - end + cache_markdown_field :title, pipeline: :single_line + cache_markdown_field :description - def save - run_callbacks :update do - changes_applied + def cache_key + "cache-key" end end - - def has_attribute?(attr_name) - attribute_names.include?(attr_name) - end - end - - def thing_subclass(new_attr) - Class.new(ThingWithMarkdownFields) { add_attr(new_attr) } end let(:markdown) { '`Foo`' } - let(:html) { '<p dir="auto"><code>Foo</code></p>' } + let(:html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Foo</code></p>' } let(:updated_markdown) { '`Bar`' } - let(:updated_html) { '<p dir="auto"><code>Bar</code></p>' } - - let(:thing) { ThingWithMarkdownFields.new(foo: markdown, foo_html: html, cached_markdown_version: cache_version) } - let(:cache_version) { CacheMarkdownField::CACHE_COMMONMARK_VERSION << 16 } - - before do - stub_commonmark_sourcepos_disabled - end + let(:updated_html) { '<p data-sourcepos="1:1-1:5" dir="auto"><code>Bar</code></p>' } - describe '.attributes' do - it 'excludes cache attributes that is blacklisted by default' do - expect(thing.attributes.keys.sort).to eq(%w[bar baz cached_markdown_version foo zoo zoo_html]) - end - end - - context 'an unchanged markdown field' do - before do - thing.foo = thing.foo - thing.save - end + let(:cache_version) { Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } - it { expect(thing.foo).to eq(markdown) } - it { expect(thing.foo_html).to eq(html) } - it { expect(thing.foo_html_changed?).not_to be_truthy } - it { expect(thing.cached_markdown_version).to eq(cache_version) } + def thing_subclass(klass, extra_attribute) + Class.new(klass) { attr_accessor(extra_attribute) } end - context 'a changed markdown field' do - let(:thing) { ThingWithMarkdownFields.new(foo: markdown, foo_html: html, cached_markdown_version: cache_version - 1) } + shared_examples 'a class with cached markdown fields' do + describe '#cached_html_up_to_date?' do + let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } - before do - thing.foo = updated_markdown - thing.save - end + subject { thing.cached_html_up_to_date?(:title) } - it { expect(thing.foo_html).to eq(updated_html) } - it { expect(thing.cached_markdown_version).to eq(cache_version) } - end + it 'returns false when the version is absent' do + thing.cached_markdown_version = nil - context 'when a markdown field is set repeatedly to an empty string' do - it do - expect(thing).to receive(:refresh_markdown_cache).once - thing.foo = '' - thing.save - thing.foo = '' - thing.save - end - end - - context 'when a markdown field is set repeatedly to a string which renders as empty html' do - it do - expect(thing).to receive(:refresh_markdown_cache).once - thing.foo = '[//]: # (This is also a comment.)' - thing.save - thing.foo = '[//]: # (This is also a comment.)' - thing.save - end - end - - context 'when a markdown field and html field are both changed' do - it do - expect(thing).not_to receive(:refresh_markdown_cache) - thing.foo = '_look over there!_' - thing.foo_html = '<em>look over there!</em>' - thing.save - end - end - - context 'a non-markdown field changed' do - let(:thing) { ThingWithMarkdownFields.new(foo: markdown, foo_html: html, cached_markdown_version: cache_version - 1) } - - before do - thing.bar = 'OK' - thing.save - end - - it { expect(thing.bar).to eq('OK') } - it { expect(thing.foo).to eq(markdown) } - it { expect(thing.foo_html).to eq(html) } - it { expect(thing.cached_markdown_version).to eq(cache_version) } - end - - context 'version is out of date' do - let(:thing) { ThingWithMarkdownFields.new(foo: updated_markdown, foo_html: html, cached_markdown_version: nil) } - - before do - thing.save - end - - it { expect(thing.foo_html).to eq(updated_html) } - it { expect(thing.cached_markdown_version).to eq(cache_version) } - end - - describe '#cached_html_up_to_date?' do - let(:thing) { ThingWithMarkdownFields.new(foo: markdown, foo_html: html, cached_markdown_version: cache_version) } - - subject { thing.cached_html_up_to_date?(:foo) } - - it 'returns false when the version is absent' do - thing.cached_markdown_version = nil - - is_expected.to be_falsy - end - - it 'returns false when the cached version is too old' do - thing.cached_markdown_version = cache_version - 1 - - is_expected.to be_falsy - end - - it 'returns false when the cached version is in future' do - thing.cached_markdown_version = cache_version + 1 - - is_expected.to be_falsy - end - - it 'returns false when the local version was bumped' do - allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2) - thing.cached_markdown_version = cache_version - - is_expected.to be_falsy - end + is_expected.to be_falsy + end - it 'returns true when the local version is default' do - thing.cached_markdown_version = cache_version + it 'returns false when the version is too early' do + thing.cached_markdown_version -= 1 - is_expected.to be_truthy - end + is_expected.to be_falsy + end - it 'returns true when the cached version is just right' do - allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2) - thing.cached_markdown_version = cache_version + 2 + it 'returns false when the version is too late' do + thing.cached_markdown_version += 1 - is_expected.to be_truthy - end + is_expected.to be_falsy + end - it 'returns false if markdown has been changed but html has not' do - thing.foo = updated_html + it 'returns false when the local version was bumped' do + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2) + thing.cached_markdown_version = cache_version - is_expected.to be_falsy - end + is_expected.to be_falsy + end - it 'returns true if markdown has not been changed but html has' do - thing.foo_html = updated_html + it 'returns true when the local version is default' do + thing.cached_markdown_version = cache_version - is_expected.to be_truthy - end + is_expected.to be_truthy + end - it 'returns true if markdown and html have both been changed' do - thing.foo = updated_markdown - thing.foo_html = updated_html + it 'returns true when the cached version is just right' do + allow(Gitlab::CurrentSettings.current_application_settings).to receive(:local_markdown_version).and_return(2) + thing.cached_markdown_version = cache_version + 2 - is_expected.to be_truthy + is_expected.to be_truthy + end end - it 'returns false if the markdown field is set but the html is not' do - thing.foo_html = nil + describe '#latest_cached_markdown_version' do + let(:thing) { klass.new } + subject { thing.latest_cached_markdown_version } - is_expected.to be_falsy + it 'returns default version' do + thing.cached_markdown_version = nil + is_expected.to eq(cache_version) + end end - end - - describe '#latest_cached_markdown_version' do - subject { thing.latest_cached_markdown_version } - it 'returns default version' do - thing.cached_markdown_version = nil - is_expected.to eq(cache_version) - end - end + describe '#refresh_markdown_cache' do + let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) } - describe '#refresh_markdown_cache' do - before do - thing.foo = updated_markdown - end + before do + thing.description = updated_markdown + end - it 'fills all html fields' do - thing.refresh_markdown_cache + it 'fills all html fields' do + thing.refresh_markdown_cache - expect(thing.foo_html).to eq(updated_html) - expect(thing.foo_html_changed?).to be_truthy - expect(thing.baz_html_changed?).to be_truthy - end + expect(thing.description_html).to eq(updated_html) + end - it 'does not save the result' do - expect(thing).not_to receive(:update_columns) + it 'does not save the result' do + expect(thing).not_to receive(:save_markdown) - thing.refresh_markdown_cache - end + thing.refresh_markdown_cache + end - it 'updates the markdown cache version' do - thing.cached_markdown_version = nil - thing.refresh_markdown_cache + it 'updates the markdown cache version' do + thing.cached_markdown_version = nil + thing.refresh_markdown_cache - expect(thing.cached_markdown_version).to eq(cache_version) + expect(thing.cached_markdown_version).to eq(cache_version) + end end - end - - describe '#refresh_markdown_cache!' do - let(:thing) { ThingWithMarkdownFields.new(foo: markdown, foo_html: html, cached_markdown_version: cache_version) } - before do - thing.foo = updated_markdown - end + describe '#refresh_markdown_cache!' do + let(:thing) { klass.new(description: markdown, description_html: html, cached_markdown_version: cache_version) } - it 'fills all html fields' do - thing.refresh_markdown_cache! + before do + thing.description = updated_markdown + end - expect(thing.foo_html).to eq(updated_html) - expect(thing.foo_html_changed?).to be_truthy - expect(thing.baz_html_changed?).to be_truthy - end + it 'fills all html fields' do + thing.refresh_markdown_cache! - it 'skips saving if not persisted' do - expect(thing).to receive(:persisted?).and_return(false) - expect(thing).not_to receive(:update_columns) + expect(thing.description_html).to eq(updated_html) + end - thing.refresh_markdown_cache! - end + it 'saves the changes' do + expect(thing) + .to receive(:save_markdown) + .with("description_html" => updated_html, "title_html" => "", "cached_markdown_version" => cache_version) - it 'saves the changes using #update_columns' do - expect(thing).to receive(:persisted?).and_return(true) - expect(thing).to receive(:update_columns) - .with( - "foo_html" => updated_html, - "baz_html" => "", - "zoo_html" => "", - "cached_markdown_version" => cache_version - ) - - thing.refresh_markdown_cache! + thing.refresh_markdown_cache! + end end - end - describe '#banzai_render_context' do - subject(:context) { thing.banzai_render_context(:foo) } + describe '#banzai_render_context' do + let(:thing) { klass.new(title: markdown, title_html: html, cached_markdown_version: cache_version) } + subject(:context) { thing.banzai_render_context(:title) } - it 'sets project to nil if the object lacks a project' do - is_expected.to have_key(:project) - expect(context[:project]).to be_nil - end + it 'sets project to nil if the object lacks a project' do + is_expected.to have_key(:project) + expect(context[:project]).to be_nil + end - it 'excludes author if the object lacks an author' do - is_expected.not_to have_key(:author) - end + it 'excludes author if the object lacks an author' do + is_expected.not_to have_key(:author) + end - it 'raises if the context for an unrecognised field is requested' do - expect { thing.banzai_render_context(:not_found) }.to raise_error(ArgumentError) - end + it 'raises if the context for an unrecognised field is requested' do + expect { thing.banzai_render_context(:not_found) }.to raise_error(ArgumentError) + end - it 'includes the pipeline' do - baz = thing.banzai_render_context(:baz) + it 'includes the pipeline' do + title_context = thing.banzai_render_context(:title) - expect(baz[:pipeline]).to eq(:single_line) - end + expect(title_context[:pipeline]).to eq(:single_line) + end - it 'returns copies of the context template' do - template = thing.cached_markdown_fields[:baz] - copy = thing.banzai_render_context(:baz) + it 'returns copies of the context template' do + template = thing.cached_markdown_fields[:description] + copy = thing.banzai_render_context(:description) - expect(copy).not_to be(template) - end + expect(copy).not_to be(template) + end - context 'with a project' do - let(:project) { create(:project, group: create(:group)) } - let(:thing) { thing_subclass(:project).new(foo: markdown, foo_html: html, project: project) } + context 'with a project' do + let(:project) { build(:project, group: create(:group)) } + let(:thing) { thing_subclass(klass, :project).new(title: markdown, title_html: html, project: project) } - it 'sets the project in the context' do - is_expected.to have_key(:project) - expect(context[:project]).to eq(project) + it 'sets the project in the context' do + is_expected.to have_key(:project) + expect(context[:project]).to eq(project) + end end - it 'invalidates the cache when project changes' do - thing.project = :new_project - allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) - - thing.save + context 'with an author' do + let(:thing) { thing_subclass(klass, :author).new(title: markdown, title_html: html, author: :author_value) } - expect(thing.foo_html).to eq(updated_html) - expect(thing.baz_html).to eq(updated_html) - expect(thing.cached_markdown_version).to eq(cache_version) + it 'sets the author in the context' do + is_expected.to have_key(:author) + expect(context[:author]).to eq(:author_value) + end end end + end - context 'with an author' do - let(:thing) { thing_subclass(:author).new(foo: markdown, foo_html: html, author: :author_value) } - - it 'sets the author in the context' do - is_expected.to have_key(:author) - expect(context[:author]).to eq(:author_value) - end + context 'for Active record classes' do + let(:klass) { ar_class } - it 'invalidates the cache when author changes' do - thing.author = :new_author - allow(Banzai::Renderer).to receive(:cacheless_render_field).and_return(updated_html) + it_behaves_like 'a class with cached markdown fields' + end - thing.save + context 'for other classes' do + let(:klass) { other_class } - expect(thing.foo_html).to eq(updated_html) - expect(thing.baz_html).to eq(updated_html) - expect(thing.cached_markdown_version).to eq(cache_version) - end - end + it_behaves_like 'a class with cached markdown fields' end end diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb index 7eeb2fae57d..cb52f154299 100644 --- a/spec/models/resource_label_event_spec.rb +++ b/spec/models/resource_label_event_spec.rb @@ -82,13 +82,13 @@ RSpec.describe ResourceLabelEvent, type: :model do end it 'returns true if markdown is outdated' do - subject.attributes = { cached_markdown_version: ((CacheMarkdownField::CACHE_COMMONMARK_VERSION - 1) << 16) | 0 } + subject.attributes = { cached_markdown_version: ((Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION - 1) << 16) | 0 } expect(subject.outdated_markdown?).to be true end it 'returns false if label and reference are set' do - subject.attributes = { reference: 'whatever', cached_markdown_version: CacheMarkdownField::CACHE_COMMONMARK_VERSION << 16 } + subject.attributes = { reference: 'whatever', cached_markdown_version: Gitlab::MarkdownCache::CACHE_COMMONMARK_VERSION << 16 } expect(subject.outdated_markdown?).to be false end diff --git a/spec/presenters/blob_presenter_spec.rb b/spec/presenters/blob_presenter_spec.rb index bb1db9a3d51..eacf383be7d 100644 --- a/spec/presenters/blob_presenter_spec.rb +++ b/spec/presenters/blob_presenter_spec.rb @@ -14,6 +14,16 @@ describe BlobPresenter, :seed_helper do end let(:blob) { Blob.new(git_blob) } + describe '.web_url' do + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:blob) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.blobs.first, repository) } + + subject { described_class.new(blob) } + + it { expect(subject.web_url).to eq("http://localhost/#{project.full_path}/blob/#{blob.commit_id}/#{blob.path}") } + end + describe '#highlight' do subject { described_class.new(blob) } diff --git a/spec/presenters/tree_entry_presenter_spec.rb b/spec/presenters/tree_entry_presenter_spec.rb new file mode 100644 index 00000000000..d74ee5dc28f --- /dev/null +++ b/spec/presenters/tree_entry_presenter_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe TreeEntryPresenter do + include Gitlab::Routing.url_helpers + + let(:project) { create(:project, :repository) } + let(:repository) { project.repository } + let(:tree) { Gitlab::Graphql::Representation::TreeEntry.new(repository.tree.trees.first, repository) } + let(:presenter) { described_class.new(tree) } + + describe '.web_url' do + it { expect(presenter.web_url).to eq("http://localhost/#{project.full_path}/tree/#{tree.commit_id}/#{tree.path}") } + end +end diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb index 92a19dd22a2..01531864c1f 100644 --- a/spec/support/features/variable_list_shared_examples.rb +++ b/spec/support/features/variable_list_shared_examples.rb @@ -45,12 +45,12 @@ shared_examples 'variable list' do end end - it 'defaults to masked' do + it 'defaults to unmasked' do page.within('.js-ci-variable-list-section .js-row:last-child') do find('.js-ci-variable-input-key').set('key') find('.js-ci-variable-input-value').set('key_value') - expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true') + expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') end click_button('Save variables') @@ -62,7 +62,7 @@ shared_examples 'variable list' do page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do expect(find('.js-ci-variable-input-key').value).to eq('key') expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value') - expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true') + expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') end end @@ -234,12 +234,14 @@ shared_examples 'variable list' do end it 'edits variable to be unmasked' do - page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do - expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true') + page.within('.js-ci-variable-list-section .js-row:last-child') do + find('.js-ci-variable-input-key').set('unmasked_key') + find('.js-ci-variable-input-value').set('unmasked_value') + expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') find('.ci-variable-masked-item .js-project-feature-toggle').click - expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') + expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true') end click_button('Save variables') @@ -248,12 +250,6 @@ shared_examples 'variable list' do visit page_path page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do - expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') - end - end - - it 'edits variable to be masked' do - page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true') find('.ci-variable-masked-item .js-project-feature-toggle').click @@ -268,6 +264,14 @@ shared_examples 'variable list' do page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') + end + end + + it 'edits variable to be masked' do + page.within('.js-ci-variable-list-section .js-row:last-child') do + find('.js-ci-variable-input-key').set('masked_key') + find('.js-ci-variable-input-value').set('masked_value') + expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false') find('.ci-variable-masked-item .js-project-feature-toggle').click @@ -348,10 +352,11 @@ shared_examples 'variable list' do end end - it 'shows validation error box about empty values' do + it 'shows validation error box about masking empty values' do page.within('.js-ci-variable-list-section .js-row:last-child') do find('.js-ci-variable-input-key').set('empty_value') find('.js-ci-variable-input-value').set('') + find('.ci-variable-masked-item .js-project-feature-toggle').click end click_button('Save variables') @@ -367,6 +372,7 @@ shared_examples 'variable list' do page.within('.js-ci-variable-list-section .js-row:last-child') do find('.js-ci-variable-input-key').set('unmaskable_value') find('.js-ci-variable-input-value').set('???') + find('.ci-variable-masked-item .js-project-feature-toggle').click end click_button('Save variables') |