Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-21 18:08:27 +00:00
parent 8d3d7b40ae
commit 34e72e5412
80 changed files with 1136 additions and 526 deletions

View File

@ -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.

View File

@ -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

View File

@ -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"

View File

@ -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>

View File

@ -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();
};

View File

@ -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(

View File

@ -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>

View File

@ -24,7 +24,6 @@ export default class SidebarMilestone {
humanTimeEstimate,
humanTimeSpent,
limitToHours: parseBoolean(limitToHours),
rootPath: '/',
},
}),
});

View File

@ -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>

View File

@ -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; }

View File

@ -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]

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
%h4.prepend-top-0
%h4.gl-mt-0
= s_('ClusterIntegration|Add a Kubernetes cluster integration')
%p
= clusterable.sidebar_text

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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"

View File

@ -1,4 +1,4 @@
%h5.prepend-top-0
%h5.gl-mt-0
= _('History of authentications')
%ul.content-list

View File

@ -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)

View File

@ -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.')

View File

@ -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.')

View File

@ -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.')

View File

@ -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

View File

@ -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') }

View File

@ -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')

View File

@ -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|

View File

@ -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

View File

@ -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.')

View File

@ -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.')

View File

@ -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

View File

@ -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")

View File

@ -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.')

View File

@ -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")

View File

@ -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?

View File

@ -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"))

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.')

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.')

View File

@ -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'

View File

@ -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

View File

@ -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?

View File

@ -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))

View File

@ -0,0 +1,5 @@
---
title: Fix time_tracking help link
merge_request: 32552
author:
type: fixed

View File

@ -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
```

View File

@ -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 instances 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 instances 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 instances 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.

View File

@ -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:

View File

@ -1,19 +1,17 @@
---
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
---
# Usage Ping Guide
> - [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.
> - 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.
This guide provides a details about how usage ping works. It includes the following sections:
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 instances 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 instances 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>
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.
## Disable Usage Ping
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 in the GitLab UI, go to the **Settings** page of your administration panel and uncheck the **Usage Ping** checkbox.
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.coms 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.
- 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.
In order to have an understanding of the queries execution we add in the MR description the following information
In order to have an understanding of the query's execution we add in the MR description the following information:
For counters that have a `time_period` test and add information for both cases.
- 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
- 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)
Query generated for the index and time
Using database-lab
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.

View File

@ -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" {
}
}
```
```hcl
terraform {
backend "http" {
}
}
```
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`:
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"
```
```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"
```
Next, [configure the backend](#configure-the-variables-and-backend).
This will initialize your Terraform state and store that state within your GitLab project.
## Get started using a GitLab CI
NOTE: YOUR-PROJECT-ID and YOUR-PROJECT-NAME can be accessed from the project main page.
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.
## Get Started using a GitLab CI
Next, [configure the backend](#configure-the-variables-and-backend).
Another route is to leverage GitLab CI to run your `terraform plan` and `terraform apply` commands.
## Configure the variables and backend
### Configure the CI variables
After executing the `terraform init` command, you must configure the needed CI
variables, the Terraform backend, and the CI YAML file:
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. 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`.
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`.
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:
If you are planning to use the ENV on a branch which is not protected, make sure to set the variable protection settings correctly.
```hcl
terraform {
backend "http" {
}
}
```
### Configure the Terraform backend
1. In the root directory of your project repository, configure a `.gitlab-ci.yaml` file.
This example uses a pre-built image:
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:
```yaml
image:
name: hashicorp/terraform:light
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
```
```hcl
terraform {
backend "http" {
}
}
```
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:
### Configure the CI YAML file
```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
Finally, configure a `.gitlab-ci.yaml`, which lives in the root of your project repository.
cache:
paths:
- .terraform
```
In our case we are using a pre-built image:
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):
```yaml
image:
name: hashicorp/terraform:light
entrypoint:
- '/usr/bin/env'
- 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
```
```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"
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.
stages:
- validate
- build
- test
- deploy
```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
validate:
stage: validate
script:
- terraform validate
cache:
paths:
- .terraform
```
plan:
stage: build
script:
- terraform plan
- terraform show
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):
apply:
stage: deploy
environment:
name: production
script:
- terraform apply
dependencies:
- plan
when: manual
only:
- master
```
```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
```
### Push to GitLab
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:
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:
```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)}'"
```
```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).
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.
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
```
```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
validate:
stage: validate
script:
- terraform validate
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
# 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
```
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:

View File

@ -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 ""

View File

@ -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

View File

@ -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);
});
});
});
});
});

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
]
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 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
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
]
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
expect_disallowed(*repository_permissions)
end
end

View File

@ -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)

View File

@ -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

View File

@ -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