Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-01-09 00:13:20 +00:00
parent 5b5ff31460
commit 3f8e9ba69e
22 changed files with 179 additions and 138 deletions

View File

@ -31,11 +31,6 @@ export default {
required: false,
default: false,
},
showDetailsLink: {
type: Boolean,
required: false,
default: false,
},
status: {
type: String,
required: true,
@ -56,7 +51,7 @@ export default {
},
showDetails() {
return this.showDetailsLink && Boolean(this.detailsPathWithId) && this.hasFailures;
return Boolean(this.detailsPathWithId) && this.hasFailures;
},
detailsPathWithId() {

View File

@ -18,7 +18,6 @@ import { createAlert } from '~/alert';
import * as Sentry from '~/sentry/sentry_browser_wrapper';
import { s__, __, n__, sprintf } from '~/locale';
import { HTTP_STATUS_TOO_MANY_REQUESTS } from '~/lib/utils/http_status';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import { getGroupPathAvailability } from '~/rest_api';
@ -69,7 +68,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagsMixin()],
props: {
sourceUrl: {
type: String,

View File

@ -1,6 +1,5 @@
<script>
import {
GlButton,
GlEmptyState,
GlIcon,
GlLink,
@ -22,7 +21,6 @@ import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { isImporting } from '../utils';
import { DEFAULT_ERROR } from '../utils/error_messages';
@ -43,7 +41,6 @@ const tableCell = (config) => ({
export default {
components: {
GlButton,
GlEmptyState,
GlIcon,
GlLink,
@ -59,8 +56,6 @@ export default {
GlTooltip,
},
mixins: [glFeatureFlagMixin()],
inject: ['realtimeChangesPath'],
data() {
@ -107,10 +102,6 @@ export default {
.map((item) => item.bulk_import_id);
},
showDetailsLink() {
return this.glFeatures.bulkImportDetailsPage;
},
paginationConfigCopy() {
return { ...this.paginationConfig };
},
@ -265,7 +256,7 @@ export default {
<template #cell(created_at)="{ value }">
<time-ago :time="value" />
</template>
<template #cell(status)="{ value, item, toggleDetails, detailsShowing }">
<template #cell(status)="{ value, item }">
<div
class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-align-items-flex-start gl-justify-content-space-between gl-gap-3"
>
@ -273,20 +264,10 @@ export default {
:id="item.bulk_import_id"
:entity-id="item.id"
:has-failures="item.has_failures"
:show-details-link="showDetailsLink"
:status="value"
/>
<gl-button
v-if="!showDetailsLink && item.failures.length"
:selected="detailsShowing"
@click="toggleDetails"
>{{ __('Details') }}</gl-button
>
</div>
</template>
<template #row-details="{ item }">
<pre><code>{{ item.failures }}</code></pre>
</template>
</gl-table-lite>
<pagination-bar
:page-info="pageInfo"

View File

@ -37,6 +37,7 @@ export default {
:id="htmlId"
v-gl-tooltip.viewport="warningMessage"
data-name="warning"
data-testid="warning"
class="gl-ml-2"
/>
</span>

View File

@ -6,10 +6,6 @@ class Import::BulkImportsController < ApplicationController
before_action :ensure_bulk_import_enabled
before_action :verify_blocked_uri, only: :status
before_action only: [:history] do
push_frontend_feature_flag(:bulk_import_details_page)
end
feature_category :importers
urgency :low
@ -53,9 +49,7 @@ class Import::BulkImportsController < ApplicationController
end
end
def details
render_404 unless Feature.enabled?(:bulk_import_details_page)
end
def details; end
def create
return render json: { success: false }, status: :too_many_requests if throttled_request?

View File

@ -1,8 +1,8 @@
---
name: bulk_import_details_page
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/135004
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/429109
milestone: '16.6'
name: agent_registry
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140859
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437540
milestone: '16.8'
type: development
group: group::import and integrate
default_enabled: true
group: "group::mlops"
default_enabled: false

View File

@ -23994,7 +23994,7 @@ Returns [`PipelineSecurityReportFinding`](#pipelinesecurityreportfinding).
##### `Pipeline.securityReportFindings`
Vulnerability findings reported on the pipeline.
Vulnerability findings reported on the pipeline. By default all the states except dismissed are included in the response.
Returns [`PipelineSecurityReportFindingConnection`](#pipelinesecurityreportfindingconnection).

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

76
doc/operations/metrics.md Normal file
View File

@ -0,0 +1,76 @@
---
stage: Monitor
group: Observability
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Metrics **(ULTIMATE SAAS EXPERIMENT)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124966) in GitLab 16.7 [with a flag](../administration/feature_flags.md) named `observability_metrics`. Disabled by default. This feature is an [Experiment](../policy/experiment-beta-support.md#experiment).
FLAG:
On self-managed GitLab, by default this feature is not available.
To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `observability_metrics`.
On GitLab.com, this feature is not available.
The feature is not ready for production use.
Metrics provide insight about the operational health of monitored systems.
Use metrics to learn more about your systems and applications in a given time range.
Metrics are structured as time series data, and are:
- Indexed by timestamp
- Continuously expanding as additional data is gathered
- Usually aggregated, downsampled, and queried by range
- Have write-intensive requirements
## Configure metrics
Configure metrics to enable them for a project.
Prerequisites:
You must have at least the Maintainer role for the project.
1. Create an access token and enable metrics:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Access Tokens**.
1. Create an access token with the following scopes: `read_api`, `read_observability`, `write_observability`. Be sure to save the access token value for later.
1. Select **Monitor > Tracing**, and then select **Enable**.
1. To configure your application to send GitLab metrics, set the following environment variables:
```shell
OTEL_EXPORTER = "otlphttp"
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT = "https://observe.gitlab.com/v3/<namespace-id>/<gitlab-project-id>/ingest/metrics"
OTEL_EXPORTER_OTLP_METRICS_HEADERS = "PRIVATE-TOKEN=<gitlab-access-token>"
```
Use the following values:
- `namespace-id` - The top-level group ID that contains the project
- `gitlab-project-id` - The project ID
- `gitlab-access-token` - The access token you created
Metrics are configured for your project.
When you run your application, the OpenTelemetry exporter sends metrics to GitLab.
## View metrics
You can view the metrics for a given project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Monitor > Metrics**.
A list of metrics is displayed.
Select a metric to view its details.
![list of metrics](img/metrics_list_v16_8.png)
### Metric details
Metrics are displayed as either a sum, a gauge, or a histogram.
The metric details page displays a chart depending on the type of metric.
On the metric details page, you can also view a metric for a specific time range.
![metrics details](img/metrics_details_v16_8.png)

View File

@ -121,7 +121,7 @@ This rule enforces the defined actions based on security scan findings.
| `scanners` | `array` of `string` | true | `sast`, `secret_detection`, `dependency_scanning`, `container_scanning`, `dast`, `coverage_fuzzing`, `api_fuzzing` | The security scanners for this rule to consider. `sast` includes results from both SAST and SAST IaC scanners. |
| `vulnerabilities_allowed` | `integer` | true | Greater than or equal to zero | Number of vulnerabilities allowed before this rule is considered. |
| `severity_levels` | `array` of `string` | true | `info`, `unknown`, `low`, `medium`, `high`, `critical` | The severity levels for this rule to consider. |
| `vulnerability_states` | `array` of `string` | true | `newly_detected`, `detected`, `confirmed`, `resolved`, `dismissed`, `new_needs_triage`, `new_dismissed` | All vulnerabilities fall into two categories:<br><br>**Newly Detected Vulnerabilities** - the `newly_detected` policy option covers vulnerabilities identified in the merge request branch itself but that do not currently exist on the default branch. This policy option requires a pipeline to complete before the rule is evaluated so that it knows whether vulnerabilities are newly detected or not. Merge requests are blocked until the pipeline and necessary security scans are complete. The `newly_detected` option considers both of the following statuses:<br><br> • Detected<br> • Dismissed<br><br> The `new_needs_triage` option considers the status<br><br> • Detected<br><br> The `new_dismissed` option considers the status<br><br> • Dismissed<br><br>**Pre-Existing Vulnerabilities** - these policy options are evaluated immediately and do not require a pipeline complete as they consider only vulnerabilities previously detected in the default branch.<br><br>`Detected` - the policy looks for vulnerabilities in the detected state.<br>`Confirmed` - the policy looks for vulnerabilities in the confirmed state.<br>`Dismissed` - the policy looks for vulnerabilities in the dismissed state.<br>`Resolved` - the policy looks for vulnerabilities in the resolved state. |
| `vulnerability_states` | `array` of `string` | true | `[]` or `newly_detected`, `detected`, `confirmed`, `resolved`, `dismissed`, `new_needs_triage`, `new_dismissed` | All vulnerabilities fall into two categories:<br><br>**Newly Detected Vulnerabilities** - the `newly_detected` policy option covers vulnerabilities identified in the merge request branch itself but that do not currently exist on the default branch. This policy option requires a pipeline to complete before the rule is evaluated so that it knows whether vulnerabilities are newly detected or not. Merge requests are blocked until the pipeline and necessary security scans are complete. The `newly_detected` option considers both of the following statuses:<br><br> • Detected<br> • Dismissed<br><br> The `new_needs_triage` option considers the status<br><br> • Detected<br><br> The `new_dismissed` option considers the status<br><br> • Dismissed<br><br>**Pre-Existing Vulnerabilities** - these policy options are evaluated immediately and do not require a pipeline complete as they consider only vulnerabilities previously detected in the default branch.<br><br>`Detected` - the policy looks for vulnerabilities in the detected state.<br>`Confirmed` - the policy looks for vulnerabilities in the confirmed state.<br>`Dismissed` - the policy looks for vulnerabilities in the dismissed state.<br>`Resolved` - the policy looks for vulnerabilities in the resolved state. <br><br>An empty array, `[]`, covers the same statuses as `newly_detected`. It is equivalent to specifying `['new_needs_triage', 'new_dismissed']`. |
| `vulnerability_attributes` | `object` | false | `{false_positive: boolean, fix_available: boolean}` | All vulnerability findings are considered by default. But filters can be applied for attributes to consider only vulnerability findings: <br><br> • With a fix available (`fix_available: true`)<br><br> • With no fix available (`fix_available: false`)<br> • That are false positive (`false_positive: true`)<br> • That are not false positive (`false_positive: false`)<br> • Or a combination of both. For example (`fix_available: true, false_positive: false`) |
| `vulnerability_age` | `object` | false | N/A | Filter pre-existing vulnerability findings by age. A vulnerability's age is calculated as the time since it was detected in the project. The criteria are `operator`, `value`, and `interval`.<br>- The `operator` criterion specifies if the age comparison used is older than (`greater_than`) or younger than (`less_than`).<br>- The `value` criterion specifies the numeric value representing the vulnerability's age.<br>- The `interval` criterion specifies the unit of measure of the vulnerability's age: `day`, `week`, `month`, or `year`.<br><br>Example: `operator: greater_than`, `value: 30`, `interval: day`. |

View File

@ -248,7 +248,8 @@ To view group import history:
## Review results of the import
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429109) in GitLab 16.6 [with a flag](../../feature_flags.md) named `bulk_import_details_page`. Enabled by default.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/429109) in GitLab 16.6 [with a flag](../../feature_flags.md) named `bulk_import_details_page`. Enabled by default.
> - Feature flag `bulk_import_details_page` removed in GitLab 16.8.
To review the results of an import:

View File

@ -1922,6 +1922,12 @@ msgstr ""
msgid "AI-generated summary"
msgstr ""
msgid "AIAgents|AI Agents"
msgstr ""
msgid "AIAgents|Create agent"
msgstr ""
msgid "AIPoweredSM|AI-powered features"
msgstr ""

View File

@ -303,27 +303,11 @@ RSpec.describe Import::BulkImportsController, feature_category: :importers do
describe 'GET details' do
subject(:request) { get :details }
context 'when bulk_import_details_page feature flag is enabled' do
before do
stub_feature_flags(bulk_import_details_page: true)
request
end
it 'responds with a 200 and shows the template', :aggregate_failures do
request
it 'responds with a 200 and shows the template', :aggregate_failures do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:details)
end
end
context 'when bulk_import_details_page feature flag is disabled' do
before do
stub_feature_flags(bulk_import_details_page: false)
request
end
it 'responds with a 404' do
expect(response).to have_gitlab_http_status(:not_found)
end
expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template(:details)
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Projects::Packages::InfrastructureRegistryController do
RSpec.describe Projects::Packages::InfrastructureRegistryController, feature_category: :package_registry do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :private) }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Projects::Packages::PackagesController do
RSpec.describe Projects::Packages::PackagesController, feature_category: :package_registry do
let_it_be(:project) { create(:project, :public) }
let(:page) { :index }

View File

@ -88,7 +88,6 @@ describe('Group import status component', () => {
id: 2,
entityId: 11,
hasFailures: true,
showDetailsLink: true,
status: STATUSES.FINISHED,
},
});

View File

@ -1,13 +1,13 @@
import { GlEmptyState, GlLoadingIcon, GlTableLite } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { getParameterValues } from '~/lib/utils/url_utility';
import BulkImportsHistoryApp from '~/pages/import/bulk_imports/history/components/bulk_imports_history_app.vue';
import ImportStatus from '~/import_entities/import_groups/components/import_status.vue';
import PaginationBar from '~/vue_shared/components/pagination_bar/pagination_bar.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
@ -39,6 +39,7 @@ describe('BulkImportsHistoryApp', () => {
destination_slug: 'top-level-group-12',
destination_namespace: 'h5bp',
created_at: '2021-07-08T10:03:44.743Z',
has_failures: false,
failures: [],
},
{
@ -56,6 +57,7 @@ describe('BulkImportsHistoryApp', () => {
project_id: null,
created_at: '2021-07-13T12:52:26.664Z',
updated_at: '2021-07-13T13:34:49.403Z',
has_failures: true,
failures: [
{
pipeline_class: 'BulkImports::Groups::Pipelines::GroupPipeline',
@ -72,15 +74,19 @@ describe('BulkImportsHistoryApp', () => {
let mock;
const mockRealtimeChangesPath = '/import/realtime_changes.json';
function createComponent({ shallow = true } = {}) {
function createComponent({ shallow = true, provide } = {}) {
const mountFn = shallow ? shallowMount : mount;
wrapper = mountFn(BulkImportsHistoryApp, {
provide: { realtimeChangesPath: mockRealtimeChangesPath },
provide: {
realtimeChangesPath: mockRealtimeChangesPath,
...provide,
},
});
}
const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const findPaginationBar = () => wrapper.findComponent(PaginationBar);
const findImportStatusAt = (index) => wrapper.findAllComponents(ImportStatus).at(index);
beforeEach(() => {
gon.api_version = 'v4';
@ -201,77 +207,59 @@ describe('BulkImportsHistoryApp', () => {
expect(findLocalStorageSync().props('value')).toBe(NEW_PAGE_SIZE);
});
it('renders link to destination_full_path for destination group', async () => {
createComponent({ shallow: false });
await waitForPromises();
expect(wrapper.find('tbody tr a').attributes().href).toBe(
`/${DUMMY_RESPONSE[0].destination_full_path}`,
);
});
it('renders destination as text when destination_full_path is not defined', async () => {
const RESPONSE = [{ ...DUMMY_RESPONSE[0], destination_full_path: null }];
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS);
createComponent({ shallow: false });
await waitForPromises();
expect(wrapper.find('tbody tr a').exists()).toBe(false);
expect(wrapper.find('tbody tr span').text()).toBe(
`${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_slug}/`,
);
});
it('adds slash to group urls', async () => {
createComponent({ shallow: false });
await waitForPromises();
expect(wrapper.find('tbody tr a').text()).toBe(`${DUMMY_RESPONSE[0].destination_full_path}/`);
});
it('does not prefixes project urls with slash', async () => {
createComponent({ shallow: false });
await waitForPromises();
expect(wrapper.findAll('tbody tr a').at(1).text()).toBe(
DUMMY_RESPONSE[1].destination_full_path,
);
});
describe('details button', () => {
beforeEach(() => {
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, DUMMY_RESPONSE, DEFAULT_HEADERS);
describe('table rendering', () => {
beforeEach(async () => {
createComponent({ shallow: false });
return waitForPromises();
await waitForPromises();
});
it('renders details button if relevant item has failures', () => {
expect(
extendedWrapper(wrapper.find('tbody').findAll('tr').at(1)).findByText('Details').exists(),
).toBe(true);
it('renders link to destination_full_path for destination group', () => {
expect(wrapper.find('tbody tr a').attributes().href).toBe(
`/${DUMMY_RESPONSE[0].destination_full_path}`,
);
});
it('does not render details button if relevant item has no failures', () => {
expect(
extendedWrapper(wrapper.find('tbody').findAll('tr').at(0)).findByText('Details').exists(),
).toBe(false);
it('renders destination as text when destination_full_path is not defined', async () => {
const RESPONSE = [{ ...DUMMY_RESPONSE[0], destination_full_path: null }];
mock.onGet(BULK_IMPORTS_API_URL).reply(HTTP_STATUS_OK, RESPONSE, DEFAULT_HEADERS);
createComponent({ shallow: false });
await waitForPromises();
expect(wrapper.find('tbody tr a').exists()).toBe(false);
expect(wrapper.find('tbody tr span').text()).toBe(
`${DUMMY_RESPONSE[0].destination_namespace}/${DUMMY_RESPONSE[0].destination_slug}/`,
);
});
it('expands details when details button is clicked', async () => {
const ORIGINAL_ROW_INDEX = 1;
await extendedWrapper(wrapper.find('tbody').findAll('tr').at(ORIGINAL_ROW_INDEX))
.findByText('Details')
.trigger('click');
it('adds slash to group urls', () => {
expect(wrapper.find('tbody tr a').text()).toBe(`${DUMMY_RESPONSE[0].destination_full_path}/`);
});
const detailsRowContent = wrapper
.find('tbody')
.findAll('tr')
.at(ORIGINAL_ROW_INDEX + 1)
.find('pre');
it('does not prefix project urls with slash', () => {
expect(wrapper.findAll('tbody tr a').at(1).text()).toBe(
DUMMY_RESPONSE[1].destination_full_path,
);
});
expect(detailsRowContent.exists()).toBe(true);
expect(JSON.parse(detailsRowContent.text())).toStrictEqual(DUMMY_RESPONSE[1].failures);
it('renders finished import status', () => {
expect(findImportStatusAt(0).text()).toBe('Complete');
});
it('renders failed import status with details link', async () => {
createComponent({
shallow: false,
provide: {
detailsPath: '/mock-details',
},
});
await waitForPromises();
const failedImportStatus = findImportStatusAt(1);
const failedImportStatusLink = failedImportStatus.find('a');
expect(failedImportStatus.text()).toContain('Failed');
expect(failedImportStatusLink.text()).toBe('See failures');
expect(failedImportStatusLink.attributes('href')).toContain('/mock-details');
});
});

View File

@ -17,6 +17,9 @@ describe('performance bar app', () => {
statsUrl: 'https://log.gprd.gitlab.net/app/dashboards#/view/',
peekUrl: '/-/peek/results',
},
stubs: {
GlEmoji: { template: '<div/>' },
},
});
};

View File

@ -1,5 +1,5 @@
import Vue from 'vue';
import { shallowMount } from '@vue/test-utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RequestWarning from '~/performance_bar/components/request_warning.vue';
Vue.config.ignoredElements = ['gl-emoji'];
@ -8,9 +8,20 @@ describe('request warning', () => {
let wrapper;
const htmlId = 'request-123';
const createComponent = ({ propsData = {} } = {}) => {
wrapper = shallowMountExtended(RequestWarning, {
propsData,
stubs: {
GlEmoji: { template: `<div id="${htmlId}" />` },
},
});
};
const findEmoji = () => wrapper.findByTestId('warning');
describe('when the request has warnings', () => {
beforeEach(() => {
wrapper = shallowMount(RequestWarning, {
createComponent({
propsData: {
htmlId,
warnings: ['gitaly calls: 30 over 10', 'gitaly duration: 1500 over 1000'],
@ -19,14 +30,14 @@ describe('request warning', () => {
});
it('adds a warning emoji with the correct ID', () => {
expect(wrapper.find('span gl-emoji[id]').attributes('id')).toEqual(htmlId);
expect(wrapper.find('span gl-emoji[id]').element.dataset.name).toEqual('warning');
expect(findEmoji().attributes('id')).toEqual(htmlId);
expect(findEmoji().element.dataset.name).toEqual('warning');
});
});
describe('when the request does not have warnings', () => {
beforeEach(() => {
wrapper = shallowMount(RequestWarning, {
createComponent({
propsData: {
htmlId,
warnings: [],
@ -35,7 +46,7 @@ describe('request warning', () => {
});
it('does nothing', () => {
expect(wrapper.html()).toBe('');
expect(findEmoji().exists()).toBe(false);
});
});
});

View File

@ -31,6 +31,7 @@ describe('CreateMenu component', () => {
stubs: {
InviteMembersTrigger,
GlDisclosureDropdown,
GlEmoji: { template: '<div/>' },
},
directives: {
GlTooltip: createMockDirective('gl-tooltip'),

View File

@ -103,6 +103,9 @@ describe('WorkItemAssignees component', () => {
},
attachTo: document.body,
apolloProvider,
stubs: {
GlEmoji: { template: '<div/>' },
},
});
};