Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2019-10-16 12:06:32 +00:00
parent 914ea32e0e
commit d2ffc30fd5
69 changed files with 638 additions and 145 deletions

View File

@ -73,16 +73,15 @@ export default {
const entry = data.entries[key];
const foundEntry = state.entries[key];
// NOTE: We can't clone `entry` in any of the below assignments because
// we need `state.entries` and the `entry.tree` to reference the same object.
if (!foundEntry) {
Object.assign(state.entries, {
[key]: entry,
});
} else if (foundEntry.deleted) {
Object.assign(state.entries, {
[key]: {
...entry,
replaces: true,
},
[key]: Object.assign(entry, { replaces: true }),
});
} else {
const tree = entry.tree.filter(

View File

@ -15,15 +15,19 @@ export default {
GlLoadingIcon,
},
props: {
endpoint: {
type: String,
required: true,
},
characterError: {
type: Boolean,
required: false,
default: false,
},
containersErrorImage: {
type: String,
required: true,
},
endpoint: {
type: String,
required: true,
},
helpPagePath: {
type: String,
required: true,
@ -32,7 +36,11 @@ export default {
type: String,
required: true,
},
containersErrorImage: {
personalAccessTokensHelpLink: {
type: String,
required: true,
},
registryHostUrlWithPort: {
type: String,
required: true,
},
@ -40,6 +48,10 @@ export default {
type: String,
required: true,
},
twoFactorAuthHelpLink: {
type: String,
required: true,
},
},
store,
computed: {
@ -79,6 +91,26 @@ export default {
false,
);
},
notLoggedInToRegistryText() {
return sprintf(
s__(`ContainerRegistry|If you are not already logged in, you need to authenticate to
the Container Registry by using your GitLab username and password. If you have
%{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a
%{personalAccessTokensDocLinkStart}Personal Access Token
%{personalAccessTokensDocLinkEnd}instead of a password.`),
{
twofaDocLinkStart: `<a href="${this.twoFactorAuthHelpLink}" target="_blank">`,
twofaDocLinkEnd: '</a>',
personalAccessTokensDocLinkStart: `<a href="${this.personalAccessTokensHelpLink}" target="_blank">`,
personalAccessTokensDocLinkEnd: '</a>',
},
false,
);
},
dockerLoginCommand() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `docker login ${this.registryHostUrlWithPort}`;
},
dockerBuildCommand() {
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
return `docker build -t ${this.repositoryUrl} .`;
@ -130,6 +162,17 @@ export default {
<template #description>
<p class="js-no-container-images-text" v-html="noContainerImagesText"></p>
<h5>{{ s__('ContainerRegistry|Quick Start') }}</h5>
<p class="js-not-logged-in-to-registry-text" v-html="notLoggedInToRegistryText"></p>
<div class="input-group append-bottom-10">
<input :value="dockerLoginCommand" type="text" class="form-control monospace" readonly />
<span class="input-group-append">
<clipboard-button
:text="dockerLoginCommand"
:title="s__('ContainerRegistry|Copy login command')"
class="input-group-text"
/>
</span>
</div>
<p>
{{
s__(

View File

@ -13,23 +13,29 @@ export default () =>
data() {
const { dataset } = document.querySelector(this.$options.el);
return {
endpoint: dataset.endpoint,
characterError: Boolean(dataset.characterError),
containersErrorImage: dataset.containersErrorImage,
endpoint: dataset.endpoint,
helpPagePath: dataset.helpPagePath,
noContainersImage: dataset.noContainersImage,
containersErrorImage: dataset.containersErrorImage,
personalAccessTokensHelpLink: dataset.personalAccessTokensHelpLink,
registryHostUrlWithPort: dataset.registryHostUrlWithPort,
repositoryUrl: dataset.repositoryUrl,
twoFactorAuthHelpLink: dataset.twoFactorAuthHelpLink,
};
},
render(createElement) {
return createElement('registry-app', {
props: {
endpoint: this.endpoint,
characterError: this.characterError,
containersErrorImage: this.containersErrorImage,
endpoint: this.endpoint,
helpPagePath: this.helpPagePath,
noContainersImage: this.noContainersImage,
containersErrorImage: this.containersErrorImage,
personalAccessTokensHelpLink: this.personalAccessTokensHelpLink,
registryHostUrlWithPort: this.registryHostUrlWithPort,
repositoryUrl: this.repositoryUrl,
twoFactorAuthHelpLink: this.twoFactorAuthHelpLink,
},
});
},

View File

@ -39,9 +39,6 @@ export default {
ariaLabel() {
return this.isCollapsed ? __('Expand') : __('Collapse');
},
isButtonDisabled() {
return this.isLoading || this.hasError;
},
},
methods: {
toggleCollapsed() {
@ -53,25 +50,35 @@ export default {
<template>
<div>
<div class="mr-widget-extension d-flex align-items-center pl-3">
<gl-button
class="btn-blank btn s32 square append-right-default"
:aria-label="ariaLabel"
:disabled="isButtonDisabled"
@click="toggleCollapsed"
>
<gl-loading-icon v-if="isLoading" />
<icon v-else :name="arrowIconName" class="js-icon" />
</gl-button>
<gl-button
variant="link"
class="js-title"
:disabled="isButtonDisabled"
:class="{ 'border-0': isButtonDisabled }"
@click="toggleCollapsed"
>
<template v-if="isCollapsed">{{ title }}</template>
<template v-else>{{ __('Collapse') }}</template>
</gl-button>
<div v-if="hasError" class="ci-widget media">
<div class="media-body">
<span class="gl-font-size-small mr-widget-margin-left gl-line-height-24 js-error-state">{{
title
}}</span>
</div>
</div>
<template v-else>
<gl-button
class="btn-blank btn s32 square append-right-default"
:aria-label="ariaLabel"
:disabled="isLoading"
@click="toggleCollapsed"
>
<gl-loading-icon v-if="isLoading" />
<icon v-else :name="arrowIconName" class="js-icon" />
</gl-button>
<gl-button
variant="link"
class="js-title"
:disabled="isLoading"
:class="{ 'border-0': isLoading }"
@click="toggleCollapsed"
>
<template v-if="isCollapsed">{{ title }}</template>
<template v-else>{{ __('Collapse') }}</template>
</gl-button>
</template>
</div>
<div v-if="!isCollapsed" class="border-top js-slot-container">

View File

@ -560,3 +560,6 @@ img.emoji {
}
}
}
.gl-font-size-small { font-size: $gl-font-size-small; }
.gl-line-height-24 { line-height: $gl-line-height-24; }

View File

@ -833,6 +833,7 @@ Merge Requests
*/
$mr-tabs-height: 48px;
$mr-version-controls-height: 56px;
$mr-widget-margin-left: 40px;
/*
Compare Branches

View File

@ -19,6 +19,8 @@
border-top: 1px solid $border-color;
}
.mr-widget-margin-left { margin-left: $mr-widget-margin-left; }
.media-section {
@include media-breakpoint-down(md) {
align-items: flex-start;

View File

@ -108,6 +108,11 @@ module ApplicationHelper
Gitlab.config.extra
end
# shortcut for gitlab registry config
def registry_config
Gitlab.config.registry
end
# Render a `time` element with Javascript-based relative date and tooltip
#
# time - Time object

View File

@ -67,7 +67,7 @@ class ContainerRepository < ApplicationRecord
def delete_tags!
return unless has_tags?
digests = tags.map { |tag| tag.digest }.to_set
digests = tags.map { |tag| tag.digest }.compact.to_set
digests.all? do |digest|
delete_tag_by_digest(digest)

View File

@ -48,10 +48,10 @@ module Projects
# rubocop: disable CodeReuse/ActiveRecord
Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many?
# deletes the dummy image
# all created tag digests are the same since they all have the same dummy image.
# Deletes the dummy image
# All created tag digests are the same since they all have the same dummy image.
# a single delete is sufficient to remove all tags with it
if container_repository.client.delete_repository_tag(container_repository.path, tag_digests.first)
if container_repository.delete_tag_by_digest(tag_digests.first)
success(deleted: tag_names)
else
error('could not delete tags')

View File

@ -5,7 +5,10 @@
.col-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json),
"help_page_path" => help_page_path('user/packages/container_registry/index'),
"two_factor_auth_help_link" => help_page_path('user/profile/account/two_factor_authentication'),
"personal_access_tokens_help_link" => help_page_path('user/profile/personal_access_tokens'),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"repository_url" => escape_once(@project.container_registry_url),
"registry_host_url_with_port" => escape_once(registry_config.host_port),
character_error: @character_error.to_s } }

View File

@ -3,6 +3,4 @@
.form-check
= form.check_box :request_access_enabled, class: 'form-check-input', data: { qa_selector: 'request_access_checkbox' }
= form.label :request_access_enabled, class: 'form-check-label' do
%span{ class: label_class }= _('Allow users to request access')
%br
%span.text-muted= _('Allow users to request access if visibility is public or internal.')
%span{ class: label_class }= _('Allow users to request access (if visibility is public or internal)')

View File

@ -0,0 +1,5 @@
---
title: Enable Request Access functionality by default for new projects and groups
merge_request: 17662
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Fix Web IDE tree not updating modified status
merge_request: 18647
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Adds login input with copy box and supporting copy to empty container registry view
merge_request: 18244
author: nate geslin
type: added

View File

@ -0,0 +1,5 @@
---
title: Expose subscribed attribute for epic on API
merge_request: 18475
author:
type: added

View File

@ -34,6 +34,12 @@ Sidekiq.configure_server do |config|
config.on(:startup) do
# webserver metrics are cleaned up in config.ru: `warmup` block
Prometheus::CleanupMultiprocDirService.new.execute
# In production, sidekiq is run in a multi-process setup where processes might interfere
# with each other cleaning up and reinitializing prometheus database files, which is why
# we're re-doing the work every time here.
# A cleaner solution would be to run the cleanup pre-fork, and the initialization once
# after all workers have forked, but I don't know how at this point.
::Prometheus::Client.reinitialize_on_pid_change(force: true)
Gitlab::Metrics::Exporter::SidekiqExporter.instance.start
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DefaultRequestAccessGroups < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
change_column_default :namespaces, :request_access_enabled, true
end
def down
change_column_default :namespaces, :request_access_enabled, false
end
end

View File

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DefaultRequestAccessProjects < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
change_column_default :projects, :request_access_enabled, true
end
def down
change_column_default :projects, :request_access_enabled, false
end
end

View File

@ -2333,7 +2333,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do
t.boolean "membership_lock", default: false
t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: false, null: false
t.boolean "request_access_enabled", default: true, null: false
t.string "ldap_sync_status", default: "ready", null: false
t.string "ldap_sync_error"
t.datetime "ldap_sync_last_update_at"
@ -2922,7 +2922,7 @@ ActiveRecord::Schema.define(version: 2019_10_04_133612) do
t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false
t.boolean "repository_read_only"
t.boolean "request_access_enabled", default: false, null: false
t.boolean "request_access_enabled", default: true, null: false
t.boolean "has_external_wiki"
t.string "ci_config_path"
t.boolean "lfs_enabled"

View File

@ -147,7 +147,8 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
"downvotes": 0,
"subscribed": true
}
```

View File

@ -36,7 +36,9 @@ module ContainerRegistry
end
def delete_repository_tag(name, reference)
faraday.delete("/v2/#{name}/manifests/#{reference}").success?
result = faraday.delete("/v2/#{name}/manifests/#{reference}")
result.success? || result.status == 404
end
def upload_raw_blob(path, blob)
@ -84,7 +86,9 @@ module ContainerRegistry
end
def delete_blob(name, digest)
faraday.delete("/v2/#{name}/blobs/#{digest}").success?
result = faraday.delete("/v2/#{name}/blobs/#{digest}")
result.success? || result.status == 404
end
def put_tag(name, reference, manifest)

View File

@ -5,7 +5,7 @@ module Gitlab
class GitalyCheck
extend BaseAbstractCheck
METRIC_PREFIX = 'gitaly_health_check'
METRIC_PREFIX = 'gitaly_health_check'.freeze
class << self
def readiness

View File

@ -6,10 +6,10 @@ module Gitlab
class Readiness
attr_reader :checks
# This accepts an array of Proc
# This accepts an array of objects implementing `:readiness`
# that returns `::Gitlab::HealthChecks::Result`
def initialize(*additional_checks)
@checks = ::Gitlab::HealthChecks::CHECKS.map { |check| check.method(:readiness) }
@checks = ::Gitlab::HealthChecks::CHECKS
@checks += additional_checks
end
@ -43,7 +43,7 @@ module Gitlab
def probe_readiness
checks
.flat_map(&:call)
.flat_map(&:readiness)
.compact
.group_by(&:name)
end

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
module Gitlab
module HealthChecks
# This check can only be run on Puma `master` process
class PumaCheck
extend SimpleAbstractCheck
class << self
private
def metric_prefix
'puma_check'
end
def successful?(result)
result > 0
end
def check
return unless defined?(::Puma)
stats = Puma.stats
stats = JSON.parse(stats)
# If `workers` is missing this means that
# Puma server is running in single mode
stats.fetch('workers', 1)
rescue NoMethodError
# server is not ready
0
end
end
end
end
end

View File

@ -7,6 +7,8 @@ module Gitlab
def readiness
check_result = check
return if check_result.nil?
if successful?(check_result)
HealthChecks::Result.new(name, true)
elsif check_result.is_a?(Timeout::Error)
@ -20,6 +22,8 @@ module Gitlab
def metrics
result, elapsed = with_timing(&method(:check))
return if result.nil?
Rails.logger.error("#{human_name} check returned unexpected result #{result}") unless successful?(result) # rubocop:disable Gitlab/RailsLogger
[
metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0),

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
module Gitlab
module HealthChecks
# This check can only be run on Unicorn `master` process
class UnicornCheck
extend SimpleAbstractCheck
class << self
include Gitlab::Utils::StrongMemoize
private
def metric_prefix
'unicorn_check'
end
def successful?(result)
result > 0
end
def check
return unless http_servers
http_servers.sum(&:worker_processes) # rubocop: disable CodeReuse/ActiveRecord
end
# Traversal of ObjectSpace is expensive, on fully loaded application
# it takes around 80ms. The instances of HttpServers are not a subject
# to change so we can cache the list of servers.
def http_servers
strong_memoize(:http_servers) do
next unless defined?(::Unicorn::HttpServer)
ObjectSpace.each_object(::Unicorn::HttpServer).to_a
end
end
end
end
end
end

View File

@ -6,6 +6,8 @@ module Gitlab
class BaseExporter < Daemon
attr_reader :server
attr_accessor :additional_checks
def enabled?
settings.enabled
end
@ -32,12 +34,10 @@ module Gitlab
Port: settings.port, BindAddress: settings.address,
Logger: logger, AccessLog: access_log)
server.mount_proc '/readiness' do |req, res|
render_probe(
::Gitlab::HealthChecks::Probes::Readiness.new, req, res)
render_probe(readiness_probe, req, res)
end
server.mount_proc '/liveness' do |req, res|
render_probe(
::Gitlab::HealthChecks::Probes::Liveness.new, req, res)
render_probe(liveness_probe, req, res)
end
server.mount '/', Rack::Handler::WEBrick, rack_app
@ -52,8 +52,10 @@ module Gitlab
def stop_working
if server
# we close sockets if thread is not longer running
# this happens, when the process forks
server.listeners.each(&:close) unless thread.alive?
server.shutdown
server.listeners.each(&:close)
end
@server = nil
@ -67,6 +69,14 @@ module Gitlab
end
end
def readiness_probe
::Gitlab::HealthChecks::Probes::Readiness.new(*additional_checks)
end
def liveness_probe
::Gitlab::HealthChecks::Probes::Liveness.new
end
def render_probe(probe, req, res)
result = probe.execute

View File

@ -7,6 +7,16 @@ module Gitlab
module Metrics
module Exporter
class WebExporter < BaseExporter
# This exporter is always run on master process
def initialize
super
self.additional_checks = [
Gitlab::HealthChecks::PumaCheck,
Gitlab::HealthChecks::UnicornCheck
]
end
def settings
Settings.monitoring.web_exporter
end

View File

@ -1413,10 +1413,7 @@ msgstr ""
msgid "Allow users to register any application to use GitLab as an OAuth provider"
msgstr ""
msgid "Allow users to request access"
msgstr ""
msgid "Allow users to request access if visibility is public or internal."
msgid "Allow users to request access (if visibility is public or internal)"
msgstr ""
msgid "Allowed email domain restriction only permitted for top-level groups"
@ -4352,12 +4349,18 @@ msgstr ""
msgid "ContainerRegistry|Copy build command"
msgstr ""
msgid "ContainerRegistry|Copy login command"
msgstr ""
msgid "ContainerRegistry|Copy push command"
msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
msgid "ContainerRegistry|If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have %{twofaDocLinkStart}Two-Factor Authentication%{twofaDocLinkEnd} enabled, use a %{personalAccessTokensDocLinkStart}Personal Access Token %{personalAccessTokensDocLinkEnd}instead of a password."
msgstr ""
msgid "ContainerRegistry|Last Updated"
msgstr ""

View File

@ -153,7 +153,8 @@ redis:
redis-ha:
enabled: false
registry:
minReplicas: 1
hpa:
minReplicas: 1
resources:
requests:
cpu: 50m

View File

@ -6,7 +6,7 @@ describe Groups::GroupMembersController do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:membership) { create(:group_member, group: group) }
describe 'GET index' do

View File

@ -4,7 +4,7 @@ require('spec_helper')
describe Projects::ProjectMembersController do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
describe 'GET index' do
it 'has the project_members address with a 200 status code' do

View File

@ -32,8 +32,8 @@ FactoryBot.define do
avatar { fixture_file_upload('spec/fixtures/dk.png') }
end
trait :access_requestable do
request_access_enabled { true }
trait :request_access_disabled do
request_access_enabled { false }
end
trait :nested do

View File

@ -117,8 +117,8 @@ FactoryBot.define do
storage_version { nil }
end
trait :access_requestable do
request_access_enabled { true }
trait :request_access_disabled do
request_access_enabled { false }
end
trait :with_avatar do

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Groups > Members > Maintainer manages access requests' do
it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:group, :public, :access_requestable) }
let(:entity) { create(:group, :public) }
let(:members_page_path) { group_group_members_path(entity) }
end
end

View File

@ -5,7 +5,7 @@ require 'spec_helper'
describe 'Groups > Members > Request access' do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let!(:project) { create(:project, :private, namespace: group) }
before do

View File

@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects members' do
let(:user) { create(:user) }
let(:developer) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let(:project) { create(:project, :public, :access_requestable, creator: user, group: group) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, creator: user, group: group) }
let(:project_invitee) { create(:project_member, project: project, invite_token: '123', invite_email: 'test1@abc.com', user: nil) }
let(:group_invitee) { create(:group_member, group: group, invite_token: '123', invite_email: 'test2@abc.com', user: nil) }
let(:project_requester) { create(:user) }

View File

@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects > Members > Group requester cannot request access to project', :js do
let(:user) { create(:user) }
let(:owner) { create(:user) }
let(:group) { create(:group, :public, :access_requestable) }
let(:project) { create(:project, :public, :access_requestable, namespace: group) }
let(:group) { create(:group, :public) }
let(:project) { create(:project, :public, namespace: group) }
before do
group.add_owner(owner)

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Projects > Members > Maintainer manages access requests' do
it_behaves_like 'Maintainer manages access requests' do
let(:entity) { create(:project, :public, :access_requestable) }
let(:entity) { create(:project, :public) }
let(:members_page_path) { project_project_members_path(entity) }
end
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe 'Projects > Members > User requests access', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable, :repository) }
let(:project) { create(:project, :public, :repository) }
let(:maintainer) { project.owner }
before do

View File

@ -7,13 +7,13 @@ describe AccessRequestsFinder do
let(:access_requester) { create(:user) }
let(:project) do
create(:project, :public, :access_requestable) do |project|
create(:project, :public) do |project|
project.request_access(access_requester)
end
end
let(:group) do
create(:group, :public, :access_requestable) do |group|
create(:group, :public) do |group|
group.request_access(access_requester)
end
end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe GroupMembersFinder, '#execute' do
let(:group) { create(:group) }
let(:nested_group) { create(:group, :access_requestable, parent: group) }
let(:nested_group) { create(:group, parent: group) }
let(:user1) { create(:user) }
let(:user2) { create(:user) }
let(:user3) { create(:user) }

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe MembersFinder, '#execute' do
set(:group) { create(:group) }
set(:nested_group) { create(:group, :access_requestable, parent: group) }
set(:nested_group) { create(:group, parent: group) }
set(:project) { create(:project, namespace: nested_group) }
set(:user1) { create(:user) }
set(:user2) { create(:user) }
@ -57,7 +57,7 @@ describe MembersFinder, '#execute' do
context 'when include_invited_groups_members == true' do
subject { described_class.new(project, user2).execute(include_invited_groups_members: true) }
set(:linked_group) { create(:group, :public, :access_requestable) }
set(:linked_group) { create(:group, :public) }
set(:nested_linked_group) { create(:group, parent: linked_group) }
set(:linked_group_member) { linked_group.add_guest(user1) }
set(:nested_linked_group_member) { nested_linked_group.add_guest(user2) }

View File

@ -0,0 +1,100 @@
import { decorateFiles } from '~/ide/lib/files';
import { createStore } from '~/ide/stores';
const TEST_BRANCH = 'test_branch';
const TEST_NAMESPACE = 'test_namespace';
const TEST_PROJECT_ID = `${TEST_NAMESPACE}/test_project`;
const TEST_PATH_DIR = 'src';
const TEST_PATH = `${TEST_PATH_DIR}/foo.js`;
const TEST_CONTENT = `Lorem ipsum dolar sit
Lorem ipsum dolar
Lorem ipsum
Lorem
`;
jest.mock('~/ide/ide_router');
describe('IDE store integration', () => {
let store;
beforeEach(() => {
store = createStore();
store.replaceState({
...store.state,
projects: {
[TEST_PROJECT_ID]: {
web_url: 'test_web_url',
branches: [],
},
},
currentProjectId: TEST_PROJECT_ID,
currentBranchId: TEST_BRANCH,
});
});
describe('with project and files', () => {
beforeEach(() => {
const { entries, treeList } = decorateFiles({
data: [`${TEST_PATH_DIR}/`, TEST_PATH, 'README.md'],
projectId: TEST_PROJECT_ID,
branchId: TEST_BRANCH,
});
Object.assign(entries[TEST_PATH], {
raw: TEST_CONTENT,
});
store.replaceState({
...store.state,
trees: {
[`${TEST_PROJECT_ID}/${TEST_BRANCH}`]: {
tree: treeList,
},
},
entries,
});
});
describe('when a file is deleted and readded', () => {
beforeEach(() => {
store.dispatch('deleteEntry', TEST_PATH);
store.dispatch('createTempEntry', { name: TEST_PATH, type: 'blob' });
});
it('has changed and staged', () => {
expect(store.state.changedFiles).toEqual([
expect.objectContaining({
path: TEST_PATH,
tempFile: true,
deleted: false,
}),
]);
expect(store.state.stagedFiles).toEqual([
expect.objectContaining({
path: TEST_PATH,
deleted: true,
}),
]);
});
it('cleans up after commit', () => {
const expected = expect.objectContaining({
path: TEST_PATH,
staged: false,
changed: false,
tempFile: false,
deleted: false,
});
store.dispatch('stageChange', TEST_PATH);
store.dispatch('commit/updateFilesAfterCommit', { data: {} });
expect(store.state.entries[TEST_PATH]).toEqual(expected);
expect(store.state.entries[TEST_PATH_DIR].tree.find(x => x.path === TEST_PATH)).toEqual(
expected,
);
});
});
});
});

View File

@ -1,5 +1,5 @@
import registry from '~/registry/components/app.vue';
import { mount } from '@vue/test-utils';
import registry from '~/registry/components/app.vue';
import { TEST_HOST } from '../../helpers/test_constants';
import { reposServerResponse, parsedReposServerResponse } from '../mock_data';
@ -8,6 +8,7 @@ describe('Registry List', () => {
const findCollapsibleContainer = w => w.findAll({ name: 'CollapsibeContainerRegisty' });
const findNoContainerImagesText = w => w.find('.js-no-container-images-text');
const findNotLoggedInToRegistryText = w => w.find('.js-not-logged-in-to-registry-text');
const findSpinner = w => w.find('.gl-spinner');
const findCharacterErrorText = w => w.find('.js-character-error-text');
@ -17,6 +18,9 @@ describe('Registry List', () => {
noContainersImage: 'foo',
containersErrorImage: 'foo',
repositoryUrl: 'foo',
registryHostUrlWithPort: 'foo',
personalAccessTokensHelpLink: 'foo',
twoFactorAuthHelpLink: 'foo',
};
const setMainEndpoint = jest.fn();
@ -67,6 +71,13 @@ describe('Registry List', () => {
'With the Container Registry, every project can have its own space to store its Docker images. More Information',
);
});
it('should render login help text', () => {
const notLoggedInToRegistryText = findNotLoggedInToRegistryText(localWrapper);
expect(notLoggedInToRegistryText.text()).toEqual(
'If you are not already logged in, you need to authenticate to the Container Registry by using your GitLab username and password. If you have Two-Factor Authentication enabled, use a Personal Access Token instead of a password.',
);
});
});
describe('while loading data', () => {

View File

@ -44,6 +44,7 @@ describe('Merge Requests Artifacts list app', () => {
const findButtons = () => wrapper.findAll('button');
const findTitle = () => wrapper.find('.js-title');
const findErrorMessage = () => wrapper.find('.js-error-state');
const findTableRows = () => wrapper.findAll('tbody tr');
describe('while loading', () => {
@ -109,13 +110,12 @@ describe('Merge Requests Artifacts list app', () => {
});
it('renders the error state', () => {
expect(findTitle().text()).toBe('An error occurred while fetching the artifacts');
expect(findErrorMessage().text()).toBe('An error occurred while fetching the artifacts');
});
it('renders disabled buttons', () => {
it('does not render buttons', () => {
const buttons = findButtons();
expect(buttons.at(0).attributes('disabled')).toBe('disabled');
expect(buttons.at(1).attributes('disabled')).toBe('disabled');
expect(buttons.exists()).toBe(false);
});
});
});

View File

@ -20,6 +20,7 @@ describe('Merge Request Collapsible Extension', () => {
};
const findTitle = () => wrapper.find('.js-title');
const findErrorMessage = () => wrapper.find('.js-error-state');
afterEach(() => {
wrapper.destroy();
@ -87,19 +88,12 @@ describe('Merge Request Collapsible Extension', () => {
mountComponent(Object.assign({}, data, { hasError: true }));
});
it('renders the buttons disabled', () => {
expect(
wrapper
.findAll('button')
.at(0)
.attributes('disabled'),
).toEqual('disabled');
expect(
wrapper
.findAll('button')
.at(1)
.attributes('disabled'),
).toEqual('disabled');
it('does not render the buttons', () => {
expect(wrapper.findAll('button').exists()).toBe(false);
});
it('renders title message provided', () => {
expect(findErrorMessage().text()).toBe(data.title);
});
});
});

View File

@ -5,11 +5,11 @@ require 'spec_helper'
describe MembersHelper do
describe '#remove_member_message' do
let(:requester) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:project_member) { build(:project_member, project: project) }
let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } }
let(:project_member_request) { project.request_access(requester) }
let(:group) { create(:group, :access_requestable) }
let(:group) { create(:group) }
let(:group_member) { build(:group_member, group: group) }
let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } }
let(:group_member_request) { group.request_access(requester) }
@ -26,10 +26,10 @@ describe MembersHelper do
describe '#remove_member_title' do
let(:requester) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:project_member) { build(:project_member, project: project) }
let(:project_member_request) { project.request_access(requester) }
let(:group) { create(:group, :access_requestable) }
let(:group) { create(:group) }
let(:group_member) { build(:group_member, group: group) }
let(:group_member_request) { group.request_access(requester) }

View File

@ -0,0 +1,65 @@
require 'spec_helper'
describe Gitlab::HealthChecks::PumaCheck do
let(:result_class) { Gitlab::HealthChecks::Result }
let(:readiness) { described_class.readiness }
let(:metrics) { described_class.metrics }
shared_examples 'with state' do |(state, message)|
it "does provide readiness" do
expect(readiness).to eq(result_class.new('puma_check', state, message))
end
it "does provide metrics" do
expect(metrics).to include(
an_object_having_attributes(name: 'puma_check_success', value: state ? 1 : 0))
expect(metrics).to include(
an_object_having_attributes(name: 'puma_check_latency_seconds', value: be >= 0))
end
end
context 'when Puma is not loaded' do
before do
hide_const('Puma')
end
it "does not provide readiness and metrics" do
expect(readiness).to be_nil
expect(metrics).to be_nil
end
end
context 'when Puma is loaded' do
before do
stub_const('Puma', Module.new)
end
context 'when stats are missing' do
before do
expect(Puma).to receive(:stats).and_raise(NoMethodError)
end
it_behaves_like 'with state', [false, 'unexpected Puma check result: 0']
end
context 'for Single mode' do
before do
expect(Puma).to receive(:stats) do
'{}'
end
end
it_behaves_like 'with state', true
end
context 'for Cluster mode' do
before do
expect(Puma).to receive(:stats) do
'{"workers":2}'
end
end
it_behaves_like 'with state', true
end
end
end

View File

@ -0,0 +1,63 @@
require 'spec_helper'
describe Gitlab::HealthChecks::UnicornCheck do
let(:result_class) { Gitlab::HealthChecks::Result }
let(:readiness) { described_class.readiness }
let(:metrics) { described_class.metrics }
before do
described_class.clear_memoization(:http_servers)
end
shared_examples 'with state' do |(state, message)|
it "does provide readiness" do
expect(readiness).to eq(result_class.new('unicorn_check', state, message))
end
it "does provide metrics" do
expect(metrics).to include(
an_object_having_attributes(name: 'unicorn_check_success', value: state ? 1 : 0))
expect(metrics).to include(
an_object_having_attributes(name: 'unicorn_check_latency_seconds', value: be >= 0))
end
end
context 'when Unicorn is not loaded' do
before do
hide_const('Unicorn')
end
it "does not provide readiness and metrics" do
expect(readiness).to be_nil
expect(metrics).to be_nil
end
end
context 'when Unicorn is loaded' do
let(:http_server_class) { Struct.new(:worker_processes) }
before do
stub_const('Unicorn::HttpServer', http_server_class)
end
context 'when no servers are running' do
it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0']
end
context 'when servers without workers are running' do
before do
http_server_class.new(0)
end
it_behaves_like 'with state', [false, 'unexpected Unicorn check result: 0']
end
context 'when servers with workers are running' do
before do
http_server_class.new(1)
end
it_behaves_like 'with state', true
end
end
end

View File

@ -64,6 +64,18 @@ describe Gitlab::Metrics::Exporter::BaseExporter do
exporter.start.join
end
end
describe 'when thread is not alive' do
it 'does close listeners' do
expect_any_instance_of(::WEBrick::HTTPServer).to receive(:start)
expect_any_instance_of(::WEBrick::HTTPServer).to receive(:listeners)
.and_call_original
expect { exporter.start.join }.to change { exporter.thread? }.from(false).to(true)
exporter.stop
end
end
end
describe '#stop' do

View File

@ -714,7 +714,7 @@ describe Notify do
describe 'project access requested' do
let(:project) do
create(:project, :public, :access_requestable) do |project|
create(:project, :public) do |project|
project.add_maintainer(project.owner)
end
end
@ -743,7 +743,7 @@ describe Notify do
end
describe 'project access denied' do
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:project_member) do
project.request_access(user)
project.requesters.find_by(user_id: user.id)
@ -765,7 +765,7 @@ describe Notify do
describe 'project access changed' do
let(:owner) { create(:user, name: "Chang O'Keefe") }
let(:project) { create(:project, :public, :access_requestable, namespace: owner.namespace) }
let(:project) { create(:project, :public, namespace: owner.namespace) }
let(:project_member) { create(:project_member, project: project, user: user) }
subject { described_class.member_access_granted_email('project', project_member.id) }
@ -1167,7 +1167,7 @@ describe Notify do
context 'for a group' do
describe 'group access requested' do
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:group_member) do
group.request_access(user)
group.requesters.find_by(user_id: user.id)

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe Ci::BuildMetadata do
set(:user) { create(:user) }
set(:group) { create(:group, :access_requestable) }
set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group, build_timeout: 2000) }
set(:pipeline) do

View File

@ -4,7 +4,7 @@ require 'spec_helper'
describe Ci::Build do
set(:user) { create(:user) }
set(:group) { create(:group, :access_requestable) }
set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group) }
set(:pipeline) do

View File

@ -5,7 +5,7 @@ require 'spec_helper'
describe AccessRequestable do
describe 'Group' do
describe '#request_access' do
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:user) { create(:user) }
it { expect(group.request_access(user)).to be_a(GroupMember) }
@ -13,7 +13,7 @@ describe AccessRequestable do
end
describe '#access_requested?' do
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:user) { create(:user) }
before do
@ -26,14 +26,14 @@ describe AccessRequestable do
describe 'Project' do
describe '#request_access' do
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
it { expect(project.request_access(user)).to be_a(ProjectMember) }
end
describe '#access_requested?' do
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do

View File

@ -3,7 +3,7 @@
require 'spec_helper'
describe Group do
let!(:group) { create(:group, :access_requestable) }
let!(:group) { create(:group) }
describe 'associations' do
it { is_expected.to have_many :projects }
@ -331,7 +331,7 @@ describe Group do
end
describe '#avatar_url' do
let!(:group) { create(:group, :access_requestable, :with_avatar) }
let!(:group) { create(:group, :with_avatar) }
let(:user) { create(:user) }
context 'when avatar file is uploaded' do

View File

@ -92,7 +92,7 @@ describe Member do
describe 'Scopes & finders' do
before do
project = create(:project, :public, :access_requestable)
project = create(:project, :public)
group = create(:group)
@owner_user = create(:user).tap { |u| group.add_owner(u) }
@owner = group.members.find_by(user_id: @owner_user.id)
@ -230,7 +230,7 @@ describe Member do
describe '.add_user' do
%w[project group].each do |source_type|
context "when source is a #{source_type}" do
let!(:source) { create(source_type, :public, :access_requestable) }
let!(:source) { create(source_type, :public) }
let!(:user) { create(:user) }
let!(:admin) { create(:admin) }
@ -437,7 +437,7 @@ describe Member do
describe '.add_users' do
%w[project group].each do |source_type|
context "when source is a #{source_type}" do
let!(:source) { create(source_type, :public, :access_requestable) }
let!(:source) { create(source_type, :public) }
let!(:admin) { create(:admin) }
let(:user1) { create(:user) }
let(:user2) { create(:user) }

View File

@ -153,7 +153,7 @@ describe Project do
end
describe '#members & #requesters' do
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:requester) { create(:user) }
let(:developer) { create(:user) }
before do

View File

@ -141,7 +141,7 @@ describe ProjectTeam do
describe '#find_member' do
context 'personal project' do
let(:project) do
create(:project, :public, :access_requestable)
create(:project, :public)
end
let(:requester) { create(:user) }
@ -161,7 +161,7 @@ describe ProjectTeam do
end
context 'group project' do
let(:group) { create(:group, :access_requestable) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:requester) { create(:user) }
@ -246,7 +246,7 @@ describe ProjectTeam do
context 'personal project' do
let(:project) do
create(:project, :public, :access_requestable)
create(:project, :public)
end
context 'when project is not shared with group' do
@ -292,7 +292,7 @@ describe ProjectTeam do
end
context 'group project' do
let(:group) { create(:group, :access_requestable) }
let(:group) { create(:group) }
let!(:project) do
create(:project, group: group)
end

View File

@ -79,7 +79,7 @@ describe User do
describe '#group_members' do
it 'does not include group memberships for which user is a requester' do
user = create(:user)
group = create(:group, :public, :access_requestable)
group = create(:group, :public)
group.request_access(user)
expect(user.group_members).to be_empty
@ -89,7 +89,7 @@ describe User do
describe '#project_members' do
it 'does not include project memberships for which user is a requester' do
user = create(:user)
project = create(:project, :public, :access_requestable)
project = create(:project, :public)
project.request_access(user)
expect(user.project_members).to be_empty
@ -1191,7 +1191,7 @@ describe User do
end
describe '.without_projects' do
let!(:project) { create(:project, :public, :access_requestable) }
let!(:project) { create(:project, :public) }
let!(:user) { create(:user) }
let!(:user_without_project) { create(:user) }
let!(:user_without_project2) { create(:user) }

View File

@ -7,7 +7,7 @@ describe API::AccessRequests do
set(:stranger) { create(:user) }
set(:project) do
create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
project.add_maintainer(maintainer)
project.request_access(access_requester)
@ -15,7 +15,7 @@ describe API::AccessRequests do
end
set(:group) do
create(:group, :public, :access_requestable) do |group|
create(:group, :public) do |group|
group.add_developer(developer)
group.add_owner(maintainer)
group.request_access(access_requester)

View File

@ -345,7 +345,7 @@ describe API::Badges do
end
def setup_project
create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: project_group) do |project|
create(:project, :public, creator_id: maintainer.id, namespace: project_group) do |project|
project.add_developer(developer)
project.add_maintainer(maintainer)
project.request_access(access_requester)
@ -356,7 +356,7 @@ describe API::Badges do
end
def setup_group
create(:group, :public, :access_requestable) do |group|
create(:group, :public) do |group|
group.add_developer(developer)
group.add_owner(maintainer)
group.request_access(access_requester)

View File

@ -7,7 +7,7 @@ describe API::Members do
let(:stranger) { create(:user) }
let(:project) do
create(:project, :public, :access_requestable, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
create(:project, :public, creator_id: maintainer.id, namespace: maintainer.namespace) do |project|
project.add_developer(developer)
project.add_maintainer(maintainer)
project.request_access(access_requester)
@ -15,7 +15,7 @@ describe API::Members do
end
let!(:group) do
create(:group, :public, :access_requestable) do |group|
create(:group, :public) do |group|
group.add_developer(developer)
group.add_owner(maintainer)
group.request_access(access_requester)

View File

@ -3,8 +3,8 @@
require 'spec_helper'
describe Members::ApproveAccessRequestService do
let(:project) { create(:project, :public, :access_requestable) }
let(:group) { create(:group, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:group) { create(:group, :public) }
let(:current_user) { create(:user) }
let(:access_requester_user) { create(:user) }
let(:access_requester) { source.requesters.find_by!(user_id: access_requester_user.id) }

View File

@ -41,7 +41,7 @@ describe Members::RequestAccessService do
context 'when access requests are disabled' do
%i[project group].each do |source_type|
it_behaves_like 'a service raising Gitlab::Access::AccessDeniedError' do
let(:source) { create(source_type, :public) }
let(:source) { create(source_type, :public, :request_access_disabled) }
end
end
end
@ -49,7 +49,7 @@ describe Members::RequestAccessService do
context 'when current user can request access to the project' do
%i[project group].each do |source_type|
it_behaves_like 'a service creating a access request' do
let(:source) { create(source_type, :public, :access_requestable) }
let(:source) { create(source_type, :public) }
end
end
end

View File

@ -1942,7 +1942,7 @@ describe NotificationService, :mailer do
let(:developer) { create(:user) }
let!(:group) do
create(:group, :public, :access_requestable) do |group|
create(:group, :public) do |group|
group.add_owner(owner)
group.add_maintainer(maintainer)
group.add_developer(developer)
@ -1968,7 +1968,7 @@ describe NotificationService, :mailer do
end
it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:notification_trigger) { group.request_access(added_user) }
end
end
@ -2029,7 +2029,7 @@ describe NotificationService, :mailer do
let(:maintainer) { create(:user) }
let!(:project) do
create(:project, :public, :access_requestable) do |project|
create(:project, :public) do |project|
project.add_developer(developer)
project.add_maintainer(maintainer)
end
@ -2053,7 +2053,7 @@ describe NotificationService, :mailer do
end
it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do
let(:project) { create(:project, :public, :access_requestable) }
let(:project) { create(:project, :public) }
let(:notification_trigger) { project.request_access(added_user) }
end
end
@ -2064,7 +2064,7 @@ describe NotificationService, :mailer do
context 'when the project has no maintainers' do
context 'when the group has at least one owner' do
let!(:project) { create(:project, :public, :access_requestable, namespace: group) }
let!(:project) { create(:project, :public, namespace: group) }
before do
reset_delivered_emails!
@ -2079,14 +2079,14 @@ describe NotificationService, :mailer do
end
it_behaves_like 'sends notification only to a maximum of ten, most recently active group owners' do
let(:group) { create(:group, :public, :access_requestable) }
let(:group) { create(:group, :public) }
let(:notification_trigger) { project.request_access(added_user) }
end
end
context 'when the group does not have any owners' do
let(:group) { create(:group) }
let!(:project) { create(:project, :public, :access_requestable, namespace: group) }
let!(:project) { create(:project, :public, namespace: group) }
context 'recipients' do
before do
@ -2107,7 +2107,7 @@ describe NotificationService, :mailer do
let(:developer) { create(:user) }
let!(:project) do
create(:project, :public, :access_requestable, namespace: group) do |project|
create(:project, :public, namespace: group) do |project|
project.add_maintainer(maintainer)
project.add_developer(developer)
end
@ -2128,7 +2128,7 @@ describe NotificationService, :mailer do
end
it_behaves_like 'sends notification only to a maximum of ten, most recently active project maintainers' do
let(:project) { create(:project, :public, :access_requestable, namespace: group) }
let(:project) { create(:project, :public, namespace: group) }
let(:notification_trigger) { project.request_access(added_user) }
end
end

View File

@ -157,6 +157,6 @@ describe Projects::ContainerRepository::CleanupTagsService do
def expect_delete(digest)
expect_any_instance_of(ContainerRegistry::Client)
.to receive(:delete_repository_tag)
.with(repository.path, digest)
.with(repository.path, digest) { true }
end
end

View File

@ -87,6 +87,21 @@ describe Projects::ContainerRepository::DeleteTagsService do
is_expected.to include(status: :success)
end
it 'succedes when tag delete returns 404' do
stub_upload("{\n \"config\": {\n }\n}", 'sha256:4435000728ee66e6a80e55637fc22725c256b61de344a2ecdeaac6bdb36e8bc3')
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/A")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:put, "http://registry.gitlab/v2/#{repository.path}/manifests/Ba")
.to_return(status: 200, body: "", headers: { 'docker-content-digest' => 'sha256:dummy' })
stub_request(:delete, "http://registry.gitlab/v2/#{repository.path}/manifests/sha256:dummy")
.to_return(status: 404, body: "", headers: {})
is_expected.to include(status: :success)
end
end
end
end