Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
17dc78581f
commit
53b01d4a2c
2
Gemfile
2
Gemfile
|
|
@ -24,7 +24,7 @@ gem 'bundler-checksum', '~> 0.1.0', path: 'vendor/gems/bundler-checksum', requir
|
|||
# https://gitlab.com/gitlab-org/gitlab/-/issues/375713
|
||||
#
|
||||
# See https://docs.gitlab.com/ee/development/gemfile.html#upgrade-rails for guidelines when upgrading Rails
|
||||
gem 'rails', '~> 7.0.8' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
gem 'rails', '~> 7.0.8.1' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
gem 'activerecord-gitlab', path: 'gems/activerecord-gitlab' # rubocop:todo Gemfile/MissingFeatureCategory
|
||||
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
{"name":"CFPropertyList","version":"3.0.5","platform":"ruby","checksum":"a78551cd4768d78ebca98488c27e33652ef818be64697a54676d34e6434674a4"},
|
||||
{"name":"RedCloth","version":"4.3.3","platform":"ruby","checksum":"d941b8ac96e2730d2d9326d97dda9fcf64cb73532b3f902d91c18970c5f4632d"},
|
||||
{"name":"acme-client","version":"2.0.11","platform":"ruby","checksum":"edf6da9f3c5dbe3ab0c6738eb3b97978b7a60e3500445480d2a72fcc610089de"},
|
||||
{"name":"actioncable","version":"7.0.8","platform":"ruby","checksum":"1f504ddb4ab6a34f7c52e9df924441a403e9f358bace330c36dcca6358ecfb84"},
|
||||
{"name":"actionmailbox","version":"7.0.8","platform":"ruby","checksum":"9420037b801e44aa4e36cf113f4bd6eb25c17eb1b84d9c8865e8abf8846c14e5"},
|
||||
{"name":"actionmailer","version":"7.0.8","platform":"ruby","checksum":"22574f270ed80bcd158f16b99068fad7772173e21c4332504238dae58fdccf70"},
|
||||
{"name":"actionpack","version":"7.0.8","platform":"ruby","checksum":"2b998c6f6540ec07ad2e16b39f9acae22c8c4fda6b377417c2cfddf8c04d61d0"},
|
||||
{"name":"actiontext","version":"7.0.8","platform":"ruby","checksum":"f7966296cec0a48e8644b59de2bfc8b7847d43a7809dfe040015a32aecc88744"},
|
||||
{"name":"actionview","version":"7.0.8","platform":"ruby","checksum":"a22d692b9a6422f36882425301a4043fbe078a66e94a909a60a6a216246fd776"},
|
||||
{"name":"activejob","version":"7.0.8","platform":"ruby","checksum":"cb63d6a9f9af3379b7927bcb09a453d63db66ba9ec681018a8b21c5a0f8bc1b2"},
|
||||
{"name":"activemodel","version":"7.0.8","platform":"ruby","checksum":"95beb8a2f6d1e0c7b4e3c0f17771b3a3024a25ad8c6e9d2d357e3cf1d5479c00"},
|
||||
{"name":"activerecord","version":"7.0.8","platform":"ruby","checksum":"f236255235ab8c15f7a7bea3b77a35377801827e24d6e536dc776080f4dd8a13"},
|
||||
{"name":"actioncable","version":"7.0.8.1","platform":"ruby","checksum":"bc5eddc8c6412d067b9fecec6a53dd14f3bd83baca96762eb864f01fb4c40649"},
|
||||
{"name":"actionmailbox","version":"7.0.8.1","platform":"ruby","checksum":"89d721547b178b397b86e1af11adb221098134d083f481ebf90b37431982c8b8"},
|
||||
{"name":"actionmailer","version":"7.0.8.1","platform":"ruby","checksum":"4c1f470e5577a82bdfbf5fd1eca6590990371e80c48eb04ae79883a7e521f21c"},
|
||||
{"name":"actionpack","version":"7.0.8.1","platform":"ruby","checksum":"5dcd2b39cb69abe899edc9faf878fb667a64a9fe306a2e56d09e3ed3701e084a"},
|
||||
{"name":"actiontext","version":"7.0.8.1","platform":"ruby","checksum":"42e4cd0e7e8f98a8ed96d04b1d6ab83bb69afc4ceb0a46f5c18ef60fff889810"},
|
||||
{"name":"actionview","version":"7.0.8.1","platform":"ruby","checksum":"4a01b36c605dc89a5e1dd3866e6db05132387827411fb7fb62cdb9e1ca79f7f9"},
|
||||
{"name":"activejob","version":"7.0.8.1","platform":"ruby","checksum":"8f4ee9288480b9fddc5aece947bcd846546d14d96c38faedc3ab87c6c84e147b"},
|
||||
{"name":"activemodel","version":"7.0.8.1","platform":"ruby","checksum":"4115aef7f780abd168629c5d77031b014eecd84a52760ab149920148dfbe2d3f"},
|
||||
{"name":"activerecord","version":"7.0.8.1","platform":"ruby","checksum":"8e5dfbad90638944fc99b64337dffa729b1aaf2cd38ea624a08dcd9259f09366"},
|
||||
{"name":"activerecord-explain-analyze","version":"0.1.0","platform":"ruby","checksum":"5debb11fe23f35b91953a80677d80ba9284ee737fd9d148c1d7603ce45217f7b"},
|
||||
{"name":"activestorage","version":"7.0.8","platform":"ruby","checksum":"8c2cae8de321ec899c7e7c4655331714fdd57f0966215286330f5c4d95a9db34"},
|
||||
{"name":"activesupport","version":"7.0.8","platform":"ruby","checksum":"458316bb5098211ba9436d3c64d883177f09c49d1e29aa00f970d160275f13a1"},
|
||||
{"name":"activestorage","version":"7.0.8.1","platform":"ruby","checksum":"1fd641a93c93a133d016e11998953221db28736a667fe5da2331969850198f38"},
|
||||
{"name":"activesupport","version":"7.0.8.1","platform":"ruby","checksum":"b0aff5023a6f84e2fefadee80c0e1c0156ee3eea318c402bfcd40140f0e9c339"},
|
||||
{"name":"acts-as-taggable-on","version":"10.0.0","platform":"ruby","checksum":"d360e96f1622010a2f8fe5c6f480f8cf95ba0bc2072e0b9974574e5f336edb83"},
|
||||
{"name":"addressable","version":"2.8.1","platform":"ruby","checksum":"bc724a176ef02118c8a3ed6b5c04c39cf59209607ffcce77b91d0261dbadedfa"},
|
||||
{"name":"aes_key_wrap","version":"1.1.0","platform":"ruby","checksum":"b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5"},
|
||||
|
|
@ -500,12 +500,12 @@
|
|||
{"name":"rack-session","version":"1.0.2","platform":"ruby","checksum":"a02115e5420b4de036839b9811e3f7967d73446a554b42aa45106af335851d76"},
|
||||
{"name":"rack-test","version":"2.1.0","platform":"ruby","checksum":"0c61fc61904049d691922ea4bb99e28004ed3f43aa5cfd495024cc345f125dfb"},
|
||||
{"name":"rack-timeout","version":"0.6.3","platform":"ruby","checksum":"1754892eacc124d405e7f1145731ec9b7421ebd1bee5d51ddc18b72c204d0ab3"},
|
||||
{"name":"rails","version":"7.0.8","platform":"ruby","checksum":"8e43af921acf766fb429126f020ec90c3b25809631f8fbdff95c3553828d5867"},
|
||||
{"name":"rails","version":"7.0.8.1","platform":"ruby","checksum":"7deb37884ac5e9afeaeb6ad503c56e819f68e53746d621b2187322f874ba2ded"},
|
||||
{"name":"rails-controller-testing","version":"1.0.5","platform":"ruby","checksum":"741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94"},
|
||||
{"name":"rails-dom-testing","version":"2.0.3","platform":"ruby","checksum":"b140c4f39f6e609c8113137b9a60dfc2ecb89864e496f87f23a68b3b8f12d8d1"},
|
||||
{"name":"rails-html-sanitizer","version":"1.6.0","platform":"ruby","checksum":"86e9f19d2e6748890dcc2633c8945ca45baa08a1df9d8c215ce17b3b0afaa4de"},
|
||||
{"name":"rails-i18n","version":"7.0.3","platform":"ruby","checksum":"e3158e98c5332d129fd5131f171ac575eb30dbb8919b21595382b08850cf2bd3"},
|
||||
{"name":"railties","version":"7.0.8","platform":"ruby","checksum":"12325c3933efd33f8ead640197dec3b8c27c8d45607dd68b7b925896bf09cc69"},
|
||||
{"name":"railties","version":"7.0.8.1","platform":"ruby","checksum":"c2e4e3617defafad5c1c2c5761d697873197422ec49f58a96d6728f3a729ea21"},
|
||||
{"name":"rainbow","version":"3.1.1","platform":"ruby","checksum":"039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a"},
|
||||
{"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"},
|
||||
{"name":"rb-fsevent","version":"0.11.2","platform":"ruby","checksum":"43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe"},
|
||||
|
|
|
|||
108
Gemfile.lock
108
Gemfile.lock
|
|
@ -203,70 +203,70 @@ GEM
|
|||
acme-client (2.0.11)
|
||||
faraday (>= 1.0, < 3.0.0)
|
||||
faraday-retry (~> 1.0)
|
||||
actioncable (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actioncable (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailbox (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
activejob (= 7.0.8.1)
|
||||
activerecord (= 7.0.8.1)
|
||||
activestorage (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
mail (>= 2.7.1)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
actionmailer (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionmailer (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
actionview (= 7.0.8.1)
|
||||
activejob (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
net-imap
|
||||
net-pop
|
||||
net-smtp
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionpack (7.0.8.1)
|
||||
actionview (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actiontext (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
activerecord (= 7.0.8.1)
|
||||
activestorage (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
globalid (>= 0.6.0)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activejob (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activemodel (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
activerecord (7.0.8.1)
|
||||
activemodel (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
activerecord-explain-analyze (0.1.0)
|
||||
activerecord (>= 4)
|
||||
pg
|
||||
activestorage (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activestorage (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
activejob (= 7.0.8.1)
|
||||
activerecord (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.0.8.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
|
@ -1334,20 +1334,20 @@ GEM
|
|||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rack-timeout (0.6.3)
|
||||
rails (7.0.8)
|
||||
actioncable (= 7.0.8)
|
||||
actionmailbox (= 7.0.8)
|
||||
actionmailer (= 7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
actiontext (= 7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activejob (= 7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activerecord (= 7.0.8)
|
||||
activestorage (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
rails (7.0.8.1)
|
||||
actioncable (= 7.0.8.1)
|
||||
actionmailbox (= 7.0.8.1)
|
||||
actionmailer (= 7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
actiontext (= 7.0.8.1)
|
||||
actionview (= 7.0.8.1)
|
||||
activejob (= 7.0.8.1)
|
||||
activemodel (= 7.0.8.1)
|
||||
activerecord (= 7.0.8.1)
|
||||
activestorage (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 7.0.8)
|
||||
railties (= 7.0.8.1)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
|
|
@ -1361,9 +1361,9 @@ GEM
|
|||
rails-i18n (7.0.3)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 6.0.0, < 8)
|
||||
railties (7.0.8)
|
||||
actionpack (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
railties (7.0.8.1)
|
||||
actionpack (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
|
|
@ -2062,7 +2062,7 @@ DEPENDENCIES
|
|||
rack-oauth2 (~> 1.21.3)
|
||||
rack-proxy (~> 0.7.7)
|
||||
rack-timeout (~> 0.6.3)
|
||||
rails (~> 7.0.8)
|
||||
rails (~> 7.0.8.1)
|
||||
rails-controller-testing
|
||||
rails-i18n (~> 7.0)
|
||||
rainbow (~> 3.0)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import {
|
||||
GlLoadingIcon,
|
||||
GlSkeletonLoader,
|
||||
GlTable,
|
||||
GlLink,
|
||||
GlButtonGroup,
|
||||
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from '@gitlab/ui';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
|
||||
import { createAlert } from '~/alert';
|
||||
import { scrollToElement } from '~/lib/utils/common_utils';
|
||||
import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import { TYPENAME_PROJECT } from '~/graphql_shared/constants';
|
||||
|
|
@ -57,7 +58,7 @@ const INITIAL_PAGINATION_STATE = {
|
|||
export default {
|
||||
name: 'JobArtifactsTable',
|
||||
components: {
|
||||
GlLoadingIcon,
|
||||
GlSkeletonLoader,
|
||||
GlTable,
|
||||
GlLink,
|
||||
GlButtonGroup,
|
||||
|
|
@ -141,6 +142,7 @@ export default {
|
|||
this.canBulkDestroyArtifacts && {
|
||||
key: 'checkbox',
|
||||
label: '',
|
||||
thClass: 'gl-w-5p',
|
||||
},
|
||||
...this.$options.fields,
|
||||
];
|
||||
|
|
@ -208,6 +210,8 @@ export default {
|
|||
currentPage: page,
|
||||
};
|
||||
}
|
||||
|
||||
scrollToElement(this.$el);
|
||||
},
|
||||
handleRowToggle(toggleDetails, hasArtifacts, id, detailsShowing) {
|
||||
if (!hasArtifacts) return;
|
||||
|
|
@ -389,8 +393,15 @@ export default {
|
|||
details-td-class="gl-bg-gray-10! gl-p-0! gl-overflow-auto"
|
||||
>
|
||||
<template #table-busy>
|
||||
<gl-loading-icon size="lg" />
|
||||
<gl-skeleton-loader v-for="i in 20" :key="i" :width="1000" :height="75">
|
||||
<rect width="90" height="20" x="40" y="5" rx="4" />
|
||||
<rect width="300" height="40" x="180" y="5" rx="4" />
|
||||
<rect width="80" height="20" x="610" y="5" rx="4" />
|
||||
<rect width="80" height="20" x="710" y="5" rx="4" />
|
||||
<rect width="100" height="30" x="900" y="5" rx="4" />
|
||||
</gl-skeleton-loader>
|
||||
</template>
|
||||
|
||||
<template v-if="canBulkDestroyArtifacts" #head(checkbox)>
|
||||
<gl-form-checkbox
|
||||
v-gl-tooltip.right
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@ const parseJsonArray = (triggers) => {
|
|||
export default (containerId = 'js-ci-pipeline-triggers-list') => {
|
||||
const containerEl = document.getElementById(containerId);
|
||||
|
||||
if (!containerEl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const triggers = parseJsonArray(containerEl.dataset.triggers);
|
||||
|
||||
return new Vue({
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import createStore from './store';
|
|||
export default () => {
|
||||
const el = document.getElementById('js-deploy-freeze-table');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { projectId, timezoneData } = el.dataset;
|
||||
|
||||
const store = createStore({
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ initInheritedGroupCiVariables();
|
|||
// hide extra auto devops settings based checkbox state
|
||||
const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
|
||||
const instanceDefaultBadge = document.querySelector('.js-instance-default-badge');
|
||||
document.querySelector('.js-toggle-extra-settings').addEventListener('click', (event) => {
|
||||
const extraSettingsToggle = document.querySelector('.js-toggle-extra-settings');
|
||||
|
||||
extraSettingsToggle?.addEventListener('click', (event) => {
|
||||
const { target } = event;
|
||||
if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none';
|
||||
autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,3 @@
|
|||
import { __ } from '~/locale';
|
||||
import NamespaceStorageApp from './storage/components/namespace_storage_app.vue';
|
||||
import { parseProvideData as parseStorageTabProvideData } from './storage/utils';
|
||||
import { STORAGE_TAB_METADATA_EL_SELECTOR } from './constants';
|
||||
import { getStorageTabMetadata } from './storage/tab_metadata';
|
||||
|
||||
export const usageQuotasViewProvideData = {
|
||||
...parseStorageTabProvideData(document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR)),
|
||||
};
|
||||
|
||||
export const storageTabMetadata = {
|
||||
title: __('Storage'),
|
||||
component: NamespaceStorageApp,
|
||||
shouldRender: () => document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR),
|
||||
};
|
||||
|
||||
export const usageQuotasTabsMetadata = [storageTabMetadata];
|
||||
export const usageQuotasTabsMetadata = [getStorageTabMetadata()];
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import {
|
||||
usageQuotasTabsMetadata,
|
||||
usageQuotasViewProvideData,
|
||||
} from 'ee_else_ce/usage_quotas/group_view_metadata';
|
||||
import { usageQuotasTabsMetadata } from 'ee_else_ce/usage_quotas/group_view_metadata';
|
||||
import UsageQuotasApp from './components/usage_quotas_app.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
|
@ -29,7 +26,6 @@ export default () => {
|
|||
provide: {
|
||||
namespaceName,
|
||||
tabs: usageQuotasTabsMetadata.filter(Boolean),
|
||||
...usageQuotasViewProvideData,
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(UsageQuotasApp);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { __ } from '~/locale';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { parseProvideData } from 'ee_else_ce/usage_quotas/storage/utils';
|
||||
import { STORAGE_TAB_METADATA_EL_SELECTOR } from '../constants';
|
||||
import NamespaceStorageApp from './components/namespace_storage_app.vue';
|
||||
|
||||
export const getStorageTabMetadata = ({ includeEl = false, customApolloProvider = false } = {}) => {
|
||||
let apolloProvider;
|
||||
const el = document.querySelector(STORAGE_TAB_METADATA_EL_SELECTOR);
|
||||
|
||||
if (!el) return false;
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
if (customApolloProvider) {
|
||||
apolloProvider = customApolloProvider;
|
||||
} else {
|
||||
apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
});
|
||||
}
|
||||
|
||||
const storageTabMetadata = {
|
||||
title: __('Storage'),
|
||||
component: {
|
||||
name: 'NamespaceStorageTab',
|
||||
provide: parseProvideData(el),
|
||||
apolloProvider,
|
||||
render(createElement) {
|
||||
return createElement(NamespaceStorageApp);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (includeEl) {
|
||||
storageTabMetadata.component.el = el;
|
||||
}
|
||||
|
||||
return storageTabMetadata;
|
||||
};
|
||||
|
|
@ -115,6 +115,7 @@ export default {
|
|||
:get-active-token-value="getActiveUser"
|
||||
:default-suggestions="defaultUsers"
|
||||
:preloaded-suggestions="preloadedUsers"
|
||||
value-identifier="username"
|
||||
v-bind="$attrs"
|
||||
@fetch-suggestions="fetchUsers"
|
||||
v-on="$listeners"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
|
||||
.commit-description,
|
||||
.commit-row-description {
|
||||
padding: $gl-padding-8 0 $gl-padding-8 $gl-padding-8;
|
||||
margin-top: $gl-padding-8;
|
||||
border: 0;
|
||||
border-radius: unset;
|
||||
background: none;
|
||||
word-break: normal;
|
||||
overflow-x: auto;
|
||||
border-left: 3px solid var(--border-color, $gray-100);
|
||||
color: var(--gray-700, $gl-text-color-secondary);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.commits-row {
|
||||
+ .commits-row {
|
||||
border-top: 1px solid $gray-50;
|
||||
border-top: 1px solid var(--gray-50, $gray-50);
|
||||
}
|
||||
|
||||
+ .commits-empty {
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
}
|
||||
|
||||
.title-hint-text {
|
||||
color: $gl-text-color-secondary;
|
||||
color: var(--gray-700, $gl-text-color-secondary);
|
||||
}
|
||||
|
||||
.gl-filtered-search-suggestion-list.dropdown-menu {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
@import 'mixins_and_variables_and_functions';
|
||||
@import 'page_bundles/commits';
|
||||
@import 'page_bundles/commit_description';
|
||||
|
||||
$tabs-holder-z-index: 250;
|
||||
$comparison-empty-state-height: 62px;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,3 @@
|
|||
.commit-description,
|
||||
.commit-row-description {
|
||||
padding: $gl-padding-8 0 $gl-padding-8 $gl-padding-8;
|
||||
margin-top: $gl-padding-8;
|
||||
border: 0;
|
||||
border-radius: unset;
|
||||
background: none;
|
||||
word-break: normal;
|
||||
overflow-x: auto;
|
||||
border-left: 3px solid $gray-100;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.commit-box {
|
||||
border-top: 1px solid $border-color;
|
||||
padding: $gl-padding 0;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ module Projects
|
|||
NUMBER_OF_RUNNERS_PER_PAGE = 20
|
||||
|
||||
layout 'project_settings'
|
||||
before_action :authorize_admin_pipeline!
|
||||
before_action :authorize_admin_pipeline!, except: :show
|
||||
before_action :authorize_admin_cicd_variables!, only: :show
|
||||
before_action :check_builds_available!
|
||||
before_action :define_variables
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Projects::VariablesController < Projects::ApplicationController
|
||||
before_action :authorize_admin_build!
|
||||
before_action :authorize_admin_build!, except: :update
|
||||
before_action :authorize_admin_cicd_variables!, only: :update
|
||||
|
||||
feature_category :secrets_management
|
||||
|
||||
|
|
|
|||
|
|
@ -382,13 +382,13 @@ module Types
|
|||
field :ci_variables, Types::Ci::ProjectVariableType.connection_type,
|
||||
null: true,
|
||||
description: "List of the project's CI/CD variables.",
|
||||
authorize: :admin_build,
|
||||
authorize: :admin_cicd_variables,
|
||||
resolver: Resolvers::Ci::VariablesResolver
|
||||
|
||||
field :inherited_ci_variables, Types::Ci::InheritedCiVariableType.connection_type,
|
||||
null: true,
|
||||
description: "List of CI/CD variables the project inherited from its parent group and ancestors.",
|
||||
authorize: :admin_build,
|
||||
authorize: :admin_cicd_variables,
|
||||
resolver: Resolvers::Ci::InheritedVariablesResolver
|
||||
|
||||
field :ci_cd_settings, Types::Ci::CiCdSettingType,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,14 @@ module Ci
|
|||
include Limitable
|
||||
include EachBatch
|
||||
include BatchNullifyDependentAssociations
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
VALID_REF_REGEX = %r{\A(#{Gitlab::Git::TAG_REF_PREFIX}|#{Gitlab::Git::BRANCH_REF_PREFIX}).+}
|
||||
|
||||
# The only way that ref can be unexpanded after #expand_short_ref runs is if the ref
|
||||
# is ambiguous because both a branch and a tag with the name exist, or it is
|
||||
# ambiguous because neither exists.
|
||||
INVALID_REF_MESSAGE = 'is ambiguous'
|
||||
|
||||
self.limit_name = 'ci_pipeline_schedules'
|
||||
self.limit_scope = :project
|
||||
|
|
@ -21,7 +29,13 @@ module Ci
|
|||
|
||||
validates :cron, unless: :importing?, cron: true, presence: { unless: :importing? }
|
||||
validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? }
|
||||
validates :ref, presence: { unless: :importing? }
|
||||
validates :ref, presence: { unless: :importing? },
|
||||
format: { with: VALID_REF_REGEX,
|
||||
allow_nil: true,
|
||||
message: INVALID_REF_MESSAGE,
|
||||
unless: ->(schedule) do
|
||||
schedule.importing || Feature.disabled?(:enforce_full_refs_for_pipeline_schedules, schedule.project)
|
||||
end }
|
||||
validates :description, presence: true
|
||||
validates :variables, nested_attributes_duplicates: true
|
||||
|
||||
|
|
@ -33,6 +47,8 @@ module Ci
|
|||
scope :owned_by, ->(user) { where(owner: user) }
|
||||
scope :for_project, ->(project_id) { where(project_id: project_id) }
|
||||
|
||||
before_validation :expand_short_ref
|
||||
|
||||
accepts_nested_attributes_for :variables, allow_destroy: true
|
||||
|
||||
alias_attribute :real_next_run, :next_run_at
|
||||
|
|
@ -91,6 +107,22 @@ module Ci
|
|||
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expand_short_ref
|
||||
return if Feature.disabled?(:enforce_full_refs_for_pipeline_schedules, project)
|
||||
return if ref.blank? || VALID_REF_REGEX.match?(ref) || ambiguous_ref?
|
||||
|
||||
# In case the ref doesn't exist default to the initial value
|
||||
self.ref = project.repository.expand_ref(ref) || ref
|
||||
end
|
||||
|
||||
def ambiguous_ref?
|
||||
strong_memoize_with(:ambiguous_ref, ref) do
|
||||
project.repository.ambiguous_ref?(ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,25 +24,16 @@ class CommitCollection
|
|||
commits.each(&block)
|
||||
end
|
||||
|
||||
def committers(with_merge_commits: false, lazy: false)
|
||||
return committers_lazy(with_merge_commits: with_merge_commits).flatten if lazy
|
||||
def committers(with_merge_commits: false)
|
||||
emails = if with_merge_commits
|
||||
commits.filter_map(&:committer_email).uniq
|
||||
else
|
||||
without_merge_commits.filter_map(&:committer_email).uniq
|
||||
end
|
||||
|
||||
User.by_any_email(committers_emails(with_merge_commits: with_merge_commits))
|
||||
User.by_any_email(emails)
|
||||
end
|
||||
|
||||
def committers_lazy(with_merge_commits: false)
|
||||
emails = committers_emails(with_merge_commits: with_merge_commits)
|
||||
|
||||
emails.map do |email|
|
||||
BatchLoader.for(email.downcase).batch(default_value: []) do |committer_emails, loader|
|
||||
User.by_any_email(committer_emails).each do |user|
|
||||
loader.call(user.email) { |memo| memo << user }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
alias_method :add_committers_to_batch_loader, :committers_lazy
|
||||
|
||||
def committer_user_ids
|
||||
committers.pluck(:id)
|
||||
end
|
||||
|
|
@ -152,12 +143,4 @@ class CommitCollection
|
|||
|
||||
self
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def committers_emails(with_merge_commits: false)
|
||||
return commits.filter_map(&:committer_email).uniq if with_merge_commits
|
||||
|
||||
without_merge_commits.filter_map(&:committer_email).uniq
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -668,8 +668,8 @@ class MergeRequest < ApplicationRecord
|
|||
[:assignees, :reviewers] + super
|
||||
end
|
||||
|
||||
def committers(with_merge_commits: false, lazy: false)
|
||||
@committers ||= commits.committers(with_merge_commits: with_merge_commits, lazy: lazy)
|
||||
def committers(with_merge_commits: false)
|
||||
@committers ||= commits.committers(with_merge_commits: with_merge_commits)
|
||||
end
|
||||
|
||||
# Verifies if title has changed not taking into account Draft prefix
|
||||
|
|
|
|||
|
|
@ -33,16 +33,7 @@ class MergeRequestDiff < ApplicationRecord
|
|||
-> { order(:merge_request_diff_id, :relative_order) },
|
||||
inverse_of: :merge_request_diff
|
||||
|
||||
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }, inverse_of: :merge_request_diff do
|
||||
def with_users
|
||||
ActiveRecord::Associations::Preloader.new(
|
||||
records: self,
|
||||
associations: [:commit_author, :committer]
|
||||
).call
|
||||
|
||||
self
|
||||
end
|
||||
end
|
||||
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }, inverse_of: :merge_request_diff
|
||||
|
||||
sha_attribute :patch_id_sha
|
||||
|
||||
|
|
@ -810,9 +801,7 @@ class MergeRequestDiff < ApplicationRecord
|
|||
end
|
||||
|
||||
def load_commits(limit: nil, load_from_gitaly: false, page: nil)
|
||||
diff_commits = merge_request_diff_commits
|
||||
diff_commits = diff_commits.page(page).per(limit) if page.present?
|
||||
diff_commits = diff_commits.limit(limit) if limit.present?
|
||||
diff_commits = page.present? ? merge_request_diff_commits.page(page).per(limit) : merge_request_diff_commits.limit(limit)
|
||||
|
||||
if load_from_gitaly
|
||||
commits = Gitlab::Git::Commit.batch_by_oid(repository, diff_commits.map(&:sha))
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ class MergeRequestDiffCommit < ApplicationRecord
|
|||
attribute :trailers, :ind_jsonb
|
||||
validates :trailers, json_schema: { filename: 'git_trailers' }
|
||||
|
||||
scope :with_users, -> { preload(:commit_author, :committer) }
|
||||
|
||||
# A list of keys of which their values need to be trimmed before they can be
|
||||
# inserted into the merge_request_diff_commit_users table.
|
||||
TRIM_USER_KEYS =
|
||||
|
|
|
|||
|
|
@ -596,6 +596,7 @@ class ProjectPolicy < BasePolicy
|
|||
enable :admin_incident_management_timeline_event_tag
|
||||
enable :stop_environment
|
||||
enable :read_import_error
|
||||
enable :admin_cicd_variables
|
||||
end
|
||||
|
||||
rule { can?(:admin_build) }.enable :manage_trigger
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
-# This is a temporary place for the page specific style migrations to be included on all pages like page_specific_files
|
||||
- if Feature.disabled?(:page_specific_styles, current_user)
|
||||
- add_page_specific_style('page_bundles/commits')
|
||||
- add_page_specific_style('page_bundles/commit_description')
|
||||
|
||||
%head{ omit_og ? { } : { prefix: "og: http://ogp.me/ns#" } }
|
||||
%meta{ charset: "utf-8" }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
- add_page_startup_api_call logs_file_project_ref_path(@project, ref, @path, format: "json", offset: 0, ref_type: @ref_type)
|
||||
- if readme_path = @project.repository.readme_path
|
||||
- add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json")
|
||||
- add_page_specific_style 'page_bundles/commit_description'
|
||||
|
||||
#tree-holder.tree-holder.clearfix.js-per-page.gl-mt-5{ data: { blame_per_page: Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE } }
|
||||
- if Feature.enabled?(:project_overview_reorg)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
- add_page_specific_style 'page_bundles/commit_description'
|
||||
|
||||
.page-content-header
|
||||
.header-main-content
|
||||
= render partial: 'signature', object: @commit.signature
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
- link = commit_path(project, commit, merge_request: merge_request)
|
||||
- show_project_name = local_assigns.fetch(:show_project_name, false)
|
||||
- toggle_commit_message = _("Toggle commit description")
|
||||
- add_page_specific_style 'page_bundles/commit_description'
|
||||
|
||||
%li{ class: ["commit flex-row", ("js-toggle-container" if collapsible)], id: "commit-#{commit.short_id}" }
|
||||
.avatar-cell.d-none.d-sm-block
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
- add_page_specific_style 'page_bundles/merge_request'
|
||||
- add_page_specific_style 'page_bundles/commits'
|
||||
|
||||
= render 'page'
|
||||
|
|
|
|||
|
|
@ -5,118 +5,121 @@
|
|||
- expanded = expanded_by_default?
|
||||
- general_expanded = @project.errors.empty? ? expanded : true
|
||||
|
||||
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("General pipelines")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Customize your pipeline configuration.")
|
||||
.settings-content
|
||||
= render 'form'
|
||||
|
||||
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { testid: 'autodevops-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= s_('CICD|Auto DevOps')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
- auto_devops_url = help_page_path('topics/autodevops/index')
|
||||
- quickstart_url = help_page_path('topics/autodevops/cloud_deployments/auto_devops_with_gke')
|
||||
- auto_devops_link = link_to('', auto_devops_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
- quickstart_link = link_to('', quickstart_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
= safe_format(s_('AutoDevOps|%{auto_devops_start}Automate building, testing, and deploying%{auto_devops_end} your applications based on your continuous integration and delivery configuration. %{quickstart_start}How do I get started?%{quickstart_end}'), tag_pair(auto_devops_link, :auto_devops_start, :auto_devops_end), tag_pair(quickstart_link, :quickstart_start, :quickstart_end))
|
||||
.settings-content
|
||||
= render 'autodevops_form', auto_devops_enabled: @project.auto_devops_enabled?
|
||||
|
||||
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
|
||||
|
||||
- expand_runners = expanded || params[:expand_runners]
|
||||
%section.settings.no-animate#js-runners-settings{ class: ('expanded' if expand_runners), data: { testid: 'runners-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Runners")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expand_runners ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
|
||||
= link_to s_('What is GitLab Runner?'), 'https://docs.gitlab.com/runner/', target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
= render 'projects/runners/settings'
|
||||
|
||||
- if Gitlab::CurrentSettings.current_application_settings.keep_latest_artifact?
|
||||
%section.settings.no-animate#js-artifacts-settings{ class: ('expanded' if expanded) }
|
||||
- if can?(current_user, :admin_pipeline, @project)
|
||||
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Artifacts")
|
||||
= _("General pipelines")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("A job artifact is an archive of files and directories saved by a job when it finishes.")
|
||||
= _("Customize your pipeline configuration.")
|
||||
.settings-content
|
||||
#js-artifacts-settings-app{ data: { full_path: @project.full_path, help_page_path: help_page_path('ci/jobs/job_artifacts', anchor: 'keep-artifacts-from-most-recent-successful-jobs') } }
|
||||
= render 'form'
|
||||
|
||||
%section.settings.no-animate#js-cicd-variables-settings{ class: ('expanded' if expanded), data: { testid: 'variables-settings-content' } }
|
||||
.settings-header
|
||||
= render 'ci/variables/header', expanded: expanded
|
||||
.settings-content
|
||||
= render 'ci/variables/index', save_endpoint: project_variables_path(@project)
|
||||
|
||||
%section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Pipeline trigger tokens")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Trigger a pipeline for a branch or tag by generating a trigger token and using it with an API call. The token impersonates a user's project access and permissions.")
|
||||
= link_to _('Learn more.'), help_page_path('ci/triggers/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
= render 'projects/triggers/index'
|
||||
|
||||
= render_if_exists 'projects/settings/ci_cd/auto_rollback', expanded: expanded
|
||||
|
||||
- if can?(current_user, :create_freeze_period, @project)
|
||||
%section.settings.no-animate#js-deploy-freeze-settings{ class: ('expanded' if expanded) }
|
||||
%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { testid: 'autodevops-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Deploy freezes")
|
||||
= s_('CICD|Auto DevOps')
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
- freeze_period_docs = help_page_path('user/project/releases/index', anchor: 'prevent-unintentional-releases-by-setting-a-deploy-freeze')
|
||||
- freeze_period_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: freeze_period_docs }
|
||||
= html_escape(s_('DeployFreeze|Add a freeze period to prevent unintended releases during a period of time for a given environment. You must update the deployment jobs in %{filename} according to the deploy freezes added here. %{freeze_period_link_start}Learn more.%{freeze_period_link_end}')) % { freeze_period_link_start: freeze_period_link_start, freeze_period_link_end: '</a>'.html_safe, filename: tag.code('.gitlab-ci.yml') }
|
||||
|
||||
- cron_syntax_url = 'https://crontab.guru/'
|
||||
- cron_syntax_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: cron_syntax_url }
|
||||
= s_('DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron syntax%{cron_syntax_link_end}.').html_safe % { cron_syntax_link_start: cron_syntax_link_start, cron_syntax_link_end: "</a>".html_safe }
|
||||
|
||||
- auto_devops_url = help_page_path('topics/autodevops/index')
|
||||
- quickstart_url = help_page_path('topics/autodevops/cloud_deployments/auto_devops_with_gke')
|
||||
- auto_devops_link = link_to('', auto_devops_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
- quickstart_link = link_to('', quickstart_url, target: '_blank', rel: 'noopener noreferrer')
|
||||
= safe_format(s_('AutoDevOps|%{auto_devops_start}Automate building, testing, and deploying%{auto_devops_end} your applications based on your continuous integration and delivery configuration. %{quickstart_start}How do I get started?%{quickstart_end}'), tag_pair(auto_devops_link, :auto_devops_start, :auto_devops_end), tag_pair(quickstart_link, :quickstart_start, :quickstart_end))
|
||||
.settings-content
|
||||
= render 'ci/deploy_freeze/index'
|
||||
= render 'autodevops_form', auto_devops_enabled: @project.auto_devops_enabled?
|
||||
|
||||
%section.settings.no-animate#js-token-access{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Token Access")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects.")
|
||||
.settings-content
|
||||
= render 'ci/token_access/index'
|
||||
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
|
||||
|
||||
- if show_secure_files_setting(@project, current_user)
|
||||
%section.settings.no-animate#js-secure-files{ class: ('expanded' if expanded) }
|
||||
- expand_runners = expanded || params[:expand_runners]
|
||||
%section.settings.no-animate#js-runners-settings{ class: ('expanded' if expand_runners), data: { testid: 'runners-settings-content' } }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Secure Files")
|
||||
= _("Runners")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expand_runners ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Runners are processes that pick up and execute CI/CD jobs for GitLab.")
|
||||
= link_to s_('What is GitLab Runner?'), 'https://docs.gitlab.com/runner/', target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
= render 'projects/runners/settings'
|
||||
|
||||
- if Gitlab::CurrentSettings.current_application_settings.keep_latest_artifact?
|
||||
%section.settings.no-animate#js-artifacts-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Artifacts")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("A job artifact is an archive of files and directories saved by a job when it finishes.")
|
||||
.settings-content
|
||||
#js-artifacts-settings-app{ data: { full_path: @project.full_path, help_page_path: help_page_path('ci/jobs/job_artifacts', anchor: 'keep-artifacts-from-most-recent-successful-jobs') } }
|
||||
|
||||
- if can?(current_user, :admin_cicd_variables, @project)
|
||||
%section.settings.no-animate#js-cicd-variables-settings{ class: ('expanded' if expanded), data: { testid: 'variables-settings-content' } }
|
||||
.settings-header
|
||||
= render 'ci/variables/header', expanded: expanded
|
||||
.settings-content
|
||||
= render 'ci/variables/index', save_endpoint: project_variables_path(@project)
|
||||
|
||||
- if can?(current_user, :admin_pipeline, @project)
|
||||
%section.settings.no-animate#js-pipeline-triggers{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Pipeline trigger tokens")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Use Secure Files to store files used by your pipelines such as Android keystores, or Apple provisioning profiles and signing certificates.")
|
||||
= link_to _('Learn more'), help_page_path('ci/secure_files/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
= _("Trigger a pipeline for a branch or tag by generating a trigger token and using it with an API call. The token impersonates a user's project access and permissions.")
|
||||
= link_to _('Learn more.'), help_page_path('ci/triggers/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
#js-ci-secure-files{ data: { project_id: @project.id, admin: can?(current_user, :admin_secure_files, @project).to_s, file_size_limit: Ci::SecureFile::FILE_SIZE_LIMIT.to_mb } }
|
||||
= render 'projects/triggers/index'
|
||||
|
||||
= render_if_exists 'projects/settings/ci_cd/auto_rollback', expanded: expanded
|
||||
|
||||
- if can?(current_user, :create_freeze_period, @project)
|
||||
%section.settings.no-animate#js-deploy-freeze-settings{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Deploy freezes")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
- freeze_period_docs = help_page_path('user/project/releases/index', anchor: 'prevent-unintentional-releases-by-setting-a-deploy-freeze')
|
||||
- freeze_period_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: freeze_period_docs }
|
||||
= html_escape(s_('DeployFreeze|Add a freeze period to prevent unintended releases during a period of time for a given environment. You must update the deployment jobs in %{filename} according to the deploy freezes added here. %{freeze_period_link_start}Learn more.%{freeze_period_link_end}')) % { freeze_period_link_start: freeze_period_link_start, freeze_period_link_end: '</a>'.html_safe, filename: tag.code('.gitlab-ci.yml') }
|
||||
|
||||
- cron_syntax_url = 'https://crontab.guru/'
|
||||
- cron_syntax_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: cron_syntax_url }
|
||||
= s_('DeployFreeze|Specify deploy freezes using %{cron_syntax_link_start}cron syntax%{cron_syntax_link_end}.').html_safe % { cron_syntax_link_start: cron_syntax_link_start, cron_syntax_link_end: "</a>".html_safe }
|
||||
|
||||
.settings-content
|
||||
= render 'ci/deploy_freeze/index'
|
||||
|
||||
%section.settings.no-animate#js-token-access{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Token Access")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects.")
|
||||
.settings-content
|
||||
= render 'ci/token_access/index'
|
||||
|
||||
- if show_secure_files_setting(@project, current_user)
|
||||
%section.settings.no-animate#js-secure-files{ class: ('expanded' if expanded) }
|
||||
.settings-header
|
||||
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
|
||||
= _("Secure Files")
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
|
||||
= expanded ? _('Collapse') : _('Expand')
|
||||
%p.gl-text-secondary
|
||||
= _("Use Secure Files to store files used by your pipelines such as Android keystores, or Apple provisioning profiles and signing certificates.")
|
||||
= link_to _('Learn more'), help_page_path('ci/secure_files/index'), target: '_blank', rel: 'noopener noreferrer'
|
||||
.settings-content
|
||||
#js-ci-secure-files{ data: { project_id: @project.id, admin: can?(current_user, :admin_secure_files, @project).to_s, file_size_limit: Ci::SecureFile::FILE_SIZE_LIMIT.to_mb } }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
- wiki_page_title @page, _('Changes')
|
||||
- add_page_specific_style 'page_bundles/wiki'
|
||||
- commit = @diffs.diffable
|
||||
- add_page_specific_style 'page_bundles/commit_description'
|
||||
|
||||
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
|
||||
= wiki_sidebar_toggle_button
|
||||
|
|
|
|||
|
|
@ -305,6 +305,7 @@ module Gitlab
|
|||
config.assets.precompile << "page_bundles/cluster_agents.css"
|
||||
config.assets.precompile << "page_bundles/clusters.css"
|
||||
config.assets.precompile << "page_bundles/commits.css"
|
||||
config.assets.precompile << "page_bundles/commit_description.css"
|
||||
config.assets.precompile << "page_bundles/cycle_analytics.css"
|
||||
config.assets.precompile << "page_bundles/dashboard.css"
|
||||
config.assets.precompile << "page_bundles/dashboard_projects.css"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
name: enforce_full_refs_for_pipeline_schedules
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/435357
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144953
|
||||
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17604
|
||||
milestone: '16.10'
|
||||
group: group::pipeline execution
|
||||
type: development
|
||||
default_enabled: false
|
||||
|
|
@ -6,5 +6,3 @@ Rails.backtrace_cleaner.remove_silencers!
|
|||
if Rails.env.development? || Rails.env.test?
|
||||
Rails.backtrace_cleaner.add_silencer { |line| %r{^lib/gitlab/database/load_balancing}.match?(line) }
|
||||
end
|
||||
|
||||
Rails.backtrace_cleaner.add_silencer { |line| !Gitlab::APP_DIRS_PATTERN.match?(line) }
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ Settings = GitlabSettings.load(file, Rails.env) do
|
|||
[
|
||||
gitlab.host,
|
||||
custom_port,
|
||||
gitlab.relative_url_root,
|
||||
'/'
|
||||
gitlab.relative_url_root
|
||||
].join('')
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UnscheduleOpenAiClearConversationsWorker < Gitlab::Database::Migration[2.2]
|
||||
milestone '16.10'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
# This is to clean up the cron schedule for OpenAi::ClearConversationsWorker
|
||||
# which was removed in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139626
|
||||
removed_job = Sidekiq::Cron::Job.find('open_ai_clear_conversations_worker')
|
||||
removed_job.destroy if removed_job
|
||||
|
||||
sidekiq_remove_jobs(job_klasses: %w[OpenAi::ClearConversationsWorker])
|
||||
end
|
||||
|
||||
def down
|
||||
# No-op
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
f5e8162f1d63cc4ba8331f9fd29f6e2fa389871448193d3ded4157fac1318456
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,7 +34,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
{
|
||||
"id": 13,
|
||||
"description": "Test schedule pipeline",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "* * * * *",
|
||||
"cron_timezone": "Asia/Tokyo",
|
||||
"next_run_at": "2017-05-19T13:41:00.000Z",
|
||||
|
|
@ -74,7 +74,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
{
|
||||
"id": 13,
|
||||
"description": "Test schedule pipeline",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "* * * * *",
|
||||
"cron_timezone": "Asia/Tokyo",
|
||||
"next_run_at": "2017-05-19T13:41:00.000Z",
|
||||
|
|
@ -84,7 +84,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
|
|||
"last_pipeline": {
|
||||
"id": 332,
|
||||
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"status": "pending"
|
||||
},
|
||||
"owner": {
|
||||
|
|
@ -168,14 +168,14 @@ Create a new pipeline schedule of a project.
|
|||
POST /projects/:id/pipeline_schedules
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|-------------|
|
||||
| `cron` | string | Yes | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
|
||||
| `description` | string | Yes | The description of the pipeline schedule. |
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
|
||||
| `ref` | string | Yes | The branch or tag name that is triggered. |
|
||||
| `active` | boolean | No | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated (default: `true`). |
|
||||
| `cron_timezone` | string | No | The time zone supported by `ActiveSupport::TimeZone`, for example: `Pacific Time (US & Canada)` (default: `UTC`). |
|
||||
| Attribute | Type | Required | Description |
|
||||
|-----------------|----------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `cron` | string | Yes | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
|
||||
| `description` | string | Yes | The description of the pipeline schedule. |
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
|
||||
| `ref` | string | Yes | The branch or tag name that is triggered. Both the short (e.g. `main`) and full (e.g. `refs/heads/main` or `refs/tags/main`) ref versions are accepted. If a short version is provided, it is automatically expanded to the full ref version but, if the ref is [ambiguous](../ci/pipelines/schedules.md#ambiguous-refs), it will be rejected |
|
||||
| `active` | boolean | No | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated (default: `true`). |
|
||||
| `cron_timezone` | string | No | The time zone supported by `ActiveSupport::TimeZone`, for example: `Pacific Time (US & Canada)` (default: `UTC`). |
|
||||
|
||||
```shell
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
|
|
@ -187,7 +187,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
{
|
||||
"id": 14,
|
||||
"description": "Build packages",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "0 1 * * 5",
|
||||
"cron_timezone": "UTC",
|
||||
"next_run_at": "2017-05-26T01:00:00.000Z",
|
||||
|
|
@ -214,15 +214,15 @@ Updates the pipeline schedule of a project. After the update is done, it is resc
|
|||
PUT /projects/:id/pipeline_schedules/:pipeline_schedule_id
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------------------|----------------|----------|-------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
|
||||
| `pipeline_schedule_id` | integer | Yes | The pipeline schedule ID. |
|
||||
| `active` | boolean | No | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated. |
|
||||
| `cron_timezone` | string | No | The time zone supported by `ActiveSupport::TimeZone` (for example `Pacific Time (US & Canada)`), or `TZInfo::Timezone` (for example `America/Los_Angeles`). |
|
||||
| `cron` | string | No | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
|
||||
| `description` | string | No | The description of the pipeline schedule. |
|
||||
| `ref` | string | No | The branch or tag name that is triggered. |
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `id` | integer/string | Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
|
||||
| `pipeline_schedule_id` | integer | Yes | The pipeline schedule ID. |
|
||||
| `active` | boolean | No | The activation of pipeline schedule. If false is set, the pipeline schedule is initially deactivated. |
|
||||
| `cron_timezone` | string | No | The time zone supported by `ActiveSupport::TimeZone` (for example `Pacific Time (US & Canada)`), or `TZInfo::Timezone` (for example `America/Los_Angeles`). |
|
||||
| `cron` | string | No | The [cron](https://en.wikipedia.org/wiki/Cron) schedule, for example: `0 1 * * *`. |
|
||||
| `description` | string | No | The description of the pipeline schedule. |
|
||||
| `ref` | string | No | The branch or tag name that is triggered. Both the short (e.g. `main`) and full (e.g. `refs/heads/main` or `refs/tags/main`) ref versions are accepted. If a short version is provided, it is automatically expanded to the full ref version but, if the ref is [ambiguous](../ci/pipelines/schedules.md#ambiguous-refs), it will be rejected |
|
||||
|
||||
```shell
|
||||
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
|
|
@ -233,7 +233,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
{
|
||||
"id": 13,
|
||||
"description": "Test schedule pipeline",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "0 2 * * *",
|
||||
"cron_timezone": "Asia/Tokyo",
|
||||
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||
|
|
@ -243,7 +243,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
|
|||
"last_pipeline": {
|
||||
"id": 332,
|
||||
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"status": "pending"
|
||||
},
|
||||
"owner": {
|
||||
|
|
@ -278,7 +278,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
|
|||
{
|
||||
"id": 13,
|
||||
"description": "Test schedule pipeline",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "0 2 * * *",
|
||||
"cron_timezone": "Asia/Tokyo",
|
||||
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||
|
|
@ -288,7 +288,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
|
|||
"last_pipeline": {
|
||||
"id": 332,
|
||||
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"status": "pending"
|
||||
},
|
||||
"owner": {
|
||||
|
|
@ -323,7 +323,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
|||
{
|
||||
"id": 13,
|
||||
"description": "Test schedule pipeline",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"cron": "0 2 * * *",
|
||||
"cron_timezone": "Asia/Tokyo",
|
||||
"next_run_at": "2017-05-19T17:00:00.000Z",
|
||||
|
|
@ -333,7 +333,7 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
|
|||
"last_pipeline": {
|
||||
"id": 332,
|
||||
"sha": "0e788619d0b5ec17388dffb973ecd505946156db",
|
||||
"ref": "main",
|
||||
"ref": "refs/heads/main",
|
||||
"status": "pending"
|
||||
},
|
||||
"owner": {
|
||||
|
|
|
|||
|
|
@ -105,3 +105,19 @@ questions that you know someone might ask.
|
|||
Each scenario can be a third-level heading, for example `### Getting error message X`.
|
||||
If you have none to add when creating a doc, leave this section in place
|
||||
but commented out to help encourage others to add to it in the future. -->
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Short refs are expanded to Full refs
|
||||
|
||||
This behavior is normal and it introduced in order to enforce explicit resources.
|
||||
The API still accepts both `short` (e.g. `main`) and `full` (e.g. `refs/heads/main` or `refs/tags/main`) refs and expands any `short`
|
||||
ref provided, to a `full` ref.
|
||||
|
||||
### Ambiguous Refs
|
||||
|
||||
When a ref is being expanded, there can be cases where the full ref can't be automatically inferred.
|
||||
Such cases can be:
|
||||
|
||||
- A `short` ref is provided (e.g. `main`) but **both** a branch and a tag exist with the provided `short` ref name
|
||||
- A `short` ref is provided, but **neither** a branch or tag with the provided `short` ref name exist
|
||||
|
|
|
|||
|
|
@ -5387,6 +5387,10 @@ In this example, the script:
|
|||
|
||||
The following keywords are deprecated.
|
||||
|
||||
NOTE:
|
||||
These keywords are still usable to ensure backwards compatibility,
|
||||
but could be scheduled for removal in a future major milestone.
|
||||
|
||||
### Globally-defined `image`, `services`, `cache`, `before_script`, `after_script`
|
||||
|
||||
Defining `image`, `services`, `cache`, `before_script`, and `after_script` globally is deprecated.
|
||||
|
|
|
|||
|
|
@ -6,21 +6,83 @@ info: Any user with at least the Maintainer role can merge updates to this conte
|
|||
|
||||
# GitLab Workhorse
|
||||
|
||||
GitLab Workhorse is a smart reverse proxy for GitLab. It handles
|
||||
"large" HTTP requests such as file downloads, file uploads, Git
|
||||
push/pull and Git archive downloads.
|
||||
GitLab Workhorse is a smart reverse proxy for GitLab intended to handle resource-intensive and long-running requests.
|
||||
It sits in front of Puma and intercepts every HTTP request destined for and emitted from GitLab Rails.
|
||||
Rails delegates requests to Workhorse and it takes responsibility for resource intensive HTTP requests
|
||||
such as file downloads and uploads, `git` over HTTP push/pull and `git` over HTTP archive downloads,
|
||||
which optimizes resource utilization and improves request handling efficiency.
|
||||
|
||||
Workhorse itself is not a feature, but there are
|
||||
[several features in GitLab](gitlab_features.md) that would not work efficiently without Workhorse.
|
||||
## Role in the GitLab stack
|
||||
|
||||
The canonical source for Workhorse is
|
||||
Workhorse can have other reverse proxy servers in front of it but only NGINX is supported.
|
||||
It is also possible (although unsupported) to use other reverse proxies such as Apache when installing
|
||||
GitLab from source.
|
||||
On many instances of GitLab, such as `gitlab.com`, a CDN like CloudFlare sits in front of NGINX.
|
||||
|
||||
Every Rails controller and other code that handles HTTP requests and returning HTTP responses is
|
||||
proxied through GitLab Workhorse.
|
||||
Workhorse is unlike other reverse proxies as it is very tightly
|
||||
coupled to GitLab Rails where as most reverse proxies are quite generic.
|
||||
When required, Workhorse makes modifications to HTTP headers which GitLab Rails depends on to offload work efficiently.
|
||||
|
||||
## Functionality and operations
|
||||
|
||||
### Request processing
|
||||
|
||||
- Workhorse primarily acts as a pass-through entity for incoming requests, forwarding them to Rails for processing.
|
||||
In essence, it performs minimal intervention on most requests, thereby maintaining a streamlined
|
||||
request handling pipeline.
|
||||
- For specific types of requests, especially those that are resource-intensive or require specialized handling
|
||||
(for example, large file uploads), Workhorse takes a more active role.
|
||||
Upon receiving directives from Rails, Workhorse executes specialized tasks such as directly
|
||||
interacting with [Gitaly](../../administration/gitaly/index.md) or offloading processing file uploads from Rails.
|
||||
|
||||
### Specialized task handling
|
||||
|
||||
- Workhorse is capable of intercepting certain requests based on Rails' responses and executing
|
||||
predefined operations.
|
||||
This includes interacting with [Gitaly](../../administration/gitaly/index.md), managing large data
|
||||
blobs, and altering request handling logic as required.
|
||||
- A notable functionality is its ability to manage file uploads efficiently.
|
||||
Workhorse can hijack the file upload process, perform necessary actions as dictated by Rails
|
||||
(such as storing files temporarily or uploading them to object storage), and update Rails when the
|
||||
process has completed.
|
||||
|
||||
### Integration with the Rails API
|
||||
|
||||
Workhorse serves as a proxy to the Rails API, especially in contexts requiring interaction with container
|
||||
registry services.
|
||||
This setup exemplifies Workhorse's ability to handle high-load services by acting as a reverse proxy,
|
||||
thereby minimizing the direct load on Rails.
|
||||
|
||||
## Architectural considerations
|
||||
|
||||
### Expanding functionality
|
||||
|
||||
- **Maintaining Simplicity:** While expanding Workhorse's functionalities to include direct handling
|
||||
of specific services (for example, container registry), it's crucial to maintain its simplicity and efficiency.
|
||||
Workhorse should not encompass complex control logic but rather focus on executing tasks as directed
|
||||
by Rails.
|
||||
- **Service Implementation and Data Migration:** Implementing new functionalities in Workhorse
|
||||
requires careful consideration of data migration strategies and service continuity.
|
||||
|
||||
### Data management and operational integrity
|
||||
|
||||
- Workhorse's architecture facilitates efficient data management strategies, including garbage
|
||||
collection and data migration.
|
||||
Workhorse's role is to support high-performance operations without directly involving complex data
|
||||
manipulation or control logic, which remains the purview of Rails.
|
||||
- For operations requiring background processing or long-running tasks, it is suggested to use
|
||||
separate services or Sidekiq job queues, with Workhorse and Rails coordinating to manage task execution and data integrity.
|
||||
|
||||
Workhorse is contained in a subfolder of the Rails monorepo at
|
||||
[`gitlab-org/gitlab/workhorse`](https://gitlab.com/gitlab-org/gitlab/tree/master/workhorse).
|
||||
|
||||
## Learning Resources
|
||||
## Learning resources
|
||||
|
||||
- Workhorse documentation (this page)
|
||||
- [GitLab Workhorse Deep Dive: Dependency Proxy](https://www.youtube.com/watch?v=9cRd-k0TRqI) _video_
|
||||
- [How Dependency Proxy via Workhorse works](https://gitlab.com/gitlab-org/gitlab/-/issues/370235)
|
||||
- Video: [GitLab Workhorse Deep Dive: Dependency Proxy](https://www.youtube.com/watch?v=9cRd-k0TRqI)
|
||||
- [How Dependency Proxy with Workhorse works](https://gitlab.com/gitlab-org/gitlab/-/issues/370235)
|
||||
- [Workhorse overview for the Dependency Proxy](https://www.youtube.com/watch?v=WmBibT9oQms)
|
||||
- [Workhorse architecture discussion](https://www.youtube.com/watch?v=QlHdh-yudtw)
|
||||
|
||||
|
|
@ -53,9 +115,8 @@ Workhorse uses [ExifTool](https://exiftool.org/) for
|
|||
removing EXIF data (which may contain sensitive information) from uploaded
|
||||
images. If you installed GitLab:
|
||||
|
||||
- Using the Omnibus package, you're all set.
|
||||
*NOTE* that if you are using CentOS Minimal, you may need to install `perl`
|
||||
package: `yum install perl`
|
||||
- Using the Linux package, you're all set.
|
||||
If you are using CentOS Minimal, you may need to install `perl` package: `yum install perl`.
|
||||
- From source, make sure `exiftool` is installed:
|
||||
|
||||
```shell
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ As a single-tenant SaaS solution, GitLab Dedicated provides infrastructure-level
|
|||
|
||||
#### Access controls
|
||||
|
||||
GitLab Dedicated adheres to the [principle of least privilege](https://handbook.gitlab.com/handbook/security/access-management-policy/#principle-of-least-privilege) to control access to customer tenant environments. Tenant AWS accounts live under a top-level GitLab Dedicated AWS parent organization. Access to the AWS Organization is restricted to select GitLab team members. All user accounts within the AWS Organization follow the overall [GitLab Access Management Policy](https://handbook.gitlab.com/handbook/security/access-management-policy/). Direct access to customer tenant environments is restricted to a single Hub account. The GitLab Dedicated Control Plane uses the Hub account to perform automated actions over tenant accounts when managing environments. Similarly, GitLab Dedicated engineers do not have direct access to customer tenant environments. In break glass situations, where access to resources in the tenant environment is required to address a high-severity issue, GitLab engineers must go through the Hub account to manage those resources. This is done via an approval process, and after permission is granted, the engineer will assume an IAM role on a temporary basis to access tenant resources through the Hub account. All actions within the hub account and tenant account are logged to CloudTrail.
|
||||
GitLab Dedicated adheres to the [principle of least privilege](https://handbook.gitlab.com/handbook/security/access-management-policy/#principle-of-least-privilege) to control access to customer tenant environments. Tenant AWS accounts live under a top-level GitLab Dedicated AWS parent organization. Access to the AWS Organization is restricted to select GitLab team members. All user accounts within the AWS Organization follow the overall [GitLab Access Management Policy](https://handbook.gitlab.com/handbook/security/access-management-policy/). Direct access to customer tenant environments is restricted to a single Hub account. The GitLab Dedicated Control Plane uses the Hub account to perform automated actions over tenant accounts when managing environments. Similarly, GitLab Dedicated engineers do not have direct access to customer tenant environments. In [break glass](https://gitlab.com/gitlab-com/gl-infra/gitlab-dedicated/team/-/blob/main/engineering/breaking_glass.md) situations, where access to resources in the tenant environment is required to address a high-severity issue, GitLab engineers must go through the Hub account to manage those resources. This is done via an approval process, and after permission is granted, the engineer will assume an IAM role on a temporary basis to access tenant resources through the Hub account. All actions within the hub account and tenant account are logged to CloudTrail.
|
||||
|
||||
Inside tenant accounts, GitLab leverages Intrusion Detection and Malware Scanning capabilities from AWS GuardDuty. Infrastructure logs are monitored by the GitLab Security Incident Response Team to detect anomalous events.
|
||||
|
||||
|
|
|
|||
|
|
@ -49,14 +49,6 @@ Prerequisites:
|
|||
synchronize subscription (**{retry}**).
|
||||
1. To the right of the user, turn on the toggle to assign GitLab Duo Pro.
|
||||
|
||||
### Assign seats in bulk
|
||||
|
||||
To assign seats in bulk, you can use [this GraphQL API endpoint](../api/graphql/reference/index.md#mutationuseraddonassignmentcreate).
|
||||
|
||||
This endpoint works for both self-managed and SaaS.
|
||||
|
||||
Administrators of self-managed instances can also assign users by using a [Rake task](../raketasks/user_management.md#bulk-assign-users-to-gitlab-duo-pro).
|
||||
|
||||
#### Configure network and proxy settings
|
||||
|
||||
For self-managed instances, you must also ensure that your firewalls and HTTP proxy servers
|
||||
|
|
@ -65,6 +57,14 @@ allow outbound connections to `cloud.gitlab.com`.
|
|||
To use an HTTP proxy, ensure that both `gitLab _workhorse` and `gitLab_rails` set the necessary
|
||||
[web proxy environment variables](https://docs.gitlab.com/omnibus/settings/environment-variables.html).
|
||||
|
||||
### Assign seats in bulk
|
||||
|
||||
To assign seats in bulk, you can use [this GraphQL API endpoint](../api/graphql/reference/index.md#mutationuseraddonassignmentcreate).
|
||||
|
||||
This endpoint works for both self-managed and SaaS.
|
||||
|
||||
Administrators of self-managed instances can also assign users by using a [Rake task](../raketasks/user_management.md#bulk-assign-users-to-gitlab-duo-pro).
|
||||
|
||||
## Purchase additional GitLab Duo Pro seats
|
||||
|
||||
DETAILS:
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ The summary is displayed only for you.
|
|||
|
||||
Provide feedback on this experimental feature in [issue 407779](https://gitlab.com/gitlab-org/gitlab/-/issues/407779).
|
||||
|
||||
**Data usage**: When you use this feature, the text of public comments on the issue are sent to the large
|
||||
**Data usage**: When you use this feature, the text of all comments on the issue are sent to the large
|
||||
language model referenced above.
|
||||
|
||||
### Forecast deployment frequency with Value stream forecasting
|
||||
|
|
@ -217,7 +217,7 @@ language model referenced above.
|
|||
| Feature | Large Language Model |
|
||||
|---|---|
|
||||
| [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | Vertex AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-chat) |
|
||||
| [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | Vertex AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) |
|
||||
| [Discussion summary](#summarize-issue-discussions-with-discussion-summary) |Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) |
|
||||
| [Issue description generation](#summarize-an-issue-with-issue-description-generation) | Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) |
|
||||
| [Code Suggestions](project/repository/code_suggestions/index.md) | For Code Completion: Vertex AI Codey [`code-gecko`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-completion) For Code Generation: Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) |
|
||||
| [Test generation](gitlab_duo_chat.md#write-tests-in-the-ide) | Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) |
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Beyond Identity
|
||||
|
||||
DETAILS:
|
||||
**Tier:** Free, Premium, Ultimate
|
||||
**Tier:** Premium, Ultimate
|
||||
**Offering:** SaaS, self-managed
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/431433) in GitLab 16.9.
|
||||
|
|
|
|||
|
|
@ -29,9 +29,15 @@ module Gitlab
|
|||
def self.clean_backtrace(backtrace)
|
||||
return unless backtrace
|
||||
|
||||
Array(Rails.backtrace_cleaner.clean(backtrace)).reject do |line|
|
||||
Array(backtrace_cleaner.clean(backtrace)).reject do |line|
|
||||
IGNORED_BACKTRACES_REGEXP.match?(line)
|
||||
end
|
||||
end
|
||||
|
||||
def self.backtrace_cleaner
|
||||
@backtrace_cleaner ||= Rails.backtrace_cleaner.dup.tap do |cleaner|
|
||||
cleaner.add_silencer { |line| !Gitlab::APP_DIRS_PATTERN.match?(line) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,14 +10,15 @@ module Gitlab
|
|||
LATEST_VERSION_KEYWORD = '~latest'
|
||||
|
||||
def self.match?(address)
|
||||
address.include?('@') && address.start_with?(Settings.gitlab_ci['component_fqdn'])
|
||||
address.include?('@') && address.start_with?(fqdn_prefix)
|
||||
end
|
||||
|
||||
attr_reader :host
|
||||
def self.fqdn_prefix
|
||||
"#{Settings.gitlab_ci['component_fqdn']}/"
|
||||
end
|
||||
|
||||
def initialize(address:)
|
||||
@full_path, @version = address.to_s.split('@', 2)
|
||||
@host = Settings.gitlab_ci['component_fqdn']
|
||||
end
|
||||
|
||||
def fetch_content!(current_user:)
|
||||
|
|
@ -74,7 +75,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def instance_path
|
||||
@full_path.delete_prefix(host)
|
||||
@full_path.delete_prefix(self.class.fqdn_prefix)
|
||||
end
|
||||
|
||||
def extract_component_name(project_path)
|
||||
|
|
|
|||
|
|
@ -44,34 +44,17 @@ module Gitlab
|
|||
def handle_not_found(exception)
|
||||
detail = Gitlab::GitalyClient.decode_detailed_error(exception)
|
||||
|
||||
handle_find_commits_error(detail, exception) if detail.is_a?(Gitaly::FindCommitsError)
|
||||
|
||||
case detail.class.name
|
||||
when Gitaly::ReferenceNotFoundError.name
|
||||
raise Gitlab::Git::ReferenceNotFoundError.new(
|
||||
exception, detail.reference_name
|
||||
)
|
||||
when Gitaly::FindCommitsError.name
|
||||
raise Gitlab::Git::Repository::CommitNotFound, exception
|
||||
else
|
||||
raise Gitlab::Git::Repository::NoRepository, exception
|
||||
end
|
||||
end
|
||||
|
||||
def handle_find_commits_error(detail, exception)
|
||||
if detail.ambiguous_ref
|
||||
raise Gitlab::Git::Repository::CommitNotFound, "ambiguous reference:#{detail.ambiguous_ref.reference}"
|
||||
end
|
||||
|
||||
if detail.bad_object
|
||||
raise Gitlab::Git::Repository::CommitNotFound,
|
||||
"bad object:#{detail.bad_object.to_h[:bad_oid]}"
|
||||
end
|
||||
|
||||
if detail.invalid_range
|
||||
raise Gitlab::Git::Repository::CommitNotFound, "invalid range:#{detail.invalid_range.range}"
|
||||
end
|
||||
|
||||
raise Gitlab::Git::Repository::CommitNotFound, exception
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ module Gitlab
|
|||
|
||||
case context
|
||||
when :block
|
||||
['NOTE:', *parts].join("\n")
|
||||
['DETAILS:', *parts].join("\n")
|
||||
when :inline
|
||||
parts.join(' ')
|
||||
end
|
||||
|
|
@ -128,8 +128,8 @@ module Gitlab
|
|||
" #{changed_in_milestone}: #{reason_text}"
|
||||
end
|
||||
|
||||
# Returns 'Deprecated in <milestone>' for proper deprecations.
|
||||
# Retruns 'Introduced in <milestone>' for :alpha deprecations.
|
||||
# Returns 'Deprecated in GitLab <milestone>' for proper deprecations.
|
||||
# Returns 'Introduced in GitLab <milestone>' for :alpha deprecations.
|
||||
# Formatted to markdown or plain format.
|
||||
def changed_in_milestone(format: :plain)
|
||||
verb = if alpha?
|
||||
|
|
@ -140,9 +140,9 @@ module Gitlab
|
|||
|
||||
case format
|
||||
when :plain
|
||||
"#{verb} in #{milestone}"
|
||||
"#{verb} in GitLab #{milestone}"
|
||||
when :markdown
|
||||
"**#{verb}** in #{milestone}"
|
||||
"**#{verb}** in GitLab #{milestone}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1918,9 +1918,6 @@ msgstr ""
|
|||
msgid "AIAgents|AI Agents"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgents|Agent name"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgents|An error has occurred when saving the agent."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1936,9 +1933,18 @@ msgstr ""
|
|||
msgid "AIAgent|Agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgent|Agent name"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgent|Ask your agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgent|Create agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgent|Prompt (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "AIAgent|Try out your agent"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -40048,9 +40054,6 @@ msgstr ""
|
|||
msgid "Promotions|You can restrict access to protected branches by choosing a role (Maintainers, Developers) as well as certain users."
|
||||
msgstr ""
|
||||
|
||||
msgid "Prompt"
|
||||
msgstr ""
|
||||
|
||||
msgid "Prompt users to upload SSH keys"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58654,15 +58657,9 @@ msgstr ""
|
|||
msgid "ciReport|Coverage fuzzing"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|Create Jira issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|Create a merge request to implement this solution, or download and apply the patch manually."
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|Create issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|DAST"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -58717,9 +58714,6 @@ msgstr ""
|
|||
msgid "ciReport|Generic Report"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|Investigate this vulnerability by creating an issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "ciReport|License Compliance"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -60467,18 +60461,9 @@ msgstr ""
|
|||
msgid "vulnerability|Add comment & dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "vulnerability|Add comment and dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "vulnerability|Dismiss vulnerability"
|
||||
msgstr ""
|
||||
|
||||
msgid "vulnerability|Save comment"
|
||||
msgstr ""
|
||||
|
||||
msgid "vulnerability|Undo dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "vulnerability|dismissed"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@
|
|||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@gitlab/web-ide": "^0.0.1-dev-20240214084918",
|
||||
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
|
||||
"@rails/actioncable": "7.0.8",
|
||||
"@rails/ujs": "7.0.8",
|
||||
"@rails/actioncable": "7.0.8-1",
|
||||
"@rails/ujs": "7.0.8-1",
|
||||
"@snowplow/browser-plugin-client-hints": "^3.9.0",
|
||||
"@snowplow/browser-plugin-form-tracking": "^3.9.0",
|
||||
"@snowplow/browser-plugin-ga-cookies": "^3.9.0",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ source 'https://rubygems.org'
|
|||
gem 'gitlab-qa', '~> 14', '>= 14.2.1', require: 'gitlab/qa'
|
||||
gem 'gitlab_quality-test_tooling', '~> 1.11.0', require: false
|
||||
gem 'gitlab-utils', path: '../gems/gitlab-utils'
|
||||
gem 'activesupport', '~> 7.0.8' # This should stay in sync with the root's Gemfile
|
||||
gem 'activesupport', '~> 7.0.8.1' # This should stay in sync with the root's Gemfile
|
||||
gem 'allure-rspec', '~> 2.24.0'
|
||||
gem 'capybara', '~> 3.39.2'
|
||||
gem 'capybara-screenshot', '~> 1.0.26'
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ PATH
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activesupport (7.0.8)
|
||||
activesupport (7.0.8.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
|
@ -207,7 +207,7 @@ GEM
|
|||
mime-types-data (3.2023.0218.1)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.5)
|
||||
minitest (5.21.1)
|
||||
minitest (5.22.2)
|
||||
mize (0.4.1)
|
||||
protocol (~> 2.0)
|
||||
multi_json (1.15.0)
|
||||
|
|
@ -344,7 +344,7 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activesupport (~> 7.0.8)
|
||||
activesupport (~> 7.0.8.1)
|
||||
airborne (~> 0.3.7)
|
||||
allure-rspec (~> 2.24.0)
|
||||
capybara (~> 3.39.2)
|
||||
|
|
|
|||
|
|
@ -2,22 +2,25 @@
|
|||
|
||||
module QA
|
||||
RSpec.describe 'Verify', :requires_admin, :reliable, product_group: :pipeline_execution do
|
||||
describe 'When user is blocked' do
|
||||
describe 'When user is blocked', feature_flag: { name: 'enforce_full_refs_for_pipeline_schedules,
|
||||
scope: :project' } do
|
||||
let!(:admin_api_client) { Runtime::API::Client.as_admin }
|
||||
let!(:user_api_client) { Runtime::API::Client.new(:gitlab, user: user) }
|
||||
|
||||
let(:user) { create(:user, api_client: admin_api_client) }
|
||||
|
||||
let(:project) { create(:project, name: 'project-for-canceled-schedule') }
|
||||
let(:ref) { 'master' }
|
||||
|
||||
before do
|
||||
project.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
|
||||
project.create_repository_branch(ref)
|
||||
|
||||
# Retry is needed due to delays with project authorization updates
|
||||
# Long term solution to accessing the status of a project authorization update
|
||||
# has been proposed in https://gitlab.com/gitlab-org/gitlab/-/issues/393369
|
||||
Support::Retrier.retry_on_exception(max_attempts: 60, sleep_interval: 1) do
|
||||
create(:pipeline_schedule, api_client: user_api_client, project: project)
|
||||
create(:pipeline_schedule, ref: ref, api_client: user_api_client, project: project)
|
||||
end
|
||||
|
||||
Support::Waiter.wait_until { !pipeline_schedule[:id].nil? && pipeline_schedule[:active] == true }
|
||||
|
|
|
|||
|
|
@ -21,6 +21,10 @@ module RuboCop
|
|||
}
|
||||
end
|
||||
end
|
||||
|
||||
def checksum
|
||||
@checksum ||= Digest::SHA256.hexdigest(dictionary_data.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(queued_migration_version)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ module RuboCop
|
|||
add_offense(node, message: format(MSG[error_code], msg_params))
|
||||
end
|
||||
|
||||
def external_dependency_checksum
|
||||
RuboCop::BatchedBackgroundMigrationsDictionary.checksum
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_url?(url)
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ RSpec.describe Settings, feature_category: :system_access do
|
|||
subject(:fqdn) { described_class.build_ci_component_fqdn }
|
||||
|
||||
where(:host, :port, :relative_url, :result) do
|
||||
'acme.com' | 9090 | '/gitlab' | 'acme.com:9090/gitlab/'
|
||||
'acme.com' | 443 | '/gitlab' | 'acme.com/gitlab/'
|
||||
'acme.com' | 443 | '' | 'acme.com/'
|
||||
'acme.com' | 9090 | '' | 'acme.com:9090/'
|
||||
'test' | 9090 | '' | 'test:9090/'
|
||||
'acme.com' | 9090 | '/gitlab' | 'acme.com:9090/gitlab'
|
||||
'acme.com' | 443 | '/gitlab' | 'acme.com/gitlab'
|
||||
'acme.com' | 443 | '' | 'acme.com'
|
||||
'acme.com' | 9090 | '' | 'acme.com:9090'
|
||||
'test' | 9090 | '' | 'test:9090'
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
|
||||
context 'when a pipeline schedule has no variables' do
|
||||
let(:basic_param) do
|
||||
{ description: 'updated_desc', cron: '0 1 * * *', cron_timezone: 'UTC', ref: 'patch-x', active: true }
|
||||
{ description: 'updated_desc', cron: '0 1 * * *', cron_timezone: 'UTC', ref: 'master', active: true }
|
||||
end
|
||||
|
||||
context 'when params include one variable' do
|
||||
|
|
@ -309,7 +309,7 @@ RSpec.describe Projects::PipelineSchedulesController, feature_category: :continu
|
|||
|
||||
context 'when a pipeline schedule has one variable' do
|
||||
let(:basic_param) do
|
||||
{ description: 'updated_desc', cron: '0 1 * * *', cron_timezone: 'UTC', ref: 'patch-x', active: true }
|
||||
{ description: 'updated_desc', cron: '0 1 * * *', cron_timezone: 'UTC', ref: 'master', active: true }
|
||||
end
|
||||
|
||||
let!(:pipeline_schedule_variable) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ FactoryBot.define do
|
|||
factory :ci_pipeline_schedule, class: 'Ci::PipelineSchedule' do
|
||||
cron { '0 1 * * *' }
|
||||
cron_timezone { Gitlab::Ci::CronParser::VALID_SYNTAX_SAMPLE_TIME_ZONE }
|
||||
ref { 'master' }
|
||||
ref { "#{Gitlab::Git::BRANCH_REF_PREFIX}master" }
|
||||
active { true }
|
||||
description { "pipeline schedule" }
|
||||
project
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { GlLoadingIcon, GlTable, GlLink, GlPagination, GlModal, GlFormCheckbox } from '@gitlab/ui';
|
||||
import {
|
||||
GlSkeletonLoader,
|
||||
GlTable,
|
||||
GlLink,
|
||||
GlPagination,
|
||||
GlModal,
|
||||
GlFormCheckbox,
|
||||
} from '@gitlab/ui';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import getJobArtifactsResponse from 'test_fixtures/graphql/ci/artifacts/graphql/queries/get_job_artifacts.query.graphql.json';
|
||||
|
|
@ -38,7 +45,7 @@ describe('JobArtifactsTable component', () => {
|
|||
|
||||
const mockToastShow = jest.fn();
|
||||
|
||||
const findLoadingState = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
|
||||
const findTable = () => wrapper.findComponent(GlTable);
|
||||
const findDetailsRows = () => wrapper.findAllComponents(ArtifactsTableRowDetails);
|
||||
const findDetailsInRow = (i) =>
|
||||
|
|
@ -165,10 +172,11 @@ describe('JobArtifactsTable component', () => {
|
|||
});
|
||||
};
|
||||
|
||||
it('when loading, shows a loading state', () => {
|
||||
it('when loading, shows a skeleton loader', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findLoadingState().exists()).toBe(true);
|
||||
expect(findTable().attributes('aria-busy')).toBe('true');
|
||||
expect(findSkeletonLoader().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('on error, shows an alert', async () => {
|
||||
|
|
@ -188,7 +196,8 @@ describe('JobArtifactsTable component', () => {
|
|||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findTable().exists()).toBe(true);
|
||||
expect(findTable().attributes('aria-busy')).toBe('false');
|
||||
expect(findSkeletonLoader().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('job details', () => {
|
||||
|
|
|
|||
|
|
@ -208,9 +208,9 @@ describe('UserToken', () => {
|
|||
|
||||
const baseTokenEl = findBaseToken();
|
||||
|
||||
expect(baseTokenEl.exists()).toBe(true);
|
||||
expect(baseTokenEl.props()).toMatchObject({
|
||||
suggestions: mockUsers,
|
||||
valueIdentifier: 'username',
|
||||
getActiveTokenValue: baseTokenEl.props('getActiveTokenValue'),
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,14 +4,6 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe Gitlab::BacktraceCleaner do
|
||||
describe '.clean_backtrace' do
|
||||
it 'uses the Rails backtrace cleaner' do
|
||||
backtrace = []
|
||||
|
||||
expect(Rails.backtrace_cleaner).to receive(:clean).with(backtrace)
|
||||
|
||||
described_class.clean_backtrace(backtrace)
|
||||
end
|
||||
|
||||
it 'removes lines from IGNORE_BACKTRACES' do
|
||||
backtrace = [
|
||||
"lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'",
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:path) { described_class.new(address: address) }
|
||||
let(:settings) { GitlabSettings::Options.build({ 'component_fqdn' => current_host }) }
|
||||
let(:current_host) { 'acme.com/' }
|
||||
let(:settings) { GitlabSettings::Options.build({ 'component_fqdn' => component_fqdn }) }
|
||||
let(:component_fqdn) { 'acme.com' }
|
||||
let(:fqdn_prefix) { "#{component_fqdn}/" }
|
||||
|
||||
before do
|
||||
allow(::Settings).to receive(:gitlab_ci).and_return(settings)
|
||||
|
|
@ -55,7 +56,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
result = path.fetch_content!(current_user: user)
|
||||
expect(result.content).to eq(file_content)
|
||||
expect(result.path).to eq(file_path)
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
expect(path.sha).to eq(project.commit('master').id)
|
||||
end
|
||||
|
|
@ -138,7 +138,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
result = path.fetch_content!(current_user: user)
|
||||
expect(result.content).to eq('image: alpine_2')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
expect(path.sha).to eq(latest_sha)
|
||||
end
|
||||
|
|
@ -156,7 +155,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
result = path.fetch_content!(current_user: user)
|
||||
expect(result.content).to eq('image: alpine_2')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
expect(path.sha).to eq(latest_sha)
|
||||
end
|
||||
|
|
@ -168,7 +166,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
|
||||
it 'returns nil', :aggregate_failures do
|
||||
expect(path.fetch_content!(current_user: user)).to be_nil
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
expect(path.sha).to be_nil
|
||||
end
|
||||
|
|
@ -176,13 +173,12 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
|
||||
context 'when current GitLab instance is installed on a relative URL' do
|
||||
let(:address) { "acme.com/gitlab/#{project_path}/secret-detection@#{version}" }
|
||||
let(:current_host) { 'acme.com/gitlab/' }
|
||||
let(:component_fqdn) { 'acme.com/gitlab' }
|
||||
|
||||
it 'fetches the component content', :aggregate_failures do
|
||||
result = path.fetch_content!(current_user: user)
|
||||
expect(result.content).to eq('image: alpine_1')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
expect(path.sha).to eq(project.commit('master').id)
|
||||
end
|
||||
|
|
@ -216,7 +212,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
|
||||
expect(result.content).to eq('image: alpine_1')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
end
|
||||
end
|
||||
|
|
@ -236,7 +231,6 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
|
||||
expect(result.content).to eq('image: alpine_1')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
end
|
||||
end
|
||||
|
|
@ -250,11 +244,32 @@ RSpec.describe Gitlab::Ci::Components::InstancePath, feature_category: :pipeline
|
|||
|
||||
expect(result.content).to eq('image: alpine_1')
|
||||
expect(result.path).to eq('templates/secret-detection.yml')
|
||||
expect(path.host).to eq(current_host)
|
||||
expect(path.project).to eq(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.match?' do
|
||||
subject(:match) { described_class.match?(address) }
|
||||
|
||||
context 'when address is a valid path' do
|
||||
let(:address) { "#{fqdn_prefix}group/project@master" }
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'when address is an invalid path' do
|
||||
let(:address) { 'group/project@master' }
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
describe '.fqdn_prefix' do
|
||||
subject(:fqdn_prefix) { described_class.fqdn_prefix }
|
||||
|
||||
it { is_expected.to eq("#{component_fqdn}/") }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ RSpec.describe Gitlab::ExceptionLogFormatter do
|
|||
|
||||
expect(payload['exception.class']).to eq('RuntimeError')
|
||||
expect(payload['exception.message']).to eq('bad request')
|
||||
expect(payload['exception.backtrace']).to eq(Gitlab::BacktraceCleaner.clean_backtrace(backtrace))
|
||||
expect(payload['exception.backtrace']).to eq(Rails.backtrace_cleaner.clean(backtrace))
|
||||
expect(payload['exception.sql']).to be_nil
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -79,48 +79,23 @@ RSpec.describe Gitlab::Git::WrapsGitalyErrors, feature_category: :gitaly do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples 'commit not found' do |message|
|
||||
it 'wraps the commit not found error' do
|
||||
expect { wrapped_gitaly_errors }.to raise_error do |wrapped_error|
|
||||
expect(wrapped_error).to be_a(Gitlab::Git::Repository::CommitNotFound)
|
||||
expect(wrapped_error.message).to eql(message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when receiving GRPC::Core::StatusCodes::NOT_FOUND' do
|
||||
subject(:wrapped_gitaly_errors) { wrapper.wrapped_gitaly_errors { raise original_error } }
|
||||
|
||||
let(:original_error) do
|
||||
new_detailed_error(
|
||||
GRPC::Core::StatusCodes::NOT_FOUND,
|
||||
'commits not found',
|
||||
Gitaly::FindCommitsError.new(commit_error)
|
||||
)
|
||||
end
|
||||
|
||||
context 'with Gitaly::FindCommitsError contains Gitaly::AmbiguousReferenceError detail' do
|
||||
let(:commit_error) do
|
||||
{ ambiguous_ref: Gitaly::AmbiguousReferenceError.new(reference: 'non-existing-ref') }
|
||||
context 'with Gitaly::FindCommitsError' do
|
||||
let(:original_error) do
|
||||
new_detailed_error(
|
||||
GRPC::Core::StatusCodes::NOT_FOUND,
|
||||
'commits not found',
|
||||
Gitaly::FindCommitsError.new
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'commit not found', 'ambiguous reference:non-existing-ref'
|
||||
end
|
||||
|
||||
context 'with Gitaly::FindCommitsError contains Gitaly::BadObjectError detail' do
|
||||
let(:commit_error) do
|
||||
{ bad_object: Gitaly::BadObjectError.new(bad_oid: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
|
||||
it 'wraps the commit not found error' do
|
||||
expect { wrapped_gitaly_errors }.to raise_error do |wrapped_error|
|
||||
expect(wrapped_error).to be_a(Gitlab::Git::Repository::CommitNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'commit not found', 'bad object:0b4bc9a49b562e85de7cc9e834518ea6828729b9'
|
||||
end
|
||||
|
||||
context 'with Gitaly::FindCommitsError contains Gitaly::InvalidRevisionRange detail' do
|
||||
let(:commit_error) do
|
||||
{ invalid_range: Gitaly::InvalidRevisionRange.new(range: '0b4bc9a49b562e85de7cc9e834518ea6828729b9') }
|
||||
end
|
||||
|
||||
it_behaves_like 'commit not found', 'invalid range:0b4bc9a49b562e85de7cc9e834518ea6828729b9'
|
||||
end
|
||||
|
||||
context 'with non Gitaly::FindCommitsError' do
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
let(:options) { { reason: :renamed, milestone: '10.10', replacement: 'X.y' } }
|
||||
|
||||
it 'renders as reason-replacement-milestone' do
|
||||
expect(deprecation.deprecation_reason).to eq('This was renamed. Please use `X.y`. Deprecated in 10.10.')
|
||||
expect(deprecation.deprecation_reason).to eq('This was renamed. Please use `X.y`. Deprecated in GitLab 10.10.')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
let(:options) { { reason: :renamed, milestone: '10.10' } }
|
||||
|
||||
it 'renders as reason-milestone' do
|
||||
expect(deprecation.deprecation_reason).to eq('This was renamed. Deprecated in 10.10.')
|
||||
expect(deprecation.deprecation_reason).to eq('This was renamed. Deprecated in GitLab 10.10.')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
it 'appends milestone:reason with a leading space if there is a description' do
|
||||
desc = deprecation.edit_description('Some description.')
|
||||
|
||||
expect(desc).to eq('Some description. Deprecated in 10.10: This was renamed.')
|
||||
expect(desc).to eq('Some description. Deprecated in GitLab 10.10: This was renamed.')
|
||||
end
|
||||
|
||||
it 'returns nil if there is no description' do
|
||||
|
|
@ -179,7 +179,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
it 'strips any leading or trailing spaces' do
|
||||
desc = deprecation.edit_description(" Some description. \n")
|
||||
|
||||
expect(desc).to eq('Some description. Deprecated in 10.10: This was renamed.')
|
||||
expect(desc).to eq('Some description. Deprecated in GitLab 10.10: This was renamed.')
|
||||
end
|
||||
|
||||
it 'strips any leading or trailing spaces in heredoc string literals' do
|
||||
|
|
@ -190,7 +190,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
|
||||
desc = deprecation.edit_description(description)
|
||||
|
||||
expect(desc).to eq("Lorem ipsum\ndolor sit amet. Deprecated in 10.10: This was renamed.")
|
||||
expect(desc).to eq("Lorem ipsum\ndolor sit amet. Deprecated in GitLab 10.10: This was renamed.")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
|
||||
context 'when the context is :inline' do
|
||||
it 'renders on one line' do
|
||||
expectation = '**Deprecated** in 10.10. This was renamed. Use: [`X.y`](#xy).'
|
||||
expectation = '**Deprecated** in GitLab 10.10. This was renamed. Use: [`X.y`](#xy).'
|
||||
|
||||
expect(deprecation.markdown).to eq(expectation)
|
||||
expect(deprecation.markdown(context: :inline)).to eq(expectation)
|
||||
|
|
@ -218,8 +218,8 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
context 'when the context is :block' do
|
||||
it 'renders a warning note' do
|
||||
expectation = <<~MD.chomp
|
||||
NOTE:
|
||||
**Deprecated** in 10.10.
|
||||
DETAILS:
|
||||
**Deprecated** in GitLab 10.10.
|
||||
This was renamed.
|
||||
Use: [`X.y`](#xy).
|
||||
MD
|
||||
|
|
@ -234,7 +234,7 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
|
||||
context 'when the context is :inline' do
|
||||
it 'renders on one line' do
|
||||
expectation = '**Deprecated** in 10.10. Removed.'
|
||||
expectation = '**Deprecated** in GitLab 10.10. Removed.'
|
||||
|
||||
expect(deprecation.markdown).to eq(expectation)
|
||||
expect(deprecation.markdown(context: :inline)).to eq(expectation)
|
||||
|
|
@ -244,8 +244,8 @@ RSpec.describe ::Gitlab::Graphql::Deprecations::Deprecation, feature_category: :
|
|||
context 'when the context is :block' do
|
||||
it 'renders a warning note' do
|
||||
expectation = <<~MD.chomp
|
||||
NOTE:
|
||||
**Deprecated** in 10.10.
|
||||
DETAILS:
|
||||
**Deprecated** in GitLab 10.10.
|
||||
Removed.
|
||||
MD
|
||||
|
||||
|
|
|
|||
|
|
@ -447,7 +447,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer, feature_category: :i
|
|||
|
||||
aggregate_failures do
|
||||
expect(pipeline_schedule.description).to eq('Schedule Description')
|
||||
expect(pipeline_schedule.ref).to eq('master')
|
||||
expect(pipeline_schedule.ref).to eq('refs/heads/master')
|
||||
expect(pipeline_schedule.cron).to eq('0 4 * * 0')
|
||||
expect(pipeline_schedule.cron_timezone).to eq('UTC')
|
||||
expect(pipeline_schedule.active).to eq(false)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ RSpec.describe Gitlab::Profiler do
|
|||
|
||||
it 'logs the backtrace, ignoring lines as appropriate' do
|
||||
# Skip Rails's backtrace cleaning.
|
||||
allow(Rails.backtrace_cleaner).to receive(:clean, &:itself)
|
||||
allow(Gitlab::BacktraceCleaner.backtrace_cleaner).to receive(:clean, &:itself)
|
||||
|
||||
expect(custom_logger).to receive(:add)
|
||||
.with(Logger::DEBUG,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,14 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration do
|
||||
let_it_be_with_reload(:project) { create_default(:project) }
|
||||
let_it_be_with_reload(:project) { create_default(:project, :repository) }
|
||||
let_it_be(:repository) { project.repository }
|
||||
|
||||
subject { build(:ci_pipeline_schedule) }
|
||||
subject { build(:ci_pipeline_schedule, project: project) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: false)
|
||||
end
|
||||
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to belong_to(:owner) }
|
||||
|
|
@ -25,31 +30,200 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
it_behaves_like 'cleanup by a loose foreign key' do
|
||||
let!(:parent) { create(:user) }
|
||||
let!(:model) { create(:ci_pipeline_schedule, owner: parent) }
|
||||
let!(:model) { create(:ci_pipeline_schedule, owner: parent, project: project) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
it 'does not allow invalid cron patterns' do
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron: '0 0 0 * *')
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron: '0 0 0 * *', project: project)
|
||||
|
||||
expect(pipeline_schedule).not_to be_valid
|
||||
end
|
||||
|
||||
it 'does not allow invalid cron patterns' do
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron_timezone: 'invalid')
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron_timezone: 'invalid', project: project)
|
||||
|
||||
expect(pipeline_schedule).not_to be_valid
|
||||
end
|
||||
|
||||
it 'does not allow empty variable key' do
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, variables_attributes: [{ secret_value: 'test_value' }])
|
||||
pipeline_schedule = build(:ci_pipeline_schedule,
|
||||
variables_attributes: [{ secret_value: 'test_value' }],
|
||||
project: project)
|
||||
|
||||
expect(pipeline_schedule).not_to be_valid
|
||||
end
|
||||
|
||||
context 'when an short ref record is being updated' do
|
||||
let(:new_description) { 'some description' }
|
||||
let(:ref) { 'other' }
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, cron: ' 0 0 * * * ', ref: ref, project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
repository.add_branch(project.creator, ref, 'master')
|
||||
pipeline_schedule.save!(validate: false)
|
||||
end
|
||||
|
||||
it 'does not update the ref' do
|
||||
pipeline_schedule.update!(description: new_description)
|
||||
|
||||
expect(pipeline_schedule.reload.ref).to eq(ref)
|
||||
expect(pipeline_schedule.description).to eq(new_description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'ref is branch and tag' do
|
||||
let(:ref) { 'ambiguous' }
|
||||
let(:pipeline_schedule) { build(:ci_pipeline_schedule, ref: ref, project: project) }
|
||||
|
||||
it 'allows ambiguous ref' do
|
||||
pipeline_schedule.valid?
|
||||
|
||||
expect(pipeline_schedule.errors.full_messages)
|
||||
.not_to include("Ref is ambiguous")
|
||||
end
|
||||
end
|
||||
|
||||
context 'ref is not a branch or tag' do
|
||||
let(:ref) { 'unknown' }
|
||||
let(:pipeline_schedule) { build(:ci_pipeline_schedule, ref: ref, project: project) }
|
||||
|
||||
it 'allows any ref' do
|
||||
pipeline_schedule.valid?
|
||||
|
||||
expect(pipeline_schedule.errors.full_messages)
|
||||
.not_to include("Ref is ambiguous")
|
||||
end
|
||||
end
|
||||
|
||||
context 'enforce_full_refs_for_pipeline_schedules is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: true)
|
||||
end
|
||||
|
||||
context 'ref is invalid' do
|
||||
let_it_be(:ref) { 'ambiguous' }
|
||||
|
||||
before_all do
|
||||
repository.add_tag(project.creator, ref, 'master')
|
||||
repository.add_branch(project.creator, ref, 'master')
|
||||
end
|
||||
|
||||
context 'when an short ref record is being updated' do
|
||||
let(:new_description) { 'some description' }
|
||||
let(:ref) { 'other' }
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, cron: ' 0 0 * * * ', ref: ref, project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
repository.add_branch(project.creator, ref, 'master')
|
||||
pipeline_schedule.save!(validate: false)
|
||||
end
|
||||
|
||||
it 'updates the ref' do
|
||||
pipeline_schedule.update!(description: new_description)
|
||||
|
||||
expect(pipeline_schedule.reload.ref).to eq("#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref}")
|
||||
expect(pipeline_schedule.description).to eq(new_description)
|
||||
end
|
||||
|
||||
context 'when an existing record has no ref' do
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule,
|
||||
cron: ' 0 0 * * * ',
|
||||
ref: nil,
|
||||
project: project,
|
||||
importing: true)
|
||||
end
|
||||
|
||||
it 'updates the record' do
|
||||
pipeline_schedule.update!(description: new_description)
|
||||
expect(pipeline_schedule.reload.description).to eq(new_description)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ref is branch and tag' do
|
||||
let(:pipeline_schedule) { build(:ci_pipeline_schedule, ref: ref, project: project) }
|
||||
|
||||
it 'does not allow ambiguous ref' do
|
||||
pipeline_schedule.valid?
|
||||
|
||||
expect(pipeline_schedule.errors.full_messages)
|
||||
.to include("Ref is ambiguous")
|
||||
end
|
||||
|
||||
context 'importing is enabled' do
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, ref: ref, project: project, importing: true)
|
||||
end
|
||||
|
||||
it 'does not validate the ref' do
|
||||
expect(pipeline_schedule)
|
||||
.to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'ref is not a branch or tag' do
|
||||
let(:ref) { 'unknown' }
|
||||
let(:pipeline_schedule) { build(:ci_pipeline_schedule, ref: ref, project: project) }
|
||||
|
||||
it 'does not allow wrong ref' do
|
||||
pipeline_schedule.valid?
|
||||
|
||||
expect(pipeline_schedule.errors.full_messages)
|
||||
.to include("Ref is ambiguous")
|
||||
end
|
||||
|
||||
context 'importing is enabled' do
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, ref: ref, project: project, importing: true)
|
||||
end
|
||||
|
||||
it 'does not validate the ref' do
|
||||
expect(pipeline_schedule)
|
||||
.to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an existing record has a valid ref' do
|
||||
let(:new_description) { 'some description' }
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, cron: ' 0 0 * * * ', project: project)
|
||||
end
|
||||
|
||||
it 'updates the record' do
|
||||
pipeline_schedule.update!(description: new_description)
|
||||
expect(pipeline_schedule.reload.description).to eq(new_description)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a record is being created' do
|
||||
let(:ref) { 'master' }
|
||||
let(:pipeline_schedule) do
|
||||
build(:ci_pipeline_schedule, cron: ' 0 0 * * * ', project: project, ref: ref)
|
||||
end
|
||||
|
||||
before do
|
||||
repository.add_branch(project.creator, ref, ref)
|
||||
end
|
||||
|
||||
it 'expands the ref' do
|
||||
pipeline_schedule.save!
|
||||
expect(pipeline_schedule.ref).to eq("#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when active is false' do
|
||||
it 'does not allow nullified ref' do
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, :inactive, ref: nil)
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, :inactive, ref: nil, project: project)
|
||||
|
||||
expect(pipeline_schedule).not_to be_valid
|
||||
end
|
||||
|
|
@ -57,7 +231,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
context 'when cron contains trailing whitespaces' do
|
||||
it 'strips the attribute' do
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron: ' 0 0 * * * ')
|
||||
pipeline_schedule = build(:ci_pipeline_schedule, cron: ' 0 0 * * * ', project: project)
|
||||
|
||||
expect(pipeline_schedule).to be_valid
|
||||
expect(pipeline_schedule.cron).to eq('0 0 * * *')
|
||||
|
|
@ -70,7 +244,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
let!(:pipeline_schedule) do
|
||||
travel_to(1.day.ago) do
|
||||
create(:ci_pipeline_schedule, :hourly)
|
||||
create(:ci_pipeline_schedule, :hourly, project: project)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -91,7 +265,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
subject { described_class.preloaded }
|
||||
|
||||
before do
|
||||
create_list(:ci_pipeline_schedule, 3)
|
||||
create_list(:ci_pipeline_schedule, 3, project: project)
|
||||
end
|
||||
|
||||
it 'preloads the associations' do
|
||||
|
|
@ -105,8 +279,8 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
describe '.owned_by' do
|
||||
let(:user) { create(:user) }
|
||||
let!(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, owner: user) }
|
||||
let!(:other_pipeline_schedule) { create(:ci_pipeline_schedule) }
|
||||
let!(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, owner: user, project: project) }
|
||||
let!(:other_pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
|
||||
|
||||
subject { described_class.owned_by(user) }
|
||||
|
||||
|
|
@ -116,7 +290,6 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
end
|
||||
|
||||
describe '.for_project' do
|
||||
let(:project) { create(:project) }
|
||||
let!(:project_pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
|
||||
let!(:other_pipeline_schedule) { create(:ci_pipeline_schedule) }
|
||||
|
||||
|
|
@ -129,7 +302,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
describe '#set_next_run_at' do
|
||||
let(:now) { Time.zone.local(2021, 3, 2, 1, 0) }
|
||||
let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: "0 1 * * *") }
|
||||
let(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: "0 1 * * *", project: project) }
|
||||
|
||||
it 'calls fallback method next_run_at if there is no plan limit' do
|
||||
allow(Settings).to receive(:cron_jobs).and_return({ 'pipeline_schedule_worker' => { 'cron' => "0 1 2 3 *" } })
|
||||
|
|
@ -144,8 +317,13 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
end
|
||||
|
||||
context 'when there are two different pipeline schedules in different time zones' do
|
||||
let(:pipeline_schedule_1) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'Eastern Time (US & Canada)') }
|
||||
let(:pipeline_schedule_2) { create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC') }
|
||||
let(:pipeline_schedule_1) do
|
||||
create(:ci_pipeline_schedule, :weekly, cron_timezone: 'Eastern Time (US & Canada)', project: project)
|
||||
end
|
||||
|
||||
let(:pipeline_schedule_2) do
|
||||
create(:ci_pipeline_schedule, :weekly, cron_timezone: 'UTC', project: project)
|
||||
end
|
||||
|
||||
it 'sets different next_run_at' do
|
||||
expect(pipeline_schedule_1.next_run_at).not_to eq(pipeline_schedule_2.next_run_at)
|
||||
|
|
@ -154,7 +332,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
end
|
||||
|
||||
describe '#schedule_next_run!' do
|
||||
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) }
|
||||
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project) }
|
||||
|
||||
before do
|
||||
pipeline_schedule.update_column(:next_run_at, nil)
|
||||
|
|
@ -179,7 +357,7 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
end
|
||||
|
||||
describe '#job_variables' do
|
||||
let!(:pipeline_schedule) { create(:ci_pipeline_schedule) }
|
||||
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project) }
|
||||
|
||||
let!(:pipeline_schedule_variables) do
|
||||
create_list(:ci_pipeline_schedule_variable, 2, pipeline_schedule: pipeline_schedule)
|
||||
|
|
@ -289,13 +467,13 @@ RSpec.describe Ci::PipelineSchedule, feature_category: :continuous_integration d
|
|||
|
||||
context 'loose foreign key on ci_pipeline_schedules.project_id' do
|
||||
it_behaves_like 'cleanup by a loose foreign key' do
|
||||
let!(:parent) { create(:project) }
|
||||
let_it_be(:parent) { create(:project, :repository) }
|
||||
let!(:model) { create(:ci_pipeline_schedule, project: parent) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'before_destroy' do
|
||||
let_it_be_with_reload(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: ' 0 0 * * * ') }
|
||||
let_it_be_with_reload(:pipeline_schedule) { create(:ci_pipeline_schedule, cron: ' 0 0 * * * ', project: project) }
|
||||
let_it_be_with_reload(:pipeline) { create(:ci_pipeline, pipeline_schedule: pipeline_schedule) }
|
||||
|
||||
it 'nullifys associated pipelines' do
|
||||
|
|
|
|||
|
|
@ -28,9 +28,8 @@ RSpec.describe CommitCollection, feature_category: :source_code_management do
|
|||
end
|
||||
|
||||
context 'when is with_merge_commits false' do
|
||||
let(:commit) { project.commit("60ecb67744cb56576c30214ff52294f8ce2def98") }
|
||||
|
||||
it 'excludes authors of merge commits' do
|
||||
commit = project.commit("60ecb67744cb56576c30214ff52294f8ce2def98")
|
||||
create(:user, email: commit.committer_email.upcase)
|
||||
|
||||
expect(collection.committers).to be_empty
|
||||
|
|
|
|||
|
|
@ -2053,10 +2053,10 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
|
|||
let(:commits) { double }
|
||||
let(:committers) { double }
|
||||
|
||||
context 'when not given with_merge_commits and lazy' do
|
||||
context 'when not given with_merge_commits' do
|
||||
it 'calls committers on the commits object with the expected param' do
|
||||
expect(subject).to receive(:commits).and_return(commits)
|
||||
expect(commits).to receive(:committers).with(with_merge_commits: false, lazy: false).and_return(committers)
|
||||
expect(commits).to receive(:committers).with(with_merge_commits: false).and_return(committers)
|
||||
|
||||
expect(subject.committers).to eq(committers)
|
||||
end
|
||||
|
|
@ -2065,20 +2065,11 @@ RSpec.describe MergeRequest, factory_default: :keep, feature_category: :code_rev
|
|||
context 'when given with_merge_commits true' do
|
||||
it 'calls committers on the commits object with the expected param' do
|
||||
expect(subject).to receive(:commits).and_return(commits)
|
||||
expect(commits).to receive(:committers).with(with_merge_commits: true, lazy: false).and_return(committers)
|
||||
expect(commits).to receive(:committers).with(with_merge_commits: true).and_return(committers)
|
||||
|
||||
expect(subject.committers(with_merge_commits: true)).to eq(committers)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when given lazy true' do
|
||||
it 'calls committers on the commits object with the expected param' do
|
||||
expect(subject).to receive(:commits).and_return(commits)
|
||||
expect(commits).to receive(:committers).with(with_merge_commits: false, lazy: true).and_return(committers)
|
||||
|
||||
expect(subject.committers(lazy: true)).to eq(committers)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#diverged_commits_count' do
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ RSpec.describe 'PipelineSchedulecreate', feature_category: :continuous_integrati
|
|||
description: 'created_desc',
|
||||
cron: '0 1 * * *',
|
||||
cronTimezone: 'UTC',
|
||||
ref: 'patch-x',
|
||||
ref: 'master',
|
||||
active: true,
|
||||
variables: [
|
||||
{ key: 'AAA', value: "AAA123", variableType: 'ENV_VAR' }
|
||||
|
|
@ -107,7 +107,11 @@ RSpec.describe 'PipelineSchedulecreate', feature_category: :continuous_integrati
|
|||
|
||||
expect(mutation_response['errors'])
|
||||
.to match_array(
|
||||
["Cron is invalid syntax", "Cron timezone is invalid syntax"]
|
||||
[
|
||||
"Cron is invalid syntax",
|
||||
"Cron timezone is invalid syntax",
|
||||
"Ref is ambiguous"
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
|
|||
create(:ci_pipeline_schedule_variable, key: 'bar', value: 'barvalue', pipeline_schedule: pipeline_schedule)
|
||||
end
|
||||
|
||||
let(:repository) { project.repository }
|
||||
let(:mutation) do
|
||||
variables = {
|
||||
id: pipeline_schedule.to_global_id.to_s,
|
||||
|
|
@ -50,6 +51,10 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
|
|||
let(:pipeline_schedule_parameters) { {} }
|
||||
let(:mutation_response) { graphql_mutation_response(:pipeline_schedule_update) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: false)
|
||||
end
|
||||
|
||||
context 'when unauthorized' do
|
||||
it_behaves_like 'a mutation on an unauthorized resource'
|
||||
end
|
||||
|
|
@ -73,6 +78,10 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
|
|||
}
|
||||
end
|
||||
|
||||
before do
|
||||
repository.add_branch(project.creator, 'patch-x', 'master')
|
||||
end
|
||||
|
||||
it do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
|
|
@ -135,7 +144,30 @@ RSpec.describe 'PipelineScheduleUpdate', feature_category: :continuous_integrati
|
|||
}
|
||||
end
|
||||
|
||||
it do
|
||||
context 'when enforce_full_refs_for_pipeline_schedules is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: true)
|
||||
end
|
||||
|
||||
it 'returns the errors' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
|
||||
expect(mutation_response['errors'])
|
||||
.to match_array(
|
||||
[
|
||||
"Cron is invalid syntax",
|
||||
"Cron timezone is invalid syntax",
|
||||
"Ref can't be blank",
|
||||
"Description can't be blank",
|
||||
"Ref is ambiguous"
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns the errors' do
|
||||
post_graphql_mutation(mutation, current_user: current_user)
|
||||
|
||||
expect(response).to have_gitlab_http_status(:success)
|
||||
|
|
|
|||
|
|
@ -64,4 +64,14 @@ RSpec.describe RuboCop::BatchedBackgroundMigrationsDictionary, feature_category:
|
|||
expect(described_class.new('random').finalize_after).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '.checksum' do
|
||||
let(:dictionary_data) { { c: "d", a: "b" } }
|
||||
|
||||
it 'returns a checksum of the dictionary_data' do
|
||||
allow(described_class).to receive(:dictionary_data).and_return(dictionary_data)
|
||||
|
||||
expect(described_class.checksum).to eq(Digest::SHA256.hexdigest(dictionary_data.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -227,4 +227,12 @@ RSpec.describe RuboCop::Cop::BackgroundMigration::DictionaryFile, feature_catego
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#external_dependency_checksum' do
|
||||
it 'uses the RuboCop::BatchedBackgroundMigrationsDictionary.checksum' do
|
||||
allow(RuboCop::BatchedBackgroundMigrationsDictionary).to receive(:checksum).and_return('aaaaa')
|
||||
|
||||
expect(cop.external_dependency_checksum).to eq('aaaaa')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ RSpec.describe Ci::PipelineSchedules::CreateService, feature_category: :continuo
|
|||
let_it_be(:reporter) { create(:user) }
|
||||
let_it_be_with_reload(:user) { create(:user) }
|
||||
let_it_be_with_reload(:project) { create(:project, :public, :repository) }
|
||||
let_it_be_with_reload(:repository) { project.repository }
|
||||
|
||||
subject(:service) { described_class.new(project, user, params) }
|
||||
|
||||
|
|
@ -14,7 +15,15 @@ RSpec.describe Ci::PipelineSchedules::CreateService, feature_category: :continuo
|
|||
project.add_reporter(reporter)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: false)
|
||||
end
|
||||
|
||||
describe "execute" do
|
||||
before_all do
|
||||
repository.add_branch(project.creator, 'patch-x', 'master')
|
||||
end
|
||||
|
||||
context 'when user does not have permission' do
|
||||
subject(:service) { described_class.new(project, reporter, {}) }
|
||||
|
||||
|
|
@ -43,12 +52,30 @@ RSpec.describe Ci::PipelineSchedules::CreateService, feature_category: :continuo
|
|||
|
||||
subject(:service) { described_class.new(project, user, params) }
|
||||
|
||||
context 'when enforce_full_refs_for_pipeline_schedules is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: true)
|
||||
end
|
||||
|
||||
it 'saves values with passed params' do
|
||||
result = service.execute
|
||||
|
||||
expect(result.payload).to have_attributes(
|
||||
description: 'desc',
|
||||
ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}patch-x",
|
||||
active: false,
|
||||
cron: '*/1 * * * *',
|
||||
cron_timezone: 'UTC'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'saves values with passed params' do
|
||||
result = service.execute
|
||||
|
||||
expect(result.payload).to have_attributes(
|
||||
description: 'desc',
|
||||
ref: 'patch-x',
|
||||
ref: "patch-x",
|
||||
active: false,
|
||||
cron: '*/1 * * * *',
|
||||
cron_timezone: 'UTC'
|
||||
|
|
|
|||
|
|
@ -14,14 +14,21 @@ RSpec.describe Ci::PipelineSchedules::UpdateService, feature_category: :continuo
|
|||
key: 'foo', value: 'foovalue', pipeline_schedule: pipeline_schedule)
|
||||
end
|
||||
|
||||
let_it_be_with_reload(:repository) { project.repository }
|
||||
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
project.add_owner(project_owner)
|
||||
project.add_reporter(reporter)
|
||||
repository.add_branch(project.creator, 'patch-x', 'master')
|
||||
|
||||
pipeline_schedule.reload
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: false)
|
||||
end
|
||||
|
||||
describe "execute" do
|
||||
context 'when user does not have permission' do
|
||||
subject(:service) { described_class.new(pipeline_schedule, reporter, {}) }
|
||||
|
|
@ -53,16 +60,55 @@ RSpec.describe Ci::PipelineSchedules::UpdateService, feature_category: :continuo
|
|||
|
||||
subject(:service) { described_class.new(pipeline_schedule, user, params) }
|
||||
|
||||
context 'when enforce_full_refs_for_pipeline_schedules is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: true)
|
||||
end
|
||||
|
||||
it 'updates database values with passed params' do
|
||||
expect do
|
||||
service.execute
|
||||
pipeline_schedule.reload
|
||||
end.to change { pipeline_schedule.description }
|
||||
.from('pipeline schedule').to('updated_desc')
|
||||
.and change { pipeline_schedule.ref }
|
||||
.from("#{Gitlab::Git::BRANCH_REF_PREFIX}master")
|
||||
.to("#{Gitlab::Git::BRANCH_REF_PREFIX}patch-x")
|
||||
.and change {
|
||||
pipeline_schedule.active
|
||||
}.from(true).to(false)
|
||||
.and change {
|
||||
pipeline_schedule.cron
|
||||
}.from('0 1 * * *').to('*/1 * * * *')
|
||||
.and change {
|
||||
pipeline_schedule.variables.last.key
|
||||
}.from('foo').to('bar')
|
||||
.and change {
|
||||
pipeline_schedule.variables.last.value
|
||||
}.from('foovalue').to('barvalue')
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates database values with passed params' do
|
||||
expect do
|
||||
service.execute
|
||||
pipeline_schedule.reload
|
||||
end.to change { pipeline_schedule.description }.from('pipeline schedule').to('updated_desc')
|
||||
.and change { pipeline_schedule.ref }.from('master').to('patch-x')
|
||||
.and change { pipeline_schedule.active }.from(true).to(false)
|
||||
.and change { pipeline_schedule.cron }.from('0 1 * * *').to('*/1 * * * *')
|
||||
.and change { pipeline_schedule.variables.last.key }.from('foo').to('bar')
|
||||
.and change { pipeline_schedule.variables.last.value }.from('foovalue').to('barvalue')
|
||||
end.to change { pipeline_schedule.description }
|
||||
.from('pipeline schedule').to('updated_desc')
|
||||
.and change { pipeline_schedule.ref }
|
||||
.from("#{Gitlab::Git::BRANCH_REF_PREFIX}master").to("patch-x")
|
||||
.and change {
|
||||
pipeline_schedule.active
|
||||
}.from(true).to(false)
|
||||
.and change {
|
||||
pipeline_schedule.cron
|
||||
}.from('0 1 * * *').to('*/1 * * * *')
|
||||
.and change {
|
||||
pipeline_schedule.variables.last.key
|
||||
}.from('foo').to('bar')
|
||||
.and change {
|
||||
pipeline_schedule.variables.last.value
|
||||
}.from('foovalue').to('barvalue')
|
||||
end
|
||||
|
||||
context 'when the new branch is protected', :request_store do
|
||||
|
|
|
|||
|
|
@ -13,13 +13,32 @@ RSpec.shared_examples 'pipeline schedules checking variables permission' do
|
|||
end
|
||||
|
||||
shared_examples 'success response' do
|
||||
context 'when enforce_full_refs_for_pipeline_schedules is enabled' do
|
||||
before do
|
||||
stub_feature_flags(enforce_full_refs_for_pipeline_schedules: true)
|
||||
end
|
||||
|
||||
it 'saves values with passed params' do
|
||||
result = service.execute
|
||||
|
||||
expect(result.status).to eq(:success)
|
||||
expect(result.payload).to have_attributes(
|
||||
description: 'desc',
|
||||
ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}patch-x",
|
||||
active: false,
|
||||
cron: '*/1 * * * *',
|
||||
cron_timezone: 'UTC'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'saves values with passed params' do
|
||||
result = service.execute
|
||||
|
||||
expect(result.status).to eq(:success)
|
||||
expect(result.payload).to have_attributes(
|
||||
description: 'desc',
|
||||
ref: 'patch-x',
|
||||
ref: "patch-x",
|
||||
active: false,
|
||||
cron: '*/1 * * * *',
|
||||
cron_timezone: 'UTC'
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
|
|||
it 'adds a formatted `deprecated_reason` to the subject' do
|
||||
deprecable = subject(deprecated: { milestone: '1.10', reason: 'Deprecation reason' })
|
||||
|
||||
expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in 1.10.')
|
||||
expect(deprecable.deprecation_reason).to eq('Deprecation reason. Deprecated in GitLab 1.10.')
|
||||
end
|
||||
|
||||
it 'appends to the description if given' do
|
||||
|
|
@ -40,7 +40,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
|
|||
description: 'Deprecable description.'
|
||||
)
|
||||
|
||||
expect(deprecable.description).to eq('Deprecable description. Deprecated in 1.10: Deprecation reason.')
|
||||
expect(deprecable.description).to eq('Deprecable description. Deprecated in GitLab 1.10: Deprecation reason.')
|
||||
end
|
||||
|
||||
it 'does not append to the description if it is absent' do
|
||||
|
|
@ -58,14 +58,14 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
|
|||
it 'supports named reasons: renamed' do
|
||||
deprecable = subject(deprecated: { milestone: '1.10', reason: :renamed })
|
||||
|
||||
expect(deprecable.deprecation_reason).to eq('This was renamed. Deprecated in 1.10.')
|
||||
expect(deprecable.deprecation_reason).to eq('This was renamed. Deprecated in GitLab 1.10.')
|
||||
end
|
||||
|
||||
it 'supports :alpha' do
|
||||
deprecable = subject(alpha: { milestone: '1.10' })
|
||||
|
||||
expect(deprecable.deprecation_reason).to eq(
|
||||
'**Status**: Experiment. Introduced in 1.10.'
|
||||
'**Status**: Experiment. Introduced in GitLab 1.10.'
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_relative '../../../../tooling/graphql/docs/renderer'
|
||||
|
||||
RSpec.describe Tooling::Graphql::Docs::Renderer do
|
||||
|
|
@ -243,7 +244,7 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="deprecatedtestfoofooarg"></a>`fooArg` **{warning-solid}** | [`String`](#string) | **Deprecated** in 101.2. Bad argument. |
|
||||
| <a id="deprecatedtestfoofooarg"></a>`fooArg` **{warning-solid}** | [`String`](#string) | **Deprecated** in GitLab 101.2. Bad argument. |
|
||||
DOC
|
||||
end
|
||||
|
||||
|
|
@ -290,8 +291,8 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="deprecatedtestbar"></a>`bar` **{warning-solid}** | [`String!`](#string) | **Deprecated** in 1.10. This was renamed. Use: [`Query.boom`](#queryboom). |
|
||||
| <a id="deprecatedtestfoo"></a>`foo` **{warning-solid}** | [`String!`](#string) | **Deprecated** in 1.10. This is deprecated. |
|
||||
| <a id="deprecatedtestbar"></a>`bar` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 1.10. This was renamed. Use: [`Query.boom`](#queryboom). |
|
||||
| <a id="deprecatedtestfoo"></a>`foo` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 1.10. This is deprecated. |
|
||||
|
||||
#### Fields with arguments
|
||||
|
||||
|
|
@ -299,8 +300,8 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
A description.
|
||||
|
||||
NOTE:
|
||||
**Deprecated** in 1.10.
|
||||
DETAILS:
|
||||
**Deprecated** in GitLab 1.10.
|
||||
Do not use.
|
||||
Use: [`X.y`](#xy).
|
||||
|
||||
|
|
@ -335,8 +336,8 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
A bar.
|
||||
|
||||
NOTE:
|
||||
**Deprecated** in 10.11.
|
||||
DETAILS:
|
||||
**Deprecated** in GitLab 10.11.
|
||||
This was renamed.
|
||||
Use: [`Query.foo`](#queryfoo).
|
||||
|
||||
|
|
@ -377,7 +378,7 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="alphatestfoofooarg"></a>`fooArg` **{warning-solid}** | [`String`](#string) | **Introduced** in 101.2. **Status**: Experiment. Argument description. |
|
||||
| <a id="alphatestfoofooarg"></a>`fooArg` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 101.2. **Status**: Experiment. Argument description. |
|
||||
DOC
|
||||
end
|
||||
|
||||
|
|
@ -415,7 +416,7 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="alphatestfoo"></a>`foo` **{warning-solid}** | [`String!`](#string) | **Introduced** in 1.10. **Status**: Experiment. A description. |
|
||||
| <a id="alphatestfoo"></a>`foo` **{warning-solid}** | [`String!`](#string) | **Introduced** in GitLab 1.10. **Status**: Experiment. A description. |
|
||||
|
||||
#### Fields with arguments
|
||||
|
||||
|
|
@ -423,8 +424,8 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
A description.
|
||||
|
||||
NOTE:
|
||||
**Introduced** in 1.10.
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 1.10.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`String!`](#string).
|
||||
|
|
@ -458,8 +459,8 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
A bar.
|
||||
|
||||
NOTE:
|
||||
**Introduced** in 10.11.
|
||||
DETAILS:
|
||||
**Introduced** in GitLab 10.11.
|
||||
**Status**: Experiment.
|
||||
|
||||
Returns [`Int`](#int).
|
||||
|
|
@ -500,9 +501,9 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
|
||||
| Value | Description |
|
||||
| ----- | ----------- |
|
||||
| <a id="myenumbar"></a>`BAR` **{warning-solid}** | **Deprecated** in 1.10. This is deprecated. |
|
||||
| <a id="myenumbar"></a>`BAR` **{warning-solid}** | **Deprecated** in GitLab 1.10. This is deprecated. |
|
||||
| <a id="myenumbaz"></a>`BAZ` | A description of BAZ. |
|
||||
| <a id="myenumboop"></a>`BOOP` **{warning-solid}** | **Deprecated** in 1.10. This was renamed. Use: [`MyEnum.BAR`](#myenumbar). |
|
||||
| <a id="myenumboop"></a>`BOOP` **{warning-solid}** | **Deprecated** in GitLab 1.10. This was renamed. Use: [`MyEnum.BAR`](#myenumbar). |
|
||||
DOC
|
||||
end
|
||||
|
||||
|
|
@ -611,7 +612,7 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationmakeitprettyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationmakeitprettyprettinessfactor"></a>`prettinessFactor` | [`Float!`](#float) | How much prettier?. |
|
||||
| <a id="mutationmakeitprettypulchritude"></a>`pulchritude` **{warning-solid}** | [`Float`](#float) | **Deprecated:** This was renamed. Please use `prettinessFactor`. Deprecated in 72.34. |
|
||||
| <a id="mutationmakeitprettypulchritude"></a>`pulchritude` **{warning-solid}** | [`Float`](#float) | **Deprecated:** This was renamed. Please use `prettinessFactor`. Deprecated in GitLab 72.34. |
|
||||
|
||||
#### Fields
|
||||
|
||||
|
|
@ -620,7 +621,7 @@ RSpec.describe Tooling::Graphql::Docs::Renderer do
|
|||
| <a id="mutationmakeitprettyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationmakeitprettyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
|
||||
| <a id="mutationmakeitprettyeverything"></a>`everything` | [`String`](#string) | What we made prettier. |
|
||||
| <a id="mutationmakeitprettyomnis"></a>`omnis` **{warning-solid}** | [`String`](#string) | **Deprecated:** This was renamed. Please use `everything`. Deprecated in 72.34. |
|
||||
| <a id="mutationmakeitprettyomnis"></a>`omnis` **{warning-solid}** | [`String`](#string) | **Deprecated:** This was renamed. Please use `everything`. Deprecated in GitLab 72.34. |
|
||||
DOC
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ RSpec.describe RunPipelineScheduleWorker, feature_category: :continuous_integrat
|
|||
|
||||
describe '#perform' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:project) { create(:project, namespace: group) }
|
||||
let_it_be(:project) { create(:project, :repository, namespace: group) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly, project: project ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,25 +7,25 @@ PATH
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actionpack (7.0.8)
|
||||
actionview (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionpack (7.0.8.1)
|
||||
actionview (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
rack (~> 2.0, >= 2.2.4)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actionview (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
actionview (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activemodel (7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activerecord (7.0.8)
|
||||
activemodel (= 7.0.8)
|
||||
activesupport (= 7.0.8)
|
||||
activesupport (7.0.8)
|
||||
activemodel (7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
activerecord (7.0.8.1)
|
||||
activemodel (= 7.0.8.1)
|
||||
activesupport (= 7.0.8.1)
|
||||
activesupport (7.0.8.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
|
|
@ -35,7 +35,7 @@ GEM
|
|||
builder (3.2.4)
|
||||
codeclimate-test-reporter (0.6.0)
|
||||
simplecov (>= 0.7.1, < 1.0.0)
|
||||
concurrent-ruby (1.2.2)
|
||||
concurrent-ruby (1.2.3)
|
||||
crass (1.0.6)
|
||||
data_objects (0.10.17)
|
||||
addressable (~> 2.1)
|
||||
|
|
@ -54,7 +54,7 @@ GEM
|
|||
erubi (1.12.0)
|
||||
i18n (1.14.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
loofah (2.21.3)
|
||||
loofah (2.22.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.12.0)
|
||||
mini_portile2 (2.8.0)
|
||||
|
|
@ -63,8 +63,8 @@ GEM
|
|||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
public_suffix (5.0.0)
|
||||
racc (1.7.1)
|
||||
rack (2.2.8)
|
||||
racc (1.7.3)
|
||||
rack (2.2.8.1)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rails-dom-testing (2.2.0)
|
||||
|
|
@ -93,8 +93,8 @@ PLATFORMS
|
|||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
actionpack (~> 7.0.8)
|
||||
activerecord (~> 7.0.8)
|
||||
actionpack (~> 7.0.8.1)
|
||||
activerecord (~> 7.0.8.1)
|
||||
attr_encrypted!
|
||||
codeclimate-test-reporter (<= 0.6.0)
|
||||
dm-sqlite-adapter
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Forked from https://github.com/attr-encrypted/attr_encrypted."
|
|||
|
||||
s.add_dependency('encryptor', ['~> 3.0.0'])
|
||||
|
||||
activerecord_version = "~> 7.0.8"
|
||||
activerecord_version = "~> 7.0.8.1"
|
||||
s.add_development_dependency('activerecord', activerecord_version)
|
||||
s.add_development_dependency('actionpack', activerecord_version)
|
||||
s.add_development_dependency('rake')
|
||||
|
|
|
|||
16
yarn.lock
16
yarn.lock
|
|
@ -1905,15 +1905,15 @@
|
|||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
|
||||
integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
|
||||
|
||||
"@rails/actioncable@7.0.8":
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.0.8.tgz#f44e7517f2d1570f1eabeea457dbeb17ed3a2d12"
|
||||
integrity sha512-GjYQv89ZOOfbFw8VMNUOG33GXzyAA/TCVoD+742Ob4svm1XXUkd+w+ewqUXd+7VHQtV35y1/O78AGIPeJDTy/g==
|
||||
"@rails/actioncable@7.0.8-1":
|
||||
version "7.0.8-1"
|
||||
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.0.8-1.tgz#aa45d584ca316474411e44df2fb3c1fb268d0e48"
|
||||
integrity sha512-d9dl+IWhYg0fozbsE0imewWEx6EpnRP3SDfdF4vwLdWJKLXZKOKJ6AiCKEvsratabS/COyxVHajuW3n5f1JlFA==
|
||||
|
||||
"@rails/ujs@7.0.8":
|
||||
version "7.0.8"
|
||||
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.8.tgz#59853367d0827b3955d2c4bedfd5eba4a79d3422"
|
||||
integrity sha512-tOQQBVH8LsUpGXqDnk+kaOGVsgZ8maHAhEiw3Git3p88q+c0Slgu47HuDnL6sVxeCfz24zbq7dOjsVYDiTpDIA==
|
||||
"@rails/ujs@7.0.8-1":
|
||||
version "7.0.8-1"
|
||||
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-7.0.8-1.tgz#4156942025aa6b016d3d9a7df3e542ac962359e2"
|
||||
integrity sha512-uZRCeEl6zY/9JvjTpQilbJyBqFr4onNJTkQTJgfsXDZPJyqCAvyGGraO0LaiHvWKNqD1fTRamdSYg/l+U7daVA==
|
||||
|
||||
"@remirror/core-constants@^2.0.0":
|
||||
version "2.0.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue