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

Automatic merge of gitlab-org/gitlab master

parents f9557174 4fa2e258
No related branches found
No related tags found
No related merge requests found
Showing
with 180 additions and 102 deletions
......@@ -89,7 +89,7 @@ export default {
<div class="blocks-container">
<div class="gl-py-5 gl-display-flex gl-align-items-center">
<tooltip-on-truncate :title="job.name" truncate-target="child"
><h4 class="my-0 mr-2 gl-text-truncate">
><h4 class="gl-my-0 gl-mr-3 gl-text-truncate">
{{ job.name }}
</h4>
</tooltip-on-truncate>
......
......@@ -29,7 +29,7 @@ def execute
private
attr_reader :token, :require_running, :raise_on_missing
attr_reader :token
def find_job_by_token
::Ci::Build.find_by_token(token)
......
......@@ -14,8 +14,6 @@ class Build < Ci::Processable
extend ::Gitlab::Utils::Override
BuildArchivedError = Class.new(StandardError)
belongs_to :project, inverse_of: :builds
belongs_to :runner
belongs_to :trigger_request
......@@ -172,7 +170,6 @@ def persisted_environment=(environment)
end
scope :with_artifacts_not_expired, -> { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.current) }
scope :with_expired_artifacts, -> { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.current) }
scope :with_pipeline_locked_artifacts, -> { joins(:pipeline).where('pipeline.locked': Ci::Pipeline.lockeds[:artifacts_locked]) }
scope :last_month, -> { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, -> { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) }
......@@ -189,11 +186,6 @@ def persisted_environment=(environment)
scope :queued_before, ->(time) { where(arel_table[:queued_at].lt(time)) }
scope :preload_project_and_pipeline_project, -> do
preload(Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE,
pipeline: Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE)
end
scope :with_coverage, -> { where.not(coverage: nil) }
scope :without_coverage, -> { where(coverage: nil) }
scope :with_coverage_regex, -> { where.not(coverage_regex: nil) }
......@@ -217,10 +209,6 @@ def model_name
ActiveModel::Name.new(self, nil, 'job')
end
def first_pending
pending.unstarted.order('created_at ASC').first
end
def with_preloads
preload(:job_artifacts_archive, :job_artifacts, :tags, project: [:namespace])
end
......@@ -556,10 +544,6 @@ def environment_deployment_tier
self.options.dig(:environment, :deployment_tier) if self.options
end
def outdated_deployment?
success? && !deployment.try(:last?)
end
def triggered_by?(current_user)
user == current_user
end
......
......@@ -21,14 +21,15 @@ Get project-level DORA metrics.
GET /projects/:id/dora/metrics
```
| Attribute | Type | Required | Description |
|-------------- |-------- |----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`.|
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
| Attribute | Type | Required | Description |
|----------------------|------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
......@@ -61,14 +62,15 @@ Get group-level DORA metrics.
GET /groups/:id/dora/metrics
```
| Attribute | Type | Required | Description |
|-------------- |-------- |----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
| Attribute | Type | Required | Description |
|---------------------|------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
......
......@@ -10969,7 +10969,8 @@ Returns [`[DoraMetric!]`](#dorametric).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dorametricsenddate"></a>`endDate` | [`Date`](#date) | Date range to end at. Default is the current date. |
| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Defaults to `PRODUCTION`. |
| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Planned for deprecation: please update to `environment_tiers` param. |
| <a id="dorametricsenvironmenttiers"></a>`environmentTiers` | [`[DeploymentTier!]`](#deploymenttier) | Deployment tiers of the environments to return. Defaults to [`PRODUCTION`]. |
| <a id="dorametricsinterval"></a>`interval` | [`DoraMetricBucketingInterval`](#dorametricbucketinginterval) | How the metric should be aggregrated. Defaults to `DAILY`. In the case of `ALL`, the `date` field in the response will be `null`. |
| <a id="dorametricsmetric"></a>`metric` | [`DoraMetricType!`](#dorametrictype) | Type of metric to return. |
| <a id="dorametricsstartdate"></a>`startDate` | [`Date`](#date) | Date range to start from. Default is 3 months ago. |
......@@ -63,18 +63,19 @@ possible, we encourage you to use all of our security scanning tools:
The following table summarizes which types of dependencies each scanning tool can detect:
| Feature | Dependency Scanning | Container Scanning |
| ----------------------------------------------------------- | ------------------- | ------------------ |
| Identify the manifest, lock file, or static file that introduced the dependency | **{check-circle}** | **{dotted-circle}** |
| Development dependencies | **{check-circle}** | **{dotted-circle}** |
| Dependencies in a lock file committed to your repository | **{check-circle}** | **{check-circle}** <sup>1</sup> |
| Binaries built by Go | **{dotted-circle}** | **{check-circle}** <sup>2</sup> |
| Dynamically-linked language-specific dependencies installed by the Operating System | **{dotted-circle}** | **{check-circle}** |
| Operating system dependencies | **{dotted-circle}** | **{check-circle}** |
| Language-specific dependencies installed on the operating system (not built by your project) | **{dotted-circle}** | **{check-circle}** |
| Feature | Dependency Scanning | Container Scanning |
| ----------------------------------------------------------- | ------------------- | ------------------- |
| Identify the manifest, lock file, or static file that introduced the dependency | **{check-circle}** | **{dotted-circle}** |
| Development dependencies | **{check-circle}** | **{dotted-circle}** |
| Dependencies in a lock file committed to your repository | **{check-circle}** | **{check-circle}** <sup>1</sup> |
| Binaries built by Go | **{dotted-circle}** | **{check-circle}** <sup>2</sup> <sup>3</sup> |
| Dynamically-linked language-specific dependencies installed by the Operating System | **{dotted-circle}** | **{check-circle}** <sup>3</sup> |
| Operating system dependencies | **{dotted-circle}** | **{check-circle}** |
| Language-specific dependencies installed on the operating system (not built by your project) | **{dotted-circle}** | **{check-circle}** |
1. Lock file must be present in the image to be detected.
1. Binary file must be present in the image to be detected.
1. Only when using Trivy
## Requirements
......
......@@ -99,45 +99,54 @@ Hello from MyPyPiPackage
After you create a project, you can create a package.
1. In your terminal, go to the `MyPyPiPackage` directory.
1. Create a `setup.py` file:
1. Create a `pyproject.toml` file:
```shell
touch setup.py
touch pyproject.toml
```
This file contains all the information about the package. For more information
about this file, see [creating setup.py](https://packaging.python.org/tutorials/packaging-projects/#creating-setup-py).
about this file, see [creating `pyproject.toml`](https://packaging.python.org/en/latest/tutorials/packaging-projects/#creating-pyproject-toml).
Because GitLab identifies packages based on
[Python normalized names (PEP-503)](https://www.python.org/dev/peps/pep-0503/#normalized-names),
ensure your package name meets these requirements. See the [installation section](#authenticate-with-a-ci-job-token)
for details.
1. Open the `setup.py` file, and then add basic information:
```python
import setuptools
setuptools.setup(
name="mypypipackage",
version="0.0.1",
author="Example Author",
author_email="author@example.com",
description="A small example package",
packages=setuptools.find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires='>=3.6',
)
1. Open the `pyproject.toml` file, and then add basic information:
```toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "mypypipackage"
version = "0.0.1"
authors = [
{ name="Example Author", email="author@example.com" },
]
description = "A small example package"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"Operating System :: OS Independent",
]
[tool.setuptools.packages]
find = {}
```
1. Save the file.
1. Execute the setup:
1. Install the package build library:
```shell
pip install build
```
1. Build the package:
```shell
python3 setup.py sdist bdist_wheel
python -m build
```
The output should be visible in a newly-created `dist` folder:
......@@ -218,8 +227,8 @@ image: python:latest
run:
script:
- pip install twine
- python setup.py sdist bdist_wheel
- pip install build twine
- python -m build
- TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
```
......
......@@ -27,9 +27,19 @@ class DoraMetricsResolver < BaseResolver
argument :environment_tier, Types::DeploymentTierEnum,
required: false,
description: 'Deployment tier of the environments to return. Defaults to `PRODUCTION`.'
description: 'Deployment tier of the environments to return. Planned for deprecation: please update to `environment_tiers` param.'
argument :environment_tiers, [Types::DeploymentTierEnum],
required: false,
description: 'Deployment tiers of the environments to return. Defaults to [`PRODUCTION`].'
def resolve(params)
# Backwards compatibility until %16.0
if params[:environment_tier]
params[:environment_tiers] ||= []
params[:environment_tiers] |= [params[:environment_tier]]
end
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: params)
.execute
......
......@@ -4,7 +4,7 @@ module Dora
class AggregateMetricsService < ::BaseContainerService
MAX_RANGE = Gitlab::Analytics::CycleAnalytics::RequestParams::MAX_RANGE_DAYS # same range as Value Stream Analytics
DEFAULT_ENVIRONMENT_TIER = 'production'
DEFAULT_ENVIRONMENT_TIERS = %w[production].freeze
DEFAULT_INTERVAL = Dora::DailyMetrics::INTERVAL_DAILY
def execute
......@@ -76,8 +76,8 @@ def validate
:bad_request)
end
unless Environment.tiers[environment_tier]
return error(_("The environment tier must be one of %{environment_tiers}.") % { environment_tiers: Environment.tiers.keys.join(',') },
unless environment_tiers.all? { |tier| Environment.tiers[tier] }
return error(_("The environment tiers must be from %{environment_tiers}.") % { environment_tiers: Environment.tiers.keys.join(', ') },
:bad_request)
end
......@@ -85,7 +85,7 @@ def validate
end
def environments
Environment.for_project(target_projects).for_tier(environment_tier)
Environment.for_project(target_projects).for_tier(environment_tiers)
end
def target_projects
......@@ -121,8 +121,8 @@ def end_date
params[:end_date] || Time.current.to_date
end
def environment_tier
params[:environment_tier] || DEFAULT_ENVIRONMENT_TIER
def environment_tiers
params[:environment_tiers] || DEFAULT_ENVIRONMENT_TIERS
end
def interval
......
......@@ -12,12 +12,22 @@ class Metrics < ::API::Base
optional :start_date, type: Date, desc: 'Date range to start from.'
optional :end_date, type: Date, desc: 'Date range to end at.'
optional :interval, type: String, desc: "The bucketing interval."
optional :environment_tier, type: String, desc: "The tier of the environment."
optional :environment_tier,
type: String,
desc: "The tier of the environment. Planned for deprecation: please use `environment_tiers` param."
optional :environment_tiers, type: Array[String], desc: "Filter by environment tiers."
end
def fetch!(container)
# Backwards compatibility until %16.0
params = declared_params(include_missing: false)
if params[:environment_tier]
params[:environment_tiers] ||= []
params[:environment_tiers] |= [params[:environment_tier]]
end
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: declared_params(include_missing: false))
.new(container: container, current_user: current_user, params: params)
.execute
if result[:status] == :success
......
......@@ -33,7 +33,7 @@ def dora_aggregate_metrics_params
start_date: options[:from].to_date,
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: 'deployment_frequency'
}
end
......
......@@ -41,7 +41,7 @@ def dora_metric
start_date: from,
end_date: to,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: metric_key
}
......
......@@ -41,7 +41,7 @@ def dora_aggregate_metrics_params
start_date: options[:from].to_date,
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: 'deployment_frequency'
}
......
......@@ -116,7 +116,20 @@ def log_to_database(events)
def log_to_file(events)
file_logger = ::Gitlab::AuditJsonLogger.build
events.each { |event| file_logger.info(event.as_json) }
events.each { |event| file_logger.info(log_payload(event)) }
end
private
def log_payload(event)
payload = event.as_json
details = formatted_details(event.details)
payload["details"] = details
payload.merge!(details).as_json
end
def formatted_details(details)
details.merge(details.slice(:from, :to).transform_values(&:to_s))
end
end
end
......
......@@ -18,8 +18,6 @@
context 'default state' do
before do
visit(general_admin_application_settings_path)
add_license_area = find('#js-add-license-toggle')
add_license_area.click_button('Expand') if add_license_area.has_button?('Expand')
end
it 'has the correct headline' do
......@@ -50,8 +48,6 @@
context "uploading license" do
before do
visit(general_admin_application_settings_path)
add_license_area = find('#js-add-license-toggle')
add_license_area.click_button('Expand') if add_license_area.has_button?('Expand')
File.write(path, new_license.export)
end
......
......@@ -210,14 +210,31 @@
end
end
context 'with environment_tier: "staging"' do
context 'with multiple environment_tiers' do
let(:args) { { metric: 'deployment_frequency', environment_tiers: %w[production staging] } }
it 'returns metrics for all environments combined' do
expect(resolve_metrics).to eq([
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 27 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 },
{ 'date' => '2021-04-04', 'value' => 14 },
{ 'date' => '2021-04-05', 'value' => 13 },
{ 'date' => '2021-04-06', 'value' => 12 },
{ 'date' => '2021-04-07', 'value' => nil }
])
end
end
context 'backwards compatibility for environment_tier' do
let(:args) { { metric: 'deployment_frequency', environment_tier: 'staging' } }
it 'returns metrics for the staging environment' do
expect(resolve_metrics).to eq([
{ 'date' => '2021-04-01', 'value' => 10 },
{ 'date' => '2021-04-02', 'value' => nil }
])
{ 'date' => '2021-04-01', 'value' => 10 },
{ 'date' => '2021-04-02', 'value' => nil }
])
end
end
end
......
......@@ -155,7 +155,7 @@
end
context 'when overriding the additional_details' do
additional_details = { action: :custom }
additional_details = { action: :custom, from: false, to: true }
let(:context) do
{ name: name,
author: author,
......@@ -181,7 +181,10 @@
expect(logger).to have_received(:info).exactly(2).times.with(
hash_including(
'details' => hash_including('action' => 'custom')
'details' => hash_including('action' => 'custom', 'from' => 'false', 'to' => 'true'),
'action' => 'custom',
'from' => 'false',
'to' => 'true'
)
)
end
......@@ -283,7 +286,8 @@
'author_name' => author.name,
'entity_id' => scope.id,
'entity_type' => scope.class.name,
'details' => kind_of(Hash)
'details' => kind_of(Hash),
'custom_message' => 'Project has been deleted'
)
)
end
......
......@@ -6,6 +6,7 @@
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:production) { create(:environment, :production, project: project) }
let_it_be(:staging) { create(:environment, :staging, project: project) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:guest) { create(:user) }
......@@ -35,6 +36,13 @@
incidents_count: 8,
environment: production,
date: '2021-01-02')
create(:dora_daily_metrics,
deployment_frequency: 100,
lead_time_for_changes_in_seconds: 200,
time_to_restore_service_in_seconds: 300,
incidents_count: 400,
environment: staging,
date: '2021-01-02')
end
before do
......@@ -60,6 +68,29 @@
end
end
context 'with multiple metrics' do
let(:params) { { metric: 'deployment_frequency', environment_tiers: %w[production staging] } }
it 'returns combined data' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_array([{ 'date' => '2021-01-01', 'value' => 1 },
{ 'date' => '2021-01-02', 'value' => 102 }])
end
end
context 'backwards compatibility for environment_tier' do
let(:params) { { metric: 'deployment_frequency', environment_tier: 'staging' } }
it 'returns combined data' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_array([{ 'date' => '2021-01-02', 'value' => 100 }])
end
end
context 'when user is guest' do
let(:user) { guest }
let(:params) { { metric: :deployment_frequency } }
......
......@@ -68,11 +68,11 @@
end
end
context 'when environment tier is invalid' do
let(:extra_params) { { environment_tier: 'unknown' } }
context 'when environment tiers are invalid' do
let(:extra_params) { { environment_tiers: ['unknown'] } }
it_behaves_like 'request failure' do
let(:message) { "The environment tier must be one of #{Environment.tiers.keys.join(',')}." }
let(:message) { "The environment tiers must be from #{Environment.tiers.keys.join(', ')}." }
let(:http_status) { :bad_request }
end
end
......@@ -136,8 +136,8 @@
end
end
context 'when environment tier is changed' do
let(:extra_params) { { environment_tier: 'staging' } }
context 'when environment tiers are changed' do
let(:extra_params) { { environment_tiers: ['staging'] } }
it 'returns the aggregated data' do
expect(subject[:status]).to eq(:success)
......@@ -232,7 +232,7 @@
context 'runs the service without authorization' do
subject { service.execute_without_authorization }
context 'when passign a non-ultimate group' do
context 'when passing a non-ultimate group' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:production) { create(:environment, :production, project: project) }
......@@ -240,7 +240,7 @@
let(:container) { group }
let(:user) { maintainer }
let(:params) { { environment_tier: 'production', interval: 'all', metric: 'deployment_frequency' } }
let(:params) { { environment_tiers: ['production'], interval: 'all', metric: 'deployment_frequency' } }
before do
group.add_maintainer(maintainer)
......
......@@ -38296,7 +38296,7 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
 
msgid "The environment tier must be one of %{environment_tiers}."
msgid "The environment tiers must be from %{environment_tiers}."
msgstr ""
 
msgid "The errors we encountered were:"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment