Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
b71a496c7a
commit
00a8c64ffd
|
@ -45,19 +45,16 @@ export const annotationsYAxis = {
|
||||||
* Fetched list of annotations are parsed into a
|
* Fetched list of annotations are parsed into a
|
||||||
* format the eCharts accepts to draw markLines
|
* format the eCharts accepts to draw markLines
|
||||||
*
|
*
|
||||||
* If Annotation is a single line, the `from` property
|
* If Annotation is a single line, the `starting_at` property
|
||||||
* has a value and the `to` is null. Because annotations
|
* has a value and the `ending_at` is null. Because annotations
|
||||||
* only supports lines the from value does not exist yet.
|
* only supports lines the `ending_at` value does not exist yet.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param {Object} annotation object
|
* @param {Object} annotation object
|
||||||
* @returns {Object} markLine object
|
* @returns {Object} markLine object
|
||||||
*/
|
*/
|
||||||
export const parseAnnotations = ({
|
export const parseAnnotations = ({ starting_at = '', color = colorValues.primaryColor }) => ({
|
||||||
from: annotationFrom = '',
|
xAxis: starting_at,
|
||||||
color = colorValues.primaryColor,
|
|
||||||
}) => ({
|
|
||||||
xAxis: annotationFrom,
|
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
color,
|
color,
|
||||||
},
|
},
|
||||||
|
@ -105,7 +102,7 @@ export const generateAnnotationsSeries = ({ deployments = [], annotations = [] }
|
||||||
const annotationsData = annotations.map(annotation => {
|
const annotationsData = annotations.map(annotation => {
|
||||||
return {
|
return {
|
||||||
name: 'annotations',
|
name: 'annotations',
|
||||||
value: [annotation.from, annotationsYAxisCoords.pos],
|
value: [annotation.starting_at, annotationsYAxisCoords.pos],
|
||||||
// style options
|
// style options
|
||||||
symbol: 'none',
|
symbol: 'none',
|
||||||
// metadata that are accessible in `formatTooltipText` method
|
// metadata that are accessible in `formatTooltipText` method
|
||||||
|
|
|
@ -4,8 +4,8 @@ query getAnnotations($projectPath: ID!) {
|
||||||
annotations: nodes {
|
annotations: nodes {
|
||||||
id
|
id
|
||||||
description
|
description
|
||||||
from
|
starting_at
|
||||||
to
|
ending_at
|
||||||
panelId
|
panelId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,11 @@ import CommitsList from '~/commits';
|
||||||
import GpgBadges from '~/gpg_badges';
|
import GpgBadges from '~/gpg_badges';
|
||||||
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
|
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
|
||||||
|
|
||||||
|
import mountCommits from '~/projects/commits';
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
|
new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
|
||||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||||
GpgBadges.fetch();
|
GpgBadges.fetch();
|
||||||
|
mountCommits(document.getElementById('js-author-dropdown'));
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
<script>
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import { mapState, mapActions } from 'vuex';
|
||||||
|
import {
|
||||||
|
GlNewDropdown,
|
||||||
|
GlNewDropdownHeader,
|
||||||
|
GlNewDropdownItem,
|
||||||
|
GlSearchBoxByType,
|
||||||
|
GlNewDropdownDivider,
|
||||||
|
GlTooltipDirective,
|
||||||
|
} from '@gitlab/ui';
|
||||||
|
import { redirectTo } from '~/lib/utils/url_utility';
|
||||||
|
import { urlParamsToObject } from '~/lib/utils/common_utils';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
|
const tooltipMessage = __('Searching by both author and message is currently not supported.');
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AuthorSelect',
|
||||||
|
components: {
|
||||||
|
GlNewDropdown,
|
||||||
|
GlNewDropdownHeader,
|
||||||
|
GlNewDropdownItem,
|
||||||
|
GlSearchBoxByType,
|
||||||
|
GlNewDropdownDivider,
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
GlTooltip: GlTooltipDirective,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
projectCommitsEl: {
|
||||||
|
type: HTMLDivElement,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
hasSearchParam: false,
|
||||||
|
searchTerm: '',
|
||||||
|
authorInput: '',
|
||||||
|
currentAuthor: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(['commitsPath', 'commitsAuthors']),
|
||||||
|
dropdownText() {
|
||||||
|
return this.currentAuthor || __('Author');
|
||||||
|
},
|
||||||
|
tooltipTitle() {
|
||||||
|
return this.hasSearchParam && tooltipMessage;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchAuthors();
|
||||||
|
const params = urlParamsToObject(window.location.search);
|
||||||
|
const { search: searchParam, author: authorParam } = params;
|
||||||
|
const commitsSearchInput = this.projectCommitsEl.querySelector('#commits-search');
|
||||||
|
|
||||||
|
if (authorParam) {
|
||||||
|
commitsSearchInput.setAttribute('disabled', true);
|
||||||
|
commitsSearchInput.setAttribute('data-toggle', 'tooltip');
|
||||||
|
commitsSearchInput.setAttribute('title', tooltipMessage);
|
||||||
|
this.currentAuthor = authorParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchParam) {
|
||||||
|
this.hasSearchParam = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
commitsSearchInput.addEventListener(
|
||||||
|
'keyup',
|
||||||
|
debounce(event => this.setSearchParam(event.target.value), 500), // keyup & time is to match effect of "filter by commit message"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(['fetchAuthors']),
|
||||||
|
selectAuthor(author) {
|
||||||
|
const { name: user } = author || {};
|
||||||
|
|
||||||
|
// Follow up issue "Remove usage of $.fadeIn from the codebase"
|
||||||
|
// > https://gitlab.com/gitlab-org/gitlab/-/issues/214395
|
||||||
|
|
||||||
|
// Follow up issue "Refactor commit list to a Vue Component"
|
||||||
|
// To resolving mixing Vue + Vanilla JS
|
||||||
|
// > https://gitlab.com/gitlab-org/gitlab/-/issues/214010
|
||||||
|
const commitListElement = this.projectCommitsEl.querySelector('#commits-list');
|
||||||
|
|
||||||
|
// To mimick effect of "filter by commit message"
|
||||||
|
commitListElement.style.opacity = 0.5;
|
||||||
|
commitListElement.style.transition = 'opacity 200ms';
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return redirectTo(this.commitsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirectTo(`${this.commitsPath}?author=${user}`);
|
||||||
|
},
|
||||||
|
searchAuthors() {
|
||||||
|
this.fetchAuthors(this.authorInput);
|
||||||
|
},
|
||||||
|
setSearchParam(value) {
|
||||||
|
this.hasSearchParam = Boolean(value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="dropdownContainer" v-gl-tooltip :title="tooltipTitle" :disabled="!hasSearchParam">
|
||||||
|
<gl-new-dropdown
|
||||||
|
:text="dropdownText"
|
||||||
|
:disabled="hasSearchParam"
|
||||||
|
class="gl-dropdown w-100 mt-2 mt-sm-0"
|
||||||
|
>
|
||||||
|
<gl-new-dropdown-header>
|
||||||
|
{{ __('Search by author') }}
|
||||||
|
</gl-new-dropdown-header>
|
||||||
|
<gl-new-dropdown-divider />
|
||||||
|
<gl-search-box-by-type
|
||||||
|
v-model.trim="authorInput"
|
||||||
|
class="m-2"
|
||||||
|
:placeholder="__('Search')"
|
||||||
|
@input="searchAuthors"
|
||||||
|
/>
|
||||||
|
<gl-new-dropdown-item :is-checked="!currentAuthor" @click="selectAuthor(null)">
|
||||||
|
{{ __('Any Author') }}
|
||||||
|
</gl-new-dropdown-item>
|
||||||
|
<gl-new-dropdown-divider />
|
||||||
|
<gl-new-dropdown-item
|
||||||
|
v-for="author in commitsAuthors"
|
||||||
|
:key="author.id"
|
||||||
|
:is-checked="author.name === currentAuthor"
|
||||||
|
:avatar-url="author.avatar_url"
|
||||||
|
:secondary-text="author.username"
|
||||||
|
@click="selectAuthor(author)"
|
||||||
|
>
|
||||||
|
{{ author.name }}
|
||||||
|
</gl-new-dropdown-item>
|
||||||
|
</gl-new-dropdown>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -0,0 +1,26 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
import AuthorSelectApp from './components/author_select.vue';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
export default el => {
|
||||||
|
if (!el) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch('setInitialData', el.dataset);
|
||||||
|
|
||||||
|
return new Vue({
|
||||||
|
el,
|
||||||
|
store,
|
||||||
|
render(h) {
|
||||||
|
return h(AuthorSelectApp, {
|
||||||
|
props: {
|
||||||
|
projectCommitsEl: document.querySelector('.js-project-commits-show'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
import * as types from './mutation_types';
|
||||||
|
import axios from '~/lib/utils/axios_utils';
|
||||||
|
import createFlash from '~/flash';
|
||||||
|
import { __ } from '~/locale';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
setInitialData({ commit }, data) {
|
||||||
|
commit(types.SET_INITIAL_DATA, data);
|
||||||
|
},
|
||||||
|
receiveAuthorsSuccess({ commit }, authors) {
|
||||||
|
commit(types.COMMITS_AUTHORS, authors);
|
||||||
|
},
|
||||||
|
receiveAuthorsError() {
|
||||||
|
createFlash(__('An error occurred fetching the project authors.'));
|
||||||
|
},
|
||||||
|
fetchAuthors({ dispatch, state }, author = null) {
|
||||||
|
const { projectId } = state;
|
||||||
|
const path = '/autocomplete/users.json';
|
||||||
|
|
||||||
|
return axios
|
||||||
|
.get(path, {
|
||||||
|
params: {
|
||||||
|
project_id: projectId,
|
||||||
|
active: true,
|
||||||
|
search: author,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ data }) => dispatch('receiveAuthorsSuccess', data))
|
||||||
|
.catch(() => dispatch('receiveAuthorsError'));
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import Vue from 'vue';
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
import actions from './actions';
|
||||||
|
import mutations from './mutations';
|
||||||
|
import state from './state';
|
||||||
|
|
||||||
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
export const createStore = () => ({
|
||||||
|
actions,
|
||||||
|
mutations,
|
||||||
|
state: state(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default new Vuex.Store(createStore());
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const SET_INITIAL_DATA = 'SET_INITIAL_DATA';
|
||||||
|
export const COMMITS_AUTHORS = 'COMMITS_AUTHORS';
|
|
@ -0,0 +1,10 @@
|
||||||
|
import * as types from './mutation_types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
[types.SET_INITIAL_DATA](state, data) {
|
||||||
|
Object.assign(state, data);
|
||||||
|
},
|
||||||
|
[types.COMMITS_AUTHORS](state, data) {
|
||||||
|
state.commitsAuthors = data;
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default () => ({
|
||||||
|
commitsPath: null,
|
||||||
|
projectId: null,
|
||||||
|
commitsAuthors: [],
|
||||||
|
});
|
|
@ -317,7 +317,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
// Temporary fix to ensure tick is aligned
|
||||||
|
// Follow up Issue to remove after the GlNewDropdownItem component is fixed
|
||||||
|
// > https://gitlab.com/gitlab-org/gitlab/-/issues/213948
|
||||||
|
li:not(.gl-new-dropdown-item) .dropdown-item {
|
||||||
@include dropdown-link;
|
@include dropdown-link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
|
|
||||||
%ul.breadcrumb.repo-breadcrumb
|
%ul.breadcrumb.repo-breadcrumb
|
||||||
= commits_breadcrumbs
|
= commits_breadcrumbs
|
||||||
.tree-controls.d-none.d-sm-none.d-md-block<
|
#js-author-dropdown{ data: { 'commits_path': project_commits_path(@project), 'project_id': @project.id } }
|
||||||
|
.tree-controls.d-none.d-sm-none.d-md-block
|
||||||
- if @merge_request.present?
|
- if @merge_request.present?
|
||||||
.control
|
.control
|
||||||
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
|
= link_to _("View open merge request"), project_merge_request_path(@project, @merge_request), class: 'btn'
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add ability to filter commits by author
|
||||||
|
merge_request: 28509
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Add rake task to update x509 signatures
|
||||||
|
merge_request: 28406
|
||||||
|
author: Roger Meier
|
||||||
|
type: added
|
|
@ -147,7 +147,12 @@ released.
|
||||||
#### TLS enabled
|
#### TLS enabled
|
||||||
|
|
||||||
NOTE: **Note**
|
NOTE: **Note**
|
||||||
This requires GitLab Runner 11.11 or higher.
|
Requires GitLab Runner 11.11 or later, but is not supported if GitLab
|
||||||
|
Runner is installed using the [Helm
|
||||||
|
chart](https://docs.gitlab.com/runner/install/kubernetes.html). See the
|
||||||
|
[related
|
||||||
|
issue](https://gitlab.com/gitlab-org/charts/gitlab-runner/issues/83) for
|
||||||
|
details.
|
||||||
|
|
||||||
The Docker daemon supports connection over TLS and it's done by default
|
The Docker daemon supports connection over TLS and it's done by default
|
||||||
for Docker 19.03.8 or higher. This is the **suggested** way to use the
|
for Docker 19.03.8 or higher. This is the **suggested** way to use the
|
||||||
|
|
|
@ -1264,7 +1264,9 @@ osx job:
|
||||||
|
|
||||||
`allow_failure` allows a job to fail without impacting the rest of the CI
|
`allow_failure` allows a job to fail without impacting the rest of the CI
|
||||||
suite.
|
suite.
|
||||||
The default value is `false`, except for [manual](#whenmanual) jobs.
|
The default value is `false`, except for [manual](#whenmanual) jobs using the
|
||||||
|
`when: manual` syntax, unless using [`rules:`](#rules) syntax, where all jobs
|
||||||
|
default to false, *including* `when: manual` jobs.
|
||||||
|
|
||||||
When enabled and the job fails, the job will show an orange warning in the UI.
|
When enabled and the job fails, the job will show an orange warning in the UI.
|
||||||
However, the logical flow of the pipeline will consider the job a
|
However, the logical flow of the pipeline will consider the job a
|
||||||
|
@ -1379,14 +1381,17 @@ manual action by clicking a _play_ button.
|
||||||
|
|
||||||
When a pipeline is blocked, it will not be merged if Merge When Pipeline Succeeds
|
When a pipeline is blocked, it will not be merged if Merge When Pipeline Succeeds
|
||||||
is set. Blocked pipelines also do have a special status, called _manual_.
|
is set. Blocked pipelines also do have a special status, called _manual_.
|
||||||
Manual actions are non-blocking by default. If you want to make manual action
|
When the `when:manual` syntax is used, manual actions are non-blocking by
|
||||||
blocking, it is necessary to add `allow_failure: false` to the job's definition
|
default. If you want to make manual action blocking, it is necessary to add
|
||||||
in `.gitlab-ci.yml`.
|
`allow_failure: false` to the job's definition in `.gitlab-ci.yml`.
|
||||||
|
|
||||||
Optional manual actions have `allow_failure: true` set by default and their
|
Optional manual actions have `allow_failure: true` set by default and their
|
||||||
Statuses do not contribute to the overall pipeline status. So, if a manual
|
Statuses do not contribute to the overall pipeline status. So, if a manual
|
||||||
action fails, the pipeline will eventually succeed.
|
action fails, the pipeline will eventually succeed.
|
||||||
|
|
||||||
|
NOTE: **Note:**
|
||||||
|
When using [`rules:`](#rules), `allow_failure` defaults to `false`, including for manual jobs.
|
||||||
|
|
||||||
Manual actions are considered to be write actions, so permissions for
|
Manual actions are considered to be write actions, so permissions for
|
||||||
[protected branches](../../user/project/protected_branches.md) are used when
|
[protected branches](../../user/project/protected_branches.md) are used when
|
||||||
a user wants to trigger an action. In other words, in order to trigger a manual
|
a user wants to trigger an action. In other words, in order to trigger a manual
|
||||||
|
|
|
@ -36,3 +36,4 @@ The following are available Rake tasks:
|
||||||
| [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. |
|
| [Uploads sanitize](../administration/raketasks/uploads/sanitize.md) | Remove EXIF data from images uploaded to earlier versions of GitLab. |
|
||||||
| [User management](user_management.md) | Perform user management tasks. |
|
| [User management](user_management.md) | Perform user management tasks. |
|
||||||
| [Webhooks administration](web_hooks.md) | Maintain project Webhooks. |
|
| [Webhooks administration](web_hooks.md) | Maintain project Webhooks. |
|
||||||
|
| [X509 signatures](x509_signatures.md) | Update x509 commit signatures, useful if certificate store has changed. |
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# X509 signatures
|
||||||
|
|
||||||
|
When [signing commits with x509](../user/project/repository/x509_signed_commits/index.md)
|
||||||
|
the trust anchor might change and the signatures stored within the database have
|
||||||
|
to be updated.
|
||||||
|
|
||||||
|
## Update all x509 signatures
|
||||||
|
|
||||||
|
This task loops through all X509 signed commits and updates their verification
|
||||||
|
based on current certificate store.
|
||||||
|
|
||||||
|
**Omnibus Installation**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo gitlab-rake gitlab:x509:update_signatures
|
||||||
|
```
|
||||||
|
|
||||||
|
**Source Installation**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -u git -H bundle exec rake gitlab:x509:update_signatures RAILS_ENV=production
|
||||||
|
```
|
|
@ -8,8 +8,8 @@ GitLab offers tiers of features. Your subscription determines which tier you hav
|
||||||
|
|
||||||
GitLab provides special subscriptions to participants in the [GitLab Education Program](https://about.gitlab.com/solutions/education/) and [GitLab Open Source Program](https://about.gitlab.com/solutions/open-source/). For details on obtaining and renewing these subscriptions, see:
|
GitLab provides special subscriptions to participants in the [GitLab Education Program](https://about.gitlab.com/solutions/education/) and [GitLab Open Source Program](https://about.gitlab.com/solutions/open-source/). For details on obtaining and renewing these subscriptions, see:
|
||||||
|
|
||||||
- [GitLab Education Program subscriptions](#gitlab-education-program-subscriptions)
|
- [GitLab for Education subscriptions](#gitlab-for-education-subscriptions)
|
||||||
- [GitLab Open Source Program subscriptions](#gitlab-open-source-program-subscriptions)
|
- [GitLab for Open Source subscriptions](#gitlab-for-open-source-subscriptions)
|
||||||
|
|
||||||
## Choosing a GitLab subscription
|
## Choosing a GitLab subscription
|
||||||
|
|
||||||
|
@ -493,9 +493,9 @@ Learn more about:
|
||||||
- The tiers of [GitLab Support](https://about.gitlab.com/support/).
|
- The tiers of [GitLab Support](https://about.gitlab.com/support/).
|
||||||
- [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new).
|
- [Submit a request via the Support Portal](https://support.gitlab.com/hc/en-us/requests/new).
|
||||||
|
|
||||||
## GitLab Education Program subscriptions
|
## GitLab for Education subscriptions
|
||||||
|
|
||||||
To renew a [GitLab Education Program](https://about.gitlab.com/solutions/education/) subscription, send an email to `education@gitlab.com` with the following information:
|
To renew a [GitLab for Education](https://about.gitlab.com/solutions/education/) subscription, send an email to `education@gitlab.com` with the following information:
|
||||||
|
|
||||||
1. The number of seats for the renewal. You can add seats if needed.
|
1. The number of seats for the renewal. You can add seats if needed.
|
||||||
1. The use case for the license. Specifically, we need verification that the use meets the conditions of the [End User License Agreement](https://about.gitlab.com/terms/#edu-oss). Note that university infrastructure operations and information technology operations don't fall within the stated terms of the Education Program. For details, see the [Education FAQ](https://about.gitlab.com/solutions/education/#FAQ).
|
1. The use case for the license. Specifically, we need verification that the use meets the conditions of the [End User License Agreement](https://about.gitlab.com/terms/#edu-oss). Note that university infrastructure operations and information technology operations don't fall within the stated terms of the Education Program. For details, see the [Education FAQ](https://about.gitlab.com/solutions/education/#FAQ).
|
||||||
|
@ -503,9 +503,9 @@ To renew a [GitLab Education Program](https://about.gitlab.com/solutions/educati
|
||||||
|
|
||||||
After we receive the above information, we will process the request and return a renewal quote for signature. Please allow a minimum of 2 business days for return. Email us at `education@gitlab.com` with any questions.
|
After we receive the above information, we will process the request and return a renewal quote for signature. Please allow a minimum of 2 business days for return. Email us at `education@gitlab.com` with any questions.
|
||||||
|
|
||||||
## GitLab Open Source Program subscriptions
|
## GitLab for Open Source subscriptions
|
||||||
|
|
||||||
All requests for our GitLab Open Source program, including subscription renewals, must be made by using the [Open Source Program](https://about.gitlab.com/solutions/open-source/program/) application process. If you have any questions, send an email to `opensource@gitlab.com` for assistance.
|
All [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/program/) requests, including subscription renewals, must be made by using the application process. If you have any questions, send an email to `opensource@gitlab.com` for assistance.
|
||||||
|
|
||||||
<!-- ## Troubleshooting
|
<!-- ## Troubleshooting
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,10 @@ also requires the GitLab Runner 11.5 or later. For earlier versions, use the
|
||||||
|
|
||||||
This example shows how to run Code Quality on your code by using GitLab CI/CD and Docker.
|
This example shows how to run Code Quality on your code by using GitLab CI/CD and Docker.
|
||||||
|
|
||||||
First, you need GitLab Runner with
|
First, you need GitLab Runner with:
|
||||||
[docker-in-docker executor](../../../ci/docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor).
|
|
||||||
|
- The [docker-in-docker executor](../../../ci/docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor).
|
||||||
|
- Enough disk space to handle generated Code Quality files. For example on the [GitLab project](https://gitlab.com/gitlab-org/gitlab) the files are approximately 7 GB.
|
||||||
|
|
||||||
Once you set up the Runner, include the CodeQuality template in your CI config:
|
Once you set up the Runner, include the CodeQuality template in your CI config:
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
module Gitlab
|
module Gitlab
|
||||||
class GitAccessWiki < GitAccess
|
class GitAccessWiki < GitAccess
|
||||||
prepend_if_ee('EE::Gitlab::GitAccessWiki') # rubocop: disable Cop/InjectEnterpriseEditionModule
|
|
||||||
|
|
||||||
ERROR_MESSAGES = {
|
ERROR_MESSAGES = {
|
||||||
read_only: "You can't push code to a read-only GitLab instance.",
|
read_only: "You can't push code to a read-only GitLab instance.",
|
||||||
write_to_wiki: "You are not allowed to write to this project's wiki."
|
write_to_wiki: "You are not allowed to write to this project's wiki."
|
||||||
|
@ -33,10 +31,8 @@ module Gitlab
|
||||||
ERROR_MESSAGES[:read_only]
|
ERROR_MESSAGES[:read_only]
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
def container
|
||||||
|
project.wiki
|
||||||
def repository
|
|
||||||
project.wiki.repository
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
require 'logger'
|
||||||
|
|
||||||
|
desc "GitLab | X509 | Update signatures when certificate store has changed"
|
||||||
|
namespace :gitlab do
|
||||||
|
namespace :x509 do
|
||||||
|
task update_signatures: :environment do
|
||||||
|
update_certificates
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_certificates
|
||||||
|
logger = Logger.new(STDOUT)
|
||||||
|
|
||||||
|
unless X509CommitSignature.exists?
|
||||||
|
logger.info("Unable to find any x509 commit signatures. Exiting.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
logger.info("Start to update x509 commit signatures")
|
||||||
|
|
||||||
|
X509CommitSignature.find_each do |sig|
|
||||||
|
sig.x509_commit&.update_signature!(sig)
|
||||||
|
end
|
||||||
|
|
||||||
|
logger.info("End update x509 commit signatures")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1289,6 +1289,9 @@ msgstr ""
|
||||||
msgid "Added at"
|
msgid "Added at"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Added for this merge request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Added in this version"
|
msgid "Added in this version"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -1855,6 +1858,9 @@ msgstr ""
|
||||||
msgid "An error occurred fetching the dropdown data."
|
msgid "An error occurred fetching the dropdown data."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "An error occurred fetching the project authors."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "An error occurred previewing the blob"
|
msgid "An error occurred previewing the blob"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -2173,6 +2179,9 @@ msgstr ""
|
||||||
msgid "Any"
|
msgid "Any"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Any Author"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Any Label"
|
msgid "Any Label"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -14195,6 +14204,9 @@ msgstr ""
|
||||||
msgid "OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels."
|
msgid "OutdatedBrowser|You can provide feedback %{feedback_link_start}on this issue%{feedback_link_end} or via your usual support channels."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Overridden"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Overview"
|
msgid "Overview"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -17691,6 +17703,9 @@ msgstr ""
|
||||||
msgid "Search branches and tags"
|
msgid "Search branches and tags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Search by author"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Search files"
|
msgid "Search files"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -17851,6 +17866,9 @@ msgid_plural "SearchResults|wiki results"
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "Searching by both author and message is currently not supported."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Seat Link"
|
msgid "Seat Link"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -213,23 +213,23 @@ export const deploymentData = [
|
||||||
export const annotationsData = [
|
export const annotationsData = [
|
||||||
{
|
{
|
||||||
id: 'gid://gitlab/Metrics::Dashboard::Annotation/1',
|
id: 'gid://gitlab/Metrics::Dashboard::Annotation/1',
|
||||||
from: '2020-04-01T12:51:58.373Z',
|
starting_at: '2020-04-01T12:51:58.373Z',
|
||||||
to: null,
|
ending_at: null,
|
||||||
panelId: null,
|
panelId: null,
|
||||||
description: 'This is a test annotation',
|
description: 'This is a test annotation',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gid://gitlab/Metrics::Dashboard::Annotation/2',
|
id: 'gid://gitlab/Metrics::Dashboard::Annotation/2',
|
||||||
description: 'test annotation 2',
|
description: 'test annotation 2',
|
||||||
from: '2020-04-02T12:51:58.373Z',
|
starting_at: '2020-04-02T12:51:58.373Z',
|
||||||
to: null,
|
ending_at: null,
|
||||||
panelId: null,
|
panelId: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'gid://gitlab/Metrics::Dashboard::Annotation/3',
|
id: 'gid://gitlab/Metrics::Dashboard::Annotation/3',
|
||||||
description: 'test annotation 3',
|
description: 'test annotation 3',
|
||||||
from: '2020-04-04T12:51:58.373Z',
|
starting_at: '2020-04-04T12:51:58.373Z',
|
||||||
to: null,
|
ending_at: null,
|
||||||
panelId: null,
|
panelId: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||||
|
import Vuex from 'vuex';
|
||||||
|
import * as urlUtility from '~/lib/utils/url_utility';
|
||||||
|
import AuthorSelect from '~/projects/commits/components/author_select.vue';
|
||||||
|
import { createStore } from '~/projects/commits/store';
|
||||||
|
import {
|
||||||
|
GlNewDropdown,
|
||||||
|
GlNewDropdownHeader,
|
||||||
|
GlSearchBoxByType,
|
||||||
|
GlNewDropdownItem,
|
||||||
|
} from '@gitlab/ui';
|
||||||
|
|
||||||
|
const localVue = createLocalVue();
|
||||||
|
localVue.use(Vuex);
|
||||||
|
|
||||||
|
const commitsPath = 'author/search/url';
|
||||||
|
const currentAuthor = 'lorem';
|
||||||
|
const authors = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: currentAuthor,
|
||||||
|
username: 'ipsum',
|
||||||
|
avatar_url: 'some/url',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'lorem2',
|
||||||
|
username: 'ipsum2',
|
||||||
|
avatar_url: 'some/url/2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('Author Select', () => {
|
||||||
|
let store;
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
const createComponent = () => {
|
||||||
|
setFixtures(`
|
||||||
|
<div class="js-project-commits-show">
|
||||||
|
<input id="commits-search" type="text" />
|
||||||
|
<div id="commits-list"></div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
wrapper = shallowMount(AuthorSelect, {
|
||||||
|
localVue,
|
||||||
|
store: new Vuex.Store(store),
|
||||||
|
propsData: {
|
||||||
|
projectCommitsEl: document.querySelector('.js-project-commits-show'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
store = createStore();
|
||||||
|
store.actions.fetchAuthors = jest.fn();
|
||||||
|
|
||||||
|
createComponent();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
wrapper.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const findDropdownContainer = () => wrapper.find({ ref: 'dropdownContainer' });
|
||||||
|
const findDropdown = () => wrapper.find(GlNewDropdown);
|
||||||
|
const findDropdownHeader = () => wrapper.find(GlNewDropdownHeader);
|
||||||
|
const findSearchBox = () => wrapper.find(GlSearchBoxByType);
|
||||||
|
const findDropdownItems = () => wrapper.findAll(GlNewDropdownItem);
|
||||||
|
|
||||||
|
describe('user is searching via "filter by commit message"', () => {
|
||||||
|
it('disables dropdown container', () => {
|
||||||
|
wrapper.setData({ hasSearchParam: true });
|
||||||
|
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(findDropdownContainer().attributes('disabled')).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has correct tooltip message', () => {
|
||||||
|
wrapper.setData({ hasSearchParam: true });
|
||||||
|
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(findDropdownContainer().attributes('title')).toBe(
|
||||||
|
'Searching by both author and message is currently not supported.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disables dropdown', () => {
|
||||||
|
wrapper.setData({ hasSearchParam: false });
|
||||||
|
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(findDropdown().attributes('disabled')).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hasSearchParam if user types a truthy string', () => {
|
||||||
|
wrapper.vm.setSearchParam('false');
|
||||||
|
|
||||||
|
expect(wrapper.vm.hasSearchParam).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dropdown', () => {
|
||||||
|
it('displays correct default text', () => {
|
||||||
|
expect(findDropdown().attributes('text')).toBe('Author');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the current selected author', () => {
|
||||||
|
wrapper.setData({ currentAuthor });
|
||||||
|
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(findDropdown().attributes('text')).toBe(currentAuthor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays correct header text', () => {
|
||||||
|
expect(findDropdownHeader().text()).toBe('Search by author');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not have popover text by default', () => {
|
||||||
|
expect(wrapper.attributes('title')).not.toExist();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dropdown search box', () => {
|
||||||
|
it('has correct placeholder', () => {
|
||||||
|
expect(findSearchBox().attributes('placeholder')).toBe('Search');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetch authors on input change', () => {
|
||||||
|
const authorName = 'lorem';
|
||||||
|
findSearchBox().vm.$emit('input', authorName);
|
||||||
|
|
||||||
|
expect(store.actions.fetchAuthors).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
authorName,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dropdown list', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
store.state.commitsAuthors = authors;
|
||||||
|
store.state.commitsPath = commitsPath;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has a "Any Author" as the first list item', () => {
|
||||||
|
expect(
|
||||||
|
findDropdownItems()
|
||||||
|
.at(0)
|
||||||
|
.text(),
|
||||||
|
).toBe('Any Author');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays the project authors', () => {
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(findDropdownItems()).toHaveLength(authors.length + 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has the correct props', () => {
|
||||||
|
const [{ avatar_url, username }] = authors;
|
||||||
|
const result = {
|
||||||
|
avatarUrl: avatar_url,
|
||||||
|
secondaryText: username,
|
||||||
|
isChecked: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper.setData({ currentAuthor });
|
||||||
|
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(
|
||||||
|
findDropdownItems()
|
||||||
|
.at(1)
|
||||||
|
.props(),
|
||||||
|
).toEqual(expect.objectContaining(result));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("display the author's name", () => {
|
||||||
|
return wrapper.vm.$nextTick().then(() => {
|
||||||
|
expect(
|
||||||
|
findDropdownItems()
|
||||||
|
.at(1)
|
||||||
|
.text(),
|
||||||
|
).toBe(currentAuthor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes selected author to redirectPath', () => {
|
||||||
|
const redirectToUrl = `${commitsPath}?author=${currentAuthor}`;
|
||||||
|
const spy = jest.spyOn(urlUtility, 'redirectTo');
|
||||||
|
spy.mockImplementation(() => 'mock');
|
||||||
|
|
||||||
|
findDropdownItems()
|
||||||
|
.at(1)
|
||||||
|
.vm.$emit('click');
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(redirectToUrl);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not pass any author to redirectPath', () => {
|
||||||
|
const redirectToUrl = commitsPath;
|
||||||
|
const spy = jest.spyOn(urlUtility, 'redirectTo');
|
||||||
|
spy.mockImplementation();
|
||||||
|
|
||||||
|
findDropdownItems()
|
||||||
|
.at(0)
|
||||||
|
.vm.$emit('click');
|
||||||
|
expect(spy).toHaveBeenCalledWith(redirectToUrl);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,69 @@
|
||||||
|
import axios from 'axios';
|
||||||
|
import MockAdapter from 'axios-mock-adapter';
|
||||||
|
import * as types from '~/projects/commits/store/mutation_types';
|
||||||
|
import testAction from 'helpers/vuex_action_helper';
|
||||||
|
import actions from '~/projects/commits/store/actions';
|
||||||
|
import createState from '~/projects/commits/store/state';
|
||||||
|
import createFlash from '~/flash';
|
||||||
|
|
||||||
|
jest.mock('~/flash');
|
||||||
|
|
||||||
|
describe('Project commits actions', () => {
|
||||||
|
let state;
|
||||||
|
let mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
state = createState();
|
||||||
|
mock = new MockAdapter(axios);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setInitialData', () => {
|
||||||
|
it(`commits ${types.SET_INITIAL_DATA}`, () =>
|
||||||
|
testAction(actions.setInitialData, undefined, state, [{ type: types.SET_INITIAL_DATA }]));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('receiveAuthorsSuccess', () => {
|
||||||
|
it(`commits ${types.COMMITS_AUTHORS}`, () =>
|
||||||
|
testAction(actions.receiveAuthorsSuccess, undefined, state, [
|
||||||
|
{ type: types.COMMITS_AUTHORS },
|
||||||
|
]));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('shows a flash message when there is an error', () => {
|
||||||
|
it('creates a flash', () => {
|
||||||
|
const mockDispatchContext = { dispatch: () => {}, commit: () => {}, state };
|
||||||
|
actions.receiveAuthorsError(mockDispatchContext);
|
||||||
|
|
||||||
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||||
|
expect(createFlash).toHaveBeenCalledWith('An error occurred fetching the project authors.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchAuthors', () => {
|
||||||
|
it('dispatches request/receive', () => {
|
||||||
|
const path = '/autocomplete/users.json';
|
||||||
|
state.projectId = '8';
|
||||||
|
const data = [{ id: 1 }];
|
||||||
|
|
||||||
|
mock.onGet(path).replyOnce(200, data);
|
||||||
|
testAction(
|
||||||
|
actions.fetchAuthors,
|
||||||
|
null,
|
||||||
|
state,
|
||||||
|
[],
|
||||||
|
[{ type: 'receiveAuthorsSuccess', payload: data }],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dispatches request/receive on error', () => {
|
||||||
|
const path = '/autocomplete/users.json';
|
||||||
|
mock.onGet(path).replyOnce(500);
|
||||||
|
|
||||||
|
testAction(actions.fetchAuthors, null, state, [], [{ type: 'receiveAuthorsError' }]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,43 @@
|
||||||
|
import * as types from '~/projects/commits/store/mutation_types';
|
||||||
|
import mutations from '~/projects/commits/store/mutations';
|
||||||
|
import createState from '~/projects/commits/store/state';
|
||||||
|
|
||||||
|
describe('Project commits mutations', () => {
|
||||||
|
let state;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
state = createState();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
state = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`${types.SET_INITIAL_DATA}`, () => {
|
||||||
|
it('sets initial data', () => {
|
||||||
|
state.commitsPath = null;
|
||||||
|
state.projectId = null;
|
||||||
|
state.commitsAuthors = [];
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
commitsPath: 'some/path',
|
||||||
|
projectId: '8',
|
||||||
|
};
|
||||||
|
|
||||||
|
mutations[types.SET_INITIAL_DATA](state, data);
|
||||||
|
|
||||||
|
expect(state).toEqual(expect.objectContaining(data));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`${types.COMMITS_AUTHORS}`, () => {
|
||||||
|
it('sets commitsAuthors', () => {
|
||||||
|
const authors = [{ id: 1 }, { id: 2 }];
|
||||||
|
state.commitsAuthors = [];
|
||||||
|
|
||||||
|
mutations[types.COMMITS_AUTHORS](state, authors);
|
||||||
|
|
||||||
|
expect(state.commitsAuthors).toEqual(authors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -21,7 +21,7 @@ describe Gitlab::ImportExport::JSON::NdjsonReader do
|
||||||
describe '#exist?' do
|
describe '#exist?' do
|
||||||
subject { ndjson_reader.exist? }
|
subject { ndjson_reader.exist? }
|
||||||
|
|
||||||
context 'given valid dir_path', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/213843' do
|
context 'given valid dir_path' do
|
||||||
let(:dir_path) { fixture }
|
let(:dir_path) { fixture }
|
||||||
|
|
||||||
it { is_expected.to be true }
|
it { is_expected.to be true }
|
||||||
|
|
|
@ -15,15 +15,17 @@ module ImportExport
|
||||||
export_path = [prefix, 'spec', 'fixtures', 'lib', 'gitlab', 'import_export', name].compact
|
export_path = [prefix, 'spec', 'fixtures', 'lib', 'gitlab', 'import_export', name].compact
|
||||||
export_path = File.join(*export_path)
|
export_path = File.join(*export_path)
|
||||||
|
|
||||||
extract_archive(export_path, 'tree.tar.gz')
|
if File.exist?(File.join(export_path, 'tree.tar.gz'))
|
||||||
|
extract_archive(export_path, 'tree.tar.gz')
|
||||||
|
end
|
||||||
|
|
||||||
allow_any_instance_of(Gitlab::ImportExport).to receive(:export_path) { export_path }
|
allow_any_instance_of(Gitlab::ImportExport).to receive(:export_path) { export_path }
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_archive(path, archive)
|
def extract_archive(path, archive)
|
||||||
if File.exist?(File.join(path, archive))
|
output, exit_status = Gitlab::Popen.popen(["cd #{path}; tar xzf #{archive}"])
|
||||||
system("cd #{path}; tar xzvf #{archive} &> /dev/null")
|
|
||||||
end
|
raise "Failed to extract archive. Output: #{output}" unless exit_status.zero?
|
||||||
end
|
end
|
||||||
|
|
||||||
def cleanup_artifacts_from_extract_archive(name, prefix = nil)
|
def cleanup_artifacts_from_extract_archive(name, prefix = nil)
|
||||||
|
@ -31,7 +33,7 @@ module ImportExport
|
||||||
export_path = File.join(*export_path)
|
export_path = File.join(*export_path)
|
||||||
|
|
||||||
if File.exist?(File.join(export_path, 'tree.tar.gz'))
|
if File.exist?(File.join(export_path, 'tree.tar.gz'))
|
||||||
system("cd #{export_path}; rm -fr tree &> /dev/null")
|
system("cd #{export_path}; rm -fr tree")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rake_helper'
|
||||||
|
|
||||||
|
describe 'gitlab:x509 namespace rake task' do
|
||||||
|
before :all do
|
||||||
|
Rake.application.rake_require 'tasks/gitlab/x509/update'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'update_signatures' do
|
||||||
|
subject { run_rake_task('gitlab:x509:update_signatures') }
|
||||||
|
|
||||||
|
let(:project) { create :project, :repository, path: X509Helpers::User1.path }
|
||||||
|
let(:x509_signed_commit) { project.commit_by(oid: '189a6c924013fc3fe40d6f1ec1dc20214183bc97') }
|
||||||
|
let(:x509_commit) { Gitlab::X509::Commit.new(x509_signed_commit).signature }
|
||||||
|
|
||||||
|
it 'changes from unverified to verified if the certificate store contains the root certificate' do
|
||||||
|
x509_commit
|
||||||
|
|
||||||
|
store = OpenSSL::X509::Store.new
|
||||||
|
certificate = OpenSSL::X509::Certificate.new X509Helpers::User1.trust_cert
|
||||||
|
store.add_cert(certificate)
|
||||||
|
allow(OpenSSL::X509::Store).to receive(:new).and_return(store)
|
||||||
|
|
||||||
|
expect(x509_commit.verification_status).to eq('unverified')
|
||||||
|
expect_any_instance_of(Gitlab::X509::Commit).to receive(:update_signature!).and_call_original
|
||||||
|
|
||||||
|
subject
|
||||||
|
|
||||||
|
x509_commit.reload
|
||||||
|
expect(x509_commit.verification_status).to eq('verified')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns if no signature is available' do
|
||||||
|
expect_any_instance_of(Gitlab::X509::Commit) do |x509_commit|
|
||||||
|
expect(x509_commit).not_to receive(:update_signature!)
|
||||||
|
|
||||||
|
subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue