Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
6c744c5ac9
commit
6de2a04a4e
54 changed files with 440 additions and 412 deletions
|
@ -1,19 +1,10 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
|
||||
export default {
|
||||
i18n: {
|
||||
edit: __('Edit'),
|
||||
webIde: __('Web IDE'),
|
||||
},
|
||||
components: {
|
||||
GlButton,
|
||||
WebIdeLink,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
showEditButton: {
|
||||
type: Boolean,
|
||||
|
@ -43,7 +34,6 @@ export default {
|
|||
|
||||
<template>
|
||||
<web-ide-link
|
||||
v-if="glFeatures.consolidatedEditButton"
|
||||
:show-edit-button="showEditButton"
|
||||
class="gl-mr-3"
|
||||
:edit-url="editPath"
|
||||
|
@ -53,26 +43,4 @@ export default {
|
|||
disable-fork-modal
|
||||
@edit="onEdit"
|
||||
/>
|
||||
<div v-else>
|
||||
<gl-button
|
||||
v-if="showEditButton"
|
||||
class="gl-mr-2"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
data-testid="edit"
|
||||
@click="onEdit('simple')"
|
||||
>
|
||||
{{ $options.i18n.edit }}
|
||||
</gl-button>
|
||||
|
||||
<gl-button
|
||||
class="gl-mr-3"
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
data-testid="web-ide"
|
||||
@click="onEdit('ide')"
|
||||
>
|
||||
{{ $options.i18n.webIde }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -187,6 +187,7 @@ export default {
|
|||
deep: true,
|
||||
handler() {
|
||||
// TODO Implement back button response using onpopstate
|
||||
// See: https://gitlab.com/gitlab-org/gitlab/-/issues/333804
|
||||
updateHistory({
|
||||
url: fromSearchToUrl(this.search),
|
||||
title: document.title,
|
||||
|
|
|
@ -91,8 +91,8 @@ export const ACCESS_LEVEL_REF_PROTECTED = 'REF_PROTECTED';
|
|||
// CiRunnerSort
|
||||
|
||||
export const CREATED_DESC = 'CREATED_DESC';
|
||||
export const CREATED_ASC = 'CREATED_ASC'; // TODO Add this to the API
|
||||
export const CONTACTED_DESC = 'CONTACTED_DESC'; // TODO Add this to the API
|
||||
export const CREATED_ASC = 'CREATED_ASC';
|
||||
export const CONTACTED_DESC = 'CONTACTED_DESC';
|
||||
export const CONTACTED_ASC = 'CONTACTED_ASC';
|
||||
|
||||
export const DEFAULT_SORT = CREATED_DESC;
|
||||
|
|
|
@ -190,6 +190,7 @@ export default {
|
|||
deep: true,
|
||||
handler() {
|
||||
// TODO Implement back button reponse using onpopstate
|
||||
// See https://gitlab.com/gitlab-org/gitlab/-/issues/333804
|
||||
updateHistory({
|
||||
url: fromSearchToUrl(this.search),
|
||||
title: document.title,
|
||||
|
|
|
@ -45,7 +45,6 @@ class Projects::BlobController < Projects::ApplicationController
|
|||
before_action do
|
||||
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml)
|
||||
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
|
||||
end
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ class Projects::TreeController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml)
|
||||
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
|
||||
push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
feature_category :source_code_management
|
||||
|
|
|
@ -41,7 +41,6 @@ class ProjectsController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:new_dir_modal, @project, default_enabled: :yaml)
|
||||
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
|
||||
push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml)
|
||||
push_frontend_feature_flag(:work_items, @project, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
|
|
|
@ -15,8 +15,19 @@ module Types
|
|||
max_page_size: 20,
|
||||
resolver: Resolvers::ContainerRepositoryTagsResolver
|
||||
|
||||
field :size,
|
||||
GraphQL::Types::Float,
|
||||
null: true,
|
||||
description: 'Deduplicated size of the image repository in bytes. This is only available on GitLab.com for repositories created after `2021-11-04`.'
|
||||
|
||||
def can_delete
|
||||
Ability.allowed?(current_user, :destroy_container_image, object)
|
||||
end
|
||||
|
||||
def size
|
||||
object.size
|
||||
rescue Faraday::Error
|
||||
raise ::Gitlab::Graphql::Errors::ResourceNotAvailable, "Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,8 @@ class ContainerRepository < ApplicationRecord
|
|||
ABORTABLE_MIGRATION_STATES = (ACTIVE_MIGRATION_STATES + %w[pre_import_done default]).freeze
|
||||
MIGRATION_STATES = (IDLE_MIGRATION_STATES + ACTIVE_MIGRATION_STATES).freeze
|
||||
|
||||
MIGRATION_PHASE_1_STARTED_AT = Date.new(2021, 11, 4).freeze
|
||||
|
||||
TooManyImportsError = Class.new(StandardError)
|
||||
NativeImportError = Class.new(StandardError)
|
||||
|
||||
|
@ -408,6 +410,16 @@ class ContainerRepository < ApplicationRecord
|
|||
update!(expiration_policy_started_at: Time.zone.now)
|
||||
end
|
||||
|
||||
def size
|
||||
strong_memoize(:size) do
|
||||
next unless Gitlab.com?
|
||||
next if self.created_at.before?(MIGRATION_PHASE_1_STARTED_AT)
|
||||
next unless gitlab_api_client.supports_gitlab_api?
|
||||
|
||||
gitlab_api_client.repository_details(self.path, with_size: true)['size_bytes']
|
||||
end
|
||||
end
|
||||
|
||||
def migration_in_active_state?
|
||||
migration_state.in?(ACTIVE_MIGRATION_STATES)
|
||||
end
|
||||
|
|
|
@ -9,6 +9,9 @@ class Integration < ApplicationRecord
|
|||
include Integrations::HasDataFields
|
||||
include FromUnion
|
||||
include EachBatch
|
||||
include IgnorableColumns
|
||||
|
||||
ignore_column :template, remove_with: '14.10', remove_after: '2022-03-22'
|
||||
|
||||
INTEGRATION_NAMES = %w[
|
||||
asana assembla bamboo bugzilla buildkite campfire confluence custom_issue_tracker datadog discord
|
||||
|
|
|
@ -145,7 +145,7 @@ module Integrations
|
|||
end
|
||||
|
||||
def one_issue_tracker
|
||||
return if template? || instance?
|
||||
return if instance?
|
||||
return if project.blank?
|
||||
|
||||
if project.integrations.external_issue_trackers.where.not(id: id).any?
|
||||
|
|
|
@ -115,7 +115,6 @@ module Integrations
|
|||
end
|
||||
|
||||
def prometheus_available?
|
||||
return false if template?
|
||||
return false unless project
|
||||
|
||||
project.all_clusters.enabled.eager_load(:integration_prometheus).any? do |cluster|
|
||||
|
|
|
@ -73,3 +73,5 @@ class DeploymentEntity < Grape::Entity
|
|||
request.try(:project) || options[:project]
|
||||
end
|
||||
end
|
||||
|
||||
DeploymentEntity.prepend_mod
|
||||
|
|
|
@ -20,6 +20,7 @@ class EnvironmentEntity < Grape::Entity
|
|||
expose :last_deployment, using: DeploymentEntity
|
||||
expose :stop_action_available?, as: :has_stop_action
|
||||
expose :rollout_status, if: -> (*) { can_read_deploy_board? }, using: RolloutStatusEntity
|
||||
expose :tier
|
||||
|
||||
expose :upcoming_deployment, if: -> (environment) { environment.upcoming_deployment } do |environment, ops|
|
||||
DeploymentEntity.represent(environment.upcoming_deployment,
|
||||
|
|
|
@ -32,12 +32,8 @@ class BulkCreateIntegrationService
|
|||
end
|
||||
|
||||
def integration_hash
|
||||
if integration.template?
|
||||
integration.to_integration_hash
|
||||
else
|
||||
integration.to_integration_hash.tap { |json| json['inherit_from_id'] = integration.inherit_from_id || integration.id }
|
||||
end
|
||||
end
|
||||
|
||||
def data_fields_hash
|
||||
integration.to_data_fields_hash
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Integrations
|
||||
# TODO: Remove this as part of https://gitlab.com/gitlab-org/gitlab/-/issues/335178
|
||||
class PropagateTemplateService
|
||||
def self.propagate(_integration)
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,11 +5,7 @@
|
|||
|
||||
.file-actions.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-md-justify-content-end<
|
||||
= render 'projects/blob/viewer_switcher', blob: blob unless blame
|
||||
- if Feature.enabled?(:consolidated_edit_button, @project)
|
||||
= render 'shared/web_ide_button', blob: blob
|
||||
- else
|
||||
= edit_blob_button(@project, @ref, @path, blob: blob)
|
||||
= ide_edit_button(@project, @ref, @path, blob: blob)
|
||||
- if can_view_pipeline_editor?(@project) && @path == @project.ci_config_path_or_default
|
||||
= link_to "Pipeline Editor", project_ci_pipeline_editor_path(@project, branch_name: @ref), class: "btn gl-button btn-confirm-secondary gl-ml-3"
|
||||
.btn-group{ role: "group", class: ("gl-ml-3" if current_user) }>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: consolidated_edit_button
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44311
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/270433
|
||||
milestone: '13.5'
|
||||
type: development
|
||||
group: group::editor
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: generic_packages
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40045
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239133
|
||||
milestone: '13.4'
|
||||
type: development
|
||||
group: group::release
|
||||
default_enabled: true
|
|
@ -210,10 +210,11 @@ GET /registry/repositories/:id
|
|||
| `id` | integer/string | yes | The ID of the registry repository accessible by the authenticated user. |
|
||||
| `tags` | boolean | no | If the parameter is included as `true`, the response includes an array of `"tags"`. |
|
||||
| `tags_count` | boolean | no | If the parameter is included as `true`, the response includes `"tags_count"`. |
|
||||
| `size` | boolean | no | If the parameter is included as `true`, the response includes `"size"`. This is the deduplicated size of all images within the repository. Deduplication eliminates extra copies of identical data. For example, if you upload the same image twice, the Container Registry stores only one copy. This field is only available on GitLab.com for repositories created after `2021-11-04`. |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
"https://gitlab.example.com/api/v4/registry/repositories/2?tags=true&tags_count=true"
|
||||
"https://gitlab.example.com/api/v4/registry/repositories/2?tags=true&tags_count=true&size=true"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
@ -234,7 +235,8 @@ Example response:
|
|||
"path": "group/project:0.0.1",
|
||||
"location": "gitlab.example.com:5000/group/project:0.0.1"
|
||||
}
|
||||
]
|
||||
],
|
||||
"size": 2818413
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -9577,6 +9577,7 @@ Details of a container repository.
|
|||
| <a id="containerrepositorydetailsname"></a>`name` | [`String!`](#string) | Name of the container repository. |
|
||||
| <a id="containerrepositorydetailspath"></a>`path` | [`String!`](#string) | Path of the container repository. |
|
||||
| <a id="containerrepositorydetailsproject"></a>`project` | [`Project!`](#project) | Project of the container registry. |
|
||||
| <a id="containerrepositorydetailssize"></a>`size` | [`Float`](#float) | Deduplicated size of the image repository in bytes. This is only available on GitLab.com for repositories created after `2021-11-04`. |
|
||||
| <a id="containerrepositorydetailsstatus"></a>`status` | [`ContainerRepositoryStatus`](#containerrepositorystatus) | Status of the container repository. |
|
||||
| <a id="containerrepositorydetailstagscount"></a>`tagsCount` | [`Int!`](#int) | Number of tags associated with this image. |
|
||||
| <a id="containerrepositorydetailsupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp when the container repository was updated. |
|
||||
|
|
|
@ -6,12 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# GitLab Generic Packages Repository **(FREE)**
|
||||
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4209) in GitLab 13.5.
|
||||
> - It's [deployed behind a feature flag](../../../user/feature_flags.md), enabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - It's able to be enabled or disabled per-project.
|
||||
> - It's recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-generic-packages-in-the-package-registry).
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4209) in GitLab 13.5 [with a flag](../../../administration/feature_flags.md) named `generic_packages`. Enabled by default.
|
||||
> - Generally available in GitLab 14.8. [Feature flag `generic_packages`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80886) removed.
|
||||
|
||||
Publish generic files, like release binaries, in your project's Package Registry. Then, install the packages whenever you need to use them as a dependency.
|
||||
|
||||
|
@ -194,31 +190,6 @@ upload:
|
|||
- Invoke-RestMethod -Headers @{ "JOB-TOKEN"="$CI_JOB_TOKEN" } -InFile path/to/file.txt -uri "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/my_package/0.0.1/file.txt" -Method put
|
||||
```
|
||||
|
||||
### Enable or disable generic packages in the Package Registry
|
||||
|
||||
Support for generic packages is under development but ready for production use.
|
||||
It is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can opt to disable it.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.enable(:generic_packages)
|
||||
# For a single project
|
||||
Feature.enable(:generic_packages, Project.find(<project id>))
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
# For the instance
|
||||
Feature.disable(:generic_packages)
|
||||
# For a single project
|
||||
Feature.disable(:generic_packages, Project.find(<project id>))
|
||||
```
|
||||
|
||||
### Generic package sample project
|
||||
|
||||
The [Write CI-CD Variables in Pipeline](https://gitlab.com/guided-explorations/cfg-data/write-ci-cd-variables-in-pipeline) project contains a working example you can use to create, upload, and download generic packages in GitLab CI/CD.
|
||||
|
|
|
@ -68,7 +68,7 @@ threads. Some quick actions might not be available to all subscription tiers.
|
|||
| `/done` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Mark to do as done. |
|
||||
| `/draft` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Toggle the draft status. |
|
||||
| `/due <date>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set due date. Examples of valid `<date>` include `in 2 days`, `this Friday` and `December 31st`. |
|
||||
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. **(FREE)** Also, mark both as related. |
|
||||
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. Also, mark both as related. |
|
||||
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
|
||||
| `/estimate <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). |
|
||||
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced in GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/213814)). |
|
||||
|
|
|
@ -23,11 +23,17 @@ module API
|
|||
params do
|
||||
optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included'
|
||||
optional :tags_count, type: Boolean, default: false, desc: 'Determines if the tags count should be included'
|
||||
optional :size, type: Boolean, default: false, desc: 'Determines if the size should be included'
|
||||
end
|
||||
get ':id' do
|
||||
authorize!(:read_container_image, repository)
|
||||
|
||||
present repository, with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count], user: current_user
|
||||
present repository,
|
||||
with: Entities::ContainerRegistry::Repository,
|
||||
tags: params[:tags],
|
||||
tags_count: params[:tags_count],
|
||||
size: params[:size],
|
||||
user: current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@ module API
|
|||
expose :tags_count, if: -> (_, options) { options[:tags_count] }
|
||||
expose :tags, using: Tag, if: -> (_, options) { options[:tags] }
|
||||
expose :delete_api_path, if: ->(object, options) { Ability.allowed?(options[:user], :admin_container_image, object) }
|
||||
expose :size, if: -> (_, options) { options[:size] }
|
||||
|
||||
private
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@ module API
|
|||
before do
|
||||
require_packages_enabled!
|
||||
authenticate_non_get!
|
||||
|
||||
require_generic_packages_available!
|
||||
end
|
||||
|
||||
params do
|
||||
|
@ -113,10 +111,6 @@ module API
|
|||
include ::API::Helpers::PackagesHelpers
|
||||
include ::API::Helpers::Packages::BasicAuthHelpers
|
||||
|
||||
def require_generic_packages_available!
|
||||
not_found! unless Feature.enabled?(:generic_packages, project, default_enabled: true)
|
||||
end
|
||||
|
||||
def project
|
||||
authorized_user_project
|
||||
end
|
||||
|
|
|
@ -31,10 +31,12 @@ module ContainerRegistry
|
|||
registry_features = Gitlab::CurrentSettings.container_registry_features || []
|
||||
next true if ::Gitlab.com? && registry_features.include?(REGISTRY_GITLAB_V1_API_FEATURE)
|
||||
|
||||
response = faraday.get('/gitlab/v1/')
|
||||
with_token_faraday do |faraday_client|
|
||||
response = faraday_client.get('/gitlab/v1/')
|
||||
response.success? || response.status == 401
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs-gitlab/api.md#import-repository
|
||||
def pre_import_repository(path)
|
||||
|
@ -50,17 +52,48 @@ module ContainerRegistry
|
|||
|
||||
# https://gitlab.com/gitlab-org/container-registry/-/blob/master/docs-gitlab/api.md#get-repository-import-status
|
||||
def import_status(path)
|
||||
body_hash = response_body(faraday.get(import_url_for(path)))
|
||||
with_import_token_faraday do |faraday_client|
|
||||
body_hash = response_body(faraday_client.get(import_url_for(path)))
|
||||
body_hash['status'] || 'error'
|
||||
end
|
||||
end
|
||||
|
||||
def repository_details(path, with_size: false)
|
||||
with_token_faraday do |faraday_client|
|
||||
req = faraday_client.get("/gitlab/v1/repositories/#{path}/") do |req|
|
||||
req.params['size'] = 'self' if with_size
|
||||
end
|
||||
|
||||
break {} unless req.success?
|
||||
|
||||
response_body(req)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_import_for(path, pre:)
|
||||
faraday.put(import_url_for(path)) do |req|
|
||||
with_import_token_faraday do |faraday_client|
|
||||
faraday_client.put(import_url_for(path)) do |req|
|
||||
req.params['pre'] = pre.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def with_token_faraday
|
||||
yield faraday
|
||||
end
|
||||
|
||||
def with_import_token_faraday
|
||||
yield faraday_with_import_token
|
||||
end
|
||||
|
||||
def faraday_with_import_token(timeout_enabled: true)
|
||||
@faraday_with_import_token ||= faraday_base(timeout_enabled: timeout_enabled) do |conn|
|
||||
# initialize the connection with the :import_token instead of :token
|
||||
initialize_connection(conn, @options.merge(token: @options[:import_token]), &method(:configure_connection))
|
||||
end
|
||||
end
|
||||
|
||||
def import_url_for(path)
|
||||
"/gitlab/v1/import/#{path}/"
|
||||
|
|
|
@ -2,26 +2,16 @@
|
|||
|
||||
module ContainerRegistry
|
||||
class Registry
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
attr_reader :uri, :client, :path
|
||||
attr_reader :uri, :client, :gitlab_api_client, :path
|
||||
|
||||
def initialize(uri, options = {})
|
||||
@uri = uri
|
||||
@options = options
|
||||
@path = @options[:path] || default_path
|
||||
@client = ContainerRegistry::Client.new(@uri, @options)
|
||||
end
|
||||
|
||||
def gitlab_api_client
|
||||
strong_memoize(:gitlab_api_client) do
|
||||
token = Auth::ContainerRegistryAuthenticationService.import_access_token
|
||||
|
||||
url = Gitlab.config.registry.api_url
|
||||
host_port = Gitlab.config.registry.host_port
|
||||
|
||||
ContainerRegistry::GitlabApiClient.new(url, token: token, path: host_port)
|
||||
end
|
||||
import_token = Auth::ContainerRegistryAuthenticationService.import_access_token
|
||||
@gitlab_api_client = ContainerRegistry::GitlabApiClient.new(@uri, @options.merge(import_token: import_token))
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -11076,6 +11076,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Branch missing"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Choose a scan method"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Could not create the scanner profile. Please try again."
|
||||
msgstr ""
|
||||
|
||||
|
@ -11217,6 +11220,9 @@ msgstr ""
|
|||
msgid "DastProfiles|Save profile"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Scan method"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Scan mode"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11295,12 +11301,24 @@ msgstr ""
|
|||
msgid "DastProfiles|Website"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|What does each method do?"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|You can either choose a passive scan or validate the target site from the site profile management page. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastSiteValidation|Copy HTTP header to clipboard"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17839,6 +17857,9 @@ msgstr ""
|
|||
msgid "HAR file path or URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Archive (HAR)"
|
||||
msgstr ""
|
||||
|
||||
msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
"qa/specs/features/ee/browser_ui/10_protect/policy_alerts_list_spec.rb": 14.055217947000074,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 14.212461153999811,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 14.218627059000028,
|
||||
"qa/specs/features/browser_ui/1_manage/group/transfer_group_spec.rb": 14.295524570999987,
|
||||
"qa/specs/features/api/1_manage/project_access_token_spec.rb": 14.394589879999785,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 14.505683429000328,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 14.804579386000114,
|
||||
|
|
|
@ -33,16 +33,11 @@ module QA
|
|||
end
|
||||
|
||||
def click_edit
|
||||
# TODO: remove this condition and else part once ff :consolidated_edit_button is enabled by default
|
||||
if has_element?(:action_dropdown)
|
||||
within_element(:action_dropdown) do
|
||||
click_button(class: 'dropdown-toggle-split')
|
||||
click_element(:edit_menu_item)
|
||||
click_element(:edit_button)
|
||||
end
|
||||
else
|
||||
click_on 'Edit'
|
||||
end
|
||||
end
|
||||
|
||||
def click_delete
|
||||
|
|
|
@ -102,16 +102,6 @@ module QA
|
|||
|
||||
click_element(:save_permissions_changes_button)
|
||||
end
|
||||
|
||||
def transfer_group(target_group, source_group)
|
||||
expand_content :advanced_settings_content
|
||||
|
||||
select_namespace(target_group)
|
||||
click_element(:transfer_button)
|
||||
|
||||
fill_confirmation_text(source_group)
|
||||
confirm_transfer
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Manage' do
|
||||
describe 'Subgroup transfer' do
|
||||
let(:source_group) do
|
||||
Resource::Group.fabricate_via_api! do |group|
|
||||
group.path = "source-group-for-transfer_#{SecureRandom.hex(8)}"
|
||||
end
|
||||
end
|
||||
|
||||
let!(:target_group) do
|
||||
Resource::Group.fabricate_via_api! do |group|
|
||||
group.path = "target-group-for-transfer_#{SecureRandom.hex(8)}"
|
||||
end
|
||||
end
|
||||
|
||||
let(:sub_group_for_transfer) do
|
||||
Resource::Group.fabricate_via_api! do |group|
|
||||
group.path = "subgroup-for-transfer_#{SecureRandom.hex(8)}"
|
||||
group.sandbox = source_group
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Flow::Login.sign_in
|
||||
sub_group_for_transfer.visit!
|
||||
end
|
||||
|
||||
it 'transfers a subgroup to another group',
|
||||
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347692' do
|
||||
Page::Group::Menu.perform(&:click_group_general_settings_item)
|
||||
Page::Group::Settings::General.perform do |general|
|
||||
general.transfer_group(target_group.path, sub_group_for_transfer.path)
|
||||
|
||||
sub_group_for_transfer.sandbox = target_group
|
||||
sub_group_for_transfer.reload!
|
||||
end
|
||||
|
||||
expect(page).to have_text("Group '#{sub_group_for_transfer.path}' was successfully transferred.")
|
||||
expect(page.driver.current_url).to include(sub_group_for_transfer.full_path)
|
||||
end
|
||||
|
||||
after do
|
||||
source_group&.remove_via_api!
|
||||
target_group&.remove_via_api!
|
||||
sub_group_for_transfer&.remove_via_api!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -217,11 +217,6 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
trait :template do
|
||||
project { nil }
|
||||
template { true }
|
||||
end
|
||||
|
||||
trait :group do
|
||||
group
|
||||
project { nil }
|
||||
|
|
|
@ -154,34 +154,52 @@ RSpec.describe 'Edit group settings' do
|
|||
namespace_select.find('button').click
|
||||
namespace_select.find('.dropdown-menu p', text: target_group_name, match: :first).click
|
||||
|
||||
click_button "Transfer group"
|
||||
click_button 'Transfer group'
|
||||
end
|
||||
|
||||
page.within(confirm_modal) do
|
||||
expect(page).to have_text "You are going to transfer #{selected_group.name} to another namespace. Are you ABSOLUTELY sure?"
|
||||
|
||||
fill_in "confirm_name_input", with: selected_group.name
|
||||
click_button "Confirm"
|
||||
fill_in 'confirm_name_input', with: selected_group.name
|
||||
click_button 'Confirm'
|
||||
end
|
||||
|
||||
expect(page).to have_text "Group '#{selected_group.name}' was successfully transferred."
|
||||
expect(current_url).to include(selected_group.reload.full_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a sub group' do
|
||||
context 'from a subgroup' do
|
||||
let(:selected_group) { create(:group, path: 'foo-subgroup', parent: group) }
|
||||
let(:target_group_name) { "No parent group" }
|
||||
|
||||
context 'to no parent group' do
|
||||
let(:target_group_name) { 'No parent group' }
|
||||
|
||||
it_behaves_like 'can transfer the group'
|
||||
end
|
||||
|
||||
context 'with a root group' do
|
||||
context 'to a different parent group' do
|
||||
let(:target_group) { create(:group, path: 'foo-parentgroup') }
|
||||
let(:target_group_name) { target_group.name }
|
||||
|
||||
before do
|
||||
target_group.add_owner(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'can transfer the group'
|
||||
end
|
||||
end
|
||||
|
||||
context 'from a root group' do
|
||||
let(:selected_group) { create(:group, path: 'foo-rootgroup') }
|
||||
|
||||
context 'to a parent group' do
|
||||
let(:target_group_name) { group.name }
|
||||
|
||||
it_behaves_like 'can transfer the group'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'disable email notifications' do
|
||||
it 'is visible' do
|
||||
|
|
|
@ -75,22 +75,5 @@ RSpec.describe 'Projects > Files > User browses LFS files' do
|
|||
expect(page).to have_selector(:link_or_button, 'Edit in Web IDE')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag :consolidated_edit_button is off' do
|
||||
before do
|
||||
stub_feature_flags(consolidated_edit_button: false)
|
||||
|
||||
click_link('files')
|
||||
click_link('lfs')
|
||||
click_link('lfs_object.iso')
|
||||
end
|
||||
|
||||
it 'does not show single file edit link' do
|
||||
page.within('.content') do
|
||||
expect(page).to have_selector(:link_or_button, 'Web IDE')
|
||||
expect(page).not_to have_css('button[data-testid="edit"')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -236,116 +236,5 @@ RSpec.describe 'Projects > Files > User edits files', :js do
|
|||
let(:project) { project2 }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when feature flag :consolidated_edit_button is off' do
|
||||
before do
|
||||
stub_feature_flags(consolidated_edit_button: false)
|
||||
end
|
||||
|
||||
context 'when an user does not have write access', :js do
|
||||
before do
|
||||
project2.add_reporter(user)
|
||||
visit(project2_tree_path_root_ref)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'inserts a content of a file in a forked project', :sidekiq_might_not_need_inline do
|
||||
set_default_button('edit')
|
||||
click_link('.gitignore')
|
||||
click_link_or_button('Edit')
|
||||
|
||||
expect_fork_prompt
|
||||
|
||||
click_link_or_button('Fork')
|
||||
|
||||
expect_fork_status
|
||||
|
||||
find('.file-editor', match: :first)
|
||||
|
||||
find('#editor')
|
||||
set_editor_value('*.rbca')
|
||||
|
||||
expect(editor_value).to eq('*.rbca')
|
||||
end
|
||||
|
||||
it 'opens the Web IDE in a forked project', :sidekiq_might_not_need_inline do
|
||||
set_default_button('webide')
|
||||
click_link('.gitignore')
|
||||
click_link_or_button('Web IDE')
|
||||
|
||||
expect_fork_prompt
|
||||
|
||||
click_link_or_button('Fork')
|
||||
|
||||
expect_fork_status
|
||||
|
||||
expect(page).to have_css('.ide-sidebar-project-title', text: "#{project2.name} #{user.namespace.full_path}/#{project2.path}")
|
||||
expect(page).to have_css('.ide .multi-file-tab', text: '.gitignore')
|
||||
end
|
||||
|
||||
it 'commits an edited file in a forked project', :sidekiq_might_not_need_inline do
|
||||
set_default_button('edit')
|
||||
click_link('.gitignore')
|
||||
click_link_or_button('Edit')
|
||||
|
||||
expect_fork_prompt
|
||||
|
||||
click_link_or_button('Fork')
|
||||
|
||||
expect_fork_status
|
||||
|
||||
find('.file-editor', match: :first)
|
||||
|
||||
find('#editor')
|
||||
set_editor_value('*.rbca')
|
||||
fill_in(:commit_message, with: 'New commit message', visible: true)
|
||||
click_button('Commit changes')
|
||||
|
||||
fork = user.fork_of(project2.reload)
|
||||
|
||||
expect(current_path).to eq(project_new_merge_request_path(fork))
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('New commit message')
|
||||
end
|
||||
|
||||
context 'when the user already had a fork of the project', :js do
|
||||
let!(:forked_project) { fork_project(project2, user, namespace: user.namespace, repository: true) }
|
||||
|
||||
before do
|
||||
visit(project2_tree_path_root_ref)
|
||||
wait_for_requests
|
||||
end
|
||||
|
||||
it 'links to the forked project for editing', :sidekiq_might_not_need_inline do
|
||||
set_default_button('edit')
|
||||
click_link('.gitignore')
|
||||
click_link_or_button('Edit')
|
||||
|
||||
expect(page).not_to have_link('Fork')
|
||||
|
||||
find('#editor')
|
||||
set_editor_value('*.rbca')
|
||||
fill_in(:commit_message, with: 'Another commit', visible: true)
|
||||
click_button('Commit changes')
|
||||
|
||||
fork = user.fork_of(project2)
|
||||
|
||||
expect(current_path).to eq(project_new_merge_request_path(fork))
|
||||
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_content('Another commit')
|
||||
expect(page).to have_content("From #{forked_project.full_path}")
|
||||
expect(page).to have_content("into #{project2.full_path}")
|
||||
end
|
||||
|
||||
it_behaves_like 'unavailable for an archived project' do
|
||||
let(:project) { project2 }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
3
spec/fixtures/api/schemas/deployment.json
vendored
3
spec/fixtures/api/schemas/deployment.json
vendored
|
@ -64,6 +64,5 @@
|
|||
"items": { "$ref": "job/job.json" }
|
||||
},
|
||||
"status": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
|
|
2
spec/fixtures/api/schemas/environment.json
vendored
2
spec/fixtures/api/schemas/environment.json
vendored
|
@ -35,6 +35,8 @@
|
|||
"auto_stop_at": { "type": "string", "format": "date-time" },
|
||||
"can_stop": { "type": "boolean" },
|
||||
"has_opened_alert": { "type": "boolean" },
|
||||
"tier": { "type": "string" },
|
||||
"required_approval_count": { "type": "integer" },
|
||||
"cluster_type": { "type": "types/nullable_string.json" },
|
||||
"terminal_path": { "type": "types/nullable_string.json" },
|
||||
"rollout_status": {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import BlobEdit from '~/repository/components/blob_edit.vue';
|
||||
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
|
||||
|
@ -13,17 +12,12 @@ const DEFAULT_PROPS = {
|
|||
describe('BlobEdit component', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (consolidatedEditButton = false, props = {}) => {
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(BlobEdit, {
|
||||
propsData: {
|
||||
...DEFAULT_PROPS,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
glFeatures: {
|
||||
consolidatedEditButton,
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -32,9 +26,7 @@ describe('BlobEdit component', () => {
|
|||
wrapper = null;
|
||||
});
|
||||
|
||||
const findButtons = () => wrapper.findAll(GlButton);
|
||||
const findEditButton = () => wrapper.find('[data-testid="edit"]');
|
||||
const findWebIdeButton = () => wrapper.find('[data-testid="web-ide"]');
|
||||
const findWebIdeLink = () => wrapper.find(WebIdeLink);
|
||||
|
||||
it('renders component', () => {
|
||||
|
@ -48,28 +40,8 @@ describe('BlobEdit component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders both buttons', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findButtons()).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('renders the Edit button', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findEditButton().text()).toBe('Edit');
|
||||
expect(findEditButton()).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('renders the Web IDE button', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findWebIdeButton().text()).toBe('Web IDE');
|
||||
expect(findWebIdeButton()).not.toBeDisabled();
|
||||
});
|
||||
|
||||
it('renders WebIdeLink component', () => {
|
||||
createComponent(true);
|
||||
createComponent();
|
||||
|
||||
const { editPath: editUrl, webIdePath: webIdeUrl, needsToFork } = DEFAULT_PROPS;
|
||||
|
||||
|
@ -86,13 +58,13 @@ describe('BlobEdit component', () => {
|
|||
const showEditButton = false;
|
||||
|
||||
it('renders WebIdeLink component without an edit button', () => {
|
||||
createComponent(true, { showEditButton });
|
||||
createComponent({ showEditButton });
|
||||
|
||||
expect(findWebIdeLink().props()).toMatchObject({ showEditButton });
|
||||
});
|
||||
|
||||
it('does not render an Edit button', () => {
|
||||
createComponent(false, { showEditButton });
|
||||
createComponent({ showEditButton });
|
||||
|
||||
expect(findEditButton().exists()).toBe(false);
|
||||
});
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe GitlabSchema.types['ContainerRepositoryDetails'] do
|
||||
fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status tags project]
|
||||
fields = %i[id name path location created_at updated_at expiration_policy_started_at status tags_count can_delete expiration_policy_cleanup_status tags size project]
|
||||
|
||||
it { expect(described_class.graphql_name).to eq('ContainerRepositoryDetails') }
|
||||
|
||||
|
|
|
@ -6,8 +6,11 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
|||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
include_context 'container registry client'
|
||||
include_context 'container registry client stubs'
|
||||
|
||||
let(:path) { 'namespace/path/to/repository' }
|
||||
let(:import_token) { 'import_token' }
|
||||
let(:options) { { token: token, import_token: import_token } }
|
||||
|
||||
describe '#supports_gitlab_api?' do
|
||||
subject { client.supports_gitlab_api? }
|
||||
|
@ -121,6 +124,40 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#repository_details' do
|
||||
let(:path) { 'namespace/path/to/repository' }
|
||||
let(:response) { { foo: :bar, this: :is_a_test } }
|
||||
let(:with_size) { true }
|
||||
|
||||
subject { client.repository_details(path, with_size: with_size) }
|
||||
|
||||
context 'with size' do
|
||||
before do
|
||||
stub_repository_details(path, with_size: with_size, respond_with: response)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(response.stringify_keys.deep_transform_values(&:to_s)) }
|
||||
end
|
||||
|
||||
context 'without_size' do
|
||||
let(:with_size) { false }
|
||||
|
||||
before do
|
||||
stub_repository_details(path, with_size: with_size, respond_with: response)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(response.stringify_keys.deep_transform_values(&:to_s)) }
|
||||
end
|
||||
|
||||
context 'with non successful response' do
|
||||
before do
|
||||
stub_repository_details(path, with_size: with_size, status_code: 404)
|
||||
end
|
||||
|
||||
it { is_expected.to eq({}) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.supports_gitlab_api?' do
|
||||
subject { described_class.supports_gitlab_api? }
|
||||
|
||||
|
@ -181,7 +218,7 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
|||
|
||||
def stub_pre_import(path, status_code, pre:)
|
||||
stub_request(:put, "#{registry_api_url}/gitlab/v1/import/#{path}/?pre=#{pre}")
|
||||
.with(headers: { 'Accept' => described_class::JSON_TYPE })
|
||||
.with(headers: { 'Accept' => described_class::JSON_TYPE, 'Authorization' => "bearer #{import_token}" })
|
||||
.to_return(status: status_code, body: '')
|
||||
end
|
||||
|
||||
|
@ -194,11 +231,19 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
|
|||
|
||||
def stub_import_status(path, status)
|
||||
stub_request(:get, "#{registry_api_url}/gitlab/v1/import/#{path}/")
|
||||
.with(headers: { 'Accept' => described_class::JSON_TYPE })
|
||||
.with(headers: { 'Accept' => described_class::JSON_TYPE, 'Authorization' => "bearer #{import_token}" })
|
||||
.to_return(
|
||||
status: 200,
|
||||
body: { status: status }.to_json,
|
||||
headers: { content_type: 'application/json' }
|
||||
)
|
||||
end
|
||||
|
||||
def stub_repository_details(path, with_size: true, status_code: 200, respond_with: {})
|
||||
url = "#{registry_api_url}/gitlab/v1/repositories/#{path}/"
|
||||
url += "?size=self" if with_size
|
||||
stub_request(:get, url)
|
||||
.with(headers: { 'Accept' => described_class::JSON_TYPE, 'Authorization' => "bearer #{token}" })
|
||||
.to_return(status: status_code, body: respond_with.to_json, headers: { 'Content-Type' => described_class::JSON_TYPE })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,10 +4,15 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe ContainerRegistry::Registry do
|
||||
let(:path) { nil }
|
||||
let(:registry) { described_class.new('http://example.com', path: path) }
|
||||
let(:registry_api_url) { 'http://example.com' }
|
||||
let(:registry) { described_class.new(registry_api_url, path: path) }
|
||||
|
||||
subject { registry }
|
||||
|
||||
before do
|
||||
stub_container_registry_config(enabled: true, api_url: registry_api_url, key: 'spec/fixtures/x509_certificate_pk.key')
|
||||
end
|
||||
|
||||
it { is_expected.to respond_to(:client) }
|
||||
it { is_expected.to respond_to(:uri) }
|
||||
it { is_expected.to respond_to(:path) }
|
||||
|
|
|
@ -7,13 +7,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::Configuration, :request_store do
|
|||
let(:db_config) { ActiveRecord::DatabaseConfigurations::HashConfig.new('test', 'ci', configuration_hash) }
|
||||
let(:model) { double(:model, connection_db_config: db_config) }
|
||||
|
||||
before do
|
||||
# It's confusing to think about these specs with this enabled by default so
|
||||
# we make it disabled by default and just write the specific spec for when
|
||||
# it's enabled
|
||||
stub_feature_flags(force_no_sharing_primary_model: false)
|
||||
end
|
||||
|
||||
describe '.for_model' do
|
||||
context 'when load balancing is not configured' do
|
||||
it 'uses the default settings' do
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe Gitlab::Integrations::StiType do
|
|||
context 'SQL SELECT' do
|
||||
let(:expected_sql) do
|
||||
<<~SQL.strip
|
||||
SELECT "integrations".* FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
|
||||
FROM "integrations" WHERE "integrations"."type" = 'AsanaService'
|
||||
SQL
|
||||
end
|
||||
|
||||
|
@ -18,7 +18,7 @@ RSpec.describe Gitlab::Integrations::StiType do
|
|||
Integration.where(type: type).to_sql
|
||||
end
|
||||
|
||||
expect(sql_statements).to all(eq(expected_sql))
|
||||
expect(sql_statements).to all(end_with(expected_sql))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -653,6 +653,58 @@ RSpec.describe ContainerRepository, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#size' do
|
||||
let(:on_com) { true }
|
||||
let(:created_at) { described_class::MIGRATION_PHASE_1_STARTED_AT + 3.months }
|
||||
|
||||
subject { repository.size }
|
||||
|
||||
before do
|
||||
allow(::Gitlab).to receive(:com?).and_return(on_com)
|
||||
allow(repository).to receive(:created_at).and_return(created_at)
|
||||
end
|
||||
|
||||
context 'supports gitlab api on .com with a recent repository' do
|
||||
before do
|
||||
expect(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(true)
|
||||
expect(repository.gitlab_api_client).to receive(:repository_details).with(repository.path, with_size: true).and_return(response)
|
||||
end
|
||||
|
||||
context 'with a size_bytes field' do
|
||||
let(:response) { { 'size_bytes' => 12345 } }
|
||||
|
||||
it { is_expected.to eq(12345) }
|
||||
end
|
||||
|
||||
context 'without a size_bytes field' do
|
||||
let(:response) { { 'foo' => 'bar' } }
|
||||
|
||||
it { is_expected.to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'does not support gitlab api' do
|
||||
before do
|
||||
expect(repository.gitlab_api_client).to receive(:supports_gitlab_api?).and_return(false)
|
||||
expect(repository.gitlab_api_client).not_to receive(:repository_details)
|
||||
end
|
||||
|
||||
it { is_expected.to eq(nil) }
|
||||
end
|
||||
|
||||
context 'not on .com' do
|
||||
let(:on_com) { false }
|
||||
|
||||
it { is_expected.to eq(nil) }
|
||||
end
|
||||
|
||||
context 'with an old repository' do
|
||||
let(:created_at) { described_class::MIGRATION_PHASE_1_STARTED_AT - 3.months }
|
||||
|
||||
it { is_expected.to eq(nil) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reset_expiration_policy_started_at!' do
|
||||
subject { repository.reset_expiration_policy_started_at! }
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ RSpec.describe Integration do
|
|||
describe '.build_from_integration' do
|
||||
context 'when integration is invalid' do
|
||||
let(:invalid_integration) do
|
||||
build(:prometheus_integration, :template, active: true, properties: {})
|
||||
build(:prometheus_integration, :instance, active: true, properties: {})
|
||||
.tap { |integration| integration.save!(validate: false) }
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::ContainerRepositories do
|
||||
include_context 'container registry client stubs'
|
||||
|
||||
let_it_be(:project) { create(:project, :private) }
|
||||
let_it_be(:reporter) { create(:user) }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
|
@ -103,6 +105,68 @@ RSpec.describe API::ContainerRepositories do
|
|||
expect(json_response['tags_count']).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with size param' do
|
||||
let(:url) { "/registry/repositories/#{repository.id}?size=true" }
|
||||
let(:on_com) { true }
|
||||
let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT + 3.months }
|
||||
|
||||
before do
|
||||
allow(::Gitlab).to receive(:com?).and_return(on_com)
|
||||
repository.update_column(:created_at, created_at)
|
||||
end
|
||||
|
||||
it 'returns a repository and its size' do
|
||||
stub_container_registry_gitlab_api_support(supported: true) do |client|
|
||||
stub_container_registry_gitlab_api_repository_details(client, path: repository.path, size_bytes: 12345)
|
||||
end
|
||||
|
||||
subject
|
||||
|
||||
expect(json_response['size']).to eq(12345)
|
||||
end
|
||||
|
||||
context 'with a network error' do
|
||||
it 'returns an error message' do
|
||||
stub_container_registry_gitlab_api_network_error
|
||||
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:service_unavailable)
|
||||
expect(json_response['message']).to include('We are having trouble connecting to the Container Registry')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with not supporting the gitlab api' do
|
||||
it 'returns nil' do
|
||||
stub_container_registry_gitlab_api_support(supported: false)
|
||||
|
||||
subject
|
||||
|
||||
expect(json_response['size']).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'not on .com' do
|
||||
let(:on_com) { false }
|
||||
|
||||
it 'returns nil' do
|
||||
subject
|
||||
|
||||
expect(json_response['size']).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an older container repository' do
|
||||
let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT - 3.months }
|
||||
|
||||
it 'returns nil' do
|
||||
subject
|
||||
|
||||
expect(json_response['size']).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with invalid repository id' do
|
||||
|
|
|
@ -170,17 +170,6 @@ RSpec.describe API::GenericPackages do
|
|||
end
|
||||
end
|
||||
|
||||
context 'generic_packages feature flag is disabled' do
|
||||
it 'responds with 404 Not Found' do
|
||||
stub_feature_flags(generic_packages: false)
|
||||
project.add_developer(user)
|
||||
|
||||
authorize_upload_file(workhorse_headers.merge(personal_access_token_header))
|
||||
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
end
|
||||
end
|
||||
|
||||
def authorize_upload_file(request_headers, package_name: 'mypackage', file_name: 'myfile.tar.gz')
|
||||
url = "/projects/#{project.id}/packages/generic/#{package_name}/0.0.1/#{file_name}/authorize"
|
||||
|
||||
|
|
|
@ -3,17 +3,19 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe 'container repository details' do
|
||||
include_context 'container registry tags'
|
||||
include_context 'container registry client stubs'
|
||||
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
include GraphqlHelpers
|
||||
|
||||
let_it_be_with_reload(:project) { create(:project) }
|
||||
let_it_be(:container_repository) { create(:container_repository, project: project) }
|
||||
let_it_be_with_reload(:container_repository) { create(:container_repository, project: project) }
|
||||
|
||||
let(:query) do
|
||||
graphql_query_for(
|
||||
'containerRepository',
|
||||
{ id: container_repository_global_id },
|
||||
all_graphql_fields_for('ContainerRepositoryDetails', excluded: ['pipeline'])
|
||||
all_graphql_fields_for('ContainerRepositoryDetails', excluded: %w[pipeline size])
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -220,6 +222,80 @@ RSpec.describe 'container repository details' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'size field' do
|
||||
let(:size_response) { container_repository_details_response.dig('size') }
|
||||
let(:on_com) { true }
|
||||
let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT + 3.months }
|
||||
let(:variables) do
|
||||
{ id: container_repository_global_id }
|
||||
end
|
||||
|
||||
let(:query) do
|
||||
<<~GQL
|
||||
query($id: ID!) {
|
||||
containerRepository(id: $id) {
|
||||
size
|
||||
}
|
||||
}
|
||||
GQL
|
||||
end
|
||||
|
||||
before do
|
||||
allow(::Gitlab).to receive(:com?).and_return(on_com)
|
||||
container_repository.update_column(:created_at, created_at)
|
||||
end
|
||||
|
||||
it 'returns the size' do
|
||||
stub_container_registry_gitlab_api_support(supported: true) do |client|
|
||||
stub_container_registry_gitlab_api_repository_details(client, path: container_repository.path, size_bytes: 12345)
|
||||
end
|
||||
|
||||
subject
|
||||
|
||||
expect(size_response).to eq(12345)
|
||||
end
|
||||
|
||||
context 'with a network error' do
|
||||
it 'returns an error' do
|
||||
stub_container_registry_gitlab_api_network_error
|
||||
|
||||
subject
|
||||
|
||||
expect_graphql_errors_to_include("Can't connect to the Container Registry. If this error persists, please review the troubleshooting documentation.")
|
||||
end
|
||||
end
|
||||
|
||||
context 'with not supporting the gitlab api' do
|
||||
it 'returns nil' do
|
||||
stub_container_registry_gitlab_api_support(supported: false)
|
||||
|
||||
subject
|
||||
|
||||
expect(size_response).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'not on .com' do
|
||||
let(:on_com) { false }
|
||||
|
||||
it 'returns nil' do
|
||||
subject
|
||||
|
||||
expect(size_response).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an older container repository' do
|
||||
let(:created_at) { ::ContainerRepository::MIGRATION_PHASE_1_STARTED_AT - 3.months }
|
||||
|
||||
it 'returns nil' do
|
||||
subject
|
||||
|
||||
expect(size_response).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with tags with a manifest containing nil fields' do
|
||||
let(:tags_response) { container_repository_details_response.dig('tags', 'nodes') }
|
||||
let(:errors) { container_repository_details_response.dig('errors') }
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe EnvironmentEntity do
|
|||
end
|
||||
|
||||
it 'exposes core elements of environment' do
|
||||
expect(subject).to include(:id, :global_id, :name, :state, :environment_path)
|
||||
expect(subject).to include(:id, :global_id, :name, :state, :environment_path, :tier)
|
||||
end
|
||||
|
||||
it 'exposes folder path' do
|
||||
|
|
|
@ -13,7 +13,6 @@ RSpec.describe BulkCreateIntegrationService do
|
|||
let_it_be(:excluded_project) { create(:project, group: excluded_group) }
|
||||
|
||||
let(:instance_integration) { create(:jira_integration, :instance) }
|
||||
let(:template_integration) { create(:jira_integration, :template) }
|
||||
let(:excluded_attributes) { %w[id project_id group_id inherit_from_id instance template created_at updated_at] }
|
||||
|
||||
shared_examples 'creates integration from batch ids' do
|
||||
|
|
|
@ -252,6 +252,20 @@ RSpec.configure do |config|
|
|||
::Ci::ApplicationRecord.set_open_transactions_baseline
|
||||
end
|
||||
|
||||
config.around do |example|
|
||||
if example.metadata.fetch(:stub_feature_flags, true)
|
||||
# It doesn't make sense for this to default to enabled as we only plan to
|
||||
# use this temporarily to override an environment variable but eventually
|
||||
# we'll just use the environment variable value when we've completed the
|
||||
# gradual rollout. This stub must happen in around block as there are other
|
||||
# around blocks in tests that will run before this and get the wrong
|
||||
# database connection.
|
||||
stub_feature_flags(force_no_sharing_primary_model: false)
|
||||
end
|
||||
|
||||
example.run
|
||||
end
|
||||
|
||||
config.append_after do
|
||||
ApplicationRecord.reset_open_transactions_baseline
|
||||
::Ci::ApplicationRecord.reset_open_transactions_baseline
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_context 'container registry client stubs' do
|
||||
def stub_container_registry_gitlab_api_support(supported: true)
|
||||
allow_next_instance_of(ContainerRegistry::GitlabApiClient) do |client|
|
||||
allow(client).to receive(:supports_gitlab_api?).and_return(supported)
|
||||
yield client if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
def stub_container_registry_gitlab_api_repository_details(client, path:, size_bytes:)
|
||||
allow(client).to receive(:repository_details).with(path, with_size: true).and_return('size_bytes' => size_bytes)
|
||||
end
|
||||
|
||||
def stub_container_registry_gitlab_api_network_error(client_method: :supports_gitlab_api?)
|
||||
allow_next_instance_of(ContainerRegistry::GitlabApiClient) do |client|
|
||||
allow(client).to receive(client_method).and_raise(::Faraday::Error, nil, nil)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue