Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-01 12:10:18 +00:00
parent 58c42f2f45
commit 3a5bee7879
26 changed files with 327 additions and 78 deletions

View File

@ -246,7 +246,7 @@ gem 're2', '~> 1.6.0'
gem 'version_sorter', '~> 2.2.4' gem 'version_sorter', '~> 2.2.4'
# Export Ruby Regex to Javascript # Export Ruby Regex to Javascript
gem 'js_regex', '~> 3.7' gem 'js_regex', '~> 3.8'
# User agent parsing # User agent parsing
gem 'device_detector' gem 'device_detector'

View File

@ -286,7 +286,7 @@
{"name":"jaro_winkler","version":"1.5.4","platform":"ruby","checksum":"50c3e83c5a9e8769c1cf5b73c8b51bb6eebbf8852a0ee53bf6ad6e4dc63414f9"}, {"name":"jaro_winkler","version":"1.5.4","platform":"ruby","checksum":"50c3e83c5a9e8769c1cf5b73c8b51bb6eebbf8852a0ee53bf6ad6e4dc63414f9"},
{"name":"jira-ruby","version":"2.1.4","platform":"ruby","checksum":"4267c095cac8323b9eef3ba866eb28bb1388b7623a5abb60c1e7caf12d4adb9e"}, {"name":"jira-ruby","version":"2.1.4","platform":"ruby","checksum":"4267c095cac8323b9eef3ba866eb28bb1388b7623a5abb60c1e7caf12d4adb9e"},
{"name":"jmespath","version":"1.6.1","platform":"ruby","checksum":"40ca83f4141bdd1e503db5485de68b84237183d84cf7a159fbeebcc6005adbd6"}, {"name":"jmespath","version":"1.6.1","platform":"ruby","checksum":"40ca83f4141bdd1e503db5485de68b84237183d84cf7a159fbeebcc6005adbd6"},
{"name":"js_regex","version":"3.7.0","platform":"ruby","checksum":"b13fac68c4416d1a5f21c3bab8a71b4530f424b7c4ff9f46d8e62b895dc05975"}, {"name":"js_regex","version":"3.8.0","platform":"ruby","checksum":"7934bcdd5a0e6d5af4a520288fd4684a02a472ae55831d9178ccaf82356344b5"},
{"name":"json","version":"2.5.1","platform":"java","checksum":"be284a0c4a9d0373e81b0d5dfe71ed5b18d0479f05970e60a77be89a2978ce6c"}, {"name":"json","version":"2.5.1","platform":"java","checksum":"be284a0c4a9d0373e81b0d5dfe71ed5b18d0479f05970e60a77be89a2978ce6c"},
{"name":"json","version":"2.5.1","platform":"ruby","checksum":"918d8c41dacb7cfdbe0c7bbd6014a5372f0cf1c454ca150e9f4010fe80cc3153"}, {"name":"json","version":"2.5.1","platform":"ruby","checksum":"918d8c41dacb7cfdbe0c7bbd6014a5372f0cf1c454ca150e9f4010fe80cc3153"},
{"name":"json-jwt","version":"1.15.3","platform":"ruby","checksum":"66db4f14e538a774c15502a5b5b26b1f3e7585481bbb96df490aa74b5c2d6110"}, {"name":"json-jwt","version":"1.15.3","platform":"ruby","checksum":"66db4f14e538a774c15502a5b5b26b1f3e7585481bbb96df490aa74b5c2d6110"},

View File

@ -758,9 +758,9 @@ GEM
multipart-post multipart-post
oauth (~> 0.5, >= 0.5.0) oauth (~> 0.5, >= 0.5.0)
jmespath (1.6.1) jmespath (1.6.1)
js_regex (3.7.0) js_regex (3.8.0)
character_set (~> 1.4) character_set (~> 1.4)
regexp_parser (~> 2.1) regexp_parser (~> 2.5)
regexp_property_values (~> 1.0) regexp_property_values (~> 1.0)
json (2.5.1) json (2.5.1)
json-jwt (1.15.3) json-jwt (1.15.3)
@ -1673,7 +1673,7 @@ DEPENDENCIES
ipaddress (~> 0.8.3) ipaddress (~> 0.8.3)
ipynbdiff! ipynbdiff!
jira-ruby (~> 2.1.4) jira-ruby (~> 2.1.4)
js_regex (~> 3.7) js_regex (~> 3.8)
json (~> 2.5.1) json (~> 2.5.1)
json_schemer (~> 0.2.18) json_schemer (~> 0.2.18)
jwt (~> 2.1.0) jwt (~> 2.1.0)

View File

@ -85,10 +85,12 @@ export const billingPlanNames = {
}; };
const INTEGRATION_TYPE_SLACK = 'slack'; const INTEGRATION_TYPE_SLACK = 'slack';
const INTEGRATION_TYPE_SLACK_APPLICATION = 'gitlab_slack_application';
const INTEGRATION_TYPE_MATTERMOST = 'mattermost'; const INTEGRATION_TYPE_MATTERMOST = 'mattermost';
export const placeholderForType = { export const placeholderForType = {
[INTEGRATION_TYPE_SLACK]: __('#general, #development'), [INTEGRATION_TYPE_SLACK]: __('#general, #development'),
[INTEGRATION_TYPE_SLACK_APPLICATION]: __('#general, #development'),
[INTEGRATION_TYPE_MATTERMOST]: __('my-channel'), [INTEGRATION_TYPE_MATTERMOST]: __('my-channel'),
}; };

View File

@ -23,7 +23,7 @@ export default {
}, },
computed: { computed: {
...mapGetters(['isInheriting']), ...mapGetters(['isInheriting']),
placeholder() { defaultPlaceholder() {
return placeholderForType[this.type]; return placeholderForType[this.type];
}, },
}, },
@ -55,7 +55,7 @@ export default {
v-if="event.field" v-if="event.field"
v-model="event.field.value" v-model="event.field.value"
:name="fieldName(event.field.name)" :name="fieldName(event.field.name)"
:placeholder="placeholder" :placeholder="event.field.placeholder || defaultPlaceholder"
:readonly="isInheriting" :readonly="isInheriting"
/> />
</gl-form-group> </gl-form-group>

View File

@ -12,6 +12,7 @@
s_('ProjectSettings|Skipped pipelines are considered successful'), s_('ProjectSettings|Skipped pipelines are considered successful'),
help_text: s_('ProjectSettings|Introduces the risk of merging changes that do not pass the pipeline.'), help_text: s_('ProjectSettings|Introduces the risk of merging changes that do not pass the pipeline.'),
checkbox_options: { class: 'gl-pl-6' } checkbox_options: { class: 'gl-pl-6' }
= render_if_exists 'projects/merge_request_merge_checks_status_checks', form: form, project: @project
= form.gitlab_ui_checkbox_component :only_allow_merge_if_all_discussions_are_resolved, = form.gitlab_ui_checkbox_component :only_allow_merge_if_all_discussions_are_resolved,
s_('ProjectSettings|All threads must be resolved'), s_('ProjectSettings|All threads must be resolved'),
checkbox_options: { data: { qa_selector: 'allow_merge_if_all_discussions_are_resolved_checkbox' } } checkbox_options: { data: { qa_selector: 'allow_merge_if_all_discussions_are_resolved_checkbox' } }

View File

@ -1,7 +1,7 @@
--- ---
name: integration_slack_app_notifications name: integration_slack_app_notifications
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98663 introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98663
rollout_issue_url: rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/381012
milestone: '15.5' milestone: '15.5'
type: development type: development
group: group::integrations group: group::integrations

View File

@ -1,15 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
# Currently we register validator only for `dev` or `test` environment # Currently we register validator only for `dev` or `test` environment
Gitlab::Database::QueryAnalyzer.instance.hook! Gitlab::Database::QueryAnalyzer.instance.tap do |query_analyzer|
Gitlab::Database::QueryAnalyzer.instance.all_analyzers.append(::Gitlab::Database::QueryAnalyzers::GitlabSchemasMetrics) query_analyzer.hook!
Gitlab::Database::QueryAnalyzer.instance.all_analyzers.append(
::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification
)
if Gitlab.dev_or_test_env? query_analyzer.all_analyzers.tap do |analyzers|
query_analyzer = ::Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection analyzers.append(::Gitlab::Database::QueryAnalyzers::GitlabSchemasMetrics)
Gitlab::Database::QueryAnalyzer.instance.all_analyzers.append(query_analyzer) analyzers.append(::Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification)
if Gitlab.dev_or_test_env?
analyzers.append(::Gitlab::Database::QueryAnalyzers::GitlabSchemasValidateConnection)
analyzers.append(::Gitlab::Database::QueryAnalyzers::QueryRecorder)
end
end
end end
Gitlab::Application.configure do |config| Gitlab::Application.configure do |config|

View File

@ -26,6 +26,10 @@ metadata:
description: Operations related to deploy tokens description: Operations related to deploy tokens
- name: deployments - name: deployments
description: Operations related to deployments description: Operations related to deployments
- name: environments
description: Operations related to environments
- name: feature_flags_user_lists
description: Operations related to accessing GitLab feature flag user lists
- name: features - name: features
description: Operations related to managing Flipper-based feature flags description: Operations related to managing Flipper-based feature flags
- name: freeze_periods - name: freeze_periods

View File

@ -20,7 +20,7 @@ GET /projects/:id/environments
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | no | Return the environment with this name. Mutually exclusive with `search` | | `name` | string | no | Return the environment with this name. Mutually exclusive with `search` |
| `search` | string | no | Return list of environments matching the search criteria. Mutually exclusive with `name` | | `search` | string | no | Return list of environments matching the search criteria. Mutually exclusive with `name` |
| `states` | string | no | List all environments that match a specific state. Accepted values: `available`, `stopping` or `stopped`. If no state value given, returns all environments. | | `states` | string | no | List all environments that match a specific state. Accepted values: `available`, `stopping`, or `stopped`. If no state value given, returns all environments. |
```shell ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/environments?name=review%2Ffix-foo" curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/environments?name=review%2Ffix-foo"
@ -279,7 +279,7 @@ Example response:
} }
``` ```
## Edit an existing environment ## Update an existing environment
Updates an existing environment's name and/or `external_url`. Updates an existing environment's name and/or `external_url`.

View File

@ -107,6 +107,33 @@ job:
- make build - make build
``` ```
#### Skip job if the branch is empty
Use [`rules:changes:compare_to`](../yaml/index.md#ruleschangescompare_to) to avoid
running a job when the branch is empty, which saves CI/CD resources. Compare the
branch to the default branch, and if the branch:
- Doesn't have changed files, the job doesn't run.
- Has changed files, the job runs.
For example, in a project with `main` as the default branch:
```yaml
job:
script:
- echo "This job only runs for branches that are not empty"
rules:
- if: $CI_COMMIT_BRANCH
changes:
compare_to: refs/heads/main
paths:
- '*'
```
The rule for this job compares all files and paths (`*`) in the current branch against
the default branch `main`. The rule matches and the job runs only when there are
changes to the files in the branch.
### Complex rules ### Complex rules
You can use all `rules` keywords, like `if`, `changes`, and `exists`, in the same You can use all `rules` keywords, like `if`, `changes`, and `exists`, in the same

View File

@ -1155,7 +1155,7 @@ that use the same cache key use the same cache, including in different pipelines
If not set, the default key is `default`. All jobs with the `cache` keyword but If not set, the default key is `default`. All jobs with the `cache` keyword but
no `cache:key` share the `default` cache. no `cache:key` share the `default` cache.
Must be used with `cache: path`, or nothing is cached. Must be used with `cache: paths`, or nothing is cached.
**Keyword type**: Job keyword. You can use it only as part of a job or in the **Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default` section](#default). [`default` section](#default).
@ -1325,7 +1325,7 @@ rspec:
Use `cache:when` to define when to save the cache, based on the status of the job. Use `cache:when` to define when to save the cache, based on the status of the job.
Must be used with `cache: path`, or nothing is cached. Must be used with `cache: paths`, or nothing is cached.
**Keyword type**: Job keyword. You can use it only as part of a job or in the **Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default` section](#default). [`default` section](#default).
@ -1365,7 +1365,7 @@ Use the `pull` policy when you have many jobs executing in parallel that use the
This policy speeds up job execution and reduces load on the cache server. You can This policy speeds up job execution and reduces load on the cache server. You can
use a job with the `push` policy to build the cache. use a job with the `push` policy to build the cache.
Must be used with `cache: path`, or nothing is cached. Must be used with `cache: paths`, or nothing is cached.
**Keyword type**: Job keyword. You can use it only as part of a job or in the **Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default` section](#default). [`default` section](#default).
@ -1773,7 +1773,7 @@ deploy:
**Additional details**: **Additional details**:
- Enviroments created from this job definition are assigned a [tier](../environments/index.md#deployment-tier-of-environments) based on this value. - Enviroments created from this job definition are assigned a [tier](../environments/index.md#deployment-tier-of-environments) based on this value.
- Existing environments don't have their tier updated if this value is added later. Existing enviroments must have their tier updated via the [Environments API](../../api/environments.md#edit-an-existing-environment). - Existing environments don't have their tier updated if this value is added later. Existing enviroments must have their tier updated via the [Environments API](../../api/environments.md#update-an-existing-environment).
**Related topics**: **Related topics**:

View File

@ -176,6 +176,8 @@ module API
mount ::API::DeployKeys mount ::API::DeployKeys
mount ::API::DeployTokens mount ::API::DeployTokens
mount ::API::Deployments mount ::API::Deployments
mount ::API::Environments
mount ::API::FeatureFlagsUserLists
mount ::API::Features mount ::API::Features
mount ::API::FreezePeriods mount ::API::FreezePeriods
mount ::API::Metadata mount ::API::Metadata
@ -225,13 +227,11 @@ module API
mount ::API::DebianProjectPackages mount ::API::DebianProjectPackages
mount ::API::DependencyProxy mount ::API::DependencyProxy
mount ::API::Discussions mount ::API::Discussions
mount ::API::Environments
mount ::API::ErrorTracking::ClientKeys mount ::API::ErrorTracking::ClientKeys
mount ::API::ErrorTracking::Collector mount ::API::ErrorTracking::Collector
mount ::API::ErrorTracking::ProjectSettings mount ::API::ErrorTracking::ProjectSettings
mount ::API::Events mount ::API::Events
mount ::API::FeatureFlags mount ::API::FeatureFlags
mount ::API::FeatureFlagsUserLists
mount ::API::Files mount ::API::Files
mount ::API::GenericPackages mount ::API::GenericPackages
mount ::API::Geo mount ::API::Geo

View File

@ -5,10 +5,10 @@ module API
class Environment < Entities::EnvironmentBasic class Environment < Entities::EnvironmentBasic
include RequestAwareEntity include RequestAwareEntity
expose :tier expose :tier, documentation: { type: 'string', example: 'development' }
expose :project, using: Entities::BasicProjectDetails expose :project, using: Entities::BasicProjectDetails
expose :last_deployment, using: Entities::Deployment, if: { last_deployment: true } expose :last_deployment, using: Entities::Deployment, if: { last_deployment: true }
expose :state expose :state, documentation: { type: 'string', example: 'available' }
end end
end end
end end

View File

@ -3,7 +3,12 @@
module API module API
module Entities module Entities
class EnvironmentBasic < Grape::Entity class EnvironmentBasic < Grape::Entity
expose :id, :name, :slug, :external_url, :created_at, :updated_at expose :id, documentation: { type: 'integer', example: 1 }
expose :name, documentation: { type: 'string', example: 'deploy' }
expose :slug, documentation: { type: 'string', example: 'deploy' }
expose :external_url, documentation: { type: 'string', example: 'https://deploy.gitlab.example.com' }
expose :created_at, documentation: { type: 'dateTime', example: '2019-05-25T18:55:13.252Z' }
expose :updated_at, documentation: { type: 'dateTime', example: '2019-05-25T18:55:13.252Z' }
end end
end end
end end

View File

@ -6,13 +6,13 @@ module API
class UserList < Grape::Entity class UserList < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
expose :id expose :id, documentation: { type: 'integer', example: 1 }
expose :iid expose :iid, documentation: { type: 'integer', example: 1 }
expose :project_id expose :project_id, documentation: { type: 'integer', example: 2 }
expose :created_at expose :created_at, documentation: { type: 'dateTime', example: '2020-02-04T08:13:10.507Z' }
expose :updated_at expose :updated_at, documentation: { type: 'dateTime', example: '2020-02-04T08:13:10.507Z' }
expose :name expose :name, documentation: { type: 'string', example: 'user_list' }
expose :user_xids expose :user_xids, documentation: { type: 'string', example: 'user1,user2' }
expose :path do |list| expose :path do |list|
project_feature_flags_user_list_path(list.project, list) project_feature_flags_user_list_path(list.project, list)

View File

@ -11,18 +11,27 @@ module API
urgency :low urgency :low
params do params do
requires :id, type: String, desc: 'The project ID' requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project owned by the authenticated user'
end end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all environments of the project' do desc 'List environments' do
detail 'This feature was introduced in GitLab 8.11.' detail 'Get all environments for a given project. This feature was introduced in GitLab 8.11.'
success Entities::Environment success Entities::Environment
is_array true
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[environments]
end end
params do params do
use :pagination use :pagination
optional :name, type: String, desc: 'Returns the environment with this name' optional :name, type: String, desc: 'Return the environment with this name. Mutually exclusive with search'
optional :search, type: String, desc: 'Returns list of environments matching the search criteria' optional :search, type: String, desc: 'Return list of environments matching the search criteria. Mutually exclusive with name'
optional :states, type: String, values: Environment.valid_states.map(&:to_s), desc: 'List all environments that match a specific state' optional :states,
type: String,
values: Environment.valid_states.map(&:to_s),
desc: 'List all environments that match a specific state. Accepted values: `available`, `stopping`, or `stopped`. If no state value given, returns all environments'
mutually_exclusive :name, :search, message: 'cannot be used together' mutually_exclusive :name, :search, message: 'cannot be used together'
end end
get ':id/environments' do get ':id/environments' do
@ -33,15 +42,21 @@ module API
present paginate(environments), with: Entities::Environment, current_user: current_user present paginate(environments), with: Entities::Environment, current_user: current_user
end end
desc 'Creates a new environment' do desc 'Create a new environment' do
detail 'This feature was introduced in GitLab 8.11.' detail 'Creates a new environment with the given name and `external_url`. It returns `201` if the environment was successfully created, `400` for wrong parameters. This feature was introduced in GitLab 8.11.'
success Entities::Environment success Entities::Environment
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 422, message: 'Unprocessable entity' }
]
tags %w[environments]
end end
params do params do
requires :name, type: String, desc: 'The name of the environment to be created' requires :name, type: String, desc: 'The name of the environment'
optional :external_url, type: String, desc: 'URL on which this deployment is viewable' optional :external_url, type: String, desc: 'Place to link to for this environment'
optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true } optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true }
optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the environment to be created' optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`'
end end
post ':id/environments' do post ':id/environments' do
authorize! :create_environment, user_project authorize! :create_environment, user_project
@ -55,17 +70,23 @@ module API
end end
end end
desc 'Updates an existing environment' do desc 'Update an existing environment' do
detail 'This feature was introduced in GitLab 8.11.' detail 'Updates an existing environment name and/or `external_url`. It returns `200` if the environment was successfully updated. In case of an error, a status code `400` is returned. This feature was introduced in GitLab 8.11.'
success Entities::Environment success Entities::Environment
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 422, message: 'Unprocessable entity' }
]
tags %w[environments]
end end
params do params do
requires :environment_id, type: Integer, desc: 'The environment ID' requires :environment_id, type: Integer, desc: 'The ID of the environment'
# TODO: disallow renaming via the API https://gitlab.com/gitlab-org/gitlab/-/issues/338897 # TODO: disallow renaming via the API https://gitlab.com/gitlab-org/gitlab/-/issues/338897
optional :name, type: String, desc: 'DEPRECATED: Renaming environment can lead to errors, this will be removed in 15.0' optional :name, type: String, desc: 'DEPRECATED: Renaming environment can lead to errors, this will be removed in 15.0'
optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable' optional :external_url, type: String, desc: 'The new URL on which this deployment is viewable'
optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true } optional :slug, absence: { message: "is automatically generated and cannot be changed" }, documentation: { hidden: true }
optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the environment to be created' optional :tier, type: String, values: Environment.tiers.keys, desc: 'The tier of the new environment. Allowed values are `production`, `staging`, `testing`, `development`, and `other`'
end end
put ':id/environments/:environment_id' do put ':id/environments/:environment_id' do
authorize! :update_environment, user_project authorize! :update_environment, user_project
@ -80,14 +101,21 @@ module API
end end
end end
desc "Delete multiple stopped review apps" do desc 'Delete multiple stopped review apps' do
detail "Remove multiple stopped review environments older than a specific age" detail 'It schedules for deletion multiple environments that have already been stopped and are in the review app folder. The actual deletion is performed after 1 week from the time of execution. By default, it only deletes environments 30 days or older. You can change this default using the `before` parameter.'
success Entities::EnvironmentBasic success Entities::EnvironmentBasic
failure [
{ code: 400, message: 'Bad request' },
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' },
{ code: 409, message: 'Conflict' }
]
tags %w[environments]
end end
params do params do
optional :before, type: Time, desc: "The timestamp before which environments can be deleted. Defaults to 30 days ago.", default: -> { 30.days.ago } optional :before, type: Time, desc: "The date before which environments can be deleted. Defaults to 30 days ago. Expected in ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)", default: -> { 30.days.ago }
optional :limit, type: Integer, desc: "Maximum number of environments to delete. Defaults to 100.", default: 100, values: 1..1000 optional :limit, type: Integer, desc: "Maximum number of environments to delete. Defaults to 100", default: 100, values: 1..1000
optional :dry_run, type: Boolean, desc: "If set, perform a dry run where no actual deletions will be performed. Defaults to true.", default: true optional :dry_run, type: Boolean, desc: "Defaults to true for safety reasons. It performs a dry run where no actual deletion will be performed. Set to false to actually delete the environment", default: true
end end
delete ":id/environments/review_apps" do delete ":id/environments/review_apps" do
authorize! :read_environment, user_project authorize! :read_environment, user_project
@ -107,12 +135,17 @@ module API
end end
end end
desc 'Deletes an existing environment' do desc 'Delete an environment' do
detail 'This feature was introduced in GitLab 8.11.' detail 'It returns 204 if the environment was successfully deleted, and 404 if the environment does not exist. This feature was introduced in GitLab 8.11.'
success Entities::Environment success Entities::Environment
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[environments]
end end
params do params do
requires :environment_id, type: Integer, desc: 'The environment ID' requires :environment_id, type: Integer, desc: 'The ID of the environment'
end end
delete ':id/environments/:environment_id' do delete ':id/environments/:environment_id' do
authorize! :read_environment, user_project authorize! :read_environment, user_project
@ -123,12 +156,18 @@ module API
destroy_conditionally!(environment) destroy_conditionally!(environment)
end end
desc 'Stops an existing environment' do desc 'Stop an environment' do
detail 'It returns 200 if the environment was successfully stopped, and 404 if the environment does not exist.'
success Entities::Environment success Entities::Environment
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[environments]
end end
params do params do
requires :environment_id, type: Integer, desc: 'The environment ID' requires :environment_id, type: Integer, desc: 'The ID of the environment'
optional :force, type: Boolean, default: false optional :force, type: Boolean, default: false, desc: 'Force environment to stop without executing `on_stop` actions'
end end
post ':id/environments/:environment_id/stop' do post ':id/environments/:environment_id/stop' do
authorize! :read_environment, user_project authorize! :read_environment, user_project
@ -141,11 +180,16 @@ module API
present environment, with: Entities::Environment, current_user: current_user present environment, with: Entities::Environment, current_user: current_user
end end
desc 'Get a single environment' do desc 'Get a specific environment' do
success Entities::Environment success Entities::Environment
failure [
{ code: 403, message: 'Unauthenticated' },
{ code: 404, message: 'Not found' }
]
tags %w[environments]
end end
params do params do
requires :environment_id, type: Integer, desc: 'The environment ID' requires :environment_id, type: Integer, desc: 'The ID of the environment'
end end
get ':id/environments/:environment_id' do get ':id/environments/:environment_id' do
authorize! :read_environment, user_project authorize! :read_environment, user_project

View File

@ -16,16 +16,19 @@ module API
end end
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project'
end end
resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
resource :feature_flags_user_lists do resource :feature_flags_user_lists do
desc 'Get all feature flags user lists of a project' do desc 'List all feature flag user lists for a project' do
detail 'This feature was introduced in GitLab 12.10' detail 'Gets all feature flag user lists for the requested project. ' \
'This feature was introduced in GitLab 12.10.'
success ::API::Entities::FeatureFlag::UserList success ::API::Entities::FeatureFlag::UserList
is_array true
tags %w[feature_flags_user_lists]
end end
params do params do
optional :search, type: String, desc: 'Returns the list of user lists matching the search critiera' optional :search, type: String, desc: 'Return user lists matching the search criteria'
use :pagination use :pagination
end end
@ -35,9 +38,10 @@ module API
with: ::API::Entities::FeatureFlag::UserList with: ::API::Entities::FeatureFlag::UserList
end end
desc 'Create a feature flags user list for a project' do desc 'Create a feature flag user list' do
detail 'This feature was introduced in GitLab 12.10' detail 'Creates a feature flag user list. This feature was introduced in GitLab 12.10.'
success ::API::Entities::FeatureFlag::UserList success ::API::Entities::FeatureFlag::UserList
tags %w[feature_flags_user_lists]
end end
params do params do
requires :name, type: String, desc: 'The name of the list' requires :name, type: String, desc: 'The name of the list'
@ -59,12 +63,13 @@ module API
end end
params do params do
requires :iid, type: String, desc: 'The internal ID of the user list' requires :iid, types: [String, Integer], desc: "The internal ID of the project's feature flag user list"
end end
resource 'feature_flags_user_lists/:iid' do resource 'feature_flags_user_lists/:iid' do
desc 'Get a single feature flag user list belonging to a project' do desc 'Get a feature flag user list' do
detail 'This feature was introduced in GitLab 12.10' detail 'Gets a feature flag user list. This feature was introduced in GitLab 12.10.'
success ::API::Entities::FeatureFlag::UserList success ::API::Entities::FeatureFlag::UserList
tags %w[feature_flags_user_lists]
end end
get do get do
present user_project.operations_feature_flags_user_lists.find_by_iid!(params[:iid]), present user_project.operations_feature_flags_user_lists.find_by_iid!(params[:iid]),
@ -72,8 +77,9 @@ module API
end end
desc 'Update a feature flag user list' do desc 'Update a feature flag user list' do
detail 'This feature was introduced in GitLab 12.10' detail 'Updates a feature flag user list. This feature was introduced in GitLab 12.10.'
success ::API::Entities::FeatureFlag::UserList success ::API::Entities::FeatureFlag::UserList
tags %w[feature_flags_user_lists]
end end
params do params do
optional :name, type: String, desc: 'The name of the list' optional :name, type: String, desc: 'The name of the list'
@ -93,8 +99,9 @@ module API
end end
end end
desc 'Delete a feature flag user list' do desc 'Delete feature flag user list' do
detail 'This feature was introduced in GitLab 12.10' detail 'Deletes a feature flag user list. This feature was introduced in GitLab 12.10.'
tags %w[feature_flags_user_lists]
end end
delete do delete do
# TODO: Move the business logic to a service class in app/services/feature_flags. # TODO: Move the business logic to a service class in app/services/feature_flags.

View File

@ -86,7 +86,11 @@ module Gitlab
analyzers.each do |analyzer| analyzers.each do |analyzer|
next if analyzer.suppressed? && !analyzer.requires_tracking?(parsed) next if analyzer.suppressed? && !analyzer.requires_tracking?(parsed)
analyzer.analyze(parsed) if analyzer.raw?
analyzer.analyze(sql)
else
analyzer.analyze(parsed)
end
rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e rescue StandardError, ::Gitlab::Database::QueryAnalyzers::Base::QueryAnalyzerError => e
# We catch all standard errors to prevent validation errors to introduce fatal errors in production # We catch all standard errors to prevent validation errors to introduce fatal errors in production
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e) Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)

View File

@ -53,6 +53,10 @@ module Gitlab
Thread.current[self.context_key] Thread.current[self.context_key]
end end
def self.raw?
false
end
def self.enabled? def self.enabled?
raise NotImplementedError raise NotImplementedError
end end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
module Gitlab
module Database
module QueryAnalyzers
class QueryRecorder < Base
LOG_FILE = 'rspec/query_recorder.ndjson'
class << self
def raw?
true
end
def enabled?
# Only enable QueryRecorder in CI
ENV['CI'].present?
end
def analyze(sql)
payload = {
sql: sql
}
log_query(payload)
end
private
def log_query(payload)
log_path = Rails.root.join(LOG_FILE)
log_dir = File.dirname(log_path)
# Create log directory if it does not exist since it is only created
# ahead of time by certain CI jobs
FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
log_line = "#{Gitlab::Json.dump(payload)}\n"
File.write(log_path, log_line, mode: 'a')
end
end
end
end
end
end

View File

@ -4548,9 +4548,6 @@ msgstr ""
msgid "Analytics" msgid "Analytics"
msgstr "" msgstr ""
msgid "AnalyticsDashboards|Dashboards"
msgstr ""
msgid "Analyze your dependencies for known vulnerabilities." msgid "Analyze your dependencies for known vulnerabilities."
msgstr "" msgstr ""
@ -25468,6 +25465,9 @@ msgstr ""
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others" msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "" msgstr ""
msgid "Merge requests can't be merged if the status checks did not succeed or are still running."
msgstr ""
msgid "Merge the feature branch into the target branch and fix any conflicts. %{linkStart}How do I fix them?%{linkEnd}" msgid "Merge the feature branch into the target branch and fix any conflicts. %{linkStart}How do I fix them?%{linkEnd}"
msgstr "" msgstr ""
@ -32060,6 +32060,9 @@ msgstr ""
msgid "ProjectSettings|Squashing is never performed and the checkbox is hidden." msgid "ProjectSettings|Squashing is never performed and the checkbox is hidden."
msgstr "" msgstr ""
msgid "ProjectSettings|Status checks must succeed"
msgstr ""
msgid "ProjectSettings|Submit changes to be merged upstream." msgid "ProjectSettings|Submit changes to be merged upstream."
msgstr "" msgstr ""

View File

@ -1,5 +1,6 @@
import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui'; import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import { placeholderForType } from 'jh_else_ce/integrations/constants';
import TriggerFields from '~/integrations/edit/components/trigger_fields.vue'; import TriggerFields from '~/integrations/edit/components/trigger_fields.vue';
@ -28,6 +29,50 @@ describe('TriggerFields', () => {
const findAllGlFormCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox); const findAllGlFormCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox);
const findAllGlFormInputs = () => wrapper.findAllComponents(GlFormInput); const findAllGlFormInputs = () => wrapper.findAllComponents(GlFormInput);
describe('placeholder text on the event fields and default values', () => {
const dummyFieldPlaceholder = '#foo';
const integrationTypes = {
INTEGRATION_TYPE_SLACK: 'slack',
INTEGRATION_TYPE_SLACK_APPLICATION: 'gitlab_slack_application',
INTEGRATION_TYPE_MATTERMOST: 'mattermost',
INTEGRATION_TYPE_NON_EXISTING: 'non_existing',
};
it.each`
integrationType | fieldPlaceholder | expectedPlaceholder
${integrationTypes.INTEGRATION_TYPE_SLACK} | ${undefined} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_SLACK]}
${integrationTypes.INTEGRATION_TYPE_SLACK} | ${''} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_SLACK]}
${integrationTypes.INTEGRATION_TYPE_SLACK} | ${dummyFieldPlaceholder} | ${dummyFieldPlaceholder}
${integrationTypes.INTEGRATION_TYPE_SLACK_APPLICATION} | ${undefined} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_SLACK_APPLICATION]}
${integrationTypes.INTEGRATION_TYPE_SLACK_APPLICATION} | ${''} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_SLACK_APPLICATION]}
${integrationTypes.INTEGRATION_TYPE_SLACK_APPLICATION} | ${dummyFieldPlaceholder} | ${dummyFieldPlaceholder}
${integrationTypes.INTEGRATION_TYPE_MATTERMOST} | ${undefined} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_MATTERMOST]}
${integrationTypes.INTEGRATION_TYPE_MATTERMOST} | ${''} | ${placeholderForType[integrationTypes.INTEGRATION_TYPE_MATTERMOST]}
${integrationTypes.INTEGRATION_TYPE_MATTERMOST} | ${dummyFieldPlaceholder} | ${dummyFieldPlaceholder}
${integrationTypes.INTEGRATION_TYPE_NON_EXISTING} | ${undefined} | ${undefined}
${integrationTypes.INTEGRATION_TYPE_NON_EXISTING} | ${''} | ${undefined}
${integrationTypes.INTEGRATION_TYPE_NON_EXISTING} | ${dummyFieldPlaceholder} | ${dummyFieldPlaceholder}
`(
'passed down correct placeholder for "$integrationType" type and "$fieldPlaceholder" placeholder on the field',
({ integrationType, fieldPlaceholder, expectedPlaceholder }) => {
createComponent({
type: integrationType,
events: [
{
field: {
name: 'foo',
value: '',
placeholder: fieldPlaceholder,
},
},
],
});
const field = wrapper.findComponent(GlFormInput);
expect(field.attributes('placeholder')).toBe(expectedPlaceholder);
},
);
});
describe.each([true, false])('template, isInheriting = `%p`', (isInheriting) => { describe.each([true, false])('template, isInheriting = `%p`', (isInheriting) => {
it('renders a label with text "Trigger"', () => { it('renders a label with text "Trigger"', () => {
createComponent(); createComponent();

View File

@ -10,6 +10,7 @@ RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
before do before do
allow(described_class.instance).to receive(:all_analyzers).and_return([analyzer, disabled_analyzer]) allow(described_class.instance).to receive(:all_analyzers).and_return([analyzer, disabled_analyzer])
allow(analyzer).to receive(:enabled?).and_return(true) allow(analyzer).to receive(:enabled?).and_return(true)
allow(analyzer).to receive(:raw?).and_return(false)
allow(analyzer).to receive(:suppressed?).and_return(false) allow(analyzer).to receive(:suppressed?).and_return(false)
allow(analyzer).to receive(:begin!) allow(analyzer).to receive(:begin!)
allow(analyzer).to receive(:end!) allow(analyzer).to receive(:end!)
@ -181,6 +182,13 @@ RSpec.describe Gitlab::Database::QueryAnalyzer, query_analyzers: false do
expect { process_sql("SELECT 1 FROM projects") }.not_to raise_error expect { process_sql("SELECT 1 FROM projects") }.not_to raise_error
end end
it 'does call analyze with raw sql when raw? is true' do
expect(analyzer).to receive(:raw?).and_return(true)
expect(analyzer).to receive(:analyze).with('SELECT 1 FROM projects')
expect { process_sql("SELECT 1 FROM projects") }.not_to raise_error
end
def process_sql(sql) def process_sql(sql)
described_class.instance.within do described_class.instance.within do
ApplicationRecord.load_balancer.read_write do |connection| ApplicationRecord.load_balancer.read_write do |connection|

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Database::QueryAnalyzers::QueryRecorder, query_analyzers: false do
# We keep only the QueryRecorder analyzer running
around do |example|
described_class.with_suppressed(false) do
example.run
end
end
context 'when analyzer is enabled for tests' do
let(:query) { 'SELECT 1 FROM projects' }
let(:log_path) { Rails.root.join(described_class::LOG_FILE) }
before do
stub_env('CI', 'true')
# This is needed to be able to stub_env the CI variable
::Gitlab::Database::QueryAnalyzer.instance.begin!([described_class])
end
after do
::Gitlab::Database::QueryAnalyzer.instance.end!([described_class])
end
it 'logs queries to a file' do
allow(FileUtils).to receive(:mkdir_p)
.with(File.dirname(log_path))
expect(File).to receive(:write)
.with(log_path, /^{"sql":"#{query}/, mode: 'a')
expect(described_class).to receive(:analyze).with(/^#{query}/).and_call_original
expect { ApplicationRecord.connection.execute(query) }.not_to raise_error
end
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
RSpec.configure do |config|
# Truncate the query_recorder log file before starting the suite
config.before(:suite) do
log_path = Rails.root.join(Gitlab::Database::QueryAnalyzers::QueryRecorder::LOG_FILE)
File.write(log_path, '') if File.exist?(log_path)
end
end