Commit 9996389e authored by Rémy Coutable's avatar Rémy Coutable

Merge branch 'ce-to-ee-2018-04-17' into 'master'

CE upstream - 2018-04-17 20:56 UTC

Closes gitaly#401 et gitlab-org/release/docs#9

See merge request gitlab-org/gitlab-ee!5399
parents f158a447 573c9fa0
<!--
# Read me first!
Create this issue under https://dev.gitlab.org/gitlab/gitlabhq
Set the title to: `[Security] Description of the original issue`
-->
### Prior to the security release
- [ ] Read the [security process for developers] if you are not familiar with it.
- [ ] Link to the original issue adding it to the [links section](#links)
- [ ] Run `scripts/security-harness` in the CE, EE, and/or Omnibus to prevent pushing to any remote besides `dev.gitlab.org`
- [ ] Create an MR targetting `org` `master`, prefixing your branch with `security-`
- [ ] Label your MR with the ~security label, prefix the title with `WIP: [master]`
- [ ] Add a link to the MR to the [links section](#links)
- [ ] Add a link to an EE MR if required
- [ ] Make sure the MR remains in-progress and gets approved after the review cycle, **but never merged**.
- [ ] Assign the MR to a RM once is reviewed and ready to be merged. Check the [RM list] to see who to ping.
#### Backports
- [ ] Once the MR is ready to be merged, create MRs targetting the last 3 releases
- [ ] At this point, it might be easy to squash the commits from the MR into one
- You can use the script `bin/secpick` instead of the following steps, to help you cherry-picking. See the [seckpick documentation]
- [ ] Create the branch `security-X-Y` from `X-Y-stable` if it doesn't exist (and make sure it's up to date with stable)
- [ ] Create each MR targetting the security branch `security-X-Y`
- [ ] Add the ~security label and prefix with the version `WIP: [X.Y]` the title of the MR
- [ ] Make sure all MRs have a link in the [links section](#links) and are assigned to a Release Manager.
[seckpick documentation]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/process.md#secpick-script
#### Documentation and final details
- [ ] Check the topic on #security to see when the next release is going ot happen and add a link to the [links section](#links)
- [ ] Find out the versions affected (the Git history of the files affected may help you with this) and add them to the [details section](#details)
- [ ] Fill in any upgrade notes that users may need to take into account in the [details section](#details)
- [ ] Add Yes/No and further details if needed to the migration and settings columns in the [details section](#details)
### Summary
#### Links
| Description | Link |
| -------- | -------- |
| Original issue | #TODO |
| Security release issue | #TODO |
| `master` MR | !TODO |
| `master` MR (EE) | !TODO |
| `Backport X.Y` MR | !TODO |
| `Backport X.Y` MR | !TODO |
| `Backport X.Y` MR | !TODO |
| `Backport X.Y` MR (EE) | !TODO |
| `Backport X.Y` MR (EE) | !TODO |
| `Backport X.Y` MR (EE) | !TODO |
#### Details
| Description | Details | Further details|
| -------- | -------- | -------- |
| Versions affected | X.Y | |
| Upgrade notes | | |
| GitLab Settings updated | Yes/No| |
| Migration required | Yes/No | |
[security process for developers]: https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/process.md
[RM list]: https://about.gitlab.com/release-managers/
/label ~security
...@@ -65,7 +65,7 @@ gem 'akismet', '~> 2.0' ...@@ -65,7 +65,7 @@ gem 'akismet', '~> 2.0'
# Two-factor authentication # Two-factor authentication
gem 'devise-two-factor', '~> 3.0.0' gem 'devise-two-factor', '~> 3.0.0'
gem 'rqrcode-rails3', '~> 0.1.7' gem 'rqrcode-rails3', '~> 0.1.7'
gem 'attr_encrypted', '~> 3.0.0' gem 'attr_encrypted', '~> 3.1.0'
gem 'u2f', '~> 0.2.1' gem 'u2f', '~> 0.2.1'
# GitLab Pages # GitLab Pages
......
...@@ -66,7 +66,7 @@ GEM ...@@ -66,7 +66,7 @@ GEM
unf unf
ast (2.4.0) ast (2.4.0)
atomic (1.1.99) atomic (1.1.99)
attr_encrypted (3.0.3) attr_encrypted (3.1.0)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
autoprefixer-rails (6.2.3) autoprefixer-rails (6.2.3)
...@@ -1033,7 +1033,7 @@ DEPENDENCIES ...@@ -1033,7 +1033,7 @@ DEPENDENCIES
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.2.0) asset_sync (~> 2.2.0)
attr_encrypted (~> 3.0.0) attr_encrypted (~> 3.1.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
aws-sdk aws-sdk
babosa (~> 1.0.2) babosa (~> 1.0.2)
......
...@@ -16,6 +16,7 @@ class DeleteModal { ...@@ -16,6 +16,7 @@ class DeleteModal {
bindEvents() { bindEvents() {
this.$toggleBtns.on('click', this.setModalData.bind(this)); this.$toggleBtns.on('click', this.setModalData.bind(this));
this.$confirmInput.on('input', this.setDeleteDisabled.bind(this)); this.$confirmInput.on('input', this.setDeleteDisabled.bind(this));
this.$deleteBtn.on('click', this.setDisableDeleteButton.bind(this));
} }
setModalData(e) { setModalData(e) {
...@@ -30,6 +31,16 @@ class DeleteModal { ...@@ -30,6 +31,16 @@ class DeleteModal {
this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName); this.$deleteBtn.attr('disabled', e.currentTarget.value !== this.branchName);
} }
setDisableDeleteButton(e) {
if (this.$deleteBtn.is('[disabled]')) {
e.preventDefault();
e.stopPropagation();
return false;
}
return true;
}
updateModal() { updateModal() {
this.$branchName.text(this.branchName); this.$branchName.text(this.branchName);
this.$confirmInput.val(''); this.$confirmInput.val('');
......
<script> <script>
import _ from 'underscore'; import _ from 'underscore';
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue'; import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue'; import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { import { APPLICATION_INSTALLED, INGRESS } from '../constants';
APPLICATION_INSTALLED,
INGRESS,
} from '../constants';
export default { export default {
components: { components: {
applicationRow, applicationRow,
clipboardButton, clipboardButton,
...@@ -43,10 +40,13 @@ ...@@ -43,10 +40,13 @@
computed: { computed: {
generalApplicationDescription() { generalApplicationDescription() {
return sprintf( return sprintf(
_.escape(s__( _.escape(
s__(
`ClusterIntegration|Install applications on your Kubernetes cluster. `ClusterIntegration|Install applications on your Kubernetes cluster.
Read more about %{helpLink}`, Read more about %{helpLink}`,
)), { ),
),
{
helpLink: `<a href="${this.helpPath}"> helpLink: `<a href="${this.helpPath}">
${_.escape(s__('ClusterIntegration|installing applications'))} ${_.escape(s__('ClusterIntegration|installing applications'))}
</a>`, </a>`,
...@@ -65,12 +65,15 @@ ...@@ -65,12 +65,15 @@
}, },
ingressDescription() { ingressDescription() {
const extraCostParagraph = sprintf( const extraCostParagraph = sprintf(
_.escape(s__( _.escape(
s__(
`ClusterIntegration|%{boldNotice} This will add some extra resources `ClusterIntegration|%{boldNotice} This will add some extra resources
like a load balancer, which may incur additional costs depending on like a load balancer, which may incur additional costs depending on
the hosting provider your Kubernetes cluster is installed on. If you are using GKE, the hosting provider your Kubernetes cluster is installed on. If you are using
you can %{pricingLink}.`, Google Kubernetes Engine, you can %{pricingLink}.`,
)), { ),
),
{
boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`, boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`,
pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer"> pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|check the pricing here'))}</a>`, ${_.escape(s__('ClusterIntegration|check the pricing here'))}</a>`,
...@@ -79,10 +82,13 @@ ...@@ -79,10 +82,13 @@
); );
const externalIpParagraph = sprintf( const externalIpParagraph = sprintf(
_.escape(s__( _.escape(
s__(
`ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS `ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS
at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`, at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`,
)), { ),
),
{
ingressHelpLink: `<a href="${this.ingressHelpPath}"> ingressHelpLink: `<a href="${this.ingressHelpPath}">
${_.escape(s__('ClusterIntegration|More information'))} ${_.escape(s__('ClusterIntegration|More information'))}
</a>`, </a>`,
...@@ -101,10 +107,13 @@ ...@@ -101,10 +107,13 @@
}, },
prometheusDescription() { prometheusDescription() {
return sprintf( return sprintf(
_.escape(s__( _.escape(
s__(
`ClusterIntegration|Prometheus is an open-source monitoring system `ClusterIntegration|Prometheus is an open-source monitoring system
with %{gitlabIntegrationLink} to monitor deployed applications.`, with %{gitlabIntegrationLink} to monitor deployed applications.`,
)), { ),
),
{
gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html" gitlabIntegrationLink: `<a href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
target="_blank" rel="noopener noreferrer"> target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`, ${_.escape(s__('ClusterIntegration|GitLab Integration'))}</a>`,
...@@ -113,7 +122,7 @@ ...@@ -113,7 +122,7 @@
); );
}, },
}, },
}; };
</script> </script>
<template> <template>
...@@ -205,7 +214,7 @@ ...@@ -205,7 +214,7 @@
> >
{{ s__(`ClusterIntegration|The IP address is in {{ s__(`ClusterIntegration|The IP address is in
the process of being assigned. Please check your Kubernetes the process of being assigned. Please check your Kubernetes
cluster or Quotas on GKE if it takes a long time.`) }} cluster or Quotas on Google Kubernetes Engine if it takes a long time.`) }}
<a <a
:href="ingressHelpPath" :href="ingressHelpPath"
......
...@@ -36,11 +36,11 @@ const router = new VueRouter({ ...@@ -36,11 +36,11 @@ const router = new VueRouter({
base: `${gon.relative_url_root}/-/ide/`, base: `${gon.relative_url_root}/-/ide/`,
routes: [ routes: [
{ {
path: '/project/:namespace/:project', path: '/project/:namespace/:project+',
component: EmptyRouterComponent, component: EmptyRouterComponent,
children: [ children: [
{ {
path: ':targetmode/:branch/*', path: ':targetmode(edit|tree|blob)/:branch/*',
component: EmptyRouterComponent, component: EmptyRouterComponent,
}, },
{ {
......
...@@ -17,12 +17,8 @@ export default { ...@@ -17,12 +17,8 @@ export default {
}); });
}, },
[types.SET_DIRECTORY_DATA](state, { data, treePath }) { [types.SET_DIRECTORY_DATA](state, { data, treePath }) {
Object.assign(state, { Object.assign(state.trees[treePath], {
trees: Object.assign(state.trees, {
[treePath]: {
tree: data, tree: data,
},
}),
}); });
}, },
[types.SET_LAST_COMMIT_URL](state, { tree = state, url }) { [types.SET_LAST_COMMIT_URL](state, { tree = state, url }) {
......
...@@ -19,7 +19,6 @@ import AjaxCache from '~/lib/utils/ajax_cache'; ...@@ -19,7 +19,6 @@ import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue'; import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight'; import syntaxHighlight from '~/syntax_highlight';
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue'; import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
import { __ } from '~/locale';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { getLocationHash } from './lib/utils/url_utility'; import { getLocationHash } from './lib/utils/url_utility';
import Flash from './flash'; import Flash from './flash';
...@@ -198,6 +197,8 @@ export default class Notes { ...@@ -198,6 +197,8 @@ export default class Notes {
); );
this.$wrapperEl.on('click', '.js-toggle-lazy-diff', this.loadLazyDiff); this.$wrapperEl.on('click', '.js-toggle-lazy-diff', this.loadLazyDiff);
this.$wrapperEl.on('click', '.js-toggle-lazy-diff-retry-button', this.onClickRetryLazyLoad.bind(this));
// fetch notes when tab becomes visible // fetch notes when tab becomes visible
this.$wrapperEl.on('visibilitychange', this.visibilityChange); this.$wrapperEl.on('visibilitychange', this.visibilityChange);
// when issue status changes, we need to refresh data // when issue status changes, we need to refresh data
...@@ -244,6 +245,7 @@ export default class Notes { ...@@ -244,6 +245,7 @@ export default class Notes {
this.$wrapperEl.off('click', '.js-comment-resolve-button'); this.$wrapperEl.off('click', '.js-comment-resolve-button');
this.$wrapperEl.off('click', '.system-note-commit-list-toggler'); this.$wrapperEl.off('click', '.system-note-commit-list-toggler');
this.$wrapperEl.off('click', '.js-toggle-lazy-diff'); this.$wrapperEl.off('click', '.js-toggle-lazy-diff');
this.$wrapperEl.off('click', '.js-toggle-lazy-diff-retry-button');
this.$wrapperEl.off('ajax:success', '.js-main-target-form'); this.$wrapperEl.off('ajax:success', '.js-main-target-form');
this.$wrapperEl.off('ajax:success', '.js-discussion-note-form'); this.$wrapperEl.off('ajax:success', '.js-discussion-note-form');
this.$wrapperEl.off('ajax:complete', '.js-main-target-form'); this.$wrapperEl.off('ajax:complete', '.js-main-target-form');
...@@ -1431,16 +1433,15 @@ export default class Notes { ...@@ -1431,16 +1433,15 @@ export default class Notes {
syntaxHighlight(fileHolder); syntaxHighlight(fileHolder);
} }
static renderDiffError($container) { onClickRetryLazyLoad(e) {
$container.find('.line_content').html( const $retryButton = $(e.currentTarget);
$(`
<div class="nothing-here-block"> $retryButton.prop('disabled', true);
${__(
'Unable to load the diff.', return this.loadLazyDiff(e)
)} <a class="js-toggle-lazy-diff" href="javascript:void(0)">Try again</a>? .then(() => {
</div> $retryButton.prop('disabled', false);
`), });
);
} }
loadLazyDiff(e) { loadLazyDiff(e) {
...@@ -1449,21 +1450,36 @@ export default class Notes { ...@@ -1449,21 +1450,36 @@ export default class Notes {
$container.find('.js-toggle-lazy-diff').removeClass('js-toggle-lazy-diff'); $container.find('.js-toggle-lazy-diff').removeClass('js-toggle-lazy-diff');
const tableEl = $container.find('tbody'); const $tableEl = $container.find('tbody');
if (tableEl.length === 0) return; if ($tableEl.length === 0) return;
const fileHolder = $container.find('.file-holder'); const fileHolder = $container.find('.file-holder');
const url = fileHolder.data('linesPath'); const url = fileHolder.data('linesPath');
axios const $errorContainer = $container.find('.js-error-lazy-load-diff');
const $successContainer = $container.find('.js-success-lazy-load');
/**
* We only fetch resolved discussions.
* Unresolved discussions don't have an endpoint being provided.
*/
if (url) {
return axios
.get(url) .get(url)
.then(({ data }) => { .then(({ data }) => {
// Reset state in case last request returned error
$successContainer.removeClass('hidden');
$errorContainer.addClass('hidden');
Notes.renderDiffContent($container, data); Notes.renderDiffContent($container, data);
}) })
.catch(() => { .catch(() => {
Notes.renderDiffError($container); $successContainer.addClass('hidden');
$errorContainer.removeClass('hidden');
}); });
} }
return Promise.resolve();
}
toggleCommitList(e) { toggleCommitList(e) {
const $element = $(e.currentTarget); const $element = $(e.currentTarget);
......
...@@ -10,29 +10,25 @@ export default class PerformanceBarService { ...@@ -10,29 +10,25 @@ export default class PerformanceBarService {
} }
static registerInterceptor(peekUrl, callback) { static registerInterceptor(peekUrl, callback) {
vueResourceInterceptor = (request, next) => { const interceptor = response => {
next(response => {
const requestId = response.headers['x-request-id']; const requestId = response.headers['x-request-id'];
const requestUrl = response.url; // Get the request URL from response.config for Axios, and response for
// Vue Resource.
const requestUrl = (response.config || response).url;
const cachedResponse = response.headers['x-gitlab-from-cache'] === 'true';
if (requestUrl !== peekUrl && requestId) { if (requestUrl !== peekUrl && requestId && !cachedResponse) {
callback(requestId, requestUrl); callback(requestId, requestUrl);
} }
});
};
Vue.http.interceptors.push(vueResourceInterceptor); return response;
};
return axios.interceptors.response.use(response => { vueResourceInterceptor = (request, next) => next(interceptor);
const requestId = response.headers['x-request-id'];
const requestUrl = response.config.url;
if (requestUrl !== peekUrl && requestId) { Vue.http.interceptors.push(vueResourceInterceptor);
callback(requestId, requestUrl);
}
return response; return axios.interceptors.response.use(interceptor);
});
} }
static removeInterceptor(interceptor) { static removeInterceptor(interceptor) {
......
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetPipelineBlocked',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="warning" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure
</span>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'PipelineFailed',
components: {
statusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span class="bold">
{{ s__(`mrWidget|The pipeline for this merge request failed.
Please retry the job or push a new commit to fix the failure`) }}
</span>
</div>
</div>
</template>
...@@ -31,7 +31,7 @@ export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/compon ...@@ -31,7 +31,7 @@ export { default as ReadyToMergeState } from 'ee/vue_merge_request_widget/compon
export { default as ShaMismatchState } from './components/states/sha_mismatch.vue'; export { default as ShaMismatchState } from './components/states/sha_mismatch.vue';
export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue'; export { default as UnresolvedDiscussionsState } from './components/states/unresolved_discussions.vue';
export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue'; export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';
export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed'; export { default as PipelineFailedState } from './components/states/pipeline_failed.vue';
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue';
......
...@@ -175,7 +175,7 @@ ...@@ -175,7 +175,7 @@
</a> </a>
</span> </span>
<span v-else> <span v-else>
Cant find HEAD commit for this branch Can't find HEAD commit for this branch
</span> </span>
</div> </div>
</div> </div>
......
...@@ -503,3 +503,7 @@ fieldset[disabled] .btn, ...@@ -503,3 +503,7 @@ fieldset[disabled] .btn,
@extend %disabled; @extend %disabled;
} }
} }
.btn-no-padding {
padding: 0;
}
...@@ -160,6 +160,11 @@ ...@@ -160,6 +160,11 @@
} }
} }
} }
.diff-loading-error-block {
padding: $gl-padding * 2 $gl-padding;
text-align: center;
}
} }
.image { .image {
......
...@@ -821,3 +821,20 @@ ...@@ -821,3 +821,20 @@
max-width: 100%; max-width: 100%;
} }
} }
// Hack alert: we've rewritten `btn` class in a way that
// we've broken it and it is not possible to use with `btn-link`
// which causes a blank button when it's disabled and hovering
// The css in here is the boostrap one
.btn-link-retry {
&[disabled] {
cursor: not-allowed;
box-shadow: none;
opacity: .65;
&:hover {
color: $file-mode-changed;
text-decoration: none;
}
}
}
...@@ -41,6 +41,11 @@ ...@@ -41,6 +41,11 @@
.commit-title { .commit-title {
margin: 0; margin: 0;
white-space: normal;
@media (max-width: $screen-sm-max) {
justify-content: flex-end;
}
} }
.ci-table { .ci-table {
......
...@@ -90,7 +90,7 @@ module TreeHelper ...@@ -90,7 +90,7 @@ module TreeHelper
end end
def commit_in_single_accessible_branch def commit_in_single_accessible_branch
branch_name = html_escape(selected_branch) branch_name = ERB::Util.html_escape(selected_branch)
message = _("Your changes can be committed to %{branch_name} because a merge "\ message = _("Your changes can be committed to %{branch_name} because a merge "\
"request is open.") % { branch_name: "<strong>#{branch_name}</strong>" } "request is open.") % { branch_name: "<strong>#{branch_name}</strong>" }
......
...@@ -4,7 +4,7 @@ module Ci ...@@ -4,7 +4,7 @@ module Ci
include HasVariable include HasVariable
include Presentable include Presentable
belongs_to :group belongs_to :group, class_name: "::Group"
alias_attribute :secret_value, :value alias_attribute :secret_value, :value
......
...@@ -338,6 +338,7 @@ class Repository ...@@ -338,6 +338,7 @@ class Repository
return unless empty? return unless empty?
expire_method_caches(%i(has_visible_content?)) expire_method_caches(%i(has_visible_content?))
raw_repository.expire_has_local_branches_cache
end end
def lookup_cache def lookup_cache
......
...@@ -13,7 +13,7 @@ module Clusters ...@@ -13,7 +13,7 @@ module Clusters
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
provider.make_errored!("Failed to request to CloudPlatform; #{e.message}") provider.make_errored!("Failed to request to CloudPlatform; #{e.message}")
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
provider.make_errored!("Failed to configure GKE Cluster: #{e.message}") provider.make_errored!("Failed to configure Google Kubernetes Engine Cluster: #{e.message}")
end end
private private
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- unless expanded - unless expanded
- diff_data = { lines_path: project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion) } - diff_data = { lines_path: project_merge_request_discussion_path(discussion.project, discussion.noteable, discussion) }
.diff-file.file-holder{ class: diff_file_class, data: diff_data } .diff-file.file-holder.js-lazy-load-discussion{ class: diff_file_class, data: diff_data }
.js-file-title.file-title.file-title-flex-parent .js-file-title.file-title.file-title-flex-parent
.file-header-content .file-header-content
= render "projects/diffs/file_header", diff_file: diff_file, url: discussion_path(discussion), show_toggle: false = render "projects/diffs/file_header", diff_file: diff_file, url: discussion_path(discussion), show_toggle: false
...@@ -28,8 +28,11 @@ ...@@ -28,8 +28,11 @@
%tr.line_holder.line-holder-placeholder %tr.line_holder.line-holder-placeholder
%td.old_line.diff-line-num %td.old_line.diff-line-num
%td.new_line.diff-line-num %td.new_line.diff-line-num
%td.line_content %td.line_content.js-success-lazy-load
.js-code-placeholder .js-code-placeholder
%td.js-error-lazy-load-diff.hidden.diff-loading-error-block
- button = button_tag(_("Try again"), class: "btn-link btn-link-retry btn-no-padding js-toggle-lazy-diff-retry-button")
= _("Unable to load the diff. %{button_try_again}").html_safe % { button_try_again: button}
= render "discussions/diff_discussion", discussions: [discussion], expanded: true = render "discussions/diff_discussion", discussions: [discussion], expanded: true
- else - else
- partial = (diff_file.new_file? || diff_file.deleted_file?) ? 'single_image_diff' : 'replaced_image_diff' - partial = (diff_file.new_file? || diff_file.deleted_file?) ? 'single_image_diff' : 'replaced_image_diff'
......
...@@ -8,6 +8,6 @@ ...@@ -8,6 +8,6 @@
%h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up Kubernetes cluster integration') %h4.prepend-top-0= s_('ClusterIntegration|Choose how to set up Kubernetes cluster integration')
%p= s_('ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab') %p= s_('ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab')
= link_to s_('ClusterIntegration|Create on GKE'), gcp_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20' = link_to s_('ClusterIntegration|Create on Google Kubernetes Engine'), gcp_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
%p= s_('ClusterIntegration|Enter the details for an existing Kubernetes cluster') %p= s_('ClusterIntegration|Enter the details for an existing Kubernetes cluster')
= link_to s_('ClusterIntegration|Add an existing Kubernetes cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20' = link_to s_('ClusterIntegration|Add an existing Kubernetes cluster'), user_new_namespace_project_clusters_path(@project.namespace, @project), class: 'btn append-bottom-20'
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
= author_avatar(commit, size: 36) = author_avatar(commit, size: 36)
.commit-detail.flex-list .commit-detail.flex-list
.commit-content .commit-content.qa-commit-content
- if view_details && merge_request - if view_details && merge_request
= link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title" = link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title"
- else - else
......
- breadcrumb_title "Pipelines" - breadcrumb_title "Pipelines"
- page_title "New Pipeline" - page_title = s_("Pipeline|Run Pipeline")
%h3.page-title %h3.page-title
New Pipeline = s_("Pipeline|Run Pipeline")
%hr %hr
= form_for @pipeline, as: :pipeline, url: project_pipelines_path(@project), html: { id: "new-pipeline-form", class: "form-horizontal js-new-pipeline-form js-requires-input" } do |f| = form_for @pipeline, as: :pipeline, url: project_pipelines_path(@project), html: { id: "new-pipeline-form", class: "form-horizontal js-new-pipeline-form js-requires-input" } do |f|
= form_errors(@pipeline) = form_errors(@pipeline)
.form-group .form-group
= f.label :ref, 'Create for', class: 'control-label' = f.label :ref, s_('Pipeline|Run on'), class: 'control-label'
.col-sm-10 .col-sm-10
= hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch = hidden_field_tag 'pipeline[ref]', params[:ref] || @project.default_branch
= dropdown_tag(params[:ref] || @project.default_branch, = dropdown_tag(params[:ref] || @project.default_branch,
options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle', options: { toggle_class: 'js-branch-select wide git-revision-dropdown-toggle',
filter: true, dropdown_class: "dropdown-menu-selectable git-revision-dropdown", placeholder: "Search branches", filter: true, dropdown_class: "dropdown-menu-selectable git-revision-dropdown", placeholder: s_("Pipeline|Search branches"),
data: { selected: params[:ref] || @project.default_branch, field_name: 'pipeline[ref]' } }) data: { selected: params[:ref] || @project.default_branch, field_name: 'pipeline[ref]' } })
.help-block Existing branch name, tag .help-block
= s_("Pipeline|Existing branch name, tag")
.form-actions .form-actions
= f.submit 'Create pipeline', class: 'btn btn-create', tabindex: 3 = f.submit s_('Pipeline|Run pipeline'), class: 'btn btn-success', tabindex: 3
= link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-cancel' = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-default pull-right'
-# haml-lint:disable InlineJavaScript -# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
- content_for :push_access_levels do - content_for :push_access_levels do
.push_access_levels-container .push_access_levels-container
= dropdown_tag('Select', = dropdown_tag('Select',
options: { toggle_class: 'js-allowed-to-push wide', options: { toggle_class: 'js-allowed-to-push qa-allowed-to-push-select wide',
dropdown_class: 'dropdown-menu-selectable capitalize-header', dropdown_class: 'dropdown-menu-selectable qa-allowed-to-push-dropdown capitalize-header',
data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }}) data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }})
= render 'projects/protected_branches/shared/create_protected_branch' = render 'projects/protected_branches/shared/create_protected_branch'
...@@ -6,5 +6,5 @@ ...@@ -6,5 +6,5 @@
%td %td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level = hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_levels.first.access_level
= dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') , = dropdown_tag( (protected_branch.push_access_levels.first.humanize || 'Select') ,
options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header', options: { toggle_class: 'js-allowed-to-push qa-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container capitalize-header',
data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }}) data: { field_name: "allowed_to_push_#{protected_branch.id}", access_level_id: protected_branch.push_access_levels.first.id }})
.protected-branches-list.js-protected-branches-list .protected-branches-list.js-protected-branches-list.qa-protected-branches-list
- if @protected_branches.empty? - if @protected_branches.empty?
.panel-heading .panel-heading
%h3.panel-title %h3.panel-title
......
= f.hidden_field(:name) = f.hidden_field(:name)
= dropdown_tag('Select branch or create wildcard', = dropdown_tag('Select branch or create wildcard',
options: { toggle_class: 'js-protected-branch-select js-filter-submit wide git-revision-dropdown-toggle', options: { toggle_class: 'js-protected-branch-select js-filter-submit wide git-revision-dropdown-toggle qa-protected-branch-select',
filter: true, dropdown_class: "dropdown-menu-selectable git-revision-dropdown", placeholder: "Search protected branches", filter: true, dropdown_class: "dropdown-menu-selectable git-revision-dropdown qa-protected-branch-dropdown", placeholder: "Search protected branches",
footer_content: true, footer_content: true,
data: { show_no: true, show_any: true, show_upcoming: true, data: { show_no: true, show_any: true, show_upcoming: true,
selected: params[:protected_branch_name], selected: params[:protected_branch_name],
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.settings-header .settings-header
%h4 %h4
Protected Branches Protected Branches
%button.btn.js-settings-toggle{ type: 'button' } %button.btn.js-settings-toggle.qa-expand-protected-branches{ type: 'button' }
= expanded ? 'Collapse' : 'Expand' = expanded ? 'Collapse' : 'Expand'
%p %p
Keep stable branches secure and force developers to use merge requests. Keep stable branches secure and force developers to use merge requests.
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } } %tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
%td %td
%span.ref-name= protected_branch.name %span.ref-name.qa-protected-branch-name= protected_branch.name
- if @project.root_ref?(protected_branch.name) - if @project.root_ref?(protected_branch.name)
%span.label.label-info.prepend-left-5 default %span.label.label-info.prepend-left-5 default
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
- @options && @options.each do |key, value| - @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil = hidden_field_tag key, value, id: nil
.dropdown .dropdown
= dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project, sort: 'updated_desc'), field_name: 'ref', submit_form_on_click: true, visit: true }, { toggle_class: "js-project-refs-dropdown" } = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_project_path(@project, sort: 'updated_desc'), field_name: 'ref', submit_form_on_click: true, visit: true }, { toggle_class: "js-project-refs-dropdown qa-branches-select" }
.dropdown-menu.dropdown-menu-selectable.git-revision-dropdown.dropdown-menu-paging{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) } .dropdown-menu.dropdown-menu-selectable.git-revision-dropdown.dropdown-menu-paging.qa-branches-dropdown{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
.dropdown-page-one .dropdown-page-one
= dropdown_title _("Switch branch/tag") = dropdown_title _("Switch branch/tag")
= dropdown_filter _("Search branches and tags") = dropdown_filter _("Search branches and tags")
......
#!/usr/bin/env ruby
require 'optparse'
require 'open3'
require 'rainbow/refinement'
using Rainbow
BRANCH_PREFIX = 'security'.freeze
STABLE_BRANCH_SUFFIX = 'stable'.freeze
REMOTE = 'dev'.freeze
options = { version: nil, branch: nil, sha: nil }
parser = OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"
opts.on('-v', '--version 10.0', 'Version') do |version|
options[:version] = version&.tr('.', '-')
end
opts.on('-b', '--branch security-fix-branch', 'Original branch name') do |branch|
options[:branch] = branch
end
opts.on('-s', '--sha abcd', 'SHA to cherry pick') do |sha|
options[:sha] = sha
end
opts.on('-h', '--help', 'Displays Help') do
puts opts
exit
end
end
parser.parse!
abort("Missing options. Use #{$0} --help to see the list of options available".red) if options.values.include?(nil)
abort("Wrong version format #{options[:version].bold}".red) unless options[:version] =~ /\A\d*\-\d*\Z/
branch = [BRANCH_PREFIX, options[:branch], options[:version]].join('-').freeze
stable_branch = "#{options[:version]}-#{STABLE_BRANCH_SUFFIX}".freeze
command = "git checkout #{stable_branch} && git pull #{REMOTE} #{stable_branch} && git checkout -B #{branch} && git cherry-pick #{options[:sha]} && git push #{REMOTE} #{branch}"
_stdin, stdout, stderr = Open3.popen3(command)
puts stdout.read&.green
puts stderr.read&.red
---
title: Improves wording in new pipeline page
merge_request:
author:
type: other
---
title: Breaks commit not found message in pipelines table
merge_request:
author:
type: fixed
---
title: Replace GKE acronym with Google Kubernetes Engine
merge_request:
author:
type: other
---
title: Fix confirmation modal for deleting a protected branch
merge_request: 18176
author: Paul Bonaud @PaulRbR
type: fixed
---
title: Fixes unresolved discussions rendering the error state instead of the diff
merge_request:
author:
type: fixed
---
title: '[API] Fix URLs in the `Link` header for `GET /projects/:id/repository/contributors`
when no value is passed for `order_by` or `sort`'
merge_request: 18393
author:
type: fixed
---
title: Fix undefined `html_escape` method during markdown rendering
merge_request: 18418
author:
type: fixed
---
title: Fix a case with secret variables being empty sometimes
merge_request: 18400
author:
type: fixed
---
title: Fixed IDE not loading for sub groups
merge_request:
author:
type: fixed
---
title: Fixed IDE not showing loading state when tree is loading
merge_request:
author:
type: fixed
---
title: Move PipelineFailed vue component
merge_request: 18277
author: George Tsiolis
type: performance
---
title: Memoize Git::Repository#has_visible_content?
merge_request:
author:
type: performance
---
title: Check if a ref exists is done by Gitaly by default
merge_request:
author:
type: performance
...@@ -27,16 +27,8 @@ module Sidekiq ...@@ -27,16 +27,8 @@ module Sidekiq
Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead. Use an `after_commit` hook, or include `AfterCommitQueue` and use a `run_after_commit` block instead.
MSG MSG
rescue Sidekiq::Worker::EnqueueFromTransactionError => e rescue Sidekiq::Worker::EnqueueFromTransactionError => e
if Rails.env.production? Rails.logger.error(e.message) if Rails.env.production?
Rails.logger.error(e.message) Gitlab::Sentry.track_exception(e)
if Gitlab::Sentry.enabled?
Gitlab::Sentry.context
Raven.capture_exception(e)
end
else
raise
end
end end
end end
......
...@@ -183,7 +183,7 @@ GET /projects/:id/repository/contributors ...@@ -183,7 +183,7 @@ GET /projects/:id/repository/contributors
Parameters: Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `order_by` (optional) - Return contributors ordered by `name`, `email`, or `commits` fields. If not given contributors are ordered by commit date. - `order_by` (optional) - Return contributors ordered by `name`, `email`, or `commits` (orders by commit date) fields. Default is `commits`
- `sort` (optional) - Return contributors sorted in `asc` or `desc` order. Default is `asc` - `sort` (optional) - Return contributors sorted in `asc` or `desc` order. Default is `asc`
Response: Response:
......
...@@ -523,7 +523,7 @@ export default new Vuex.Store({ ...@@ -523,7 +523,7 @@ export default new Vuex.Store({
``` ```
_Note:_ If the state of the application is too complex, an individual file for the state may be better. _Note:_ If the state of the application is too complex, an individual file for the state may be better.
#### `actions.js` ##### `actions.js`
An action commits a mutatation. In this file, we will write the actions that will call the respective mutation: An action commits a mutatation. In this file, we will write the actions that will call the respective mutation:
```javascript ```javascript
...@@ -550,7 +550,7 @@ import { mapActions } from 'vuex'; ...@@ -550,7 +550,7 @@ import { mapActions } from 'vuex';
}; };
``` ```
#### `getters.js` ##### `getters.js`
Sometimes we may need to get derived state based on store state, like filtering for a specific prop. This can be done through the `getters`: Sometimes we may need to get derived state based on store state, like filtering for a specific prop. This can be done through the `getters`:
```javascript ```javascript
...@@ -573,7 +573,7 @@ import { mapGetters } from 'vuex'; ...@@ -573,7 +573,7 @@ import { mapGetters } from 'vuex';
}; };
``` ```
#### `mutations.js` ##### `mutations.js`
The only way to actually change state in a Vuex store is by committing a mutation. The only way to actually change state in a Vuex store is by committing a mutation.
```javascript ```javascript
...@@ -586,7 +586,7 @@ The only way to actually change state in a Vuex store is by committing a mutatio ...@@ -586,7 +586,7 @@ The only way to actually change state in a Vuex store is by committing a mutatio
}; };
``` ```
#### `mutations_types.js` ##### `mutations_types.js`
From [vuex mutations docs][vuex-mutations]: From [vuex mutations docs][vuex-mutations]:
> It is a commonly seen pattern to use constants for mutation types in various Flux implementations. This allows the code to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application. > It is a commonly seen pattern to use constants for mutation types in various Flux implementations. This allows the code to take advantage of tooling like linters, and putting all constants in a single file allows your collaborators to get an at-a-glance view of what mutations are possible in the entire application.
......
...@@ -23,6 +23,7 @@ are very appreciative of the work done by translators and proofreaders! ...@@ -23,6 +23,7 @@ are very appreciative of the work done by translators and proofreaders!
- Italian - Italian
- Paolo Falomo - [GitLab](https://gitlab.com/paolofalomo), [Crowdin](https://crowdin.com/profile/paolo.falomo) - Paolo Falomo - [GitLab](https://gitlab.com/paolofalomo), [Crowdin](https://crowdin.com/profile/paolo.falomo)
- Japanese - Japanese
- Yamana Tokiuji - [GitLab](https://gitlab.com/tokiuji), [Crowdin](https://crowdin.com/profile/yamana)
- Korean - Korean
- Chang-Ho Cha - [GitLab](https://gitlab.com/changho-cha), [Crowdin](https://crowdin.com/profile/zzazang) - Chang-Ho Cha - [GitLab](https://gitlab.com/changho-cha), [Crowdin](https://crowdin.com/profile/zzazang)
- Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
......
...@@ -31,8 +31,8 @@ the hardware requirements. ...@@ -31,8 +31,8 @@ the hardware requirements.
- [Install GitLab on DC/OS](https://mesosphere.com/blog/gitlab-dcos/) via [GitLab-Mesosphere integration](https://about.gitlab.com/2016/09/16/announcing-gitlab-and-mesosphere/) - [Install GitLab on DC/OS](https://mesosphere.com/blog/gitlab-dcos/) via [GitLab-Mesosphere integration](https://about.gitlab.com/2016/09/16/announcing-gitlab-and-mesosphere/)
- [Install GitLab on Azure](azure/index.md) - [Install GitLab on Azure](azure/index.md)
- [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md) - [Install GitLab on Google Cloud Platform](google_cloud_platform/index.md)
- [Install GitLab on Google Container Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/): video tutorial on - [Install GitLab on Google Kubernetes Engine (GKE)](https://about.gitlab.com/2017/01/23/video-tutorial-idea-to-production-on-google-container-engine-gke/): video tutorial on
the full process of installing GitLab on Google Container Engine (GKE), pushing an application to GitLab, building the app with GitLab CI/CD, and deploying to production. the full process of installing GitLab on Google Kubernetes Engine (GKE), pushing an application to GitLab, building the app with GitLab CI/CD, and deploying to production.
- [Install on AWS](https://about.gitlab.com/aws/) - [Install on AWS](https://about.gitlab.com/aws/)
- _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) - - _Testing only!_ [DigitalOcean and Docker Machine](digitaloceandocker.md) -
Quickly test any version of GitLab on DigitalOcean using Docker Machine. Quickly test any version of GitLab on DigitalOcean using Docker Machine.
......
...@@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source. ...@@ -93,9 +93,9 @@ Is the system packaged Git too old? Remove it and compile from source.
# Download and compile from source # Download and compile from source
cd /tmp cd /tmp
curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.16.2.tar.gz curl --remote-name --progress https://www.kernel.org/pub/software/scm/git/git-2.16.3.tar.gz
echo '9acc4339b7a2ab484eea69d705923271682b7058015219cf5a7e6ed8dee5b5fb git-2.16.2.tar.gz' | shasum -a256 -c - && tar -xzf git-2.16.2.tar.gz echo 'dda229e9c73f4fbb7d4324e0d993e11311673df03f73b194c554c2e9451e17cd git-2.16.3.tar.gz' | shasum -a256 -c - && tar -xzf git-2.16.3.tar.gz
cd git-2.16.2/ cd git-2.16.3/
./configure ./configure
make prefix=/usr/local all make prefix=/usr/local all
......
...@@ -73,7 +73,7 @@ feature 'EE Clusters' do ...@@ -73,7 +73,7 @@ feature 'EE Clusters' do
end end
end end
context 'when user adds an GKE cluster' do context 'when user adds an Google Kubernetes Engine cluster' do
before do before do
allow_any_instance_of(Projects::Clusters::GcpController) allow_any_instance_of(Projects::Clusters::GcpController)
.to receive(:token_in_session).and_return('token') .to receive(:token_in_session).and_return('token')
...@@ -105,7 +105,7 @@ feature 'EE Clusters' do ...@@ -105,7 +105,7 @@ feature 'EE Clusters' do
context 'when user filled form with environment scope' do context 'when user filled form with environment scope' do
before do before do
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
fill_in 'cluster_name', with: 'staging-cluster' fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: 'staging/*' fill_in 'cluster_environment_scope', with: 'staging/*'
...@@ -135,7 +135,7 @@ feature 'EE Clusters' do ...@@ -135,7 +135,7 @@ feature 'EE Clusters' do
context 'when user updates duplicated environment scope' do context 'when user updates duplicated environment scope' do
before do before do
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
fill_in 'cluster_name', with: 'staging-cluster' fill_in 'cluster_name', with: 'staging-cluster'
fill_in 'cluster_environment_scope', with: '*' fill_in 'cluster_environment_scope', with: '*'
......
Feature: Project Builds Permissions
Background:
Given I sign in as a user
And project exists in some group namespace
And project has CI enabled
And project has a recent build
Scenario: I try to visit build details as guest
Given I am member of a project with a guest role
When I visit recent build details page
Then page status code should be 404
Scenario: I try to visit project builds page as guest
Given I am member of a project with a guest role
When I visit project builds page
Then page status code should be 404
Scenario: I try to visit build details of internal project without access to builds
Given The project is internal
And public access for builds is disabled
When I visit recent build details page
Then page status code should be 404
Scenario: I try to visit internal project builds page without access to builds
Given The project is internal
And public access for builds is disabled
When I visit project builds page
Then page status code should be 404
@javascript
Scenario: I try to visit build details of internal project with access to builds
Given The project is internal
And public access for builds is enabled
When I visit recent build details page
Then I see details of a build
And I see build trace
Scenario: I try to visit internal project builds page with access to builds
Given The project is internal
And public access for builds is enabled
When I visit project builds page
Then I see the build
Scenario: I try to download build artifacts as guest
Given I am member of a project with a guest role
And recent build has artifacts available
When I access artifacts download page
Then page status code should be 404
Scenario: I try to download build artifacts as reporter
Given I am member of a project with a reporter role
And recent build has artifacts available
When I access artifacts download page
Then download of build artifacts archive starts
class Spinach::Features::ProjectBuildsPermissions < Spinach::FeatureSteps
include SharedAuthentication
include SharedProject
include SharedBuilds
include SharedPaths
include RepoHelpers
end
...@@ -30,10 +30,6 @@ module SharedBuilds ...@@ -30,10 +30,6 @@ module SharedBuilds
visit project_job_path(@project, @build) visit project_job_path(@project, @build)
end end
step 'I visit project builds page' do
visit project_jobs_path(@project)
end
step 'recent build has artifacts available' do step 'recent build has artifacts available' do
artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip' artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
archive = fixture_file_upload(artifacts, 'application/zip') archive = fixture_file_upload(artifacts, 'application/zip')
...@@ -54,25 +50,4 @@ module SharedBuilds ...@@ -54,25 +50,4 @@ module SharedBuilds
expect(page.response_headers['Content-Type']).to eq 'application/zip' expect(page.response_headers['Content-Type']).to eq 'application/zip'
expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary' expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary'
end end
step 'I access artifacts download page' do
visit download_project_job_artifacts_path(@project, @build)
end
step 'I see details of a build' do
expect(page).to have_content "Job ##{@build.id}"
end
step 'I see build trace' do
expect(page).to have_css '#build-trace'
end
step 'I see the build' do
page.within('.build') do
expect(page).to have_content "##{@build.id}"
expect(page).to have_content @build.sha[0..7]
expect(page).to have_content @build.ref
expect(page).to have_content @build.name
end
end
end end
...@@ -455,12 +455,4 @@ module SharedPaths ...@@ -455,12 +455,4 @@ module SharedPaths
mr = MergeRequest.find_by(title: title) mr = MergeRequest.find_by(title: title)
project_merge_request_path(mr.target_project, mr) project_merge_request_path(mr.target_project, mr)
end end
# ----------------------------------------
# Errors
# ----------------------------------------
step 'page status code should be 404' do
expect(status_code).to eq 404
end
end end
...@@ -13,11 +13,6 @@ module SharedProject ...@@ -13,11 +13,6 @@ module SharedProject
@project.add_master(@user) @project.add_master(@user)
end end
step "project exists in some group namespace" do
@group = create(:group, name: 'some group')
@project = create(:project, :repository, namespace: @group, public_builds: false)
end
# Create a specific project called "Shop" # Create a specific project called "Shop"
step 'I own project "Shop"' do step 'I own project "Shop"' do
@project = Project.find_by(name: "Shop") @project = Project.find_by(name: "Shop")
...@@ -29,18 +24,6 @@ module SharedProject ...@@ -29,18 +24,6 @@ module SharedProject
@project ||= Project.first @project ||= Project.first
end end
# ----------------------------------------
# Project permissions
# ----------------------------------------
step 'I am member of a project with a guest role' do
@project.add_guest(@user)
end
step 'I am member of a project with a reporter role' do
@project.add_reporter(@user)
end
# ---------------------------------------- # ----------------------------------------
# Visibility of archived project # Visibility of archived project
# ---------------------------------------- # ----------------------------------------
...@@ -140,18 +123,6 @@ module SharedProject ...@@ -140,18 +123,6 @@ module SharedProject
create(:label, project: project, title: 'enhancement') create(:label, project: project, title: 'enhancement')
end end
step 'The project is internal' do
@project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
step 'public access for builds is enabled' do
@project.update(public_builds: true)
end
step 'public access for builds is disabled' do
@project.update(public_builds: false)
end
def user_owns_project(user_name:, project_name:, visibility: :private) def user_owns_project(user_name:, project_name:, visibility: :private)
user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore) user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
project = Project.find_by(name: project_name) project = Project.find_by(name: project_name)
......
...@@ -111,8 +111,8 @@ module API ...@@ -111,8 +111,8 @@ module API
end end
params do params do
use :pagination use :pagination
optional :order_by, type: String, values: %w[email name commits], default: nil, desc: 'Return contributors ordered by `name` or `email` or `commits`' optional :order_by, type: String, values: %w[email name commits], default: 'commits', desc: 'Return contributors ordered by `name` or `email` or `commits`'
optional :sort, type: String, values: %w[asc desc], default: nil, desc: 'Sort by asc (ascending) or desc (descending)' optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
end end
get ':id/repository/contributors' do get ':id/repository/contributors' do
begin begin
......
...@@ -9,7 +9,7 @@ module Banzai ...@@ -9,7 +9,7 @@ module Banzai
lang_attr = lang.present? ? %Q{ lang="#{lang}"} : '' lang_attr = lang.present? ? %Q{ lang="#{lang}"} : ''
result = result =
"<pre>" \ "<pre>" \
"<code#{lang_attr}>#{html_escape(code)}</code>" \ "<code#{lang_attr}>#{ERB::Util.html_escape(code)}</code>" \
"</pre>" "</pre>"
out(result) out(result)
......
...@@ -6,7 +6,7 @@ module Banzai ...@@ -6,7 +6,7 @@ module Banzai
lang_attr = lang ? %Q{ lang="#{lang}"} : '' lang_attr = lang ? %Q{ lang="#{lang}"} : ''
"\n<pre>" \ "\n<pre>" \
"<code#{lang_attr}>#{html_escape(code)}</code>" \ "<code#{lang_attr}>#{ERB::Util.html_escape(code)}</code>" \
"</pre>" "</pre>"
end end
end end
......
...@@ -33,10 +33,7 @@ module Gitlab ...@@ -33,10 +33,7 @@ module Gitlab
# match the blob, which is a bug. But we shouldn't fail to render # match the blob, which is a bug. But we shouldn't fail to render
# completely in that case, even though we want to report the error. # completely in that case, even though we want to report the error.
rescue RangeError => e rescue RangeError => e
if Gitlab::Sentry.enabled? Gitlab::Sentry.track_exception(e, issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/45441')
Gitlab::Sentry.context
Raven.capture_exception(e)
end
end end
end end
......
...@@ -5,7 +5,7 @@ module Gitlab ...@@ -5,7 +5,7 @@ module Gitlab
CANONICAL_CE_PROJECT_URL = 'https://gitlab.com/gitlab-org/gitlab-ce'.freeze CANONICAL_CE_PROJECT_URL = 'https://gitlab.com/gitlab-org/gitlab-ce'.freeze
CANONICAL_EE_REPO_URL = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze CANONICAL_EE_REPO_URL = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
CHECK_DIR = Rails.root.join('ee_compat_check') CHECK_DIR = Rails.root.join('ee_compat_check')
IGNORED_FILES_REGEX = %r{VERSION|CHANGELOG\.md|db/schema\.rb}i.freeze IGNORED_FILES_REGEX = %r{VERSION|CHANGELOG\.md|db/schema\.rb|locale/gitlab\.pot}i.freeze
PLEASE_READ_THIS_BANNER = %Q{ PLEASE_READ_THIS_BANNER = %Q{
============================================================ ============================================================
===================== PLEASE READ THIS ===================== ===================== PLEASE READ THIS =====================
......
...@@ -50,7 +50,7 @@ module Gitlab ...@@ -50,7 +50,7 @@ module Gitlab
status_code = Gitlab::PollingInterval.polling_enabled? ? 304 : 429 status_code = Gitlab::PollingInterval.polling_enabled? ? 304 : 429
[status_code, { 'ETag' => etag }, []] [status_code, { 'ETag' => etag, 'X-Gitlab-From-Cache' => 'true' }, []]
end end
def track_cache_miss(if_none_match, cached_value_present, route) def track_cache_miss(if_none_match, cached_value_present, route)
......
...@@ -9,6 +9,7 @@ module Gitlab ...@@ -9,6 +9,7 @@ module Gitlab
include Gitlab::Git::RepositoryMirroring include Gitlab::Git::RepositoryMirroring
include Gitlab::Git::Popen include Gitlab::Git::Popen
include Gitlab::EncodingHelper include Gitlab::EncodingHelper
include Gitlab::Utils::StrongMemoize
ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[ ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[
GIT_OBJECT_DIRECTORY GIT_OBJECT_DIRECTORY
...@@ -231,13 +232,13 @@ module Gitlab ...@@ -231,13 +232,13 @@ module Gitlab
end end
end end
def has_local_branches? def expire_has_local_branches_cache
gitaly_migrate(:has_local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| clear_memoization(:has_local_branches)
if is_enabled
gitaly_repository_client.has_local_branches?
else
has_local_branches_rugged?
end end
def has_local_branches?
strong_memoize(:has_local_branches) do
uncached_has_local_branches?
end end
end end
...@@ -299,7 +300,8 @@ module Gitlab ...@@ -299,7 +300,8 @@ module Gitlab
# #
# Ref names must start with `refs/`. # Ref names must start with `refs/`.
def ref_exists?(ref_name) def ref_exists?(ref_name)
gitaly_migrate(:ref_exists) do |is_enabled| gitaly_migrate(:ref_exists,
status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled if is_enabled
gitaly_ref_exists?(ref_name) gitaly_ref_exists?(ref_name)
else else
...@@ -1573,6 +1575,16 @@ module Gitlab ...@@ -1573,6 +1575,16 @@ module Gitlab
private private
def uncached_has_local_branches?
gitaly_migrate(:has_local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_repository_client.has_local_branches?
else
has_local_branches_rugged?
end
end
end
def local_write_ref(ref_path, ref, old_ref: nil, shell: true) def local_write_ref(ref_path, ref, old_ref: nil, shell: true)
if shell if shell
shell_write_ref(ref_path, ref, old_ref) shell_write_ref(ref_path, ref, old_ref)
......
...@@ -18,6 +18,25 @@ module Gitlab ...@@ -18,6 +18,25 @@ module Gitlab
end end
end end
# This can be used for investigating exceptions that can be recovered from in
# code. The exception will still be raised in development and test
# environments.
#
# That way we can track down these exceptions with as much information as we
# need to resolve them.
#
# Provide an issue URL for follow up.
def self.track_exception(exception, issue_url: nil, extra: {})
if enabled?
extra[:issue_url] = issue_url if issue_url
context # Make sure we've set everything we know in the context
Raven.capture_exception(exception, extra: extra)
end
raise exception if should_raise?
end
def self.program_context def self.program_context
if Sidekiq.server? if Sidekiq.server?
'sidekiq' 'sidekiq'
...@@ -25,5 +44,9 @@ module Gitlab ...@@ -25,5 +44,9 @@ module Gitlab
'rails' 'rails'
end end
end end
def self.should_raise?
Rails.env.development? || Rails.env.test?
end
end end
end end
...@@ -8,8 +8,8 @@ msgid "" ...@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-04-05 09:57+0200\n" "POT-Creation-Date: 2018-04-18 09:58+0200\n"
"PO-Revision-Date: 2018-04-05 09:57+0200\n" "PO-Revision-Date: 2018-04-18 09:58+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -21,6 +21,16 @@ msgstr "" ...@@ -21,6 +21,16 @@ msgstr ""
msgid " and" msgid " and"
msgstr "" msgstr ""
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
msgstr[0] ""
msgstr[1] ""
msgid " improved on %d point"
msgid_plural " improved on %d points"
msgstr[0] ""
msgstr[1] ""
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
msgstr[0] "" msgstr[0] ""
...@@ -56,6 +66,11 @@ msgid_plural "%d metrics" ...@@ -56,6 +66,11 @@ msgid_plural "%d metrics"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "%d vulnerability"
msgid_plural "%d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "%s additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "" msgstr[0] ""
...@@ -98,6 +113,11 @@ msgstr[1] "" ...@@ -98,6 +113,11 @@ msgstr[1] ""
msgid "%{text} is available" msgid "%{text} is available"
msgstr "" msgstr ""
msgid "%{type} detected %d vulnerability"
msgid_plural "%{type} detected %d vulnerabilities"
msgstr[0] ""
msgstr[1] ""
msgid "(checkout the %{link} for information on how to install it)." msgid "(checkout the %{link} for information on how to install it)."
msgstr "" msgstr ""
...@@ -181,12 +201,18 @@ msgstr "" ...@@ -181,12 +201,18 @@ msgstr ""
msgid "Add Readme" msgid "Add Readme"
msgstr "" msgstr ""
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
msgid "Add new directory" msgid "Add new directory"
msgstr "" msgstr ""
msgid "Add todo" msgid "Add todo"
msgstr "" msgstr ""
msgid "Additional text"
msgstr ""
msgid "AdminArea|Stop all jobs" msgid "AdminArea|Stop all jobs"
msgstr "" msgstr ""
...@@ -238,9 +264,6 @@ msgstr "" ...@@ -238,9 +264,6 @@ msgstr ""
msgid "AdminUsers|To confirm, type %{username}" msgid "AdminUsers|To confirm, type %{username}"
msgstr "" msgstr ""
msgid "Advanced"
msgstr ""
msgid "Advanced settings" msgid "Advanced settings"
msgstr "" msgstr ""
...@@ -370,7 +393,7 @@ msgstr "" ...@@ -370,7 +393,7 @@ msgstr ""
msgid "April" msgid "April"
msgstr "" msgstr ""
msgid "Archived project! Repository is read-only" msgid "Archived project! Repository and other project resources are read-only"
msgstr "" msgstr ""
msgid "Are you sure you want to delete this pipeline schedule?" msgid "Are you sure you want to delete this pipeline schedule?"
...@@ -490,6 +513,75 @@ msgstr "" ...@@ -490,6 +513,75 @@ msgstr ""
msgid "Background jobs" msgid "Background jobs"
msgstr "" msgstr ""
msgid "Badges"
msgstr ""
msgid "Badges|A new badge was added."
msgstr ""
msgid "Badges|Add badge"
msgstr ""
msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
msgstr ""
msgid "Badges|Badge image URL"
msgstr ""
msgid "Badges|Badge image preview"
msgstr ""
msgid "Badges|Delete badge"
msgstr ""
msgid "Badges|Delete badge?"
msgstr ""
msgid "Badges|Deleting the badge failed, please try again."
msgstr ""
msgid "Badges|Group Badge"
msgstr ""
msgid "Badges|Link"
msgstr ""
msgid "Badges|No badge image"
msgstr ""
msgid "Badges|No image to preview"
msgstr ""
msgid "Badges|Project Badge"
msgstr ""
msgid "Badges|Reload badge image"
msgstr ""
msgid "Badges|Save changes"
msgstr ""
msgid "Badges|Saving the badge failed, please check the entered URLs and try again."
msgstr ""
msgid "Badges|The %{docsLinkStart}variables%{docsLinkEnd} GitLab supports: %{placeholders}"
msgstr ""
msgid "Badges|The badge was deleted."
msgstr ""
msgid "Badges|The badge was saved."
msgstr ""
msgid "Badges|This group has no badges"
msgstr ""
msgid "Badges|This project has no badges"
msgstr ""
msgid "Badges|Your badges"
msgstr ""
msgid "Begin with the selected commit" msgid "Begin with the selected commit"
msgstr "" msgstr ""
...@@ -547,6 +639,9 @@ msgstr "" ...@@ -547,6 +639,9 @@ msgstr ""
msgid "BillingPlans|per user" msgid "BillingPlans|per user"
msgstr "" msgstr ""
msgid "Blame"
msgstr ""
msgid "Branch (%{branch_count})" msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})" msgid_plural "Branches (%{branch_count})"
msgstr[0] "" msgstr[0] ""
...@@ -705,7 +800,7 @@ msgstr "" ...@@ -705,7 +800,7 @@ msgstr ""
msgid "Browse files" msgid "Browse files"
msgstr "" msgstr ""
msgid "Business" msgid "Business metrics (Custom)"
msgstr "" msgstr ""
msgid "ByAuthor|by" msgid "ByAuthor|by"
...@@ -729,6 +824,9 @@ msgstr "" ...@@ -729,6 +824,9 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
msgid "Cancel this job"
msgstr ""
msgid "Cannot be merged automatically" msgid "Cannot be merged automatically"
msgstr "" msgstr ""
...@@ -900,6 +998,12 @@ msgstr "" ...@@ -900,6 +998,12 @@ msgstr ""
msgid "CircuitBreakerApiLink|circuitbreaker api" msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "" msgstr ""
msgid "Click any <strong>project name</strong> in the project list below to navigate to the project milestone."
msgstr ""
msgid "Click the <strong>Promote</strong> button in the top right corner to promote it to a group milestone."
msgstr ""
msgid "Click the button below to begin the install process by navigating to the Kubernetes page" msgid "Click the button below to begin the install process by navigating to the Kubernetes page"
msgstr "" msgstr ""
...@@ -984,7 +1088,7 @@ msgstr "" ...@@ -984,7 +1088,7 @@ msgstr ""
msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab" msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr "" msgstr ""
msgid "ClusterIntegration|Create on GKE" msgid "ClusterIntegration|Create on Google Kubernetes Engine"
msgstr "" msgstr ""
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster" msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
...@@ -1408,6 +1512,9 @@ msgstr "" ...@@ -1408,6 +1512,9 @@ msgstr ""
msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images." msgid "ContainerRegistry|With the Docker Container Registry integrated into GitLab, every project can have its own space to store its Docker images."
msgstr "" msgstr ""
msgid "ContainerRegistry|You can also %{deploy_token} for read-only access to the registry images."
msgstr ""
msgid "Continuous Integration and Deployment" msgid "Continuous Integration and Deployment"
msgstr "" msgstr ""
...@@ -1605,6 +1712,78 @@ msgstr[1] "" ...@@ -1605,6 +1712,78 @@ msgstr[1] ""
msgid "Deploy Keys" msgid "Deploy Keys"
msgstr "" msgstr ""
msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
msgstr ""
msgid "DeployTokens|Add a deploy token"
msgstr ""
msgid "DeployTokens|Allows read-only access to the registry images"
msgstr ""
msgid "DeployTokens|Allows read-only access to the repository"
msgstr ""
msgid "DeployTokens|Copy deploy token to clipboard"
msgstr ""
msgid "DeployTokens|Copy username to clipboard"
msgstr ""
msgid "DeployTokens|Create deploy token"
msgstr ""
msgid "DeployTokens|Created"
msgstr ""
msgid "DeployTokens|Deploy Tokens"
msgstr ""
msgid "DeployTokens|Deploy tokens allow read-only access to your repository and registry images."
msgstr ""
msgid "DeployTokens|Expires"
msgstr ""
msgid "DeployTokens|Name"
msgstr ""
msgid "DeployTokens|Pick a name for the application, and we'll give you a unique deploy token."
msgstr ""
msgid "DeployTokens|Revoke"
msgstr ""
msgid "DeployTokens|Revoke %{name}"
msgstr ""
msgid "DeployTokens|Scopes"
msgstr ""
msgid "DeployTokens|This action cannot be undone."
msgstr ""
msgid "DeployTokens|This project has no active Deploy Tokens."
msgstr ""
msgid "DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again."
msgstr ""
msgid "DeployTokens|Use this username as a login."
msgstr ""
msgid "DeployTokens|Username"
msgstr ""
msgid "DeployTokens|You are about to revoke"
msgstr ""
msgid "DeployTokens|Your New Deploy Token"
msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
msgstr ""
msgid "Description" msgid "Description"
msgstr "" msgstr ""
...@@ -1704,6 +1883,9 @@ msgstr "" ...@@ -1704,6 +1883,9 @@ msgstr ""
msgid "Emails" msgid "Emails"
msgstr "" msgstr ""
msgid "Embed"
msgstr ""
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
...@@ -2003,10 +2185,13 @@ msgstr "" ...@@ -2003,10 +2185,13 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage." msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "" msgstr ""
msgid "GeoNodes|%{eventId} events behind"
msgstr ""
msgid "GeoNodes|Checksummed" msgid "GeoNodes|Checksummed"
msgstr "" msgstr ""
msgid "GeoNodes|Database replication lag:" msgid "GeoNodes|Data replication lag:"
msgstr "" msgstr ""
msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?" msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
...@@ -2030,22 +2215,19 @@ msgstr "" ...@@ -2030,22 +2215,19 @@ msgstr ""
msgid "GeoNodes|Health status:" msgid "GeoNodes|Health status:"
msgstr "" msgstr ""
msgid "GeoNodes|Last event ID processed by cursor:"
msgstr ""
msgid "GeoNodes|Last event ID seen from primary:" msgid "GeoNodes|Last event ID seen from primary:"
msgstr "" msgstr ""
msgid "GeoNodes|Loading nodes" msgid "GeoNodes|Latest event log status:"
msgstr "" msgstr ""
msgid "GeoNodes|Local Attachments:" msgid "GeoNodes|Loading nodes"
msgstr "" msgstr ""
msgid "GeoNodes|Local LFS objects:" msgid "GeoNodes|Local LFS objects:"
msgstr "" msgstr ""
msgid "GeoNodes|Local job artifacts:" msgid "GeoNodes|Local attachments:"
msgstr "" msgstr ""
msgid "GeoNodes|New node" msgid "GeoNodes|New node"
...@@ -2072,21 +2254,24 @@ msgstr "" ...@@ -2072,21 +2254,24 @@ msgstr ""
msgid "GeoNodes|Replication slots:" msgid "GeoNodes|Replication slots:"
msgstr "" msgstr ""
msgid "GeoNodes|Repositories checksummed:"
msgstr ""
msgid "GeoNodes|Repositories:" msgid "GeoNodes|Repositories:"
msgstr "" msgstr ""
msgid "GeoNodes|Repository checksums verified:" msgid "GeoNodes|Repository checksums verified:"
msgstr "" msgstr ""
msgid "GeoNodes|Repository verification progress:"
msgstr ""
msgid "GeoNodes|Selective" msgid "GeoNodes|Selective"
msgstr "" msgstr ""
msgid "GeoNodes|Something went wrong while changing node status" msgid "GeoNodes|Something went wrong while changing node status"
msgstr "" msgstr ""
msgid "GeoNodes|Something went wrong while fetching nodes"
msgstr ""
msgid "GeoNodes|Something went wrong while removing node" msgid "GeoNodes|Something went wrong while removing node"
msgstr "" msgstr ""
...@@ -2117,7 +2302,7 @@ msgstr "" ...@@ -2117,7 +2302,7 @@ msgstr ""
msgid "GeoNodes|Wiki checksums verified:" msgid "GeoNodes|Wiki checksums verified:"
msgstr "" msgstr ""
msgid "GeoNodes|Wikis checksummed:" msgid "GeoNodes|Wikis checksums calculated verifies:"
msgstr "" msgstr ""
msgid "GeoNodes|Wikis:" msgid "GeoNodes|Wikis:"
...@@ -2421,6 +2606,9 @@ msgstr "" ...@@ -2421,6 +2606,9 @@ msgstr ""
msgid "January" msgid "January"
msgstr "" msgstr ""
msgid "Job has been erased"
msgstr ""
msgid "Jobs" msgid "Jobs"
msgstr "" msgstr ""
...@@ -3001,6 +3189,9 @@ msgstr "" ...@@ -3001,6 +3189,9 @@ msgstr ""
msgid "Options" msgid "Options"
msgstr "" msgstr ""
msgid "Other information"
msgstr ""
msgid "Otherwise it is recommended you start with one of the options below." msgid "Otherwise it is recommended you start with one of the options below."
msgstr "" msgstr ""
...@@ -3040,6 +3231,9 @@ msgstr "" ...@@ -3040,6 +3231,9 @@ msgstr ""
msgid "Performance optimization" msgid "Performance optimization"
msgstr "" msgstr ""
msgid "Permalink"
msgstr ""
msgid "Personal Access Token" msgid "Personal Access Token"
msgstr "" msgstr ""
...@@ -3154,12 +3348,27 @@ msgstr "" ...@@ -3154,12 +3348,27 @@ msgstr ""
msgid "Pipelines|This project is not currently set up to run pipelines." msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "" msgstr ""
msgid "Pipeline|Existing branch name, tag"
msgstr ""
msgid "Pipeline|Retry pipeline" msgid "Pipeline|Retry pipeline"
msgstr "" msgstr ""
msgid "Pipeline|Retry pipeline #%{pipelineId}?" msgid "Pipeline|Retry pipeline #%{pipelineId}?"
msgstr "" msgstr ""
msgid "Pipeline|Run Pipeline"
msgstr ""
msgid "Pipeline|Run on"
msgstr ""
msgid "Pipeline|Run pipeline"
msgstr ""
msgid "Pipeline|Search branches"
msgstr ""
msgid "Pipeline|Stop pipeline" msgid "Pipeline|Stop pipeline"
msgstr "" msgstr ""
...@@ -3193,6 +3402,9 @@ msgstr "" ...@@ -3193,6 +3402,9 @@ msgstr ""
msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again." msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
msgstr "" msgstr ""
msgid "Please select at least one filter to see results"
msgstr ""
msgid "Please solve the reCAPTCHA" msgid "Please solve the reCAPTCHA"
msgstr "" msgstr ""
...@@ -3223,6 +3435,12 @@ msgstr "" ...@@ -3223,6 +3435,12 @@ msgstr ""
msgid "Profiles|Account scheduled for removal." msgid "Profiles|Account scheduled for removal."
msgstr "" msgstr ""
msgid "Profiles|Change username"
msgstr ""
msgid "Profiles|Current path: %{path}"
msgstr ""
msgid "Profiles|Delete Account" msgid "Profiles|Delete Account"
msgstr "" msgstr ""
...@@ -3241,9 +3459,21 @@ msgstr "" ...@@ -3241,9 +3459,21 @@ msgstr ""
msgid "Profiles|Invalid username" msgid "Profiles|Invalid username"
msgstr "" msgstr ""
msgid "Profiles|Path"
msgstr ""
msgid "Profiles|Type your %{confirmationValue} to confirm:" msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "" msgstr ""
msgid "Profiles|Update username"
msgstr ""
msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
msgstr ""
msgid "Profiles|You don't have access to delete this user." msgid "Profiles|You don't have access to delete this user."
msgstr "" msgstr ""
...@@ -3274,6 +3504,9 @@ msgstr "" ...@@ -3274,6 +3504,9 @@ msgstr ""
msgid "Project '%{project_name}' was successfully updated." msgid "Project '%{project_name}' was successfully updated."
msgstr "" msgstr ""
msgid "Project Badges"
msgstr ""
msgid "Project access must be granted explicitly to each user." msgid "Project access must be granted explicitly to each user."
msgstr "" msgstr ""
...@@ -3316,15 +3549,6 @@ msgstr "" ...@@ -3316,15 +3549,6 @@ msgstr ""
msgid "ProjectCreationLevel|No one" msgid "ProjectCreationLevel|No one"
msgstr "" msgstr ""
msgid "ProjectFeature|Disabled"
msgstr ""
msgid "ProjectFeature|Everyone with access"
msgstr ""
msgid "ProjectFeature|Only team members"
msgstr ""
msgid "ProjectFileTree|Name" msgid "ProjectFileTree|Name"
msgstr "" msgstr ""
...@@ -3340,6 +3564,12 @@ msgstr "" ...@@ -3340,6 +3564,12 @@ msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting." msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "" msgstr ""
msgid "ProjectSettings|Failed to protect the tag"
msgstr ""
msgid "ProjectSettings|Failed to update tag!"
msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository." msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "" msgstr ""
...@@ -3379,6 +3609,9 @@ msgstr "" ...@@ -3379,6 +3609,9 @@ msgstr ""
msgid "ProjectsDropdown|This feature requires browser localStorage support" msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr "" msgstr ""
msgid "PrometheusDashboard|Time"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found" msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr "" msgstr ""
...@@ -3457,6 +3690,9 @@ msgstr "" ...@@ -3457,6 +3690,9 @@ msgstr ""
msgid "Promote" msgid "Promote"
msgstr "" msgstr ""
msgid "Promote these project milestones into a group milestone."
msgstr ""
msgid "Promote to Group Label" msgid "Promote to Group Label"
msgstr "" msgstr ""
...@@ -3490,6 +3726,9 @@ msgstr "" ...@@ -3490,6 +3726,9 @@ msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes." msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "" msgstr ""
msgid "Raw"
msgstr ""
msgid "Read more" msgid "Read more"
msgstr "" msgstr ""
...@@ -3568,6 +3807,9 @@ msgstr "" ...@@ -3568,6 +3807,9 @@ msgstr ""
msgid "Repository storage" msgid "Repository storage"
msgstr "" msgstr ""
msgid "RepositorySettingsAccessLevel|Select"
msgstr ""
msgid "Request Access" msgid "Request Access"
msgstr "" msgstr ""
...@@ -3583,7 +3825,13 @@ msgstr "" ...@@ -3583,7 +3825,13 @@ msgstr ""
msgid "Resolve discussion" msgid "Resolve discussion"
msgstr "" msgstr ""
msgid "Response" msgid "Response metrics (Custom)"
msgstr ""
msgid "Retry this job"
msgstr ""
msgid "Retry verification"
msgstr "" msgstr ""
msgid "Reveal value" msgid "Reveal value"
...@@ -3597,6 +3845,9 @@ msgstr "" ...@@ -3597,6 +3845,9 @@ msgstr ""
msgid "Revert this merge request" msgid "Revert this merge request"
msgstr "" msgstr ""
msgid "Review"
msgstr ""
msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"." msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"."
msgstr "" msgstr ""
...@@ -3756,6 +4007,9 @@ msgstr "" ...@@ -3756,6 +4007,9 @@ msgstr ""
msgid "Setup a specific Runner automatically" msgid "Setup a specific Runner automatically"
msgstr "" msgstr ""
msgid "Share"
msgstr ""
msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider" msgid "Share the <strong>%{sso_label}</strong> with members so they can sign in to your group through your identity provider"
msgstr "" msgstr ""
...@@ -3818,12 +4072,6 @@ msgstr "" ...@@ -3818,12 +4072,6 @@ msgstr ""
msgid "Something went wrong when toggling the button" msgid "Something went wrong when toggling the button"
msgstr "" msgstr ""
msgid "Something went wrong while fetching Dependency Scanning."
msgstr ""
msgid "Something went wrong while fetching SAST."
msgstr ""
msgid "Something went wrong while fetching the projects." msgid "Something went wrong while fetching the projects."
msgstr "" msgstr ""
...@@ -3980,6 +4228,9 @@ msgstr "" ...@@ -3980,6 +4228,9 @@ msgstr ""
msgid "Status" msgid "Status"
msgstr "" msgstr ""
msgid "Stop this environment"
msgstr ""
msgid "Stopped" msgid "Stopped"
msgstr "" msgstr ""
...@@ -3992,7 +4243,7 @@ msgstr "" ...@@ -3992,7 +4243,7 @@ msgstr ""
msgid "Switch branch/tag" msgid "Switch branch/tag"
msgstr "" msgstr ""
msgid "System" msgid "Sync information"
msgstr "" msgstr ""
msgid "System Hooks" msgid "System Hooks"
...@@ -4001,6 +4252,9 @@ msgstr "" ...@@ -4001,6 +4252,9 @@ msgstr ""
msgid "System header and footer:" msgid "System header and footer:"
msgstr "" msgstr ""
msgid "System metrics (Custom)"
msgstr ""
msgid "Tag (%{tag_count})" msgid "Tag (%{tag_count})"
msgid_plural "Tags (%{tag_count})" msgid_plural "Tags (%{tag_count})"
msgstr[0] "" msgstr[0] ""
...@@ -4195,9 +4449,6 @@ msgstr "" ...@@ -4195,9 +4449,6 @@ msgstr ""
msgid "There are problems accessing Git storage: " msgid "There are problems accessing Git storage: "
msgstr "" msgstr ""
msgid "There was an error loading results"
msgstr ""
msgid "There was an error loading users activity calendar." msgid "There was an error loading users activity calendar."
msgstr "" msgstr ""
...@@ -4243,6 +4494,15 @@ msgstr "" ...@@ -4243,6 +4494,15 @@ msgstr ""
msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered" msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr "" msgstr ""
msgid "This job does not have a trace."
msgstr ""
msgid "This job has been canceled"
msgstr ""
msgid "This job has been skipped"
msgstr ""
msgid "This job has not been triggered yet" msgid "This job has not been triggered yet"
msgstr "" msgstr ""
...@@ -4264,6 +4524,9 @@ msgstr "" ...@@ -4264,6 +4524,9 @@ msgstr ""
msgid "This page is unavailable because you are not allowed to read information across multiple projects." msgid "This page is unavailable because you are not allowed to read information across multiple projects."
msgstr "" msgstr ""
msgid "This page will be removed in a future release."
msgstr ""
msgid "This project" msgid "This project"
msgstr "" msgstr ""
...@@ -4514,9 +4777,15 @@ msgstr "" ...@@ -4514,9 +4777,15 @@ msgstr ""
msgid "Trigger this manual action" msgid "Trigger this manual action"
msgstr "" msgstr ""
msgid "Try again"
msgstr ""
msgid "Turn on Service Desk" msgid "Turn on Service Desk"
msgstr "" msgstr ""
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
msgid "Unknown" msgid "Unknown"
msgstr "" msgstr ""
...@@ -4532,6 +4801,9 @@ msgstr "" ...@@ -4532,6 +4801,9 @@ msgstr ""
msgid "Unstar" msgid "Unstar"
msgstr "" msgstr ""
msgid "Unverified"
msgstr ""
msgid "Up to date" msgid "Up to date"
msgstr "" msgstr ""
...@@ -4571,6 +4843,9 @@ msgstr "" ...@@ -4571,6 +4843,9 @@ msgstr ""
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab" msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
msgstr "" msgstr ""
msgid "Use group milestones to manage issues from multiple projects in the same milestone."
msgstr ""
msgid "Use the following registration token during setup:" msgid "Use the following registration token during setup:"
msgstr "" msgstr ""
...@@ -4595,6 +4870,12 @@ msgstr "" ...@@ -4595,6 +4870,12 @@ msgstr ""
msgid "Various settings that affect GitLab performance." msgid "Various settings that affect GitLab performance."
msgstr "" msgstr ""
msgid "Verification information"
msgstr ""
msgid "Verified"
msgstr ""
msgid "View and edit lines" msgid "View and edit lines"
msgstr "" msgstr ""
...@@ -4921,28 +5202,64 @@ msgstr "" ...@@ -4921,28 +5202,64 @@ msgstr ""
msgid "by" msgid "by"
msgstr "" msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about DAST %{linkEndTag}"
msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about Dependency Scanning %{linkEndTag}"
msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about SAST %{linkEndTag}"
msgstr ""
msgid "ciReport|%{linkStartTag}Learn more about SAST image %{linkEndTag}"
msgstr ""
msgid "ciReport|%{reportName} is loading"
msgstr ""
msgid "ciReport|%{reportName} resulted in error while loading results"
msgstr ""
msgid "ciReport|%{type} detected no new security vulnerabilities" msgid "ciReport|%{type} detected no new security vulnerabilities"
msgstr "" msgstr ""
msgid "ciReport|%{type} detected no security vulnerabilities" msgid "ciReport|%{type} detected no security vulnerabilities"
msgstr "" msgstr ""
msgid "ciReport|%{type} detected no vulnerabilities"
msgstr ""
msgid "ciReport|Code quality" msgid "ciReport|Code quality"
msgstr "" msgstr ""
msgid "ciReport|DAST detected no alerts by analyzing the review app" msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr ""
msgid "ciReport|Container scanning is loading"
msgstr ""
msgid "ciReport|Container scanning resulted in error while loading results"
msgstr ""
msgid "ciReport|DAST is loading"
msgstr "" msgstr ""
msgid "ciReport|Dependency scanning" msgid "ciReport|DAST resulted in error while loading results"
msgstr ""
msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code\\'s dependencies."
msgstr "" msgstr ""
msgid "ciReport|Dependency scanning detected" msgid "ciReport|Dependency scanning detected"
msgstr "" msgstr ""
msgid "ciReport|Dependency scanning detected no new security vulnerabilities" msgid "ciReport|Dependency scanning is loading"
msgstr ""
msgid "ciReport|Dependency scanning resulted in error while loading results"
msgstr "" msgstr ""
msgid "ciReport|Dependency scanning detected no security vulnerabilities" msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr "" msgstr ""
msgid "ciReport|Failed to load %{reportName} report" msgid "ciReport|Failed to load %{reportName} report"
...@@ -4969,19 +5286,13 @@ msgstr "" ...@@ -4969,19 +5286,13 @@ msgstr ""
msgid "ciReport|Performance metrics" msgid "ciReport|Performance metrics"
msgstr "" msgstr ""
msgid "ciReport|SAST"
msgstr ""
msgid "ciReport|SAST detected" msgid "ciReport|SAST detected"
msgstr "" msgstr ""
msgid "ciReport|SAST detected no new security vulnerabilities" msgid "ciReport|SAST is loading"
msgstr ""
msgid "ciReport|SAST detected no security vulnerabilities"
msgstr "" msgstr ""
msgid "ciReport|SAST:container no vulnerabilities were found" msgid "ciReport|SAST resulted in error while loading results"
msgstr "" msgstr ""
msgid "ciReport|Security scanning" msgid "ciReport|Security scanning"
...@@ -4990,10 +5301,22 @@ msgstr "" ...@@ -4990,10 +5301,22 @@ msgstr ""
msgid "ciReport|Security scanning failed loading any results" msgid "ciReport|Security scanning failed loading any results"
msgstr "" msgstr ""
msgid "ciReport|Security scanning is loading"
msgstr ""
msgid "ciReport|Show complete code vulnerabilities report" msgid "ciReport|Show complete code vulnerabilities report"
msgstr "" msgstr ""
msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}" msgid "ciReport|Static Application Security Testing (SAST) detects known vulnerabilities in your source code."
msgstr ""
msgid "ciReport|There was an error loading SAST report"
msgstr ""
msgid "ciReport|There was an error loading dependency scanning report"
msgstr ""
msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved."
msgstr "" msgstr ""
msgid "ciReport|no vulnerabilities" msgid "ciReport|no vulnerabilities"
...@@ -5013,6 +5336,9 @@ msgid_plural "days" ...@@ -5013,6 +5336,9 @@ msgid_plural "days"
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
msgid "deploy token"
msgstr ""
msgid "detected %d fixed vulnerability" msgid "detected %d fixed vulnerability"
msgid_plural "detected %d fixed vulnerabilities" msgid_plural "detected %d fixed vulnerabilities"
msgstr[0] "" msgstr[0] ""
...@@ -5035,9 +5361,6 @@ msgstr "" ...@@ -5035,9 +5361,6 @@ msgstr ""
msgid "importing" msgid "importing"
msgstr "" msgstr ""
msgid "in progress"
msgstr ""
msgid "is invalid because there is downstream lock" msgid "is invalid because there is downstream lock"
msgstr "" msgstr ""
...@@ -5115,6 +5438,9 @@ msgstr "" ...@@ -5115,6 +5438,9 @@ msgstr ""
msgid "mrWidget|Closes" msgid "mrWidget|Closes"
msgstr "" msgstr ""
msgid "mrWidget|Create an issue to resolve them later"
msgstr ""
msgid "mrWidget|Deployment statistics are not available currently" msgid "mrWidget|Deployment statistics are not available currently"
msgstr "" msgstr ""
...@@ -5211,6 +5537,9 @@ msgstr "" ...@@ -5211,6 +5537,9 @@ msgstr ""
msgid "mrWidget|There are merge conflicts" msgid "mrWidget|There are merge conflicts"
msgstr "" msgstr ""
msgid "mrWidget|There are unresolved discussions. Please resolve these discussions"
msgstr ""
msgid "mrWidget|This merge request failed to be merged automatically" msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "" msgstr ""
......
...@@ -31,6 +31,7 @@ module QA ...@@ -31,6 +31,7 @@ module QA
autoload :Project, 'qa/factory/resource/project' autoload :Project, 'qa/factory/resource/project'
autoload :MergeRequest, 'qa/factory/resource/merge_request' autoload :MergeRequest, 'qa/factory/resource/merge_request'
autoload :DeployKey, 'qa/factory/resource/deploy_key' autoload :DeployKey, 'qa/factory/resource/deploy_key'
autoload :Branch, 'qa/factory/resource/branch'
autoload :SecretVariable, 'qa/factory/resource/secret_variable' autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner' autoload :Runner, 'qa/factory/resource/runner'
autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token' autoload :PersonalAccessToken, 'qa/factory/resource/personal_access_token'
...@@ -132,6 +133,7 @@ module QA ...@@ -132,6 +133,7 @@ module QA
autoload :Repository, 'qa/page/project/settings/repository' autoload :Repository, 'qa/page/project/settings/repository'
autoload :CICD, 'qa/page/project/settings/ci_cd' autoload :CICD, 'qa/page/project/settings/ci_cd'
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys' autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
autoload :ProtectedBranches, 'qa/page/project/settings/protected_branches'
autoload :SecretVariables, 'qa/page/project/settings/secret_variables' autoload :SecretVariables, 'qa/page/project/settings/secret_variables'
autoload :Runners, 'qa/page/project/settings/runners' autoload :Runners, 'qa/page/project/settings/runners'
autoload :MergeRequest, 'qa/page/project/settings/merge_request' autoload :MergeRequest, 'qa/page/project/settings/merge_request'
......
module QA
module Factory
module Resource
class Branch < Factory::Base
attr_accessor :project, :branch_name, :allow_to_push, :protected
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'protected-branch-project'
end
product :name do
Page::Project::Settings::Repository.act do
expand_protected_branches(&:last_branch_name)
end
end
product :push_allowance do
Page::Project::Settings::Repository.act do
expand_protected_branches(&:last_push_allowance)
end
end
def initialize
@branch_name = 'test/branch'
@allow_to_push = true
@protected = false
end
def fabricate!
project.visit!
Factory::Repository::Push.fabricate! do |resource|
resource.project = project
resource.file_name = 'kick-off.txt'
resource.commit_message = 'First commit'
end
branch = Factory::Repository::Push.fabricate! do |resource|
resource.project = project
resource.file_name = 'README.md'
resource.commit_message = 'Add readme'
resource.branch_name = "master:#{@branch_name}"
end
Page::Project::Show.act { wait_for_push }
# The upcoming process will make it access the Protected Branches page,
# select the already created branch and protect it according
# to `allow_to_push` variable.
return branch unless @protected
Page::Menu::Side.act do
click_repository_settings
end
Page::Project::Settings::Repository.perform do |setting|
setting.expand_protected_branches do |page|
page.select_branch(branch_name)
if allow_to_push
page.allow_devs_and_masters_to_push
else
page.allow_no_one_to_push
end
page.protect_branch
end
end
end
end
end
end
end
require 'cgi' require 'cgi'
require 'uri' require 'uri'
require 'open3'
module QA module QA
module Git module Git
class Repository class Repository
include Scenario::Actable include Scenario::Actable
attr_reader :push_error
def self.perform(*args) def self.perform(*args)
Dir.mktmpdir do |dir| Dir.mktmpdir do |dir|
Dir.chdir(dir) { super } Dir.chdir(dir) { super }
...@@ -65,7 +68,8 @@ module QA ...@@ -65,7 +68,8 @@ module QA
end end
def push_changes(branch = 'master') def push_changes(branch = 'master')
`git push #{@uri.to_s} #{branch} #{suppress_output}` # capture3 returns stdout, stderr and status.
_, @push_error, _ = Open3.capture3("git push #{@uri} #{branch} #{suppress_output}")
end end
def commits def commits
......
module QA
module Page
module Project
module Settings
class ProtectedBranches < Page::Base
view 'app/views/projects/protected_branches/shared/_dropdown.html.haml' do
element :protected_branch_select
element :protected_branch_dropdown
end
view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
element :allowed_to_push_select
element :allowed_to_push_dropdown
end
view 'app/views/projects/protected_branches/shared/_branches_list.html.haml' do
element :protected_branches_list
end
view 'app/views/projects/protected_branches/shared/_protected_branch.html.haml' do
element :protected_branch_name
end
def select_branch(branch_name)
click_element :protected_branch_select
within_element(:protected_branch_dropdown) do
click_on branch_name
end
end
def allow_no_one_to_push
allow_to_push('No one')
end
def allow_devs_and_masters_to_push
allow_to_push('Developers + Masters')
end
def protect_branch
click_on 'Protect'
end
def last_branch_name
within_element(:protected_branches_list) do
all('.qa-protected-branch-name').last
end
end
def last_push_allowance
within_element(:protected_branches_list) do
all('.qa-allowed-to-push').last
end
end
private
def allow_to_push(text)
click_element :allowed_to_push_select
within_element(:allowed_to_push_dropdown) do
click_on text
end
end
end
end
end
end
end
...@@ -14,6 +14,12 @@ module QA ...@@ -14,6 +14,12 @@ module QA
DeployKeys.perform(&block) DeployKeys.perform(&block)
end end
end end
def expand_protected_branches(&block)
expand_section('Protected Branches') do
ProtectedBranches.perform(&block)
end
end
end end
end end
end end
......
...@@ -21,6 +21,11 @@ module QA ...@@ -21,6 +21,11 @@ module QA
element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)" element :new_issue_link, "link_to 'New issue', new_project_issue_path(@project)"
end end
view 'app/views/shared/_ref_switcher.html.haml' do
element :branches_select
element :branches_dropdown
end
def choose_repository_clone_http def choose_repository_clone_http
choose_repository_clone('HTTP', 'http') choose_repository_clone('HTTP', 'http')
end end
...@@ -44,6 +49,18 @@ module QA ...@@ -44,6 +49,18 @@ module QA
find('.qa-project-name').text find('.qa-project-name').text
end end
def switch_to_branch(branch_name)
find_element(:branches_select).click
within_element(:branches_dropdown) do
click_on branch_name
end
end
def last_commit_content
find_element(:commit_content).text
end
def new_merge_request def new_merge_request
wait(reload: true) do wait(reload: true) do
has_css?(element_selector_css(:create_merge_request)) has_css?(element_selector_css(:create_merge_request))
......
module QA
feature 'branch protection support', :core do
given(:branch_name) { 'protected-branch' }
given(:commit_message) { 'Protected push commit message' }
given(:project) do
Factory::Resource::Project.fabricate! do |resource|
resource.name = 'protected-branch-project'
end
end
given(:location) do
Page::Project::Show.act do
choose_repository_clone_http
repository_location
end
end
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
end
scenario 'user is able to protect a branch' do
protected_branch = Factory::Resource::Branch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allow_to_push = true
resource.protected = true
end
expect(protected_branch.name).to have_content(branch_name)
expect(protected_branch.push_allowance).to have_content('Developers + Masters')
end
scenario 'users without authorization cannot push to protected branch' do
Factory::Resource::Branch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
resource.allow_to_push = false
resource.protected = true
end
project.visit!
Git::Repository.perform do |repository|
repository.location = location
repository.use_default_credentials
repository.act do
clone
configure_identity('GitLab QA', 'root@gitlab.com')
checkout('protected-branch')
commit_file('README.md', 'readme content', 'Add a readme')
push_changes('protected-branch')
end
expect(repository.push_error)
.to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
expect(repository.push_error)
.to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
end
end
require 'spec_helper'
describe 'IDE', :js do
describe 'sub-groups' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, :repository, namespace: subgroup) }
before do
subgroup_project.add_master(user)
sign_in(user)
visit project_path(subgroup_project)
click_link('Web IDE')
wait_for_requests
end
it 'loads project in web IDE' do
expect(page).to have_selector('.context-header', text: subgroup_project.name)
end
end
end
...@@ -27,6 +27,23 @@ describe 'Merge request > User scrolls to note on load', :js do ...@@ -27,6 +27,23 @@ describe 'Merge request > User scrolls to note on load', :js do
expect(fragment_position_top).to be < (page_scroll_y + page_height) expect(fragment_position_top).to be < (page_scroll_y + page_height)
end end
it 'renders un-collapsed notes with diff' do
page.current_window.resize_to(1000, 1000)
visit "#{project_merge_request_path(project, merge_request)}#{fragment_id}"
page.execute_script "window.scrollTo(0,0)"
note_element = find(fragment_id)
note_container = note_element.ancestor('.js-toggle-container')
expect(note_element.visible?).to eq true
page.within note_container do
expect(page).not_to have_selector('.js-error-lazy-load-diff')
end
end
it 'expands collapsed notes' do it 'expands collapsed notes' do
visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}" visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}"
note_element = find(collapsed_fragment_id) note_element = find(collapsed_fragment_id)
......
...@@ -33,7 +33,7 @@ feature 'Gcp Cluster', :js do ...@@ -33,7 +33,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
end end
context 'when user filled form with valid parameters' do context 'when user filled form with valid parameters' do
...@@ -139,7 +139,7 @@ feature 'Gcp Cluster', :js do ...@@ -139,7 +139,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
fill_in 'cluster_name', with: 'dev-cluster' fill_in 'cluster_name', with: 'dev-cluster'
...@@ -159,7 +159,7 @@ feature 'Gcp Cluster', :js do ...@@ -159,7 +159,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123' fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
fill_in 'cluster_name', with: 'dev-cluster' fill_in 'cluster_name', with: 'dev-cluster'
...@@ -177,7 +177,7 @@ feature 'Gcp Cluster', :js do ...@@ -177,7 +177,7 @@ feature 'Gcp Cluster', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
end end
it 'user sees a login page' do it 'user sees a login page' do
......
...@@ -83,7 +83,7 @@ feature 'Clusters', :js do ...@@ -83,7 +83,7 @@ feature 'Clusters', :js do
visit project_clusters_path(project) visit project_clusters_path(project)
click_link 'Add Kubernetes cluster' click_link 'Add Kubernetes cluster'
click_link 'Create on GKE' click_link 'Create on Google Kubernetes Engine'
end end
it 'user sees a login page' do it 'user sees a login page' do
......
require 'spec_helper'
describe 'Project Jobs Permissions' do
let(:user) { create(:user) }
let(:group) { create(:group, name: 'some group') }
let(:project) { create(:project, :repository, namespace: group) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.sha, ref: 'master') }
let!(:job) { create(:ci_build, :running, :coverage, :trace_artifact, pipeline: pipeline) }
before do
sign_in(user)
project.enable_ci
end
describe 'jobs pages' do
shared_examples 'recent job page details responds with status' do |status|
before do
visit project_job_path(project, job)
end
it { expect(status_code).to eq(status) }
end
shared_examples 'project jobs page responds with status' do |status|
before do
visit project_jobs_path(project)
end
it { expect(status_code).to eq(status) }
end
context 'when public access for jobs is disabled' do
before do
project.update(public_builds: false)
end
context 'when user is a guest' do
before do
project.add_guest(user)
end
it_behaves_like 'recent job page details responds with status', 404
it_behaves_like 'project jobs page responds with status', 404
end
context 'when project is internal' do
before do
project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
it_behaves_like 'recent job page details responds with status', 404
it_behaves_like 'project jobs page responds with status', 404
end
end
context 'when public access for jobs is enabled' do
before do
project.update(public_builds: true)
end
context 'when project is internal' do
before do
project.update(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
end
it_behaves_like 'recent job page details responds with status', 200 do
it 'renders job details', :js do
expect(page).to have_content "Job ##{job.id}"
expect(page).to have_css '#build-trace'
end
end
it_behaves_like 'project jobs page responds with status', 200 do
it 'renders job' do
page.within('.build') do
expect(page).to have_content("##{job.id}")
.and have_content(job.sha[0..7])
.and have_content(job.ref)
.and have_content(job.name)
end
end
end
end
end
end
describe 'artifacts page' do
context 'when recent job has artifacts available' do
before do
artifacts = Rails.root.join('spec/fixtures/ci_build_artifacts.zip')
archive = fixture_file_upload(artifacts, 'application/zip')
job.update_attributes(legacy_artifacts_file: archive)
end
context 'when public access for jobs is disabled' do
before do
project.update(public_builds: false)
end
context 'when user with guest role' do
before do
project.add_guest(user)
end
it 'responds with 404 status' do
visit download_project_job_artifacts_path(project, job)
expect(status_code).to eq(404)
end
end
context 'when user with reporter role' do
before do
project.add_reporter(user)
end
it 'starts download artifact' do
visit download_project_job_artifacts_path(project, job)
expect(status_code).to eq(200)
expect(page.response_headers['Content-Type']).to eq 'application/zip'
expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary'
end
end
end
end
end
end
...@@ -517,7 +517,7 @@ describe 'Pipelines', :js do ...@@ -517,7 +517,7 @@ describe 'Pipelines', :js do
end end
it 'creates a new pipeline' do it 'creates a new pipeline' do
expect { click_on 'Create pipeline' } expect { click_on 'Run pipeline' }
.to change { Ci::Pipeline.count }.by(1) .to change { Ci::Pipeline.count }.by(1)
expect(Ci::Pipeline.last).to be_web expect(Ci::Pipeline.last).to be_web
...@@ -526,7 +526,7 @@ describe 'Pipelines', :js do ...@@ -526,7 +526,7 @@ describe 'Pipelines', :js do
context 'without gitlab-ci.yml' do context 'without gitlab-ci.yml' do
before do before do
click_on 'Create pipeline' click_on 'Run pipeline'
end end
it { expect(page).to have_content('Missing .gitlab-ci.yml file') } it { expect(page).to have_content('Missing .gitlab-ci.yml file') }
...@@ -539,7 +539,7 @@ describe 'Pipelines', :js do ...@@ -539,7 +539,7 @@ describe 'Pipelines', :js do
click_link 'master' click_link 'master'
end end
expect { click_on 'Create pipeline' } expect { click_on 'Run pipeline' }
.to change { Ci::Pipeline.count }.by(1) .to change { Ci::Pipeline.count }.by(1)
end end
end end
...@@ -557,7 +557,7 @@ describe 'Pipelines', :js do ...@@ -557,7 +557,7 @@ describe 'Pipelines', :js do
it 'has field to add a new pipeline' do it 'has field to add a new pipeline' do
expect(page).to have_selector('.js-branch-select') expect(page).to have_selector('.js-branch-select')
expect(find('.js-branch-select')).to have_content project.default_branch expect(find('.js-branch-select')).to have_content project.default_branch
expect(page).to have_content('Create for') expect(page).to have_content('Run on')
end end
end end
......
import $ from 'jquery';
import DeleteModal from '~/branches/branches_delete_modal';
describe('branches delete modal', () => {
describe('setDisableDeleteButton', () => {
let submitSpy;
let $deleteButton;
beforeEach(() => {
setFixtures(`
<div id="modal-delete-branch">
<form>
<button type="submit" class="js-delete-branch">Delete</button>
</form>
</div>
`);
$deleteButton = $('.js-delete-branch');
submitSpy = jasmine.createSpy('submit').and.callFake(event => event.preventDefault());
$('#modal-delete-branch form').on('submit', submitSpy);
// eslint-disable-next-line no-new
new DeleteModal();
});
it('does not submit if button is disabled', () => {
$deleteButton.attr('disabled', true);
$deleteButton.click();
expect(submitSpy).not.toHaveBeenCalled();
});
it('submits if button is not disabled', () => {
$deleteButton.attr('disabled', false);
$deleteButton.click();
expect(submitSpy).toHaveBeenCalled();
});
});
});
...@@ -55,6 +55,16 @@ describe('Multi-file store tree mutations', () => { ...@@ -55,6 +55,16 @@ describe('Multi-file store tree mutations', () => {
expect(tree.tree[1].name).toBe('submodule'); expect(tree.tree[1].name).toBe('submodule');
expect(tree.tree[2].name).toBe('blob'); expect(tree.tree[2].name).toBe('blob');
}); });
it('keeps loading state', () => {
mutations.CREATE_TREE(localState, { treePath: 'project/master' });
mutations.SET_DIRECTORY_DATA(localState, {
data,
treePath: 'project/master',
});
expect(localState.trees['project/master'].loading).toBe(true);
});
}); });
describe('REMOVE_ALL_CHANGES_FILES', () => { describe('REMOVE_ALL_CHANGES_FILES', () => {
......
import Vue from 'vue'; import Vue from 'vue';
import pipelineFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_failed'; import PipelineFailed from '~/vue_merge_request_widget/components/states/pipeline_failed.vue';
import { removeBreakLine } from 'spec/helpers/vue_component_helper';
describe('MRWidgetPipelineFailed', () => { describe('PipelineFailed', () => {
describe('template', () => { describe('template', () => {
const Component = Vue.extend(pipelineFailedComponent); const Component = Vue.extend(PipelineFailed);
const vm = new Component({ const vm = new Component({
el: document.createElement('div'), el: document.createElement('div'),
}); });
it('should have correct elements', () => { it('should have correct elements', () => {
expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy(); expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy(); expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy();
expect(vm.$el.innerText).toContain('The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure'); expect(
removeBreakLine(vm.$el.innerText).trim(),
).toContain('The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure');
}); });
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import commitComp from '~/vue_shared/components/commit.vue'; import commitComp from '~/vue_shared/components/commit.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Commit component', () => { describe('Commit component', () => {
let props; let props;
...@@ -10,15 +11,19 @@ describe('Commit component', () => { ...@@ -10,15 +11,19 @@ describe('Commit component', () => {
CommitComponent = Vue.extend(commitComp); CommitComponent = Vue.extend(commitComp);
}); });
afterEach(() => {
component.$destroy();
});
it('should render a fork icon if it does not represent a tag', () => { it('should render a fork icon if it does not represent a tag', () => {
component = new CommitComponent({ component = mountComponent(CommitComponent, {
propsData: {
tag: false, tag: false,
commitRef: { commitRef: {
name: 'master', name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
}, },
commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', commitUrl:
'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
shortSha: 'b7836edd', shortSha: 'b7836edd',
title: 'Commit message', title: 'Commit message',
author: { author: {
...@@ -27,8 +32,7 @@ describe('Commit component', () => { ...@@ -27,8 +32,7 @@ describe('Commit component', () => {
path: '/jschatz1', path: '/jschatz1',
username: 'jschatz1', username: 'jschatz1',
}, },
}, });
}).$mount();
expect(component.$el.querySelector('.icon-container').children).toContain('svg'); expect(component.$el.querySelector('.icon-container').children).toContain('svg');
}); });
...@@ -41,7 +45,8 @@ describe('Commit component', () => { ...@@ -41,7 +45,8 @@ describe('Commit component', () => {
name: 'master', name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
}, },
commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', commitUrl:
'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
shortSha: 'b7836edd', shortSha: 'b7836edd',
title: 'Commit message', title: 'Commit message',
author: { author: {
...@@ -53,9 +58,7 @@ describe('Commit component', () => { ...@@ -53,9 +58,7 @@ describe('Commit component', () => {
commitIconSvg: '<svg></svg>', commitIconSvg: '<svg></svg>',
}; };
component = new CommitComponent({ component = mountComponent(CommitComponent, props);
propsData: props,
}).$mount();
}); });
it('should render a tag icon if it represents a tag', () => { it('should render a tag icon if it represents a tag', () => {
...@@ -63,7 +66,9 @@ describe('Commit component', () => { ...@@ -63,7 +66,9 @@ describe('Commit component', () => {
}); });
it('should render a link to the ref url', () => { it('should render a link to the ref url', () => {
expect(component.$el.querySelector('.ref-name').getAttribute('href')).toEqual(props.commitRef.ref_url); expect(component.$el.querySelector('.ref-name').getAttribute('href')).toEqual(
props.commitRef.ref_url,
);
}); });
it('should render the ref name', () => { it('should render the ref name', () => {
...@@ -71,7 +76,9 @@ describe('Commit component', () => { ...@@ -71,7 +76,9 @@ describe('Commit component', () => {
}); });
it('should render the commit short sha with a link to the commit url', () => { it('should render the commit short sha with a link to the commit url', () => {
expect(component.$el.querySelector('.commit-sha').getAttribute('href')).toEqual(props.commitUrl); expect(component.$el.querySelector('.commit-sha').getAttribute('href')).toEqual(
props.commitUrl,
);
expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha); expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha);
}); });
...@@ -88,21 +95,25 @@ describe('Commit component', () => { ...@@ -88,21 +95,25 @@ describe('Commit component', () => {
it('Should render the author avatar with title and alt attributes', () => { it('Should render the author avatar with title and alt attributes', () => {
expect( expect(
component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('data-original-title'), component.$el
.querySelector('.commit-title .avatar-image-container img')
.getAttribute('data-original-title'),
).toContain(props.author.username); ).toContain(props.author.username);
expect( expect(
component.$el.querySelector('.commit-title .avatar-image-container img').getAttribute('alt'), component.$el
.querySelector('.commit-title .avatar-image-container img')
.getAttribute('alt'),
).toContain(`${props.author.username}'s avatar`); ).toContain(`${props.author.username}'s avatar`);
}); });
}); });
it('should render the commit title', () => { it('should render the commit title', () => {
expect( expect(component.$el.querySelector('a.commit-row-message').getAttribute('href')).toEqual(
component.$el.querySelector('a.commit-row-message').getAttribute('href'), props.commitUrl,
).toEqual(props.commitUrl); );
expect( expect(component.$el.querySelector('a.commit-row-message').textContent).toContain(
component.$el.querySelector('a.commit-row-message').textContent, props.title,
).toContain(props.title); );
}); });
}); });
...@@ -114,19 +125,18 @@ describe('Commit component', () => { ...@@ -114,19 +125,18 @@ describe('Commit component', () => {
name: 'master', name: 'master',
ref_url: 'http://localhost/namespace2/gitlabhq/tree/master', ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
}, },
commitUrl: 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067', commitUrl:
'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
shortSha: 'b7836edd', shortSha: 'b7836edd',
title: null, title: null,
author: {}, author: {},
}; };
component = new CommitComponent({ component = mountComponent(CommitComponent, props);
propsData: props,
}).$mount();
expect( expect(component.$el.querySelector('.commit-title span').textContent).toContain(
component.$el.querySelector('.commit-title span').textContent, "Can't find HEAD commit for this branch",
).toContain('Cant find HEAD commit for this branch'); );
}); });
}); });
}); });
...@@ -79,6 +79,8 @@ describe Gitlab::Diff::Highlight do ...@@ -79,6 +79,8 @@ describe Gitlab::Diff::Highlight do
end end
it 'keeps the original rich line' do it 'keeps the original rich line' do
allow(Gitlab::Sentry).to receive(:track_exception)
code = %q{+ raise RuntimeError, "System commands must be given as an array of strings"} code = %q{+ raise RuntimeError, "System commands must be given as an array of strings"}
expect(subject[5].text).to eq(code) expect(subject[5].text).to eq(code)
...@@ -86,12 +88,9 @@ describe Gitlab::Diff::Highlight do ...@@ -86,12 +88,9 @@ describe Gitlab::Diff::Highlight do
end end
it 'reports to Sentry if configured' do it 'reports to Sentry if configured' do
allow(Gitlab::Sentry).to receive(:enabled?).and_return(true) expect(Gitlab::Sentry).to receive(:track_exception).and_call_original
expect(Gitlab::Sentry).to receive(:context)
expect(Raven).to receive(:capture_exception)
subject expect { subject }. to raise_exception(RangeError)
end end
end end
end end
......
...@@ -470,9 +470,20 @@ describe Gitlab::Git::Repository, seed_helper: true do ...@@ -470,9 +470,20 @@ describe Gitlab::Git::Repository, seed_helper: true do
FileUtils.rm_rf(heads_dir) FileUtils.rm_rf(heads_dir)
FileUtils.mkdir_p(heads_dir) FileUtils.mkdir_p(heads_dir)
repository.expire_has_local_branches_cache
expect(repository.has_local_branches?).to eq(false) expect(repository.has_local_branches?).to eq(false)
end end
end end
context 'memoizes the value' do
it 'returns true' do
expect(repository).to receive(:uncached_has_local_branches?).once.and_call_original
2.times do
expect(repository.has_local_branches?).to eq(true)
end
end
end
end end
context 'with gitaly' do context 'with gitaly' do
......
...@@ -7,7 +7,49 @@ describe Gitlab::Sentry do ...@@ -7,7 +7,49 @@ describe Gitlab::Sentry do
described_class.context(nil) described_class.context(nil)
expect(Raven.tags_context[:locale]).to eq(I18n.locale.to_s) expect(Raven.tags_context[:locale].to_s).to eq(I18n.locale.to_s)
end
end
describe '.track_exception' do
let(:exception) { RuntimeError.new('boom') }
before do
allow(described_class).to receive(:enabled?).and_return(true)
end
it 'raises the exception if it should' do
expect(described_class).to receive(:should_raise?).and_return(true)
expect { described_class.track_exception(exception) }
.to raise_error(RuntimeError)
end
context 'when exceptions should not be raised' do
before do
allow(described_class).to receive(:should_raise?).and_return(false)
end
it 'logs the exception with all attributes passed' do
expected_extras = {
some_other_info: 'info',
issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1'
}
expect(Raven).to receive(:capture_exception)
.with(exception, extra: a_hash_including(expected_extras))
described_class.track_exception(
exception,
issue_url: 'http://gitlab.com/gitlab-org/gitlab-ce/issues/1',
extra: { some_other_info: 'info' }
)
end
it 'sets the context' do
expect(described_class).to receive(:context)
described_class.track_exception(exception)
end
end end
end end
end end
...@@ -1469,6 +1469,12 @@ describe Repository do ...@@ -1469,6 +1469,12 @@ describe Repository do
repository.expire_emptiness_caches repository.expire_emptiness_caches
end end
it 'expires the memoized repository cache' do
allow(repository.raw_repository).to receive(:expire_has_local_branches_cache).and_call_original
repository.expire_emptiness_caches
end
end end
describe 'skip_merges option' do describe 'skip_merges option' do
......
...@@ -427,5 +427,20 @@ describe API::Repositories do ...@@ -427,5 +427,20 @@ describe API::Repositories do
let(:request) { get api(route, guest) } let(:request) { get api(route, guest) }
end end
end end
# Regression: https://gitlab.com/gitlab-org/gitlab-ce/issues/45363
describe 'Links header contains working URLs when no `order_by` nor `sort` is given' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
it 'returns `Link` header that includes URLs with default value for `order_by` & `sort`' do
get api(route, current_user)
first_link_url = response.headers['Link'].split(';').first
expect(first_link_url).to include('order_by=commits')
expect(first_link_url).to include('sort=asc')
end
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment