Commit d2ffc30f authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 914ea32e
......@@ -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(
......
......@@ -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__(
......
......@@ -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,
},
});
},
......
......@@ -39,9 +39,6 @@ export default {
ariaLabel() {
return this.isCollapsed ? __('Expand') : __('Collapse');
},
isButtonDisabled() {
return this.isLoading || this.hasError;
},
},
methods: {
toggleCollapsed() {
......@@ -53,10 +50,19 @@ export default {
<template>
<div>
<div class="mr-widget-extension d-flex align-items-center pl-3">
<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="isButtonDisabled"
:disabled="isLoading"
@click="toggleCollapsed"
>
<gl-loading-icon v-if="isLoading" />
......@@ -65,13 +71,14 @@ export default {
<gl-button
variant="link"
class="js-title"
:disabled="isButtonDisabled"
:class="{ 'border-0': isButtonDisabled }"
: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">
......
......@@ -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; }
......@@ -833,6 +833,7 @@ Merge Requests
*/
$mr-tabs-height: 48px;
$mr-version-controls-height: 56px;
$mr-widget-margin-left: 40px;
/*
Compare Branches
......
......@@ -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;
......
......@@ -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
......
......@@ -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)
......
......@@ -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')
......
......@@ -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 } }
......@@ -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)')
---
title: Enable Request Access functionality by default for new projects and groups
merge_request: 17662
author:
type: changed
---
title: Fix Web IDE tree not updating modified status
merge_request: 18647
author:
type: fixed
---
title: Adds login input with copy box and supporting copy to empty container registry view
merge_request: 18244
author: nate geslin
type: added
---
title: Expose subscribed attribute for epic on API
merge_request: 18475
author:
type: added
......@@ -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
......
# 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
# 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
......@@ -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"
......
......@@ -147,7 +147,8 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
"downvotes": 0,
"subscribed": true
}
```
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......
# 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
......@@ -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),
......
# 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
......@@ -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
......
......@@ -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
......
......@@ -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 ""
......
......@@ -153,6 +153,7 @@ redis:
redis-ha:
enabled: false
registry:
hpa:
minReplicas: 1
resources:
requests:
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......@@ -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
......
......@@ -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) }
......
......@@ -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)
......
......@@ -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
......@@ -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
......
......@@ -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
......
......@@ -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) }
......
......@@ -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) }
......
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,
);
});
});
});
});
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', () => {
......
......@@ -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);
});
});
});
......@@ -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);
});
});
});
......@@ -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) }
......
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
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
......@@ -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
......
......@@ -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)
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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) }
......
......@@ -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
......
......@@ -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
......
......@@ -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) }
......
......@@ -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)
......
......@@ -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)
......
......@@ -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)
......
......@@ -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) }
......
......@@ -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
......
......@@ -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
......
......@@ -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
......@@ -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
......
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