Commit aa99514d authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖

Add latest changes from gitlab-org/[email protected]

parent 3aaaf9cd
<script>
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { GlAlert, GlButton, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
......@@ -25,6 +25,8 @@ export default {
GlAlert,
GlLink,
GlSprintf,
GlEmptyState,
GlButton,
},
props: {
graphUrl: {
......@@ -32,6 +34,16 @@ export default {
required: false,
default: '',
},
emptySvgPath: {
type: String,
required: true,
default: '',
},
dagDocPath: {
type: String,
required: true,
default: '',
},
},
data() {
return {
......@@ -40,6 +52,7 @@ export default {
graphData: null,
showFailureAlert: false,
showBetaInfo: true,
hasNoDependentJobs: false,
};
},
errorTexts: {
......@@ -48,6 +61,16 @@ export default {
[UNSUPPORTED_DATA]: __('DAG visualization requires at least 3 dependent jobs.'),
[DEFAULT]: __('An unknown error occurred while loading this graph.'),
},
emptyStateTexts: {
title: __('Start using Directed Acyclic Graphs (DAG)'),
firstDescription: __(
"This pipeline does not use the %{codeStart}needs%{codeEnd} keyword and can't be represented as a directed acyclic graph.",
),
secondDescription: __(
'Using %{codeStart}needs%{codeEnd} allows jobs to run before their stage is reached, as soon as their individual dependencies are met, which speeds up your pipelines.',
),
button: __('Learn more about job dependencies'),
},
computed: {
betaMessage() {
return __(
......@@ -114,11 +137,18 @@ export default {
return;
}
if (parsed.links.length < 2) {
if (parsed.links.length === 1) {
this.reportFailure(UNSUPPORTED_DATA);
return;
}
// If there are no links, we don't report failure
// as it simply means the user does not use job dependencies
if (parsed.links.length === 0) {
this.hasNoDependentJobs = true;
return;
}
this.graphData = parsed;
},
hideAlert() {
......@@ -172,9 +202,38 @@ export default {
<dag-graph
v-if="shouldDisplayGraph"
:graph-data="graphData"
@on-failure="reportFailure"
@onFailure="reportFailure"
@update-annotation="updateAnnotation"
/>
<gl-empty-state
v-else-if="hasNoDependentJobs"
:svg-path="emptySvgPath"
:title="$options.emptyStateTexts.title"
>
<template #description>
<div class="gl-text-left">
<p>
<gl-sprintf :message="$options.emptyStateTexts.firstDescription">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
<p>
<gl-sprintf :message="$options.emptyStateTexts.secondDescription">
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</p>
</div>
</template>
<template #actions>
<gl-button :href="dagDocPath" target="__blank" variant="success">
{{ $options.emptyStateTexts.button }}
</gl-button>
</template>
</gl-empty-state>
</div>
</div>
</template>
......@@ -151,7 +151,8 @@ const createDagApp = () => {
}
const el = document.querySelector('#js-pipeline-dag-vue');
const graphUrl = el?.dataset?.pipelineDataPath;
const { pipelineDataPath, emptySvgPath, dagDocPath } = el?.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
......@@ -161,7 +162,9 @@ const createDagApp = () => {
render(createElement) {
return createElement('dag', {
props: {
graphUrl,
graphUrl: pipelineDataPath,
emptySvgPath,
dagDocPath,
},
});
},
......
import renderKramdownList from './renderers/render_kramdown_list';
import renderKramdownText from './renderers/render_kramdown_text';
import renderIdentifierText from './renderers/render_identifier_text';
const listRenderers = [renderKramdownList];
const textRenderers = [renderKramdownText];
const textRenderers = [renderKramdownText, renderIdentifierText];
const executeRenderer = (renderers, node, context) => {
const availableRenderer = renderers.find(renderer => renderer.canRender(node, context));
return availableRenderer ? availableRenderer.render(context) : context.origin();
return availableRenderer ? availableRenderer.render(node, context) : context.origin();
};
const buildCustomRendererFunctions = (customRenderers, defaults) => {
......
......@@ -16,6 +16,10 @@ export const buildUneditableOpenTokens = token => {
export const buildUneditableCloseToken = () => buildToken('closeTag', 'div');
export const buildUneditableCloseTokens = token => {
return [token, buildToken('closeTag', 'div')];
};
export const buildUneditableTokens = token => {
return [...buildUneditableOpenTokens(token), buildUneditableCloseToken()];
};
import {
buildUneditableOpenTokens,
buildUneditableCloseTokens,
buildUneditableTokens,
} from './build_uneditable_token';
const identifierRegex = /(^\[.+\]: .+)/;
const isBasicIdentifier = ({ literal }) => {
return identifierRegex.test(literal);
};
const isInlineCodeNode = ({ type, tickCount }) => type === 'code' && tickCount === 1;
const hasAdjacentInlineCode = (isForward, node) => {
const direction = isForward ? 'next' : 'prev';
let currentNode = node;
while (currentNode[direction] && currentNode.literal !== null) {
if (isInlineCodeNode(currentNode)) {
return true;
}
currentNode = currentNode[direction];
}
return false;
};
const hasEnteringPotential = literal => literal.includes('[');
const hasExitingPotential = literal => literal.includes(']: ');
const hasAdjacentExit = node => {
let currentNode = node;
while (currentNode.next && currentNode.literal !== null) {
if (hasExitingPotential(currentNode.literal)) {
return true;
}
currentNode = currentNode.next;
}
return false;
};
const isEnteringWithAdjacentInlineCode = ({ literal, next }) => {
if (next && hasEnteringPotential(literal) && !hasExitingPotential(literal)) {
return hasAdjacentInlineCode(true, next) && hasAdjacentExit(next);
}
return false;
};
const isExitingWithAdjacentInlineCode = ({ literal, prev }) => {
if (prev && !hasEnteringPotential(literal) && hasExitingPotential(literal)) {
return hasAdjacentInlineCode(false, prev);
}
return false;
};
const isAdjacentInlineCodeIdentifier = node => {
return isEnteringWithAdjacentInlineCode(node) || isExitingWithAdjacentInlineCode(node);
};
const canRender = (node, context) => {
return isBasicIdentifier(node) || isAdjacentInlineCodeIdentifier(node, context);
};
const render = (node, { origin }) => {
if (isEnteringWithAdjacentInlineCode(node)) {
return buildUneditableOpenTokens(origin());
} else if (isExitingWithAdjacentInlineCode(node)) {
return buildUneditableCloseTokens(origin());
}
return buildUneditableTokens(origin());
};
export default { canRender, render };
......@@ -21,7 +21,7 @@ const canRender = node => {
return false;
};
const render = ({ entering, origin }) =>
const render = (_, { entering, origin }) =>
entering ? buildUneditableOpenTokens(origin()) : buildUneditableCloseToken();
export default { canRender, render };
import { buildUneditableTokens } from './build_uneditable_token';
const kramdownRegex = /(^{:.+}$)/;
const canRender = ({ literal }) => {
const kramdownRegex = /(^{:.+}$)/gm;
return kramdownRegex.test(literal);
};
const render = ({ origin }) => {
const render = (_, { origin }) => {
return buildUneditableTokens(origin());
};
......
......@@ -1798,6 +1798,7 @@ class Project < ApplicationRecord
after_create_default_branch
join_pool_repository
refresh_markdown_cache!
write_repository_config
end
def update_project_counter_caches
......
......@@ -84,8 +84,12 @@ module Projects
def after_create_actions
log_info("#{@project.owner.name} created a new project \"#{@project.full_name}\"")
# Skip writing the config for project imports/forks because it
# will always fail since the Git directory doesn't exist until
# a background job creates it (see Project#add_import_job).
@project.write_repository_config unless @project.import?
unless @project.gitlab_project_import?
@project.write_repository_config
@project.create_wiki unless skip_wiki?
end
......
......@@ -83,7 +83,7 @@
- if dag_pipeline_tab_enabled
#js-tab-dag.tab-pane
#js-pipeline-dag-vue{ data: { pipeline_data_path: dag_project_pipeline_path(@project, @pipeline) } }
#js-pipeline-dag-vue{ data: { pipeline_data_path: dag_project_pipeline_path(@project, @pipeline), empty_svg_path: image_path('illustrations/empty-state/empty-dag-md.svg'), dag_doc_path: help_page_path('ci/yaml/README.md', anchor: 'needs')} }
#js-tab-tests.tab-pane
#js-pipeline-tests-detail
......
......@@ -339,14 +339,6 @@
:weight: 1
:idempotent:
:tags: []
- :name: cronjob:stuck_import_jobs
:feature_category: :importers
:has_external_dependencies:
:urgency: :low
:resource_boundary: :cpu
:weight: 1
:idempotent:
:tags: []
- :name: cronjob:stuck_merge_jobs
:feature_category: :source_code_management
:has_external_dependencies:
......
# frozen_string_literal: true
class StuckImportJobsWorker # rubocop:disable Scalability/IdempotentWorker
include Gitlab::Import::StuckImportJob
private
def track_metrics(with_jid_count, without_jid_count)
Gitlab::Metrics.add_event(
:stuck_import_jobs,
projects_without_jid_count: without_jid_count,
projects_with_jid_count: with_jid_count
)
end
def enqueued_import_states
ProjectImportState.with_status([:scheduled, :started])
end
end
---
title: Resolve Add no graph empty state for DAG
merge_request: 35053
author:
type: changed
---
title: Add a custom HTML renderer to the Static Site Editor for markdown identifier syntax
merge_request: 35077
author:
type: added
---
title: 'Static Site Editor can’t be opened in projects belonging to a subgroup'
merge_request: 35378
author:
type: fixed
---
title: Defer updating .git/config for imported projects
merge_request: 35308
author:
type: fixed
---
title: Ensure .git/config is updated for forks
merge_request: 35305
author:
type: fixed
......@@ -605,6 +605,12 @@ When enabled, the following applies:
- Users are not allowed to share project with other groups or invite members to
a project created in a group.
To enable it you need to:
1. [Enable LDAP](#configuration-core-only)
1. Navigate to **(admin)** **Admin Area > Settings -> Visibility and access controls**.
1. Make sure the "Lock memberships to LDAP synchronization" checkbox is enabled.
### Adjusting LDAP group sync schedule **(STARTER ONLY)**
NOTE: **Note:**
......
......@@ -21,6 +21,11 @@ GitLab has several features based on receiving incoming emails:
## Requirements
NOTE: **Note:**
It is **not** recommended to use an email address that receives or will receive any
messages not intended for the GitLab instance. Any incoming emails not intended
for GitLab will receive a reject notice.
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
email account. GitLab requires one of the following three strategies:
......@@ -106,6 +111,16 @@ Alternatively, use a dedicated domain for GitLab email communications such as
See GitLab issue [#30366](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30366)
for a real-world example of this exploit.
CAUTION:**Caution:**
Be sure to use a mail server that has been configured to reduce
spam.
A Postfix mail server that is running on a default configuration, for example,
can result in abuse. All messages received on the configured mailbox will be processed
and messages that are not intended for the GitLab instance will receive a reject notice.
If the sender's address is spoofed, the reject notice will be delivered to the spoofed
`FROM` address, which can cause the mail server's IP or domain to appear on a block
list.
### Omnibus package installations
1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature
......
......@@ -50,7 +50,7 @@ The JWT's payload looks like this:
}
```
The JWT is encoded by using RS256 and signed with your GitLab instance's OpenID Connect private key. The expire time for the token will be set to job's timeout, if specifed, or 5 minutes if it is not. The key used to sign this token may change without any notice. In such case retrying the job will generate new JWT using the current signing key.
The JWT is encoded by using RS256 and signed with your GitLab instance's OpenID Connect private key. The expire time for the token will be set to job's timeout, if specified, or 5 minutes if it is not. The key used to sign this token may change without any notice. In such case retrying the job will generate new JWT using the current signing key.
You can use this JWT and your instance's JWKS endpoint (`https://gitlab.example.com/-/jwks`) to authenticate with a Vault server that is configured to allow the JWT Authentication method for authentication.
......
......@@ -218,6 +218,16 @@ be used both locally in development and on any deployed GitLab instance to
diagnose poor search performance. This will show the exact queries being made,
which is useful to diagnose why a search might be slow.
### Correlation ID and X-Opaque-Id
Our [correlation
ID](./distributed_tracing.md#developer-guidelines-for-working-with-correlation-ids)
is forwarded by all requests from Rails to Elasticsearch as the
[`X-Opaque-Id`](https://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html#_identifying_running_tasks)
header which allows us to track any
[tasks](https://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html)
in the cluster back the request in GitLab.
## Troubleshooting
### Getting `flood stage disk watermark [95%] exceeded`
......
......@@ -39,7 +39,7 @@ A user can be deactivated from the Admin Area. To do this:
Please note that for the deactivation option to be visible to an admin, the user:
- Must be currently active.
- Must not have any signins or activity in the last 180 days.
- Must not have signed in, or have any activity, in the last 180 days.
Users can also be deactivated using the [GitLab API](../../api/users.md#deactivate-user).
......
......@@ -90,7 +90,7 @@ Once you're on the dashboard, at the top you should see a series of filters for:
NOTE: **Note:**
The dashboard only shows projects with [security reports](#supported-reports) enabled in a group.
![Dashboard with action buttons and metrics](img/group_security_dashboard_v13_0.png)
![Dashboard with action buttons and metrics](img/group_security_dashboard_v13_2.png)
Selecting one or more filters will filter the results in this page.
......@@ -162,7 +162,7 @@ To add projects to the dashboard:
Once added, the dashboard will display the vulnerabilities found in your chosen
projects.
![Instance Security Dashboard with projects](img/instance_security_dashboard_with_projects_v13_0.png)
![Instance Security Dashboard with projects](img/instance_security_dashboard_with_projects_v13_2.png)
### Export vulnerabilities
......
......@@ -1155,7 +1155,7 @@ below are examples and should be replaced with settings specific to your environ
ui:
enabled: true
server:
# Disable the built in data storage volume as it's not safe for Hight Availablity mode
# Disable the built in data storage volume as it's not safe for Hight Availability mode
dataStorage:
enabled: false
# Enable High Availability Mode
......
......@@ -241,7 +241,7 @@ can configure this manually as follows:
For a full example using the pre-built image, see [Example `.gitlab-ci.yaml`
file](#example-gitlab-ciyaml-file).
For an example displaying multiple reports, see [`.gitlab-ci.yaml` multiple reports file](#mulitple-terraform-plan-reports).
For an example displaying multiple reports, see [`.gitlab-ci.yaml` multiple reports file](#multiple-terraform-plan-reports).
1. Running the pipeline displays the widget in the merge request, like this:
......@@ -311,7 +311,7 @@ apply:
- master
```
### Mulitple Terraform Plan reports
### Multiple Terraform Plan reports
Starting with 13.2, you can display mutiple reports on the Merge Request page. The reports will also display the `artifact: name:`. See example below for a suggested setup.
......
......@@ -34,10 +34,12 @@ Setting up a Status Page is pretty painless but there are a few things you need
To use GitLab Status Page you first need to set up your account details for your cloud provider in the operations settings page. Today, only AWS is supported.
1. Within your AWS account, create an AWS access key.
1. Add the following permissions policies:
#### AWS Setup
1. Within your AWS acccout, create two new IAM policies.
- [Create bucket](https://gitlab.com/gitlab-org/status-page/-/blob/master/deploy/etc/s3_create_policy.json).
- [Update bucket contents](https://gitlab.com/gitlab-org/status-page/-/blob/master/deploy/etc/s3_update_bucket_policy.json) (Remember replace `S3_BUCKET_NAME` with your bucket name).
1. Create a new AWS access key with the permissions policies created in the first step.
### Status Page project
......
......@@ -21,6 +21,11 @@ module Gitlab
issue_metrics_table[:first_mentioned_in_commit_at]
end
override :column_list
def column_list
[timestamp_projection]
end
# rubocop: disable CodeReuse/ActiveRecord
def apply_query_customization(query)
issue_metrics_join = mr_closing_issues_table
......
......@@ -21,6 +21,7 @@ module Gitlab
Arel::Nodes::NamedFunction.new('COALESCE', column_list)
end
override :column_list
def column_list
[
issue_metrics_table[:first_associated_with_milestone_at],
......
......@@ -11,6 +11,7 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
override :column_list
def column_list
[timestamp_projection]
end
......
......@@ -21,6 +21,7 @@ module Gitlab
Arel::Nodes::NamedFunction.new('COALESCE', column_list)
end
override :column_list
def column_list
[
issue_metrics_table[:first_associated_with_milestone_at],
......
......@@ -21,6 +21,7 @@ module Gitlab
mr_metrics_table[:first_deployed_to_production_at]
end
override :column_list
def column_list
[timestamp_projection]
end
......
......@@ -7,6 +7,7 @@ module Gitlab
# Base class for expressing an event that can be used for a stage.
class StageEvent
include Gitlab::CycleAnalytics::MetricsTables
extend Gitlab::Utils::Override
delegate :label_based?, to: :class
......
......@@ -5,8 +5,10 @@ require 'elasticsearch-transport'
module Gitlab
module Instrumentation
module ElasticsearchTransportInterceptor
def perform_request(*args)
def perform_request(method, path, params = {}, body = nil, headers = nil)
start = Time.now
headers = (headers || {})
.reverse_merge({ 'X-Opaque-Id': Labkit::Correlation::CorrelationId.current_or_new_id })
super
ensure
if ::Gitlab::SafeRequestStore.active?
......@@ -14,7 +16,7 @@ module Gitlab
::Gitlab::Instrumentation::ElasticsearchTransport.increment_request_count
::Gitlab::Instrumentation::ElasticsearchTransport.add_duration(duration)
::Gitlab::Instrumentation::ElasticsearchTransport.add_call_details(duration, args)
::Gitlab::Instrumentation::ElasticsearchTransport.add_call_details(duration, method, path, params, body)
end
end
end
......@@ -47,14 +49,14 @@ module Gitlab
::Gitlab::SafeRequestStore[ELASTICSEARCH_CALL_DURATION] += duration
end
def self.add_call_details(duration, args)
def self.add_call_details(duration, method, path, params, body)
return unless Gitlab::PerformanceBar.enabled_for_request?
detail_store << {
method: args[0],
path: args[1],
params: args[2],
body: args[3],
method: method,
path: path,
params: params,
body: body,
duration: duration,
backtrace: ::Gitlab::BacktraceCleaner.clean_backtrace(caller)
}
......
......@@ -20,7 +20,7 @@ module Gitlab
commit_id: commit_id,
project_id: project.id,
project: project.path,
namespace: project.namespace.path,
namespace: project.namespace.full_path,
return_url: sanitize_url(return_url),