Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-05-15 15:07:43 +00:00
parent e6fed37d94
commit f8a5275c45
67 changed files with 821 additions and 416 deletions

View File

@ -1463,14 +1463,15 @@
.qa:rules:package-and-test-sidebar:
rules:
- !reference [".qa:rules:package-and-test-common", rules]
- <<: *if-dot-com-gitlab-org-schedule
- <<: *if-merge-request
changes: *code-patterns
when: manual
allow_failure: true
- <<: *if-default-branch-schedule-nightly
allow_failure: true
variables:
SKIP_REPORT_IN_ISSUES: "true"
PROCESS_TEST_RESULTS: "false"
KNAPSACK_GENERATE_REPORT: "false"
UPDATE_QA_CACHE: "false"
SKIP_REPORT_IN_ISSUES: "false"
PROCESS_TEST_RESULTS: "true"
QA_SAVE_TEST_METRICS: "true"
QA_EXPORT_TEST_METRICS: "false"

View File

@ -46,7 +46,7 @@ gem 'devise', '~> 4.8.1'
gem 'devise-pbkdf2-encryptable', '~> 0.0.0', path: 'vendor/gems/devise-pbkdf2-encryptable'
gem 'bcrypt', '~> 3.1', '>= 3.1.14'
gem 'doorkeeper', '~> 5.6', '>= 5.6.6'
gem 'doorkeeper-openid_connect', '~> 1.8', '>= 1.8.5'
gem 'doorkeeper-openid_connect', '~> 1.8', '>= 1.8.6'
gem 'rexml', '~> 3.2.5'
gem 'ruby-saml', '~> 1.13.0'
gem 'omniauth', '~> 2.1.0'

View File

@ -123,7 +123,7 @@
{"name":"docile","version":"1.4.0","platform":"ruby","checksum":"5f1734bde23721245c20c3d723e76c104208e1aa01277a69901ce770f0ebb8d3"},
{"name":"domain_name","version":"0.5.20190701","platform":"ruby","checksum":"000a600454cb4a344769b2f10b531765ea7bd3a304fe47ed12e5ca1eab969851"},
{"name":"doorkeeper","version":"5.6.6","platform":"ruby","checksum":"2344e86c77770526efcda893b5217aa13d1c7eb1b40de840b58b19eb1ff757e0"},
{"name":"doorkeeper-openid_connect","version":"1.8.5","platform":"ruby","checksum":"d4ee57687945402843c948cee399c758cdddf04468c42b1fb02a8800dd0627f6"},
{"name":"doorkeeper-openid_connect","version":"1.8.6","platform":"ruby","checksum":"8dc46543e697476f441496a5d465bbc68c10d052e54348cec4db06d123b1e003"},
{"name":"dotenv","version":"2.7.6","platform":"ruby","checksum":"2451ed5e8e43776d7a787e51d6f8903b98e446146c7ad143d5678cc2c409d547"},
{"name":"dry-configurable","version":"0.12.0","platform":"ruby","checksum":"87a9579a04dfbae73e401d694282800d64bbdb8631cb3e987bfb79b673df7c67"},
{"name":"dry-container","version":"0.7.2","platform":"ruby","checksum":"a071824ba3451048b23500210f96a2b9facd6e46ac687f65e49c75d18786f6da"},

View File

@ -393,7 +393,7 @@ GEM
unf (>= 0.0.5, < 1.0.0)
doorkeeper (5.6.6)
railties (>= 5)
doorkeeper-openid_connect (1.8.5)
doorkeeper-openid_connect (1.8.6)
doorkeeper (>= 5.5, < 5.7)
jwt (>= 2.5)
dotenv (2.7.6)
@ -1714,7 +1714,7 @@ DEPENDENCIES
diffy (~> 3.4)
discordrb-webhooks (~> 3.4)
doorkeeper (~> 5.6, >= 5.6.6)
doorkeeper-openid_connect (~> 1.8, >= 1.8.5)
doorkeeper-openid_connect (~> 1.8, >= 1.8.6)
duo_api (~> 1.3)
ed25519 (~> 1.3.0)
elasticsearch-api (= 7.13.3)

View File

@ -0,0 +1,31 @@
<script>
export default {
inject: {
canAdminAchievement: {
type: Boolean,
required: true,
},
canAwardAchievement: {
type: Boolean,
required: true,
},
groupFullPath: {
type: String,
required: true,
},
groupId: {
type: Number,
required: true,
},
textQuery: {
type: String,
required: false,
default: null,
},
},
};
</script>
<template>
<div></div>
</template>

View File

@ -0,0 +1,7 @@
export const INDEX_ROUTE_NAME = 'index';
export const NEW_ROUTE_NAME = 'new';
export const EDIT_ROUTE_NAME = 'edit';
export const trackViewsOptions = {
category: 'Achievements' /* eslint-disable-line @gitlab/require-i18n-strings */,
action: 'view_achievements_list',
};

View File

@ -0,0 +1,16 @@
import { INDEX_ROUTE_NAME, NEW_ROUTE_NAME, EDIT_ROUTE_NAME } from './constants';
export default [
{
name: INDEX_ROUTE_NAME,
path: '/',
},
{
name: NEW_ROUTE_NAME,
path: '/new',
},
{
name: EDIT_ROUTE_NAME,
path: '/:id/edit',
},
];

View File

@ -0,0 +1,43 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import AchievementsApp from '~/achievements/components/achievements_app.vue';
import routes from '~/achievements/routes';
Vue.use(VueApollo);
Vue.use(VueRouter);
const init = () => {
const el = document.getElementById('js-achievements-app');
if (!el) {
return false;
}
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
const { basePath, viewModel } = el.dataset;
const provide = JSON.parse(viewModel);
const router = new VueRouter({
base: basePath,
mode: 'history',
routes,
});
return new Vue({
el,
router,
apolloProvider,
provide: convertObjectPropsToCamelCase(provide),
render(createElement) {
return createElement(AchievementsApp);
},
});
};
init();

View File

@ -30,11 +30,6 @@ export default {
required: false,
default: 'div',
},
collectionStyle: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
@ -94,13 +89,7 @@ export default {
</slot>
</span>
<span
class="gl-pr-3 gl-truncate-end gl-text-gray-900"
:class="{
'gl-font-sm gl-font-weight-semibold': collectionStyle,
}"
data-testid="section-title"
>
<span class="gl-pr-3 gl-text-gray-900 gl-truncate-end">
{{ item.title }}
</span>

View File

@ -70,7 +70,6 @@ export default {
:item="sectionItem"
:expanded="expanded"
:separated="true"
collection-style
@collapse-toggle="expanded = !expanded"
>
<draggable

View File

@ -150,7 +150,6 @@ export default {
v-for="item in nonStaticItems"
:key="item.id"
:item="item"
:collection-style="supportsPins"
tag="li"
@pin-add="createPin"
@pin-remove="destroyPin"

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Groups
class AchievementsController < Groups::ApplicationController
feature_category :user_profile
urgency :low
before_action :authorize_read_achievement!
private
def authorize_read_achievement!
render_404 unless can?(current_user, :read_achievement, group)
end
end
end

View File

@ -613,8 +613,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Feature.enabled?(:summarize_my_code_review, current_user) &&
namespace.group_namespace? &&
namespace.licensed_feature_available?(:summarize_my_mr_code_review) &&
namespace.experiment_features_enabled &&
namespace.third_party_ai_features_enabled &&
Gitlab::Llm::StageCheck.available?(namespace, :summarize_my_mr_code_review) &&
merge_request.send_to_ai?
end
end

View File

@ -0,0 +1,14 @@
- breadcrumb_title _('Achievements')
- page_title _('Achievements')
- @content_wrapper_class = "gl-relative"
= content_for :after_content do
#js-achievements-form-portal
#js-achievements-app{ data: { base_path: group_achievements_path(@group), view_model: Gitlab::Json.generate({
can_admin_achievement: can?(current_user, :admin_achievement, @group),
can_award_achievement: can?(current_user, :award_achievement, @group),
group_full_path: @group.full_path,
group_id: @group.id,
text_query: params[:search]
}) } }

View File

@ -1272,6 +1272,15 @@
:weight: 1
:idempotent: false
:tags: []
- :name: github_importer:github_import_pull_requests_import_merged_by
:worker_name: Gitlab::GithubImport::PullRequests::ImportMergedByWorker
:feature_category: :importers
:has_external_dependencies: true
:urgency: :low
:resource_boundary: :cpu
:weight: 1
:idempotent: false
:tags: []
- :name: github_importer:github_import_pull_requests_import_review
:worker_name: Gitlab::GithubImport::PullRequests::ImportReviewWorker
:feature_category: :importers

View File

@ -1,5 +1,7 @@
# frozen_string_literal: true
# TODO: remove in 16.1 milestone
# https://gitlab.com/gitlab-org/gitlab/-/issues/409706
module Gitlab
module GithubImport
class ImportPullRequestMergedByWorker # rubocop:disable Scalability/IdempotentWorker
@ -12,7 +14,7 @@ module Gitlab
end
def importer_class
Importer::PullRequestMergedByImporter
Importer::PullRequests::MergedByImporter
end
def object_type

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
# TODO: remove in 16.X milestone
# https://gitlab.com/gitlab-org/gitlab/-/issues/377059
# TODO: remove in 16.1 milestone
# https://gitlab.com/gitlab-org/gitlab/-/issues/409706
module Gitlab
module GithubImport
class ImportReleaseAttachmentsWorker # rubocop:disable Scalability/IdempotentWorker

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module PullRequests
class ImportMergedByWorker # rubocop:disable Scalability/IdempotentWorker
include ObjectImporter
worker_resource_boundary :cpu
def representation_class
Gitlab::GithubImport::Representation::PullRequest
end
def importer_class
Importer::PullRequests::MergedByImporter
end
def object_type
:pull_request_merged_by
end
end
end
end
end

View File

@ -15,7 +15,7 @@ module Gitlab
# client - An instance of Gitlab::GithubImport::Client.
# project - An instance of Project.
def import(client, project)
waiter = Importer::PullRequestsMergedByImporter
waiter = Importer::PullRequests::AllMergedByImporter
.new(project, client)
.execute

View File

@ -1,8 +0,0 @@
---
name: namespace_storage_limit_bypass_date_check
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86794
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/361785
milestone: '15.0'
type: development
group: group::utilization
default_enabled: false

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
# This pulls in
# https://github.com/doorkeeper-gem/doorkeeper-openid_connect/pull/194
# to ensure generated `kid` values are RFC 7638-compliant.
require 'doorkeeper/openid_connect'
raise 'This patch is only needed for doorkeeper_openid_connect v1.8.5' if Doorkeeper::OpenidConnect::VERSION != '1.8.5'
module Doorkeeper
module OpenidConnect
def self.signing_key
key =
if %i[HS256 HS384 HS512].include?(signing_algorithm)
configuration.signing_key
else
OpenSSL::PKey.read(configuration.signing_key)
end
::JWT::JWK.new(key, { kid_generator: JWT::JWK::Thumbprint })
end
end
end

View File

@ -158,6 +158,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resources :contacts, only: [:index, :new, :edit]
resources :organizations, only: [:index, :new, :edit]
end
resources :achievements, only: [:index, :new, :edit]
end
scope(

View File

@ -2,16 +2,20 @@ const { parse, compile: compilerDomCompile } = require('@vue/compiler-dom');
const COMMENT_NODE_TYPE = 3;
const getPropIndex = (node, prop) => node.props?.findIndex((p) => p.name === prop) ?? -1;
const hasProp = (node, prop) => node.props?.some((p) => p.name === prop);
function modifyKeysInsideTemplateTag(templateNode) {
if (!templateNode.tag === 'template' || !hasProp(templateNode, 'for')) {
return;
}
let keyCandidate = null;
for (const node of templateNode.children) {
const keyBindingIndex = node.props
? node.props.findIndex((prop) => prop.arg && prop.arg.content === 'key')
: -1;
if (keyBindingIndex !== -1 && getPropIndex(node, 'for') === -1) {
if (keyBindingIndex !== -1 && !hasProp(node, 'for')) {
if (!keyCandidate) {
keyCandidate = node.props[keyBindingIndex];
}
@ -24,40 +28,97 @@ function modifyKeysInsideTemplateTag(templateNode) {
}
}
function getSlotName(node) {
return node?.props?.find((prop) => prop.name === 'slot')?.arg?.content;
}
function filterCommentNodeAndTrailingSpace(node, idx, list) {
if (node.type === COMMENT_NODE_TYPE) {
return false;
}
if (node.content !== ' ') {
return true;
}
if (list[idx - 1]?.type === COMMENT_NODE_TYPE) {
return false;
}
return true;
}
function filterCommentNodes(node) {
const { length: originalLength } = node.children;
// eslint-disable-next-line no-param-reassign
node.children = node.children.filter(filterCommentNodeAndTrailingSpace);
if (node.children.length !== originalLength) {
// trim remaining spaces
while (node.children.at(-1)?.content === ' ') {
node.children.pop();
}
}
}
function dropVOnceForChildrenInsideVIfBecauseOfIssue7725(node) {
// See https://github.com/vuejs/core/issues/7725 for details
if (!hasProp(node, 'if')) {
return;
}
node.children?.forEach((child) => {
if (Array.isArray(child.props)) {
// eslint-disable-next-line no-param-reassign
child.props = child.props.filter((prop) => prop.name !== 'once');
}
});
}
function fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(node) {
// See https://github.com/vuejs/core/issues/6063 for details
// eslint-disable-next-line no-param-reassign
node.children = node.children.filter((child, idx) => {
if (child.content !== ' ') {
// We need to drop only comment nodes
return true;
}
const previousNodeSlotName = getSlotName(node.children[idx - 1]);
const nextNodeSlotName = getSlotName(node.children[idx + 1]);
if (previousNodeSlotName && previousNodeSlotName === nextNodeSlotName) {
// We have a space beween two slot entries with same slot name, we need to drop it
return false;
}
return true;
});
}
module.exports = {
parse,
compile(template, options) {
const rootNode = parse(template, options);
// We do not want to switch to whitespace: collapse mode which is Vue.js 3 default
// It will be too devastating to codebase
// However, without `whitespace: condense` Vue will treat spaces between comments
// and nodes itself as text nodes, resulting in multi-root component
// For multi-root component passing classes / attributes fallthrough will not work
// See https://github.com/vuejs/core/issues/7909 for details
// To fix that we simply drop all component comments only on top-level
rootNode.children = rootNode.children.filter((n) => n.type !== COMMENT_NODE_TYPE);
const pendingNodes = [rootNode];
while (pendingNodes.length) {
const currentNode = pendingNodes.pop();
if (getPropIndex(currentNode, 'for') !== -1) {
if (currentNode.tag === 'template') {
// This one will be dropped all together with compiler when we drop Vue.js 2 support
modifyKeysInsideTemplateTag(currentNode);
}
if (Array.isArray(currentNode.children)) {
// This one will be dropped all together with compiler when we drop Vue.js 2 support
modifyKeysInsideTemplateTag(currentNode);
// This one will be dropped when https://github.com/vuejs/core/issues/7725 will be fixed
const vOncePropIndex = getPropIndex(currentNode, 'once');
if (vOncePropIndex !== -1) {
currentNode.props.splice(vOncePropIndex, 1);
}
dropVOnceForChildrenInsideVIfBecauseOfIssue7725(currentNode);
// See https://github.com/vuejs/core/issues/7909 for details
// However, this issue applies not only to root-level nodes
// But on any level comments could change slot emptiness detection
// so we simply drop them
filterCommentNodes(currentNode);
fixSameSlotsInsideTemplateFailingWhenUsingWhitespacePreserveDueToIssue6063(currentNode);
currentNode.children.forEach((child) => pendingNodes.push(child));
}
currentNode.children?.forEach((child) => pendingNodes.push(child));
}
return compilerDomCompile(rootNode, options);

View File

@ -0,0 +1,41 @@
# This is a template for a feature deprecation.
#
# Please refer to the deprecation guidelines to confirm your understanding of the
# definitions for "Deprecation", "End of Support", and "Removal":
# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology
#
# Deprecations must be announced at least three releases prior to removal.
# See the OPTIONAL END OF SUPPORT FIELDS section below if an End of Support period also applies.
#
# Breaking changes must happen in a major release.
#
# For more information please refer to the handbook documentation here:
# https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations
#
# Please delete this line and above before submitting your merge request.
#
# REQUIRED FIELDS
#
- title: "`POST ci/lint` API endpoint removed" # (required) The name of the feature to be deprecated
announcement_milestone: "15.7" # (required) The milestone when this feature was first announced as deprecated.
removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed
breaking_change: true # (required) If this deprecation is a breaking change, set this value to true
reporter: dhershkovitch # (required) GitLab username of the person reporting the deprecation
stage: verify # (required) String value of the stage that the feature was created in. e.g., Growth
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381669 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
The `POST ci/lint` API endpoint was deprecated in 15.7, and removed in 16.0. This endpoint did not validate the full range of CI/CD configuration options. Instead, use [`POST /projects/:id/ci/lint`](https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace), which properly validates CI/CD configuration.
#
# OPTIONAL END OF SUPPORT FIELDS
#
# If an End of Support period applies, the announcement should be shared with GitLab Support
# in the `#spt_managers` channel in Slack, and mention `@gitlab-com/support` in this MR.
#
end_of_support_milestone: # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
#
# OTHER OPTIONAL FIELDS
#
tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
documentation_url: # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View File

@ -761,7 +761,7 @@ users, [see what to do when no users are found](#no-users-are-found).
### GitLab logs
If a user account is blocked or unblocked due to the LDAP configuration, a
message is [logged to `application.log`](../../logs/index.md#applicationlog).
message is [logged to `application_json.log`](../../logs/index.md#application_jsonlog).
If there is an unexpected error during an LDAP lookup (configuration error,
timeout), the sign-in is rejected and a message is [logged to `production.log`](../../logs/index.md#productionlog).

View File

@ -350,7 +350,9 @@ the `view_duration_s` is calculated by [`duration_s - db_duration_s`](https://gi
Therefore, `view_duration_s` can be affected by multiple different factors, like read-write
process on Redis or external HTTP, not only the serialization process.
## `application.log`
## `application.log` (deprecated)
> [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111046) in GitLab 15.10.
Depending on your installation method, this file is located at:

View File

@ -29,7 +29,7 @@ You must replace the `vault.example.com` URL below with the URL of your Vault se
## How it works
Each job has JSON Web Token (JWT) provided as CI/CD variable named `CI_JOB_JWT`. This JWT can be used to authenticate with Vault using the [JWT Auth](https://developer.hashicorp.com/vault/docs/auth/jwt#jwt-authentication) method.
ID tokens are JSON Web Tokens (JWTs) used for OIDC authentication with third-party services. If a job has at least one ID token defined, the `secrets` keyword automatically uses that token to authenticate with Vault.
The following fields are included in the JWT:
@ -256,61 +256,36 @@ $ vault write auth/jwt/config \
For the full list of available configuration options, see Vault's [API documentation](https://developer.hashicorp.com/vault/api-docs/auth/jwt#configure).
The following job, when run for the default branch, is able to read secrets under `secret/myproject/staging/`, but not the secrets under `secret/myproject/production/`:
In GitLab, create the following [CI/CD variables](../../variables/index.md#for-a-project) to provide details about your Vault server:
- `VAULT_SERVER_URL` - The URL of your Vault server, for example `https://vault.example.com:8200`.
- `VAULT_AUTH_ROLE` - Optional. The role to use when attempting to authenticate. If no role is specified, Vault uses the [default role](https://developer.hashicorp.com/vault/api-docs/auth/jwt#default_role) specified when the authentication method was configured.
- `VAULT_AUTH_PATH` - Optional. The path where the authentication method is mounted. Default is `jwt`.
- `VAULT_NAMESPACE` - Optional. The [Vault Enterprise namespace](https://developer.hashicorp.com/vault/docs/enterprise/namespaces) to use for reading secrets and authentication. If no namespace is specified, Vault uses the root (`/`) namespace. The setting is ignored by Vault Open Source.
The following job, when run for the default branch, can read secrets under `secret/myproject/staging/`, but not the secrets under `secret/myproject/production/`:
```yaml
read_secrets:
image: vault:latest
job_with_secrets:
id_tokens:
VAULT_ID_TOKEN:
aud: https://example.vault.com
secrets:
STAGING_DB_PASSWORD:
vault: secret/myproject/staging/db/password@secrets # authenticates using $VAULT_ID_TOKEN
script:
# Check job's ref name
- echo $CI_COMMIT_REF_NAME
# and is this ref protected
- echo $CI_COMMIT_REF_PROTECTED
# Vault's address can be provided here or as CI/CD variable
- export VAULT_ADDR=http://vault.example.com:8200
# Authenticate and get token. Token expiry time and other properties can be configured
# when configuring JWT Auth - https://developer.hashicorp.com/vault/api-docs/auth/jwt#parameters-1
- export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-staging jwt=$CI_JOB_JWT)"
# Now use the VAULT_TOKEN to read the secret and store it in an environment variable
- export PASSWORD="$(vault kv get -field=password secret/myproject/staging/db)"
# Use the secret
- echo $PASSWORD
# This will fail because the role myproject-staging can not read secrets from secret/myproject/production/*
- export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"
- access-staging-db.sh --token $STAGING_DB_PASSWORD
```
NOTE:
If you're using a Vault instance provided by HashiCorp Cloud Platform,
you need to export the `VAULT_NAMESPACE` variable. Its default value is `admin`.
In this example:
![read secrets staging example](img/vault-read-secrets-staging.png)
The following job is able to authenticate using the `myproject-production` role and read secrets under `/secret/myproject/production/`:
```yaml
read_secrets:
image: vault:latest
script:
# Check job's ref name
- echo $CI_COMMIT_REF_NAME
# and is this ref protected
- echo $CI_COMMIT_REF_PROTECTED
# Vault's address can be provided here or as CI/CD variable
- export VAULT_ADDR=http://vault.example.com:8200
# Authenticate and get token. Token expiry time and other properties can be configured
# when configuring JWT Auth - https://developer.hashicorp.com/vault/api-docs/auth/jwt#parameters-1
- export VAULT_TOKEN="$(vault write -field=token auth/jwt/login role=myproject-production jwt=$CI_JOB_JWT)"
# Now use the VAULT_TOKEN to read the secret and store it in environment variable
- export PASSWORD="$(vault kv get -field=password secret/myproject/production/db)"
# Use the secret
- echo $PASSWORD
```
![read secrets production example](img/vault-read-secrets-production.png)
- `@secrets` - The vault name, where your Secrets Engines are enabled.
- `secret/myproject/staging/db` - The path location of the secret in Vault.
- `password` The field to be fetched within the referenced secret.
### Limit token access to Vault secrets
You can control `CI_JOB_JWT` access to Vault secrets by using Vault protections
You can control ID token access to Vault secrets by using Vault protections
and GitLab features. For example, restrict the token by:
- Using Vault [bound claims](https://developer.hashicorp.com/vault/docs/auth/jwt#bound-claims)

View File

@ -65,7 +65,7 @@ test2:
`&` sets up the name of the anchor (`job_configuration`), `<<` means "merge the
given hash into the current one," and `*` includes the named anchor
(`job_configuration` again). The expanded version of this example is:
(`job_configuration` again). The [expanded](../pipeline_editor/index.md#view-full-configuration) version of this example is:
```yaml
.job_template:
@ -123,7 +123,7 @@ test:mysql:
services: *mysql_configuration
```
The expanded version is:
The [expanded](../pipeline_editor/index.md#view-full-configuration) version is:
```yaml
.job_template:

View File

@ -83,3 +83,65 @@ Quoting the [documentation](https://clickhouse.com/docs/en/sql-reference/stateme
> If there's some aggregation in the view query, it's applied only to the batch
> of freshly inserted data. Any changes to existing data of the source table
> (like update, delete, drop a partition, etc.) do not change the materialized view.
## Secure and sensible defaults
ClickHouse instances should follow these security recommendations:
### Users
Files: `users.xml` and `config.xml`.
| Topic | Security Requirement | Reason |
| ----- | -------------------- | ------ |
| [`user_name/password`](https://clickhouse.com/docs/en/operations/settings/settings-users/#user-namepassword) | Usernames **must not** be blank. Passwords **must** use `password_sha256_hex` and **must not** be blank. | `plaintext` and `password_double_sha1_hex` are insecure. If username isn't specified, [`default` is used with no password](https://clickhouse.com/docs/en/operations/settings/settings-users/). |
| [`access_management`](https://clickhouse.com/docs/en/operations/settings/settings-users/#access_management-user-setting) | Use Server [configuration files](https://clickhouse.com/docs/en/operations/configuration-files) `users.xml` and `config.xml`. Avoid SQL-driven workflow. | SQL-driven workflow implies that at least one user has `access_management` which can be avoided via configuration files. These files are easier to audit and monitor too, considering that ["You can't manage the same access entity by both configuration methods simultaneously."](https://clickhouse.com/docs/en/operations/access-rights/#access-control). |
| [`user_name/networks`](https://clickhouse.com/docs/en/operations/settings/settings-users/#user-namenetworks) | At least one of `<ip>`, `<host>`, `<host_regexp>` **must** be set. Do not use `<ip>::/0</ip>` to open access for any network. | Network controls. ([Trust cautiously](https://about.gitlab.com/handbook/security/architecture/#trust-cautiously) principle) |
| [`user_name/profile`](https://clickhouse.com/docs/en/operations/settings/settings-users/#user-nameprofile) | Use profiles to set similar properties across multiple users and set limits (from the user interface). | [Least privilege](https://about.gitlab.com/handbook/security/architecture/#assign-the-least-privilege-possible) principle and limits. |
| [`user_name/quota`](https://clickhouse.com/docs/en/operations/settings/settings-users/#user-namequota) | Set quotas for users whenever possible. | Limit resource usage over a period of time or track the use of resources. |
| [`user_name/databases`](https://clickhouse.com/docs/en/operations/settings/settings-users/#user-namedatabases) | Restrict access to data, and avoid users with full access. | [Least privilege](https://about.gitlab.com/handbook/security/architecture/#assign-the-least-privilege-possible) principle. |
### Network
Files: `config.xml`
| Topic | Security Requirement | Reason |
| ----- | -------------------- | ------ |
| [`mysql_port`](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/#server_configuration_parameters-mysql_port) | Disable MySQL access unless strictly necessary:<br/> `<!-- <mysql_port>9004</mysql_port> -->`. | Close unnecessary ports and features exposure. ([Defense in depth](https://about.gitlab.com/handbook/security/architecture/#implement-defense-in-depth) principle) |
| [`postgresql_port`](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/#server_configuration_parameters-postgresql_port) | Disable PostgreSQL access unless strictly necessary:<br/> `<!-- <mysql_port>9005</mysql_port> -->` | Close unnecessary ports and features exposure. ([Defense in depth](https://about.gitlab.com/handbook/security/architecture/#implement-defense-in-depth) principle) |
| [`http_port/https_port`](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/#http-porthttps-port) & [`tcp_port/tcp_port_secure`](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/#http-porthttps-port) | Configure [SSL-TLS](https://clickhouse.com/docs/en/guides/sre/configuring-ssl), and disable non SSL ports:<br/>`<!-- <http_port>8123</http_port> -->`<br/>`<!-- <tcp_port>9000</tcp_port> -->`<br/>and enable secure ports:<br/>`<https_port>8443</https_port>`<br/>`<tcp_port_secure>9440</tcp_port_secure>` | Encrypt data in transit. ([Defense in depth](https://about.gitlab.com/handbook/security/architecture/#implement-defense-in-depth) principle) |
| [`interserver_http_host`](https://clickhouse.com/docs/en/operations/server-configuration-parameters/settings/#interserver-http-host) | Disable `interserver_http_host` in favor of `interserver_https_host` (`<interserver_https_port>9010</interserver_https_port>`) if ClickHouse is configured as a cluster. | Encrypt data in transit. ([Defense in depth](https://about.gitlab.com/handbook/security/architecture/#implement-defense-in-depth) principle) |
### Storage
| Topic | Security Requirement | Reason |
| ----- | -------------------- | ------ |
| Permissions | ClickHouse runs by default with the `clickhouse` user. Running as `root` is never needed. Use the principle of least privileges for the folders: `/etc/clickhouse-server`, `/var/lib/clickhouse`, `/var/log/clickhouse-server`. These folders must belong to the `clickhouse` user and group, and no other system user must have access to them. | Default passwords, ports and rules are "open doors". ([Fail securely & use secure defaults](https://about.gitlab.com/handbook/security/architecture/#fail-securely--use-secure-defaults) principle) |
| Encryption | Use an encrypted storage for logs and data if RED data is processed. On Kubernetes, the [StorageClass](https://kubernetes.io/docs/concepts/storage/storage-classes/) used must be encrypted. | Encrypt data at rest. ([Defense in depth](https://about.gitlab.com/handbook/security/architecture/#implement-defense-in-depth)) |
### Logging
| Topic | Security Requirement | Reason |
| ----- | -------------------- | ------ |
| `logger` | `Log` and `errorlog` **must** be defined and writable by `clickhouse`. | Make sure logs are stored. |
| SIEM | If hosted on GitLab.com, the ClickHouse instance or cluster **must** report [logs to our SIEM](https://internal-handbook.gitlab.io/handbook/security/infrastructure_security_logging/tooling/devo/) (internal link). | [GitLab logs critical information system activity](https://about.gitlab.com/handbook/security/audit-logging-policy.html). |
| Log sensitive data | Query masking rules **must** be used if sensitive data can be logged. See [example masking rules](#example-masking-rules). | [Column level encryption](https://clickhouse.com/docs/en/sql-reference/functions/encryption-functions/) can be used and leak sensitive data (keys) in logs. |
#### Example masking rules
```xml
<query_masking_rules>
<rule>
<name>hide SSN</name>
<regexp>(^|\D)\d{3}-\d{2}-\d{4}($|\D)</regexp>
<replace>000-00-0000</replace>
</rule>
<rule>
<name>hide encrypt/decrypt arguments</name>
<regexp>
((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)
</regexp>
<replace>\1(???)</replace>
</rule>
</query_masking_rules>
```

View File

@ -81,7 +81,7 @@ This worker imports the pull requests' _merged-by_ user information. The
[_List pull requests_](https://docs.github.com/en/rest/pulls#list-pull-requests)
API doesn't provide this information. Therefore, this stage must fetch each merged pull request
individually to import this information. A
`Gitlab::GithubImport::ImportPullRequestMergedByWorker` job is scheduled for each fetched pull
`Gitlab::GithubImport::PullRequests::ImportMergedByWorker` job is scheduled for each fetched pull
request.
### 7. Stage::ImportPullRequestsReviewRequestsWorker

View File

@ -1989,20 +1989,6 @@ For more information, refer to [security report validation](https://docs.gitlab.
<div class="deprecation breaking-change" data-milestone="16.0">
### Self-monitoring project is removed
<div class="deprecation-notes">
- Announced in: GitLab <span class="milestone">14.9</span>
- This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/groups/gitlab-org/-/epics/10030).
</div>
GitLab self-monitoring project was meant to enable self-hosted GitLab administrators to visualize performance metrics of GitLab within GitLab itself. This feature relied on GitLab Metrics dashboards. With metrics dashboard being removed, self-monitoring project is also removed. We recommended that self-hosted users monitor their GitLab instance with alternative visualization tools, such as Grafana.
</div>
<div class="deprecation breaking-change" data-milestone="16.0">
### Shimo integration
<div class="deprecation-notes">

View File

@ -442,6 +442,14 @@ Review the details carefully before upgrading.
Version 14.x.x [security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) have been removed.
Security reports that use schema version 14.x.x will cause an error in the pipeline's **Security** tab. For more information, refer to [security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation).
### Self-monitoring project is removed
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
GitLab self-monitoring project was meant to enable self-hosted GitLab administrators to visualize performance metrics of GitLab within GitLab itself. This feature relied on GitLab Metrics dashboards. With metrics dashboard being removed, self-monitoring project is also removed. We recommended that self-hosted users monitor their GitLab instance with alternative visualization tools, such as Grafana.
### Starboard directive in the config for the GitLab agent for Kubernetes removed
WARNING:
@ -539,6 +547,14 @@ The predefined CI/CD variables that start with `CI_BUILD_*` were deprecated in G
| `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` |
| `CI_BUILD_TRIGGERED` | `CI_PIPELINE_TRIGGERED` |
### `POST ci/lint` API endpoint removed
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
The `POST ci/lint` API endpoint was deprecated in 15.7, and removed in 16.0. This endpoint did not validate the full range of CI/CD configuration options. Instead, use [`POST /projects/:id/ci/lint`](https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace), which properly validates CI/CD configuration.
### vulnerabilityFindingDismiss GraphQL mutation
WARNING:

View File

@ -458,7 +458,7 @@ For multi-node systems we recommend ingesting the logs into services like Elasti
| Log file | Contents |
|:------------------------|:---------|
| `application.log` | GitLab user activity |
| `application_json.log` | GitLab user activity |
| `git_json.log` | Failed GitLab interaction with Git repositories |
| `production.log` | Requests received from Puma, and the actions taken to serve those requests |
| `sidekiq.log` | Background jobs |

View File

@ -50,6 +50,11 @@ configured for personal access tokens.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
> - Ability to create non-expiring group access tokens [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0.
WARNING:
Project access tokens are treated as [internal users](../../../development/internal_users.md).
If an internal user creates a project access token, that token is able to access
all projects that have visibility level set to [Internal](../../public_access.md).
To create a group access token:
1. On the top bar, select **Main menu > Groups** and find your group.
@ -66,11 +71,6 @@ To create a group access token:
A group access token is displayed. Save the group access token somewhere safe. After you leave or refresh the page, you can't view it again.
WARNING:
Group access tokens are treated as [internal users](../../../development/internal_users.md).
If an internal user creates a group access token, that token is able to access all
groups that have visibility level set to [Internal](../../public_access.md).
## Create a group access token using Rails console
GitLab 14.6 and earlier doesn't support creating group access tokens using the UI

View File

@ -50,6 +50,11 @@ configured for personal access tokens.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
> - Ability to create non-expiring project access tokens [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/392855) in GitLab 16.0.
WARNING:
Project access tokens are treated as [internal users](../../../development/internal_users.md).
If an internal user creates a project access token, that token is able to access
all projects that have visibility level set to [Internal](../../public_access.md).
To create a project access token:
1. On the top bar, select **Main menu > Projects** and find your project.
@ -66,11 +71,6 @@ To create a project access token:
A project access token is displayed. Save the project access token somewhere safe. After you leave or refresh the page, you can't view it again.
WARNING:
Project access tokens are treated as [internal users](../../../development/internal_users.md).
If an internal user creates a project access token, that token is able to access
all projects that have visibility level set to [Internal](../../public_access.md).
## Revoke a project access token
To revoke a project access token:

View File

@ -51,6 +51,7 @@ module.exports = (path, options = {}) => {
experimentalCSSCompile: false,
compiler: require.resolve('./config/vue3migration/compiler'),
compilerOptions: {
whitespace: 'preserve',
compatConfig: {
MODE: 2,
},

View File

@ -1,69 +0,0 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module Importer
class PullRequestMergedByImporter
# pull_request - An instance of
# `Gitlab::GithubImport::Representation::PullRequest`
# project - An instance of `Project`
# client - An instance of `Gitlab::GithubImport::Client`
def initialize(pull_request, project, client)
@pull_request = pull_request
@project = project
@client = client
end
def execute
user_finder = GithubImport::UserFinder.new(project, client)
gitlab_user_id = user_finder.user_id_for(pull_request.merged_by)
metrics_upsert(gitlab_user_id)
add_note!
end
private
attr_reader :project, :pull_request, :client
def metrics_upsert(gitlab_user_id)
MergeRequest::Metrics.upsert({
target_project_id: project.id,
merge_request_id: merge_request.id,
merged_by_id: gitlab_user_id,
merged_at: pull_request.merged_at,
created_at: timestamp,
updated_at: timestamp
}, unique_by: :merge_request_id)
end
def add_note!
merge_request.notes.create!(
importing: true,
note: missing_author_note,
author_id: project.creator_id,
project: project,
created_at: pull_request.merged_at
)
end
def merge_request
@merge_request ||= project.merge_requests.find_by_iid(pull_request.iid)
end
def timestamp
@timestamp ||= Time.new.utc
end
def missing_author_note
s_("GitHubImporter|*Merged by: %{author} at %{timestamp}*") % {
author: pull_request.merged_by&.login || 'ghost',
timestamp: pull_request.merged_at
}
end
end
end
end
end

View File

@ -0,0 +1,59 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module Importer
module PullRequests
class AllMergedByImporter
include ParallelScheduling
def importer_class
MergedByImporter
end
def representation_class
Gitlab::GithubImport::Representation::PullRequest
end
def sidekiq_worker_class
Gitlab::GithubImport::PullRequests::ImportMergedByWorker
end
def collection_method
:pull_requests_merged_by
end
def object_type
:pull_request_merged_by
end
def id_for_already_imported_cache(merge_request)
merge_request.id
end
def each_object_to_import
merge_requests_to_import.find_each do |merge_request|
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
pull_request = client.pull_request(project.import_source, merge_request.iid)
yield(pull_request)
mark_as_imported(merge_request)
end
end
private
# Returns only the merge requests that still have merged_by to be imported.
def merge_requests_to_import
project.merge_requests.id_not_in(already_imported_objects).with_state(:merged)
end
def already_imported_objects
Gitlab::Cache::Import::Caching.values_from_set(already_imported_cache_key)
end
end
end
end
end
end

View File

@ -0,0 +1,71 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module Importer
module PullRequests
class MergedByImporter
# pull_request - An instance of
# `Gitlab::GithubImport::Representation::PullRequest`
# project - An instance of `Project`
# client - An instance of `Gitlab::GithubImport::Client`
def initialize(pull_request, project, client)
@pull_request = pull_request
@project = project
@client = client
end
def execute
user_finder = GithubImport::UserFinder.new(project, client)
gitlab_user_id = user_finder.user_id_for(pull_request.merged_by)
metrics_upsert(gitlab_user_id)
add_note!
end
private
attr_reader :project, :pull_request, :client
def metrics_upsert(gitlab_user_id)
MergeRequest::Metrics.upsert({
target_project_id: project.id,
merge_request_id: merge_request.id,
merged_by_id: gitlab_user_id,
merged_at: pull_request.merged_at,
created_at: timestamp,
updated_at: timestamp
}, unique_by: :merge_request_id)
end
def add_note!
merge_request.notes.create!(
importing: true,
note: missing_author_note,
author_id: project.creator_id,
project: project,
created_at: pull_request.merged_at
)
end
def merge_request
@merge_request ||= project.merge_requests.find_by_iid(pull_request.iid)
end
def timestamp
@timestamp ||= Time.new.utc
end
def missing_author_note
format(s_("GitHubImporter|*Merged by: %{author} at %{timestamp}*"),
author: pull_request.merged_by&.login || 'ghost',
timestamp: pull_request.merged_at
)
end
end
end
end
end
end

View File

@ -1,57 +0,0 @@
# frozen_string_literal: true
module Gitlab
module GithubImport
module Importer
class PullRequestsMergedByImporter
include ParallelScheduling
def importer_class
PullRequestMergedByImporter
end
def representation_class
Gitlab::GithubImport::Representation::PullRequest
end
def sidekiq_worker_class
ImportPullRequestMergedByWorker
end
def collection_method
:pull_requests_merged_by
end
def object_type
:pull_request_merged_by
end
def id_for_already_imported_cache(merge_request)
merge_request.id
end
def each_object_to_import
merge_requests_to_import.find_each do |merge_request|
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
pull_request = client.pull_request(project.import_source, merge_request.iid)
yield(pull_request)
mark_as_imported(merge_request)
end
end
private
# Returns only the merge requests that still have merged_by to be imported.
def merge_requests_to_import
project.merge_requests.id_not_in(already_imported_objects).with_state(:merged)
end
def already_imported_objects
Gitlab::Cache::Import::Caching.values_from_set(already_imported_cache_key)
end
end
end
end
end

View File

@ -2375,6 +2375,9 @@ msgstr ""
msgid "AccountValidation|Verification is required to discourage and reduce the abuse on GitLab infrastructure. If you verify with a credit or debit card, %{strong_start}GitLab will not charge your card, it will only be used for validation.%{strong_end} %{learn_more_link}"
msgstr ""
msgid "Achievements"
msgstr ""
msgid "Achievements|%{namespace_full_path} awarded you the %{achievement_name} achievement"
msgstr ""

View File

@ -649,6 +649,8 @@ RSpec.describe 'Admin updates settings', feature_category: :shared do
end
it 'loads togglable usage ping payload on click', :js do
allow(Gitlab::Usage::ServicePingReport).to receive(:for).and_return({ uuid: '12345678', hostname: '127.0.0.1' })
stub_usage_data_connections
stub_database_flavor_check

View File

@ -1,7 +1,7 @@
/* eslint-disable no-return-assign, no-new, no-underscore-dangle */
import $ from 'jquery';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import htmlStaticLineHighlighter from 'test_fixtures_static/line_highlighter.html';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import LineHighlighter from '~/blob/line_highlighter';
import * as utils from '~/lib/utils/common_utils';
@ -17,7 +17,7 @@ describe('LineHighlighter', () => {
};
beforeEach(() => {
loadHTMLFixture('static/line_highlighter.html');
setHTMLFixture(htmlStaticLineHighlighter);
testContext.class = new LineHighlighter();
testContext.css = testContext.class.highlightLineClass;
return (testContext.spies = {

View File

@ -21,6 +21,7 @@ beforeEach(() => {
describe('CompareVersions', () => {
let wrapper;
let store;
let dispatchMock;
const targetBranchName = 'tmp-wine-dev';
const { commit } = getDiffWithCommit;
@ -29,6 +30,8 @@ describe('CompareVersions', () => {
store.state.diffs.commit = { ...store.state.diffs.commit, ...commitArgs };
}
dispatchMock = jest.spyOn(store, 'dispatch');
wrapper = mount(CompareVersionsComponent, {
store,
propsData: {
@ -146,7 +149,7 @@ describe('CompareVersions', () => {
it('renders short commit ID', () => {
expect(wrapper.text()).toContain('Viewing commit');
expect(wrapper.text()).toContain(wrapper.vm.commit.short_id);
expect(wrapper.text()).toContain(commit.short_id);
});
});
@ -204,10 +207,6 @@ describe('CompareVersions', () => {
setWindowLocation(`?commit_id=${mrCommit.id}`);
});
beforeEach(() => {
jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
});
it('uses the correct href', () => {
const link = getPrevCommitNavElement();
@ -219,7 +218,7 @@ describe('CompareVersions', () => {
link.trigger('click');
await nextTick();
expect(wrapper.vm.moveToNeighboringCommit).toHaveBeenCalledWith({
expect(dispatchMock).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
direction: 'previous',
});
});
@ -238,10 +237,6 @@ describe('CompareVersions', () => {
setWindowLocation(`?commit_id=${mrCommit.id}`);
});
beforeEach(() => {
jest.spyOn(wrapper.vm, 'moveToNeighboringCommit').mockImplementation(() => {});
});
it('uses the correct href', () => {
const link = getNextCommitNavElement();
@ -253,7 +248,9 @@ describe('CompareVersions', () => {
link.trigger('click');
await nextTick();
expect(wrapper.vm.moveToNeighboringCommit).toHaveBeenCalledWith({ direction: 'next' });
expect(dispatchMock).toHaveBeenCalledWith('diffs/moveToNeighboringCommit', {
direction: 'next',
});
});
it('renders a disabled button when there is no next commit', () => {

View File

@ -1,5 +1,5 @@
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import MockAdapter from 'axios-mock-adapter';
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';

View File

@ -1,5 +1,5 @@
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import MockAdapter from 'axios-mock-adapter';
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';

View File

@ -10,8 +10,6 @@ describe('MenuSection component', () => {
const findButton = () => wrapper.find('button');
const findCollapse = () => wrapper.getComponent(GlCollapse);
const findNavItems = () => wrapper.findAllComponents(NavItem);
const findSectionTitle = () => wrapper.findByTestId('section-title');
const createWrapper = (item, otherProps) => {
wrapper = shallowMountExtended(MenuSection, {
propsData: { item, ...otherProps },
@ -70,17 +68,6 @@ describe('MenuSection component', () => {
});
});
describe('`collectionStyle` prop', () => {
const newClasses = 'gl-font-sm gl-font-weight-semibold'.split(' ');
it('applies new classes when using new styles', () => {
createWrapper({ title: 'Asdf' }, { collectionStyle: true });
const classes = findSectionTitle().classes();
newClasses.forEach((newClass) => expect(classes).toContain(newClass));
});
});
describe('`separated` prop', () => {
describe('by default (false)', () => {
it('does not render a separator', () => {

View File

@ -1,6 +1,5 @@
import { mountExtended } from 'helpers/vue_test_utils_helper';
import SidebarMenu from '~/super_sidebar/components/sidebar_menu.vue';
import MenuSection from '~/super_sidebar/components/menu_section.vue';
import { PANELS_WITH_PINS } from '~/super_sidebar/constants';
import { sidebarData } from '../mock_data';
@ -102,10 +101,6 @@ describe('SidebarMenu component', () => {
'Also with subitems',
]);
});
it('passes `supportsPin` to menu sections', () => {
expect(wrapper.findAllComponents(MenuSection).at(1).props('collectionStyle')).toBe(true);
});
});
describe('when the sidebar does not support pins', () => {
@ -120,10 +115,6 @@ describe('SidebarMenu component', () => {
it('keeps all items as non-static', () => {
expect(wrapper.vm.nonStaticItems).toEqual(menuItems);
});
it('passes `supportsPin` to menu sections', () => {
expect(wrapper.findAllComponents(MenuSection).at(1).props('collectionStyle')).toBe(false);
});
});
});

View File

@ -0,0 +1,38 @@
import { mount } from '@vue/test-utils';
import SlotsWithSameName from './components/slots_with_same_name.vue';
import VOnceInsideVIf from './components/v_once_inside_v_if.vue';
import KeyInsideTemplate from './components/key_inside_template.vue';
import CommentsOnRootLevel from './components/comments_on_root_level.vue';
import SlotWithComment from './components/slot_with_comment.vue';
import DefaultSlotWithComment from './components/default_slot_with_comment.vue';
describe('Vue.js 3 compiler edge cases', () => {
it('workarounds issue #6063 when same slot is used with whitespace preserve', () => {
expect(() => mount(SlotsWithSameName)).not.toThrow();
});
it('workarounds issue #7725 when v-once is used inside v-if', () => {
expect(() => mount(VOnceInsideVIf)).not.toThrow();
});
it('renders vue.js 2 component when key is inside template', () => {
const wrapper = mount(KeyInsideTemplate);
expect(wrapper.text()).toBe('12345');
});
it('passes attributes to component with trailing comments on root level', () => {
const wrapper = mount(CommentsOnRootLevel, { propsData: { 'data-testid': 'test' } });
expect(wrapper.html()).toBe('<div data-testid="test"></div>');
});
it('treats empty slots with comments as empty', () => {
const wrapper = mount(SlotWithComment);
expect(wrapper.html()).toBe('<div>Simple</div>');
});
it('treats empty default slot with comments as empty', () => {
const wrapper = mount(DefaultSlotWithComment);
expect(wrapper.html()).toBe('<div>Simple</div>');
});
});

View File

@ -0,0 +1,5 @@
<template>
<!-- root level comment -->
<div><slot></slot></div>
<!-- root level comment -->
</template>

View File

@ -0,0 +1,18 @@
<script>
import Simple from './simple.vue';
export default {
components: {
Simple,
},
};
</script>
<template>
<simple>
<!-- slot comment typical for gitlab-ui, for example -->
<!-- slot comment typical for gitlab-ui, for example -->
<slot></slot>
<!-- slot comment typical for gitlab-ui, for example -->
<!-- slot comment typical for gitlab-ui, for example -->
</simple>
</template>

View File

@ -0,0 +1,7 @@
<template>
<div>
<template v-for="count in 5"
><span :key="count">{{ count }}</span></template
>
</div>
</template>

View File

@ -0,0 +1,10 @@
<script>
export default {
name: 'Simple',
};
</script>
<template>
<div>
<slot>{{ $options.name }}</slot>
</div>
</template>

View File

@ -0,0 +1,20 @@
<script>
import Simple from './simple.vue';
export default {
components: {
Simple,
},
};
</script>
<template>
<simple>
<template #default>
<!-- slot comment typical for gitlab-ui, for example -->
<!-- slot comment typical for gitlab-ui, for example -->
<slot></slot>
<!-- slot comment typical for gitlab-ui, for example -->
<!-- slot comment typical for gitlab-ui, for example -->
</template>
</simple>
</template>

View File

@ -0,0 +1,14 @@
<script>
import Simple from './simple.vue';
export default {
name: 'SlotsWithSameName',
components: { Simple },
};
</script>
<template>
<simple>
<template v-if="true" #default>{{ $options.name }}</template>
<template v-else #default>{{ $options.name }}</template>
</simple>
</template>

View File

@ -0,0 +1,12 @@
<script>
export default {
name: 'VOnceInsideVIf',
};
</script>
<template>
<div>
<template v-if="true">
<div v-once>{{ $options.name }}</div>
</template>
</div>
</template>

View File

@ -63,7 +63,7 @@ if (global.document) {
};
let compatH;
Vue.config.compilerOptions.whitespace = 'condense';
Vue.config.compilerOptions.whitespace = 'preserve';
Vue.createApp({
compatConfig: {
MODE: 3,

View File

@ -1,74 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
require_relative '../../config/initializers/doorkeeper_openid_connect_patch'
RSpec.describe 'doorkeeper_openid_connect_patch', feature_category: :integrations do
describe '.signing_key' do
let(:config) { Doorkeeper::OpenidConnect::Config.new }
before do
allow(config).to receive(:signing_key).and_return(key)
allow(config).to receive(:signing_algorithm).and_return(algorithm)
allow(Doorkeeper::OpenidConnect).to receive(:configuration).and_return(config)
end
context 'with RS256 algorithm' do
let(:algorithm) { :RS256 }
# Taken from https://github.com/doorkeeper-gem/doorkeeper-openid_connect/blob/01903c81a2b6237a3bf576ed45864f69ef20184e/spec/dummy/config/initializers/doorkeeper_openid_connect.rb#L6-L34
let(:key) do
<<~KEY
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAsjdnSA6UWUQQHf6BLIkIEUhMRNBJC1NN/pFt1EJmEiI88GS0
ceROO5B5Ooo9Y3QOWJ/n+u1uwTHBz0HCTN4wgArWd1TcqB5GQzQRP4eYnWyPfi4C
feqAHzQp+v4VwbcK0LW4FqtW5D0dtrFtI281FDxLhARzkhU2y7fuYhL8fVw5rUhE
8uwvHRZ5CEZyxf7BSHxIvOZAAymhuzNLATt2DGkDInU1BmF75tEtBJAVLzWG/j4L
PZh1EpSdfezqaXQlcy9PJi916UzTl0P7Yy+ulOdUsMlB6yo8qKTY1+AbZ5jzneHb
GDU/O8QjYvii1WDmJ60t0jXicmOkGrOhruOptwIDAQABAoIBAQChYNwMeu9IugJi
NsEf4+JDTBWMRpOuRrwcpfIvQAUPrKNEB90COPvCoju0j9OxCDmpdPtq1K/zD6xx
khlw485FVAsKufSp4+g6GJ75yT6gZtq1JtKo1L06BFFzb7uh069eeP7+wB6JxPHw
KlAqwxvsfADhxeolQUKCTMb3Vjv/Aw2cO/nn6RAOeftw2aDmFy8Xl+oTUtSxyib0
YCdU9cK8MxsxDdmowwHp04xRTm/wfG5hLEn7HMz1PP86iP9BiFsCqTId9dxEUTS1
K+VAt9FbxRAq5JlBocxUMHNxLigb94Ca2FOMR7F6l/tronLfHD801YoObF0fN9qW
Cgw4aTO5AoGBAOR79hiZVM7/l1cBid7hKSeMWKUZ/nrwJsVfNpu1H9xt9uDu+79U
mcGfM7pm7L2qCNGg7eeWBHq2CVg/XQacRNtcTlomFrw4tDXUkFN1hE56t1iaTs9m
dN9IDr6jFgf6UaoOxxoPT9Q1ZtO46l043Nzrkoz8cBEBaBY20bUDwCYjAoGBAMet
tt1ImGF1cx153KbOfjl8v54VYUVkmRNZTa1E821nL/EMpoONSqJmRVsX7grLyPL1
QyZe245NOvn63YM0ng0rn2osoKsMVJwYBEYjHL61iF6dPtW5p8FIs7auRnC3NrG0
XxHATZ4xhHD0iIn14iXh0XIhUVk+nGktHU1gbmVdAoGBANniwKdqqS6RHKBTDkgm
Dhnxw6MGa+CO3VpA1xGboxuRHeoY3KfzpIC5MhojBsZDvQ8zWUwMio7+w2CNZEfm
g99wYiOjyPCLXocrAssj+Rzh97AdzuQHf5Jh4/W2Dk9jTbdPSl02ltj2Z+2lnJFz
pWNjnqimHrSI09rDQi5NulJjAoGBAImquujVpDmNQFCSNA7NTzlTSMk09FtjgCZW
67cKUsqa2fLXRfZs84gD+s1TMks/NMxNTH6n57e0h3TSAOb04AM0kDQjkKJdXfhA
lrHEg4z4m4yf3TJ9Tat09HJ+tRIBPzRFp0YVz23Btg4qifiUDdcQWdbWIb/l6vCY
qhsu4O4BAoGBANbceYSDYRdT7a5QjJGibkC90Z3vFe4rDTBgZWg7xG0cpSU4JNg7
SFR3PjWQyCg7aGGXiooCM38YQruACTj0IFub24MFRA4ZTXvrACvpsVokJlQiG0Z4
tuQKYki41JvYqPobcq/rLE/AM7PKJftW35nqFuj0MrsUwPacaVwKBf5J
-----END RSA PRIVATE KEY-----
KEY
end
it 'returns the private key as JWK instance' do
expect(Doorkeeper::OpenidConnect.signing_key).to be_a ::JWT::JWK::KeyBase
expect(Doorkeeper::OpenidConnect.signing_key.kid).to eq 'IqYwZo2cE6hsyhs48cU8QHH4GanKIx0S4Dc99kgTIMA'
end
it 'matches json-jwt implementation' do
json_jwt_key = OpenSSL::PKey::RSA.new(key).public_key.to_jwk.slice(:kty, :kid, :e, :n)
expect(Doorkeeper::OpenidConnect.signing_key.export.sort.to_json).to eq(json_jwt_key.sort.to_json)
end
end
context 'with HS512 algorithm' do
let(:algorithm) { :HS512 }
let(:key) { 'the_greatest_secret_key' }
it 'returns the HMAC public key parameters' do
expect(Doorkeeper::OpenidConnect.signing_key_normalized).to eq(
kty: 'oct',
kid: 'lyAW7LdxryFWQtLdgxZpOrI87APHrzJKgWLT0BkWVog'
)
end
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
RSpec.describe Gitlab::GithubImport::Importer::PullRequests::AllMergedByImporter, feature_category: :importers do
let(:client) { double }
let_it_be(:project) { create(:project, import_source: 'http://somegithub.com') }
@ -16,7 +16,11 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
end
describe '#importer_class' do
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestMergedByImporter) }
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequests::MergedByImporter) }
end
describe '#sidekiq_worker_class' do
it { expect(subject.sidekiq_worker_class).to eq(Gitlab::GithubImport::PullRequests::ImportMergedByWorker) }
end
describe '#collection_method' do
@ -24,7 +28,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
end
describe '#id_for_already_imported_cache' do
it { expect(subject.id_for_already_imported_cache(double(id: 1))).to eq(1) }
it { expect(subject.id_for_already_imported_cache(instance_double(MergeRequest, id: 1))).to eq(1) }
end
describe '#each_object_to_import', :clean_gitlab_redis_cache do
@ -44,7 +48,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
expect { |b| subject.each_object_to_import(&b) }
.to yield_with_args(pull_request)
subject.each_object_to_import {}
subject.each_object_to_import
end
it 'skips cached merge requests' do
@ -55,7 +59,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestsMergedByImporter do
expect(client).not_to receive(:pull_request)
subject.each_object_to_import {}
subject.each_object_to_import
end
end
end

View File

@ -2,12 +2,16 @@
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :clean_gitlab_redis_cache do
RSpec.describe Gitlab::GithubImport::Importer::PullRequests::MergedByImporter,
:clean_gitlab_redis_cache, feature_category: :importers do
let_it_be(:merge_request) { create(:merged_merge_request) }
let(:project) { merge_request.project }
let(:merged_at) { Time.new(2017, 1, 1, 12, 00).utc }
let(:client_double) { double(user: { id: 999, login: 'merger', email: 'merger@email.com' } ) }
let(:merged_at) { Time.utc(2017, 1, 1, 12) }
let(:client_double) do
instance_double(Gitlab::GithubImport::Client, user: { id: 999, login: 'merger', email: 'merger@email.com' })
end
let(:merger_user) { { id: 999, login: 'merger' } }
let(:pull_request) do
@ -25,7 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
shared_examples 'adds a note referencing the merger user' do
it 'adds a note referencing the merger user' do
expect { subject.execute }
.to change(Note, :count).by(1)
.to change { Note.count }.by(1)
.and not_change(merge_request, :updated_at)
metrics = merge_request.metrics.reload
@ -68,7 +72,7 @@ RSpec.describe Gitlab::GithubImport::Importer::PullRequestMergedByImporter, :cle
it 'adds a note referencing the merger user' do
expect { subject.execute }
.to change(Note, :count).by(1)
.to change { Note.count }.by(1)
.and not_change(merge_request, :updated_at)
metrics = merge_request.metrics.reload

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Groups::AchievementsController, feature_category: :user_profile do
let_it_be(:user) { create(:user) }
shared_examples 'response with 404 status' do
it 'returns 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
shared_examples 'ok response with index template' do
it 'renders the index template' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:index)
end
end
shared_examples 'ok response with index template if authorized' do
context 'with a private group' do
let(:group) { create(:group, :private) }
context 'with authorized user' do
before do
group.add_guest(user)
sign_in(user)
end
it_behaves_like 'ok response with index template'
context 'when achievements ff is disabled' do
before do
stub_feature_flags(achievements: false)
end
it_behaves_like 'response with 404 status'
end
end
context 'with unauthorized user' do
before do
sign_in(user)
end
it_behaves_like 'response with 404 status'
end
context 'with anonymous user' do
it 'redirects to sign_in page' do
subject
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(new_user_session_path)
end
end
end
context 'with a public group' do
let(:group) { create(:group, :public) }
context 'with anonymous user' do
it_behaves_like 'ok response with index template'
end
end
end
describe 'GET #index' do
subject { get group_achievements_path(group) }
it_behaves_like 'ok response with index template if authorized'
end
end

View File

@ -62,7 +62,7 @@ RSpec.describe Import::GithubFailureEntity, feature_category: :importers do
end
it_behaves_like 'import failure entity' do
let(:source) { 'Gitlab::GithubImport::Importer::PullRequestMergedByImporter' }
let(:source) { 'Gitlab::GithubImport::Importer::PullRequests::MergedByImporter' }
let(:title) { 'Pull request 2 merger' }
let(:provider_url) { 'https://github.com/example/repo/pull/2' }
end

View File

@ -277,6 +277,7 @@ RSpec.describe 'Every Sidekiq worker', feature_category: :shared do
'Gitlab::GithubImport::ImportPullRequestReviewWorker' => 5,
'Gitlab::GithubImport::PullRequests::ImportReviewRequestWorker' => 5,
'Gitlab::GithubImport::PullRequests::ImportReviewWorker' => 5,
'Gitlab::GithubImport::PullRequests::ImportMergedByWorker' => 5,
'Gitlab::GithubImport::ImportPullRequestWorker' => 5,
'Gitlab::GithubImport::RefreshImportJidWorker' => 5,
'Gitlab::GithubImport::Stage::FinishImportWorker' => 5,

View File

@ -10,6 +10,6 @@ RSpec.describe Gitlab::GithubImport::ImportPullRequestMergedByWorker, feature_ca
end
describe '#importer_class' do
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequestMergedByImporter) }
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequests::MergedByImporter) }
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::PullRequests::ImportMergedByWorker, feature_category: :importers do
it { is_expected.to include_module(Gitlab::GithubImport::ObjectImporter) }
describe '#representation_class' do
it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::PullRequest) }
end
describe '#importer_class' do
it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::PullRequests::MergedByImporter) }
end
describe '#object_type' do
it { expect(subject.object_type).to eq(:pull_request_merged_by) }
end
end

View File

@ -13,7 +13,7 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportPullRequestsMergedByWorker, fe
client = double(:client)
waiter = Gitlab::JobWaiter.new(2, '123')
expect(Gitlab::GithubImport::Importer::PullRequestsMergedByImporter)
expect(Gitlab::GithubImport::Importer::PullRequests::AllMergedByImporter)
.to receive(:new)
.with(project, client)
.and_return(importer)