Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a3dfd311f4
commit
844e3ef899
|
@ -0,0 +1,59 @@
|
|||
<script>
|
||||
import { s__ } from '~/locale';
|
||||
import IntegrationsTable from './integrations_table.vue';
|
||||
|
||||
export default {
|
||||
name: 'IntegrationsList',
|
||||
components: {
|
||||
IntegrationsTable,
|
||||
},
|
||||
props: {
|
||||
integrations: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
integrationsGrouped() {
|
||||
return this.integrations.reduce(
|
||||
(integrations, integration) => {
|
||||
if (integration.active) {
|
||||
integrations.active.push(integration);
|
||||
} else {
|
||||
integrations.inactive.push(integration);
|
||||
}
|
||||
|
||||
return integrations;
|
||||
},
|
||||
{ active: [], inactive: [] },
|
||||
);
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
activeTableEmptyText: s__("Integrations|You haven't activated any integrations yet."),
|
||||
inactiveTableEmptyText: s__("Integrations|You've activated every integration 🎉"),
|
||||
activeIntegrationsHeading: s__('Integrations|Active integrations'),
|
||||
inactiveIntegrationsHeading: s__('Integrations|Add an integration'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h4>{{ $options.i18n.activeIntegrationsHeading }}</h4>
|
||||
<integrations-table
|
||||
class="gl-mb-7!"
|
||||
:integrations="integrationsGrouped.active"
|
||||
:empty-text="$options.i18n.activeTableEmptyText"
|
||||
show-updated-at
|
||||
data-testid="active-integrations-table"
|
||||
/>
|
||||
|
||||
<h4>{{ $options.i18n.inactiveIntegrationsHeading }}</h4>
|
||||
<integrations-table
|
||||
:integrations="integrationsGrouped.inactive"
|
||||
:empty-text="$options.i18n.inactiveTableEmptyText"
|
||||
data-testid="inactive-integrations-table"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,95 @@
|
|||
<script>
|
||||
import { GlIcon, GlLink, GlTable, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { sprintf, s__, __ } from '~/locale';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlTable,
|
||||
TimeAgoTooltip,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
integrations: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
showUpdatedAt: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
emptyText: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return [
|
||||
{
|
||||
key: 'active',
|
||||
label: '',
|
||||
thClass: 'gl-w-10',
|
||||
},
|
||||
{
|
||||
key: 'title',
|
||||
label: __('Integration'),
|
||||
thClass: 'gl-w-quarter',
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
label: __('Description'),
|
||||
thClass: 'gl-display-none d-sm-table-cell',
|
||||
tdClass: 'gl-display-none d-sm-table-cell',
|
||||
},
|
||||
{
|
||||
key: 'updated_at',
|
||||
label: this.showUpdatedAt ? __('Last updated') : '',
|
||||
thClass: 'gl-w-20p',
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getStatusTooltipTitle(integration) {
|
||||
return sprintf(s__('Integrations|%{integrationTitle}: active'), {
|
||||
integrationTitle: integration.title,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-table :items="integrations" :fields="fields" :empty-text="emptyText" show-empty fixed>
|
||||
<template #cell(active)="{ item }">
|
||||
<gl-icon
|
||||
v-if="item.active"
|
||||
v-gl-tooltip
|
||||
name="check"
|
||||
class="gl-text-green-500"
|
||||
:title="getStatusTooltipTitle(item)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell(title)="{ item }">
|
||||
<gl-link
|
||||
:href="item.edit_path"
|
||||
class="gl-font-weight-bold"
|
||||
:data-qa-selector="`${item.name}_link`"
|
||||
>
|
||||
{{ item.title }}
|
||||
</gl-link>
|
||||
</template>
|
||||
|
||||
<template #cell(updated_at)="{ item }">
|
||||
<time-ago-tooltip v-if="showUpdatedAt && item.updated_at" :time="item.updated_at" />
|
||||
</template>
|
||||
</gl-table>
|
||||
</template>
|
|
@ -0,0 +1,23 @@
|
|||
import Vue from 'vue';
|
||||
import IntegrationList from './components/integrations_list.vue';
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-integrations-list');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { integrations } = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render(createElement) {
|
||||
return createElement(IntegrationList, {
|
||||
props: {
|
||||
integrations: JSON.parse(integrations),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
import initIntegrationsList from '~/integrations/index';
|
||||
|
||||
initIntegrationsList();
|
|
@ -1,4 +1,7 @@
|
|||
import initIntegrationsList from '~/integrations/index';
|
||||
import PersistentUserCallout from '~/persistent_user_callout';
|
||||
|
||||
const callout = document.querySelector('.js-webhooks-moved-alert');
|
||||
PersistentUserCallout.factory(callout);
|
||||
|
||||
initIntegrationsList();
|
||||
|
|
|
@ -115,6 +115,12 @@ module ServicesHelper
|
|||
form_data
|
||||
end
|
||||
|
||||
def integration_list_data(integrations)
|
||||
{
|
||||
integrations: integrations.map { |i| serialize_integration(i) }.to_json
|
||||
}
|
||||
end
|
||||
|
||||
def trigger_events_for_service(integration)
|
||||
ServiceEventSerializer.new(service: integration).represent(integration.configurable_events).to_json
|
||||
end
|
||||
|
@ -155,6 +161,17 @@ module ServicesHelper
|
|||
'project'
|
||||
end
|
||||
end
|
||||
|
||||
def serialize_integration(integration)
|
||||
{
|
||||
active: integration.operating?,
|
||||
title: integration.title,
|
||||
description: integration.description,
|
||||
updated_at: integration.updated_at,
|
||||
edit_path: scoped_edit_integration_path(integration),
|
||||
name: integration.to_param
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
ServicesHelper.prepend_if_ee('EE::ServicesHelper')
|
||||
|
|
|
@ -72,7 +72,7 @@ module Pages
|
|||
path: File.join(project.full_path, 'public/')
|
||||
}
|
||||
rescue LegacyStorageDisabledError => e
|
||||
Gitlab::ErrorTracking.track_exception(e)
|
||||
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
|
||||
|
||||
nil
|
||||
end
|
||||
|
|
|
@ -79,21 +79,25 @@ class DroneCiService < CiService
|
|||
end
|
||||
|
||||
def title
|
||||
'Drone CI'
|
||||
'Drone'
|
||||
end
|
||||
|
||||
def description
|
||||
'Drone is a Continuous Integration platform built on Docker, written in Go'
|
||||
s_('ProjectService|Run CI/CD pipelines with Drone.')
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'drone_ci'
|
||||
end
|
||||
|
||||
def help
|
||||
s_('ProjectService|Run CI/CD pipelines with Drone.')
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'token', placeholder: 'Drone CI project specific token', required: true },
|
||||
{ type: 'text', name: 'drone_url', title: s_('ProjectService|Drone URL'), placeholder: 'http://drone.example.com', required: true },
|
||||
{ type: 'text', name: 'token', help: s_('ProjectService|Token for the Drone project.'), required: true },
|
||||
{ type: 'text', name: 'drone_url', title: s_('ProjectService|Drone server URL'), placeholder: 'http://drone.example.com', required: true },
|
||||
{ type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" }
|
||||
]
|
||||
end
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
.gl-alert-actions
|
||||
= link_to s_('AdminSettings|Go to General Settings'), general_admin_application_settings_path, class: 'btn gl-alert-action btn-info gl-button'
|
||||
|
||||
%h4= s_('AdminSettings|Apply integration settings to all Projects')
|
||||
%p
|
||||
= s_('AdminSettings|Integrations configured here will automatically apply to all projects on this instance.')
|
||||
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
|
||||
%h3= s_('Integrations|Project integration management')
|
||||
|
||||
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
|
||||
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
|
||||
= render 'shared/integrations/index', integrations: @integrations
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
- page_title _('Integrations')
|
||||
- @content_class = 'limit-container-width' unless fluid_layout
|
||||
|
||||
%h4= s_('GroupSettings|Apply integration settings to all Projects')
|
||||
%p
|
||||
= s_('GroupSettings|Integrations configured here will automatically apply to all projects in this group.')
|
||||
= link_to _('Learn more'), integrations_help_page_path, target: '_blank', rel: 'noopener noreferrer'
|
||||
%h3= s_('Integrations|Project integration management')
|
||||
|
||||
- integrations_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: integrations_help_page_path }
|
||||
%p= s_('Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}.').html_safe % { integrations_link_start: integrations_link_start, link_end: '</a>'.html_safe }
|
||||
= render 'shared/integrations/index', integrations: @integrations
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
.gl-alert-actions
|
||||
= link_to _('Go to Webhooks'), project_hooks_path(@project), class: 'gl-button btn gl-alert-action btn-info'
|
||||
|
||||
%h4= _('Integrations')
|
||||
%h3= _('Integrations')
|
||||
- integrations_link_start = '<a href="%{url}">'.html_safe % { url: help_page_url('user/project/integrations/overview') }
|
||||
- webhooks_link_start = '<a href="%{url}">'.html_safe % { url: project_hooks_path(@project) }
|
||||
%p= _("%{integrations_link_start}Integrations%{link_end} enable you to make third-party applications part of your GitLab workflow. If the available integrations don't meet your needs, consider using a %{webhooks_link_start}webhook%{link_end}.").html_safe % { integrations_link_start: integrations_link_start, webhooks_link_start: webhooks_link_start, link_end: '</a>'.html_safe }
|
||||
|
|
|
@ -1,27 +1 @@
|
|||
%table.table.b-table.gl-table{ role: 'table', 'aria-busy': false, 'aria-colcount': 4 }
|
||||
%colgroup
|
||||
%col
|
||||
%col
|
||||
%col.d-none.d-sm-table-column
|
||||
%col{ width: 135 }
|
||||
%thead{ role: 'rowgroup' }
|
||||
%tr{ role: 'row' }
|
||||
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 1 }
|
||||
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 2 }= _('Integration')
|
||||
%th.d-none.d-sm-block{ role: 'columnheader', scope: 'col', 'aria-colindex': 3 }= _('Description')
|
||||
%th{ role: 'columnheader', scope: 'col', 'aria-colindex': 4 }= _('Last updated')
|
||||
|
||||
%tbody{ role: 'rowgroup' }
|
||||
- integrations.each do |integration|
|
||||
- activated_label = (integration.activated? ? s_("ProjectService|%{service_title}: status on") : s_("ProjectService|%{service_title}: status off")) % { service_title: integration.title }
|
||||
%tr{ role: 'row' }
|
||||
%td{ role: 'cell', 'aria-colindex': 1, 'aria-label': activated_label, title: activated_label }
|
||||
- if integration.operating?
|
||||
= sprite_icon('check', css_class: 'gl-text-green-500')
|
||||
%td{ role: 'cell', 'aria-colindex': 2 }
|
||||
= link_to integration.title, scoped_edit_integration_path(integration), class: 'gl-font-weight-bold', data: { qa_selector: "#{integration.to_param}_link" }
|
||||
%td.d-none.d-sm-table-cell{ role: 'cell', 'aria-colindex': 3 }
|
||||
= integration.description
|
||||
%td{ role: 'cell', 'aria-colindex': 4 }
|
||||
- if integration.updated_at.present?
|
||||
= time_ago_with_tooltip integration.updated_at
|
||||
.js-integrations-list{ data: integration_list_data(integrations) }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Return 403 status code to the Runner when CI Job is deleted
|
||||
merge_request: 59382
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add index on (created_at, web_hook_id) to the partitioned web_hook_logs
|
||||
merge_request: 59261
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update drone integration UI text
|
||||
merge_request: 59231
|
||||
author:
|
||||
type: other
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/292897
|
|||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::dynamic analysis
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedAtWebHookIdIndexToPartitionedWebHookLog < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::PartitioningMigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
CREATED_AT_WEB_HOOK_ID_INDEX_NAME = 'index_web_hook_logs_part_on_created_at_and_web_hook_id'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_partitioned_index :web_hook_logs_part_0c5294f417,
|
||||
[:created_at, :web_hook_id],
|
||||
name: CREATED_AT_WEB_HOOK_ID_INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_partitioned_index_by_name :web_hook_logs_part_0c5294f417, CREATED_AT_WEB_HOOK_ID_INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
fe98a617ac8bacf270425c1e9b9b60aee1c3c0e47d5c915fe122cb99c1c1c822
|
|
@ -24260,6 +24260,8 @@ CREATE INDEX index_web_hook_logs_on_created_at_and_web_hook_id ON web_hook_logs
|
|||
|
||||
CREATE INDEX index_web_hook_logs_on_web_hook_id ON web_hook_logs USING btree (web_hook_id);
|
||||
|
||||
CREATE INDEX index_web_hook_logs_part_on_created_at_and_web_hook_id ON ONLY web_hook_logs_part_0c5294f417 USING btree (created_at, web_hook_id);
|
||||
|
||||
CREATE INDEX index_web_hook_logs_part_on_web_hook_id ON ONLY web_hook_logs_part_0c5294f417 USING btree (web_hook_id);
|
||||
|
||||
CREATE INDEX index_web_hooks_on_group_id ON web_hooks USING btree (group_id) WHERE ((type)::text = 'GroupHook'::text);
|
||||
|
|
|
@ -312,6 +312,67 @@ Example response:
|
|||
]
|
||||
```
|
||||
|
||||
## List memberships for a billable member of a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321560) in GitLab 13.11.
|
||||
|
||||
Gets a list of memberships for a billable member of a group.
|
||||
|
||||
Lists all projects and groups a user is a member of. Only projects and groups within the group hierarchy are included.
|
||||
For instance, if the requested group is `Root Group`, and the requested user is a direct member of both `Root Group / Sub Group One` and `Other Group / Sub Group Two`, then only `Root Group / Sub Group One` will be returned, because `Other Group / Sub Group Two` is not within the `Root Group` hierarchy.
|
||||
|
||||
The response represents only direct memberships. Inherited memberships are not included.
|
||||
|
||||
This API endpoint works on top-level groups only. It does not work on subgroups.
|
||||
|
||||
This API endpoint requires permission to admin memberships for the group.
|
||||
|
||||
This API endpoint takes [pagination](README.md#pagination) parameters `page` and `per_page` to restrict the list of memberships.
|
||||
|
||||
```plaintext
|
||||
GET /groups/:id/billable_members/:user_id/memberships
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the billable member |
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members/:user_id/memberships"
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 168,
|
||||
"source_id": 131,
|
||||
"source_full_name": "Root Group / Sub Group One",
|
||||
"source_members_url": "https://gitlab.example.com/groups/root-group/sub-group-one/-/group_members",
|
||||
"created_at": "2021-03-31T17:28:44.812Z",
|
||||
"expires_at": "2022-03-21",
|
||||
"access_level": {
|
||||
"string_value": "Developer",
|
||||
"integer_value": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 169,
|
||||
"source_id": 63,
|
||||
"source_full_name": "Root Group / Sub Group One / My Project",
|
||||
"source_members_url": "https://gitlab.example.com/root-group/sub-group-one/my-project/-/project_members",
|
||||
"created_at": "2021-03-31T17:29:14.934Z",
|
||||
"expires_at": null,
|
||||
"access_level": {
|
||||
"string_value": "Maintainer",
|
||||
"integer_value": 40
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Remove a billable member from a group
|
||||
|
||||
Removes a billable member from a group and its subgroups and projects.
|
||||
|
|
|
@ -3169,6 +3169,76 @@ artifacts are restored after [caches](#cache).
|
|||
|
||||
[Read more about artifacts](../pipelines/job_artifacts.md).
|
||||
|
||||
#### `dependencies`
|
||||
|
||||
By default, all `artifacts` from previous stages
|
||||
are passed to each job. However, you can use the `dependencies` keyword to
|
||||
define a limited list of jobs to fetch artifacts from. You can also set a job to download no artifacts at all.
|
||||
|
||||
To use this feature, define `dependencies` in context of the job and pass
|
||||
a list of all previous jobs the artifacts should be downloaded from.
|
||||
|
||||
You can define jobs from stages that were executed before the current one.
|
||||
An error occurs if you define jobs from the current or an upcoming stage.
|
||||
|
||||
To prevent a job from downloading artifacts, define an empty array.
|
||||
|
||||
When you use `dependencies`, the status of the previous job is not considered.
|
||||
If a job fails or it's a manual job that isn't triggered, no error occurs.
|
||||
|
||||
The following example defines two jobs with artifacts: `build:osx` and
|
||||
`build:linux`. When the `test:osx` is executed, the artifacts from `build:osx`
|
||||
are downloaded and extracted in the context of the build. The same happens
|
||||
for `test:linux` and artifacts from `build:linux`.
|
||||
|
||||
The job `deploy` downloads artifacts from all previous jobs because of
|
||||
the [stage](#stages) precedence:
|
||||
|
||||
```yaml
|
||||
build:osx:
|
||||
stage: build
|
||||
script: make build:osx
|
||||
artifacts:
|
||||
paths:
|
||||
- binaries/
|
||||
|
||||
build:linux:
|
||||
stage: build
|
||||
script: make build:linux
|
||||
artifacts:
|
||||
paths:
|
||||
- binaries/
|
||||
|
||||
test:osx:
|
||||
stage: test
|
||||
script: make test:osx
|
||||
dependencies:
|
||||
- build:osx
|
||||
|
||||
test:linux:
|
||||
stage: test
|
||||
script: make test:linux
|
||||
dependencies:
|
||||
- build:linux
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: make deploy
|
||||
```
|
||||
|
||||
##### When a dependent job fails
|
||||
|
||||
> Introduced in GitLab 10.3.
|
||||
|
||||
If the artifacts of the job that is set as a dependency are
|
||||
[expired](#artifactsexpire_in) or
|
||||
[erased](../pipelines/job_artifacts.md#erase-job-artifacts), then
|
||||
the dependent job fails.
|
||||
|
||||
You can ask your administrator to
|
||||
[flip this switch](../../administration/job_artifacts.md#validation-for-dependencies)
|
||||
and bring back the old behavior.
|
||||
|
||||
#### `artifacts:exclude`
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15122) in GitLab 13.1
|
||||
|
@ -3717,76 +3787,6 @@ plan report uploads to GitLab as an artifact and displays
|
|||
in merge requests. For more information, see
|
||||
[Output `terraform plan` information into a merge request](../../user/infrastructure/mr_integration.md).
|
||||
|
||||
##### `dependencies`
|
||||
|
||||
By default, all `artifacts` from previous stages
|
||||
are passed to each job. However, you can use the `dependencies` keyword to
|
||||
define a limited list of jobs to fetch artifacts from. You can also set a job to download no artifacts at all.
|
||||
|
||||
To use this feature, define `dependencies` in context of the job and pass
|
||||
a list of all previous jobs the artifacts should be downloaded from.
|
||||
|
||||
You can define jobs from stages that were executed before the current one.
|
||||
An error occurs if you define jobs from the current or an upcoming stage.
|
||||
|
||||
To prevent a job from downloading artifacts, define an empty array.
|
||||
|
||||
When you use `dependencies`, the status of the previous job is not considered.
|
||||
If a job fails or it's a manual job that isn't triggered, no error occurs.
|
||||
|
||||
The following example defines two jobs with artifacts: `build:osx` and
|
||||
`build:linux`. When the `test:osx` is executed, the artifacts from `build:osx`
|
||||
are downloaded and extracted in the context of the build. The same happens
|
||||
for `test:linux` and artifacts from `build:linux`.
|
||||
|
||||
The job `deploy` downloads artifacts from all previous jobs because of
|
||||
the [stage](#stages) precedence:
|
||||
|
||||
```yaml
|
||||
build:osx:
|
||||
stage: build
|
||||
script: make build:osx
|
||||
artifacts:
|
||||
paths:
|
||||
- binaries/
|
||||
|
||||
build:linux:
|
||||
stage: build
|
||||
script: make build:linux
|
||||
artifacts:
|
||||
paths:
|
||||
- binaries/
|
||||
|
||||
test:osx:
|
||||
stage: test
|
||||
script: make test:osx
|
||||
dependencies:
|
||||
- build:osx
|
||||
|
||||
test:linux:
|
||||
stage: test
|
||||
script: make test:linux
|
||||
dependencies:
|
||||
- build:linux
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script: make deploy
|
||||
```
|
||||
|
||||
###### When a dependent job fails
|
||||
|
||||
> Introduced in GitLab 10.3.
|
||||
|
||||
If the artifacts of the job that is set as a dependency are
|
||||
[expired](#artifactsexpire_in) or
|
||||
[erased](../pipelines/job_artifacts.md#erase-job-artifacts), then
|
||||
the dependent job fails.
|
||||
|
||||
You can ask your administrator to
|
||||
[flip this switch](../../administration/job_artifacts.md#validation-for-dependencies)
|
||||
and bring back the old behavior.
|
||||
|
||||
#### `artifacts:untracked`
|
||||
|
||||
Use `artifacts:untracked` to add all Git untracked files as artifacts (along
|
||||
|
|
|
@ -940,6 +940,14 @@ A site profile contains the following:
|
|||
|
||||
- **Profile name**: A name you assign to the site to be scanned.
|
||||
- **Target URL**: The URL that DAST runs against.
|
||||
- **Excluded URLs**: A comma-separated list of URLs to exclude from the scan.
|
||||
- **Request headers**: A comma-separated list of HTTP request headers, including names and values. These headers are added to every request made by DAST.
|
||||
- **Authentication**:
|
||||
- **Authenticated URL**: The URL of the page containing the sign-in HTML form on the target website. The username and password are submitted with the login form to create an authenticated scan.
|
||||
- **Username**: The username used to authenticate to the website.
|
||||
- **Password**: The password used to authenticate to the website.
|
||||
- **Username form field**: The name of username field at the sign-in HTML form.
|
||||
- **Password form field**: The name of password field at the sign-in HTML form.
|
||||
|
||||
#### Site profile validation
|
||||
|
||||
|
|
|
@ -38,10 +38,17 @@ module API
|
|||
end
|
||||
end
|
||||
|
||||
# HTTP status codes to terminate the job on GitLab Runner:
|
||||
# - 403
|
||||
def authenticate_job!(require_running: true)
|
||||
job = current_job
|
||||
|
||||
not_found! unless job
|
||||
# 404 is not returned here because we want to terminate the job if it's
|
||||
# running. A 404 can be returned from anywhere in the networking stack which is why
|
||||
# we are explicit about a 403, we should improve this in
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/327703
|
||||
forbidden! unless job
|
||||
|
||||
forbidden! unless job_token_valid?(job)
|
||||
|
||||
forbidden!('Project has been deleted!') if job.project.nil? || job.project.pending_delete?
|
||||
|
|
|
@ -2285,9 +2285,6 @@ msgstr ""
|
|||
msgid "AdminProjects|Delete Project %{projectName}?"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Apply integration settings to all Projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Auto DevOps domain"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2306,9 +2303,6 @@ msgstr ""
|
|||
msgid "AdminSettings|Go to General Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Integrations configured here will automatically apply to all projects on this instance."
|
||||
msgstr ""
|
||||
|
||||
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10142,7 +10136,7 @@ msgstr ""
|
|||
msgid "DastProfiles|URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|URLs to skip during the authenticated scan. Use regular expression syntax to match multiple URLs."
|
||||
msgid "DastProfiles|URLs to skip during the authenticated scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "DastProfiles|Username"
|
||||
|
@ -15457,9 +15451,6 @@ msgstr ""
|
|||
msgid "GroupSettings|Allow project access token creation"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Apply integration settings to all Projects"
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15508,9 +15499,6 @@ msgstr ""
|
|||
msgid "GroupSettings|If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Integrations configured here will automatically apply to all projects in this group."
|
||||
msgstr ""
|
||||
|
||||
msgid "GroupSettings|Learn more about group-level project templates."
|
||||
msgstr ""
|
||||
|
||||
|
@ -17068,12 +17056,21 @@ msgstr ""
|
|||
msgid "Integrations"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|%{integrationTitle}: active"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|%{integration} settings saved and active."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|%{integration} settings saved, but not active."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Active integrations"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Add an integration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Add namespace"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17086,6 +17083,9 @@ msgstr ""
|
|||
msgid "Integrations|All projects inheriting these settings will also be reset."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|As a GitLab administrator, you can set default configuration parameters for a given integration that all projects can inherit and use. When you set these parameters, your changes update the integration for all projects that are not already using custom settings. Learn more about %{integrations_link_start}Project integration management%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Browser limitations"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17149,6 +17149,9 @@ msgstr ""
|
|||
msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Project integration management"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Projects using custom settings will not be affected."
|
||||
msgstr ""
|
||||
|
||||
|
@ -17203,12 +17206,18 @@ msgstr ""
|
|||
msgid "Integrations|You can now close this window and return to the GitLab for Jira application."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You haven't activated any integrations yet."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You must have owner or maintainer permissions to link namespaces."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|You've activated every integration 🎉"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interactive mode"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24590,13 +24599,7 @@ msgstr ""
|
|||
msgid "ProjectSelect|Search for project"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|%{service_title}: status off"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|%{service_title}: status on"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|Drone URL"
|
||||
msgid "ProjectService|Drone server URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|Enter new API key"
|
||||
|
@ -24632,12 +24635,18 @@ msgstr ""
|
|||
msgid "ProjectService|Perform common operations on GitLab project: %{project_name}"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|Run CI/CD pipelines with Drone."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|TeamCity URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|To configure this integration, you should:"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|Token for the Drone project."
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectService|Trigger event for new comments on confidential issues."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ module QA
|
|||
module Project
|
||||
module Settings
|
||||
class Integrations < QA::Page::Base
|
||||
view 'app/views/shared/integrations/_index.html.haml' do
|
||||
element :prometheus_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
|
||||
element :jira_link, 'data: { qa_selector: "#{integration.to_param' # rubocop:disable QA/ElementWithPattern
|
||||
view 'app/assets/javascripts/integrations/index/components/integrations_table.vue' do
|
||||
element :prometheus_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
|
||||
element :jira_link, %q(:data-qa-selector="`${item.name}_link`") # rubocop:disable QA/ElementWithPattern
|
||||
end
|
||||
|
||||
def click_on_prometheus_integration
|
||||
|
|
|
@ -24,7 +24,7 @@ RSpec.describe 'User searches group settings', :js do
|
|||
visit group_settings_integrations_path(group)
|
||||
end
|
||||
|
||||
it_behaves_like 'can highlight results', 'integration settings'
|
||||
it_behaves_like 'can highlight results', 'set default configuration'
|
||||
end
|
||||
|
||||
context 'in Repository page' do
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'User views services' do
|
||||
RSpec.describe 'User views services', :js do
|
||||
include_context 'project service activation'
|
||||
|
||||
it 'shows the list of available services' do
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import IntegrationsList from '~/integrations/index/components/integrations_list.vue';
|
||||
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
|
||||
|
||||
describe('IntegrationsList', () => {
|
||||
let wrapper;
|
||||
|
||||
const findActiveIntegrationsTable = () => wrapper.findByTestId('active-integrations-table');
|
||||
const findInactiveIntegrationsTable = () => wrapper.findByTestId('inactive-integrations-table');
|
||||
|
||||
const createComponent = (propsData = {}) => {
|
||||
wrapper = extendedWrapper(shallowMount(IntegrationsList, { propsData }));
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('provides correct `integrations` prop to the IntegrationsTable instance', () => {
|
||||
createComponent({ integrations: [...mockInactiveIntegrations, ...mockActiveIntegrations] });
|
||||
|
||||
expect(findActiveIntegrationsTable().props('integrations')).toEqual(mockActiveIntegrations);
|
||||
expect(findInactiveIntegrationsTable().props('integrations')).toEqual(mockInactiveIntegrations);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
import { GlTable, GlIcon } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import IntegrationsTable from '~/integrations/index/components/integrations_table.vue';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
|
||||
import { mockActiveIntegrations, mockInactiveIntegrations } from '../mock_data';
|
||||
|
||||
describe('IntegrationsTable', () => {
|
||||
let wrapper;
|
||||
|
||||
const findTable = () => wrapper.findComponent(GlTable);
|
||||
|
||||
const createComponent = (propsData = {}) => {
|
||||
wrapper = mount(IntegrationsTable, {
|
||||
propsData: {
|
||||
integrations: mockActiveIntegrations,
|
||||
...propsData,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
describe.each([true, false])('when `showUpdatedAt` is %p', (showUpdatedAt) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ showUpdatedAt });
|
||||
});
|
||||
|
||||
it(`${showUpdatedAt ? 'renders' : 'does not render'} content in "Last updated" column`, () => {
|
||||
const headers = findTable().findAll('th');
|
||||
expect(headers.wrappers.some((header) => header.text() === 'Last updated')).toBe(
|
||||
showUpdatedAt,
|
||||
);
|
||||
expect(wrapper.findComponent(TimeAgoTooltip).exists()).toBe(showUpdatedAt);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each`
|
||||
scenario | integrations | shouldRenderActiveIcon
|
||||
${'when integration is active'} | ${[mockActiveIntegrations[0]]} | ${true}
|
||||
${'when integration is inactive'} | ${[mockInactiveIntegrations[0]]} | ${false}
|
||||
`('$scenario', ({ shouldRenderActiveIcon, integrations }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({ integrations });
|
||||
});
|
||||
|
||||
it(`${shouldRenderActiveIcon ? 'renders' : 'does not render'} icon in first column`, () => {
|
||||
expect(findTable().findComponent(GlIcon).exists()).toBe(shouldRenderActiveIcon);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
export const mockActiveIntegrations = [
|
||||
{
|
||||
active: true,
|
||||
title: 'Asana',
|
||||
description: 'Asana - Teamwork without email',
|
||||
updated_at: '2021-03-18T00:27:09.634Z',
|
||||
edit_path:
|
||||
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/asana/edit',
|
||||
name: 'asana',
|
||||
},
|
||||
{
|
||||
active: true,
|
||||
title: 'Jira',
|
||||
description: 'Jira issue tracker',
|
||||
updated_at: '2021-01-29T06:41:25.806Z',
|
||||
edit_path:
|
||||
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/jira/edit',
|
||||
name: 'jira',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockInactiveIntegrations = [
|
||||
{
|
||||
active: false,
|
||||
title: 'Webex Teams',
|
||||
description: 'Receive event notifications in Webex Teams',
|
||||
updated_at: null,
|
||||
edit_path:
|
||||
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/webex_teams/edit',
|
||||
name: 'webex_teams',
|
||||
},
|
||||
{
|
||||
active: false,
|
||||
title: 'YouTrack',
|
||||
description: 'YouTrack issue tracker',
|
||||
updated_at: null,
|
||||
edit_path:
|
||||
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/youtrack/edit',
|
||||
name: 'youtrack',
|
||||
},
|
||||
{
|
||||
active: false,
|
||||
title: 'Atlassian Bamboo CI',
|
||||
description: 'A continuous integration and build server',
|
||||
updated_at: null,
|
||||
edit_path:
|
||||
'/gitlab-qa-sandbox-group/project_with_jenkins_6a55a67c-57c6ed0597c9319a/-/services/bamboo/edit',
|
||||
name: 'bamboo',
|
||||
},
|
||||
];
|
|
@ -113,7 +113,7 @@ RSpec.describe Gitlab::Sanitizers::Exif do
|
|||
it 'cleans only jpg/tiff images with the correct mime types' do
|
||||
expect(sanitizer).not_to receive(:extra_tags)
|
||||
|
||||
expect { subject }.to raise_error(RuntimeError, /File type text\/plain not supported/)
|
||||
expect { subject }.to raise_error(RuntimeError, %r{File type text/plain not supported})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -59,7 +59,7 @@ RSpec.describe Pages::LookupPath do
|
|||
it 'return nil when legacy storage is disabled and there is no deployment' do
|
||||
stub_feature_flags(pages_serve_from_legacy_storage: false)
|
||||
expect(Gitlab::ErrorTracking).to receive(:track_exception)
|
||||
.with(described_class::LegacyStorageDisabledError)
|
||||
.with(described_class::LegacyStorageDisabledError, project_id: project.id)
|
||||
.and_call_original
|
||||
|
||||
expect(source).to eq(nil)
|
||||
|
|
|
@ -180,6 +180,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
it_behaves_like 'authorizes local file'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job does not exist anymore' do
|
||||
before do
|
||||
allow(job).to receive(:id).and_return(non_existing_record_id)
|
||||
end
|
||||
|
||||
it 'returns 403 Forbidden' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -321,6 +333,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when job does not exist anymore' do
|
||||
before do
|
||||
allow(job).to receive(:id).and_return(non_existing_record_id)
|
||||
end
|
||||
|
||||
it 'returns 403 Forbidden' do
|
||||
upload_artifacts(file_upload, headers_with_token)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when job is running' do
|
||||
shared_examples 'successful artifacts upload' do
|
||||
it 'updates successfully' do
|
||||
|
@ -867,6 +891,18 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when job does not exist anymore' do
|
||||
before do
|
||||
allow(job).to receive(:id).and_return(non_existing_record_id)
|
||||
end
|
||||
|
||||
it 'responds with 403 Forbidden' do
|
||||
get api("/jobs/#{job.id}/artifacts"), params: { token: token }, headers: headers
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
def download_artifact(params = {}, request_headers = headers)
|
||||
params = params.merge(token: token)
|
||||
job.reload
|
||||
|
|
|
@ -278,14 +278,22 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
def update_job(token = job.token, **params)
|
||||
context 'when job does not exist anymore' do
|
||||
it 'returns 403 Forbidden' do
|
||||
update_job(non_existing_record_id, state: 'success', trace: 'BUILD TRACE UPDATED')
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
def update_job(job_id = job.id, token = job.token, **params)
|
||||
new_params = params.merge(token: token)
|
||||
put api("/jobs/#{job.id}"), params: new_params
|
||||
put api("/jobs/#{job_id}"), params: new_params
|
||||
end
|
||||
|
||||
def update_job_after_time(update_interval = 20.minutes, state = 'running')
|
||||
travel_to(job.updated_at + update_interval) do
|
||||
update_job(job.token, state: state)
|
||||
update_job(job.id, job.token, state: state)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -219,6 +219,14 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when job does not exist anymore' do
|
||||
it 'returns 403 Forbidden' do
|
||||
patch_the_trace(job_id: non_existing_record_id)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:forbidden)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Runner makes a force-patch' do
|
||||
before do
|
||||
force_patch_the_trace
|
||||
|
@ -264,7 +272,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
it { expect(response).to have_gitlab_http_status(:forbidden) }
|
||||
end
|
||||
|
||||
def patch_the_trace(content = ' appended', request_headers = nil)
|
||||
def patch_the_trace(content = ' appended', request_headers = nil, job_id: job.id)
|
||||
unless request_headers
|
||||
job.trace.read do |stream|
|
||||
offset = stream.size
|
||||
|
@ -274,7 +282,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
|
|||
end
|
||||
|
||||
Timecop.travel(job.updated_at + update_interval) do
|
||||
patch api("/jobs/#{job.id}/trace"), params: content, headers: request_headers
|
||||
patch api("/jobs/#{job_id}/trace"), params: content, headers: request_headers
|
||||
job.reload
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue