Commit 64fc6a9b authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@13-6-stable-ee

parent 6317794d
......@@ -56,7 +56,7 @@ export default {
<template>
<metric-card
:title="__('Instance Statistics')"
:title="__('Usage Trends')"
:metrics="counts"
:is-loading="$apollo.queries.counts.loading"
class="gl-mt-4"
......
......@@ -42,7 +42,7 @@ export function membersBeforeSave(members) {
title: sanitize(title),
search: sanitize(`${member.username} ${member.name}`),
icon: avatarIcon,
availability: member.availability,
availability: member?.availability,
};
});
}
......
import initFilteredSearch from '~/pages/search/init_filtered_search';
import AdminRunnersFilteredSearchTokenKeys from '~/filtered_search/admin_runners_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
useDefaultState: true,
document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: AdminRunnersFilteredSearchTokenKeys,
useDefaultState: true,
});
});
initInstallRunner();
import ClustersBundle from '~/clusters/clusters_bundle';
new ClustersBundle(); // eslint-disable-line no-new
document.addEventListener('DOMContentLoaded', () => {
new ClustersBundle(); // eslint-disable-line no-new
});
import ClustersBundle from '~/clusters/clusters_bundle';
new ClustersBundle(); // eslint-disable-line no-new
document.addEventListener('DOMContentLoaded', () => {
new ClustersBundle(); // eslint-disable-line no-new
});
import initCreateCluster from '~/create_cluster/init_create_cluster';
import initIntegrationForm from '~/clusters/forms/show/index';
initCreateCluster(document, gon);
initIntegrationForm();
document.addEventListener('DOMContentLoaded', () => {
initCreateCluster(document, gon);
initIntegrationForm();
});
import PersistentUserCallout from '~/persistent_user_callout';
import initClustersListApp from '~/clusters_list';
const callout = document.querySelector('.gcp-signup-offer');
PersistentUserCallout.factory(callout);
initClustersListApp();
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.gcp-signup-offer');
PersistentUserCallout.factory(callout);
initClustersListApp();
});
import initNewCluster from '~/clusters/new_cluster';
initNewCluster();
document.addEventListener('DOMContentLoaded', () => {
initNewCluster();
});
import ClustersBundle from '~/clusters/clusters_bundle';
import initClusterHealth from '~/pages/projects/clusters/show/cluster_health';
new ClustersBundle(); // eslint-disable-line no-new
initClusterHealth();
document.addEventListener('DOMContentLoaded', () => {
new ClustersBundle(); // eslint-disable-line no-new
initClusterHealth();
});
......@@ -4,18 +4,18 @@ import initFilteredSearch from '~/pages/search/init_filtered_search';
import GroupRunnersFilteredSearchTokenKeys from '~/filtered_search/group_runners_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import initSharedRunnersForm from '~/group_settings/mount_shared_runners';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
// Initialize expandable settings panels
initSettingsPanels();
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys,
anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR,
useDefaultState: false,
});
initFilteredSearch({
page: FILTERED_SEARCH.ADMIN_RUNNERS,
filteredSearchTokenKeys: GroupRunnersFilteredSearchTokenKeys,
anchor: FILTERED_SEARCH.GROUP_RUNNERS_ANCHOR,
useDefaultState: false,
});
initSharedRunnersForm();
initVariableList();
initInstallRunner();
initSharedRunnersForm();
initVariableList();
});
......@@ -4,32 +4,32 @@ import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
import initVariableList from '~/ci_variable_list';
import initDeployFreeze from '~/deploy_freeze';
import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
// Initialize expandable settings panels
initSettingsPanels();
document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
const runnerToken = document.querySelector('.js-secret-runner-token');
if (runnerToken) {
const runnerTokenSecretValue = new SecretValues({
container: runnerToken,
});
runnerTokenSecretValue.init();
}
const runnerToken = document.querySelector('.js-secret-runner-token');
if (runnerToken) {
const runnerTokenSecretValue = new SecretValues({
container: runnerToken,
});
runnerTokenSecretValue.init();
}
initVariableList();
initVariableList();
// 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 { target } = event;
if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none';
autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked);
});
// 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 { target } = event;
if (instanceDefaultBadge) instanceDefaultBadge.style.display = 'none';
autoDevOpsExtraSettings.classList.toggle('hidden', !target.checked);
});
registrySettingsApp();
initDeployFreeze();
registrySettingsApp();
initDeployFreeze();
initSettingsPipelinesTriggers();
initInstallRunner();
initSettingsPipelinesTriggers();
});
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import InstallRunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
Vue.use(VueApollo);
export function initInstallRunner(componentId = 'js-install-runner') {
const installRunnerEl = document.getElementById(componentId);
const { projectPath, groupPath } = installRunnerEl?.dataset;
if (installRunnerEl) {
const defaultClient = createDefaultClient();
const apolloProvider = new VueApollo({
defaultClient,
});
// eslint-disable-next-line no-new
new Vue({
el: installRunnerEl,
apolloProvider,
provide: {
projectPath,
groupPath,
},
render(createElement) {
return createElement(InstallRunnerInstructions);
},
});
}
}
query getRunnerPlatforms($projectPath: ID!, $groupPath: ID!) {
runnerPlatforms {
nodes {
name
humanReadableName
architectures {
nodes {
name
downloadLocation
}
}
}
}
project(fullPath: $projectPath) {
id
}
group(fullPath: $groupPath) {
id
}
}
query runnerSetupInstructions(
$platform: String!
$architecture: String!
$projectId: ID!
$groupId: ID!
) {
runnerSetup(
platform: $platform
architecture: $architecture
projectId: $projectId
groupId: $groupId
) {
installInstructions
registerInstructions
}
}
<script>
import {
GlAlert,
GlButton,
GlModal,
GlModalDirective,
GlButtonGroup,
GlDropdown,
GlDropdownItem,
GlIcon,
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
import getRunnerPlatforms from './graphql/queries/get_runner_platforms.query.graphql';
import getRunnerSetupInstructions from './graphql/queries/get_runner_setup.query.graphql';
export default {
components: {
GlAlert,
GlButton,
GlButtonGroup,
GlDropdown,
GlDropdownItem,
GlModal,
GlIcon,
},
directives: {
GlModalDirective,
},
inject: {
projectPath: {
default: '',
},
groupPath: {
default: '',
},
},
apollo: {
runnerPlatforms: {
query: getRunnerPlatforms,
variables() {
return {
projectPath: this.projectPath,
groupPath: this.groupPath,
};
},
update(data) {
return data;
},
error() {
this.showAlert = true;
},
},
},
data() {
return {
showAlert: false,
selectedPlatformArchitectures: [],
selectedPlatform: {},
selectedArchitecture: {},
runnerPlatforms: {},
instructions: {},
};
},
computed: {
isPlatformSelected() {
return Object.keys(this.selectedPlatform).length > 0;
},
instructionsEmpty() {
return this.instructions && Object.keys(this.instructions).length === 0;
},
groupId() {
return this.runnerPlatforms?.group?.id ?? '';
},
projectId() {
return this.runnerPlatforms?.project?.id ?? '';
},
platforms() {
return this.runnerPlatforms.runnerPlatforms?.nodes;
},
},
methods: {
selectPlatform(name) {
this.selectedPlatform = this.platforms.find(platform => platform.name === name);
this.selectedPlatformArchitectures = this.selectedPlatform?.architectures?.nodes;
[this.selectedArchitecture] = this.selectedPlatformArchitectures;
this.selectArchitecture(this.selectedArchitecture);
},
selectArchitecture(architecture) {
this.selectedArchitecture = architecture;
this.$apollo.addSmartQuery('instructions', {
variables() {
return {
platform: this.selectedPlatform.name,
architecture: this.selectedArchitecture.name,
projectId: this.projectId,
groupId: this.groupId,
};
},
query: getRunnerSetupInstructions,
update(data) {
return data?.runnerSetup;
},
error() {
this.showAlert = true;
},
});
},
toggleAlert(state) {
this.showAlert = state;
},
},
modalId: 'installation-instructions-modal',
i18n: {
installARunner: __('Install a Runner'),
architecture: s__('Runners|Architecture'),
downloadInstallBinary: s__('Runners|Download and Install Binary'),
downloadLatestBinary: s__('Runners|Download Latest Binary'),
registerRunner: s__('Runners|Register Runner'),
method: __('Method'),
fetchError: s__('An error has occurred fetching instructions'),
instructions: __('Show Runner installation instructions'),
},
closeButton: {
text: __('Close'),
attributes: [{ variant: 'default' }],
},
};
</script>
<template>
<div>
<gl-button v-gl-modal-directive="$options.modalId" data-testid="show-modal-button">
{{ $options.i18n.instructions }}
</gl-button>
<gl-modal
:modal-id="$options.modalId"
:title="$options.i18n.installARunner"
:action-secondary="$options.closeButton"
>
<gl-alert v-if="showAlert" variant="danger" @dismiss="toggleAlert(false)">
{{ $options.i18n.fetchError }}
</gl-alert>
<h5>{{ __('Environment') }}</h5>
<gl-button-group class="gl-mb-5">
<gl-button
v-for="platform in platforms"
:key="platform.name"
data-testid="platform-button"
@click="selectPlatform(platform.name)"
>
{{ platform.humanReadableName }}
</gl-button>
</gl-button-group>
<template v-if="isPlatformSelected">
<h5>
{{ $options.i18n.architecture }}
</h5>
<gl-dropdown class="gl-mb-5" :text="selectedArchitecture.name">
<gl-dropdown-item
v-for="architecture in selectedPlatformArchitectures"
:key="architecture.name"
data-testid="architecture-dropdown-item"
@click="selectArchitecture(architecture)"
>
{{ architecture.name }}
</gl-dropdown-item>
</gl-dropdown>
<div class="gl-display-flex gl-align-items-center gl-mb-5">
<h5>{{ $options.i18n.downloadInstallBinary }}</h5>
<gl-button
class="gl-ml-auto"
:href="selectedArchitecture.downloadLocation"
download
data-testid="binary-download-button"
>
{{ $options.i18n.downloadLatestBinary }}
</gl-button>
</div>
</template>
<template v-if="!instructionsEmpty">
<div class="gl-display-flex">
<pre
class="bg-light gl-flex-fill-1 gl-white-space-pre-line"
data-testid="binary-instructions"
>
{{ instructions.installInstructions }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.installationInstructions"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
</div>
<hr />
<h5 class="gl-mb-5">{{ $options.i18n.registerRunner }}</h5>
<h5 class="gl-mb-5">{{ $options.i18n.method }}</h5>
<div class="gl-display-flex">
<pre
class="bg-light gl-flex-fill-1 gl-white-space-pre-line"
data-testid="runner-instructions"
>
{{ instructions.registerInstructions }}
</pre>
<gl-button
class="gl-align-self-start gl-ml-2 gl-mt-2"
category="tertiary"
variant="link"
:data-clipboard-text="instructions.registerInstructions"
>
<gl-icon name="copy-to-clipboard" />
</gl-button>
</div>
</template>
</gl-modal>
</div>
</template>
......@@ -12,7 +12,7 @@ module Ci
end
def execute
return [] unless Ability.allowed?(@current_user, :read_pipeline, @project)
return {} unless Ability.allowed?(@current_user, :read_pipeline, @project)
commit_statuses
end
......
......@@ -27,7 +27,11 @@ module Projects::AlertManagementHelper
private
def alert_management_enabled?(project)
!!(project.alerts_service_activated? || project.prometheus_service_active?)
!!(
project.alerts_service_activated? ||
project.prometheus_service_active? ||
AlertManagement::HttpIntegrationsFinder.new(project, active: true).execute.any?
)
end
end
......
......@@ -46,8 +46,9 @@ module Users
username: user.username,
name: user.name,
avatar_url: user.avatar_url,
availability: user&.status&.availability
availability: nil
}
# Return nil for availability for now due to https://gitlab.com/gitlab-org/gitlab/-/issues/285442
end
def group_as_hash(group, group_counts)
......
......@@ -7,7 +7,7 @@ module Packages
::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(current_user.id, redis_event_name)
end
if Feature.enabled?(:collect_package_events)
if Feature.enabled?(:collect_package_events) && Gitlab::Database.read_write?
::Packages::Event.create!(
event_type: event_name,
originator: current_user&.id,
......
......@@ -71,7 +71,10 @@ module Projects
Project.transaction do
project.expire_caches_before_rename(@old_path)
# Apply changes to the project
update_namespace_and_visibility(@new_namespace)
update_shared_runners_settings
project.save!
# Notifications
project.send_move_instructions(@old_path)
......@@ -84,10 +87,6 @@ module Projects
# Move uploads
move_project_uploads(project)
# If a project is being transferred to another group it means it can already
# have shared runners enabled but we need to check whether the new group allows that.
project.shared_runners_enabled = false if project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
project.old_path_with_namespace = @old_path
update_repository_configuration(@new_path)
......@@ -120,7 +119,6 @@ module Projects
# Apply new namespace id and visibility level
project.namespace = to_namespace
project.visibility_level = to_namespace.visibility_level unless project.visibility_level_allowed_by_group?
project.save!
end
def update_repository_configuration(full_path)
......@@ -208,6 +206,14 @@ module Projects
def new_design_repo_path
"#{new_path}#{::Gitlab::GlRepository::DESIGN.path_suffix}"
end
def update_shared_runners_settings
# If a project is being transferred to another group it means it can already
# have shared runners enabled but we need to check whether the new group allows that.
if project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
project.shared_runners_enabled = false
end
end
end
end
......
- breadcrumb_title _("Instance Statistics")
- page_title _("Instance Statistics")
- breadcrumb_title _("Usage Trends")
- page_title _("Usage Trends")
#js-instance-statistics-app
......@@ -39,9 +39,7 @@
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
type: 'shared',
reset_token_url: reset_registration_token_admin_application_settings_path,
project_path: '',
group_path: '' }
reset_token_url: reset_registration_token_admin_application_settings_path }
.row
.col-sm-9
......
......@@ -19,5 +19,3 @@
data: { confirm: _("Are you sure you want to reset registration token?") }
%li
= _("Start the Runner!")
#js-install-runner{ data: { project_path: project_path, group_path: group_path } }
......@@ -17,6 +17,4 @@
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @group.runners_token,
type: 'group',
reset_token_url: reset_registration_token_group_settings_ci_cd_path,
project_path: '',
group_path: @group.path }
reset_token_url: reset_registration_token_group_settings_ci_cd_path }
......@@ -71,9 +71,9 @@
= _('Cohorts')
- if Feature.enabled?(:instance_statistics, default_enabled: true)
= nav_link(controller: :instance_statistics) do
= link_to admin_instance_statistics_path, title: _('Instance Statistics') do
= link_to admin_instance_statistics_path, title: _('Usage Trends') do
%span
= _('Instance Statistics')
= _('Usage Trends')
= nav_link(controller: admin_monitoring_nav_links) do
= link_to admin_system_info_path, data: { qa_selector: 'admin_monitoring_link' } do
......
......@@ -9,9 +9,7 @@
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @project.runners_token,
type: 'specific',
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path,
project_path: @project.path_with_namespace,
group_path: '' }
reset_token_url: reset_registration_token_namespace_project_settings_ci_cd_path }
- if @project_runners.any?
%h4.underlined-title= _('Runners activated for this project')
......
......@@ -2,6 +2,6 @@
.project-item-select-holder.btn-group.gl-ml-auto.gl-mr-auto.gl-py-3.gl-relative.gl-display-flex.gl-overflow-hidden
%a.btn.gl-button.btn-success.new-project-item-link.block-truncated.qa-new-project-item-link{ href: '', data: { label: local_assigns[:label], type: local_assigns[:type] }, class: "gl-m-0!" }
= loading_icon(color: 'light')
= project_select_tag :project_path, class: "project-item-select gl-absolute gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
= project_select_tag :project_path, class: "project-item-select gl-absolute! gl-visibility-hidden", data: { include_groups: local_assigns[:include_groups], order_by: 'last_activity_at', relative_path: local_assigns[:path], with_shared: local_assigns[:with_shared], include_projects_in_subgroups: local_assigns[:include_projects_in_subgroups] }, with_feature_enabled: local_assigns[:with_feature_enabled]
%button.btn.dropdown-toggle.btn-success.btn-md.gl-button.gl-dropdown-toggle.dropdown-toggle-split.new-project-item-select-button.qa-new-project-item-select-button.gl-p-0.gl-w-100{ class: "gl-m-0!", 'aria-label': _('Toggle project select') }
= sprite_icon('chevron-down')
---
title: Fix project select split button bug
merge_request: 48065
author:
type: fixed
---
title: Re-name Instance Statistics as Usage Trends
merge_request: 48183
author:
type: changed
---
title: Does not track package events on a read-only instance
merge_request: 48257
author:
type: fixed
---
title: Fix project transfer corrupting shared runners state
merge_request: 48032
author:
type: fixed
---
title: Fix tags pages erroring for projects with private pipelines
merge_request: 48184
author:
type: fixed
---
title: Ensure Alerts list loads when only HTTP integrations are enabled
merge_request: 48247
author:
type: fixed
......@@ -13,5 +13,5 @@ Administrators have access to instance-wide analytics, as shown in **Admin Area
There are several kinds of statistics:
- [DevOps Report](dev_ops_report.md): Provides an overview of your entire instance's feature usage. **(CORE)**
- [Instance Statistics](instance_statistics.md): Shows how much data your instance contains, and how that is changing. **(CORE)**
- [Usage Trends](usage_trends.md): Shows how much data your instance contains, and how that is changing. **(CORE)**
- [User Cohorts](user_cohorts.md): Display the monthly cohorts of new users and their activities over time. **(CORE)**
......@@ -4,23 +4,24 @@ group: Value Stream Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Instance Statistics **(CORE)**
# Usage Trends **(CORE)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235754) in GitLab 13.5 behind a feature flag, disabled by default.
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46962) in GitLab 13.6.
> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/285220) from Instance Statistics to Usage Trends in GitLab 13.6.
> - It's enabled on GitLab.com.
> - It's recommended for production use.
CAUTION: **Warning:**
This feature might not be available to you. Check the **version history** note above for details.
Instance Statistics gives you an overview of how much data your instance contains, and how quickly this volume is changing over time.
Usage Trends gives you an overview of how much data your instance contains, and how quickly this volume is changing over time.
To see Instance Statistics, go to **Admin Area > Analytics > Instance Statistics**.
To see Usage Trends, go to **Admin Area > Analytics > Usage Trends**.
## Total counts
At the top of the page, Instance Statistics shows total counts for:
At the top of the page, Usage Trends shows total counts for:
- Users
- Projects
......@@ -33,16 +34,16 @@ These figures can be useful for understanding how much data your instance contai
## Past year trend charts
Instance Statistics also displays line charts that show total counts per month, over the past 12 months,
Usage Trends also displays line charts that show total counts per month, over the past 12 months,
in the categories shown in [Total counts](#total-counts).
These charts help you visualize how rapidly these records are being created on your instance.
![Instance Activity Pipelines chart](img/instance_activity_pipelines_chart_v13_6.png)
### Enable or disable Instance Statistics
### Enable or disable Usage Trends
In GitLab version 13.5 only, Instance Statistics was under development and not ready for production use.
In GitLab version 13.5 only, Usage Trends was under development and not ready for production use.
It was deployed behind a feature flag that was **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can opt to enable it.
......
......@@ -128,7 +128,7 @@ always take the latest Secret Detection artifact available.
### Post-processing
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/46390) in GitLab 13.6.
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
Upon detection of a secret, GitLab supports post processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
......
......@@ -366,7 +366,7 @@ status.
## Actions you can take on an issue board
- [Create a new list](#create-a-new-list).
- [Delete an existing list](#delete-a-list).
- [Remove an existing list](#remove-a-list).
- [Add issues to a list](#add-issues-to-a-list).
- [Remove an issue from a list](#remove-an-issue-from-a-list).
- [Filter issues](#filter-issues) that appear across your issue board.
......@@ -407,23 +407,36 @@ choosing **Create project label** or **Create group label**.
This creates the label immediately and adds it to the dropdown.
You can now choose it to create a list.
### Delete a list
### Remove a list
To delete a list from the issue board, use the small trash icon present
in the list's heading. A confirmation dialog appears for you to confirm.
Deleting a list doesn't have any effect on issues and labels, as it's just the
Removing a list doesn't have any effect on issues and labels, as it's just the
list view that's removed. You can always restore it later if you need.
To remove a list from an issue board:
1. Select the **List settings** icon (**{settings}**) on the top of the list you want to remove. The
list settings sidebar opens on the right.
1. Select **Remove list**. A confirmation dialog appears.
1. Select **OK**.
### Add issues to a list
You can add issues to a list by clicking the **Add issues** button
You can add issues to a list in a project issue board by clicking the **Add issues** button
in the top right corner of the issue board. This opens up a modal
window where you can see all the issues that do not belong to any list.
Select one or more issues by clicking the cards and then click **Add issues**
to add them to the selected list. You can limit the issues you want to add to
the list by filtering by author, assignee, milestone, and label.
the list by filtering by the following:
- Assignee
- Author
- Epic
- Label
- Milestone
- My Reaction
- Release
- Weight
![Bulk adding issues to lists](img/issue_boards_add_issues_modal_v13_6.png)
......@@ -441,7 +454,16 @@ You should be able to use the filters on top of your issue board to show only
the results you want. It's similar to the filtering used in the issue tracker,
as the metadata from the issues and labels is re-used in the issue board.
You can filter by author, assignee, milestone, and label.
You can filter by the following:
- Assignee
- Author
- Epic
- Label
- Milestone
- My Reaction
- Release
- Weight
### Create workflows
......
......@@ -66,10 +66,12 @@ easily [edit your content](#edit-content).
1. To get started, create a new project from the [Static Site Editor - Middleman](https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman)
template. You can either [fork it](../repository/forking_workflow.md#creating-a-fork)
or [create a new project from a template](../../../gitlab-basics/create-project.md#built-in-templates).
1. Edit the [`data/config.yml`](#configuration-files) configuration file
1. Edit the [`data/config.yml`](#static-site-generator-configuration) configuration file
to replace `<username>` and `<project-name>` with the proper values for
your project's path. This triggers a CI/CD pipeline to deploy your project
with GitLab Pages.
your project's path.
1. (Optional) Edit the [`.gitlab/static-site-editor.yml`](#static-site-editor-configuration-file) file
to customize the behavior of the Static Site Editor.
1. When you submit your changes, GitLab triggers a CI/CD pipeline to deploy your project with GitLab Pages.
1. When the pipeline finishes, from your project's left-side menu, go to **Settings > Pages** to find the URL of your new website.
1. Visit your website and look at the bottom-left corner of the screen to see the new **Edit this page** button.
......@@ -179,6 +181,41 @@ yet. You can do so by editing the file locally, through the GitLab regular file
## Configuration files
You can customize the behavior of a project which uses the Static Site Editor with
the following configuration files:
- The [`.gitlab/static-site-editor.yml`](#static-site-editor-configuration-file), which customizes the
behavior of the Static Site Editor.
- [Static Site Generator configuration files](#static-site-generator-configuration),
such as `data/config.yml`, which configures the Static Site Generator itself.
It also controls the **Edit this page** button when the site is generated.
### Static Site Editor configuration file
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4267) in GitLab 13.6.
The `.gitlab/static-site-editor.yml` configuration file contains entries you can
use to customize behavior of the Static Site Editor (SSE). If the file does not exist,
default values which support a default Middleman project configuration are used.
The [Static Site Editor - Middleman](https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman) project template generates a file pre-populated with these defaults.
To customize the behavior of the SSE, edit `.gitlab/static-site-editor.yml`'s entries
(described in the table below) according to what works best for your project (respecting YAML syntax).
After the table, see an [example of the SSE configuration file](#gitlabstatic-site-editoryml-example).
| Entry | GitLab version | Type | Default value | Description |
|---|---|---|---|---|
| `image_upload_path` | [13.6](https://gitlab.com/gitlab-org/gitlab/-/issues/216641) | String | `source/images` | Directory for images uploaded from the WYSIWYG editor. |
#### `.gitlab/static-site-editor.yml` example
```yaml
image_upload_path: 'source/images' # Relative path to the project's root. Don't include leading or trailing slashes.
```
### Static Site Generator configuration
The Static Site Editor uses Middleman's configuration file, `data/config.yml`
to customize the behavior of the project itself and to control the **Edit this
page** button, rendered through the file [`layout.erb`](https://gitlab.com/gitlab-org/project-templates/static-site-editor-middleman/-/blob/master/source/layouts/layout.erb).
......
......@@ -2943,9 +2943,6 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
msgid "An error has occurred fetching instructions"
msgstr ""
msgid "An error occured while making the changes: %{error}"
msgstr ""
......@@ -14493,9 +14490,6 @@ msgstr ""
msgid "Install Runner on Kubernetes"
msgstr ""
msgid "Install a Runner"
msgstr ""
msgid "Install a soft token authenticator like %{free_otp_link} or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the %{help_link_start}documentation%{help_link_end}."
msgstr ""
......@@ -14519,9 +14513,6 @@ msgstr[1] ""
msgid "Instance Configuration"
msgstr ""
msgid "Instance Statistics"
msgstr ""
msgid "Instance administrators group already exists"
msgstr ""
......@@ -23426,12 +23417,6 @@ msgstr ""
msgid "Runners|Description"
msgstr ""
msgid "Runners|Download Latest Binary"
msgstr ""
msgid "Runners|Download and Install Binary"
msgstr ""
msgid "Runners|Group"
msgstr ""
......@@ -23459,9 +23444,6 @@ msgstr ""
msgid "Runners|Protected"
msgstr ""
msgid "Runners|Register Runner"
msgstr ""
msgid "Runners|Revision"
msgstr ""
......@@ -24869,9 +24851,6 @@ msgstr ""
msgid "Should you ever lose your phone or access to your one time password secret, each of these recovery codes can be used one time each to regain access to your account. Please save them in a safe place, or you %{b_start}will%{b_end} lose access to your account."
msgstr ""
msgid "Show Runner installation instructions"
msgstr ""
msgid "Show all activity"
msgstr ""
......@@ -29270,6 +29249,9 @@ msgstr ""
msgid "Usage"
msgstr ""
msgid "Usage Trends"
msgstr ""
msgid "Usage ping is off"
msgstr ""
......
......@@ -18,7 +18,7 @@ RSpec.describe 'Alert Management index', :js do
wait_for_requests
end
context 'when a developer displays the alert list and the alert service is not enabled' do
context 'when a developer displays the alert list and alert integrations are not enabled' do
it 'shows the alert page title' do
expect(page).to have_content('Alerts')
end
......@@ -38,8 +38,8 @@ RSpec.describe 'Alert Management index', :js do
end
end
context 'when a developer displays the alert list and the alert service is enabled' do
let_it_be(:alerts_service) { create(:alerts_service, project: project) }
context 'when a developer displays the alert list and an HTTP integration is enabled' do
let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
it 'shows the alert page title' do
expect(page).to have_content('Alerts')
......
......@@ -152,8 +152,8 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it 'returns nil' do
expect(subject).to be_empty
it 'returns a blank hash' do
expect(subject).to eq({})
end
end
......@@ -170,8 +170,8 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
status: :running)
end
it 'returns nil' do
expect(private_subject).to be_empty
it 'returns a blank hash' do
expect(private_subject).to eq({})
end
end
end
......
export const mockGraphqlRunnerPlatforms = {
data: {
runnerPlatforms: {
nodes: [
{
name: 'linux',
humanReadableName: 'Linux',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64',
__typename: 'RunnerArchitecture',
},
{
name: '386',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386',
__typename: 'RunnerArchitecture',
},
{
name: 'arm',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm',
__typename: 'RunnerArchitecture',
},
{
name: 'arm64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-arm64',
__typename: 'RunnerArchitecture',
},
],
__typename: 'RunnerArchitectureConnection',
},
__typename: 'RunnerPlatform',
},
{
name: 'osx',
humanReadableName: 'macOS',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64',
__typename: 'RunnerArchitecture',
},
],
__typename: 'RunnerArchitectureConnection',
},
__typename: 'RunnerPlatform',
},
{
name: 'windows',
humanReadableName: 'Windows',
architectures: {
nodes: [
{
name: 'amd64',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-amd64.exe',
__typename: 'RunnerArchitecture',
},
{
name: '386',
downloadLocation:
'https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-windows-386.exe',
__typename: 'RunnerArchitecture',
},
],
__typename: 'RunnerArchitectureConnection',
},
__typename: 'RunnerPlatform',
},
{
name: 'docker',
humanReadableName: 'Docker',
architectures: null,
__typename: 'RunnerPlatform',
},
{
name: 'kubernetes',
humanReadableName: 'Kubernetes',
architectures: null,
__typename: 'RunnerPlatform',
},
],
__typename: 'RunnerPlatformConnection',
},
project: { id: 'gid://gitlab/Project/1', __typename: 'Project' },
group: null,
},
};
export const mockGraphqlInstructions = {
data: {
runnerSetup: {
installInstructions:
"# Download the binary for your system\nsudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64\n\n# Give it permissions to execute\nsudo chmod +x /usr/local/bin/gitlab-runner\n\n# Create a GitLab CI user\nsudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash\n\n# Install and run as service\nsudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner\nsudo gitlab-runner start\n",
registerInstructions:
'sudo gitlab-runner register --url http://192.168.1.81:3000/ --registration-token GE5gsjeep_HAtBf9s3Yz',
__typename: 'RunnerSetup',
},
},
};
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'jest/helpers/mock_apollo_helper';
import RunnerInstructions from '~/vue_shared/components/runner_instructions/runner_instructions.vue';
import getRunnerPlatforms from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql';
import getRunnerSetupInstructions from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql';
import { mockGraphqlRunnerPlatforms, mockGraphqlInstructions } from './mock_data';
const projectPath = 'gitlab-org/gitlab';
const localVue = createLocalVue();
localVue.use(VueApollo);
describe('RunnerInstructions component', () => {
let wrapper;
let fakeApollo;
const findModalButton = () => wrapper.find('[data-testid="show-modal-button"]');
const findPlatformButtons = () => wrapper.findAll('[data-testid="platform-button"]');
const findArchitectureDropdownItems = () =>
wrapper.findAll('[data-testid="architecture-dropdown-item"]');
const findBinaryInstructionsSection = () => wrapper.find('[data-testid="binary-instructions"]');
const findRunnerInstructionsSection = () => wrapper.find('[data-testid="runner-instructions"]');
beforeEach(() => {
const requestHandlers = [
[getRunnerPlatforms, jest.fn().mockResolvedValue(mockGraphqlRunnerPlatforms)],
[getRunnerSetupInstructions, jest.fn().mockResolvedValue(mockGraphqlInstructions)],
];
fakeApollo = createMockApollo(requestHandlers);
wrapper = shallowMount(RunnerInstructions, {
provide: {
projectPath,
},
localVue,
apolloProvider: fakeApollo,
});
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('should show the "Show Runner installation instructions" button', () => {
const button = findModalButton();
expect(button.exists()).toBe(true);
expect(button.text()).toBe('Show Runner installation instructions');
});
it('should contain a number of platforms buttons', () => {
const buttons = findPlatformButtons();
expect(buttons).toHaveLength(mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes.length);
});
it('should contain a number of dropdown items for the architecture options', () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
return wrapper.vm.$nextTick(() => {
const dropdownItems = findArchitectureDropdownItems();
expect(dropdownItems).toHaveLength(
mockGraphqlRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes.length,
);
});
});
it('should display the binary installation instructions for a selected architecture', async () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
await wrapper.vm.$nextTick();
const dropdownItem = findArchitectureDropdownItems().at(0);
dropdownItem.vm.$emit('click');
await wrapper.vm.$nextTick();
const runner = findBinaryInstructionsSection();
expect(runner.text()).toEqual(
expect.stringContaining('sudo chmod +x /usr/local/bin/gitlab-runner'),
);
expect(runner.text()).toEqual(
expect.stringContaining(
`sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash`,
),
);
expect(runner.text()).toEqual(
expect.stringContaining(
'sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner',
),
);
expect(runner.text()).toEqual(expect.stringContaining('sudo gitlab-runner start'));
});
it('should display the runner register instructions for a selected architecture', async () => {
const platformButton = findPlatformButtons().at(0);
platformButton.vm.$emit('click');
await wrapper.vm.$nextTick();
const dropdownItem = findArchitectureDropdownItems().at(0);
dropdownItem.vm.$emit('click');
await wrapper.vm.$nextTick();
const runner = findRunnerInstructionsSection();
expect(runner.text()).toEqual(
expect.stringContaining(mockGraphqlInstructions.data.runnerSetup.registerInstructions),
);
});
});
......@@ -83,6 +83,28 @@ RSpec.describe Projects::AlertManagementHelper do
end
end
context 'with http integration' do
let_it_be(:integration) { create(:alert_management_http_integration, project: project) }
context 'when integration is active' do
it 'enables alert management' do
expect(data).to include(
'alert-management-enabled' => 'true'
)
end
end
context 'when integration is inactive' do
it 'disables alert management' do
integration.update!(active: false)
expect(data).to include(
'alert-management-enabled' => 'false'
)
end
end
end
context 'when user does not have requisite enablement permissions' do
let(:user_can_enable_alert_management) { false }
......
......@@ -25,7 +25,7 @@ RSpec.describe Packages::CreateEventService do
stub_feature_flags(collect_package_events: false)
end
it 'does not create an event object' do
it 'does not create an event' do
expect { subject }.not_to change { Packages::Event.count }
end
end
......@@ -43,6 +43,16 @@ RSpec.describe Packages::CreateEventService do
expect(subject.event_scope).to eq(expected_scope)
expect(subject.event_type).to eq(event_name)
end
context 'on a read-only instance' do
before do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
end
it 'does not create an event' do
expect { subject }.not_to change { Packages::Event.count }
end
end
end
end
......
......@@ -9,7 +9,7 @@ RSpec.describe Projects::TransferService do
let_it_be(:group) { create(:group) }
let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) }
subject(:execute_transfer) { described_class.new(project, user).execute(group) }
subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } }
context 'with npm packages' do
before do
......
......@@ -81,5 +81,14 @@ RSpec.describe 'projects/tags/index.html.haml' do
expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24'
end
it 'shows no build status or placeholder when pipelines are private' do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
assign(:tag_pipeline_statuses, Ci::CommitStatusesFinder.new(project, project.repository, build(:user), tags).execute)
render
expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24'
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment