Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d14219486e
commit
404bb44ef7
|
|
@ -11,7 +11,7 @@ schedule:package-and-qa:notify-success:
|
|||
- .only-canonical-schedules
|
||||
- .notify
|
||||
script:
|
||||
- 'scripts/notify-slack qa-master ":tada: Scheduled QA against `master` passed! :tada: See $CI_PIPELINE_URL." ci_passing'
|
||||
- 'scripts/notify-slack qa-master ":tada: Scheduled QA against master passed! :tada: See $CI_PIPELINE_URL." ci_passing'
|
||||
needs: ["schedule:package-and-qa"]
|
||||
when: on_success
|
||||
|
||||
|
|
@ -20,6 +20,6 @@ schedule:package-and-qa:notify-failure:
|
|||
- .only-canonical-schedules
|
||||
- .notify
|
||||
script:
|
||||
- 'scripts/notify-slack qa-master ":skull_and_crossbones: Scheduled QA against `master` failed! :skull_and_crossbones: See $CI_PIPELINE_URL." ci_failing'
|
||||
- 'scripts/notify-slack qa-master ":skull_and_crossbones: Scheduled QA against master failed! :skull_and_crossbones: See $CI_PIPELINE_URL." ci_failing'
|
||||
needs: ["schedule:package-and-qa"]
|
||||
when: on_failure
|
||||
|
|
|
|||
|
|
@ -21,14 +21,6 @@ export default {
|
|||
type: String,
|
||||
required: true,
|
||||
},
|
||||
/* This prop can be used to replace some of the `render_commit_status`
|
||||
used across GitLab, this way we could use this vue component and add a
|
||||
realtime status where it makes sense
|
||||
realtime: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true,
|
||||
}, */
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -47,6 +39,9 @@ export default {
|
|||
this.service = new CommitPipelineService(this.endpoint);
|
||||
this.initPolling();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.poll.stop();
|
||||
},
|
||||
methods: {
|
||||
successCallback(res) {
|
||||
const { pipelines } = res.data;
|
||||
|
|
@ -95,9 +90,6 @@ export default {
|
|||
.catch(this.errorCallback);
|
||||
},
|
||||
},
|
||||
destroy() {
|
||||
this.poll.stop();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
The access levels are defined in the `Gitlab::Access` module. Currently, these levels are recognized:
|
||||
|
||||
```
|
||||
```plaintext
|
||||
10 => Guest access
|
||||
20 => Reporter access
|
||||
30 => Developer access
|
||||
|
|
@ -18,14 +18,16 @@
|
|||
|
||||
Gets a list of access requests viewable by the authenticated user.
|
||||
|
||||
```
|
||||
```plaintext
|
||||
GET /groups/:id/access_requests
|
||||
GET /projects/:id/access_requests
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
|
||||
Example request:
|
||||
|
||||
```bash
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/access_requests
|
||||
|
|
@ -59,14 +61,16 @@ Example response:
|
|||
|
||||
Requests access for the authenticated user to a group or project.
|
||||
|
||||
```
|
||||
```plaintext
|
||||
POST /groups/:id/access_requests
|
||||
POST /projects/:id/access_requests
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
|
||||
Example request:
|
||||
|
||||
```bash
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/access_requests
|
||||
|
|
@ -90,16 +94,18 @@ Example response:
|
|||
|
||||
Approves an access request for the given user.
|
||||
|
||||
```
|
||||
```plaintext
|
||||
PUT /groups/:id/access_requests/:user_id/approve
|
||||
PUT /projects/:id/access_requests/:user_id/approve
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the access requester |
|
||||
| `access_level` | integer | no | A valid access level (defaults: `30`, developer access level) |
|
||||
| Attribute | Type | Required | Description |
|
||||
| -------------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the access requester |
|
||||
| `access_level` | integer | no | A valid access level (defaults: `30`, developer access level) |
|
||||
|
||||
Example request:
|
||||
|
||||
```bash
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/access_requests/:user_id/approve?access_level=20
|
||||
|
|
@ -123,15 +129,17 @@ Example response:
|
|||
|
||||
Denies an access request for the given user.
|
||||
|
||||
```
|
||||
```plaintext
|
||||
DELETE /groups/:id/access_requests/:user_id
|
||||
DELETE /projects/:id/access_requests/:user_id
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the access requester |
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | -------------- | -------- | ----------- |
|
||||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
|
||||
| `user_id` | integer | yes | The user ID of the access requester |
|
||||
|
||||
Example request:
|
||||
|
||||
```bash
|
||||
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/:id/access_requests/:user_id
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ description: 'Learn how to contribute to GitLab.'
|
|||
- [Git LFS](lfs.md)
|
||||
- [Developing against interacting components or features](interacting_components.md)
|
||||
- [File uploads](uploads.md)
|
||||
- [Auto DevOps development guide](auto_devops.md)
|
||||
|
||||
## Performance guides
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
# Auto DevOps development guide
|
||||
|
||||
This document provides a development guide for contributors to
|
||||
[Auto DevOps](../topics/autodevops/index.md)
|
||||
|
||||
## Development
|
||||
|
||||
Auto DevOps builds on top of GitLab CI to create an automatic pipeline
|
||||
based on your project contents. When Auto DevOps is enabled for a
|
||||
project, the user does not need to explicitly include any pipeline configuration
|
||||
through a [`.gitlab-ci.yml` file](../ci/yaml/README.md).
|
||||
|
||||
In the absence of a `.gitlab-ci.yml` file, the [Auto DevOps CI
|
||||
template](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
|
||||
is used implicitly to configure the pipeline for the project. This
|
||||
template is a top-level template that includes other sub-templates,
|
||||
which then defines jobs.
|
||||
|
||||
Some jobs use images that are built from external projects:
|
||||
|
||||
- [Auto Build](../topics/autodevops/index.md#auto-build) uses
|
||||
[configuration](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml)
|
||||
in which the `build` job uses an image that is built using the
|
||||
[`auto-build-image`](https://gitlab.com/gitlab-org/cluster-integration/auto-build-image)
|
||||
project.
|
||||
- [Auto Deploy](../topics/autodevops/index.md#auto-deploy) uses
|
||||
[configuration](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml)
|
||||
in which the jobs defined in this template use an image that is built using the
|
||||
[`auto-deploy-image`](https://gitlab.com/gitlab-org/cluster-integration/auto-deploy-image)
|
||||
project. By default, the Helm chart defined in
|
||||
[`auto-deploy-app`](https://gitlab.com/gitlab-org/charts/auto-deploy-app)
|
||||
is used to deploy.
|
||||
|
||||
There are extra variables that get passed to the CI jobs when Auto
|
||||
DevOps is enabled that are not present in a normal CI job. These can be
|
||||
found in
|
||||
[`ProjectAutoDevops`](https://gitlab.com/gitlab-org/gitlab/blob/bf69484afa94e091c3e1383945f60dbe4e8681af/app/models/project_auto_devops.rb).
|
||||
|
||||
## Development environment
|
||||
|
||||
Configuring [GDK for Auto
|
||||
DevOps](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/auto_devops.md).
|
||||
|
|
@ -1183,22 +1183,6 @@ As of GitLab 10.0, the supported buildpacks are:
|
|||
|
||||
The following restrictions apply.
|
||||
|
||||
### Private project support
|
||||
|
||||
CAUTION: **Caution:** Private project support in Auto DevOps is experimental.
|
||||
|
||||
When a project has been marked as private, GitLab's [Container
|
||||
Registry][container-registry] requires authentication when downloading
|
||||
containers. Auto DevOps will automatically provide the required authentication
|
||||
information to Kubernetes, allowing temporary access to the registry.
|
||||
Authentication credentials will be valid while the pipeline is running, allowing
|
||||
for a successful initial deployment.
|
||||
|
||||
After the pipeline completes, Kubernetes will no longer be able to access the
|
||||
Container Registry. **Restarting a pod, scaling a service, or other actions which
|
||||
require on-going access to the registry may fail**. On-going secure access is
|
||||
planned for a subsequent release.
|
||||
|
||||
### Private registry support
|
||||
|
||||
There is no documented way of using private container registry with Auto DevOps.
|
||||
|
|
@ -1274,4 +1258,4 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
|
|||
|
||||
## Development guides
|
||||
|
||||
Configuring [GDK for Auto DevOps](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/auto_devops.md).
|
||||
[Development guide for Auto DevOps](../../development/auto_devops.md)
|
||||
|
|
|
|||
|
|
@ -129,17 +129,7 @@ module QA
|
|||
end
|
||||
|
||||
def try_to_merge!
|
||||
# The merge button is disabled on load
|
||||
wait do
|
||||
has_element?(:merge_button)
|
||||
end
|
||||
|
||||
# The merge button is enabled via JS
|
||||
wait(reload: false) do
|
||||
!find_element(:merge_button).disabled?
|
||||
end
|
||||
|
||||
merge_immediately
|
||||
merge_immediately if ready_to_merge?
|
||||
end
|
||||
|
||||
def merge!
|
||||
|
|
@ -187,6 +177,18 @@ module QA
|
|||
click_element :edit_button
|
||||
end
|
||||
|
||||
def ready_to_merge?
|
||||
# The merge button is disabled on load
|
||||
wait do
|
||||
has_element?(:merge_button)
|
||||
end
|
||||
|
||||
# The merge button is enabled via JS
|
||||
wait(reload: false) do
|
||||
!find_element(:merge_button).disabled?
|
||||
end
|
||||
end
|
||||
|
||||
def view_email_patches
|
||||
click_element :dropdown_toggle
|
||||
visit_link_in_element(:download_email_patches)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Commit pipeline status component when polling is not successful renders not found CI icon without loader 1`] = `
|
||||
<div
|
||||
class="ci-status-link"
|
||||
>
|
||||
<a>
|
||||
<ciicon-stub
|
||||
aria-label="Pipeline: not found"
|
||||
cssclasses=""
|
||||
data-container="body"
|
||||
data-original-title="Pipeline: not found"
|
||||
size="24"
|
||||
status="[object Object]"
|
||||
title=""
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Commit pipeline status component when polling is successful renders CI icon without loader 1`] = `
|
||||
<div
|
||||
class="ci-status-link"
|
||||
>
|
||||
<a
|
||||
href="/frontend-fixtures/pipelines-project/pipelines/47"
|
||||
>
|
||||
<ciicon-stub
|
||||
aria-label="Pipeline: pending"
|
||||
cssclasses=""
|
||||
data-container="body"
|
||||
data-original-title="Pipeline: pending"
|
||||
size="24"
|
||||
status="[object Object]"
|
||||
title=""
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import Visibility from 'visibilityjs';
|
||||
import { GlLoadingIcon } from '@gitlab/ui';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import flash from '~/flash';
|
||||
import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getJSONFixture } from '../helpers/fixtures';
|
||||
|
||||
jest.mock('~/lib/utils/poll');
|
||||
jest.mock('visibilityjs');
|
||||
jest.mock('~/flash');
|
||||
|
||||
const mockFetchData = jest.fn();
|
||||
jest.mock('~/projects/tree/services/commit_pipeline_service', () =>
|
||||
jest.fn().mockImplementation(() => ({
|
||||
fetchData: mockFetchData.mockReturnValue(Promise.resolve()),
|
||||
})),
|
||||
);
|
||||
|
||||
describe('Commit pipeline status component', () => {
|
||||
let wrapper;
|
||||
const { pipelines } = getJSONFixture('pipelines/pipelines.json');
|
||||
const { status: mockCiStatus } = pipelines[0].details;
|
||||
|
||||
const defaultProps = {
|
||||
endpoint: 'endpoint',
|
||||
};
|
||||
|
||||
const createComponent = (props = {}) => {
|
||||
wrapper = shallowMount(CommitPipelineStatus, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
sync: false,
|
||||
});
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('Visibility management', () => {
|
||||
describe('when component is hidden', () => {
|
||||
beforeEach(() => {
|
||||
Visibility.hidden.mockReturnValue(true);
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('does not start polling', () => {
|
||||
const [pollInstance] = Poll.mock.instances;
|
||||
expect(pollInstance.makeRequest).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('requests pipeline data', () => {
|
||||
expect(mockFetchData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when component is visible', () => {
|
||||
beforeEach(() => {
|
||||
Visibility.hidden.mockReturnValue(false);
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('starts polling', () => {
|
||||
const [pollInstance] = [...Poll.mock.instances].reverse();
|
||||
expect(pollInstance.makeRequest).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when component changes its visibility', () => {
|
||||
it.each`
|
||||
visibility | action
|
||||
${false} | ${'restart'}
|
||||
${true} | ${'stop'}
|
||||
`(
|
||||
'$action polling when component visibility becomes $visibility',
|
||||
({ visibility, action }) => {
|
||||
Visibility.hidden.mockReturnValue(!visibility);
|
||||
createComponent();
|
||||
const [pollInstance] = Poll.mock.instances;
|
||||
expect(pollInstance[action]).not.toHaveBeenCalled();
|
||||
Visibility.hidden.mockReturnValue(visibility);
|
||||
const [visibilityHandler] = Visibility.change.mock.calls[0];
|
||||
visibilityHandler();
|
||||
expect(pollInstance[action]).toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('stops polling when component is destroyed', () => {
|
||||
createComponent();
|
||||
wrapper.destroy();
|
||||
const [pollInstance] = Poll.mock.instances;
|
||||
expect(pollInstance.stop).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when polling', () => {
|
||||
let pollConfig;
|
||||
beforeEach(() => {
|
||||
Poll.mockImplementation(config => {
|
||||
pollConfig = config;
|
||||
return { makeRequest: jest.fn(), restart: jest.fn(), stop: jest.fn() };
|
||||
});
|
||||
createComponent();
|
||||
});
|
||||
|
||||
it('shows the loading icon at start', () => {
|
||||
createComponent();
|
||||
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
|
||||
|
||||
pollConfig.successCallback({
|
||||
data: { pipelines: [] },
|
||||
});
|
||||
|
||||
return wrapper.vm.$nextTick().then(() => {
|
||||
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('is successful', () => {
|
||||
beforeEach(() => {
|
||||
pollConfig.successCallback({
|
||||
data: { pipelines: [{ details: { status: mockCiStatus } }] },
|
||||
});
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('renders CI icon without loader', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('is not successful', () => {
|
||||
beforeEach(() => {
|
||||
pollConfig.errorCallback();
|
||||
});
|
||||
|
||||
it('renders not found CI icon without loader', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('displays flash error message', () => {
|
||||
expect(flash).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
describe('Commit pipeline status component', () => {
|
||||
let vm;
|
||||
let Component;
|
||||
let mock;
|
||||
const mockCiStatus = {
|
||||
details_path: '/root/hello-world/pipelines/1',
|
||||
favicon: 'canceled.ico',
|
||||
group: 'canceled',
|
||||
has_details: true,
|
||||
icon: 'status_canceled',
|
||||
label: 'canceled',
|
||||
text: 'canceled',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
Component = Vue.extend(commitPipelineStatus);
|
||||
});
|
||||
|
||||
describe('While polling pipeline data successfully', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onGet('/dummy/endpoint').reply(() => {
|
||||
const res = Promise.resolve([
|
||||
200,
|
||||
{
|
||||
pipelines: [
|
||||
{
|
||||
details: {
|
||||
status: mockCiStatus,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
return res;
|
||||
});
|
||||
vm = mountComponent(Component, {
|
||||
endpoint: '/dummy/endpoint',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.poll.stop();
|
||||
vm.$destroy();
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('shows the loading icon when polling is starting', done => {
|
||||
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.loading-container')).toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('contains a ciStatus when the polling is successful ', done => {
|
||||
setTimeout(() => {
|
||||
expect(vm.ciStatus).toEqual(mockCiStatus);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('contains a ci-status icon when polling is successful', done => {
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null);
|
||||
expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(
|
||||
`ci-status-icon-${mockCiStatus.group}`,
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When polling data was not successful', () => {
|
||||
beforeEach(() => {
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onGet('/dummy/endpoint').reply(502, {});
|
||||
vm = new Component({
|
||||
props: {
|
||||
endpoint: '/dummy/endpoint',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vm.poll.stop();
|
||||
vm.$destroy();
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it('calls an errorCallback', done => {
|
||||
spyOn(vm, 'errorCallback').and.callThrough();
|
||||
vm.$mount();
|
||||
setTimeout(() => {
|
||||
expect(vm.errorCallback.calls.count()).toEqual(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue