Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-18 18:10:42 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-18 18:10:42 +0300
commitbfc7eec58ee891178d2a81c424f6c1de22feae5f (patch)
tree7cf9e2f76befc383e99d28d397ecec4bb9e8d2c9 /app
parentf23a9a17ed6237c346d2e9210c6841e319e8d030 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/boards/components/board_card_layout.vue11
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue18
-rw-r--r--app/assets/javascripts/environments/components/canary_deployment_callout.vue68
-rw-r--r--app/assets/javascripts/environments/components/container.vue12
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue12
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue26
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js3
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue12
-rw-r--r--app/assets/javascripts/environments/index.js3
-rw-r--r--app/assets/javascripts/environments/mixins/canary_callout_mixin.js26
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js11
-rw-r--r--app/assets/javascripts/jira_connect/api.js9
-rw-r--r--app/assets/javascripts/jira_connect/components/app.vue36
-rw-r--r--app/assets/javascripts/jira_connect/components/groups_list.vue88
-rw-r--r--app/assets/javascripts/jira_connect/components/groups_list_item.vue42
-rw-r--r--app/assets/javascripts/jira_connect/constants.js1
-rw-r--r--app/assets/javascripts/jira_connect/index.js7
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js44
-rw-r--r--app/assets/stylesheets/page_bundles/environments.scss54
-rw-r--r--app/assets/stylesheets/page_bundles/jira_connect.scss19
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss2
-rw-r--r--app/graphql/types/notes/note_type.rb7
-rw-r--r--app/helpers/jira_connect_helper.rb6
-rw-r--r--app/services/jira_connect/sync_service.rb2
-rw-r--r--app/services/projects/update_pages_service.rb41
-rw-r--r--app/views/jira_connect/subscriptions/index.html.haml2
-rw-r--r--app/workers/container_expiration_policies/cleanup_container_repository_worker.rb7
27 files changed, 304 insertions, 265 deletions
diff --git a/app/assets/javascripts/boards/components/board_card_layout.vue b/app/assets/javascripts/boards/components/board_card_layout.vue
index 8b0265237ba..0a2301394c1 100644
--- a/app/assets/javascripts/boards/components/board_card_layout.vue
+++ b/app/assets/javascripts/boards/components/board_card_layout.vue
@@ -1,13 +1,17 @@
<script>
+import { mapActions, mapGetters } from 'vuex';
import IssueCardInner from './issue_card_inner.vue';
import IssueCardInnerDeprecated from './issue_card_inner_deprecated.vue';
import boardsStore from '../stores/boards_store';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { ISSUABLE } from '~/boards/constants';
export default {
name: 'BoardCardLayout',
components: {
IssueCardInner: gon.features?.graphqlBoardLists ? IssueCardInner : IssueCardInnerDeprecated,
},
+ mixins: [glFeatureFlagMixin()],
props: {
list: {
type: Object,
@@ -42,11 +46,13 @@ export default {
};
},
computed: {
+ ...mapGetters(['isSwimlanesOn']),
multiSelectVisible() {
return this.multiSelect.list.findIndex((issue) => issue.id === this.issue.id) > -1;
},
},
methods: {
+ ...mapActions(['setActiveId']),
mouseDown() {
this.showDetail = true;
},
@@ -57,6 +63,11 @@ export default {
// Don't do anything if this happened on a no trigger element
if (e.target.classList.contains('js-no-trigger')) return;
+ if (this.glFeatures.graphqlBoardLists || this.isSwimlanesOn) {
+ this.setActiveId({ id: this.issue.id, sidebarType: ISSUABLE });
+ return;
+ }
+
const isMultiSelect = e.ctrlKey || e.metaKey;
if (this.showDetail || isMultiSelect) {
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 2d62dc0fa4b..19254343208 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -109,14 +109,14 @@ export default {
/>
</component>
- <template v-else>
- <epics-swimlanes
- ref="swimlanes"
- :lists="boardListsToUse"
- :can-admin-list="canAdminList"
- :disabled="disabled"
- />
- <board-content-sidebar />
- </template>
+ <epics-swimlanes
+ v-else
+ ref="swimlanes"
+ :lists="boardListsToUse"
+ :can-admin-list="canAdminList"
+ :disabled="disabled"
+ />
+
+ <board-content-sidebar v-if="isSwimlanesOn || glFeatures.graphqlBoardLists" />
</div>
</template>
diff --git a/app/assets/javascripts/environments/components/canary_deployment_callout.vue b/app/assets/javascripts/environments/components/canary_deployment_callout.vue
deleted file mode 100644
index a5c0d78524b..00000000000
--- a/app/assets/javascripts/environments/components/canary_deployment_callout.vue
+++ /dev/null
@@ -1,68 +0,0 @@
-<script>
-import { GlButton, GlLink, GlIcon } from '@gitlab/ui';
-import PersistentUserCallout from '~/persistent_user_callout';
-
-export default {
- components: {
- GlButton,
- GlLink,
- GlIcon,
- },
- props: {
- canaryDeploymentFeatureId: {
- type: String,
- required: true,
- },
- userCalloutsPath: {
- type: String,
- required: true,
- },
- lockPromotionSvgPath: {
- type: String,
- required: true,
- },
- helpCanaryDeploymentsPath: {
- type: String,
- required: true,
- },
- },
- mounted() {
- const callout = this.$refs['canary-deployment-callout'];
- PersistentUserCallout.factory(callout);
- },
-};
-</script>
-
-<template>
- <div
- ref="canary-deployment-callout"
- class="p-3 canary-deployment-callout"
- :data-dismiss-endpoint="userCalloutsPath"
- :data-feature-id="canaryDeploymentFeatureId"
- >
- <img class="canary-deployment-callout-lock pr-3" :src="lockPromotionSvgPath" />
-
- <div class="pl-3">
- <p class="font-weight-bold mb-1">
- {{ __('Upgrade plan to unlock Canary Deployments feature') }}
- </p>
-
- <p class="canary-deployment-callout-message">
- {{
- __(
- 'Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application.',
- )
- }}
- <gl-link :href="helpCanaryDeploymentsPath">{{ __('Read more') }}</gl-link>
- </p>
-
- <gl-button href="https://about.gitlab.com/sales/" category="secondary" variant="info">{{
- __('Contact sales to upgrade')
- }}</gl-button>
- </div>
-
- <div class="ml-auto pr-2 canary-deployment-callout-close js-close">
- <gl-icon name="close" />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index e7697f14802..c6b34fecbb7 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -10,11 +10,6 @@ export default {
GlLoadingIcon,
},
props: {
- canaryDeploymentFeatureId: {
- type: String,
- required: false,
- default: null,
- },
isLoading: {
type: Boolean,
required: true,
@@ -46,11 +41,6 @@ export default {
required: false,
default: '',
},
- showCanaryDeploymentCallout: {
- type: Boolean,
- required: false,
- default: false,
- },
userCalloutsPath: {
type: String,
required: false,
@@ -75,8 +65,6 @@ export default {
<environment-table
:environments="environments"
:can-read-environment="canReadEnvironment"
- :canary-deployment-feature-id="canaryDeploymentFeatureId"
- :show-canary-deployment-callout="showCanaryDeploymentCallout"
:user-callouts-path="userCalloutsPath"
:lock-promotion-svg-path="lockPromotionSvgPath"
:help-canary-deployments-path="helpCanaryDeploymentsPath"
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index 13228873f44..6f68c6e864a 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -39,11 +39,6 @@ export default {
type: String,
required: true,
},
- canaryDeploymentFeatureId: {
- type: String,
- required: false,
- default: '',
- },
canCreateEnvironment: {
type: Boolean,
required: true,
@@ -75,11 +70,6 @@ export default {
required: false,
default: '',
},
- showCanaryDeploymentCallout: {
- type: Boolean,
- required: false,
- default: false,
- },
userCalloutsPath: {
type: String,
required: false,
@@ -205,8 +195,6 @@ export default {
:environments="state.environments"
:pagination="state.paginationInformation"
:can-read-environment="canReadEnvironment"
- :canary-deployment-feature-id="canaryDeploymentFeatureId"
- :show-canary-deployment-callout="showCanaryDeploymentCallout"
:user-callouts-path="userCalloutsPath"
:lock-promotion-svg-path="lockPromotionSvgPath"
:help-canary-deployments-path="helpCanaryDeploymentsPath"
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index ff183e51395..bbb56ca6f26 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -8,14 +8,12 @@ import { s__ } from '~/locale';
import EnvironmentItem from './environment_item.vue';
import DeployBoard from './deploy_board.vue';
import CanaryUpdateModal from './canary_update_modal.vue';
-import CanaryDeploymentCallout from './canary_deployment_callout.vue';
export default {
components: {
EnvironmentItem,
GlLoadingIcon,
DeployBoard,
- CanaryDeploymentCallout,
EnvironmentAlert: () => import('ee_component/environments/components/environment_alert.vue'),
CanaryUpdateModal,
},
@@ -35,11 +33,6 @@ export default {
required: false,
default: false,
},
- canaryDeploymentFeatureId: {
- type: String,
- required: false,
- default: '',
- },
helpCanaryDeploymentsPath: {
type: String,
required: false,
@@ -50,11 +43,6 @@ export default {
required: false,
default: '',
},
- showCanaryDeploymentCallout: {
- type: Boolean,
- required: false,
- default: false,
- },
userCalloutsPath: {
type: String,
required: false,
@@ -123,9 +111,6 @@ export default {
shouldRenderFolderContent(env) {
return env.isFolder && env.isOpen && env.children && env.children.length > 0;
},
- shouldShowCanaryCallout(env) {
- return env.showCanaryCallout && this.showCanaryDeploymentCallout;
- },
shouldRenderAlert(env) {
return env?.has_opened_alert;
},
@@ -243,17 +228,6 @@ export default {
</div>
</template>
</template>
-
- <template v-if="shouldShowCanaryCallout(model)">
- <canary-deployment-callout
- :key="`canary-promo-${i}`"
- :canary-deployment-feature-id="canaryDeploymentFeatureId"
- :user-callouts-path="userCalloutsPath"
- :lock-promotion-svg-path="lockPromotionSvgPath"
- :help-canary-deployments-path="helpCanaryDeploymentsPath"
- :data-js-canary-promo-key="i"
- />
- </template>
</template>
</div>
</template>
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 6c547c3713a..e4726412f99 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -1,6 +1,5 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import canaryCalloutMixin from '../mixins/canary_callout_mixin';
import environmentsFolderApp from './environments_folder_view.vue';
import { parseBoolean } from '../../lib/utils/common_utils';
import Translate from '../../vue_shared/translate';
@@ -21,7 +20,6 @@ export default () => {
components: {
environmentsFolderApp,
},
- mixins: [canaryCalloutMixin],
apolloProvider,
provide: {
projectPath: el.dataset.projectPath,
@@ -43,7 +41,6 @@ export default () => {
folderName: this.folderName,
cssContainerClass: this.cssContainerClass,
canReadEnvironment: this.canReadEnvironment,
- ...this.canaryCalloutProps,
},
});
},
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 25f5483c58b..dbb60fa4622 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -34,16 +34,6 @@ export default {
type: Boolean,
required: true,
},
- canaryDeploymentFeatureId: {
- type: String,
- required: false,
- default: '',
- },
- showCanaryDeploymentCallout: {
- type: Boolean,
- required: false,
- default: false,
- },
userCalloutsPath: {
type: String,
required: false,
@@ -98,8 +88,6 @@ export default {
:environments="state.environments"
:pagination="state.paginationInformation"
:can-read-environment="canReadEnvironment"
- :canary-deployment-feature-id="canaryDeploymentFeatureId"
- :show-canary-deployment-callout="showCanaryDeploymentCallout"
:user-callouts-path="userCalloutsPath"
:lock-promotion-svg-path="lockPromotionSvgPath"
:help-canary-deployments-path="helpCanaryDeploymentsPath"
diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js
index 8e8af3f32f7..4d734a457ab 100644
--- a/app/assets/javascripts/environments/index.js
+++ b/app/assets/javascripts/environments/index.js
@@ -1,6 +1,5 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import canaryCalloutMixin from './mixins/canary_callout_mixin';
import environmentsComponent from './components/environments_app.vue';
import { parseBoolean } from '../lib/utils/common_utils';
import Translate from '../vue_shared/translate';
@@ -20,7 +19,6 @@ export default () => {
components: {
environmentsComponent,
},
- mixins: [canaryCalloutMixin],
apolloProvider,
provide: {
projectPath: el.dataset.projectPath,
@@ -46,7 +44,6 @@ export default () => {
deployBoardsHelpPath: this.deployBoardsHelpPath,
canCreateEnvironment: this.canCreateEnvironment,
canReadEnvironment: this.canReadEnvironment,
- ...this.canaryCalloutProps,
},
});
},
diff --git a/app/assets/javascripts/environments/mixins/canary_callout_mixin.js b/app/assets/javascripts/environments/mixins/canary_callout_mixin.js
deleted file mode 100644
index e9f1a144cb3..00000000000
--- a/app/assets/javascripts/environments/mixins/canary_callout_mixin.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import { parseBoolean } from '~/lib/utils/common_utils';
-
-export default {
- data() {
- const data = this.$options.el.dataset;
-
- return {
- canaryDeploymentFeatureId: data.environmentsDataCanaryDeploymentFeatureId,
- showCanaryDeploymentCallout: parseBoolean(data.environmentsDataShowCanaryDeploymentCallout),
- userCalloutsPath: data.environmentsDataUserCalloutsPath,
- lockPromotionSvgPath: data.environmentsDataLockPromotionSvgPath,
- helpCanaryDeploymentsPath: data.environmentsDataHelpCanaryDeploymentsPath,
- };
- },
- computed: {
- canaryCalloutProps() {
- return {
- canaryDeploymentFeatureId: this.canaryDeploymentFeatureId,
- showCanaryDeploymentCallout: this.showCanaryDeploymentCallout,
- userCalloutsPath: this.userCalloutsPath,
- lockPromotionSvgPath: this.lockPromotionSvgPath,
- helpCanaryDeploymentsPath: this.helpCanaryDeploymentsPath,
- };
- },
- },
-};
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index 2f4f53953f6..8911885e920 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -81,17 +81,6 @@ export default class EnvironmentsStore {
this.state.environments = filteredEnvironments;
- /**
- * Add the canary callout banner underneath the second environment listed.
- *
- * If there is only one environment, then add to it underneath the first.
- */
- if (this.state.environments.length >= 2) {
- this.state.environments[1].showCanaryCallout = true;
- } else if (this.state.environments.length === 1) {
- this.state.environments[0].showCanaryCallout = true;
- }
-
return filteredEnvironments;
}
diff --git a/app/assets/javascripts/jira_connect/api.js b/app/assets/javascripts/jira_connect/api.js
index 55f2ef4f820..d689a2d1962 100644
--- a/app/assets/javascripts/jira_connect/api.js
+++ b/app/assets/javascripts/jira_connect/api.js
@@ -22,3 +22,12 @@ export const removeSubscription = async (removePath) => {
},
});
};
+
+export const fetchGroups = async (groupsPath, { page, perPage }) => {
+ return axios.get(groupsPath, {
+ params: {
+ page,
+ per_page: perPage,
+ },
+ });
+};
diff --git a/app/assets/javascripts/jira_connect/components/app.vue b/app/assets/javascripts/jira_connect/components/app.vue
index e08918a6720..f5bf30f4488 100644
--- a/app/assets/javascripts/jira_connect/components/app.vue
+++ b/app/assets/javascripts/jira_connect/components/app.vue
@@ -1,20 +1,33 @@
<script>
import { mapState } from 'vuex';
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import GroupsList from './groups_list.vue';
export default {
name: 'JiraConnectApp',
components: {
GlAlert,
+ GlButton,
+ GlModal,
+ GroupsList,
+ },
+ directives: {
+ GlModalDirective,
},
mixins: [glFeatureFlagsMixin()],
computed: {
...mapState(['errorMessage']),
- showNewUi() {
+ showNewUI() {
return this.glFeatures.newJiraConnectUi;
},
},
+ modal: {
+ cancelProps: {
+ text: __('Cancel'),
+ },
+ },
};
</script>
@@ -26,8 +39,25 @@ export default {
<h1>GitLab for Jira Configuration</h1>
- <div v-if="showNewUi">
+ <div
+ v-if="showNewUI"
+ class="gl-display-flex gl-justify-content-space-between gl-my-5 gl-pb-4 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
+ >
<h3 data-testid="new-jira-connect-ui-heading">{{ s__('Integrations|Linked namespaces') }}</h3>
+ <gl-button
+ v-gl-modal-directive="'add-namespace-modal'"
+ category="primary"
+ variant="info"
+ class="gl-align-self-center"
+ >{{ s__('Integrations|Add namespace') }}</gl-button
+ >
+ <gl-modal
+ modal-id="add-namespace-modal"
+ :title="s__('Integrations|Link namespaces')"
+ :action-cancel="$options.modal.cancelProps"
+ >
+ <groups-list />
+ </gl-modal>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/jira_connect/components/groups_list.vue b/app/assets/javascripts/jira_connect/components/groups_list.vue
new file mode 100644
index 00000000000..eeddd32addc
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/components/groups_list.vue
@@ -0,0 +1,88 @@
+<script>
+import { GlTabs, GlTab, GlLoadingIcon, GlPagination } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
+import { fetchGroups } from '~/jira_connect/api';
+import { defaultPerPage } from '~/jira_connect/constants';
+import GroupsListItem from './groups_list_item.vue';
+
+export default {
+ components: {
+ GlTabs,
+ GlTab,
+ GlLoadingIcon,
+ GlPagination,
+ GroupsListItem,
+ },
+ inject: {
+ groupsPath: {
+ default: '',
+ },
+ },
+ data() {
+ return {
+ groups: [],
+ isLoading: false,
+ page: 1,
+ perPage: defaultPerPage,
+ totalItems: 0,
+ };
+ },
+ mounted() {
+ this.loadGroups();
+ },
+ methods: {
+ loadGroups() {
+ this.isLoading = true;
+
+ fetchGroups(this.groupsPath, {
+ page: this.page,
+ perPage: this.perPage,
+ })
+ .then((response) => {
+ const { page, total } = parseIntPagination(normalizeHeaders(response.headers));
+ this.page = page;
+ this.totalItems = total;
+ this.groups = response.data;
+ })
+ .catch(() => {
+ // eslint-disable-next-line no-alert
+ alert(s__('Integrations|Failed to load namespaces. Please try again.'));
+ })
+ .finally(() => {
+ this.isLoading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-tabs>
+ <gl-tab :title="__('Groups and subgroups')" class="gl-pt-3">
+ <gl-loading-icon v-if="isLoading" size="md" />
+ <div v-else-if="groups.length === 0" class="gl-text-center">
+ <h5>{{ s__('Integrations|No available namespaces.') }}</h5>
+ <p class="gl-mt-5">
+ {{
+ s__('Integrations|You must have owner or maintainer permissions to link namespaces.')
+ }}
+ </p>
+ </div>
+ <ul v-else class="gl-list-style-none gl-pl-0">
+ <groups-list-item v-for="group in groups" :key="group.id" :group="group" />
+ </ul>
+
+ <div class="gl-display-flex gl-justify-content-center gl-mt-5">
+ <gl-pagination
+ v-if="totalItems > perPage && groups.length > 0"
+ v-model="page"
+ class="gl-mb-0"
+ :per-page="perPage"
+ :total-items="totalItems"
+ @input="loadGroups"
+ />
+ </div>
+ </gl-tab>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/jira_connect/components/groups_list_item.vue b/app/assets/javascripts/jira_connect/components/groups_list_item.vue
new file mode 100644
index 00000000000..15e37ab3cb0
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/components/groups_list_item.vue
@@ -0,0 +1,42 @@
+<script>
+import { GlIcon, GlAvatar } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlIcon,
+ GlAvatar,
+ },
+ props: {
+ group: {
+ type: Object,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="gl-border-b-1 gl-border-b-solid gl-border-b-gray-200">
+ <div class="gl-display-flex gl-align-items-center gl-py-3">
+ <gl-icon name="folder-o" class="gl-mr-3" />
+ <div class="gl-display-none gl-flex-shrink-0 gl-display-sm-flex gl-mr-3">
+ <gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatar_url" />
+ </div>
+ <div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center">
+ <div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1">
+ <div class="gl-display-flex gl-align-items-center gl-flex-wrap">
+ <span
+ class="gl-mr-3 gl-text-gray-900! gl-font-weight-bold"
+ data-testid="group-list-item-name"
+ >
+ {{ group.full_name }}
+ </span>
+ </div>
+ <div v-if="group.description" data-testid="group-list-item-description">
+ <p class="gl-mt-2! gl-mb-0 gl-text-gray-600" v-text="group.description"></p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </li>
+</template>
diff --git a/app/assets/javascripts/jira_connect/constants.js b/app/assets/javascripts/jira_connect/constants.js
new file mode 100644
index 00000000000..2b3be5cd5cd
--- /dev/null
+++ b/app/assets/javascripts/jira_connect/constants.js
@@ -0,0 +1 @@
+export const defaultPerPage = 10;
diff --git a/app/assets/javascripts/jira_connect/index.js b/app/assets/javascripts/jira_connect/index.js
index 0dbcb778a6c..dc2a77f4e0c 100644
--- a/app/assets/javascripts/jira_connect/index.js
+++ b/app/assets/javascripts/jira_connect/index.js
@@ -73,11 +73,16 @@ function initJiraConnect() {
Vue.use(Translate);
Vue.use(GlFeatureFlagsPlugin);
+ const { groupsPath } = el.dataset;
+
return new Vue({
el,
store,
+ provide: {
+ groupsPath,
+ },
render(createElement) {
- return createElement(JiraConnectApp, {});
+ return createElement(JiraConnectApp);
},
});
}
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 2d40f60a758..15f7c0c874e 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -4,6 +4,8 @@ import * as timeago from 'timeago.js';
import dateFormat from 'dateformat';
import { languageCode, s__, __, n__ } from '../../locale';
+const MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
+
window.timeago = timeago;
/**
@@ -851,3 +853,45 @@ export const format24HourTimeStringFromInt = (time) => {
const formatted24HourString = time > 9 ? `${time}:00` : `0${time}:00`;
return formatted24HourString;
};
+
+/**
+ * A utility function which checks if two date ranges overlap.
+ *
+ * @param {Object} givenPeriodLeft - the first period to compare.
+ * @param {Object} givenPeriodRight - the second period to compare.
+ * @returns {Object} { overlap: number of days the overlap is present, overlapStartDate: the start date of the overlap in time format, overlapEndDate: the end date of the overlap in time format }
+ * @throws {Error} Uncaught Error: Invalid period
+ *
+ * @example
+ * getOverlappingDaysInPeriods(
+ * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 13) },
+ * { start: new Date(2021, 0, 11), end: new Date(2021, 0, 14) }
+ * ) => { daysOverlap: 2, overlapStartDate: 1610323200000, overlapEndDate: 1610496000000 }
+ *
+ */
+export const getOverlappingDaysInPeriods = (givenPeriodLeft = {}, givenPeriodRight = {}) => {
+ const leftStartTime = new Date(givenPeriodLeft.start).getTime();
+ const leftEndTime = new Date(givenPeriodLeft.end).getTime();
+ const rightStartTime = new Date(givenPeriodRight.start).getTime();
+ const rightEndTime = new Date(givenPeriodRight.end).getTime();
+
+ if (!(leftStartTime <= leftEndTime && rightStartTime <= rightEndTime)) {
+ throw new Error(__('Invalid period'));
+ }
+
+ const isOverlapping = leftStartTime < rightEndTime && rightStartTime < leftEndTime;
+
+ if (!isOverlapping) {
+ return { daysOverlap: 0 };
+ }
+
+ const overlapStartDate = Math.max(leftStartTime, rightStartTime);
+ const overlapEndDate = rightEndTime > leftEndTime ? leftEndTime : rightEndTime;
+ const differenceInMs = overlapEndDate - overlapStartDate;
+
+ return {
+ daysOverlap: Math.ceil(differenceInMs / MILLISECONDS_IN_DAY),
+ overlapStartDate,
+ overlapEndDate,
+ };
+};
diff --git a/app/assets/stylesheets/page_bundles/environments.scss b/app/assets/stylesheets/page_bundles/environments.scss
index 470660adba9..7d5f501d633 100644
--- a/app/assets/stylesheets/page_bundles/environments.scss
+++ b/app/assets/stylesheets/page_bundles/environments.scss
@@ -218,57 +218,3 @@
padding-right: 10px;
}
}
-
-.canary-deployment-callout {
- border-bottom: 1px solid var(--gray-500, $gray-500);
- display: flex;
-
- @include media-breakpoint-down(sm) {
- display: none;
- }
-
- &-lock {
- height: 82px;
- width: 92px;
- }
-
- &-message {
- max-width: 600px;
- color: var(--gray-500, $gray-500);
- }
-
- &-close {
- color: var(--gray-500, $gray-500);
- cursor: pointer;
- }
-
- &-button {
- border-color: var(--blue-500, $blue-500);
- color: var(--blue-500, $blue-500);
-
- &:not(:disabled):not(.disabled):active {
- background-color: var(--blue-200, $blue-200);
- border: 2px solid var(--blue-600, $blue-600);
- color: var(--blue-700, $blue-700);
- height: 34px;
- padding: 5px 9px;
- }
-
- &:focus {
- background-color: var(--blue-500, $blue-500);
- border: 2px solid var(--blue-500, $blue-500);
- box-shadow: 0 0 4px 1px var(--blue-200, $blue-200);
- color: var(--blue-600, $blue-600);
- height: 34px;
- padding: 5px 9px;
- }
-
- &:hover {
- background-color: var(--blue-500, $blue-500);
- border: 2px solid var(--blue-500, $blue-500);
- color: var(--blue-600, $blue-600);
- height: 34px;
- padding: 5px 9px;
- }
- }
-}
diff --git a/app/assets/stylesheets/page_bundles/jira_connect.scss b/app/assets/stylesheets/page_bundles/jira_connect.scss
index 81e9b04b18e..231723ca4e3 100644
--- a/app/assets/stylesheets/page_bundles/jira_connect.scss
+++ b/app/assets/stylesheets/page_bundles/jira_connect.scss
@@ -1,15 +1,24 @@
@import 'mixins_and_variables_and_functions';
-/**
-NOTE: We should only import styles that we actually use.
-Ex:
- @import '@gitlab/ui/src/scss/gitlab_ui';
-*/
+
@import '@gitlab/ui/src/scss/bootstrap';
@import 'bootstrap-vue/src/index';
@import '@gitlab/ui/src/scss/utilities';
@import '@gitlab/ui/src/components/base/alert/alert';
+// We should only import styles that we actually use.
+@import '@gitlab/ui/src/components/base/alert/alert';
+@import '@gitlab/ui/src/components/base/avatar/avatar';
+@import '@gitlab/ui/src/components/base/badge/badge';
+@import '@gitlab/ui/src/components/base/button/button';
+@import '@gitlab/ui/src/components/base/icon/icon';
+@import '@gitlab/ui/src/components/base/link/link';
+@import '@gitlab/ui/src/components/base/loading_icon/loading_icon';
+@import '@gitlab/ui/src/components/base/modal/modal';
+@import '@gitlab/ui/src/components/base/pagination/pagination';
+@import '@gitlab/ui/src/components/base/tabs/tabs/tabs';
+@import '@gitlab/ui/src/components/base/tooltip/tooltip';
+
$atlaskit-border-color: #dfe1e6;
.ac-content {
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 2ab3bdcc474..1b190024457 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -113,8 +113,6 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
}
.item-label {
- @include gl-py-4;
- @include gl-pl-4;
border-right: $border-style;
border-bottom: $border-style;
}
diff --git a/app/graphql/types/notes/note_type.rb b/app/graphql/types/notes/note_type.rb
index f4e05e19eca..84b61340e93 100644
--- a/app/graphql/types/notes/note_type.rb
+++ b/app/graphql/types/notes/note_type.rb
@@ -46,6 +46,13 @@ module Types
field :confidential, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if this note is confidential',
method: :confidential?
+ field :url, GraphQL::STRING_TYPE,
+ null: true,
+ description: 'URL to view this Note in the Web UI'
+
+ def url
+ ::Gitlab::UrlBuilder.build(object)
+ end
def system_note_icon_name
SystemNoteHelper.system_note_icon_name(object) if object.system?
diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb
index c30eb1b007a..f1527b9b85a 100644
--- a/app/helpers/jira_connect_helper.rb
+++ b/app/helpers/jira_connect_helper.rb
@@ -4,4 +4,10 @@ module JiraConnectHelper
def new_jira_connect_ui?
Feature.enabled?(:new_jira_connect_ui, type: :development, default_enabled: :yaml)
end
+
+ def jira_connect_app_data
+ {
+ groups_path: api_v4_groups_path(params: { min_access_level: Gitlab::Access::MAINTAINER })
+ }
+ end
end
diff --git a/app/services/jira_connect/sync_service.rb b/app/services/jira_connect/sync_service.rb
index b2af284f1f0..bddc7cbe5a0 100644
--- a/app/services/jira_connect/sync_service.rb
+++ b/app/services/jira_connect/sync_service.rb
@@ -31,7 +31,7 @@ module JiraConnect
jira_response: response&.to_json
}
- if response && (response['errorMessages'] || response['rejectedBuilds'].present?)
+ if response && response['errorMessages'].present?
logger.error(message)
else
logger.info(message)
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index 53872c67f49..25d46ada885 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -37,16 +37,11 @@ module Projects
raise InvalidStateError, 'missing pages artifacts' unless build.artifacts?
raise InvalidStateError, 'build SHA is outdated for this ref' unless latest?
- # Create temporary directory in which we will extract the artifacts
- make_secure_tmp_dir(tmp_path) do |archive_path|
- extract_archive!(archive_path)
+ build.artifacts_file.use_file do |artifacts_path|
+ deploy_to_legacy_storage(artifacts_path)
- # Check if we did extract public directory
- archive_public_path = File.join(archive_path, PUBLIC_DIR)
- raise InvalidStateError, 'pages miss the public folder' unless Dir.exist?(archive_public_path)
- raise InvalidStateError, 'build SHA is outdated for this ref' unless latest?
+ create_pages_deployment(artifacts_path, build)
- deploy_page!(archive_public_path)
success
end
rescue InvalidStateError => e
@@ -84,15 +79,29 @@ module Projects
)
end
- def extract_archive!(temp_path)
+ def deploy_to_legacy_storage(artifacts_path)
+ # Create temporary directory in which we will extract the artifacts
+ make_secure_tmp_dir(tmp_path) do |tmp_path|
+ extract_archive!(artifacts_path, tmp_path)
+
+ # Check if we did extract public directory
+ archive_public_path = File.join(tmp_path, PUBLIC_DIR)
+ raise InvalidStateError, 'pages miss the public folder' unless Dir.exist?(archive_public_path)
+ raise InvalidStateError, 'build SHA is outdated for this ref' unless latest?
+
+ deploy_page!(archive_public_path)
+ end
+ end
+
+ def extract_archive!(artifacts_path, temp_path)
if artifacts.ends_with?('.zip')
- extract_zip_archive!(temp_path)
+ extract_zip_archive!(artifacts_path, temp_path)
else
raise InvalidStateError, 'unsupported artifacts format'
end
end
- def extract_zip_archive!(temp_path)
+ def extract_zip_archive!(artifacts_path, temp_path)
raise InvalidStateError, 'missing artifacts metadata' unless build.artifacts_metadata?
# Calculate page size after extract
@@ -102,11 +111,8 @@ module Projects
raise InvalidStateError, "artifacts for pages are too large: #{public_entry.total_size}"
end
- build.artifacts_file.use_file do |artifacts_path|
- SafeZip::Extract.new(artifacts_path)
- .extract(directories: [PUBLIC_DIR], to: temp_path)
- create_pages_deployment(artifacts_path, build)
- end
+ SafeZip::Extract.new(artifacts_path)
+ .extract(directories: [PUBLIC_DIR], to: temp_path)
rescue SafeZip::Extract::Error => e
raise FailedToExtractError, e.message
end
@@ -150,6 +156,9 @@ module Projects
deployment = project.pages_deployments.create!(file: file,
file_count: entries_count,
file_sha256: sha256)
+
+ raise InvalidStateError, 'build SHA is outdated for this ref' unless latest?
+
project.update_pages_deployment!(deployment)
end
diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml
index 30dad19be37..ed765f80b74 100644
--- a/app/views/jira_connect/subscriptions/index.html.haml
+++ b/app/views/jira_connect/subscriptions/index.html.haml
@@ -20,7 +20,7 @@
.gl-mt-5
%p Note: this integration only works with accounts on GitLab.com (SaaS).
- else
- .js-jira-connect-app
+ .js-jira-connect-app{ data: jira_connect_app_data }
- unless new_jira_connect_ui?
%form#add-subscription-form.subscription-form{ action: jira_connect_subscriptions_path }
diff --git a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
index 64e65958113..7c86b194574 100644
--- a/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
+++ b/app/workers/container_expiration_policies/cleanup_container_repository_worker.rb
@@ -109,6 +109,13 @@ module ContainerExpirationPolicies
log_extra_metadata_on_done(field, value)
end
+
+ before_truncate_size = result.payload[:cleanup_tags_service_before_truncate_size]
+ after_truncate_size = result.payload[:cleanup_tags_service_after_truncate_size]
+ truncated = before_truncate_size &&
+ after_truncate_size &&
+ before_truncate_size != after_truncate_size
+ log_extra_metadata_on_done(:cleanup_tags_service_truncated, !!truncated)
end
end
end