Commit 0b881f91 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 7671216b
...@@ -9,10 +9,11 @@ Set the title to: `Description of the original issue` ...@@ -9,10 +9,11 @@ Set the title to: `Description of the original issue`
## Prior to starting the security release work ## Prior to starting the security release work
- [ ] Read the [security process for developers] if you are not familiar with it. - [ ] Read the [security process for developers] if you are not familiar with it.
- [ ] Link this issue in the Security Release issue on GitLab.com. You can find this issue in the topic of the `#releases` channel. - [ ] Mark this [issue as related] to the Security Release tracking issue. You can find it on the topic of the `#releases` Slack channel.
- [ ] Add a link to the confidential `gitlab-org/gitlab` issue describing the vulnerability next to **Original issue** in the [links table](#links).
- [ ] Add a link to the confidential `gitlab-org/gitlab` Security release issue next to **Security release issue** in the [links table](#links).
- [ ] Run `scripts/security-harness` in your local repository to prevent accidentally pushing to any remote besides `gitlab.com/gitlab-org/security`. - [ ] Run `scripts/security-harness` in your local repository to prevent accidentally pushing to any remote besides `gitlab.com/gitlab-org/security`.
- Fill out the [Links section](#links):
- [ ] Next to **Issue on GitLab**, add a link to the `gitlab-org/gitlab` issue that describes the security vulnerability.
- [ ] Next to **Security Release tracking issue**, add a link to the security release issue that will include this security issue.
## Development ## Development
...@@ -29,7 +30,8 @@ After your merge request has being approved according to our [approval guideline ...@@ -29,7 +30,8 @@ After your merge request has being approved according to our [approval guideline
* You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation] * You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [secpick documentation]
- [ ] Create each MR targeting the stable branch `X-Y-stable`, using the [Security Release merge request template]. - [ ] Create each MR targeting the stable branch `X-Y-stable`, using the [Security Release merge request template].
* Every merge request will have its own set of TODOs, so make sure to complete those. * Every merge request will have its own set of TODOs, so make sure to complete those.
- [ ] Make sure all MRs are linked in the [Links section](#links) - [ ] On the "Related merge requests" section, ensure all MRs are linked to this issue.
* This section should only list the merge requests created for this issue: One targeting `master` and the 3 backports.
## Documentation and final details ## Documentation and final details
...@@ -46,8 +48,8 @@ After your merge request has being approved according to our [approval guideline ...@@ -46,8 +48,8 @@ After your merge request has being approved according to our [approval guideline
| Description | Link | | Description | Link |
| -------- | -------- | | -------- | -------- |
| Original issue | #TODO | | Issue on [GitLab](https://gitlab.com/gitlab-org/gitlab/issues) | #TODO |
| Security release issue | #TODO | | Security Release tracking issue | #TODO |
| `master` MR | !TODO | | `master` MR | !TODO |
| `Backport X.Y` MR | !TODO | | `Backport X.Y` MR | !TODO |
| `Backport X.Y` MR | !TODO | | `Backport X.Y` MR | !TODO |
...@@ -68,5 +70,6 @@ After your merge request has being approved according to our [approval guideline ...@@ -68,5 +70,6 @@ After your merge request has being approved according to our [approval guideline
[security Release merge request template]: https://gitlab.com/gitlab-org/security/gitlab/blob/master/.gitlab/merge_request_templates/Security%20Release.md [security Release merge request template]: https://gitlab.com/gitlab-org/security/gitlab/blob/master/.gitlab/merge_request_templates/Security%20Release.md
[code review process]: https://docs.gitlab.com/ee/development/code_review.html [code review process]: https://docs.gitlab.com/ee/development/code_review.html
[approval guidelines]: https://docs.gitlab.com/ee/development/code_review.html#approval-guidelines [approval guidelines]: https://docs.gitlab.com/ee/development/code_review.html#approval-guidelines
[issue as related]: https://docs.gitlab.com/ee/user/project/issues/related_issues.html#adding-a-related-issue
/label ~security /label ~security
...@@ -2,7 +2,7 @@ source 'https://rubygems.org' ...@@ -2,7 +2,7 @@ source 'https://rubygems.org'
gem 'rails', '6.0.2' gem 'rails', '6.0.2'
gem 'bootsnap', '~> 1.4' gem 'bootsnap', '~> 1.4.6'
# Improves copy-on-write performance for MRI # Improves copy-on-write performance for MRI
gem 'nakayoshi_fork', '~> 0.0.4' gem 'nakayoshi_fork', '~> 0.0.4'
......
...@@ -123,7 +123,7 @@ GEM ...@@ -123,7 +123,7 @@ GEM
binding_ninja (0.2.3) binding_ninja (0.2.3)
binding_of_caller (0.8.0) binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootsnap (1.4.5) bootsnap (1.4.6)
msgpack (~> 1.0) msgpack (~> 1.0)
bootstrap_form (4.2.0) bootstrap_form (4.2.0)
actionpack (>= 5.0) actionpack (>= 5.0)
...@@ -1171,7 +1171,7 @@ DEPENDENCIES ...@@ -1171,7 +1171,7 @@ DEPENDENCIES
benchmark-memory (~> 0.1) benchmark-memory (~> 0.1)
better_errors (~> 2.5.0) better_errors (~> 2.5.0)
binding_of_caller (~> 0.8.0) binding_of_caller (~> 0.8.0)
bootsnap (~> 1.4) bootsnap (~> 1.4.6)
bootstrap_form (~> 4.2.0) bootstrap_form (~> 4.2.0)
brakeman (~> 4.2) brakeman (~> 4.2)
browser (~> 2.5) browser (~> 2.5)
......
<script> <script>
import { GlLoadingIcon } from '@gitlab/ui';
export default { export default {
components: {
GlLoadingIcon,
},
props: { props: {
canEdit: { canEdit: {
type: Boolean, type: Boolean,
...@@ -13,7 +18,7 @@ export default { ...@@ -13,7 +18,7 @@ export default {
<div class="title hide-collapsed append-bottom-10"> <div class="title hide-collapsed append-bottom-10">
{{ __('Labels') }} {{ __('Labels') }}
<template v-if="canEdit"> <template v-if="canEdit">
<i aria-hidden="true" class="fa fa-spinner fa-spin block-loading" data-hidden="true"> </i> <gl-loading-icon inline class="align-text-top block-loading" />
<button <button
type="button" type="button"
class="edit-link btn btn-blank float-right js-sidebar-dropdown-toggle" class="edit-link btn btn-blank float-right js-sidebar-dropdown-toggle"
......
...@@ -6,8 +6,11 @@ module Releases ...@@ -6,8 +6,11 @@ module Releases
belongs_to :release belongs_to :release
FILEPATH_REGEX = /\A\/([\-\.\w]+\/?)*[\da-zA-Z]+\z/.freeze
validates :url, presence: true, addressable_url: { schemes: %w(http https ftp) }, uniqueness: { scope: :release } validates :url, presence: true, addressable_url: { schemes: %w(http https ftp) }, uniqueness: { scope: :release }
validates :name, presence: true, uniqueness: { scope: :release } validates :name, presence: true, uniqueness: { scope: :release }
validates :filepath, uniqueness: { scope: :release }, format: { with: FILEPATH_REGEX }, allow_blank: true, length: { maximum: 128 }
scope :sorted, -> { order(created_at: :desc) } scope :sorted, -> { order(created_at: :desc) }
......
...@@ -31,7 +31,7 @@ class ProjectSnippetPolicy < BasePolicy ...@@ -31,7 +31,7 @@ class ProjectSnippetPolicy < BasePolicy
~can?(:read_all_resources)) ~can?(:read_all_resources))
end.prevent :read_snippet end.prevent :read_snippet
rule { internal_snippet & ~is_author & ~admin }.policy do rule { internal_snippet & ~is_author & ~admin & ~project.maintainer }.policy do
prevent :update_snippet prevent :update_snippet
prevent :admin_snippet prevent :admin_snippet
end end
...@@ -42,7 +42,7 @@ class ProjectSnippetPolicy < BasePolicy ...@@ -42,7 +42,7 @@ class ProjectSnippetPolicy < BasePolicy
prevent :admin_snippet prevent :admin_snippet
end end
rule { is_author | admin }.policy do rule { is_author | admin | project.maintainer }.policy do
enable :read_snippet enable :read_snippet
enable :update_snippet enable :update_snippet
enable :admin_snippet enable :admin_snippet
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= runner_status_icon(runner) = runner_status_icon(runner)
- if @project_runners.include?(runner) - if @project_runners.include?(runner)
= link_to runner.short_sha, project_runner_path(@project, runner), class: 'commit-sha' = link_to runner.short_sha.concat("..."), project_runner_path(@project, runner), class: 'commit-sha has-tooltip', title: _("Partial token for reference only")
- if runner.locked? - if runner.locked?
= icon('lock', class: 'has-tooltip', title: _('Locked to current projects')) = icon('lock', class: 'has-tooltip', title: _('Locked to current projects'))
......
---
title: Add filepath to ReleaseLink
merge_request: 25512
author:
type: added
---
title: Improvement in token reference
merge_request:
author:
type: other
---
title: Add API pagination for deployed merge requests
merge_request: 25733
author:
type: performance
---
title: Fix bug deleting internal project snippets by project maintainer
merge_request: 25792
author:
type: fixed
---
title: Migrated the sidebar label select dropdown title component spinner to utilize GlLoadingIcon
merge_request: 24914
author: Raihan Kabir
type: changed
---
title: Clean stale background migration jobs
merge_request: 25707
author:
type: fixed
---
title: Upgrade to Bootsnap 1.4.6
merge_request: 25844
author:
type: performance
---
title: Add support for alert-based metric embeds in GFM
merge_request: 25075
author:
type: added
# frozen_string_literal: true
class AddFilepathToReleaseLinks < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :release_links, :filepath, :string, limit: 128
end
end
# frozen_string_literal: true
class DropActivatePrometheusServicesBackgroundJobs < ActiveRecord::Migration[6.0]
DOWNTIME = false
DROPPED_JOB_CLASS = 'ActivatePrometheusServicesForSharedClusterApplications'.freeze
QUEUE = 'background_migration'.freeze
def up
sidekiq_queues.each do |queue|
queue.each do |job|
klass, project_id, *should_be_empty = job.args
next unless klass == DROPPED_JOB_CLASS && project_id.is_a?(Integer) && should_be_empty.empty?
job.delete
end
end
end
def down
# no-op
end
def sidekiq_queues
[Sidekiq::ScheduledSet.new, Sidekiq::RetrySet.new, Sidekiq::Queue.new(QUEUE)]
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2020_02_21_105436) do ActiveRecord::Schema.define(version: 2020_02_21_144534) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm" enable_extension "pg_trgm"
...@@ -3648,6 +3648,7 @@ ActiveRecord::Schema.define(version: 2020_02_21_105436) do ...@@ -3648,6 +3648,7 @@ ActiveRecord::Schema.define(version: 2020_02_21_105436) do
t.string "name", null: false t.string "name", null: false
t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false t.datetime_with_timezone "updated_at", null: false
t.string "filepath", limit: 128
t.index ["release_id", "name"], name: "index_release_links_on_release_id_and_name", unique: true t.index ["release_id", "name"], name: "index_release_links_on_release_id_and_name", unique: true
t.index ["release_id", "url"], name: "index_release_links_on_release_id_and_url", unique: true t.index ["release_id", "url"], name: "index_release_links_on_release_id_and_url", unique: true
end end
......
...@@ -143,6 +143,7 @@ module API ...@@ -143,6 +143,7 @@ module API
success Entities::MergeRequestBasic success Entities::MergeRequestBasic
end end
params do params do
use :pagination
requires :deployment_id, type: Integer, desc: 'The deployment ID' requires :deployment_id, type: Integer, desc: 'The deployment ID'
use :merge_requests_base_params use :merge_requests_base_params
end end
...@@ -153,7 +154,7 @@ module API ...@@ -153,7 +154,7 @@ module API
mr_params = declared_params.merge(deployment_id: params[:deployment_id]) mr_params = declared_params.merge(deployment_id: params[:deployment_id])
merge_requests = MergeRequestsFinder.new(current_user, mr_params).execute merge_requests = MergeRequestsFinder.new(current_user, mr_params).execute
present merge_requests, { with: Entities::MergeRequestBasic, current_user: current_user } present paginate(merge_requests), { with: Entities::MergeRequestBasic, current_user: current_user }
end end
end end
end end
......
...@@ -143,3 +143,5 @@ module Banzai ...@@ -143,3 +143,5 @@ module Banzai
end end
end end
end end
Banzai::Filter::InlineMetricsRedactorFilter.prepend_if_ee('EE::Banzai::Filter::InlineMetricsRedactorFilter')
...@@ -13655,6 +13655,9 @@ msgstr "" ...@@ -13655,6 +13655,9 @@ msgstr ""
msgid "Part of merge request changes" msgid "Part of merge request changes"
msgstr "" msgstr ""
msgid "Partial token for reference only"
msgstr ""
msgid "Participants" msgid "Participants"
msgstr "" msgstr ""
......
...@@ -5,5 +5,6 @@ FactoryBot.define do ...@@ -5,5 +5,6 @@ FactoryBot.define do
release release
sequence(:name) { |n| "release-18.#{n}.dmg" } sequence(:name) { |n| "release-18.#{n}.dmg" }
sequence(:url) { |n| "https://example.com/scrambled-url/app-#{n}.zip" } sequence(:url) { |n| "https://example.com/scrambled-url/app-#{n}.zip" }
sequence(:filepath) { |n| "/binaries/awesome-app-#{n}" }
end end
end end
...@@ -2,25 +2,32 @@ ...@@ -2,25 +2,32 @@
require 'spec_helper' require 'spec_helper'
describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidekiq_might_not_need_inline do describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do
include PrometheusHelpers include PrometheusHelpers
include GrafanaApiHelpers include GrafanaApiHelpers
include MetricsDashboardUrlHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:prometheus_project) }
let_it_be(:environment) { create(:environment, project: project) }
let(:user) { create(:user) }
let(:project) { create(:prometheus_project) }
let(:environment) { create(:environment, project: project) }
let(:issue) { create(:issue, project: project, description: description) } let(:issue) { create(:issue, project: project, description: description) }
let(:description) { "See [metrics dashboard](#{metrics_url}) for info." } let(:description) { "See [metrics dashboard](#{metrics_url}) for info." }
let(:metrics_url) { metrics_project_environment_url(project, environment) } let(:metrics_url) { urls.metrics_project_environment_url(project, environment) }
before do before do
configure_host clear_host_from_memoized_variables
allow(::Gitlab.config.gitlab)
.to receive(:url)
.and_return(urls.root_url.chomp('/'))
project.add_developer(user) project.add_developer(user)
sign_in(user) sign_in(user)
end end
after do after do
restore_host clear_host_from_memoized_variables
end end
context 'internal metrics embeds' do context 'internal metrics embeds' do
...@@ -38,7 +45,7 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek ...@@ -38,7 +45,7 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek
end end
context 'when dashboard params are in included the url' do context 'when dashboard params are in included the url' do
let(:metrics_url) { metrics_project_environment_url(project, environment, **chart_params) } let(:metrics_url) { urls.metrics_project_environment_url(project, environment, **chart_params) }
let(:chart_params) do let(:chart_params) do
{ {
...@@ -81,32 +88,4 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek ...@@ -81,32 +88,4 @@ describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching, :sidek
def import_common_metrics def import_common_metrics
::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute
end end
def configure_host
@original_default_host = default_url_options[:host]
@original_gitlab_url = Gitlab.config.gitlab[:url]
# Ensure we create a metrics url with the right host.
# Configure host for route helpers in specs (also updates root_url):
default_url_options[:host] = Capybara.server_host
# Ensure we identify urls with the appropriate host.
# Configure host to include port in app:
Gitlab.config.gitlab[:url] = root_url.chomp('/')
clear_host_from_memoized_variables
end
def restore_host
default_url_options[:host] = @original_default_host
Gitlab.config.gitlab[:url] = @original_gitlab_url
clear_host_from_memoized_variables
end
def clear_host_from_memoized_variables
[:metrics_regex, :grafana_regex].each do |method_name|
Gitlab::Metrics::Dashboard::Url.clear_memoization(method_name)
end
end
end end
import Vue from 'vue'; import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import mountComponent from 'helpers/vue_mount_component_helper';
import dropdownTitleComponent from '~/vue_shared/components/sidebar/labels_select/dropdown_title.vue'; import dropdownTitleComponent from '~/vue_shared/components/sidebar/labels_select/dropdown_title.vue';
const createComponent = (canEdit = true) => { const createComponent = (canEdit = true) =>
const Component = Vue.extend(dropdownTitleComponent); shallowMount(dropdownTitleComponent, {
propsData: {
return mountComponent(Component, { canEdit,
canEdit, },
}); });
};
describe('DropdownTitleComponent', () => { describe('DropdownTitleComponent', () => {
let vm; let wrapper;
beforeEach(() => { beforeEach(() => {
vm = createComponent(); wrapper = createComponent();
}); });
afterEach(() => { afterEach(() => {
vm.$destroy(); wrapper.destroy();
wrapper = null;
}); });
describe('template', () => { describe('template', () => {
it('renders title text', () => { it('renders title text', () => {
expect(vm.$el.classList.contains('title', 'hide-collapsed')).toBe(true); expect(wrapper.vm.$el.classList.contains('title', 'hide-collapsed')).toBe(true);
expect(vm.$el.innerText.trim()).toContain('Labels'); expect(wrapper.vm.$el.innerText.trim()).toContain('Labels');
}); });
it('renders spinner icon element', () => { it('renders spinner icon element', () => {
expect(vm.$el.querySelector('.fa-spinner.fa-spin.block-loading')).not.toBeNull(); expect(wrapper.find(GlLoadingIcon)).not.toBeNull();
}); });
it('renders `Edit` button element', () => { it('renders `Edit` button element', () => {
const editBtnEl = vm.$el.querySelector('button.edit-link.js-sidebar-dropdown-toggle'); const editBtnEl = wrapper.vm.$el.querySelector('button.edit-link.js-sidebar-dropdown-toggle');
expect(editBtnEl).not.toBeNull(); expect(editBtnEl).not.toBeNull();
expect(editBtnEl.innerText.trim()).toBe('Edit'); expect(editBtnEl.innerText.trim()).toBe('Edit');
......
...@@ -133,6 +133,7 @@ Releases::Link: ...@@ -133,6 +133,7 @@ Releases::Link:
- id - id
- url - url
- name - name
- filepath
- created_at - created_at
- updated_at - updated_at
ProjectMember: ProjectMember:
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20200221144534_drop_activate_prometheus_services_background_jobs.rb')
describe DropActivatePrometheusServicesBackgroundJobs, :sidekiq, :redis, :migration, schema: 2020_02_21_144534 do
subject(:migration) { described_class.new }
describe '#up' do
let(:retry_set) { Sidekiq::RetrySet.new }
let(:scheduled_set) { Sidekiq::ScheduledSet.new }
context 'there are only affected jobs on the queue' do
let(:payload) { { 'class' => ::BackgroundMigrationWorker, 'args' => [described_class::DROPPED_JOB_CLASS, 1] } }
let(:queue_payload) { payload.merge('queue' => described_class::QUEUE) }
it 'removes enqueued ActivatePrometheusServicesForSharedClusterApplications background jobs' do
Sidekiq::Testing.disable! do # https://github.com/mperham/sidekiq/wiki/testing#api Sidekiq's API does not have a testing mode
retry_set.schedule(1.hour.from_now, payload)
scheduled_set.schedule(1.hour.from_now, payload)
Sidekiq::Client.push(queue_payload)
expect { migration.up }.to change { Sidekiq::Queue.new(described_class::QUEUE).size }.from(1).to(0)
expect(retry_set.size).to eq(0)
expect(scheduled_set.size).to eq(0)
end
end
end
context "there aren't any affected jobs on the queue" do
let(:payload) { { 'class' => ::BackgroundMigrationWorker, 'args' => ['SomeOtherClass', 1] } }
let(:queue_payload) { payload.merge('queue' => described_class::QUEUE) }
it 'skips other enqueued jobs' do
Sidekiq::Testing.disable! do
retry_set.schedule(1.hour.from_now, payload)
scheduled_set.schedule(1.hour.from_now, payload)
Sidekiq::Client.push(queue_payload)
expect { migration.up }.not_to change { Sidekiq::Queue.new(described_class::QUEUE).size }
expect(retry_set.size).to eq(1)
expect(scheduled_set.size).to eq(1)
end
end
end
context "there are multiple types of jobs on the queue" do
let(:payload) { { 'class' => ::BackgroundMigrationWorker, 'args' => [described_class::DROPPED_JOB_CLASS, 1] } }
let(:queue_payload) { payload.merge('queue' => described_class::QUEUE) }
it 'skips other enqueued jobs' do
Sidekiq::Testing.disable! do
queue = Sidekiq::Queue.new(described_class::QUEUE)
# these jobs will be deleted
retry_set.schedule(1.hour.from_now, payload)
scheduled_set.schedule(1.hour.from_now, payload)
Sidekiq::Client.push(queue_payload)
# this jobs will be skipped
skipped_jobs_args = [['SomeOtherClass', 1], [described_class::DROPPED_JOB_CLASS, 'wrong id type'], [described_class::DROPPED_JOB_CLASS, 1, 'some wired argument']]
skipped_jobs_args.each do |args|
retry_set.schedule(1.hour.from_now, { 'class' => ::BackgroundMigrationWorker, 'args' => args })
scheduled_set.schedule(1.hour.from_now, { 'class' => ::BackgroundMigrationWorker, 'args' => args })
Sidekiq::Client.push('queue' => described_class::QUEUE, 'class' => ::BackgroundMigrationWorker, 'args' => args)
end
migration.up
expect(retry_set.size).to be 3
expect(scheduled_set.size).to be 3
expect(queue.size).to be 3
expect(queue.map(&:args)).to match_array skipped_jobs_args
expect(retry_set.map(&:args)).to match_array skipped_jobs_args
expect(scheduled_set.map(&:args)).to match_array skipped_jobs_args
end
end
end
context "other queues" do
it 'does not modify them' do
Sidekiq::Testing.disable! do
Sidekiq::Client.push('queue' => 'other', 'class' => ::BackgroundMigrationWorker, 'args' => ['SomeOtherClass', 1])
Sidekiq::Client.push('queue' => 'other', 'class' => ::BackgroundMigrationWorker, 'args' => [described_class::DROPPED_JOB_CLASS, 1])
expect { migration.up }.not_to change { Sidekiq::Queue.new('other').size }
end
end
end
end
end
...@@ -13,6 +13,7 @@ describe Releases::Link do ...@@ -13,6 +13,7 @@ describe Releases::Link do
describe 'validation' do describe 'validation' do
it { is_expected.to validate_presence_of(:url) } it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_length_of(:filepath).is_at_most(128) }
context 'when url is invalid' do context 'when url is invalid' do
let(:link) { build(:release_link, url: 'hoge') } let(:link) { build(:release_link, url: 'hoge') }
...@@ -43,6 +44,16 @@ describe Releases::Link do ...@@ -43,6 +44,16 @@ describe Releases::Link do
end end
end end
context 'when duplicate filepath is added to a release' do
let!(:link) { create(:release_link, filepath: '/binaries/gitlab-runner-linux-amd64', release: release) }
it 'raises an error' do
expect do
create(:release_link, filepath: '/binaries/gitlab-runner-linux-amd64', release: release)
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
describe '.sorted' do describe '.sorted' do
subject { described_class.sorted } subject { described_class.sorted }
...@@ -101,4 +112,38 @@ describe Releases::Link do ...@@ -101,4 +112,38 @@ describe Releases::Link do
end end
end end
end end
describe 'FILEPATH_REGEX with table' do
using RSpec::Parameterized::TableSyntax
let(:link) { build(:release_link)}
where(:reason, :filepath, :result) do
'cannot contain `//`' | '/https//www.example.com' | be_invalid
'cannot start with `//`' | '//www.example.com' | be_invalid
'cannot contain a `?`' | '/example.com/?stuff=true' | be_invalid
'cannot contain a `:`' | '/example:5000' | be_invalid
'cannot end in a `-`' | '/binaries/awesome-app.dmg-' | be_invalid
'cannot end in a `.`' | '/binaries/awesome-app.dmg.' | be_invalid
'cannot end in a `_`' | '/binaries/awesome-app.dmg_' | be_invalid
'cannot start with a `.`' | '.binaries/awesome-app.dmg' | be_invalid
'cannot start with a `-`' | '-binaries/awesome-app.dmg' | be_invalid
'cannot start with a `_`' | '_binaries/awesome-app.dmg' | be_invalid
'cannot start with a number' | '3binaries/awesome-app.dmg' | be_invalid
'cannot start with a letter' | 'binaries/awesome-app.dmg' | be_invalid
'cannot contain accents' | '/binarïes/âwésome-app.dmg' | be_invalid
'can end in a character' | '/binaries/awesome-app.dmg' | be_valid
'can end in a number' | '/binaries/awesome-app-1' | be_valid
'can contain one or more dots, dashes or underscores' | '/sub_tr__ee.ex..ample-2--1/v99.com' | be_valid
'can contain multiple non-sequential slashes' | '/example.com/path/to/file.exe' | be_valid
'can be nil' | nil | be_valid
end
with_them do
specify do
link.filepath = filepath
expect(link).to result
end
end
end
end end
...@@ -20,28 +20,39 @@ describe ProjectSnippetPolicy do ...@@ -20,28 +20,39 @@ describe ProjectSnippetPolicy do
subject { described_class.new(current_user, snippet) } subject { described_class.new(current_user, snippet) }
shared_examples 'regular user access rights' do shared_examples 'regular user access rights' do
context 'project team member (non guest)' do context 'not snippet author' do
before do context 'project team member (non guest)' do
project.add_developer(current_user) before do
end project.add_developer(current_user)
end
it do it do
expect_allowed(:read_snippet, :create_note) expect_allowed(:read_snippet, :create_note)
expect_disallowed(*author_permissions) expect_disallowed(*author_permissions)
end
end end
end
context 'project team member (guest)' do context 'project team member (guest)' do
before do before do
project.add_guest(current_user) project.add_guest(current_user)
end end
context 'not snippet author' do
it do it do
expect_allowed(:read_snippet, :create_note) expect_allowed(:read_snippet, :create_note)
expect_disallowed(:admin_snippet) expect_disallowed(:admin_snippet)
end end
end end
context 'project team member (maintainer)' do
before do
project.add_maintainer(current_user)
end
it do
expect_allowed(:read_snippet, :create_note)
expect_allowed(*author_permissions)
end
end
end end
context 'snippet author' do context 'snippet author' do
...@@ -69,6 +80,17 @@ describe ProjectSnippetPolicy do ...@@ -69,6 +80,17 @@ describe ProjectSnippetPolicy do
end end
end end
context 'project team member (maintainer)' do
before do
project.add_maintainer(current_user)
end
it do
expect_allowed(:read_snippet, :create_note)
expect_allowed(*author_permissions)
end
end
context 'not a project member' do context 'not a project member' do
it do it do
expect_allowed(:read_snippet, :create_note) expect_allowed(:read_snippet, :create_note)
......
...@@ -444,6 +444,7 @@ describe API::Deployments do ...@@ -444,6 +444,7 @@ describe API::Deployments do
subject subject
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response.map { |d| d['id'] }).to contain_exactly(merge_request1.id, merge_request2.id) expect(json_response.map { |d| d['id'] }).to contain_exactly(merge_request1.id, merge_request2.id)
end end
......
# frozen_string_literal: true
module MetricsDashboardUrlHelpers
# Using the url_helpers available in the test suite uses
# the sample host, but the urls generated may need to
# point to the configured host in the :js trait
def urls
::Gitlab::Routing.url_helpers
end
def clear_host_from_memoized_variables
[:metrics_regex, :grafana_regex, :cluster_metrics, :alerts_regex].each do |method_name|
Gitlab::Metrics::Dashboard::Url.clear_memoization(method_name)
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment