Commit cec262fc authored by Coung Ngo's avatar Coung Ngo

Merge branch 'master' of gitlab.com:gitlab-org/gitlab into 9838-hide-labels-from-issue-board-cards

parents d6dc962d c47991bc
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.22-chrome-73.0-node-12.x-yarn-1.16-postgresql-9.6-graphicsmagick-1.3.33"
stages:
- sync
- prepare
- quick-test
- test
......@@ -40,3 +41,4 @@ include:
- local: .gitlab/ci/setup.gitlab-ci.yml
- local: .gitlab/ci/test-metadata.gitlab-ci.yml
- local: .gitlab/ci/yaml.gitlab-ci.yml
- local: .gitlab/ci/releases.gitlab-ci.yml
---
# Syncs any changes pushed to a stable branch to the corresponding CE stable
# branch. We run this prior to any tests so that random failures don't prevent a
# sync.
sync-stable-branch:
# We don't need/want any global before/after commands, so we overwrite these
# settings.
image: alpine:edge
stage: sync
# This job should only run on EE stable branches on the canonical GitLab.com
# repository.
only:
variables:
- $CI_SERVER_HOST == "gitlab.com"
refs:
- /^[\d-]+-stable-ee$/@gitlab-org/gitlab
before_script:
- apk add --no-cache --update curl bash
after_script: []
script:
- bash scripts/sync-stable-branch.sh
......@@ -35,14 +35,12 @@ build-qa-image:
- echo "${CI_JOB_TOKEN}" | docker login --username gitlab-ci-token --password-stdin ${CI_REGISTRY}
- time docker push ${QA_IMAGE}
schedule:review-cleanup:
.base-review-cleanup:
extends:
- .default-tags
- .default-retry
- .default-only
- .only-code-qa-changes
- .only-review-schedules
- .except-deploys
stage: prepare
image: registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-charts-build-base
allow_failure: true
......@@ -55,6 +53,16 @@ schedule:review-cleanup:
script:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
schedule:review-cleanup:
extends:
- .base-review-cleanup
- .only-review-schedules
manual:review-cleanup:
extends:
- .base-review-cleanup
when: manual
.review-build-cng-base:
extends:
- .default-only
......
......@@ -259,9 +259,6 @@ gem 'loofah', '~> 2.2'
# Working with license
gem 'licensee', '~> 8.9'
# Protect against bruteforcing
gem 'rack-attack', '~> 4.4.1'
# Ace editor
gem 'ace-rails-ap', '~> 4.1.0'
......@@ -293,6 +290,9 @@ gem 'base32', '~> 0.3.0'
gem "gitlab-license", "~> 1.0"
# Protect against bruteforcing
gem 'rack-attack', '~> 6.2.0'
# Sentry integration
gem 'sentry-raven', '~> 2.9'
......
......@@ -734,8 +734,8 @@ GEM
rack (2.0.7)
rack-accept (0.4.5)
rack (>= 0.4)
rack-attack (4.4.1)
rack
rack-attack (6.2.0)
rack (>= 1.0, < 3)
rack-cors (1.0.2)
rack-oauth2 (1.9.3)
activesupport
......@@ -1256,7 +1256,7 @@ DEPENDENCIES
puma (~> 3.12)
puma_worker_killer
rack (~> 2.0.7)
rack-attack (~> 4.4.1)
rack-attack (~> 6.2.0)
rack-cors (~> 1.0.0)
rack-oauth2 (~> 1.9.3)
rack-proxy (~> 0.6.0)
......
12.3.0-pre
12.5.0-pre
// capture anything starting with http:// or https://
// up until a disallowed character or whitespace
export const blobLinkRegex = /https?:\/\/[^"<>\\^`{|}\s]+/g;
export default { blobLinkRegex };
......@@ -4,6 +4,10 @@ import Flash from '../../flash';
import { handleLocationHash } from '../../lib/utils/common_utils';
import axios from '../../lib/utils/axios_utils';
import { __ } from '~/locale';
import { blobLinkRegex } from '~/blob/blob_utils';
const SIMPLE_VIEWER_NAME = 'simple';
const RICH_VIEWER_NAME = 'rich';
export default class BlobViewer {
constructor() {
......@@ -21,7 +25,7 @@ export default class BlobViewer {
}
static initRichViewer() {
const viewer = document.querySelector('.blob-viewer[data-type="rich"]');
const viewer = document.querySelector(`.blob-viewer[data-type="${RICH_VIEWER_NAME}"]`);
if (!viewer || !viewer.dataset.richType) return;
const initViewer = promise =>
......@@ -61,8 +65,12 @@ export default class BlobViewer {
this.switcherBtns = document.querySelectorAll('.js-blob-viewer-switch-btn');
this.copySourceBtn = document.querySelector('.js-copy-blob-source-btn');
this.simpleViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="simple"]');
this.richViewer = this.$fileHolder[0].querySelector('.blob-viewer[data-type="rich"]');
this.simpleViewer = this.$fileHolder[0].querySelector(
`.blob-viewer[data-type="${SIMPLE_VIEWER_NAME}"]`,
);
this.richViewer = this.$fileHolder[0].querySelector(
`.blob-viewer[data-type="${RICH_VIEWER_NAME}"]`,
);
this.initBindings();
......@@ -71,10 +79,10 @@ export default class BlobViewer {
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.getAttribute('data-type');
let initialViewerName = initialViewer.dataset.type;
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
initialViewerName = SIMPLE_VIEWER_NAME;
}
this.switchToViewer(initialViewerName);
......@@ -91,35 +99,41 @@ export default class BlobViewer {
this.copySourceBtn.addEventListener('click', () => {
if (this.copySourceBtn.classList.contains('disabled')) return this.copySourceBtn.blur();
return this.switchToViewer('simple');
return this.switchToViewer(SIMPLE_VIEWER_NAME);
});
}
}
static linkifyURLs(viewer) {
if (viewer.dataset.linkified) return;
document.querySelectorAll('.js-blob-content .code .line').forEach(line => {
// eslint-disable-next-line no-param-reassign
line.innerHTML = line.innerHTML.replace(blobLinkRegex, '<a href="$&">$&</a>');
});
// eslint-disable-next-line no-param-reassign
viewer.dataset.linkified = true;
}
switchViewHandler(e) {
const target = e.currentTarget;
e.preventDefault();
this.switchToViewer(target.getAttribute('data-viewer'));
this.switchToViewer(target.dataset.viewer);
}
toggleCopyButtonState() {
if (!this.copySourceBtn) return;
if (this.simpleViewer.getAttribute('data-loaded')) {
this.copySourceBtn.setAttribute('title', __('Copy file contents'));
if (this.simpleViewer.dataset.loaded) {
this.copySourceBtn.dataset.title = __('Copy file contents');
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
this.copySourceBtn.setAttribute(
'title',
__('Wait for the file to load to copy its contents'),
);
this.copySourceBtn.dataset.title = __('Wait for the file to load to copy its contents');
this.copySourceBtn.classList.add('disabled');
} else {
this.copySourceBtn.setAttribute(
'title',
__('Switch to the source to copy the file contents'),
);
this.copySourceBtn.dataset.title = __('Switch to the source to copy the file contents');
this.copySourceBtn.classList.add('disabled');
}
......@@ -159,6 +173,8 @@ export default class BlobViewer {
this.$fileHolder.trigger('highlight:line');
handleLocationHash();
if (name === SIMPLE_VIEWER_NAME) BlobViewer.linkifyURLs(viewer);
this.toggleCopyButtonState();
})
.catch(() => new Flash(__('Error loading viewer')));
......@@ -166,17 +182,17 @@ export default class BlobViewer {
static loadViewer(viewerParam) {
const viewer = viewerParam;
const url = viewer.getAttribute('data-url');
const { url, loaded, loading } = viewer.dataset;
if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
if (!url || loaded || loading) {
return Promise.resolve(viewer);
}
viewer.setAttribute('data-loading', 'true');
viewer.dataset.loading = true;
return axios.get(url).then(({ data }) => {
viewer.innerHTML = data.html;
viewer.setAttribute('data-loaded', 'true');
viewer.dataset.loaded = true;
return viewer;
});
......
......@@ -4,7 +4,8 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { __ } from '~/locale';
import TemplateSelectorMediator from '../blob/file_template_mediator';
import { blobLinkRegex } from '~/blob/blob_utils';
import TemplateSelectorMediator from '~/blob/file_template_mediator';
import getModeByFileExtension from '~/lib/utils/ace_utils';
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
......@@ -17,6 +18,7 @@ export default class EditBlob {
this.initModePanesAndLinks();
this.initSoftWrap();
this.initFileSelectors();
this.initBlobContentLinkClickability();
}
configureAceEditor() {
......@@ -89,6 +91,22 @@ export default class EditBlob {
return this.editor.focus();
}
initBlobContentLinkClickability() {
this.editor.renderer.on('afterRender', () => {
document.querySelectorAll('.ace_text-layer .ace_line > *').forEach(token => {
if (token.dataset.linkified || !token.textContent.includes('http')) return;
// eslint-disable-next-line no-param-reassign
token.innerHTML = token.innerHTML.replace(
blobLinkRegex,
'<a target="_blank" href="$&">$&</a>',
);
// eslint-disable-next-line no-param-reassign
token.dataset.linkified = true;
});
});
}
initSoftWrap() {
this.isSoftWrapped = false;
this.$toggleButton = $('.soft-wrap-toggle');
......
<script>
import { mapActions, mapState } from 'vuex';
import { GlEmptyState, GlButton, GlLink, GlLoadingIcon, GlTable } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import {
GlEmptyState,
GlButton,
GlLink,
GlLoadingIcon,
GlTable,
GlSearchBoxByType,
} from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import { __ } from '~/locale';
......@@ -20,6 +27,7 @@ export default {
GlLink,
GlLoadingIcon,
GlTable,
GlSearchBoxByType,
Icon,
TimeAgo,
},
......@@ -48,8 +56,17 @@ export default {
required: true,
},
},
data() {
return {
errorSearchQuery: '',
};
},
computed: {
...mapState(['errors', 'externalUrl', 'loading']),
...mapGetters(['filterErrorsByTitle']),
filteredErrors() {
return this.errorSearchQuery ? this.filterErrorsByTitle(this.errorSearchQuery) : this.errors;
},
},
created() {
if (this.errorTrackingEnabled) {
......@@ -71,10 +88,17 @@ export default {
<gl-loading-icon :size="3" />
</div>
<div v-else>
<div class="d-flex justify-content-end">
<div class="d-flex flex-row justify-content-around bg-secondary border">
<gl-search-box-by-type
v-model="errorSearchQuery"
class="col-lg-10 m-3 p-0"
:placeholder="__('Search or filter results...')"
type="search"
autofocus
/>
<gl-button
v-track-event="trackViewInSentryOptions(externalUrl)"
class="my-3 ml-auto"
class="m-3"
variant="primary"
:href="externalUrl"
target="_blank"
......@@ -84,7 +108,14 @@ export default {
</gl-button>
</div>
<gl-table :items="errors" :fields="$options.fields" :show-empty="true" fixed stacked="sm">
<gl-table
class="mt-3"
:items="filteredErrors"
:fields="$options.fields"
:show-empty="true"
fixed
stacked="sm"
>
<template slot="HEAD_events" slot-scope="data">
<div class="text-md-right">{{ data.label }}</div>
</template>
......
export const filterErrorsByTitle = state => errorQuery =>
state.errors.filter(error => error.title.match(new RegExp(`${errorQuery}`, 'i')));
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
Vue.use(Vuex);
......@@ -14,6 +15,7 @@ export const createStore = () =>
},
actions,
mutations,
getters,
});
export default createStore();
<script>
import { s__, __ } from '~/locale';
import { GlLink, GlButton, GlTooltip } from '@gitlab/ui';
import { GlLink, GlButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat';
import { debounceByAnimationFrame, roundOffFloat } from '~/lib/utils/common_utils';
import { roundOffFloat } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
import { chartHeight, graphTypes, lineTypes, symbolSizes, dateFormats } from '../../constants';
import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
let debouncedResize;
export default {
components: {
GlAreaChart,
......@@ -22,6 +20,9 @@ export default {
GlLink,
Icon,
},
directives: {
GlResizeObserverDirective,
},
inheritAttrs: false,
props: {
graphData: {
......@@ -29,10 +30,6 @@ export default {
required: true,
validator: graphDataValidatorForValues.bind(null, false),
},
containerWidth: {
type: Number,
required: true,
},
deploymentData: {
type: Array,
required: false,
......@@ -206,21 +203,13 @@ export default {
return `${this.graphData.y_label}`;
},
},
watch: {
containerWidth: 'onResize',
},
mounted() {
const graphTitleEl = this.$refs.graphTitle;
if (graphTitleEl && graphTitleEl.scrollWidth > graphTitleEl.offsetWidth) {
this.showTitleTooltip = true;
}
},
beforeDestroy() {
window.removeEventListener('resize', debouncedResize);
},
created() {
debouncedResize = debounceByAnimationFrame(this.onResize);
window.addEventListener('resize', debouncedResize);
this.setSvg('rocket');
this.setSvg('scroll-handle');
},
......@@ -276,7 +265,7 @@ export default {
</script>
<template>
<div class="prometheus-graph">
<div v-gl-resize-observer-directive="onResize" class="prometheus-graph">
<div class="prometheus-graph-header">
<h5
ref="graphTitle"
......
......@@ -22,12 +22,9 @@ import MonitorTimeSeriesChart from './charts/time_series.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
import { sidebarAnimationDuration } from '../constants';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import { getTimeDiff, isValidDate, downloadCSVOptions, generateLinkToChartOptions } from '../utils';
let sidebarMutationObserver;
export default {
components: {
VueDraggable,
......@@ -167,7 +164,6 @@ export default {
data() {
return {
state: 'gettingStarted',
elWidth: 0,
formIsValid: null,
selectedTimeWindow: {},
isRearrangingPanels: false,
......@@ -214,11 +210,6 @@ export default {
projectPath: this.projectPath,
});
},
beforeDestroy() {
if (sidebarMutationObserver) {
sidebarMutationObserver.disconnect();
}
},
mounted() {
if (!this.hasMetrics) {
this.setGettingStartedEmptyState();
......@@ -239,13 +230,6 @@ export default {
} else {
this.fetchData(range);
}
sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
attributes: true,
childList: false,
subtree: false,
});
}
},
methods: {
......@@ -306,11 +290,6 @@ export default {
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
},
onSidebarMutation() {
setTimeout(() => {
this.elWidth = this.$el.clientWidth;
}, sidebarAnimationDuration);
},
toggleRearrangingPanels() {
this.isRearrangingPanels = !this.isRearrangingPanels;
},
......@@ -503,7 +482,6 @@ export default {
generateLink(groupData.group, graphData.title, graphData.y_label)
"
:graph-data="graphData"
:dashboard-width="elWidth"
:alerts-endpoint="alertsEndpoint"
:prometheus-alerts-available="prometheusAlertsAvailable"
:index="`${index}-${graphIndex}`"
......@@ -520,7 +498,6 @@ export default {
:graph-data="graphData"
:deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth"
:project-path="projectPath"
group-id="monitor-time-series-chart"
>
......
......@@ -45,7 +45,7 @@ export default {
<div v-if="showPanels" class="card prometheus-panel">
<div class="card-header d-flex align-items-center">
<h4 class="flex-grow-1">{{ name }}</h4>
<a role="button" @click="collapse">
<a role="button" class="js-graph-group-toggle" @click="collapse">
<icon :size="16" :aria-label="__('Toggle collapse')" :name="caretIcon" />
</a>
</div>
......
......@@ -40,10 +40,6 @@ export default {
type: Object,
required: true,
},
dashboardWidth: {
type: Number,
required: true,
},
index: {
type: String,
required: false,
......@@ -103,7 +99,6 @@ export default {
:deployment-data="deploymentData"
:project-path="projectPath"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="dashboardWidth"
group-id="monitor-area-chart"
>
<div class="d-flex align-items-center">
......
import { __ } from '~/locale';
<script>
export default {
data() {
return {
inputEnabled: false,
urlOrRequestId: '',
};
},
methods: {
toggleInput() {
this.inputEnabled = !this.inputEnabled;
},
addRequest() {
this.$emit('add-request', this.urlOrRequestId);
this.clearForm();
},
clearForm() {
this.urlOrRequestId = '';
this.toggleInput();
},
},
};
</script>
<template>
<div id="peek-view-add-request" class="view">
<form class="form-inline" @submit.prevent>
<button
class="btn-blank btn-link bold"
type="button"
:title="__(`Add request manually`)"
@click="toggleInput"
>
+
</button>
<input
v-if="inputEnabled"
v-model="urlOrRequestId"
type="text"
:placeholder="__(`URL or request ID`)"
class="form-control form-control-sm d-inline-block ml-1"
@keyup.enter="addRequest"
@keyup.esc="clearForm"
/>
</form>
</div>
</template>
<script>
import { glEmojiTag } from '~/emoji';
import AddRequest from './add_request.vue';
import DetailedMetric from './detailed_metric.vue';
import RequestSelector from './request_selector.vue';
import { s__ } from '~/locale';
export default {
components: {
AddRequest,
DetailedMetric,
RequestSelector,
},
......@@ -118,6 +120,7 @@ export default {
>
<a :href="currentRequest.details.tracing.tracing_url">{{ s__('PerformanceBar|trace') }}</a>
</div>
<add-request v-on="$listeners" />
<request-selector
v-if="currentRequest"
:current-request="currentRequest"
......
import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import PerformanceBarService from './services/performance_bar_service';
import PerformanceBarStore from './stores/performance_bar_store';
......@@ -32,6 +34,15 @@ export default ({ container }) =>
PerformanceBarService.removeInterceptor(this.interceptor);
},
methods: {
addRequestManually(urlOrRequestId) {
if (urlOrRequestId.startsWith('https://') || urlOrRequestId.startsWith('http://')) {
// We don't need to do anything with the response, we just
// want to trace the request.
axios.get(urlOrRequestId);
} else {
this.loadRequestDetails(urlOrRequestId, urlOrRequestId);
}
},
loadRequestDetails(requestId, requestUrl) {
if (!this.store.canTrackRequest(requestUrl)) {
return;
......@@ -58,6 +69,9 @@ export default ({ container }) =>
peekUrl: this.peekUrl,
profileUrl: this.profileUrl,
},
on: {
'add-request': this.addRequestManually,
},
});
},
});
......@@ -125,19 +125,21 @@ export default {
</div>
<div class="commit-actions flex-row">
<div v-if="commit.signatureHtml" v-html="commit.signatureHtml"></div>
<gl-link
v-if="commit.latestPipeline"
v-gl-tooltip
:href="commit.latestPipeline.detailedStatus.detailsPath"
:title="statusTitle"
class="js-commit-pipeline"
>
<ci-icon
:status="commit.latestPipeline.detailedStatus"
:size="24"
:aria-label="statusTitle"
/>
</gl-link>
<div class="ci-status-link">
<gl-link
v-if="commit.latestPipeline"
v-gl-tooltip
:href="commit.latestPipeline.detailedStatus.detailsPath"
:title="statusTitle"
class="js-commit-pipeline"
>
<ci-icon
:status="commit.latestPipeline.detailedStatus"
:size="24"
:aria-label="statusTitle"
/>
</gl-link>
</div>
<div class="commit-sha-group d-flex">
<div class="label label-monospace monospace">
{{ showCommitId }}
......
......@@ -102,7 +102,6 @@ export function initUserTracking() {
window.snowplow('enableActivityTracking', 30, 30);
window.snowplow('trackPageView'); // must be after enableActivityTracking
if (opts.userId) window.snowplow('setUserId', opts.userId);
if (opts.formTracking) window.snowplow('enableFormTracking');
if (opts.linkClickTracking) window.snowplow('enableLinkClickTracking');
......
......@@ -2,7 +2,7 @@
@import 'framework/variables_overrides';
@import 'framework/mixins';
@import '@gitlab/ui/scss/gitlab_ui';
@import '@gitlab/ui/src/scss/gitlab_ui';
@import 'bootstrap_migration';
@import 'framework/layout';
......
......@@ -258,6 +258,17 @@
}
}
}
.file-editor {
.ace_underline {
text-decoration: none;
}
.ace_line a {
pointer-events: auto;
color: inherit;
}
}
}
span.idiff {
......
.memory-graph-container {
svg {
background: $white-light;
cursor: pointer;
&:hover {
box-shadow: 0 0 4px $gray-darkest inset;
}
border: 1px solid $gray-200;
}
path {
......
......@@ -29,3 +29,12 @@
color: $link;
}
}
// Links to URLs, emails, or dependencies
.code .line a {
color: inherit;
&:hover {
text-decoration: underline;
}
}
......@@ -193,11 +193,6 @@ $dark-il: #de935f;
color: $dark-highlight-color !important;
}
// Links to URLs, emails, or dependencies
.line a {
color: $dark-na;
}
.hll { background-color: $dark-hll-bg; }
.c { color: $dark-c; } /* Comment */
.err { color: $dark-err; } /* Error */
......
......@@ -193,11 +193,6 @@ $monokai-gi: #a6e22e;
color: $black !important;
}
// Links to URLs, emails, or dependencies
.line a {
color: $monokai-k;
}
.hll { background-color: $monokai-hll; }
.c { color: $monokai-c; } /* Comment */
.err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
......
......@@ -143,12 +143,6 @@
background-color: $white-normal;
}
// Links to URLs, emails, or dependencies
.line a {
color: $gl-text-color;
text-decoration: underline;
}
.hll { background-color: $white-light; }
.gd {
......
......@@ -196,11 +196,6 @@ $solarized-dark-il: #2aa198;
background-color: $solarized-dark-highlight !important;
}
// Links to URLs, emails, or dependencies
.line a {
color: $solarized-dark-kd;
}
/* Solarized Dark
For use with Jekyll and Pygments
......
......@@ -204,11 +204,6 @@ $solarized-light-il: #2aa198;
background-color: $solarized-light-highlight !important;
}
// Links to URLs, emails, or dependencies
.line a {
color: $solarized-light-kd;
}
/* Solarized Light
For use with Jekyll and Pygments
......
......@@ -209,11 +209,6 @@ span.highlight_word {
background-color: $white-highlight !important;
}
// Links to URLs, emails, or dependencies
.line a {
color: $white-nb;
}
.hll { background-color: $white-hll-bg; }
.c { color: $white-c;
......
......@@ -18,6 +18,11 @@
width: 200px;
}
input {
color: $gl-gray-400;
width: $input-short-width - 60px;
}
&.disabled {
display: none;
}
......@@ -25,7 +30,8 @@
&.production {
background-color: $perf-bar-production;
select {
select,
input {
background: $perf-bar-production;
}
}
......@@ -33,7 +39,8 @@
&.staging {
background-color: $perf-bar-staging;
select {
select,
input {
background: $perf-bar-staging;
}
}
......@@ -41,7 +48,8 @@
&.development {
background-color: $perf-bar-development;
select {
select,
input {
background: $perf-bar-development;
}
}
......
......@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
include SessionlessAuthentication
include SessionsHelper
include ConfirmEmailWarning
include Gitlab::Tracking::ControllerConcern
include Gitlab::Experimentation::ControllerConcern
......@@ -35,7 +36,7 @@ class ApplicationController < ActionController::Base
around_action :set_session_storage
after_action :set_page_title_header, if: :json_request?
after_action :limit_unauthenticated_session_times
after_action :limit_session_time, if: -> { !current_user }
protect_from_forgery with: :exception, prepend: true
......@@ -101,24 +102,6 @@ class ApplicationController < ActionController::Base
end
end
# By default, all sessions are given the same expiration time configured in
# the session store (e.g. 1 week). However, unauthenticated users can
# generate a lot of sessions, primarily for CSRF verification. It makes
# sense to reduce the TTL for unauthenticated to something much lower than
# the default (e.g. 1 hour) to limit Redis memory. In addition, Rails
# creates a new session after login, so the short TTL doesn't even need to
# be extended.
def limit_unauthenticated_session_times
return if current_user
# Rack sets this header, but not all tests may have it: https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L251-L259
return unless request.env['rack.session.options']
# This works because Rack uses these options every time a request is handled:
# https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L342
request.env['rack.session.options'][:expire_after] = Settings.gitlab['unauthenticated_session_expire_delay']
end
def render(*args)
super.tap do
# Set a header for custom error pages to prevent them from being intercepted by gitlab-workhorse
......
......@@ -142,6 +142,7 @@ class Clusters::ClustersController < Clusters::BaseController
:environment_scope,
:managed,
:base_domain,
:management_project_id,
platform_kubernetes_attributes: [
:api_url,
:token,
......@@ -155,6 +156,7 @@ class Clusters::ClustersController < Clusters::BaseController
:environment_scope,
:managed,
:base_domain,
:management_project_id,
platform_kubernetes_attributes: [
:namespace
]
......
......@@ -47,7 +47,7 @@ module RoutableActions
canonical_path = routable.full_path
if canonical_path != requested_full_path
if canonical_path.casecmp(requested_full_path) != 0
if !request.xhr? && request.format.html? && canonical_path.casecmp(requested_full_path) != 0
flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path."
end
......
......@@ -16,7 +16,7 @@ module Groups
render json: ContainerRepositoriesSerializer
.new(current_user: current_user)
.represent(@images)
.represent_read_only(@images)
end
end
end
......
......@@ -156,14 +156,21 @@ class Projects::PipelinesController < Projects::ApplicationController
def test_report
return unless Feature.enabled?(:junit_pipeline_view, project)
if pipeline_test_report == :error
render json: { status: :error_parsing_report }
return
end
respond_to do |format|
format.html do
render 'show'
end
render json: TestReportSerializer
.new(current_user: @current_user)
.represent(pipeline_test_report)
format.json do
if pipeline_test_report == :error
render json: { status: :error_parsing_report }
else
render json: TestReportSerializer
.new(current_user: @current_user)
.represent(pipeline_test_report)
end
end
end
end
private
......
......@@ -371,6 +371,7 @@ class ProjectsController < Projects::ApplicationController
:path,
:printing_merge_request_link_enabled,
:public_builds,
:remove_source_branch_after_merge,
:request_access_enabled,
:runners_token,
:tag_list,
......
......@@ -188,11 +188,9 @@ class TodosFinder
end
def by_state(items)
if params[:state].to_s == 'done'
items.done
else
items.pending
end
return items.pending if params[:state].blank?
items.with_states(params[:state])
end
def by_type(items)
......
......@@ -66,6 +66,7 @@ module Types
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true # rubocop:disable Graphql/Descriptions
field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true # rubocop:disable Graphql/Descriptions
field :printing_merge_request_link_enabled, GraphQL::BOOLEAN_TYPE, null: true # rubocop:disable Graphql/Descriptions
field :remove_source_branch_after_merge, GraphQL::BOOLEAN_TYPE, null: true, description: 'Remove the source branch by default after merge'
field :namespace, Types::NamespaceType, null: true # rubocop:disable Graphql/Descriptions
field :group, Types::GroupType, null: true # rubocop:disable Graphql/Descriptions
......
......@@ -96,8 +96,9 @@ module SearchHelper
result
end
def search_blob_title(project, filename)
filename
# Overriden in EE
def search_blob_title(project, path)
path
end
def search_service
......
......@@ -4,4 +4,20 @@ module SessionsHelper
def unconfirmed_email?
flash[:alert] == t(:unconfirmed, scope: [:devise, :failure])
end
# By default, all sessions are given the same expiration time configured in
# the session store (e.g. 1 week). However, unauthenticated users can
# generate a lot of sessions, primarily for CSRF verification. It makes
# sense to reduce the TTL for unauthenticated to something much lower than
# the default (e.g. 1 hour) to limit Redis memory. In addition, Rails
# creates a new session after login, so the short TTL doesn't even need to
# be extended.
def limit_session_time
# Rack sets this header, but not all tests may have it: https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L251-L259
return unless request.env['rack.session.options']
# This works because Rack uses these options every time a request is handled:
# https://github.com/rack/rack/blob/fdcd03a3c5a1c51d1f96fc97f9dfa1a9deac0c77/lib/rack/session/abstract/id.rb#L342
request.env['rack.session.options'][:expire_after] = Settings.gitlab['unauthenticated_session_expire_delay']
end
end
......@@ -78,12 +78,42 @@ module Clusters
"controller" => {
"config" => {
"enable-modsecurity" => "true",
"enable-owasp-modsecurity-crs" => "true"
}
"enable-owasp-modsecurity-crs" => "true",
"modsecurity.conf" => modsecurity_config_content
},
"extraVolumeMounts" => [
{
"name" => "modsecurity-template-volume",
"mountPath" => "/etc/nginx/modsecurity/modsecurity.conf",
"subPath" => "modsecurity.conf"
}
],
"extraVolumes" => [
{
"name" => "modsecurity-template-volume",
"configMap" => {
"name" => "ingress-nginx-ingress-controller",
"items" => [
{
"key" => "modsecurity.conf",
"path" => "modsecurity.conf"
}
]
}
}
]
}
}
end
def modsecurity_config_content
File.read(modsecurity_config_file_path)
end
def modsecurity_config_file_path
Rails.root.join('vendor', 'ingress', 'modsecurity.conf')
end
def content_values
YAML.load_file(chart_values_file).deep_merge!(specification)
end
......
......@@ -9,8 +9,6 @@ module Clusters
self.table_name = 'clusters'
PROJECT_ONLY_APPLICATIONS = {
}.freeze
APPLICATIONS = {
Applications::Helm.application_name => Applications::Helm,
Applications::Ingress.application_name => Applications::Ingress,
......@@ -20,7 +18,7 @@ module Clusters
Applications::Jupyter.application_name => Applications::Jupyter,
Applications::Knative.application_name => Applications::Knative,
Applications::ElasticStack.application_name => Applications::ElasticStack
}.merge(PROJECT_ONLY_APPLICATIONS).freeze
}.freeze
DEFAULT_ENVIRONMENT = '*'
KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'
......@@ -117,6 +115,8 @@ module Clusters
scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
scope :for_project_namespace, -> (namespace_id) { joins(:projects).where(projects: { namespace_id: namespace_id }) }
def self.ancestor_clusters_for_clusterable(clusterable, hierarchy_order: :asc)
return [] if clusterable.is_a?(Instance)
......
......@@ -20,7 +20,7 @@ module Clusters
.with
.recursive(cte.to_arel)
.from(cte_alias)
.order(DEPTH_COLUMN => :asc)
.order(depth_order_clause)
end
private
......@@ -40,7 +40,7 @@ module Clusters
end
if clusterable.is_a?(::Project) && include_management_project
cte << management_clusters_query
cte << same_namespace_management_clusters_query
end
cte << base_query
......@@ -49,13 +49,42 @@ module Clusters
cte
end
# Returns project-level clusters where the project is the management project
# for the cluster. The management project has to be in the same namespace /
# group as the cluster's project.
#
# Support for management project in sub-groups is planned in
# https://gitlab.com/gitlab-org/gitlab/issues/34650
#
# NB: group_parent_id is un-used but we still need to match the same number of
# columns as other queries in the CTE.
def same_namespace_management_clusters_query
clusterable.management_clusters
.project_type
.select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"])
.for_project_namespace(clusterable.namespace_id)
end
# Management clusters should be first in the hierarchy so we use 0 for the
# depth column.
#
# group_parent_id is un-used but we still need to match the same number of
# columns as other queries in the CTE.
def management_clusters_query
clusterable.management_clusters.select([clusters_star, 'NULL AS group_parent_id', "0 AS #{DEPTH_COLUMN}"])
# Only applicable if the clusterable is a project (most especially when
# requesting project.deployment_platform).
def depth_order_clause
return { DEPTH_COLUMN => :asc } unless clusterable.is_a?(::Project) && include_management_project
order = <<~SQL
(CASE clusters.management_project_id
WHEN :project_id THEN 0
ELSE #{DEPTH_COLUMN}
END) ASC
SQL
values = {
project_id: clusterable.id
}
model.sanitize_sql_array([Arel.sql(order), values])
end
def group_clusters_base_query
......
......@@ -12,7 +12,7 @@ module DeploymentPlatform
private
def cluster_management_project_enabled?
Feature.enabled?(:cluster_management_project, default_enabled: true)
Feature.enabled?(:cluster_management_project, self)
end
def find_deployment_platform(environment)
......
......@@ -11,7 +11,7 @@ class ContainerRepository < ApplicationRecord
delegate :client, to: :registry
scope :ordered, -> { order(:name) }
scope :with_api_entity_associations, -> { preload(:project) }
scope :with_api_entity_associations, -> { preload(project: [:route, { namespace: :route }]) }
# rubocop: disable CodeReuse/ServiceClass
def registry
......
......@@ -91,6 +91,7 @@ class Project < ApplicationRecord
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
default_value_for :remove_source_branch_after_merge, true
add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
......
# frozen_string_literal: true
class Release < ApplicationRecord
include Presentable
include CacheMarkdownField
include Gitlab::Utils::StrongMemoize
......
# frozen_string_literal: true
class ReleasePresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
presents :release
delegate :project, :tag, to: :release
def commit_path
return unless release.commit && can_download_code?
project_commit_path(project, release.commit.id)
end
def tag_path
return unless can_download_code?
project_tag_path(project, release.tag)
end
def merge_requests_url
return unless release_mr_issue_urls_available?
project_merge_requests_url(project, params_for_issues_and_mrs)
end
def issues_url
return unless release_mr_issue_urls_available?
project_issues_url(project, params_for_issues_and_mrs)
end
private
def can_download_code?
can?(current_user, :download_code, project)
end
def params_for_issues_and_mrs
{ scope: 'all', state: 'opened', release_tag: release.tag }
end
def release_mr_issue_urls_available?
::Feature.enabled?(:release_mr_issue_urls, project)
end
end
......@@ -2,4 +2,8 @@
class ContainerRepositoriesSerializer < BaseSerializer
entity ContainerRepositoryEntity
def represent_read_only(resource)
represent(resource, except: [:destroy_path])
end
end
......@@ -21,6 +21,8 @@ class MergeRequestDiffEntity < Grape::Entity
expose :latest?, as: :latest
expose :short_commit_sha do |merge_request_diff|
next unless merge_request_diff.head_commit_sha
short_sha(merge_request_diff.head_commit_sha)
end
......
......@@ -64,19 +64,13 @@ module Clusters
end
def invalid_application?
unknown_application? || (!cluster.project_type? && project_only_application?) || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack))
unknown_application? || (application_name == Applications::ElasticStack.application_name && !Feature.enabled?(:enable_cluster_application_elastic_stack))
end
def unknown_application?
Clusters::Cluster::APPLICATIONS.keys.exclude?(application_name)
end
# These applications will need extra configuration to enable them to work
# with groups of projects
def project_only_application?
Clusters::Cluster::PROJECT_ONLY_APPLICATIONS.include?(application_name)
end
def application_name
params[:application]
end
......
......@@ -9,7 +9,55 @@ module Clusters
end
def execute(cluster)
cluster.update(params)
if validate_params(cluster)
cluster.update(params)
else
false
end
end
private
def can_admin_pipeline_for_project?(project)
Ability.allowed?(current_user, :admin_pipeline, project)
end
def validate_params(cluster)
if params[:management_project_id]
management_project = management_project_scope(cluster).find_by_id(params[:management_project_id])
unless management_project
cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action'))
return false
end
unless can_admin_pipeline_for_project?(management_project)
# Use same message as not found to prevent enumeration
cluster.errors.add(:management_project_id, _('Project does not exist or you don\'t have permission to perform this action'))
return false
end
end
true
end
def management_project_scope(cluster)
return ::Project.all if cluster.instance_type?
group =
if cluster.group_type?
cluster.first_group
elsif cluster.project_type?
cluster.first_project&.namespace
end
# Prevent users from selecting nested projects until
# https://gitlab.com/gitlab-org/gitlab/issues/34650 is resolved
include_subgroups = cluster.group_type?
::GroupProjectsFinder.new(group: group, current_user: current_user, options: { only_owned: true, include_subgroups: include_subgroups }).execute
end
end
end
......@@ -10,13 +10,20 @@ module MergeRequests
# TODO: this should handle all quick actions that don't have side effects
# https://gitlab.com/gitlab-org/gitlab-foss/issues/53658
merge_quick_actions_into_params!(merge_request, only: [:target_branch])
merge_request.merge_params['force_remove_source_branch'] = params.delete(:force_remove_source_branch) if params.has_key?(:force_remove_source_branch)
# Assign the projects first so we can use policies for `filter_params`
merge_request.author = current_user
merge_request.source_project = find_source_project
merge_request.target_project = find_target_project
# Source project sets the default source branch removal setting
merge_request.merge_params['force_remove_source_branch'] =
if params.key?(:force_remove_source_branch)
params.delete(:force_remove_source_branch)
else
merge_request.source_project.remove_source_branch_after_merge?
end
filter_params(merge_request)
# merge_request.assign_attributes(...) below is a Rails
......
......@@ -7,9 +7,9 @@
.form-check
= f.check_box :mirror_available, class: 'form-check-input'
= f.label :mirror_available, class: 'form-check-label' do
= _('Allow mirrors to be set up for projects')
= _('Allow repository mirroring to be configured by project maintainers')
%span.form-text.text-muted
= _('If disabled, only admins will be able to set up mirrors in projects.')
= _('If disabled, only admins will be able to configure repository mirroring.')
= link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
= render_if_exists 'admin/application_settings/mirror_settings', form: f
......
......@@ -29,4 +29,4 @@
= render 'third_party_offers', application_setting: @application_setting
= render 'admin/application_settings/snowplow', expanded: expanded_by_default?
= render 'admin/application_settings/pendo'
= render_if_exists 'admin/application_settings/pendo'
......@@ -5,11 +5,11 @@
%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4
= _('Repository mirror')
= _('Repository mirroring')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded_by_default? ? 'Collapse' : 'Expand'
%p
= _('Configure push mirrors.')
= _('Configure repository mirroring.')
.settings-content
= render partial: 'repository_mirrors_form'
......
- form_field = local_assigns.fetch(:form_field, nil)
- variable = local_assigns.fetch(:variable, nil)
- key = variable[0]
- value = variable[1]
- variable_type = variable[2] || "env_var"
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
- variable_type_input_name = "#{form_field}[variables_attributes][][variable_type]"
- key_input_name = "#{form_field}[variables_attributes][][key]"
- value_input_name = "#{form_field}[variables_attributes][][secret_value]"
%li.js-row.ci-variable-row
.ci-variable-row-body.border-bottom
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
%select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.custom-select.table-section.section-15{ name: variable_type_input_name }
= options_for_select(ci_variable_type_options, variable_type)
%input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control.table-section.section-15{ type: "text",
name: key_input_name,
value: key,
placeholder: s_('CiVariables|Input variable key') }
.ci-variable-body-item.gl-show-field-errors.table-section.section-15.border-top-0.p-0
%textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ rows: 1,
name: value_input_name,
placeholder: s_('CiVariables|Input variable value') }
= value
%button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle')
......@@ -4,7 +4,7 @@
= link_to new_project_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_project")
= custom_icon("add_new_project", size: 50)
.blank-state-body
%h3.blank-state-title
Create a project
......@@ -14,7 +14,7 @@
- if current_user.can_create_group?
= link_to new_group_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_group")
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group
......@@ -23,7 +23,7 @@
= link_to new_admin_user_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_user")
= custom_icon("add_new_user", size: 50)
.blank-state-body
%h3.blank-state-title
Add people
......@@ -32,7 +32,7 @@
= link_to admin_root_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/configure_server")
= custom_icon("configure_server", size: 50)
.blank-state-body
%h3.blank-state-title
Configure GitLab
......
......@@ -4,7 +4,7 @@
- if current_user.can_create_project?
= link_to new_project_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_project")
= custom_icon("add_new_project", size: 50)
.blank-state-body
%h3.blank-state-title
Create a project
......@@ -13,7 +13,7 @@
- else
.blank-state
.blank-state-icon
= image_tag("illustrations/welcome/add_new_project")
= custom_icon("add_new_project", size: 50)
.blank-state-body
%h3.blank-state-title
Create a project
......@@ -23,7 +23,7 @@
- if current_user.can_create_group?
= link_to new_group_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/add_new_group")
= custom_icon("add_new_group", size: 50)
.blank-state-body
%h3.blank-state-title
Create a group
......@@ -33,7 +33,7 @@
- if public_project_count > 0
= link_to trending_explore_projects_path, class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/globe")
= custom_icon("globe", size: 50)
.blank-state-body
%h3.blank-state-title
Explore public projects
......@@ -46,7 +46,7 @@
= link_to "https://docs.gitlab.com/", class: "blank-state blank-state-link" do
.blank-state-icon
= image_tag("illustrations/welcome/lightbulb")
= custom_icon("lightbulb", size: 50)
.blank-state-body
%h3.blank-state-title
Learn more about GitLab
......
......@@ -11,5 +11,5 @@
= form_tag search_path, method: :get, class: 'form-inline-flex' do |f|
.field
= search_field_tag :search, '', placeholder: _('Search for projects, issues, etc.'), class: 'form-control'
= button_tag 'Search', class: 'btn btn-success', name: nil, type: 'submit'
= button_tag _('Search'), class: 'btn btn-sm btn-success', name: nil, type: 'submit'
= render 'errors/footer'
......@@ -90,4 +90,4 @@
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/snowplow'
= render 'layouts/pendo'
= render_if_exists 'layouts/pendo'
......@@ -7,4 +7,4 @@
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","#{asset_url('snowplow/sp.js')}","snowplow"));
window.snowplowOptions = #{Gitlab::Tracking.snowplow_options(@group, current_user).to_json}
window.snowplowOptions = #{Gitlab::Tracking.snowplow_options(@group).to_json}
......@@ -12,3 +12,9 @@
= form.check_box :printing_merge_request_link_enabled, class: 'form-check-input'
= form.label :printing_merge_request_link_enabled, class: 'form-check-label' do
= s_('ProjectSettings|Show link to create/view merge request when pushing from the command line')
.form-check.mb-2
= form.check_box :remove_source_branch_after_merge, class: 'form-check-input'
= form.label :remove_source_branch_after_merge, class: 'form-check-label' do
= s_("ProjectSettings|Enable 'Delete source branch' option by default")
.descr.text-secondary
= s_('ProjectSettings|Existing merge requests and protected branches are not affected')
......@@ -19,7 +19,7 @@
= author_avatar(commit, size: 36, has_tooltip: false)
.commit-row-title
%span.item-title.str-truncated-100
= link_to_markdown commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
= link_to commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
.float-right
= link_to commit.short_id, project_commit_path(@project, commit), class: "commit-sha"
&nbsp;
......
......@@ -8,8 +8,9 @@
.input-group-text
= s_("CompareBranches|Source")
= hidden_field_tag :to, params[:to]
= button_tag type: 'button', title: params[:to], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip monospace", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
.dropdown-toggle-text.str-truncated= params[:to] || _("Select branch/tag")
= button_tag type: 'button', title: params[:to], class: "btn form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-to-dropdown", selected: params[:to], field_name: :to } do
.dropdown-toggle-text.str-truncated.monospace.float-left= params[:to] || _("Select branch/tag")
= sprite_icon('arrow-down', size: 16, css_class: 'float-right')
= render 'shared/ref_dropdown'
.compare-ellipsis.inline ...
.form-group.dropdown.compare-form-group.from.js-compare-from-dropdown
......@@ -18,8 +19,9 @@
.input-group-text
= s_("CompareBranches|Target")
= hidden_field_tag :from, params[:from]
= button_tag type: 'button', title: params[:from], class: "form-control compare-dropdown-toggle js-compare-dropdown has-tooltip monospace", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text.str-truncated= params[:from] || _("Select branch/tag")
= button_tag type: 'button', title: params[:from], class: "btn form-control compare-dropdown-toggle js-compare-dropdown has-tooltip", required: true, data: { refs_url: refs_project_path(@project), toggle: "dropdown", target: ".js-compare-from-dropdown", selected: params[:from], field_name: :from } do
.dropdown-toggle-text.str-truncated.monospace.float-left= params[:from] || _("Select branch/tag")
= sprite_icon('arrow-down', size: 16, css_class: 'float-right')
= render 'shared/ref_dropdown'
&nbsp;
= button_tag s_("CompareBranches|Compare"), class: "btn btn-success commits-compare-btn"
......
......@@ -3,10 +3,12 @@
.form-group
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
= f.select :auth_method,
options_for_select(auth_options, mirror.auth_method),
{}, { class: "form-control js-mirror-auth-type qa-authentication-method" }
= f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type"
.select-wrapper
= f.select :auth_method,
options_for_select(auth_options, mirror.auth_method),
{}, { class: "form-control select-control js-mirror-auth-type qa-authentication-method" }
= icon('chevron-down')
= f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type"
.form-group
.collapse.js-well-changing-auth
......
.form-group
= label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control js-mirror-direction qa-mirror-direction', disabled: true
.select-wrapper
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control select-control js-mirror-direction qa-mirror-direction', disabled: true
= icon('chevron-down')
= render partial: "projects/mirrors/mirror_repos_push", locals: { f: f }
......@@ -23,6 +23,13 @@
%label
= s_('Pipeline|Variables')
%ul.ci-variable-list
- if params[:var]
- params[:var].each do |variable|
= render 'ci/variables/url_query_variable_row', form_field: 'pipeline', variable: variable
- if params[:file_var]
- params[:file_var].each do |variable|
- variable.push("file")
= render 'ci/variables/url_query_variable_row', form_field: 'pipeline', variable: variable
= render 'ci/variables/variable_row', form_field: 'pipeline', only_key_value: true
.form-text.text-muted
= (s_("Pipeline|Specify variable values to be used in this run. The values specified in %{settings_link} will be used by default.") % {settings_link: settings_link}).html_safe
......
......@@ -5,6 +5,7 @@
%p.settings-message.text-center
= s_("ProtectedBranch|There are currently no protected branches, protect a branch with the form above.")
- else
.flash-container
%table.table.table-bordered
%colgroup
%col{ width: "20%" }
......@@ -27,8 +28,6 @@
- if can_admin_project
%th
%tbody
%tr
%td.flash-container{ colspan: 5 }
= yield
= paginate @protected_branches, theme: 'gitlab'
......@@ -2,6 +2,6 @@
- return unless project
- blob = parse_search_result(blob)
- blob_link = project_blob_path(project, tree_join(blob.ref, blob.filename))
- blob_link = project_blob_path(project, tree_join(blob.ref, blob.path))
= render partial: 'search/results/blob_data', locals: { blob: blob, project: project, file_name: blob.filename, blob_link: blob_link }
= render partial: 'search/results/blob_data', locals: { blob: blob, project: project, path: blob.path, blob_link: blob_link }
......@@ -4,7 +4,7 @@
= link_to blob_link do
%i.fa.fa-file
%strong
= search_blob_title(project, file_name)
= search_blob_title(project, path)
- if blob.data
.file-content.code.term{ data: { qa_selector: 'file_text_content' } }
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
......@@ -2,4 +2,4 @@
- wiki_blob = parse_search_result(wiki_blob)
- wiki_blob_link = project_wiki_path(project, wiki_blob.basename)
= render partial: 'search/results/blob_data', locals: { blob: wiki_blob, project: project, file_name: wiki_blob.filename, blob_link: wiki_blob_link }
= render partial: 'search/results/blob_data', locals: { blob: wiki_blob, project: project, path: wiki_blob.path, blob_link: wiki_blob_link }
......@@ -7,3 +7,7 @@
= link_to _('Settings'), project_settings_ci_cd_path(project), class: 'alert-link'
|
= link_to _('Dismiss'), '#', class: 'hide-auto-devops-implicitly-enabled-banner alert-link', data: { project_id: project.id }
- unless Gitlab.config.registry.enabled
%div
= icon('exclamation-triangle')
= _('Container registry is not enabled on this GitLab instance. Ask an administrator to enable it in order for AutoDevOps to work.')
......@@ -10,7 +10,7 @@
%a.diff-line-num{ href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i }
= link_icon
= i
.blob-content{ data: { blob_id: blob.id } }
.blob-content.js-blob-content{ data: { blob_id: blob.id } }
%pre.code.highlight
%code
= blob.present.highlight
---
title: 'Geo: Add resigns-related fields to Geo Node Status table'
merge_request: 18379
author:
type: other
---
title: Close issues on Prometheus alert recovery
merge_request: 18431
author:
type: added
---
title: Populate new pipeline CI vars from params
merge_request: 19023
author:
type: added
---
title: Adding dropdown arrow icon and updated text alignment
merge_request:
author:
type: other
---
title: Change selects from default browser style to custom style
merge_request:
author:
type: other
---
title: Expose mergeable state of a merge request
merge_request: 18888
author: briankabiro
type: added
---
title: Remove pointer cursor from MemoryUsage chart on MR widget deployment
merge_request: 18599
author:
type: fixed
---
title: Fix "project or group was moved" alerts showing up in the wrong pages
merge_request: 18985
author:
type: fixed
---
title: Fix empty chart in collapsed sections
merge_request: 18699
author:
type: fixed
---
title: Removes arrow icons for old collapsible sections
merge_request:
author:
type: fixed
---
title: Show start and end dates in Epics list page
merge_request: 19006
author:
type: added
---
title: Bump Auto-Deploy image to v0.3.0
merge_request: 18809
author:
type: added
---
title: Add modsecurity template for ingress-controller
merge_request: 18485
author:
type: changed
title: Create table for elastic stack.
merge_request: 18015
author:
type: added
---
title: Allow adding requests to performance bar manually
merge_request: 18464
author:
type: other
---
title: Removes `export_designs` feature flag
merge_request: 18507
author: nate geslin
type: other
---
title: Adds ability to set management project for cluster via API
merge_request: 18429
author:
type: added
---
title: Drop `id` column from `ci_build_trace_sections` table
merge_request: 18741
author:
type: changed
---
title: Add extra sentence about registry to AutoDevOps popup
merge_request: 19092
author:
type: changed
---
title: Fix N+1 for group container repositories view
merge_request: 18979
author:
type: performance
---
title: Serialize short sha as nil if head commit is blank
merge_request: 19014
author:
type: fixed
---
title: Support Enable/Disable operations in Feature Flag API
merge_request: 18368
author:
type: added
---
title: Do not render links in commit message on blame page
merge_request: 19128
author:
type: performance
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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