Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-06-02 12:09:21 +00:00
parent 9cd5033338
commit d2eb61914a
32 changed files with 324 additions and 156 deletions

View file

@ -23,6 +23,7 @@ import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import animateMixin from '../mixins/animate';
import { convertDescriptionWithNewSort } from '../utils';
@ -314,7 +315,7 @@ export default {
this.workItemId = workItemId;
this.updateWorkItemIdUrlQuery(issue);
this.track('viewed_work_item_from_modal', {
category: 'workItems:show',
category: TRACKING_CATEGORY_SHOW,
label: 'work_item_view',
property: `type_${referenceType}`,
});

View file

@ -83,7 +83,7 @@ export function initIssueApp(issueData, store) {
bootstrapApollo({ ...issueState, issueType: el.dataset.issueType });
const { canCreateIncident, ...issueProps } = issueData;
const { canCreateIncident, hasIssueWeightsFeature, ...issueProps } = issueData;
return new Vue({
el,
@ -93,6 +93,7 @@ export function initIssueApp(issueData, store) {
provide: {
canCreateIncident,
fullPath,
hasIssueWeightsFeature,
},
computed: {
...mapGetters(['getNoteableData']),

View file

@ -1,6 +1,7 @@
<script>
import { GlSafeHtmlDirective } from '@gitlab/ui';
import Tracking from '~/tracking';
import { TRACKING_CATEGORY_SHOW } from '../constants';
export default {
directives: {
@ -21,7 +22,7 @@ export default {
computed: {
tracking() {
return {
category: 'workItems:show',
category: TRACKING_CATEGORY_SHOW,
label: 'item_description',
property: `type_${this.workItem.workItemType.name}`,
};

View file

@ -1,7 +1,12 @@
<script>
import { GlAlert, GlSkeletonLoader } from '@gitlab/ui';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { i18n, WIDGET_TYPE_ASSIGNEE, WIDGET_TYPE_DESCRIPTION } from '../constants';
import {
i18n,
WIDGET_TYPE_ASSIGNEE,
WIDGET_TYPE_DESCRIPTION,
WIDGET_TYPE_WEIGHT,
} from '../constants';
import workItemQuery from '../graphql/work_item.query.graphql';
import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql';
import WorkItemActions from './work_item_actions.vue';
@ -10,6 +15,7 @@ import WorkItemTitle from './work_item_title.vue';
import WorkItemDescription from './work_item_description.vue';
import WorkItemLinks from './work_item_links/work_item_links.vue';
import WorkItemAssignees from './work_item_assignees.vue';
import WorkItemWeight from './work_item_weight.vue';
export default {
i18n,
@ -22,6 +28,7 @@ export default {
WorkItemTitle,
WorkItemState,
WorkItemLinks,
WorkItemWeight,
},
mixins: [glFeatureFlagMixin()],
props: {
@ -77,12 +84,15 @@ export default {
workItemDescription() {
return this.workItem?.widgets?.find((widget) => widget.type === WIDGET_TYPE_DESCRIPTION);
},
workItemAssigneesEnabled() {
return this.glFeatures.workItemAssignees;
workItemsMvc2Enabled() {
return this.glFeatures.workItemsMvc2;
},
workItemAssignees() {
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_ASSIGNEE);
},
workItemWeight() {
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_WEIGHT);
},
},
};
</script>
@ -117,10 +127,10 @@ export default {
@error="error = $event"
/>
</div>
<work-item-assignees
v-if="workItemAssigneesEnabled && workItemAssignees"
:assignees="workItemAssignees.nodes"
/>
<template v-if="workItemsMvc2Enabled">
<work-item-assignees v-if="workItemAssignees" :assignees="workItemAssignees.nodes" />
<work-item-weight v-if="workItemWeight" :weight="workItemWeight.weight" />
</template>
<work-item-state
:work-item="workItem"
@error="error = $event"

View file

@ -7,6 +7,7 @@ import {
STATE_CLOSED,
STATE_EVENT_CLOSE,
STATE_EVENT_REOPEN,
TRACKING_CATEGORY_SHOW,
} from '../constants';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import ItemState from './item_state.vue';
@ -33,7 +34,7 @@ export default {
},
tracking() {
return {
category: 'workItems:show',
category: TRACKING_CATEGORY_SHOW,
label: 'item_state',
property: `type_${this.workItemType}`,
};

View file

@ -1,6 +1,6 @@
<script>
import Tracking from '~/tracking';
import { i18n } from '../constants';
import { i18n, TRACKING_CATEGORY_SHOW } from '../constants';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import ItemTitle from './item_title.vue';
@ -29,7 +29,7 @@ export default {
computed: {
tracking() {
return {
category: 'workItems:show',
category: TRACKING_CATEGORY_SHOW,
label: 'item_title',
property: `type_${this.workItemType}`,
};

View file

@ -0,0 +1,26 @@
<script>
import { __ } from '~/locale';
export default {
inject: ['hasIssueWeightsFeature'],
props: {
weight: {
type: Number,
required: false,
default: undefined,
},
},
computed: {
weightText() {
return this.weight ?? __('None');
},
},
};
</script>
<template>
<div v-if="hasIssueWeightsFeature" class="gl-mb-5">
<span class="gl-display-inline-block gl-font-weight-bold gl-w-15">{{ __('Weight') }}</span>
{{ weightText }}
</div>
</template>

View file

@ -6,6 +6,8 @@ export const STATE_CLOSED = 'CLOSED';
export const STATE_EVENT_REOPEN = 'REOPEN';
export const STATE_EVENT_CLOSE = 'CLOSE';
export const TRACKING_CATEGORY_SHOW = 'workItems:show';
export const i18n = {
fetchError: s__('WorkItem|Something went wrong when fetching the work item. Please try again.'),
updateError: s__('WorkItem|Something went wrong while updating the work item. Please try again.'),
@ -15,3 +17,4 @@ export const DEFAULT_MODAL_TYPE = 'Task';
export const WIDGET_TYPE_ASSIGNEE = 'ASSIGNEES';
export const WIDGET_TYPE_DESCRIPTION = 'DESCRIPTION';
export const WIDGET_TYPE_WEIGHT = 'WEIGHT';

View file

@ -39,6 +39,11 @@ export const temporaryConfig = {
},
],
},
{
__typename: 'LocalWorkItemWeight',
type: 'WEIGHT',
weight: 0,
},
];
},
},

View file

@ -1,5 +1,6 @@
enum LocalWidgetType {
ASSIGNEES
WEIGHT
}
interface LocalWorkItemWidget {
@ -11,6 +12,11 @@ type LocalWorkItemAssignees implements LocalWorkItemWidget {
nodes: [UserCore]
}
type LocalWorkItemWeight implements LocalWorkItemWidget {
type: LocalWidgetType!
weight: Int
}
extend type WorkItem {
mockWidgets: [LocalWorkItemWidget]
}

View file

@ -14,6 +14,10 @@ query workItem($id: WorkItemID!) {
webUrl
}
}
... on LocalWorkItemWeight {
type
weight
}
}
}
}

View file

@ -1,11 +1,12 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import App from './components/app.vue';
import { createRouter } from './router';
import { createApolloProvider } from './graphql/provider';
export const initWorkItemsRoot = () => {
const el = document.querySelector('#js-work-items');
const { fullPath, issuesListPath } = el.dataset;
const { fullPath, hasIssueWeightsFeature, issuesListPath } = el.dataset;
return new Vue({
el,
@ -13,6 +14,7 @@ export const initWorkItemsRoot = () => {
apolloProvider: createApolloProvider(),
provide: {
fullPath,
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
issuesListPath,
},
render(createElement) {

View file

@ -0,0 +1,26 @@
# frozen_string_literal: true
module ZuoraCSP
extend ActiveSupport::Concern
ZUORA_URL = 'https://*.zuora.com'
included do
content_security_policy do |policy|
next if policy.directives.blank?
default_script_src = policy.directives['script-src'] || policy.directives['default-src']
script_src_values = Array.wrap(default_script_src) | ["'self'", "'unsafe-eval'", ZUORA_URL]
default_frame_src = policy.directives['frame-src'] || policy.directives['default-src']
frame_src_values = Array.wrap(default_frame_src) | ["'self'", ZUORA_URL]
default_child_src = policy.directives['child-src'] || policy.directives['default-src']
child_src_values = Array.wrap(default_child_src) | ["'self'", ZUORA_URL]
policy.script_src(*script_src_values)
policy.frame_src(*frame_src_values)
policy.child_src(*child_src_values)
end
end
end

View file

@ -49,7 +49,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:paginated_issue_discussions, project)
push_frontend_feature_flag(:realtime_labels, project)
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:work_item_assignees)
push_frontend_feature_flag(:work_items_mvc_2)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]

View file

@ -4,6 +4,7 @@ class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
include RedisTracking
include ProjectStatsRefreshConflictsGuard
include ZuoraCSP
urgency :low, [
:index, :new, :builds, :show, :failures, :create,
@ -43,23 +44,6 @@ class Projects::PipelinesController < Projects::ApplicationController
POLLING_INTERVAL = 10_000
content_security_policy do |policy|
next if policy.directives.blank?
default_script_src = policy.directives['script-src'] || policy.directives['default-src']
script_src_values = Array.wrap(default_script_src) | ["'self'", "'unsafe-eval'", 'https://*.zuora.com']
default_frame_src = policy.directives['frame-src'] || policy.directives['default-src']
frame_src_values = Array.wrap(default_frame_src) | ["'self'", 'https://*.zuora.com']
default_child_src = policy.directives['child-src'] || policy.directives['default-src']
child_src_values = Array.wrap(default_child_src) | ["'self'", 'https://*.zuora.com']
policy.script_src(*script_src_values)
policy.frame_src(*frame_src_values)
policy.child_src(*child_src_values)
end
feature_category :continuous_integration, [
:charts, :show, :config_variables, :stage, :cancel, :retry,
:builds, :dag, :failures, :status,

View file

@ -3,7 +3,7 @@
class Projects::WorkItemsController < Projects::ApplicationController
before_action do
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:work_item_assignees)
push_frontend_feature_flag(:work_items_mvc_2)
push_frontend_feature_flag(:work_items_hierarchy, project)
end

View file

@ -42,7 +42,7 @@ class ProjectsController < Projects::ApplicationController
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies)
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
push_frontend_feature_flag(:work_item_assignees)
push_frontend_feature_flag(:work_items_mvc_2)
push_frontend_feature_flag(:package_registry_access_level)
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
module WorkItemsHelper
def work_items_index_data(project)
{
full_path: project.full_path,
issues_list_path: project_issues_path(project)
}
end
end

View file

@ -1,3 +1,3 @@
- page_title s_('WorkItem|Work Items')
#js-work-items{ data: { full_path: @project.full_path, issues_list_path: project_issues_path(@project) } }
#js-work-items{ data: work_items_index_data(@project) }

View file

@ -1,6 +1,6 @@
---
name: work_item_assignees
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88003
name: work_items_mvc_2
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89028
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363030
milestone: '15.1'
type: development

View file

@ -60,29 +60,22 @@ NOT_AVAILABLE_TEMPLATES = {
integrations_fe: group_not_available_template('#g_ecosystem_integrations', '@gitlab-org/ecosystem-stage/integrations')
}.freeze
def note_for_spins_role(spins, role, category)
def note_for_spin_role(spin, role, category)
template = NOT_AVAILABLE_TEMPLATES[category] || NOT_AVAILABLE_TEMPLATES[:default]
spins.each do |spin|
note = note_for_spin_role(spin, role)
note =
if spin.optional_role == role
OPTIONAL_REVIEW_TEMPLATE % { role: role.capitalize, category: helper.label_for_category(spin.category) }
else
spin.public_send(role)&.markdown_name(author: roulette.team_mr_author) # rubocop:disable GitlabSecurity/PublicSend
end
return note if note
end
template % { role: role }
note || template % { role: role }
end
def note_for_spin_role(spin, role)
if spin.optional_role == role
return OPTIONAL_REVIEW_TEMPLATE % { role: role.capitalize, category: helper.label_for_category(spin.category) }
end
spin.public_send(role)&.markdown_name(author: roulette.team_mr_author) # rubocop:disable GitlabSecurity/PublicSend
end
def markdown_row_for_spins(category, spins_array)
maintainer_note = note_for_spins_role(spins_array, :maintainer, category)
reviewer_note = note_for_spins_role(spins_array, :reviewer, category)
def markdown_row_for_spin(category, spin)
maintainer_note = note_for_spin_role(spin, :maintainer, category)
reviewer_note = note_for_spin_role(spin, :reviewer, category)
"| #{helper.label_for_category(category)} | #{reviewer_note} | #{maintainer_note} |"
end
@ -115,7 +108,7 @@ if changes.any?
random_roulette_spins = roulette.spin(nil, categories, timezone_experiment: false)
rows = random_roulette_spins.map do |spin|
markdown_row_for_spins(spin.category, [spin])
markdown_row_for_spin(spin.category, spin)
end
markdown(REVIEW_ROULETTE_SECTION)

View file

@ -230,6 +230,22 @@ All three tracks can be worked on in parallel:
In progress.
## Timeline
- 2021-01-21: Parent [CI Scaling](../ci_scale/) blueprint [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52203) created.
- 2021-04-26: CI Scaling blueprint approved and merged.
- 2021-09-10: CI/CD data time decay blueprint discussions started.
- 2022-01-07: CI/CD data time decay blueprint [merged](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70052).
- 2022-02-01: Blueprint [updated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79110) with new content and links to epics.
- 2022-02-08: Pipeline partitioning PoC [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186) started.
- 2022-02-23: Pipeline partitioning PoC [successful](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186#note_852704724)
- 2022-03-07: A way to attach an existing table as a partition [found and proven](https://gitlab.com/gitlab-org/gitlab/-/issues/353380#note_865237214).
- 2022-03-23: Pipeline partitioning design [Google Doc](https://docs.google.com/document/d/1ARdoTZDy4qLGf6Z1GIHh83-stG_ZLpqsibjKr_OXMgc) started.
- 2022-03-29: Pipeline partitioning PoC [concluded](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186#note_892674358).
- 2022-04-15: Partitioned pipeline data associations PoC [shipped](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84071).
- 2022-04-30: Additional [benchmarking started](https://gitlab.com/gitlab-org/gitlab/-/issues/361019) to evaluate impact.
- 2022-06-31: [Pipeline partitioning design](pipeline_partitioning.md) document [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87683) merged.
## Who
Proposal:

View file

@ -287,6 +287,7 @@ Use this regex for commonly used test tools.
- .NET (OpenCover). Example: `(Visited Points).*\((.*)\)`.
- .NET (`dotnet test` line coverage). Example: `Total\s*\|\s*(\d+(?:\.\d+)?)`.
- tarpaulin (Rust). Example: `^\d+.\d+% coverage`.
- Pester (PowerShell). Example: `Covered (\d+\.\d+%)`.
<!-- vale gitlab.Spelling = YES -->

View file

@ -4,59 +4,56 @@ group: Workspace
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/#assignments"
---
# Contribute to GitLab project templates
# Contribute a built-in project template
Thanks for considering a contribution to the GitLab
[built-in project templates](../user/project/working_with_projects.md#create-a-project-from-a-built-in-template).
This page provides instructions about how to contribute a
[built-in project template](../user/project/working_with_projects.md#create-a-project-from-a-built-in-template).
To contribute a built-in project template, you must complete the following tasks:
1. [Create a project template for GitLab review](#create-a-project-template-for-review)
1. [Add the template SVG icon to GitLab SVGs](#add-the-template-svg-icon-to-gitlab-svgs)
1. [Create a merge request with vendor details](#create-a-merge-request-with-vendor-details)
You can contribute the following types of project templates:
- Enterprise: For users with GitLab Premium and above.
- Non-enterprise: For users with GitLab Free and above.
## Prerequisites
To add a new or update an existing template, you must have the following tools
To add or update an existing template, you must have the following tools
installed:
- `wget`
- `tar`
- `jq`
## Create a new project
## Create a project template for review
To contribute a new built-in project template to be distributed with GitLab:
1. In your selected namespace, create a public project.
1. Add the project content you want to use in the template. Do not include unnecessary assets or dependencies. For an example,
[see this project](https://gitlab.com/gitlab-org/project-templates/dotnetcore).
1. When the project is ready for review, [create an issue](https://gitlab.com/gitlab-org/gitlab/issues) with a link to your project.
In your issue, mention the relevant [Backend Engineering Manager and Product Manager](https://about.gitlab.com/handbook/product/categories/#source-code-group)
for the Templates feature.
1. Create a new public project with the project content you'd like to contribute
in a namespace of your choosing. You can [view a working example](https://gitlab.com/gitlab-org/project-templates/dotnetcore).
Projects should be as simple as possible and free of any unnecessary assets or dependencies.
1. When the project is ready for review, [create a new issue](https://gitlab.com/gitlab-org/gitlab/issues) with a link to your project.
In your issue, `@` mention the relevant Backend Engineering Manager and Product
Manager for the [Templates feature](https://about.gitlab.com/handbook/product/categories/#source-code-group).
## Add the template SVG icon to GitLab SVGs
## Add the SVG icon to GitLab SVGs
If the project template has an SVG icon, you must add it to the
[GitLab SVGs project](https://gitlab.com/gitlab-org/gitlab-svgs/-/blob/main/README.md#adding-icons-or-illustrations)
before you can create a merge request with vendor details.
If the template you're adding has an SVG icon, you need to first add it to
<https://gitlab.com/gitlab-org/gitlab-svgs>:
## Create a merge request with vendor details
1. Follow the steps outlined in the
[GitLab SVGs project](https://gitlab.com/gitlab-org/gitlab-svgs/-/blob/main/README.md#adding-icons-or-illustrations)
and submit a merge request.
1. When the merge request is merged, `gitlab-bot` will pull the new changes in
the `gitlab-org/gitlab` project.
1. You can now continue on the vendoring process.
## Vendoring process
To make the project template available when creating a new project, the vendoring
process will have to be completed:
Before GitLab can implement the project template, you must [create a merge request](../user/project/merge_requests/creating_merge_requests.md) in [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) that includes vendor details about the project.
1. [Export the project](../user/project/settings/import_export.md#export-a-project-and-its-data)
you created in the previous step and save the file as `<name>.tar.gz`, where
`<name>` is the short name of the project.
1. Edit the following files to include the project template. Two types of built-in
templates are available within GitLab:
- **Normal templates**: Available in GitLab Free and above (this is the most common type of built-in template).
See MR [!25318](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318) for an example.
To add a normal template:
1. Open `lib/gitlab/project_template.rb` and add details of the template
and save the file as `<name>.tar.gz`, where `<name>` is the short name of the project.
Move this file to the root directory of `gitlab-org/gitlab`.
1. In `gitlab-org/gitlab`, create and checkout a new branch.
1. Edit the following files to include the project template:
- For **non-Enterprise** project templates:
- In `lib/gitlab/project_template.rb`, add details about the template
in the `localized_templates_table` method. In the following example,
the short name of the project is `hugo`:
@ -64,11 +61,11 @@ process will have to be completed:
ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo'), 'https://gitlab.com/pages/hugo', 'illustrations/logos/hugo.svg'),
```
If the vendored project doesn't have an SVG icon, omit `, 'illustrations/logos/hugo.svg'`.
If the project doesn't have an SVG icon, exclude `, 'illustrations/logos/hugo.svg'`.
1. Open `spec/lib/gitlab/project_template_spec.rb` and add the short name
of the template in the `.all` test.
1. Open `app/assets/javascripts/projects/default_project_templates.js` and
- In `spec/support/helpers/project_template_test_helper.rb`, append the short name
of the template in the `all_templates` method.
- In `app/assets/javascripts/projects/default_project_templates.js`,
add details of the template. For example:
```javascript
@ -78,25 +75,19 @@ process will have to be completed:
},
```
If the vendored project doesn't have an SVG icon, use `.icon-gitlab_logo`
If the project doesn't have an SVG icon, use `.icon-gitlab_logo`
instead.
- **Enterprise templates**: Introduced in GitLab 12.10, that are available only in GitLab Premium and above.
See MR [!28187](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28187) for an example.
To add an Enterprise template:
1. Open `ee/lib/ee/gitlab/project_template.rb` and add details of the template
in the `localized_ee_templates_table` method. For example:
- For **Enterprise** project templates:
- In `ee/lib/ee/gitlab/project_template.rb`, in the `localized_ee_templates_table` method, add details about the template. For example:
```ruby
::Gitlab::ProjectTemplate.new('hipaa_audit_protocol', 'HIPAA Audit Protocol', _('A project containing issues for each audit inquiry in the HIPAA Audit Protocol published by the U.S. Department of Health & Human Services'), 'https://gitlab.com/gitlab-org/project-templates/hipaa-audit-protocol', 'illustrations/logos/asklepian.svg')
```
1. Open `ee/spec/lib/gitlab/project_template_spec.rb` and add the short name
- In `ee/spec/lib/gitlab/project_template_spec.rb`, add the short name
of the template in the `.all` test.
1. Open `ee/app/assets/javascripts/projects/default_project_templates.js` and
add details of the template. For example:
- In `ee/app/assets/javascripts/projects/default_project_templates.js`,
add the template details. For example:
```javascript
hipaa_audit_protocol: {
@ -105,10 +96,11 @@ process will have to be completed:
},
```
1. Run the `vendor_template` script. Make sure to pass the correct arguments:
1. Run the following Rake task, where `<path>/<name>` is the
name you gave the template in `lib/gitlab/project_template.rb`:
```shell
scripts/vendor_template <git_repo_url> <name> <comment>
bin/rake gitlab:update_project_templates\[<path>/<name>\]
```
1. Regenerate `gitlab.pot`:
@ -117,41 +109,24 @@ process will have to be completed:
bin/rake gettext:regenerate
```
1. By now, there should be one new file under `vendor/project_templates/` and
4 changed files. Commit all of them in a new branch and create a merge
request.
1. After you run the scripts, there is one new file in `vendor/project_templates/` and four changed files. Commit all changes and push your branch to update the merge request. For an example, see this [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25318).
## Test with GDK
## Test your built-in project with the GitLab Development Kit
If you are using the GitLab Development Kit (GDK) you must disable `praefect`
and regenerate the Procfile, as the Rake task is not currently compatible with it:
Complete the following steps to test the project template in your own GitLab Development Kit instance:
```yaml
# gitlab-development-kit/gdk.yml
praefect:
enabled: false
```
1. Follow the steps described in the [vendoring process](#vendoring-process).
1. Run the following Rake task where `<path>/<name>` is the
1. Run the following Rake task, where `<path>/<name>` is the
name you gave the template in `lib/gitlab/project_template.rb`:
```shell
bin/rake gitlab:update_project_templates[<path>/<name>]
bin/rake gitlab:update_project_templates\[<path>/<name>\]
```
You can now test to create a new project by importing the new template in GDK.
## Contribute an improvement to an existing template
Existing templates are imported from the following groups:
To update an existing built-in project template:
- [`project-templates`](https://gitlab.com/gitlab-org/project-templates)
- [`pages`](htps://gitlab.com/pages)
To contribute a change, open a merge request in the relevant project
and mention `@gitlab-org/manage/import/backend` when you are ready for a review.
Then, if your merge request gets accepted, either [open an issue](https://gitlab.com/gitlab-org/gitlab/-/issues)
to ask for it to get updated, or open a merge request updating
the [vendored template](#vendoring-process).
1. Create a merge request in the relevant project of the `project-templates` and `pages` group and mention `@gitlab-org/manage/import/backend` when you are ready for a review.
1. If your merge request is accepted, either:
- [Create an issue](https://gitlab.com/gitlab-org/gitlab/-/issues) to ask for the template to get updated.
- [Create a merge request with vendor details](#create-a-merge-request-with-vendor-details) to update the template.

View file

@ -115,7 +115,7 @@ Built-in templates are sourced from the following groups:
- [`project-templates`](https://gitlab.com/gitlab-org/project-templates)
- [`pages`](https://gitlab.com/pages)
Anyone can contribute a built-in template by following [these steps](https://about.gitlab.com/community/contribute/project-templates/).
Anyone can [contribute a built-in template](../../development/project_templates.md).
To create a project from a built-in template:

View file

@ -34,7 +34,7 @@ namespace :knapsack do
desc "Report long running spec files"
task :notify_long_running_specs do
QA::Support::LongRunningSpecReporter.execute
QA::Tools::LongRunningSpecReporter.execute
end
end
# rubocop:enable Rails/RakeEnvironment

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Zuora content security policy' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
before do
project.add_developer(user)
sign_in(user)
end
it 'has proper Content Security Policy headers' do
visit pipeline_path(pipeline)
expect(response_headers['Content-Security-Policy']).to include('https://*.zuora.com')
end
end

View file

@ -17,6 +17,7 @@ import { updateHistory } from '~/lib/utils/url_utility';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import TaskList from '~/task_list';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import {
descriptionProps as initialProps,
@ -370,10 +371,10 @@ describe('Description component', () => {
await findTaskLink().trigger('click');
expect(trackingSpy).toHaveBeenCalledWith(
'workItems:show',
TRACKING_CATEGORY_SHOW,
'viewed_work_item_from_modal',
{
category: 'workItems:show',
category: TRACKING_CATEGORY_SHOW,
label: 'work_item_view',
property: 'type_task',
},

View file

@ -12,6 +12,7 @@ import {
STATE_CLOSED,
STATE_EVENT_CLOSE,
STATE_EVENT_REOPEN,
TRACKING_CATEGORY_SHOW,
} from '~/work_items/constants';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import { updateWorkItemMutationResponse, workItemQueryResponse } from '../mock_data';
@ -107,8 +108,8 @@ describe('WorkItemState component', () => {
findItemState().vm.$emit('changed', STATE_CLOSED);
await waitForPromises();
expect(trackingSpy).toHaveBeenCalledWith('workItems:show', 'updated_state', {
category: 'workItems:show',
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_state', {
category: TRACKING_CATEGORY_SHOW,
label: 'item_state',
property: 'type_Task',
});

View file

@ -6,7 +6,7 @@ import { mockTracking } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
import ItemTitle from '~/work_items/components/item_title.vue';
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
import { i18n } from '~/work_items/constants';
import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import { updateWorkItemMutationResponse, workItemQueryResponse } from '../mock_data';
@ -91,8 +91,8 @@ describe('WorkItemTitle component', () => {
findItemTitle().vm.$emit('title-changed', 'new title');
await waitForPromises();
expect(trackingSpy).toHaveBeenCalledWith('workItems:show', 'updated_title', {
category: 'workItems:show',
expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_title', {
category: TRACKING_CATEGORY_SHOW,
label: 'item_title',
property: 'type_Task',
});

View file

@ -0,0 +1,47 @@
import { shallowMount } from '@vue/test-utils';
import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
describe('WorkItemAssignees component', () => {
let wrapper;
const createComponent = ({ weight, hasIssueWeightsFeature = true } = {}) => {
wrapper = shallowMount(WorkItemWeight, {
propsData: {
weight,
},
provide: {
hasIssueWeightsFeature,
},
});
};
describe('weight licensed feature', () => {
describe.each`
description | hasIssueWeightsFeature | exists
${'when available'} | ${true} | ${true}
${'when not available'} | ${false} | ${false}
`('$description', ({ hasIssueWeightsFeature, exists }) => {
it(hasIssueWeightsFeature ? 'renders component' : 'does not render component', () => {
createComponent({ hasIssueWeightsFeature });
expect(wrapper.find('div').exists()).toBe(exists);
});
});
});
describe('weight text', () => {
describe.each`
description | weight | text
${'renders 1'} | ${1} | ${'1'}
${'renders 0'} | ${0} | ${'0'}
${'renders None'} | ${null} | ${'None'}
${'renders None'} | ${undefined} | ${'None'}
`('when weight is $weight', ({ description, weight, text }) => {
it(description, () => {
createComponent({ weight });
expect(wrapper.text()).toContain(text);
});
});
});
});

View file

@ -9,6 +9,7 @@ import WorkItemDescription from '~/work_items/components/work_item_description.v
import WorkItemState from '~/work_items/components/work_item_state.vue';
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
import { i18n } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
@ -29,13 +30,14 @@ describe('WorkItemDetail component', () => {
const findWorkItemState = () => wrapper.findComponent(WorkItemState);
const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription);
const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
const findWorkItemWeight = () => wrapper.findComponent(WorkItemWeight);
const createComponent = ({
workItemId = workItemQueryResponse.data.workItem.id,
handler = successHandler,
subscriptionHandler = initialSubscriptionHandler,
assigneesEnabled = false,
includeAssigneesWidget = false,
workItemsMvc2Enabled = false,
includeWidgets = false,
} = {}) => {
wrapper = shallowMount(WorkItemDetail, {
apolloProvider: createMockApollo(
@ -45,13 +47,13 @@ describe('WorkItemDetail component', () => {
],
{},
{
typePolicies: includeAssigneesWidget ? temporaryConfig.cacheConfig.typePolicies : {},
typePolicies: includeWidgets ? temporaryConfig.cacheConfig.typePolicies : {},
},
),
propsData: { workItemId },
provide: {
glFeatures: {
workItemAssignees: assigneesEnabled,
workItemsMvc2: workItemsMvc2Enabled,
},
},
});
@ -153,11 +155,11 @@ describe('WorkItemDetail component', () => {
expect(wrapper.emitted('workItemUpdated')).toEqual([[], []]);
});
describe('when assignees feature flag is enabled', () => {
describe('when work_items_mvc_2 feature flag is enabled', () => {
it('renders assignees component when assignees widget is returned from the API', async () => {
createComponent({
assigneesEnabled: true,
includeAssigneesWidget: true,
workItemsMvc2Enabled: true,
includeWidgets: true,
});
await waitForPromises();
@ -166,8 +168,8 @@ describe('WorkItemDetail component', () => {
it('does not render assignees component when assignees widget is not returned from the API', async () => {
createComponent({
assigneesEnabled: true,
includeAssigneesWidget: false,
workItemsMvc2Enabled: true,
includeWidgets: false,
});
await waitForPromises();
@ -181,4 +183,36 @@ describe('WorkItemDetail component', () => {
expect(findWorkItemAssignees().exists()).toBe(false);
});
describe('weight widget', () => {
describe('when work_items_mvc_2 feature flag is enabled', () => {
describe.each`
description | includeWidgets | exists
${'when widget is returned from API'} | ${true} | ${true}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ includeWidgets, exists }) => {
it(`${includeWidgets ? 'renders' : 'does not render'} weight component`, async () => {
createComponent({ includeWidgets, workItemsMvc2Enabled: true });
await waitForPromises();
expect(findWorkItemWeight().exists()).toBe(exists);
});
});
});
describe('when work_items_mvc_2 feature flag is disabled', () => {
describe.each`
description | includeWidgets | exists
${'when widget is returned from API'} | ${true} | ${false}
${'when widget is not returned from API'} | ${false} | ${false}
`('$description', ({ includeWidgets, exists }) => {
it(`${includeWidgets ? 'renders' : 'does not render'} weight component`, async () => {
createComponent({ includeWidgets, workItemsMvc2Enabled: false });
await waitForPromises();
expect(findWorkItemWeight().exists()).toBe(exists);
});
});
});
});
});