summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG-EE.md326
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.vue2
-rw-r--r--app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/dag/dag.vue65
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js21
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue6
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js1
-rw-r--r--app/assets/javascripts/snippets/components/snippet_title.vue2
-rw-r--r--app/assets/stylesheets/framework/common.scss1
-rw-r--r--app/controllers/projects/environments_controller.rb2
-rw-r--r--app/graphql/resolvers/base_resolver.rb33
-rw-r--r--app/helpers/projects_helper.rb35
-rw-r--r--app/models/project.rb31
-rw-r--r--app/policies/project_policy.rb19
-rw-r--r--app/views/admin/appearances/_form.html.haml10
-rw-r--r--app/views/admin/appearances/_system_header_footer_form.html.haml2
-rw-r--r--app/views/admin/hook_logs/_index.html.haml2
-rw-r--r--app/views/clusters/clusters/_sidebar.html.haml2
-rw-r--r--app/views/devise/confirmations/almost_there.haml2
-rw-r--r--app/views/doorkeeper/applications/index.html.haml4
-rw-r--r--app/views/import/phabricator/new.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/layouts/terms.html.haml2
-rw-r--r--app/views/profiles/_event_table.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml8
-rw-r--r--app/views/profiles/active_sessions/index.html.haml2
-rw-r--r--app/views/profiles/audit_log.html.haml2
-rw-r--r--app/views/profiles/chat_names/index.html.haml2
-rw-r--r--app/views/profiles/emails/index.html.haml6
-rw-r--r--app/views/profiles/gpg_keys/index.html.haml4
-rw-r--r--app/views/profiles/keys/index.html.haml4
-rw-r--r--app/views/profiles/notifications/show.html.haml4
-rw-r--r--app/views/profiles/passwords/edit.html.haml4
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml8
-rw-r--r--app/views/profiles/preferences/_sourcegraph.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml10
-rw-r--r--app/views/profiles/show.html.haml10
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml12
-rw-r--r--app/views/projects/cleanup/_show.html.haml4
-rw-r--r--app/views/projects/empty.html.haml2
-rw-r--r--app/views/projects/environments/_form.html.haml2
-rw-r--r--app/views/projects/forks/new.html.haml4
-rw-r--r--app/views/projects/hook_logs/_index.html.haml2
-rw-r--r--app/views/projects/hook_logs/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml8
-rw-r--r--app/views/projects/protected_branches/show.html.haml2
-rw-r--r--app/views/projects/protected_tags/show.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml2
-rw-r--r--app/views/projects/settings/access_tokens/index.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml2
-rw-r--r--app/views/projects/triggers/edit.html.haml2
-rw-r--r--app/views/shared/access_tokens/_created_container.html.haml2
-rw-r--r--app/views/shared/access_tokens/_form.html.haml2
-rw-r--r--app/views/shared/deploy_keys/_index.html.haml2
-rw-r--r--app/views/shared/deploy_tokens/_index.html.haml2
-rw-r--r--app/views/shared/deploy_tokens/_new_deploy_token.html.haml2
-rw-r--r--app/views/shared/file_hooks/_index.html.haml2
-rw-r--r--app/views/shared/milestones/_deprecation_message.html.haml2
-rw-r--r--app/views/shared/notifications/_custom_notifications.html.haml4
-rw-r--r--app/views/shared/snippets/_header.html.haml2
-rw-r--r--app/views/shared/web_hooks/_title_and_docs.html.haml2
-rw-r--r--changelogs/unreleased/aalakkad-fix-time-tracking-help-link.yml5
-rw-r--r--doc/development/api_graphql_styleguide.md27
-rw-r--r--doc/development/telemetry/index.md66
-rw-r--r--doc/development/telemetry/snowplow.md6
-rw-r--r--doc/development/telemetry/usage_ping.md99
-rw-r--r--doc/user/infrastructure/index.md346
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb57
-rw-r--r--spec/frontend/pipelines/components/dag/dag_spec.js77
-rw-r--r--spec/graphql/resolvers/base_resolver_spec.rb52
-rw-r--r--spec/helpers/projects_helper_spec.rb20
-rw-r--r--spec/models/project_spec.rb58
-rw-r--r--spec/policies/project_policy_spec.rb96
-rw-r--r--spec/support/helpers/graphql_helpers.rb24
-rw-r--r--spec/support/matchers/graphql_matchers.rb11
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb2
80 files changed, 1142 insertions, 532 deletions
diff --git a/CHANGELOG-EE.md b/CHANGELOG-EE.md
index c63254a24b4..401f4008a5f 100644
--- a/CHANGELOG-EE.md
+++ b/CHANGELOG-EE.md
@@ -1,5 +1,331 @@
Please view this file on the master branch, on stable branches it's out of date.
+## 13.0.0 (2020-05-22)
+
+### Security (1 change)
+
+- Apply CODEOWNERS validations to web requests. !31283
+
+### Removed (1 change)
+
+- Remove deprecated route for serving full-size Design Management design files. !30917
+
+### Fixed (77 changes, 5 of them are from the community)
+
+- Enforce CODEOWNER rules for renaming of files. !26513
+- Preserve date filters in value stream and productivity analytics. !27102
+- Fix 404s when clicking links in full code quality report. !27138
+- Remove Admin > Settings > Templates link from sidenav when insufficient license. !27172
+- Geo - Does not write to DB when using 2FA via OTP for admin mode. !27450
+- Use group parent for subgroup labels requests. !27564
+- Hidden stages should not appear in duration chart. !27568
+- Maven packages API allows HEAD requests to package files when using Amazon S3 as a object storage backend. !27612
+- Prevent renaming a locked file. !27623
+- Use absolute URLs to ensure links to the source of SAST vulnerabilities resolve. !27747
+- Prevent issues from being promoted twice. !27837
+- Display error message in custom metric form validation when prometheus URL is blocked. !27863
+- Append inapplicable rules when creating MR. !27886
+- Fix passing project_ids to Value Stream Analytics. !27895
+- Support inapplicable rules when creating MR. !27971
+- Fix vsa label dropdown limit. !28073
+- Fix analytics group and project loading spinners styling. !28094
+- Include subgroups when populating group security dashboard. !28154
+- Hide ability to create alert on custom metrics dashboard. !28180
+- Fix epics not preserving their state on Group Import. !28203
+- Fix invalid scoped label documentation URL when rendered in Markdown. !28268
+- Fix create epic in tree after selecting Add issue. !28300
+- Fix HTTP status code for Todos API when an epic cannot be found. !28310
+- Allow SCIM to create an identity for an existing user. !28379
+- Perform Geo actions on projects only. !28421
+- Allow unsetting "Required pipeline configuration" for CI/CD. !28432
+- Geo: Self-service framework does not associate geo_event_log row to geo_event. !28457
+- Return overridden property in API response. !28521
+- Fix billed users id and count from shared group. !28645
+- Hide Request subtitle when Threat Monitoring has no data. !28760
+- Fix repository settings page loading issues for some imported projects with pull mirroring. !28879
+- Fix group_plan in group member data for system hooks. !29013
+- Fix imageLoading bug when scrolling back to design. !29223
+- Fix Elasticsearch rollout stale cache bug. !29233
+- Resolve Creating an annotation on the design that is bigger that screen size is broken. !29351
+- Fix incorrect dropdown styling for embedded metric charts. !29380 (Gilang Gumilar)
+- Close all other sidebars when the boards list settings sidebar is opened. !29456
+- Fix incorrect repositioning of design comment pins when mouse leaves viewport. !29464
+- Fix board edit weight values 0 or None. !29606
+- Fix lack-of padding on design view notes sidebar. !29654
+- Remove duplicate QA attribute for burndown charts. !29719
+- Add validation to Conan recipe that conforms with Conan.io. !29739 (Bola Ahmed Buari)
+- Fix caching performance and some cache bugs with elasticsearch enabled check. !29751
+- Fix wiki indexing for imported projects. !29952
+- Fix filter todos by design. !29972 (Gilang Gumilar)
+- Fix the error message on Security & Operations dashboard by fixing the API response. !30047
+- Fix showing New requirement button to unauthenticated user. !30085 (Gilang Gumilar)
+- Ignore invalid license_scanning reports. !30114
+- Sort dependency scanning reports before merging. !30190
+- Fix typos on Threat Management page. !30218
+- Fix infinte loading spinner when visiting non-existent design. !30263
+- Fix third party advisory links. !30271
+- Fix dismissal state not being updated. !30503
+- Add sort and order for policy violations. !30568
+- If a parent group enforces SAML SSO, when adding a member to that group, its subgroup or its project the autocomplete shows only users who have identity with the SAML provider of the parent group. !30607
+- Geo: Fix empty synchronisation status when nothing is synchronised. !30710
+- Don't hide Open/Closed Icon for Issue. !30819
+- No seat link sync for licenses without expiration. !30874
+- Fix project insights page when projects.only parameter is used. !30988
+- Fixes styling on vulnerability counts. !31076
+- Hide child epic icon on Roadmap for accounts without child epics support. !31250
+- Add license check for the 'send emails from Admin area' feature. !31434
+- Avoid saving author object in audit_events table. !31456
+- Fix 500 errors caused by globally searching for scopes which cannot be used without Elasticsearch. !31508
+- Show .Net license scan results as links. !31552
+- Fix incorrect notice text on insights page. !31570
+- Add license restriction to HealthStatus. !31571
+- Fix issues search to include the epic filter ANY. !31614
+- Reduce 413 errors when making bulk indexing requests to Elasticsearch. !31653
+- Fix CI minutes notification when unauthenticated. !31724
+- Add LFS workhorse upload path to allowed upload paths. !31794
+- Relax force pull mirror update restriction. !32075
+- Show correct last successful update timestamp. !32078
+- Fix missing dismissed_by field. !32147
+- Fix empty unit display of time metrics. !32388
+- Fixes file row commits not showing for certain projects.
+- Validates ElasticSearch URLs are valid HTTP(S) URLs. (mbergeron)
+
+### Deprecated (2 changes)
+
+- Document planned deprecation of 'marked_for_deletion_at' attribute in Projects API in GitLab 13.0. !28993
+- Document planned deprecation of 'projects' and 'shared_projects' attributes in Groups API in GitLab 13.0. !29113
+
+### Changed (64 changes, 1 of them is from the community)
+
+- Allow Value Stream Analytics custom stages to be manually ordered. !26074
+- Create more intuitive Popover information for Geo Node Sync Status. !27033
+- Make "Value Stream" the default page that appears when clicking the group-level "Analytics" sidebar item. !27277 (Gilang Gumilar)
+- Update renewal banner messaging and styling. !27530
+- Hide company question for new account signups. !27563
+- Create more intuitive Verification Popover for Geo Node Syncs. !27624
+- Disabled Primary Node Removal button when removal is not allowed. !27836
+- Move issue/apic hierarchy items to a tooltip. !27969
+- Update copy for Adding Licenses. !27970
+- Sort events alphabetically on Value Stream Analytics Stage form. !28005
+- Return 202 Accepted status code when fetching incompleted Vulnerability Export from API. !28314
+- Use dropdown to change health status. !28547
+- Change GraphQL arguments in group.timelogs query to use startTime and endTime. !28560
+- Enable issue board focus mode for all tiers on Enterprise Edition. !28597
+- Clarify detected license results in merge request: Group licenses by status. !28631
+- Deleting packages from a deeper paginated page will no longer return you to the first page. !28638
+- Sort Dependency List by severity by default. !28654
+- Make Dependency List pipeline subheading more succinct. !28665
+- Enable export issues feature for all tiers on Enterprise Edition. !28675
+- Remove scoped labels documentation link. !28701
+- Change summary text copy for license-compliance MR widget. !28732
+- Update License Compliance docs url. !28853
+- Differentiate between empty and disabled Geo sync. !28963
+- Move deploy keys section back to repository settings. !29184
+- Hide Pipeline Security tab from reporters. !29334
+- Make Geo Selective Sync More Clear from the Node Details view. !29596
+- Fix checkbox alignment and responsive word-break for secure table rows. !29659
+- Add tracking to Subscription banner. !29735
+- SSO Enforcement requires sign in within 7 days. !29786
+- Geo - Better Out of Date Errors. !29800
+- Replace non-standard term in Issues Analytics chart labels. !29810
+- Change default icon for neutral-state items in merge request widget. !30008
+- Remove tasks_by_type_chart feature flag. !30034
+- Modify existing out of runner minutes banner to handle 3 different warning levels. !30088
+- Resolve Allow multiple screenshots to be uploaded in copy paste. !30152
+- Improve the running out of CI minutes email notification. !30188
+- Change 'Whats New' dropdown item url. !30198
+- Link Buy additional minutes button straight to funnel. !30248
+- Improve design management image loads by avoiding refetching image urls for designs. !30280
+- Change logic to find the current license. !30296
+- Link license management button to license compliance policies section. !30344
+- Sort license policy violations first. !30564
+- Change UI requirements route from project/requirements to project/requirements_management/requirements. !30583
+- Change default concurrency of merge trains to twenty. !30599
+- Clarify security report findings in merge request widget. !30688
+- Consolidate epic tree buttons. !30816
+- Increase ProcessBookkeepingService batch to 10_000. !30817
+- Modify GraphQL mutation for adding projects to Instance Security Dashboard to support only single project id. !30865
+- Add subscription banner to group/subgroup pages. !30883
+- Geo - Add Empty States. !31010
+- Remove customizable_cycle_analytics feature flag. !31189
+- Geo - Bring settings UI in-line with other Geo Views. !31257
+- Cards in Epic Tree have two lines of content. !31300
+- Add request information to vulnerability-detail modal. !31422
+- Move registrations progress bar to a generic place and make it configurable. !31484
+- Reduce epic health status noise in epic tree. !31555
+- Modify GraphQL mutation for removing project from Instance Security Dashboard to use id instead of projectId. !31575
+- Enable onboarding issues experiment on Welcome screen. !31656
+- Expring Subscription banner has plan specific text. !31777
+- Geo - Better visualize type of node on form. !31784
+- Improve tooltips for compliance framework badges. !31883
+- Allow developers to see ci minutes notifications. !31937
+- Log Audit Event when Group SAML adds a user to a group. !32333
+- Audit logs now uses filtered search.
+
+### Performance (13 changes, 1 of them is from the community)
+
+- Geo - Improve query to retrieve Job Artifacts, LFS Objects, and Uploads with files stored locally. !24891
+- Geo - Use bulk insert to improve performance of RegistryBackfillService. !27720
+- Improve Group Security Dashboard performance. !27959
+- Fix N+1 queries in Audit Events controllers. !28399
+- Move Clusters Application CertManager to batch counting. !28771
+- Move Clusters Application Helm to batch counting. !28995
+- Move Clusters Application Ingress to batch counting. !28999
+- Move Clusters Application Knative to batch counting. !29003
+- Preload path locks for TreeSummary. !29949
+- Debounce pull mirror invocation. !30157
+- Advanced Search API: Eager load more issue associations to reduce N+1 queries. !30233
+- Make the ElasticCommitIndexer idempotent to enable job de-duplication. !31500 (mbergeron)
+- Use data-interchange format based on .ndjson for Project import and export. !31601
+
+### Added (129 changes, 13 of them are from the community)
+
+- Support setting threshold for browser performance degradation through CI config. !21824
+- Restrict page access when restricted level is public. !22522 (briankabiro)
+- Add ability to expand epics in roadmap. !23600
+- Prefer smaller image size for design cards in Design Management. !24828
+- Warn users when they close a blocked issue. !25089
+- Added setting to use a custom service desk email. !25240
+- Add ability to explore zoomed in designs via click-and-drag. !25405
+- Allow GMA groups to specify their own PAT expiry setting. !25963
+- Add vulnerabilities field to QueryType. !26348
+- On-demand Evidence creation. !26591
+- Add API endpoint for new members' count in Group Activity Analytics. !26601
+- Allow multiple root certificates for smartcard auth. !26812
+- Add scanned URL count and link to scanned resources in DAST reports. !26825
+- Add a button to export vulnerabilities in CSV format. !26838
+- Add #resolved_on_default_branch to Vulnerability. !26906
+- Migrate SAML to SCIM Identities. !26940
+- Expose smaller sized Design Management design images in GraphQL. !26947
+- Add Snowplow tracking for Container Registry events. !27001
+- Geo - Support git clone/pull operations for repositories that are not yet replicated. !27072
+- Add an indicator icon to issues on subepics when filtering by epic. !27212
+- Add a count of the scanned resources to Security::Scan. !27260
+- Track primary package file checksum counts. !27271
+- Anonymize GitLab user/group names on Status Detail Pages. !27273
+- Enable NetworkPolicy Statistics by default. !27365
+- Separate approval setting entities into own class files. !27423 (Rajendra Kadam)
+- Show custom 'media broken' icon for broken images in Design Management. !27460
+- Display loading spinner when Design Card images are loading. !27475
+- Updated link to Status Page docs. !27500
+- Adds support for storing notes for vulnerabilities. !27515
+- Add endpoints to fetch notes/discussions for vulnerability. !27585
+- Support PyPi package upload. !27632
+- Separate conan package entities into own class files. !27642 (Rajendra Kadam)
+- Separate model only entities into own class files. !27665 (Rajendra Kadam)
+- Add terraform report to merge request widget. !27700
+- Add a DB column to allow GMA groups to specify their PAT expiry setting. !27769
+- Separate user, project and entity helper modules into own class files. !27771 (Rajendra Kadam)
+- Add rake task for reindexing Elasticsearch. !27772
+- Separate group, member, group_detail and identity modules into own class files. !27797 (Rajendra Kadam)
+- Indicate whether the alert is firing. !27825
+- Support PyPi package installation. !27827
+- Separate code review, design and group modules into own class files. !27860 (Rajendra Kadam)
+- Add Health Status badge in Epic tree. !27869
+- Separate nuget package entities into own class files. !27898 (Rajendra Kadam)
+- Create issue link when creating issue from vulnerability. !27899
+- Geo: Proxy SSH git operations for repositories that are not yet replicated. !27994
+- Add pipeline statuses to Compliance Dashboard. !28001
+- Add Usage Ping For Status Page. !28002
+- Adds additional pipeline information to packages API result. !28040
+- Allow to save issue health status. !28146
+- Add a compliance framework setting to project. !28182
+- Add project template for HIPAA Audit Protocol. !28187
+- Create system note when health status is updated. !28232
+- Pre-populate prometheus alert modal. !28291 (Gilang Gumilar)
+- Usage ping for customized Value Stream Management stage count. !28308
+- Add evidence to vulnerability details. !28315
+- Added confidential column to epics table. !28428
+- Geo GraphQL API: Add geoNode at root. !28454
+- Refactor duplicate specs in ee user model. !28513 (Rajendra Kadam)
+- Refactor duplicate specs in ee group model. !28538 (Rajendra Kadam)
+- Lazy-loading design images with IntersectionObserver. !28555
+- Create group-level audit event for Group SAML SSO sign in. !28575
+- Renewal banner has auto-renew specific messaging. !28579
+- Automatically embed metrics in issues for alerts from manually configured Prometheus instances. !28622
+- Epic tree move child with drag and drop. !28629
+- Show the number of scanned resources in the DAST vulnerability report. !28718
+- Expose 'marked_for_deletion_on' attribute in Projects API. !28754
+- Show policy violations in license compliance. !28862
+- Migrate project snippets to the ghost user when the user is deleted. !28870 (George Thomas @thegeorgeous)
+- Provide instance level setting to enable or disable 'default branch protection' at the group level for group owners. !28997
+- Add spentAt field to TimelogType and deprecate date field. !29024
+- Add hierarchy depth to roadmaps. !29105
+- Add mutation to Dismiss Vulnerability GraphQL API. !29150
+- Add "whats new" item to help dropdown. !29183
+- Expose hasParent GraphQL field on epic. !29214
+- Display indicator to rule name in approval rules. !29315
+- Add status page url to setting form in UI. !29359
+- Enable Standalone Vulnerabilities feature for improving Vulnerability Management. !29431
+- Add `group_id` column into vulnerability_exports table. !29498
+- Enable creation on custom index with rake. !29598 (mbergeron)
+- Replace pipeline quota with usage quotas for user namespaces. !29806
+- Add GraphQL mutation to update limit metric settings on board lists. !29897
+- Adds PyPi installation instructions to package details page. !29935
+- Survey Responses landing page. !29951
+- Add limit metric to list type. !30028
+- Add GraphQL query for Instance Security Dashboard projects. !30064
+- Adds PyPi tab to the packages list page. !30078
+- Export an instance-level vulnerabilities report. !30079
+- Add GraphQL mutation for adding projects to Instance Security Dashboard. !30092
+- Add GraphQL mutation for removing projects from Instance Security Dashboard. !30100
+- Show specific success message when uploading a future license. !30161
+- Show all licenses and highlight current license in license history. !30172
+- Adds pipeline project name and link to package title. !30275
+- Enable expiring subscription banner. !30304
+- Handle subscription purchase flow via GitLab. !30324
+- Add Approved By in filtered MR search. !30335
+- Add autocompletion to Design View comment form. !30347
+- Add confidential flag in epic create. !30370
+- Add API endpoints to generate instance level security report exports. !30397
+- Add scanner name, version and URL to Vulnerability Modal. !30458
+- Introduce negative filters for code review analytics. !30506
+- Add NuGet dependencies extraction to the GitLab Packages Repository. !30618
+- Add vulnerability fields to GraphQL project, group, and global scope. !30663
+- Add vulnerability history to graphQL. !30674
+- Add package tags support to the GitLab NuGet Packages Repository. !30726
+- Add `Group Name` and `Project Name` into vulnerability export CSV file. !30755
+- Display banner to admins when there are users over license. !30813
+- Geo Replication View. !30890
+- Record impersonator information on audit event. !30970
+- Measure project import and export service execution. !30977
+- Adds package_name filter to group packages API. !30980
+- Add Nuget metadatum support in GitLab Packages. !30994
+- Show the You're running out of CI minutes warnings on relevant pages in GitLab. !31012
+- Add spinner to roadmap. !31080
+- Add standaloneVulnerabilitiesEnabled filter to group projects resolver for GraphQL. !31081
+- Add push rules configuration for groups - frontend. !31085
+- Add project import to group as audit event. !31103
+- Add Web IDE terminal usage counter. !31158
+- Add versions array to the package API payload. !31231
+- Rate limit the 'Send emails from Admin Area' feature. !31308
+- Add graphql endpoint for project packages list. !31344
+- Add project filter. !31444
+- Show specific content for future-dated licenses. !31463
+- Add lead time and cycle time to value stream analytics. !31559
+- Geo - Enable geo_file_registry_ssot_sync feature flag by default. !31671
+- Geo - Enable geo_job_artifact_registry_ssot_sync feature flag by default. !31672
+- Introduce a new API endpoint to generate group-level vulnerability exports. !31889
+- Add number of projects with compliance framework to usage ping. !31923
+- Adds versions tab and additional versions list to packages details page. !31940
+- Add link to customer portal from license dashboard.
+
+### Other (12 changes, 3 of them are from the community)
+
+- Add verification related fields to packages_package_files table. !25411
+- Refactor expected_paginated_array_response. !25500 (George Thomas @thegeorgeous)
+- Monitoring for Elasticsearch incremental updates buffer queue. !27384
+- Use warning icon for alert widget in monitoring dashboard. !27545
+- Improved tests by removing duplicated specs. !28525 (Leandro Silva)
+- Move service desk from Premium to Starter plan. !29980
+- Change license history wording. !30148
+- Make active_users param mandatory for SyncSeatLinkRequestWorker#perform. !30810
+- Track group wiki storage in DB. !31121
+- Replace undefined confidence with unknown severity for occurrences. !31200
+- Replace undefined confidence with unknown severity for vulnerabilities. !31593
+- Translate unauthenticated user string for Audit Event. !31856 (Sashi Kumar)
+
+
## 12.10.6 (2020-05-15)
- No changes.
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index a589fb325b2..642d104675a 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -147,7 +147,7 @@ export default {
<template>
<div>
<div class="d-flex board-card-header" dir="auto">
- <h4 class="board-card-title append-bottom-0 prepend-top-0">
+ <h4 class="board-card-title append-bottom-0 gl-mt-0">
<icon
v-if="issue.blocked"
v-gl-tooltip
diff --git a/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue b/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue
index e5c0d1e4970..f60be52d6ca 100644
--- a/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue
+++ b/app/assets/javascripts/custom_metrics/components/custom_metrics_form.vue
@@ -66,7 +66,7 @@ export default {
</script>
<template>
<div class="row my-3">
- <h4 class="prepend-top-0 col-lg-8 offset-lg-2">{{ titleText }}</h4>
+ <h4 class="gl-mt-0 col-lg-8 offset-lg-2">{{ titleText }}</h4>
<form ref="form" class="col-lg-8 offset-lg-2" :action="customMetricsPath" method="post">
<custom-metrics-form-fields
:form-operation="formOperation"
diff --git a/app/assets/javascripts/pipelines/components/dag/dag.vue b/app/assets/javascripts/pipelines/components/dag/dag.vue
new file mode 100644
index 00000000000..297affcd41f
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/dag/dag.vue
@@ -0,0 +1,65 @@
+<script>
+import { GlAlert } from '@gitlab/ui';
+import axios from '~/lib/utils/axios_utils';
+
+export default {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ name: 'Dag',
+ components: {
+ GlAlert,
+ },
+ props: {
+ graphUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ showFailureAlert: false,
+ };
+ },
+ computed: {
+ shouldDisplayGraph() {
+ return !this.showFailureAlert;
+ },
+ },
+ mounted() {
+ const { drawGraph, reportFailure } = this;
+
+ if (!this.graphUrl) {
+ reportFailure();
+ return;
+ }
+
+ axios
+ .get(this.graphUrl)
+ .then(response => {
+ drawGraph(response.data);
+ })
+ .catch(reportFailure);
+ },
+ methods: {
+ drawGraph(data) {
+ return data;
+ },
+ hideAlert() {
+ this.showFailureAlert = false;
+ },
+ reportFailure() {
+ this.showFailureAlert = true;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-alert v-if="showFailureAlert" variant="danger" @dismiss="hideAlert">
+ {{ __('We are currently unable to fetch data for this graph.') }}
+ </gl-alert>
+ <div v-if="shouldDisplayGraph" data-testid="dag-graph-container">
+ <!-- graph goes here -->
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index 01295874e56..e5ac7775476 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -4,6 +4,7 @@ import Translate from '~/vue_shared/translate';
import { __ } from '~/locale';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
import pipelineGraph from './components/graph/graph_component.vue';
+import Dag from './components/dag/dag.vue';
import GraphBundleMixin from './mixins/graph_pipeline_bundle_mixin';
import PipelinesMediator from './pipeline_details_mediator';
import pipelineHeader from './components/header_component.vue';
@@ -144,6 +145,25 @@ const createTestDetails = detailsEndpoint => {
.catch(() => {});
};
+const createDagApp = () => {
+ const el = document.querySelector('#js-pipeline-dag-vue');
+ const graphUrl = el.dataset?.pipelineDataPath;
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ Dag,
+ },
+ render(createElement) {
+ return createElement('dag', {
+ props: {
+ graphUrl,
+ },
+ });
+ },
+ });
+};
+
export default () => {
const { dataset } = document.querySelector('.js-pipeline-details-vue');
const mediator = new PipelinesMediator({ endpoint: dataset.endpoint });
@@ -153,4 +173,5 @@ export default () => {
createPipelineHeaderApp(mediator);
createPipelinesTabs(dataset);
createTestDetails(dataset.testReportsCountEndpoint);
+ createDagApp();
};
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index 51cd5810ac0..67abde0c22a 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -1,17 +1,12 @@
<script>
import { sprintf, s__ } from '../../../locale';
+import { joinPaths } from '~/lib/utils/url_utility';
export default {
name: 'TimeTrackingHelpState',
- props: {
- rootPath: {
- type: String,
- required: true,
- },
- },
computed: {
href() {
- return `${this.rootPath}help/workflow/time_tracking.md`;
+ return joinPaths(gon.relative_url_root || '', '/help/user/project/time_tracking.md');
},
estimateText() {
return sprintf(
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 1e8a31fff81..5cf574e1387 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -42,10 +42,6 @@ export default {
default: false,
required: false,
},
- rootPath: {
- type: String,
- required: true,
- },
},
data() {
return {
@@ -137,7 +133,7 @@ export default {
:limit-to-hours="limitToHours"
/>
<transition name="help-state-toggle">
- <time-tracking-help-state v-if="showHelpState" :root-path="rootPath" />
+ <time-tracking-help-state v-if="showHelpState" />
</transition>
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
index d934463382f..0f5f8f2b53b 100644
--- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -24,7 +24,6 @@ export default class SidebarMilestone {
humanTimeEstimate,
humanTimeSpent,
limitToHours: parseBoolean(limitToHours),
- rootPath: '/',
},
}),
});
diff --git a/app/assets/javascripts/snippets/components/snippet_title.vue b/app/assets/javascripts/snippets/components/snippet_title.vue
index 5267c3748ca..cad07581d94 100644
--- a/app/assets/javascripts/snippets/components/snippet_title.vue
+++ b/app/assets/javascripts/snippets/components/snippet_title.vue
@@ -20,7 +20,7 @@ export default {
</script>
<template>
<div class="snippet-header limited-header-width">
- <h2 class="snippet-title prepend-top-0 mb-3" data-qa-selector="snippet_title">
+ <h2 class="snippet-title gl-mt-0 mb-3" data-qa-selector="snippet_title">
{{ snippet.title }}
</h2>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 3b2c161224f..90a6f471374 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -396,7 +396,6 @@ img.emoji {
🚨 Do not use these classes — they are deprecated and being removed. 🚨
See https://gitlab.com/gitlab-org/gitlab/-/issues/217418 for more details.
**/
-.prepend-top-0 { margin-top: 0; }
.prepend-top-2 { margin-top: 2px; }
.prepend-top-4 { margin-top: $gl-padding-4; }
.prepend-top-5 { margin-top: 5px; }
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 5f4d88c57e9..4d774123ef1 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -10,7 +10,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:prometheus_computed_alerts)
end
- before_action :authorize_read_environment!
+ before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect]
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update, :cancel_auto_stop]
diff --git a/app/graphql/resolvers/base_resolver.rb b/app/graphql/resolvers/base_resolver.rb
index cf0642930ad..7daff68c069 100644
--- a/app/graphql/resolvers/base_resolver.rb
+++ b/app/graphql/resolvers/base_resolver.rb
@@ -3,27 +3,33 @@
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
extend ::Gitlab::Utils::Override
+ include ::Gitlab::Utils::StrongMemoize
def self.single
@single ||= Class.new(self) do
+ def ready?(**args)
+ ready, early_return = super
+ [ready, select_result(early_return)]
+ end
+
def resolve(**args)
- super.first
+ select_result(super)
end
def single?
true
end
+
+ def select_result(results)
+ results&.first
+ end
end
end
def self.last
- @last ||= Class.new(self) do
- def resolve(**args)
- super.last
- end
-
- def single?
- true
+ @last ||= Class.new(self.single) do
+ def select_result(results)
+ results&.last
end
end
end
@@ -59,6 +65,17 @@ module Resolvers
end
end
+ def synchronized_object
+ strong_memoize(:synchronized_object) do
+ case object
+ when BatchLoader::GraphQL
+ object.sync
+ else
+ object
+ end
+ end
+ end
+
def single?
false
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index d743ea6aeea..79546212bcc 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -410,7 +410,7 @@ module ProjectsHelper
nav_tabs << :pipelines
end
- if can?(current_user, :read_environment, project) || can?(current_user, :read_cluster, project)
+ if can_view_operations_tab?(current_user, project)
nav_tabs << :operations
end
@@ -438,22 +438,29 @@ module ProjectsHelper
def tab_ability_map
{
- environments: :read_environment,
- milestones: :read_milestone,
- snippets: :read_snippet,
- settings: :admin_project,
- builds: :read_build,
- clusters: :read_cluster,
- serverless: :read_cluster,
- error_tracking: :read_sentry_issue,
- alert_management: :read_alert_management_alert,
- labels: :read_label,
- issues: :read_issue,
- project_members: :read_project_member,
- wiki: :read_wiki
+ environments: :read_environment,
+ metrics_dashboards: :metrics_dashboard,
+ milestones: :read_milestone,
+ snippets: :read_snippet,
+ settings: :admin_project,
+ builds: :read_build,
+ clusters: :read_cluster,
+ serverless: :read_cluster,
+ error_tracking: :read_sentry_issue,
+ alert_management: :read_alert_management_alert,
+ labels: :read_label,
+ issues: :read_issue,
+ project_members: :read_project_member,
+ wiki: :read_wiki
}
end
+ def can_view_operations_tab?(current_user, project)
+ [:read_environment, :read_cluster, :metrics_dashboard].any? do |ability|
+ can?(current_user, ability, project)
+ end
+ end
+
def search_tab_ability_map
@search_tab_ability_map ||= tab_ability_map.merge(
blobs: :download_code,
diff --git a/app/models/project.rb b/app/models/project.rb
index ff39218460d..c9e6a8a49e8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1267,16 +1267,7 @@ class Project < ApplicationRecord
def find_or_initialize_service(name)
return if disabled_services.include?(name)
- service = find_service(services, name)
- return service if service
-
- template = find_service(services_templates, name)
-
- if template
- Service.build_from_integration(id, template)
- else
- public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
- end
+ find_service(services, name) || build_from_instance_or_template(name) || public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
end
# rubocop: disable CodeReuse/ServiceClass
@@ -2444,6 +2435,22 @@ class Project < ApplicationRecord
services.find { |service| service.to_param == name }
end
+ def build_from_instance_or_template(name)
+ instance = find_service(services_instances, name)
+ return Service.build_from_integration(id, instance) if instance
+
+ template = find_service(services_templates, name)
+ return Service.build_from_integration(id, template) if template
+ end
+
+ def services_templates
+ @services_templates ||= Service.templates
+ end
+
+ def services_instances
+ @services_instances ||= Service.instances
+ end
+
def closest_namespace_setting(name)
namespace.closest_setting(name)
end
@@ -2572,10 +2579,6 @@ class Project < ApplicationRecord
end
end
- def services_templates
- @services_templates ||= Service.where(template: true)
- end
-
def ensure_pages_metadatum
pages_metadatum || create_pages_metadatum!
rescue ActiveRecord::RecordNotUnique
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index a24c0471d6c..44de17121de 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -278,7 +278,6 @@ class ProjectPolicy < BasePolicy
rule { can?(:metrics_dashboard) }.policy do
enable :read_prometheus
- enable :read_environment
enable :read_deployment
end
@@ -429,27 +428,11 @@ class ProjectPolicy < BasePolicy
rule { builds_disabled | repository_disabled }.policy do
prevent(*create_read_update_admin_destroy(:build))
prevent(*create_read_update_admin_destroy(:pipeline_schedule))
+ prevent(*create_read_update_admin_destroy(:environment))
prevent(*create_read_update_admin_destroy(:cluster))
prevent(*create_read_update_admin_destroy(:deployment))
end
- # Enabling `read_environment` specifically for the condition of `metrics_dashboard_allowed` is
- # necessary due to the route for metrics dashboard requiring an environment id.
- # This will be addressed in https://gitlab.com/gitlab-org/gitlab/-/issues/213833 when
- # environments and metrics are decoupled and these rules will be removed.
-
- rule { (builds_disabled | repository_disabled) & ~metrics_dashboard_allowed}.policy do
- prevent(*create_read_update_admin_destroy(:environment))
- end
-
- rule { (builds_disabled | repository_disabled) & metrics_dashboard_allowed}.policy do
- prevent :create_environment
- prevent :update_environment
- prevent :admin_environment
- prevent :destroy_environment
- enable :read_environment
- end
-
# There's two separate cases when builds_disabled is true:
# 1. When internal CI is disabled - builds_disabled && internal_builds_disabled
# - We do not prevent the user from accessing Pipelines to allow them to access external CI
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 5bb05bcba26..aa47daf4a57 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -6,7 +6,7 @@
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0 Navigation bar
+ %h4.gl-mt-0 Navigation bar
.col-lg-8
.form-group
@@ -25,7 +25,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0 Favicon
+ %h4.gl-mt-0 Favicon
.col-lg-8
.form-group
@@ -49,7 +49,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0 Sign in/Sign up pages
+ %h4.gl-mt-0 Sign in/Sign up pages
.col-lg-8
.form-group
@@ -77,7 +77,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0 New project pages
+ %h4.gl-mt-0 New project pages
.col-lg-8
.form-group
@@ -90,7 +90,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0 Profile image guideline
+ %h4.gl-mt-0 Profile image guideline
.col-lg-8
.form-group
diff --git a/app/views/admin/appearances/_system_header_footer_form.html.haml b/app/views/admin/appearances/_system_header_footer_form.html.haml
index 4301ebd05af..7f53b2baa32 100644
--- a/app/views/admin/appearances/_system_header_footer_form.html.haml
+++ b/app/views/admin/appearances/_system_header_footer_form.html.haml
@@ -3,7 +3,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('System header and footer')
.col-lg-8
diff --git a/app/views/admin/hook_logs/_index.html.haml b/app/views/admin/hook_logs/_index.html.haml
index 1d7c9930b6a..841640efad2 100644
--- a/app/views/admin/hook_logs/_index.html.haml
+++ b/app/views/admin/hook_logs/_index.html.haml
@@ -1,6 +1,6 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
Recent Deliveries
%p When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.
.col-lg-9
diff --git a/app/views/clusters/clusters/_sidebar.html.haml b/app/views/clusters/clusters/_sidebar.html.haml
index 60ccad5b943..24a74c59b97 100644
--- a/app/views/clusters/clusters/_sidebar.html.haml
+++ b/app/views/clusters/clusters/_sidebar.html.haml
@@ -1,4 +1,4 @@
-%h4.prepend-top-0
+%h4.gl-mt-0
= s_('ClusterIntegration|Add a Kubernetes cluster integration')
%p
= clusterable.sidebar_text
diff --git a/app/views/devise/confirmations/almost_there.haml b/app/views/devise/confirmations/almost_there.haml
index 79826a364db..a1fcbea5bf2 100644
--- a/app/views/devise/confirmations/almost_there.haml
+++ b/app/views/devise/confirmations/almost_there.haml
@@ -1,5 +1,5 @@
.well-confirmation.text-center.append-bottom-20
- %h1.prepend-top-0
+ %h1.gl-mt-0
Almost there...
%p.lead.append-bottom-20
Please check your email to confirm your account
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index 5d85d9e431f..9aab1556373 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -3,7 +3,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
- if user_oauth_applications?
@@ -12,7 +12,7 @@
= _("Manage applications that you've authorized to use your account.")
.col-lg-8
- if user_oauth_applications?
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Add new application')
= render 'form', application: @application
%hr
diff --git a/app/views/import/phabricator/new.html.haml b/app/views/import/phabricator/new.html.haml
index 811e126579e..eaa36b9012e 100644
--- a/app/views/import/phabricator/new.html.haml
+++ b/app/views/import/phabricator/new.html.haml
@@ -11,7 +11,7 @@
= form_tag import_phabricator_path, class: 'new_project', method: :post do
= render 'import/shared/new_project_form'
- %h4.prepend-top-0= _('Enter in your Phabricator Server URL and personal access token below')
+ %h4.gl-mt-0= _('Enter in your Phabricator Server URL and personal access token below')
.form-group.row
= label_tag :phabricator_server_url, _('Phabricator Server URL'), class: 'col-form-label col-md-2'
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index a67860e8e2e..c738493507f 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -216,7 +216,7 @@
= _('Operations')
%li.divider.fly-out-top-item
- - if project_nav_tab? :environments
+ - if project_nav_tab? :metrics_dashboards
= nav_link(controller: :environments, action: [:metrics, :metrics_redirect]) do
= link_to metrics_project_environments_path(@project), title: _('Metrics'), class: 'shortcuts-metrics', data: { qa_selector: 'operations_metrics_link' } do
%span
diff --git a/app/views/layouts/terms.html.haml b/app/views/layouts/terms.html.haml
index 1f7cf486b2c..f9ca77e994d 100644
--- a/app/views/layouts/terms.html.haml
+++ b/app/views/layouts/terms.html.haml
@@ -5,7 +5,7 @@
%body{ data: { page: body_data_page } }
.layout-page.terms{ class: page_class }
- .content-wrapper.prepend-top-0
+ .content-wrapper.gl-mt-0
.mobile-overlay
.alert-wrapper
= render "layouts/broadcast"
diff --git a/app/views/profiles/_event_table.html.haml b/app/views/profiles/_event_table.html.haml
index 977ff30d5a6..c65c4fd0d81 100644
--- a/app/views/profiles/_event_table.html.haml
+++ b/app/views/profiles/_event_table.html.haml
@@ -1,4 +1,4 @@
-%h5.prepend-top-0
+%h5.gl-mt-0
= _('History of authentications')
%ul.content-list
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index e6380817c8f..f4a97206a19 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -7,7 +7,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Profiles|Two-Factor Authentication')
%p
= s_("Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)")
@@ -24,7 +24,7 @@
- if display_providers_on_profile?
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Profiles|Social sign-in')
%p
= s_('Profiles|Activate signin with one of the following services')
@@ -34,7 +34,7 @@
- if current_user.can_change_username?
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0.warning-title
+ %h4.gl-mt-0.warning-title
= s_('Profiles|Change username')
%p
= s_('Profiles|Changing your username can have unintended side effects.')
@@ -47,7 +47,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0.danger-title
+ %h4.gl-mt-0.danger-title
= s_('Profiles|Delete account')
.col-lg-8
- if current_user.can_be_removed? && can?(current_user, :destroy_user, current_user)
diff --git a/app/views/profiles/active_sessions/index.html.haml b/app/views/profiles/active_sessions/index.html.haml
index d651319fc3f..6d01d055f0c 100644
--- a/app/views/profiles/active_sessions/index.html.haml
+++ b/app/views/profiles/active_sessions/index.html.haml
@@ -3,7 +3,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize.')
diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml
index 275c0428d34..02aadcc5c8b 100644
--- a/app/views/profiles/audit_log.html.haml
+++ b/app/views/profiles/audit_log.html.haml
@@ -3,7 +3,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('This is a security log of important events involving your account.')
diff --git a/app/views/profiles/chat_names/index.html.haml b/app/views/profiles/chat_names/index.html.haml
index 0c8098a97d5..05870e0e221 100644
--- a/app/views/profiles/chat_names/index.html.haml
+++ b/app/views/profiles/chat_names/index.html.haml
@@ -3,7 +3,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('You can see your chat accounts.')
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index e28c74dd650..e90bda0e187 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -3,12 +3,12 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('Control emails linked to your account')
.col-lg-8
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('Add email address')
= form_for 'email', url: profile_emails_path do |f|
.form-group
@@ -17,7 +17,7 @@
.prepend-top-default
= f.submit _('Add email address'), class: 'btn btn-success', data: { qa_selector: 'add_email_address_button' }
%hr
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
.account-well.append-bottom-default
%ul
diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml
index f9f898a9225..31610e7505b 100644
--- a/app/views/profiles/gpg_keys/index.html.haml
+++ b/app/views/profiles/gpg_keys/index.html.haml
@@ -3,12 +3,12 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('GPG keys allow you to verify signed commits.')
.col-lg-8
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Add a GPG key')
%p.profile-settings-content
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/project/repository/gpg_signed_commits/index.md') }
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index da6aa0fce3a..788c67b3704 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -3,12 +3,12 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('SSH keys allow you to establish a secure connection between your computer and GitLab.')
.col-lg-8
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Add an SSH key')
%p.profile-settings-content
- generate_link_url = help_page_path("ssh/README", anchor: 'generating-a-new-ssh-key-pair')
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 73f6a821b51..498f80aed2b 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -11,14 +11,14 @@
= hidden_field_tag :notification_type, 'global'
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('You can specify notification level per group or per project.')
%p
= _('By default, all projects and groups will use the global notifications setting.')
.col-lg-8
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Global notification settings')
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index af6fa6b1b61..9deaf7f84be 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -4,12 +4,12 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('After a successful password update, you will be redirected to the login page where you can log in with your new password.')
.col-lg-8
- %h5.prepend-top-0
+ %h5.gl-mt-0
- if @user.password_automatically_set
= _('Change your password')
- else
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 81b22d964a5..769502e0026 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -6,7 +6,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= s_('AccessTokens|You can generate a personal access token for each application you use that needs access to the GitLab API.')
@@ -35,7 +35,7 @@
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('AccessTokens|Feed token')
%p
= s_('AccessTokens|Your feed token is used to authenticate you when your RSS reader loads a personalized RSS feed or when your calendar application loads a personalized calendar, and is included in those feed URLs.')
@@ -53,7 +53,7 @@
%hr
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('AccessTokens|Incoming email token')
%p
= s_('AccessTokens|Your incoming email token is used to authenticate you when you create a new issue by email, and is included in your personal project-specific email addresses.')
@@ -71,7 +71,7 @@
%hr
.row.prepend-top-default
.col-lg-4
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('AccessTokens|Static object token')
%p
= s_('AccessTokens|Your static object token is used to authenticate you when repository static objects (e.g. archives, blobs, ...) are being served from an external storage.')
diff --git a/app/views/profiles/preferences/_sourcegraph.html.haml b/app/views/profiles/preferences/_sourcegraph.html.haml
index 20a904694ca..7328d36b0fb 100644
--- a/app/views/profiles/preferences/_sourcegraph.html.haml
+++ b/app/views/profiles/preferences/_sourcegraph.html.haml
@@ -5,7 +5,7 @@
%hr
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Preferences|Integrations')
%p
= s_('Preferences|Customize integrations with third party services.')
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 12d42ce9892..cc44d137848 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -3,7 +3,7 @@
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row prepend-top-default js-preferences-form' } do |f|
.col-lg-4.application-theme
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Preferences|Navigation theme')
%p
= s_('Preferences|Customize the appearance of the application header and navigation sidebar.')
@@ -18,7 +18,7 @@
%hr
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Preferences|Syntax highlighting theme')
%p
= s_('Preferences|This setting allows you to customize the appearance of the syntax.')
@@ -35,7 +35,7 @@
%hr
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('Preferences|Behavior')
%p
= s_('Preferences|This setting allows you to customize the behavior of the system layout and default views.')
@@ -83,7 +83,7 @@
%hr
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('Localization')
%p
= _('Customize language and region related settings.')
@@ -104,7 +104,7 @@
.col-sm-12
%hr
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0= s_('Preferences|Time preferences')
+ %h4.gl-mt-0= s_('Preferences|Time preferences')
%p= s_('Preferences|These settings will update how dates and times are displayed for you.')
.col-lg-8
.form-group
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 43fc9150e99..78fdcdef3c4 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -7,7 +7,7 @@
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_("Profiles|Public Avatar")
%p
- if @user.avatar?
@@ -27,7 +27,7 @@
.clearfix.avatar-image.append-bottom-default
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
- %h5.prepend-top-0= s_("Profiles|Upload new avatar")
+ %h5.gl-mt-0= s_("Profiles|Upload new avatar")
.prepend-top-5.append-bottom-10
%button.btn.js-choose-user-avatar-button{ type: 'button' }= s_("Profiles|Choose file...")
%span.avatar-file-name.prepend-left-default.js-avatar-filename= s_("Profiles|No file chosen")
@@ -40,7 +40,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0= s_("Profiles|Current status")
+ %h4.gl-mt-0= s_("Profiles|Current status")
%p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface.")
.col-lg-8
= f.fields_for :status, @user.status do |status_form|
@@ -71,7 +71,7 @@
%hr
.row.user-time-preferences
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0= s_("Profiles|Time settings")
+ %h4.gl-mt-0= s_("Profiles|Time settings")
%p= s_("Profiles|You can set your current timezone here")
.col-lg-8
-# TODO: might need an entry in user/profile.md to describe some of these settings
@@ -83,7 +83,7 @@
%hr
.row
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_("Profiles|Main settings")
%p
= s_("Profiles|This information will appear on your profile")
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 4a2d0a4f8ce..92386daf696 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -5,7 +5,7 @@
.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
.row.prepend-top-default
.col-lg-4
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('Register Two-Factor Authenticator')
%p
= _('Use an one time password authenticator on your mobile device or computer to enable two-factor authentication (2FA).')
@@ -33,13 +33,13 @@
= raw @qr_code
.col-md-8
.account-well
- %p.prepend-top-0.append-bottom-0
+ %p.gl-mt-0.append-bottom-0
= _("Can't scan the code?")
- %p.prepend-top-0.append-bottom-0
+ %p.gl-mt-0.append-bottom-0
= _('To add the entry manually, provide the following details to the application on your phone.')
- %p.prepend-top-0.append-bottom-0
+ %p.gl-mt-0.append-bottom-0
= _('Account: %{account}') % { account: @account_string }
- %p.prepend-top-0.append-bottom-0
+ %p.gl-mt-0.append-bottom-0
= _('Key: %{key}') %{ key: current_user.otp_secret.scan(/.{4}/).join(' ') }
%p.two-factor-new-manual-content
= _('Time based: Yes')
@@ -57,7 +57,7 @@
.row.prepend-top-default
.col-lg-4
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('Register Universal Two-Factor (U2F) Device')
%p
= _('Use a hardware device to add the second factor of authentication.')
diff --git a/app/views/projects/cleanup/_show.html.haml b/app/views/projects/cleanup/_show.html.haml
index ed3c9890efd..042bd4ed396 100644
--- a/app/views/projects/cleanup/_show.html.haml
+++ b/app/views/projects/cleanup/_show.html.haml
@@ -14,9 +14,9 @@
.settings-content
- url = cleanup_namespace_project_settings_repository_path(@project.namespace, @project)
= form_for @project, url: url, method: :post, authenticity_token: true, html: { class: 'js-requires-input' } do |f|
- %fieldset.prepend-top-0.append-bottom-10
+ %fieldset.gl-mt-0.append-bottom-10
.append-bottom-10
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _("Upload object map")
%button.btn.btn-default.js-choose-file{ type: "button" }
= _("Choose a file")
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 9e06358beba..222fc251c61 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -6,7 +6,7 @@
%div{ class: [("limit-container-width" unless fluid_layout)] }
= render "home_panel"
- %h4.prepend-top-0.append-bottom-8
+ %h4.gl-mt-0.append-bottom-8
= _('The repository for this project is empty')
- if @project.can_current_user_push_code?
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
index 1fbe34cfff3..a62cd368f3a 100644
--- a/app/views/projects/environments/_form.html.haml
+++ b/app/views/projects/environments/_form.html.haml
@@ -1,6 +1,6 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _("Environments")
%p
- link_to_read_more = link_to(_("Read more about environments"), help_page_path("ci/environments"))
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 8a5b08a19c8..7352c3ceec5 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -2,14 +2,14 @@
.row.prepend-top-default
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _("Fork project")
%p
= _("A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project.").html_safe
.col-lg-9
- if @namespaces.present?
.fork-thumbnail-container.js-fork-content
- %h5.prepend-top-0.append-bottom-0.prepend-left-default.append-right-default
+ %h5.gl-mt-0.append-bottom-0.prepend-left-default.append-right-default
= _("Select a namespace to fork the project")
- @namespaces.each do |namespace|
= render 'fork_button', namespace: namespace
diff --git a/app/views/projects/hook_logs/_index.html.haml b/app/views/projects/hook_logs/_index.html.haml
index f3cea6bea68..4d392141937 100644
--- a/app/views/projects/hook_logs/_index.html.haml
+++ b/app/views/projects/hook_logs/_index.html.haml
@@ -1,6 +1,6 @@
.row.prepend-top-32.append-bottom-default
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
Recent Deliveries
%p When an event in GitLab triggers a webhook, you can use the request details to figure out if something went wrong.
.col-lg-9
diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml
index 873fb4d47b7..a6a3f56c28c 100644
--- a/app/views/projects/hook_logs/show.html.haml
+++ b/app/views/projects/hook_logs/show.html.haml
@@ -4,7 +4,7 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
Request details
.col-lg-9
= link_to 'Resend Request', @hook_log.present.retry_path, method: :post, class: "btn btn-default float-right prepend-left-10"
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index c18af6a267b..9a9b9c42ba7 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -9,7 +9,7 @@
= render 'projects/errors'
.row
.col-lg-3.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('New project')
%p
- among_other_things_link = link_to _('among other things'), help_page_path("user/project/index.md", anchor: "projects-features"), target: '_blank'
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index ff1aa73cddb..f022c19d5f1 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -6,14 +6,14 @@
%li.js-pipeline-tab-link
= link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
= _('Pipeline')
- %li.js-builds-tab-link
- = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
- = _('Jobs')
- %span.badge.badge-pill.js-builds-counter= pipeline.total_size
- if dag_pipeline_tab_enabled
%li.js-dag-tab-link
= link_to dag_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-dag', action: 'dag', toggle: 'tab' }, class: 'dag-tab' do
= _('DAG')
+ %li.js-builds-tab-link
+ = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
+ = _('Jobs')
+ %span.badge.badge-pill.js-builds-counter= pipeline.total_size
- if @pipeline.failed_builds.present?
%li.js-failures-tab-link
= link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml
index 1012ceefe93..ffaf118a5e3 100644
--- a/app/views/projects/protected_branches/show.html.haml
+++ b/app/views/projects/protected_branches/show.html.haml
@@ -2,7 +2,7 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
- %h4.prepend-top-0.ref-name
+ %h4.gl-mt-0.ref-name
= @protected_ref.name
.col-lg-9
diff --git a/app/views/projects/protected_tags/show.html.haml b/app/views/projects/protected_tags/show.html.haml
index 86629f1753b..6f4535a0b3f 100644
--- a/app/views/projects/protected_tags/show.html.haml
+++ b/app/views/projects/protected_tags/show.html.haml
@@ -2,7 +2,7 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
- %h4.prepend-top-0.ref-name
+ %h4.gl-mt-0.ref-name
= @protected_ref.name
.col-lg-9.edit_protected_tag
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 3f91bdc4266..05c49a7b6ce 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -1,6 +1,6 @@
.row.prepend-top-default.append-bottom-default
.col-lg-4
- %h4.prepend-top-0
+ %h4.gl-mt-0
= @service.title
- [true, false].each do |value|
- hide_class = 'd-none' if @service.operating? != value
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index 926671845c7..728a52f024f 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -1,6 +1,6 @@
.row
.col-lg-3
- %h4.prepend-top-0
+ %h4.gl-mt-0
= s_('PrometheusService|Metrics')
.row.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
diff --git a/app/views/projects/settings/access_tokens/index.html.haml b/app/views/projects/settings/access_tokens/index.html.haml
index 07784dce677..092f9c2333c 100644
--- a/app/views/projects/settings/access_tokens/index.html.haml
+++ b/app/views/projects/settings/access_tokens/index.html.haml
@@ -6,7 +6,7 @@
.row.prepend-top-default
.col-lg-4.profile-settings-sidebar
- %h4.prepend-top-0
+ %h4.gl-mt-0
= page_title
%p
= _('You can generate an access token scoped to this project for each application to use the GitLab API.')
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index b50f712922f..a1809cecafb 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -4,7 +4,7 @@
= form_errors(@project)
%fieldset.builds-feature
.form-group
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _("Git strategy for pipelines")
%p
= _("Choose between <code>clone</code> or <code>fetch</code> to get the recent application code").html_safe
diff --git a/app/views/projects/triggers/edit.html.haml b/app/views/projects/triggers/edit.html.haml
index 0f74d733c06..e287f05fe6a 100644
--- a/app/views/projects/triggers/edit.html.haml
+++ b/app/views/projects/triggers/edit.html.haml
@@ -2,6 +2,6 @@
.row.prepend-top-default.append-bottom-default
.col-lg-12
- %h4.prepend-top-0
+ %h4.gl-mt-0
Update trigger
= render "form", btn_text: "Save trigger"
diff --git a/app/views/shared/access_tokens/_created_container.html.haml b/app/views/shared/access_tokens/_created_container.html.haml
index f11ef1e01de..c5a18d98b89 100644
--- a/app/views/shared/access_tokens/_created_container.html.haml
+++ b/app/views/shared/access_tokens/_created_container.html.haml
@@ -1,5 +1,5 @@
.created-personal-access-token-container
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Your new %{type}') % { type: type }
.form-group
.input-group
diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml
index cb7f907308f..680626f7880 100644
--- a/app/views/shared/access_tokens/_form.html.haml
+++ b/app/views/shared/access_tokens/_form.html.haml
@@ -1,7 +1,7 @@
- title = local_assigns.fetch(:title, _('Add a %{type}') % { type: type })
- prefix = local_assigns.fetch(:prefix, :personal_access_token)
-%h5.prepend-top-0
+%h5.gl-mt-0
= title
%p.profile-settings-content
= _("Enter the name of your application, and we'll return a unique %{type}.") % { type: type }
diff --git a/app/views/shared/deploy_keys/_index.html.haml b/app/views/shared/deploy_keys/_index.html.haml
index f28e745f4c5..358075b9e44 100644
--- a/app/views/shared/deploy_keys/_index.html.haml
+++ b/app/views/shared/deploy_keys/_index.html.haml
@@ -7,7 +7,7 @@
%p
= _('Deploy keys allow read-only or read-write (if enabled) access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.')
.settings-content
- %h5.prepend-top-0
+ %h5.gl-mt-0
= _('Create a new deploy key for this project')
= render @deploy_keys.form_partial_path
%hr
diff --git a/app/views/shared/deploy_tokens/_index.html.haml b/app/views/shared/deploy_tokens/_index.html.haml
index b0c9c72dfaa..8203b378297 100644
--- a/app/views/shared/deploy_tokens/_index.html.haml
+++ b/app/views/shared/deploy_tokens/_index.html.haml
@@ -10,7 +10,7 @@
.settings-content
- if @new_deploy_token.persisted?
= render 'shared/deploy_tokens/new_deploy_token', deploy_token: @new_deploy_token
- %h5.prepend-top-0
+ %h5.gl-mt-0
= s_('DeployTokens|Add a deploy token')
= render 'shared/deploy_tokens/form', group_or_project: group_or_project, token: @new_deploy_token, presenter: @deploy_tokens
%hr
diff --git a/app/views/shared/deploy_tokens/_new_deploy_token.html.haml b/app/views/shared/deploy_tokens/_new_deploy_token.html.haml
index f295fa82192..a9728dc841f 100644
--- a/app/views/shared/deploy_tokens/_new_deploy_token.html.haml
+++ b/app/views/shared/deploy_tokens/_new_deploy_token.html.haml
@@ -1,6 +1,6 @@
.qa-created-deploy-token-section.created-deploy-token-container.info-well
.well-segment
- %h5.prepend-top-0
+ %h5.gl-mt-0
= s_('DeployTokens|Your New Deploy Token')
.form-group
diff --git a/app/views/shared/file_hooks/_index.html.haml b/app/views/shared/file_hooks/_index.html.haml
index 0e1f41bbbf6..436bd305df1 100644
--- a/app/views/shared/file_hooks/_index.html.haml
+++ b/app/views/shared/file_hooks/_index.html.haml
@@ -2,7 +2,7 @@
.row.prepend-top-default
.col-lg-4
- %h4.prepend-top-0
+ %h4.gl-mt-0
= _('File Hooks')
%p
= _('File hooks are similar to system hooks but are executed as files instead of sending data to a URL.')
diff --git a/app/views/shared/milestones/_deprecation_message.html.haml b/app/views/shared/milestones/_deprecation_message.html.haml
index acd90fa9178..7dc2c205141 100644
--- a/app/views/shared/milestones/_deprecation_message.html.haml
+++ b/app/views/shared/milestones/_deprecation_message.html.haml
@@ -1,7 +1,7 @@
.banner-callout.compact.milestone-deprecation-message.js-milestone-deprecation-message.prepend-top-20
.banner-graphic= image_tag 'illustrations/milestone_removing-page.svg'
.banner-body.prepend-left-10.append-right-10
- %h5.banner-title.prepend-top-0= _('This page will be removed in a future release.')
+ %h5.banner-title.gl-mt-0= _('This page will be removed in a future release.')
%p.milestone-banner-text= _('Use group milestones to manage issues from multiple projects in the same milestone.')
= button_tag _('Promote these project milestones into a group milestone.'), class: 'btn btn-link js-popover-link text-align-left milestone-banner-link'
.milestone-banner-buttons.prepend-top-20= link_to _('Learn more'), help_page_url('user/project/milestones/index', anchor: 'promoting-project-milestones-to-group-milestones'), class: 'btn btn-default', target: '_blank'
diff --git a/app/views/shared/notifications/_custom_notifications.html.haml b/app/views/shared/notifications/_custom_notifications.html.haml
index 0142deb47f8..9bd08c2296f 100644
--- a/app/views/shared/notifications/_custom_notifications.html.haml
+++ b/app/views/shared/notifications/_custom_notifications.html.haml
@@ -16,7 +16,7 @@
= hidden_field_tag("hide_label", true) if hide_label
.row
.col-lg-4
- %h4.prepend-top-0= _('Notification events')
+ %h4.gl-mt-0= _('Notification events')
%p
- notification_link = link_to _('notification emails'), help_page_path('user/profile/notifications'), target: '_blank'
- paragraph = _('Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.') % { notification_link: notification_link.html_safe }
@@ -26,7 +26,7 @@
- next if notification_event_disabled?(event)
- field_id = "#{notifications_menu_identifier("modal", notification_setting)}_notification_setting[#{event}]"
.form-group
- .form-check{ class: ("prepend-top-0" if index == 0) }
+ .form-check{ class: ("gl-mt-0" if index == 0) }
= check_box("notification_setting", event, id: field_id, class: "js-custom-notification-event form-check-input", checked: notification_setting.public_send(event))
%label.form-check-label{ for: field_id }
%strong
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index e663d57ae6a..5998f32e1ac 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -17,7 +17,7 @@
= render "snippets/actions"
.snippet-header.limited-header-width
- %h2.snippet-title.prepend-top-0.mb-3{ data: { qa_selector: 'snippet_title' } }
+ %h2.snippet-title.gl-mt-0.mb-3{ data: { qa_selector: 'snippet_title' } }
= markdown_field(@snippet, :title)
- if @snippet.description.present?
diff --git a/app/views/shared/web_hooks/_title_and_docs.html.haml b/app/views/shared/web_hooks/_title_and_docs.html.haml
index 359f5f34f5b..d74372bcb0f 100644
--- a/app/views/shared/web_hooks/_title_and_docs.html.haml
+++ b/app/views/shared/web_hooks/_title_and_docs.html.haml
@@ -1,4 +1,4 @@
-%h4.prepend-top-0
+%h4.gl-mt-0
= page_title
%p
- link = link_to(hook.pluralized_name, help_page_path(hook.help_path))
diff --git a/changelogs/unreleased/aalakkad-fix-time-tracking-help-link.yml b/changelogs/unreleased/aalakkad-fix-time-tracking-help-link.yml
new file mode 100644
index 00000000000..48a48e34c69
--- /dev/null
+++ b/changelogs/unreleased/aalakkad-fix-time-tracking-help-link.yml
@@ -0,0 +1,5 @@
+---
+title: Fix time_tracking help link
+merge_request: 32552
+author:
+type: fixed
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index c8fa90e94d4..9ebcd70d1f9 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -619,6 +619,31 @@ lot of dependent objects.
To limit the amount of queries performed, we can use `BatchLoader`.
+### Correct use of `Resolver#ready?`
+
+Resolvers have two public API methods as part of the framework: `#ready?(**args)` and `#resolve(**args)`.
+We can use `#ready?` to perform set-up, validation or early-return without invoking `#resolve`.
+
+Good reasons to use `#ready?` include:
+
+- validating mutually exclusive arguments (see [validating arguments](#validating-arguments))
+- Returning `Relation.none` if we know before-hand that no results are possible
+- Performing setup such as initializing instance variables (although consider lazily initialized methods for this)
+
+Implementations of [`Resolver#ready?(**args)`](https://graphql-ruby.org/api-doc/1.10.9/GraphQL/Schema/Resolver#ready%3F-instance_method) should
+return `(Boolean, early_return_data)` as follows:
+
+```ruby
+def ready?(**args)
+ [false, 'have this instead']
+end
+```
+
+For this reason, whenever you call a resolver (mainly in tests - as framework
+abstractions Resolvers should not be considered re-usable, finders are to be
+preferred), remember to call the `ready?` method and check the boolean flag
+before calling `resolve`! An example can be seen in our [`GraphQLHelpers`](https://gitlab.com/gitlab-org/gitlab/-/blob/2d395f32d2efbb713f7bc861f96147a2a67e92f2/spec/support/helpers/graphql_helpers.rb#L20-27).
+
## Mutations
Mutations are used to change any stored values, or to trigger
@@ -785,7 +810,7 @@ def ready?(**args)
end
# Always remember to call `#super`
- super(args)
+ super
end
```
diff --git a/doc/development/telemetry/index.md b/doc/development/telemetry/index.md
index 80b5b3c7d49..aee16e4049a 100644
--- a/doc/development/telemetry/index.md
+++ b/doc/development/telemetry/index.md
@@ -1,3 +1,9 @@
+---
+stage: Growth
+group: Telemetry
+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
+---
+
# Telemetry Guide
At GitLab, we collect telemetry for the purpose of helping us build a better GitLab. Data about how GitLab is used is collected to better understand what parts of GitLab needs improvement and what features to build next. Telemetry also helps our team better understand the reasons why people use GitLab and with this knowledge we are able to make better product decisions.
@@ -19,11 +25,11 @@ Telemetry Guide:
1. [What is Usage Ping](usage_ping.md#what-is-usage-ping)
1. [Usage Ping payload](usage_ping.md#usage-ping-payload)
- 1. [Disabling Usage Ping](usage_ping.md#disabling-usage-ping)
+ 1. [Disable Usage Ping](usage_ping.md#disable-usage-ping)
1. [Usage Ping request flow](usage_ping.md#usage-ping-request-flow)
1. [How Usage Ping works](usage_ping.md#how-usage-ping-works)
1. [Implementing Usage Ping](usage_ping.md#implementing-usage-ping)
- 1. [Developing and testing usage ping](usage_ping.md#developing-and-testing-usage-ping)
+ 1. [Developing and testing Usage Ping](usage_ping.md#developing-and-testing-usage-ping)
[Snowplow Guide](snowplow.md)
@@ -44,7 +50,7 @@ More useful links:
## Our tracking tools
-In this section we will explain the six different technologies we use to gather product usage data.
+We use several different technologies to gather product usage data.
### Snowplow JS (Frontend)
@@ -58,7 +64,7 @@ Snowplow is an enterprise-grade marketing and product analytics platform which h
Usage Ping is a method for GitLab Inc to collect usage data on a GitLab instance. Usage Ping is primarily composed of row counts for different tables in the instance’s database. By comparing these counts month over month (or week over week), we can get a rough sense for how an instance is using the different features within the product. This high-level data is used to help our product, support, and sales teams.
-Read more about how this works in the [Usage Ping guide](usage_ping.md)
+For more details, read the [Usage Ping](usage_ping.md) guide.
### Database import
@@ -72,51 +78,51 @@ System logs are the application logs generated from running the GitLab Rails app
Our different tracking tools allows us to track different types of events. The event types and examples of what data can be tracked are outlined below.
-| Event Type | Snowplow JS (Frontend) | Snowplow Ruby (Backend) | Usage Ping | Database import | Log system |
-| ------ | ------ | ------ | ------ | ------ | ------ |
-| Database counts | ❌ | ❌ | ✅ | ✅ | ❌ |
-| Pageview events | ✅ | ✅ | ❌ | ❌ | ❌ |
-| UI events | ✅ | ❌ | ❌ | ❌ | ❌ |
-| CRUD and API events | ❌ | ✅ | ❌ | ❌ | ❌ |
-| Event funnels | ✅ | ✅ | ❌ | ❌ | ❌ |
-| PostgreSQL Data | ❌ | ❌ | ❌ | ✅ | ❌ |
-| Logs | ❌ | ❌ | ❌ | ❌ | ✅ |
-| External services | ❌ | ❌ | ❌ | ❌ | ❌ |
+| Event Type | Snowplow JS (Frontend) | Snowplow Ruby (Backend) | Usage Ping | Database import | Log system |
+|---------------------|------------------------|-------------------------|---------------------|---------------------|---------------------|
+| Database counts | **{dotted-circle}** | **{dotted-circle}** | **{check-circle}** | **{check-circle}** | **{dotted-circle}** |
+| Pageview events | **{check-circle}** | **{check-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** |
+| UI events | **{check-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** |
+| CRUD and API events | **{dotted-circle}** | **{check-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** |
+| Event funnels | **{check-circle}** | **{check-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** |
+| PostgreSQL Data | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{check-circle}** | **{dotted-circle}** |
+| Logs | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{check-circle}** |
+| External services | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** | **{dotted-circle}** |
### Database counts
-- How many Projects have been created by unique users
-- How many users logged in the past 28 day
+- Number of Projects created by unique users
+- Number of users logged in the past 28 day
-Database counts are row counts for different tables in an instance’s database. These are SQL count queries which have been filtered, grouped, or aggregated which provide high level usage data. The full list of available tables can be found in [structure.sql](https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/structure.sql)
+Database counts are row counts for different tables in an instance’s database. These are SQL count queries which have been filtered, grouped, or aggregated which provide high level usage data. The full list of available tables can be found in [structure.sql](https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/structure.sql).
### Pageview events
-- How many sessions visited the /dashboard/groups page
+- Number of sessions that visited the /dashboard/groups page
### UI Events
-- How many sessions clicked on a button or link
-- How many sessions closed a modal
+- Number of sessions that clicked on a button or link
+- Number of sessions that closed a modal
UI events are any interface-driven actions from the browser including click data.
### CRUD or API events
-- How many Git pushes were made
-- How many GraphQL queries were made
-- How many requests were made to a Rails action or controller.
+- Number of Git pushes
+- Number of GraphQL queries
+- Number of requests to a Rails action or controller
These are backend events that include the creation, read, update, deletion of records, and other events that might be triggered from layers other than those available in the interface.
### Event funnels
-- How many sessions performed action A, B, then C
-- What is our conversion rate from step A to B?
+- Number of sessions that performed action A, B, then C
+- Conversion rate from step A to B
### PostgreSQL data
-These are raw database records which can be explored using business intelligence tools like Sisense. The full list of available tables can be found in [structure.sql](https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/structure.sql)
+These are raw database records which can be explored using business intelligence tools like Sisense. The full list of available tables can be found in [structure.sql](https://gitlab.com/gitlab-org/gitlab/-/blob/master/db/structure.sql).
### Logs
@@ -157,9 +163,9 @@ As shown by the green lines, on GitLab.com system logs flow into GitLab Inc's mo
The differences between GitLab.com and self-managed are summarized below:
-| Environment | Snowplow JS (Frontend) | Snowplow Ruby (Backend) | Usage Ping | Database import | Logs system |
-| ------ | ------ | ------ | ------ | ------ | ------ |
-| GitLab.com | ✅ | ✅ | ✅ | ✅ | ✅ |
-| Self-Managed | ❌(1) | ❌(1) | ✅ | ❌ | ❌ |
+| Environment | Snowplow JS (Frontend) | Snowplow Ruby (Backend) | Usage Ping | Database import | Logs system |
+|--------------|------------------------|-------------------------|--------------------|---------------------|---------------------|
+| GitLab.com | **{check-circle}** | **{check-circle}** | **{check-circle}** | **{check-circle}** | **{check-circle}** |
+| Self-Managed | **{dotted-circle}**(1) | **{dotted-circle}**(1) | **{check-circle}** | **{dotted-circle}** | **{dotted-circle}** |
Note (1): Snowplow JS and Snowplow Ruby are available on self-managed, however, the Snowplow Collector endpoint is set to a self-managed Snowplow Collector which GitLab Inc does not have access to.
diff --git a/doc/development/telemetry/snowplow.md b/doc/development/telemetry/snowplow.md
index b05aeaa3dbb..18ecf2bc506 100644
--- a/doc/development/telemetry/snowplow.md
+++ b/doc/development/telemetry/snowplow.md
@@ -1,3 +1,9 @@
+---
+stage: Growth
+group: Telemetry
+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
+---
+
# Snowplow Guide
This guide provides a details about how Snowplow works. It includes the following sections:
diff --git a/doc/development/telemetry/usage_ping.md b/doc/development/telemetry/usage_ping.md
index e9b959eaa96..6630f8e779e 100644
--- a/doc/development/telemetry/usage_ping.md
+++ b/doc/development/telemetry/usage_ping.md
@@ -1,19 +1,17 @@
-# Usage Ping Guide
+---
+stage: Growth
+group: Telemetry
+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
+---
-> - [Introduced][ee-557] in GitLab Enterprise Edition 8.10.
-> - More statistics [were added][ee-735] in GitLab Enterprise Edition 8.12.
-> - [Moved to GitLab Core][ce-23361] in 9.1.
-> - More statistics [were added][ee-6602] in GitLab Ultimate 11.2.
+# Usage Ping Guide
-This guide provides a details about how usage ping works. It includes the following sections:
+> - Introduced in GitLab Enterprise Edition 8.10.
+> - More statistics were added in GitLab Enterprise Edition 8.12.
+> - Moved to GitLab Core in 9.1.
+> - More statistics were added in GitLab Ultimate 11.2.
-1. [What is Usage Ping](#what-is-usage-ping)
-1. [Usage Ping payload](#usage-ping-payload)
-1. [Disabling Usage Ping](#disabling-usage-ping)
-1. [Usage Ping request flow](#usage-ping-request-flow)
-1. [How Usage Ping works](#how-usage-ping-works)
-1. [Implementing Usage Ping](#implementing-usage-ping)
-1. [Developing and testing usage ping](#developing-and-testing-usage-ping)
+This guide describes Usage Ping's purpose and how it's implemented.
For more information about Telemetry, see:
@@ -27,38 +25,40 @@ More useful links:
- [Data for Product Managers](https://about.gitlab.com/handbook/business-ops/data-team/data-for-product-managers/)
- [Data Infrastructure](https://about.gitlab.com/handbook/business-ops/data-team/data-infrastructure/)
-## What is Usage Ping
+## What is Usage Ping?
-- GitLab sends a weekly payload containing usage data to GitLab Inc. The usage ping uses high-level data to help our product, support, and sales teams. It does not send any project names, usernames, or any other specific data. The information from the usage ping is not anonymous, it is linked to the hostname of the instance. Sending usage ping is optional, and any instance can disable analytics.
+- GitLab sends a weekly payload containing usage data to GitLab Inc. Usage Ping provides high-level data to help our product, support, and sales teams. It does not send any project names, usernames, or any other specific data. The information from the usage ping is not anonymous, it is linked to the hostname of the instance. Sending usage ping is optional, and any instance can disable analytics.
- The usage data is primarily composed of row counts for different tables in the instance’s database. By comparing these counts month over month (or week over week), we can get a rough sense for how an instance is using the different features within the product.
-- Usage ping is important to GitLab as we use it to calculate our and Stage Monthly Active Users (SMAU) which helps us measure the success of our stages and features.
+- Usage ping is important to GitLab as we use it to calculate our Stage Monthly Active Users (SMAU) which helps us measure the success of our stages and features.
- Once usage ping is enabled, GitLab will gather data from the other instances and will be able to show usage statistics of your instance to your users.
-### Why Should We Enable Usage Ping?
+### Why should we enable Usage Ping?
-- The main purpose of Usage Ping is to build a better GitLab. Data about how GitLab is used is collected to better understand feature/stage adoption and usage, which helps us understand how GitLab is adding value and helps our team better understand the reasons why people use GitLab and with this knowledge we are able to make better product decisions.
+- The main purpose of Usage Ping is to build a better GitLab. Data about how GitLab is used is collected to better understand feature/stage adoption and usage, which helps us understand how GitLab is adding value and helps our team better understand the reasons why people use GitLab and with this knowledge we're able to make better product decisions.
- As a benefit of having the usage ping active, GitLab lets you analyze the users’ activities over time of your GitLab installation.
- As a benefit of having the usage ping active, GitLab provides you with The DevOps Score,which gives you an overview of your entire instance’s adoption of Concurrent DevOps from planning to monitoring.
- You will get better, more proactive support. (assuming that our TAMs and support organization used the data to deliver more value)
- You will get insight and advice into how to get the most value out of your investment in GitLab. Wouldn't you want to know that a number of features or values are not being adopted in your organization?
- You get a report that illustrates how you compare against other similar organizations (anonymized), with specific advice and recommendations on how to improve your DevOps processes.
+- Usage Ping is enabled by default. To disable it, see [Disable Usage Ping](#disable-usage-ping).
### Limitations
-- Usage Ping does not track frontend events things like page views, link clicks, or user sessions and only focuses on aggregated backend events.
+- Usage Ping does not track frontend events things like page views, link clicks, or user sessions, and only focuses on aggregated backend events.
- Because of these limitations we recommend instrumenting your products with Snowplow for more detailed analytics on GitLab.com and use Usage Ping to track aggregated backend events on self-managed.
## Usage Ping payload
You can view the exact JSON payload sent to GitLab Inc. in the administration panel. To view the payload:
-1. Navigate to the **Admin Area > Settings > Metrics and profiling**.
+1. Navigate to **Admin Area > Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. Click the **Preview payload** button.
-Here is an example of the payload structure
+<details>
+<summary>Click to view an example of the payload structure.</summary>
-``` json
+```json
{
"uuid": "0000000-0000-0000-0000-000000000000",
"hostname": "example.com",
@@ -247,17 +247,19 @@ Here is an example of the payload structure
}
```
-## Disabling usage ping
+</details>
+
+## Disable Usage Ping
-The usage ping is opt-out. If you want to deactivate this feature, go to the Settings page of your administration panel and uncheck the Usage Ping checkbox.
+To disable Usage Ping in the GitLab UI, go to the **Settings** page of your administration panel and uncheck the **Usage Ping** checkbox.
-To disable the usage ping and prevent it from being configured in future through the administration panel, Omnibus installs can set the following in [`gitlab.rb`](https://docs.gitlab.com/omnibus/settings/configuration.html#configuration-options):
+To disable Usage Ping and prevent it from being configured in the future through the administration panel, Omnibus installs can set the following in [`gitlab.rb`](https://docs.gitlab.com/omnibus/settings/configuration.html#configuration-options):
```ruby
gitlab_rails['usage_ping_enabled'] = false
```
-And source installs can set the following in `gitlab.yml`:
+Source installations can set the following in `gitlab.yml`:
```yaml
production: &base
@@ -267,9 +269,9 @@ production: &base
usage_ping_enabled: false
```
-## Usage Ping Request Flow
+## Usage Ping request flow
-The following example shows a basic request/response flow between a GitLab Instance, the Versions Application, the License Application, Salesforce, GitLab's S3 Bucket, GitLab's Snowflake Data Warehouse, and Sisense.:
+The following example shows a basic request/response flow between a GitLab instance, the Versions Application, the License Application, Salesforce, GitLab's S3 Bucket, GitLab's Snowflake Data Warehouse, and Sisense:
```mermaid
sequenceDiagram
@@ -317,13 +319,14 @@ Usage Ping consists of four types of counters which are all found in `usage_data
- **Alternative Counters:** Used for settings and configurations
- **Redis Counters:** Used for in-memory counts. This method is being deprecated due to data inaccuracies and will be replaced with a persistent method.
-Note: Only use the provided counter methods. Each counter method contains a built in fail safe to isolate each counter to avoid breaking the entire Usage Ping.
+NOTE: **Note:**
+Only use the provided counter methods. Each counter method contains a built in fail safe to isolate each counter to avoid breaking the entire Usage Ping.
### Why batch counting
For large tables, PostgreSQL can take a long time to count rows due to MVCC [(Multi-version Concurrency Control)](https://en.wikipedia.org/wiki/Multiversion_concurrency_control). Batch counting is a counting method where a single large query is broken into multiple smaller queries. For example, instead of a single query querying 1,000,000 records, with batch counting, you can execute 100 queries of 10,000 records each. Batch counting is useful for avoiding database timeouts as each batch query is significantly shorter than one single long running query.
-For GitLab.com, there are extremely large tables with 15 second query timeouts, so, we use batch counting to avoid encountering timeouts. Here are the sizes of some GitLab.com tables:
+For GitLab.com, there are extremely large tables with 15 second query timeouts, so we use batch counting to avoid encountering timeouts. Here are the sizes of some GitLab.com tables:
| Table | Row counts in millions |
| ------ | ------ |
@@ -413,8 +416,8 @@ Method: `alt_usage_data(value = nil, fallback: -1, &block)`
Arguments:
-- `value`: a simple static value in wich case the value is simply returned.
-- or a `block`: wich is evaluated
+- `value`: a simple static value in which case the value is simply returned.
+- or a `block`: which is evaluated
- `fallback: -1`: the common value used for any metrics that are failing.
Example of usage:
@@ -441,7 +444,7 @@ Gitlab::UsageData.distinct_count(::Note.with_suggestions.where(time_period), :au
### 2. Generate the SQL query
-Your Rails console will give back the generated SQL queries.
+Your Rails console will return the generated SQL queries.
Example:
@@ -457,27 +460,21 @@ Example:
Paste the SQL query into `#database-lab` to see how the query performs at scale.
-- #database-lab is a Slack channel which uses a production-sized environment to test your queries
+- `#database-lab` is a Slack channel which uses a production-sized environment to test your queries.
- GitLab.com’s production database has a 15 second timeout.
-- For each query we require an execution time of under 1 second due do cold caches which can 10x this time.
-- Add a specialized index on columns involved to reduce your the execution time.
-
-In order to have an understanding of the queries execution we add in the MR description the following information
-
-For counters that have a `time_period` test and add information for both cases.
-
-- with `time_period = {}` for all time period
-- and `time_period = { created_at: 28.days.ago..Time.current }` for last 28 days period
-
-Execution plan and query time before and after optimization
-
-Using database-lab and [explain.depesz.com](https://explain.depesz.com/) see more details in [database review guide](../database_review.md#preparation-when-adding-or-modifying-queries)
+- For each query we require an execution time of under 1 second due to cold caches which can 10x this time.
+- Add a specialized index on columns involved to reduce the execution time.
-Query generated for the index and time
+In order to have an understanding of the query's execution we add in the MR description the following information:
-Using database-lab
+- For counters that have a `time_period` test we add information for both cases:
+ - `time_period = {}` for all time periods
+ - `time_period = { created_at: 28.days.ago..Time.current }` for last 28 days period
+- Execution plan and query time before and after optimization
+- Query generated for the index and time
+- Migration output for up and down execution
-Migration output for up and down execution
+We also use `#database-lab` and [explain.depesz.com](https://explain.depesz.com/). For more details, see the [database review guide](../database_review.md#preparation-when-adding-or-modifying-queries).
Examples of query optimization work:
@@ -486,4 +483,4 @@ Examples of query optimization work:
### 4. Ask for a Telemetry Review
-On GitLab.com, we have DangerBot setup to monitor Telemetry related files and DangerBot will recommend a Telemetry review. Simply `@gitlab-org/growth/telemetry/engineers` in your MR for a review.
+On GitLab.com, we have DangerBot setup to monitor Telemetry related files and DangerBot will recommend a Telemetry review. Mention `@gitlab-org/growth/telemetry/engineers` in your MR for a review.
diff --git a/doc/user/infrastructure/index.md b/doc/user/infrastructure/index.md
index b8aa1e36755..1166e70fa56 100644
--- a/doc/user/infrastructure/index.md
+++ b/doc/user/infrastructure/index.md
@@ -22,136 +22,147 @@ Amazon S3 or Google Cloud Storage. Its features include:
- Locking and unlocking state.
- Remote Terraform plan and apply execution.
-To get started, there are two different options when using GitLab managed Terraform State.
+To get started with a GitLab-managed Terraform State, there are two different options:
-- Use a local machine
-- Use GitLab CI
+- [Use a local machine](#get-started-using-local-development).
+- [Use GitLab CI](#get-started-using-a-gitlab-ci).
-## Get Started using local development
+## Get started using local development
-If you are planning to only run `terraform plan` and `terraform apply` commands from your local machine, this is a simple way to get started.
+If you plan to only run `terraform plan` and `terraform apply` commands from your
+local machine, this is a simple way to get started:
-First, create your project on your GitLab instance.
+1. Create your project on your GitLab instance.
+1. Navigate to **{settings}** **Settings > General** and note your **Project name**
+ and **Project ID**.
+1. Define the Terraform backend in your Terraform project to be:
-Next, define the Terraform backend in your Terraform project to be:
-
-```hcl
-terraform {
- backend "http" {
- }
-}
-```
-
-Finally, you need to run `terraform init` on your local machine and pass in the following options. The below example is using GitLab.com:
-
-```shell
-terraform init \
- -backend-config="address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>" \
- -backend-config="lock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>/lock" \
- -backend-config="unlock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>/lock" \
- -backend-config="username=<YOUR-USERNAME>" \
- -backend-config="password=<YOUR-ACCESS-TOKEN>" \
- -backend-config="lock_method=POST" \
- -backend-config="unlock_method=DELETE" \
- -backend-config="retry_wait_min=5"
-```
-
-This will initialize your Terraform state and store that state within your GitLab project.
-
-NOTE: YOUR-PROJECT-ID and YOUR-PROJECT-NAME can be accessed from the project main page.
-
-## Get Started using a GitLab CI
-
-Another route is to leverage GitLab CI to run your `terraform plan` and `terraform apply` commands.
-
-### Configure the CI variables
+ ```hcl
+ terraform {
+ backend "http" {
+ }
+ }
+ ```
-To use the Terraform backend, [first create a Personal Access Token](../profile/personal_access_tokens.md) with the `api` scope. Keep in mind that the Terraform backend is restricted to tokens with [Maintainer access](../permissions.md) to the repository.
+1. On your local machine, run `terraform init`, passing in the following options,
+ replacing `<YOUR-PROJECT-NAME>` and `<YOUR-PROJECT-ID>` with the values for
+ your project. This command initializes your Terraform state, and stores that
+ state within your GitLab project. This example uses `gitlab.com`:
+
+ ```shell
+ terraform init \
+ -backend-config="address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>" \
+ -backend-config="lock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>/lock" \
+ -backend-config="unlock_address=https://gitlab.com/api/v4/projects/<YOUR-PROJECT-ID>/terraform/state/<YOUR-PROJECT-NAME>/lock" \
+ -backend-config="username=<YOUR-USERNAME>" \
+ -backend-config="password=<YOUR-ACCESS-TOKEN>" \
+ -backend-config="lock_method=POST" \
+ -backend-config="unlock_method=DELETE" \
+ -backend-config="retry_wait_min=5"
+ ```
-To keep the Personal Access Token secure, add it as a [CI/CD environment variable](../../ci/variables/README.md). In this example we set ours to the ENV: `GITLAB_TF_PASSWORD`.
+Next, [configure the backend](#configure-the-variables-and-backend).
-If you are planning to use the ENV on a branch which is not protected, make sure to set the variable protection settings correctly.
+## Get started using a GitLab CI
-### Configure the Terraform backend
+If you don't want to start with local development, you can also use GitLab CI to
+run your `terraform plan` and `terraform apply` commands.
-Next we need to define the [http backend](https://www.terraform.io/docs/backends/types/http.html). In your Terraform project add the following code block in a `.tf` file such as `backend.tf` or wherever you desire to define the remote backend:
+Next, [configure the backend](#configure-the-variables-and-backend).
-```hcl
-terraform {
- backend "http" {
- }
-}
-```
+## Configure the variables and backend
-### Configure the CI YAML file
+After executing the `terraform init` command, you must configure the needed CI
+variables, the Terraform backend, and the CI YAML file:
-Finally, configure a `.gitlab-ci.yaml`, which lives in the root of your project repository.
+1. Create a [Personal Access Token](../profile/personal_access_tokens.md) with
+ the `api` scope. The Terraform backend is restricted to tokens with
+ [Maintainer access](../permissions.md) to the repository.
+1. To keep the Personal Access Token secure, add it as a
+ [CI/CD environment variable](../../ci/variables/README.md). For the examples on
+ this page, it's set to the environment variable `GITLAB_TF_PASSWORD`.
-In our case we are using a pre-built image:
+ CAUTION: **Important:**
+ If you plan to use the environment variable on an unprotected branch, make sure
+ to set the variable protection settings correctly.
+1. In your Terraform project, define the [HTTP backend](https://www.terraform.io/docs/backends/types/http.html)
+ by adding the following code block in a `.tf` file (such as `backend.tf`) to
+ define the remote backend:
-```yaml
-image:
- name: hashicorp/terraform:light
- entrypoint:
- - '/usr/bin/env'
- - 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
-```
-
-We then define some environment variables to make life easier. `GITLAB_TF_ADDRESS` is the URL of the GitLab instance where this pipeline runs, and `TF_ROOT` is the directory where the Terraform commands must be executed.
-
-```yaml
-variables:
- GITLAB_TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
- TF_ROOT: ${CI_PROJECT_DIR}/environments/cloudflare/production
-
-cache:
- paths:
- - .terraform
-```
+ ```hcl
+ terraform {
+ backend "http" {
+ }
+ }
+ ```
-In a `before_script`, pass a `terraform init` call containing configuration parameters.
-These parameters correspond to variables required by the
-[http backend](https://www.terraform.io/docs/backends/types/http.html):
+1. In the root directory of your project repository, configure a `.gitlab-ci.yaml` file.
+ This example uses a pre-built image:
-```yaml
-before_script:
- - cd ${TF_ROOT}
- - terraform --version
- - terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=${GITLAB_USER_LOGIN}" -backend-config="password=${GITLAB_TF_PASSWORD}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"
+ ```yaml
+ image:
+ name: hashicorp/terraform:light
+ entrypoint:
+ - '/usr/bin/env'
+ - 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+ ```
-stages:
- - validate
- - build
- - test
- - deploy
+1. In the `.gitlab-ci.yaml` file, define some environment variables to ease development. In this
+ example, `GITLAB_TF_ADDRESS` is the URL of the GitLab instance where this pipeline
+ runs, and `TF_ROOT` is the directory where the Terraform commands must be executed:
-validate:
- stage: validate
- script:
- - terraform validate
+ ```yaml
+ variables:
+ GITLAB_TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
+ TF_ROOT: ${CI_PROJECT_DIR}/environments/cloudflare/production
-plan:
- stage: build
- script:
- - terraform plan
- - terraform show
+ cache:
+ paths:
+ - .terraform
+ ```
-apply:
- stage: deploy
- environment:
- name: production
- script:
- - terraform apply
- dependencies:
- - plan
- when: manual
- only:
- - master
-```
+1. In a `before_script`, pass a `terraform init` call containing configuration parameters
+ corresponding to variables required by the
+ [HTTP backend](https://www.terraform.io/docs/backends/types/http.html):
-### Push to GitLab
+ ```yaml
+ before_script:
+ - cd ${TF_ROOT}
+ - terraform --version
+ - terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=${GITLAB_USER_LOGIN}" -backend-config="password=${GITLAB_TF_PASSWORD}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"
+
+ stages:
+ - validate
+ - build
+ - test
+ - deploy
+
+ validate:
+ stage: validate
+ script:
+ - terraform validate
+
+ plan:
+ stage: build
+ script:
+ - terraform plan
+ - terraform show
+
+ apply:
+ stage: deploy
+ environment:
+ name: production
+ script:
+ - terraform apply
+ dependencies:
+ - plan
+ when: manual
+ only:
+ - master
+ ```
-Pushing your project to GitLab triggers a CI job pipeline, which runs the `terraform init`, `terraform validate`, and `terraform plan` commands automatically.
+1. Push your project to GitLab, which triggers a CI job pipeline. This pipeline runs
+ the `terraform init`, `terraform validate`, and `terraform plan` commands.
The output from the above `terraform` commands should be viewable in the job logs.
@@ -161,14 +172,14 @@ See [this reference project](https://gitlab.com/nicholasklick/gitlab-terraform-a
## Output Terraform Plan information into a merge request
-Using the [GitLab Terraform Report Artifact](../../ci/pipelines/job_artifacts.md#artifactsreportsterraform),
+Using the [GitLab Terraform Report artifact](../../ci/pipelines/job_artifacts.md#artifactsreportsterraform),
you can expose details from `terraform plan` runs directly into a merge request widget,
enabling you to see statistics about the resources that Terraform will create,
modify, or destroy.
-Let's explore how to configure a GitLab Terraform Report Artifact:
+Let's explore how to configure a GitLab Terraform Report artifact:
-1. First, for simplicity, let's define a few reusable variables to allow us to
+1. For simplicity, let's define a few reusable variables to allow us to
refer to these files multiple times:
```yaml
@@ -177,96 +188,39 @@ Let's explore how to configure a GitLab Terraform Report Artifact:
PLAN_JSON: tfplan.json
```
-1. Next we need to install `jq`, a [lightweight and flexible command-line JSON processor](https://stedolan.github.io/jq/). We will also create an alias for a specific `jq` command that parses out the extact information we want to extract from the `terraform plan` output:
-
-```yaml
-before_script:
- - apk --no-cache add jq
- - alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
-```
-
-1. Finally, we define a `script` that runs `terraform plan` and also a `terraform show` which pipes the output and converts the relevant bits into a store variable `PLAN_JSON`. This json is then leveraged to create a [GitLab Terraform Report Artifact](../../ci/pipelines/job_artifacts.md#artifactsreportsterraform).
-
-The terraform report obtains a Terraform tfplan.json file. The collected Terraform plan report will be uploaded to GitLab as an artifact and will be automatically shown in merge requests.
-
-```yaml
-plan:
- stage: build
- script:
- - terraform plan -out=$PLAN
- - terraform show --json $PLAN | convert_report > $PLAN_JSON
- artifacts:
- name: plan
- paths:
- - $PLAN
- reports:
- terraform: $PLAN_JSON
-```
-
-A full `.gitlab-ci.yaml` file could look like this:
-
-```yaml
-image:
- name: hashicorp/terraform:light
- entrypoint:
- - '/usr/bin/env'
- - 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
-
-# Default output file for Terraform plan
-variables:
- GITLAB_TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
- PLAN: plan.tfplan
- PLAN_JSON: tfplan.json
- TF_ROOT: ${CI_PROJECT_DIR}
-
-cache:
- paths:
- - .terraform
-
-before_script:
- - apk --no-cache add jq
- - alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
- - cd ${TF_ROOT}
- - terraform --version
- - terraform init -backend-config="address=${GITLAB_TF_ADDRESS}" -backend-config="lock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="unlock_address=${GITLAB_TF_ADDRESS}/lock" -backend-config="username=${GITLAB_USER_LOGIN}" -backend-config="password=${GITLAB_TF_PASSWORD}" -backend-config="lock_method=POST" -backend-config="unlock_method=DELETE" -backend-config="retry_wait_min=5"
-
-stages:
- - validate
- - build
- - deploy
+1. Install `jq`, a
+ [lightweight and flexible command-line JSON processor](https://stedolan.github.io/jq/).
+1. Create an alias for a specific `jq` command that parses out the information we
+ want to extract from the `terraform plan` output:
-validate:
- stage: validate
- script:
- - terraform validate
+ ```yaml
+ before_script:
+ - apk --no-cache add jq
+ - alias convert_report="jq -r '([.resource_changes[]?.change.actions?]|flatten)|{\"create\":(map(select(.==\"create\"))|length),\"update\":(map(select(.==\"update\"))|length),\"delete\":(map(select(.==\"delete\"))|length)}'"
+ ```
-plan:
- stage: build
- script:
- - terraform plan -out=$PLAN
- - terraform show --json $PLAN | convert_report > $PLAN_JSON
- artifacts:
- name: plan
- paths:
- - ${TF_ROOT}/plan.tfplan
- reports:
- terraform: ${TF_ROOT}/tfplan.json
+1. Define a `script` that runs `terraform plan` and `terraform show`. These commands
+ pipe the output and convert the relevant bits into a store variable `PLAN_JSON`.
+ This JSON is used to create a
+ [GitLab Terraform Report artifact](../../ci/pipelines/job_artifacts.md#artifactsreportsterraform).
+ The Terraform report obtains a Terraform `tfplan.json` file. The collected
+ Terraform plan report is uploaded to GitLab as an artifact, and is shown in merge requests.
-# Separate apply job for manual launching Terraform as it can be destructive
-# action.
-apply:
- stage: deploy
- environment:
- name: production
- script:
- - terraform apply -input=false $PLAN
- dependencies:
- - plan
- when: manual
- only:
- - master
+ ```yaml
+ plan:
+ stage: build
+ script:
+ - terraform plan -out=$PLAN
+ - terraform show --json $PLAN | convert_report > $PLAN_JSON
+ artifacts:
+ name: plan
+ paths:
+ - $PLAN
+ reports:
+ terraform: $PLAN_JSON
+ ```
-```
+ For a full example, see [Example `.gitlab-ci.yaml` file](#example-gitlab-ciyaml-file).
1. Running the pipeline displays the widget in the merge request, like this:
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5bdee7e3d6b..fb812d9695c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -24326,6 +24326,9 @@ msgstr ""
msgid "Warning: Displaying this diagram might cause performance issues on this page."
msgstr ""
+msgid "We are currently unable to fetch data for this graph."
+msgstr ""
+
msgid "We could not determine the path to remove the epic"
msgstr ""
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 56fff2771ec..cd4896ede43 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -354,6 +354,19 @@ describe Projects::EnvironmentsController do
expect(response).to redirect_to(environment_metrics_path(environment))
end
+ context 'with anonymous user and public dashboard visibility' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+
+ it 'redirects successfully' do
+ project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
+
+ get :metrics_redirect, params: { namespace_id: project.namespace, project_id: project }
+
+ expect(response).to redirect_to(environment_metrics_path(environment))
+ end
+ end
+
context 'when there are no environments' do
let(:environment) { }
@@ -422,6 +435,19 @@ describe Projects::EnvironmentsController do
get :metrics, params: environment_params
end
end
+
+ context 'with anonymous user and public dashboard visibility' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+
+ it 'returns success' do
+ project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
+
+ get :metrics, params: environment_params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
describe 'GET #additional_metrics' do
@@ -497,6 +523,26 @@ describe Projects::EnvironmentsController do
get :metrics, params: environment_params
end
end
+
+ context 'with anonymous user and public dashboard visibility' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+
+ it 'does not fail' do
+ allow(environment)
+ .to receive(:additional_metrics)
+ .and_return({
+ success: true,
+ data: {},
+ last_update: 42
+ })
+ project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
+
+ additional_metrics(window_params)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
end
describe 'GET #metrics_dashboard' do
@@ -673,6 +719,17 @@ describe Projects::EnvironmentsController do
it_behaves_like 'dashboard can be specified'
it_behaves_like 'dashboard can be embedded'
+ context 'with anonymous user and public dashboard visibility' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+
+ before do
+ project.project_feature.update!(metrics_dashboard_access_level: ProjectFeature::ENABLED)
+ end
+
+ it_behaves_like 'the default dashboard'
+ end
+
context 'permissions' do
before do
allow(controller).to receive(:can?).and_return true
diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js
new file mode 100644
index 00000000000..31ee4e1a0a1
--- /dev/null
+++ b/spec/frontend/pipelines/components/dag/dag_spec.js
@@ -0,0 +1,77 @@
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import { GlAlert } from '@gitlab/ui';
+import Dag from '~/pipelines/components/dag/dag.vue';
+
+describe('Pipeline DAG graph', () => {
+ let wrapper;
+ let axiosMock;
+ const getAlert = () => wrapper.find(GlAlert);
+ const getGraph = () => wrapper.find('[data-testid="dag-graph-container"]');
+
+ const dataPath = 'root/test/pipelines/90/dag.json';
+
+ const createComponent = (propsData = {}, method = mount) => {
+ axiosMock = new MockAdapter(axios);
+
+ if (wrapper?.destroy) {
+ wrapper.destroy();
+ }
+
+ wrapper = method(Dag, {
+ propsData,
+ data() {
+ return {
+ showFailureAlert: false,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ axiosMock.restore();
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when there is no dataUrl', () => {
+ beforeEach(() => {
+ createComponent({ graphUrl: undefined });
+ });
+
+ it('shows the alert and not the graph', () => {
+ expect(getAlert().exists()).toBe(true);
+ expect(getGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('when there is a dataUrl', () => {
+ beforeEach(() => {
+ createComponent({ graphUrl: dataPath });
+ });
+
+ it('shows the graph and not the alert', () => {
+ expect(getAlert().exists()).toBe(false);
+ expect(getGraph().exists()).toBe(true);
+ });
+
+ describe('but the data fetch fails', () => {
+ beforeEach(() => {
+ axiosMock.onGet(dataPath).replyOnce(500);
+ createComponent({ graphUrl: dataPath });
+ });
+
+ it('shows the alert and not the graph', () => {
+ return wrapper.vm
+ .$nextTick()
+ .then(waitForPromises)
+ .then(() => {
+ expect(getAlert().exists()).toBe(true);
+ expect(getGraph().exists()).toBe(false);
+ });
+ });
+ });
+ });
+});
diff --git a/spec/graphql/resolvers/base_resolver_spec.rb b/spec/graphql/resolvers/base_resolver_spec.rb
index 0a21b2797ee..6c384349577 100644
--- a/spec/graphql/resolvers/base_resolver_spec.rb
+++ b/spec/graphql/resolvers/base_resolver_spec.rb
@@ -41,9 +41,35 @@ describe Resolvers::BaseResolver do
end
end
+ context 'when the resolver returns early' do
+ let(:resolver) do
+ Class.new(described_class) do
+ def ready?(**args)
+ [false, %w(early return)]
+ end
+
+ def resolve(**args)
+ raise 'Should not get here'
+ end
+ end
+ end
+
+ it 'runs correctly in our test framework' do
+ expect(resolve(resolver)).to contain_exactly('early', 'return')
+ end
+
+ it 'single selects the first early return value' do
+ expect(resolve(resolver.single)).to eq('early')
+ end
+
+ it 'last selects the last early return value' do
+ expect(resolve(resolver.last)).to eq('return')
+ end
+ end
+
describe '.last' do
it 'returns a subclass from the resolver' do
- expect(last_resolver.last.superclass).to eq(last_resolver)
+ expect(last_resolver.last.ancestors).to include(last_resolver)
end
it 'returns the same subclass every time' do
@@ -95,4 +121,28 @@ describe Resolvers::BaseResolver do
end
end
end
+
+ describe '#synchronized_object' do
+ let(:object) { double(foo: :the_foo) }
+
+ let(:resolver) do
+ Class.new(described_class) do
+ def resolve(**args)
+ [synchronized_object.foo]
+ end
+ end
+ end
+
+ it 'handles raw objects' do
+ expect(resolve(resolver, obj: object)).to contain_exactly(:the_foo)
+ end
+
+ it 'handles lazy objects' do
+ delayed = BatchLoader::GraphQL.for(1).batch do |_, loader|
+ loader.call(1, object)
+ end
+
+ expect(resolve(resolver, obj: delayed)).to contain_exactly(:the_foo)
+ end
+ end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 189ab1a8354..27dc8707bd8 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -5,6 +5,9 @@ require 'spec_helper'
describe ProjectsHelper do
include ProjectForksHelper
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
describe '#project_incident_management_setting' do
let(:project) { create(:project) }
@@ -500,6 +503,23 @@ describe ProjectsHelper do
end
end
+ describe '#can_view_operations_tab?' do
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ subject { helper.send(:can_view_operations_tab?, user, project) }
+
+ [:read_environment, :read_cluster, :metrics_dashboard].each do |ability|
+ it 'includes operations tab' do
+ allow(helper).to receive(:can?).and_return(false)
+ allow(helper).to receive(:can?).with(user, ability, project).and_return(true)
+
+ is_expected.to be(true)
+ end
+ end
+ end
+
describe '#show_projects' do
let(:projects) do
create(:project)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 5f8b51c250d..ec8c03513ce 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -5241,12 +5241,10 @@ describe Project do
end
end
- describe "#find_or_initialize_services" do
- subject { build(:project) }
-
+ describe '#find_or_initialize_services' do
it 'returns only enabled services' do
- allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
- allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+ allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover])
+ allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
services = subject.find_or_initialize_services
@@ -5255,11 +5253,9 @@ describe Project do
end
end
- describe "#find_or_initialize_service" do
- subject { build(:project) }
-
+ describe '#find_or_initialize_service' do
it 'avoids N+1 database queries' do
- allow(Service).to receive(:available_services_names).and_return(%w(prometheus pushover))
+ allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover])
control_count = ActiveRecord::QueryRecorder.new { subject.find_or_initialize_service('prometheus') }.count
@@ -5268,11 +5264,51 @@ describe Project do
expect { subject.find_or_initialize_service('prometheus') }.not_to exceed_query_limit(control_count)
end
- it 'returns nil if service is disabled' do
- allow(subject).to receive(:disabled_services).and_return(%w(prometheus))
+ it 'returns nil if integration is disabled' do
+ allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
expect(subject.find_or_initialize_service('prometheus')).to be_nil
end
+
+ context 'with an existing integration' do
+ subject { create(:project) }
+
+ before do
+ create(:prometheus_service, project: subject, api_url: 'https://prometheus.project.com/')
+ end
+
+ it 'retrieves the integration' do
+ expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.project.com/')
+ end
+ end
+
+ context 'with an instance-level and template integrations' do
+ before do
+ create(:prometheus_service, :instance, api_url: 'https://prometheus.instance.com/')
+ create(:prometheus_service, :template, api_url: 'https://prometheus.template.com/')
+ end
+
+ it 'builds the service from the instance if exists' do
+ expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.instance.com/')
+ end
+ end
+
+ context 'with an instance-level and template integrations' do
+ before do
+ create(:prometheus_service, :template, api_url: 'https://prometheus.template.com/')
+ end
+
+ it 'builds the service from the template if instance does not exists' do
+ expect(subject.find_or_initialize_service('prometheus').api_url).to eq('https://prometheus.template.com/')
+ end
+ end
+
+ context 'without an exisiting integration, nor instance-level or template' do
+ it 'builds the service if instance or template does not exists' do
+ expect(subject.find_or_initialize_service('prometheus')).to be_a(PrometheusService)
+ expect(subject.find_or_initialize_service('prometheus').api_url).to be_nil
+ end
+ end
end
describe '.for_group' do
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 09d54eb9df6..7c98d8cc856 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -218,41 +218,16 @@ describe ProjectPolicy do
project.project_feature.update(builds_access_level: ProjectFeature::DISABLED)
end
- context 'without metrics_dashboard_allowed' do
- before do
- project.project_feature.update(metrics_dashboard_access_level: ProjectFeature::DISABLED)
- end
-
- it 'disallows all permissions except pipeline when the feature is disabled' do
- builds_permissions = [
- :create_build, :read_build, :update_build, :admin_build, :destroy_build,
- :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
- :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
- :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
- ]
-
- expect_disallowed(*builds_permissions)
- end
- end
-
- context 'with metrics_dashboard_allowed' do
- before do
- project.project_feature.update(metrics_dashboard_access_level: ProjectFeature::ENABLED)
- end
+ it 'disallows all permissions except pipeline when the feature is disabled' do
+ builds_permissions = [
+ :create_build, :read_build, :update_build, :admin_build, :destroy_build,
+ :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
+ :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
+ :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
+ :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
+ ]
- it 'disallows all permissions except pipeline and read_environment when the feature is disabled' do
- builds_permissions = [
- :create_build, :read_build, :update_build, :admin_build, :destroy_build,
- :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
- :create_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster, :destroy_cluster,
- :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment
- ]
-
- expect_disallowed(*builds_permissions)
- expect_allowed(:read_environment)
- end
+ expect_disallowed(*builds_permissions)
end
end
@@ -277,49 +252,20 @@ describe ProjectPolicy do
context 'repository feature' do
subject { described_class.new(owner, project) }
- before do
+ it 'disallows all permissions when the feature is disabled' do
project.project_feature.update(repository_access_level: ProjectFeature::DISABLED)
- end
-
- context 'without metrics_dashboard_allowed' do
- before do
- project.project_feature.update(metrics_dashboard_access_level: ProjectFeature::DISABLED)
- end
- it 'disallows all permissions when the feature is disabled' do
- repository_permissions = [
- :create_pipeline, :update_pipeline, :admin_pipeline, :destroy_pipeline,
- :create_build, :read_build, :update_build, :admin_build, :destroy_build,
- :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
- :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster,
- :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment,
- :destroy_release
- ]
-
- expect_disallowed(*repository_permissions)
- end
- end
-
- context 'with metrics_dashboard_allowed' do
- before do
- project.project_feature.update(metrics_dashboard_access_level: ProjectFeature::ENABLED)
- end
-
- it 'disallows all permissions when the feature is disabled' do
- repository_permissions = [
- :create_pipeline, :update_pipeline, :admin_pipeline, :destroy_pipeline,
- :create_build, :read_build, :update_build, :admin_build, :destroy_build,
- :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
- :create_environment, :update_environment, :admin_environment, :destroy_environment,
- :create_cluster, :read_cluster, :update_cluster, :admin_cluster,
- :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment,
- :destroy_release
- ]
-
- expect_disallowed(*repository_permissions)
- expect_allowed(:read_environment)
- end
+ repository_permissions = [
+ :create_pipeline, :update_pipeline, :admin_pipeline, :destroy_pipeline,
+ :create_build, :read_build, :update_build, :admin_build, :destroy_build,
+ :create_pipeline_schedule, :read_pipeline_schedule, :update_pipeline_schedule, :admin_pipeline_schedule, :destroy_pipeline_schedule,
+ :create_environment, :read_environment, :update_environment, :admin_environment, :destroy_environment,
+ :create_cluster, :read_cluster, :update_cluster, :admin_cluster,
+ :create_deployment, :read_deployment, :update_deployment, :admin_deployment, :destroy_deployment,
+ :destroy_release
+ ]
+
+ expect_disallowed(*repository_permissions)
end
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index b3d7f7bcece..7082424b899 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -11,9 +11,19 @@ module GraphqlHelpers
underscored_field_name.to_s.camelize(:lower)
end
- # Run a loader's named resolver
+ # Run a loader's named resolver in a way that closely mimics the framework.
+ #
+ # First the `ready?` method is called. If it turns out that the resolver is not
+ # ready, then the early return is returned instead.
+ #
+ # Then the resolve method is called.
def resolve(resolver_class, obj: nil, args: {}, ctx: {}, field: nil)
- resolver_class.new(object: obj, context: ctx, field: field).resolve(args)
+ resolver = resolver_class.new(object: obj, context: ctx, field: field)
+ ready, early_return = sync_all { resolver.ready?(**args) }
+
+ return early_return unless ready
+
+ resolver.resolve(args)
end
# Eagerly run a loader's named resolver
@@ -51,12 +61,12 @@ module GraphqlHelpers
# BatchLoader::GraphQL returns a wrapper, so we need to :sync in order
# to get the actual values
def batch_sync(max_queries: nil, &blk)
- wrapper = proc do
- lazy_vals = yield
- lazy_vals.is_a?(Array) ? lazy_vals.map { |val| sync(val) } : sync(lazy_vals)
- end
+ batch(max_queries: max_queries) { sync_all(&blk) }
+ end
- batch(max_queries: max_queries, &wrapper)
+ def sync_all(&blk)
+ lazy_vals = yield
+ lazy_vals.is_a?(Array) ? lazy_vals.map { |val| sync(val) } : sync(lazy_vals)
end
def graphql_query_for(name, attributes = {}, fields = nil)
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index 3e2193a9069..e204b9ccd68 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -85,9 +85,16 @@ end
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
+ def expected_names
+ @names ||= Array.wrap(expected).map { |name| GraphqlHelpers.fieldnamerize(name) }
+ end
+
match do |field|
- argument_names = expected.map { |name| GraphqlHelpers.fieldnamerize(name) }
- expect(field.arguments.keys).to contain_exactly(*argument_names)
+ expect(field.arguments.keys).to contain_exactly(*expected_names)
+ end
+
+ failure_message do |field|
+ "expected that #{field.name} would have the following fields: #{expected_names.inspect}, but it has #{field.arguments.keys.inspect}."
end
end
diff --git a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
index 37a504cd56a..37ee2548dfe 100644
--- a/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb
@@ -86,7 +86,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
page.within '.time-tracking-component-wrap' do
find('.help-button').click
- expect(find_link('Learn more')[:href]).to have_content('/help/workflow/time_tracking.md')
+ expect(find_link('Learn more')[:href]).to have_content('/help/user/project/time_tracking.md')
end
end
end