Skip to content
Snippets Groups Projects
Commit 184c2ced authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 238d22c0
No related branches found
No related tags found
No related merge requests found
Showing
with 249 additions and 138 deletions
Loading
Loading
@@ -130,6 +130,10 @@ export default {
 
return title;
},
shouldRenderHeaderCallout() {
return this.shouldRenderCalloutMessage && !this.hasUnmetPrerequisitesFailure;
},
},
watch: {
// Once the job log is loaded,
Loading
Loading
@@ -239,10 +243,9 @@ export default {
/>
</div>
 
<callout
v-if="shouldRenderCalloutMessage && !hasUnmetPrerequisitesFailure"
:message="job.callout_message"
/>
<callout v-if="shouldRenderHeaderCallout">
<div v-html="job.callout_message"></div>
</callout>
</header>
<!-- EO Header Section -->
 
Loading
Loading
/**
* Checks if the first argument is a subset of the second argument.
* @param {Set} subset The set to be considered as the subset.
* @param {Set} superset The set to be considered as the superset.
* @returns {boolean}
*/
// eslint-disable-next-line import/prefer-default-export
export const isSubset = (subset, superset) =>
Array.from(subset).every(value => superset.has(value));
<script>
import { mapActions, mapGetters } from 'vuex';
import { GlLoadingIcon, GlButton, GlTooltipDirective, GlModal, GlModalDirective } from '@gitlab/ui';
import {
GlLoadingIcon,
GlButton,
GlTooltipDirective,
GlModal,
GlModalDirective,
GlEmptyState,
} from '@gitlab/ui';
import createFlash from '../../flash';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import Icon from '../../vue_shared/components/icon.vue';
Loading
Loading
@@ -17,6 +24,7 @@ export default {
GlButton,
Icon,
GlModal,
GlEmptyState,
},
directives: {
GlTooltip: GlTooltipDirective,
Loading
Loading
@@ -103,10 +111,18 @@ export default {
 
<div v-else-if="!repo.isLoading && isOpen" class="container-image-tags">
<table-registry v-if="repo.list.length" :repo="repo" :can-delete-repo="canDeleteRepo" />
<div v-else class="nothing-here-block">
{{ s__('ContainerRegistry|No tags in Container Registry for this container image.') }}
</div>
<gl-empty-state
v-else
:title="s__('ContainerRegistry|This image has no active tags')"
:description="
s__(
`ContainerRegistry|The last tag related to this image was recently removed.
This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process.
If you have any questions, contact your administrator.`,
)
"
class="mx-auto my-0"
/>
</div>
<gl-modal :modal-id="modalId" ok-variant="danger" @ok="handleDeleteRepository">
<template v-slot:modal-title>{{ s__('ContainerRegistry|Remove repository') }}</template>
Loading
Loading
Loading
Loading
@@ -43,6 +43,7 @@ export default {
},
data() {
return {
selectedItems: [],
itemsToBeDeleted: [],
modalId: `confirm-image-deletion-modal-${this.repo.id}`,
selectAllChecked: false,
Loading
Loading
@@ -96,6 +97,7 @@ export default {
},
deleteSingleItem(index) {
this.setModalDescription(index);
this.itemsToBeDeleted = [index];
 
this.$refs.deleteModal.$refs.modal.$once('ok', () => {
this.removeModalEvents();
Loading
Loading
@@ -103,9 +105,10 @@ export default {
});
},
deleteMultipleItems() {
if (this.itemsToBeDeleted.length === 1) {
this.itemsToBeDeleted = [...this.selectedItems];
if (this.selectedItems.length === 1) {
this.setModalDescription(this.itemsToBeDeleted[0]);
} else if (this.itemsToBeDeleted.length > 1) {
} else if (this.selectedItems.length > 1) {
this.setModalDescription();
}
 
Loading
Loading
@@ -115,6 +118,7 @@ export default {
});
},
handleSingleDelete(itemToDelete) {
this.itemsToBeDeleted = [];
this.deleteItem(itemToDelete)
.then(() => this.fetchList({ repo: this.repo }))
.catch(() => this.showError(errorMessagesTypes.DELETE_REGISTRY));
Loading
Loading
@@ -122,6 +126,7 @@ export default {
handleMultipleDelete() {
const { itemsToBeDeleted } = this;
this.itemsToBeDeleted = [];
this.selectedItems = [];
 
if (this.bulkDeletePath) {
this.multiDeleteItems({
Loading
Loading
@@ -150,23 +155,23 @@ export default {
}
},
selectAll() {
this.itemsToBeDeleted = this.repo.list.map((x, index) => index);
this.selectedItems = this.repo.list.map((x, index) => index);
this.selectAllChecked = true;
},
deselectAll() {
this.itemsToBeDeleted = [];
this.selectedItems = [];
this.selectAllChecked = false;
},
updateItemsToBeDeleted(index) {
const delIndex = this.itemsToBeDeleted.findIndex(x => x === index);
updateselectedItems(index) {
const delIndex = this.selectedItems.findIndex(x => x === index);
 
if (delIndex > -1) {
this.itemsToBeDeleted.splice(delIndex, 1);
this.selectedItems.splice(delIndex, 1);
this.selectAllChecked = false;
} else {
this.itemsToBeDeleted.push(index);
this.selectedItems.push(index);
 
if (this.itemsToBeDeleted.length === this.repo.list.length) {
if (this.selectedItems.length === this.repo.list.length) {
this.selectAllChecked = true;
}
}
Loading
Loading
@@ -199,7 +204,7 @@ export default {
v-if="canDeleteRepo"
v-gl-tooltip
v-gl-modal="modalId"
:disabled="!itemsToBeDeleted || itemsToBeDeleted.length === 0"
:disabled="!selectedItems || selectedItems.length === 0"
class="js-delete-registry float-right"
data-track-event="click_button"
data-track-label="bulk_registry_tag_delete"
Loading
Loading
@@ -219,8 +224,8 @@ export default {
<gl-form-checkbox
v-if="canDeleteRow(item)"
class="js-select-checkbox"
:checked="itemsToBeDeleted && itemsToBeDeleted.includes(index)"
@change="updateItemsToBeDeleted(index)"
:checked="selectedItems && selectedItems.includes(index)"
@change="updateselectedItems(index)"
/>
</td>
<td class="monospace">
Loading
Loading
Loading
Loading
@@ -14,13 +14,12 @@
.blank-state-row {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
height: 100%;
justify-content: space-between;
}
 
.blank-state-welcome {
text-align: center;
padding: 20px 0 40px;
padding: $gl-padding 0 ($gl-padding * 2);
 
.blank-state-welcome-title {
font-size: 24px;
Loading
Loading
@@ -32,23 +31,9 @@
}
 
.blank-state-link {
display: block;
color: $gl-text-color;
flex: 0 0 100%;
margin-bottom: 15px;
 
@include media-breakpoint-up(sm) {
flex: 0 0 49%;
&:nth-child(odd) {
margin-right: 5px;
}
&:nth-child(even) {
margin-left: 5px;
}
}
&:hover {
background-color: $gray-light;
text-decoration: none;
Loading
Loading
@@ -63,15 +48,25 @@
}
 
.blank-state {
padding: 20px;
display: flex;
align-items: center;
padding: 20px 50px;
border: 1px solid $border-color;
border-radius: $border-radius-default;
min-height: 240px;
margin-bottom: $gl-padding;
width: calc(50% - #{$gl-padding-8});
@include media-breakpoint-down(sm) {
width: 100%;
flex-direction: column;
justify-content: center;
padding: 50px 20px;
.column-small & {
width: 100%;
}
 
@include media-breakpoint-up(sm) {
display: flex;
height: 100%;
align-items: center;
padding: 50px 30px;
}
}
 
Loading
Loading
@@ -90,7 +85,7 @@
}
 
.blank-state-body {
@include media-breakpoint-down(xs) {
@include media-breakpoint-down(sm) {
text-align: center;
margin-top: 20px;
}
Loading
Loading
@@ -121,9 +116,3 @@
}
}
}
@include media-breakpoint-down(xs) {
.blank-state-icon svg {
width: 315px;
}
}
Loading
Loading
@@ -69,10 +69,6 @@
 
details {
margin-bottom: $gl-padding;
summary {
margin-bottom: $gl-padding;
}
}
 
// Single code lines should wrap
Loading
Loading
Loading
Loading
@@ -4,6 +4,7 @@ module Groups
class RepositoriesController < Groups::ApplicationController
before_action :verify_container_registry_enabled!
before_action :authorize_read_container_image!
before_action :feature_flag_group_container_registry_browser!
 
def index
track_event(:list_repositories)
Loading
Loading
@@ -22,6 +23,10 @@ module Groups
 
private
 
def feature_flag_group_container_registry_browser!
render_404 unless Feature.enabled?(:group_container_registry_browser, group)
end
def verify_container_registry_enabled!
render_404 unless Gitlab.config.registry.enabled
end
Loading
Loading
Loading
Loading
@@ -22,7 +22,9 @@ module GroupsHelper
end
 
def group_container_registry_nav?
Gitlab.config.registry.enabled && can?(current_user, :read_container_image, @group)
Gitlab.config.registry.enabled &&
can?(current_user, :read_container_image, @group) &&
Feature.enabled?(:group_container_registry_browser, @group)
end
 
def group_sidebar_links
Loading
Loading
# frozen_string_literal: true
module Emails
module Releases
def new_release_email(user_id, release, reason = nil)
@release = release
@project = @release.project
@target_url = namespace_project_releases_url(
namespace_id: @project.namespace,
project_id: @project
)
user = User.find(user_id)
mail(
to: user.notification_email_for(@project.group),
subject: subject(release_email_subject)
)
end
private
def release_email_subject
release_info = [@release.name, @release.tag].select(&:presence).join(' - ')
"New release: #{release_info}"
end
end
end
Loading
Loading
@@ -16,6 +16,7 @@ class Notify < BaseMailer
include Emails::Members
include Emails::AutoDevops
include Emails::RemoteMirrors
include Emails::Releases
 
helper MilestonesHelper
helper MergeRequestsHelper
Loading
Loading
Loading
Loading
@@ -754,6 +754,10 @@ module Ci
true
end
 
def invalid_dependencies
dependencies.reject(&:valid_dependency?)
end
def runner_required_feature_names
strong_memoize(:runner_required_feature_names) do
RUNNER_FEATURES.select do |feature, method|
Loading
Loading
Loading
Loading
@@ -25,6 +25,7 @@ class NotificationSetting < ApplicationRecord
end
 
EMAIL_EVENTS = [
:new_release,
:new_note,
:new_issue,
:reopen_issue,
Loading
Loading
Loading
Loading
@@ -26,10 +26,12 @@ class Release < ApplicationRecord
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
 
scope :sorted, -> { order(released_at: :desc) }
scope :with_project_and_namespace, -> { includes(project: :namespace) }
 
delegate :repository, to: :project
 
after_commit :create_evidence!, on: :create
after_commit :notify_new_release, on: :create
 
def commit
strong_memoize(:commit) do
Loading
Loading
@@ -73,6 +75,10 @@ class Release < ApplicationRecord
def create_evidence!
CreateEvidenceWorker.perform_async(self.id)
end
def notify_new_release
NewReleaseWorker.perform_async(id)
end
end
 
Release.prepend_if_ee('EE::Release')
Loading
Loading
@@ -121,4 +121,28 @@ class BuildDetailsEntity < JobEntity
def can_admin_build?
can?(request.current_user, :admin_build, project)
end
def callout_message
return super unless build.failure_reason.to_sym == :missing_dependency_failure
docs_url = "https://docs.gitlab.com/ce/ci/yaml/README.html#dependencies"
[
failure_message.html_safe,
help_message(docs_url).html_safe
].join("<br />")
end
def invalid_dependencies
build.invalid_dependencies.map(&:name).join(', ')
end
def failure_message
_("This job depends on other jobs with expired/erased artifacts: %{invalid_dependencies}") %
{ invalid_dependencies: invalid_dependencies }
end
def help_message(docs_url)
_("Please refer to <a href=\"%{docs_url}\">%{docs_url}</a>") % { docs_url: docs_url }
end
end
Loading
Loading
@@ -28,6 +28,10 @@ module NotificationRecipientService
Builder::ProjectMaintainers.new(*args).notification_recipients
end
 
def self.build_new_release_recipients(*args)
Builder::NewRelease.new(*args).notification_recipients
end
module Builder
class Base
def initialize(*)
Loading
Loading
@@ -359,6 +363,26 @@ module NotificationRecipientService
end
end
 
class NewRelease < Base
attr_reader :target
def initialize(target)
@target = target
end
def build!
add_recipients(target.project.authorized_users, :custom, nil)
end
def custom_action
:new_release
end
def acting_user
target.author
end
end
class MergeRequestUnmergeable < Base
attr_reader :target
def initialize(merge_request)
Loading
Loading
Loading
Loading
@@ -289,6 +289,15 @@ class NotificationService
end
end
 
# Notify users when a new release is created
def send_new_release_notifications(release)
recipients = NotificationRecipientService.build_new_release_recipients(release)
recipients.each do |recipient|
mailer.new_release_email(recipient.user.id, release, recipient.reason).deliver_later
end
end
# Members
def new_access_request(member)
return true unless member.notifiable?(:subscription)
Loading
Loading
Loading
Loading
@@ -45,12 +45,11 @@
= form_tag admin_runners_path, id: 'runners-search', method: :get, class: 'filter-form js-filter-form' do
.filtered-search-wrapper.d-flex
.filtered-search-box
= dropdown_tag(custom_icon('icon_history'),
= dropdown_tag(_('Recent searches'),
options: { wrapper_class: 'filtered-search-history-dropdown-wrapper',
toggle_class: 'filtered-search-history-dropdown-toggle-button',
dropdown_class: 'filtered-search-history-dropdown',
content_class: 'filtered-search-history-dropdown-content',
title: _('Recent searches') }) do
content_class: 'filtered-search-history-dropdown-content' }) do
.js-filtered-search-history-dropdown{ data: { full_path: admin_runners_path } }
.filtered-search-box-input-container.droplab-dropdown
.scroll-container
Loading
Loading
.blank-state-row
= link_to new_project_path, class: "blank-state-link" do
.blank-state
- if has_start_trial?
= render_if_exists "dashboard/projects/blank_state_ee_trial"
= link_to new_project_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_project")
.blank-state-body
%h3.blank-state-title
Create a project
%p.blank-state-text
Projects are where you store your code, access issues, wiki and other features of GitLab.
- if current_user.can_create_group?
= link_to new_group_path, class: "blank-state blank-state-link" do
.blank-state-icon
= custom_icon("add_new_project", size: 50)
= image_tag("illustrations/welcome/add_new_group")
.blank-state-body
%h3.blank-state-title
Create a project
Create a group
%p.blank-state-text
Projects are where you store your code, access issues, wiki and other features of GitLab.
Groups are a great way to organize projects and people.
 
- if current_user.can_create_group?
= link_to new_group_path, class: "blank-state-link" do
.blank-state
.blank-state-icon
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group
%p.blank-state-text
Groups are a great way to organize projects and people.
= link_to new_admin_user_path, class: "blank-state-link" do
.blank-state
.blank-state-icon
= custom_icon("add_new_user", size: 50)
.blank-state-body
%h3.blank-state-title
Add people
%p.blank-state-text
Add your team members and others to GitLab.
= link_to admin_root_path, class: "blank-state-link" do
.blank-state
= link_to new_admin_user_path, class: "blank-state blank-state-link" do
.blank-state-icon
= custom_icon("configure_server", size: 50)
= image_tag("illustrations/welcome/add_new_user")
.blank-state-body
%h3.blank-state-title
Configure GitLab
Add people
%p.blank-state-text
Make adjustments to how your GitLab instance is set up.
Add your team members and others to GitLab.
= link_to admin_root_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/configure_server")
.blank-state-body
%h3.blank-state-title
Configure GitLab
%p.blank-state-text
Make adjustments to how your GitLab instance is set up.
Loading
Loading
@@ -2,19 +2,18 @@
 
.blank-state-row
- if current_user.can_create_project?
= link_to new_project_path, class: "blank-state-link" do
.blank-state
.blank-state-icon
= custom_icon("add_new_project", size: 50)
.blank-state-body
%h3.blank-state-title
Create a project
%p.blank-state-text
Projects are where you store your code, access issues, wiki and other features of GitLab.
= link_to new_project_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_project")
.blank-state-body
%h3.blank-state-title
Create a project
%p.blank-state-text
Projects are where you store your code, access issues, wiki and other features of GitLab.
- else
.blank-state
.blank-state-icon
= custom_icon("add_new_project", size: 50)
= image_tag("illustrations/welcome/add_new_project")
.blank-state-body
%h3.blank-state-title
Create a project
Loading
Loading
@@ -22,37 +21,34 @@
If you are added to a project, it will be displayed here.
 
- if current_user.can_create_group?
= link_to new_group_path, class: "blank-state-link" do
.blank-state
.blank-state-icon
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group
%p.blank-state-text
Groups are the best way to manage projects and members.
= link_to new_group_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_group")
.blank-state-body
%h3.blank-state-title
Create a group
%p.blank-state-text
Groups are the best way to manage projects and members.
 
- if public_project_count > 0
= link_to trending_explore_projects_path, class: "blank-state-link" do
.blank-state
.blank-state-icon
= custom_icon("globe", size: 50)
.blank-state-body
%h3.blank-state-title
Explore public projects
%p.blank-state-text
There are
= number_with_delimiter(public_project_count)
public projects on this server.
Public projects are an easy way to allow
everyone to have read-only access.
= link_to "https://docs.gitlab.com/", class: "blank-state-link" do
.blank-state
= link_to trending_explore_projects_path, class: "blank-state blank-state-link" do
.blank-state-icon
= custom_icon("lightbulb", size: 50)
= image_tag("illustrations/welcome/globe")
.blank-state-body
%h3.blank-state-title
Learn more about GitLab
Explore public projects
%p.blank-state-text
Take a look at the documentation to discover all of GitLab's capabilities.
There are
= number_with_delimiter(public_project_count)
public projects on this server.
Public projects are an easy way to allow
everyone to have read-only access.
= link_to "https://docs.gitlab.com/", class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/lightbulb")
.blank-state-body
%h3.blank-state-title
Learn more about GitLab
%p.blank-state-text
Take a look at the documentation to discover all of GitLab's capabilities.
.blank-state-parent-container{ class: ('has-start-trial-container' if has_start_trial?) }
.blank-state-parent-container
.section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" }
.container.section-body
.row
Loading
Loading
@@ -7,12 +7,7 @@
= _('Welcome to GitLab')
%p.blank-state-text
= _('Faster releases. Better code. Less pain.')
.blank-state-row
%div{ class: ('column-large' if has_start_trial?) }
- if current_user.admin?
= render "blank_state_admin_welcome"
- else
= render "blank_state_welcome"
- if has_start_trial?
.column-small
= render_if_exists "blank_state_ee_trial"
- if current_user.admin?
= render "blank_state_admin_welcome"
- else
= render "blank_state_welcome"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment