### Install Dependencies Source: https://backstage.spotify.com/docs/plugins/soundcheck/tutorials/custom-fact-collector-ncui Run this command after updating your package.json to install the necessary dependencies. ```bash yarn install ``` -------------------------------- ### Install RBAC Backend Package (Legacy) Source: https://backstage.spotify.com/docs/plugins/rbac/setup-and-installation For users still on the Legacy Backend System, install the RBAC backend package separately. ```bash yarn workspace backend add @spotify/backstage-plugin-rbac-backend ``` -------------------------------- ### Entity Configuration for Multiple SonarQube Instances Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example of prefixing the 'sonarqube.org/project-key' annotation with the SonarQube instance name for multi-instance setups. ```yaml metadata: annotations: sonarqube.org/project-key: sonarqube-instance1/test-project-key ``` -------------------------------- ### Full Example: App.tsx with Hybrid Migration Source: https://backstage.spotify.com/docs/plugins/soundcheck/tutorials/custom-fact-collector-ncui This example shows the complete App.tsx file after migrating to the hybrid approach, including legacy app conversion and feature registration. ```tsx import React from 'react'; import { createApp } from '@backstage/frontend-defaults'; import { convertLegacyApp } from '@backstage/core-compat-api'; // Import the Soundcheck plugin and your custom integration import soundCheckPlugin from '@spotify/backstage-plugin-soundcheck/alpha'; import examplePagerDutyIntegration from '@internal/backstage-plugin-soundcheck-integrations/alpha'; // Your legacy app routes (excluding the Soundcheck route which is now handled by the new frontend system) const legacyApp = convertLegacyApp(); // ... your legacy routes const app = createApp({ features: [ legacyApp, soundCheckPlugin, examplePagerDutyIntegration, // ... other features ], }); export default app.createRoot(); ``` -------------------------------- ### Initial Delay Configuration Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/data-registry Example of configuring the initial delay before the first invocation of a fact collector using HumanDuration in seconds. ```yaml initialDelay: seconds: 30 ``` -------------------------------- ### Frequency Configuration Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/data-registry Example of configuring the collection frequency for a fact collector using HumanDuration in minutes. ```yaml frequency: minutes: 10 ``` -------------------------------- ### Soundcheck Catalog Indexer Log Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/performance Example log output from the Soundcheck Catalog Indexer, showing build statistics and memory usage. ```log [CatalogIndexer] Index built successfully: 240346 entities, 8739 owners in ownedBy index, 287644ms build time, heap 1.57 GB → 2.21 GB (Δ 651.9 MB), entity map size: 711.75 MB, ownedBy index size: 17.86 MB, total: 729.61 MB ``` -------------------------------- ### SonarQube Projects Fact Check Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example of a Soundcheck fact check for the 'projects' fact, verifying project existence. ```yaml soundcheck: checks: - id: requires_project_to_exist description: Project exists in sonarqube passedMessage: The check has passed! failedMessage: The check has failed! rule: factRef: sonarqube:default/projects path: $.paging.total operator: greaterThan value: 0 ``` -------------------------------- ### Start Backstage Application with AWS IAM Configuration Source: https://backstage.spotify.com/docs/plugins/getting-started/aws-marketplace Starts the Backstage application using multiple configuration files, including the AWS-specific configuration file generated in the previous step. ```bash yarn start --config path/to/app-config.yaml --config path/to/app-config.staging.yaml --config path/to/app-config.aws.yaml ``` -------------------------------- ### Kubernetes Configuration Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/kubernetes Example YAML configuration for the Kubernetes integration in Backstage. This includes service locator method and cluster locator methods, specifying cluster details and authentication. ```yaml kubernetes: serviceLocatorMethod: type: 'singleTenant' clusterLocatorMethods: - type: catalog - type: 'config' clusters: - url: https://127.0.0.1:59974 name: minikube authProvider: 'serviceAccount' skipTLSVerify: true skipMetricsLookup: true serviceAccountToken: ${K8S_MINIKUBE_TOKEN} ``` -------------------------------- ### Install RBAC Backend Packages Source: https://backstage.spotify.com/docs/plugins/rbac/setup-and-installation Add the necessary RBAC backend packages to your workspace. This command installs the core RBAC backend and the RBAC module for the permission backend. ```bash yarn workspace backend add @spotify/backstage-plugin-rbac-backend @spotify/backstage-plugin-permission-backend-module-rbac ``` -------------------------------- ### Example HTTP Fact Check Configuration Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/http This example demonstrates how to configure an HTTP fact check. It specifies a rule to verify if the collected fact contains a 'production' tag. ```yaml soundcheck: checks: - id: has-production-dashboard-tag name: Grafana dashboard has production tag ownerEntityRef: group:default/toast-infra description: Grafana dashboard has production tag filter: kind: Component spec.type: service rule: factRef: http:default/grafana_dashboard path: $.tags operator: any:equal value: production passedMessage: Success! Grafana dashboard has production tag failedMessage: Failure! Grafana dashboard doesn't have production tag ``` -------------------------------- ### GitLab Project Details Fact Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/gitlab An example of the collected `project_details` fact, showing the data points gathered about a GitLab project. ```yaml factRef: gitlab:default/project_details entityRef: component:default/queue-proxy timestamp: 2023-02-24T15:50+00Z data: id: 3 description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' description_html: '
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
' default_branch: 'main' visibility: 'private' ssh_url_to_repo: 'git@example.com:diaspora/diaspora-project-site.git' http_url_to_repo: 'http://example.com/diaspora/diaspora-project-site.git' web_url: 'http://example.com/diaspora/diaspora-project-site' readme_url: 'http://example.com/diaspora/diaspora-project-site/blob/main/README.md' tag_list: - 'example' - 'disapora project' topics: - 'example' - 'disapora project' owner: id: 3 name: 'Diaspora' created_at: '2013-09-30T13:46:02Z' name: 'Diaspora Project Site' name_with_namespace: 'Diaspora / Diaspora Project Site' path: 'diaspora-project-site' path_with_namespace: 'diaspora/diaspora-project-site' issues_enabled: true open_issues_count: 1 merge_requests_enabled: true jobs_enabled: true wiki_enabled: true snippets_enabled: false can_create_merge_request_in: true resolve_outdated_diff_discussions: false container_registry_enabled: false container_registry_access_level: 'disabled' security_and_compliance_access_level: 'disabled' container_expiration_policy: cadence: '7d' enabled: false keep_n: null older_than: null name_regex: null name_regex_delete: null name_regex_keep: null next_run_at: '2020-01-07T21:42:58.658Z' created_at: '2013-09-30T13:46:02Z' updated_at: '2013-09-30T13:46:02Z' last_activity_at: '2013-09-30T13:46:02Z' creator_id: 3 namespace: id: 3 name: 'Diaspora' path: 'diaspora' kind: 'group' full_path: 'diaspora' avatar_url: 'http://localhost:3000/uploads/group/avatar/3/foo.jpg' web_url: 'http://localhost:3000/groups/diaspora' import_url: null import_type: null import_status: 'none' import_error: null permissions: project_access: access_level: 10 notification_level: 3 group_access: access_level: 50 notification_level: 3 archived: false avatar_url: 'http://example.com/uploads/project/avatar/3/uploads/avatar.png' license_url: 'http://example.com/diaspora/diaspora-client/blob/main/LICENSE' license: key: 'lgpl-3.0' name: 'GNU Lesser General Public License v3.0' nickname: 'GNU LGPLv3' html_url: 'http://choosealicense.com/licenses/lgpl-3.0/' source_url: 'http://www.gnu.org/licenses/lgpl-3.0.txt' shared_runners_enabled: true group_runners_enabled: true forks_count: 0 star_count: 0 runners_token: 'b8bc4a7a29eb76ea83cf79e4908c2b' ci_default_git_depth: 50 ci_forward_deployment_enabled: true ci_forward_deployment_rollback_allowed: true ci_allow_fork_pipelines_to_run_in_parent_project: true ci_separated_caches: true ci_restrict_pipeline_cancellation_role: 'developer' public_jobs: true shared_with_groups: - group_id: 4 group_name: 'Twitter' group_full_path: 'twitter' group_access_level: 30 - group_id: 3 group_name: 'Gitlab Org' group_full_path: 'gitlab-org' group_access_level: 10 repository_storage: 'default' only_allow_merge_if_pipeline_succeeds: false allow_merge_on_skipped_pipeline: false restrict_user_defined_variables: false only_allow_merge_if_all_discussions_are_resolved: false remove_source_branch_after_merge: false printing_merge_requests_link_enabled: true request_access_enabled: false merge_method: 'merge' squash_option: 'default_on' auto_devops_enabled: true auto_devops_deploy_strategy: 'continuous' approvals_before_merge: 0 mirror: false mirror_user_id: 45 mirror_trigger_builds: false only_mirror_protected_branches: false mirror_overwrites_diverged_branches: false external_authorization_classification_label: null packages_enabled: true service_desk_enabled: false service_desk_address: null autoclose_referenced_issues: true suggestion_commit_message: null enforce_auth_checks_on_uploads: true merge_commit_template: null squash_commit_template: null issue_branch_template: 'gitlab/%{id}-%{title}' marked_for_deletion_at: '2020-04-03' marked_for_deletion_on: '2020-04-03' compliance_frameworks: - 'sox' warn_about_potentially_unwanted_characters: true statistics: commit_count: 37 storage_size: 1038090 ``` -------------------------------- ### Install Skill Exchange Actions Module in Backend Source: https://backstage.spotify.com/docs/plugins/skill-exchange/setup-and-installation Install the Skill Exchange actions module in the backend by adding the import to your backend configuration. ```typescript backend.add( import('@spotify/backstage-plugin-skill-exchange-backend-module-actions'), ); ``` -------------------------------- ### Example Collector Response Source: https://backstage.spotify.com/docs/plugins/soundcheck/tutorials/custom-fact-collector-ncui Illustrates the structure of a typical response when fetching collector configurations. This example shows a PagerDuty collector with its facts, configuration, and collection settings. ```json { "id": "pagerduty", "name": "PagerDuty", "description": "Collects facts from your PagerDuty instance.", "isConfigurable": true, "isEditable": true, "factNames": [ "pagerduty:default/service", "pagerduty:default/standards", "pagerduty:default/incidents" ], "config": { "collects": [ { "type": "Service", "frequency": { "hours": 1 }, "filter": [ { "kind": ["Component"] } ], "cache": false }, { "type": "Standards", "frequency": { "hours": 1 }, "filter": [ { "kind": ["Component"] } ], "cache": false }, { "factName": "incidents", "type": "Incidents", "statuses": ["triggered", "acknowledged", "resolved"], "frequency": { "hours": 1 }, "filter": [ { "kind": ["Component"] } ], "cache": false } ] }, "collectionConfigs": [ { "factRefs": ["pagerduty:default/service"], "filter": [ { "kind": ["Component"] } ], "frequency": { "hours": 1 }, "cache": false, "exclude": null }, { "factRefs": ["pagerduty:default/standards"], "filter": [ { "kind": ["Component"] } ], "frequency": { "hours": 1 }, "cache": false, "exclude": null }, { "factRefs": ["pagerduty:default/incidents"], "filter": [ { "kind": ["Component"] } ], "frequency": { ``` -------------------------------- ### Example Rate Limiting Configuration for GitHub Collector Source: https://backstage.spotify.com/docs/plugins/soundcheck/setup-and-installation Example configuration to limit Soundcheck to 5000 fact collections per hour from the 'github' fact collector. ```yaml soundcheck: job: workers: github: limiter: max: 5000 duration: 3600000 ``` -------------------------------- ### Default Lists Configuration Example Source: https://backstage.spotify.com/docs/plugins/skill-exchange/setup-and-installation Example YAML structure for defining default lists of skills, locations, or embed priority labels. This file can be referenced by the Skill Exchange plugin. ```yaml skills: - name: "JavaScript" - name: "Python" - name: "TypeScript" locations: - name: "New York" - name: "London" - name: "Remote" embedPriorities: - name: "High" - name: "Medium" - name: "Low" ``` -------------------------------- ### Lodash Get Expression Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks An example of using lodash.get to access a nested property in the sample JSON object. ```javascript 'store.book[0].author' ``` -------------------------------- ### HTTP Request Configuration Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/http Demonstrates how to configure an HTTP request with a target plugin, method, URL, headers, and body. Liquid templating is supported for dynamic values. ```yaml request: targetPluginId: my-plugin method: POST url: "http://example.com/api/{{ entity.metadata.name }}" headers: Content-Type: "application/json" Authorization: "Bearer {{ secrets.myApiToken }}" body: name: "{{ entity.metadata.name }}" value: "{{ config.someValue }}" ``` -------------------------------- ### Example HTTP Fact Collector Configuration Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/http Defines a Grafana dashboard fact collector with GET request, JSON schema, and retry logic. Also includes an example for collecting failed check results from the Soundcheck plugin. ```yaml - factName: grafana_dashboard request: method: GET url: http://grafana.com/api/dashboards/uid/{{ entity.metadata.annotations['grafana.com/dashboard-uid'] }} headers: Accept: application/json Content-Type: application/json Authorization: Bearer {{ secrets.grafanaApiToken }} response: transform: dashboard emptyOnStatuses: [404] schema: | { "title": "Grafana Dashboard", "description": "Grafana Dashboard", "type": "object", "properties": { "id": { "type": "string" }, "uid": { "type": "string" }, "title": { "type": "string" }, "tags": { "type": "array", "items": { "type": "string" } } } } retries: maxRetries: 3 retryDelay: 5000 retryOnStatuses: [408, 502, 503, 504] - factName: owners_failed_check_results filter: kind: 'Component' spec.type: 'service' request: targetPluginId: soundcheck method: GET url: '{{ targetPluginBaseUrl }}/results?entityRef={{ entity.relations | where: "type", "ownedBy" | first | map: "targetRef" }}' headers: Authorization: Bearer {{ targetPluginAuthToken }} response: transform: results[state='failed'] emptyOnStatuses: [404] schema: | { "title": "Check Results", "description": "Soundcheck Check Results", "type": "object", "properties": { "results": { "type": "array", "items": { "type": "object", "properties": { "entityRef": { "type": "string" }, "checkId": { "type": "string" }, "scope": { "type": "string" }, "state": { "type": "string" }, "timestamp": { "type": "string" } } } } } } ``` -------------------------------- ### Configure Software Catalog in Backstage Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/catalog Example configuration for Backstage's Software Catalog, which must be set up at the root level of app-config.yaml. ```yaml catalog: locations: - type: url target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/components/artist-lookup-component.yaml ``` -------------------------------- ### HTTP Request to Search SonarQube Metrics Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube An example HTTP GET request to the SonarQube API to search for available metrics. Requires an Authorization token. ```http GET https://sonarqube.com/api/metrics/search?ps=500 Authorization: Token Accept: application/json ``` -------------------------------- ### Example YAML Campaign Definition Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/campaigns Defines a campaign with specific fields like start date, target completion date, milestones, and support channel. Used to ensure components meet production readiness criteria. ```yaml - id: production-lifecycle-campaign name: Ensure Lifecycle Production Set type: campaign ownerEntityRef: group:default/toast-infra description: > Ensure all components have a production lifecycle defined in their catalog-info.yaml to indicate production readiness. filter: kind: 'Component' startDate: '2026-04-01' targetCompletionDate: '2030-04-01' supportChannel: toasty-devs milestones: - name: 80% Adoption targetPercent: 80 levels: - checks: - id: has-production-lifecycle name: Has Production Lifecycle description: > Verifies that the component has a production lifecycle defined in its catalog-info.yaml. ``` -------------------------------- ### Enable In-Memory Catalog and Cache Warming Service Source: https://backstage.spotify.com/docs/plugins/soundcheck/performance Quick start configuration to enable both the in-memory catalog and the cache warming service for Soundcheck. ```yaml soundcheck: catalogOptions: type: memory # Use in-memory catalog refreshInterval: 4 # Rebuild index every X hours (4 by default) cacheWarmer: frequency: 3 # Run every 3 hours (default) timeout: 1 # Max 1 hour per run (default) warmGroupsAboveSize: 50 # Only warm groups with 50+ members (default) ``` -------------------------------- ### Install Soundcheck Frontend Package Source: https://backstage.spotify.com/docs/plugins/soundcheck/setup-and-installation Add the Soundcheck frontend package as a dependency to your Backstage application. ```sh yarn workspace app add @spotify/backstage-plugin-soundcheck ``` -------------------------------- ### Collected RepositoryDetails Fact Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/github An example of the data collected by the RepositoryDetails Fact Collector, mirroring GitHub API response. ```yaml factRef: github:default/repository_details entityRef: component:default/queue-proxy timestamp: 2023-02-24T15:50+00Z data: name: backstage full_name: backstage/backstage private: true html_url: 'https://github.com/backstage/backstage' description: null fork: false url: 'https://api.github.com/repos/backstage/backstage' homepage: null size: 3 stargazers_count: 0 watchers_count: 0 language: null has_issues: true has_projects: true has_downloads: true has_wiki: true has_pages: false has_discussions: false forks_count: 0 mirror_url: null archived: false disabled: false open_issues_count: 0 license: null allow_forking: true is_template: false web_commit_signoff_required: false visibility: 'private' forks: 0 open_issues: 0 watchers: 0 default_branch: main permissions: admin: true maintain: true push: true triage: true pull: true allow_squash_merge: true allow_merge_commit: true allow_rebase_merge: true allow_auto_merge: false delete_branch_on_merge: false allow_update_branch: false use_squash_pr_title_as_default: false squash_merge_commit_message: 'COMMIT_MESSAGES' squash_merge_commit_title: 'COMMIT_OR_PR_TITLE' merge_commit_message: 'PR_TITLE' merge_commit_title: 'MERGE_MESSAGE' security_and_analysis: secret_scanning: status: 'disabled' secret_scanning_push_protection: status: 'disabled' network_count: 0 subscribers_count: 1 ``` -------------------------------- ### HTTP Response Configuration Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/http Shows how to configure the HTTP response, including transforming the body with JSONata, handling specific status codes, and defining a response schema. ```yaml response: transform: "{\"data\": $.results}" emptyOnStatuses: - 404 - 500 schema: type: object properties: data: type: array items: { type: string } ``` -------------------------------- ### Project Languages Fact Check Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/gitlab Example of a fact check for project languages, verifying if a project uses JavaScript. ```yaml soundcheck: checks: - id: uses_java_script rule: factRef: gitlab:default/project_languages path: $.JavaScript operator: greaterThan value: 0 ``` -------------------------------- ### Create New Frontend Plugin Source: https://backstage.spotify.com/docs/plugins/soundcheck/tutorials/custom-fact-collector-ncui Use the Backstage CLI to scaffold a new frontend plugin. This command initiates the creation process for a new plugin within your Backstage instance. ```bash yarn backstage-cli new ``` -------------------------------- ### Configure Multiple SonarQube Instances Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example configuration for multiple SonarQube instances in Backstage's app-config.yaml. Each instance requires a name, baseUrl, and token. ```yaml soundcheck: collectors: sonarqube: - name: sonarqube-instance1 baseUrl: http://sonarqube-instance1.example.com token: ${SONARQUBE_INSTANCE1_TOKEN} - name: sonarqube-instance2 baseUrl: http://sonarqube-instance2.example.com token: ${SONARQUBE_INSTANCE2_TOKEN} ``` -------------------------------- ### SonarQube Projects Fact Response Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example JSON response structure for the 'projects' fact collector, detailing project information. ```json { "paging": { "pageIndex": 1, "pageSize": 500, "total": 2 }, "components": [ { "key": "test-project-key", "name": "Project Name 1", "qualifier": "TRK", "visibility": "public", "lastAnalysisDate": "2017-03-01T11:39:03+0300", "revision": "cfb82f55c6ef32e61828c4cb3db2da12795fd767", "managed": false } ] } ``` -------------------------------- ### Install Soundcheck Backend Package Source: https://backstage.spotify.com/docs/plugins/soundcheck/setup-and-installation Add the Soundcheck backend package as a dependency to your Backstage instance using yarn. ```sh yarn workspace backend add @spotify/backstage-plugin-soundcheck-backend ``` -------------------------------- ### Install RBAC Frontend Package Source: https://backstage.spotify.com/docs/plugins/rbac/setup-and-installation Add the RBAC frontend plugin package as a dependency to your Backstage application. ```bash yarn workspace app add @spotify/backstage-plugin-rbac ``` -------------------------------- ### PagerDuty Standards Fact Response Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/pagerduty Example JSON response from the PagerDuty Standards Fact collector, detailing service standards. ```json { "standards": [ { "active": true, "description": "A description provides critical context about what a service represents or is used for to inform team members and responders. The description should be kept concise and understandable by those without deep knowledge of the service.", "id": "01CXX38Q0U8XKHO4LNKXUJTBFG", "pass": true, "name": "Service has a name", "type": "has_technical_service" }, { "active": true, "description": "Ensure that no incident goes unaddressed, even if the on-call responder on the first level of escalation policy is unavailable.", "exclusions": [], "id": "01CXX38Q0Y8D9IYFAEDCH5F53L", "inclusions": [], "name": "Service has an escalation policy with 2 or more unique levels", "resource_type": "technical_service", "type": "minimum_escalation_policy_rule_depth" } ] } ``` -------------------------------- ### Project Languages Fact Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/gitlab Example of a collected project languages fact, showing language distribution within a GitLab project. ```yaml factRef: gitlab:default/project_languages entityRef: component:default/queue-proxy timestamp: 2023-02-24T15:50+00Z data: Ruby: 66.69 JavaScript: 22.98 HTML: 7.91 CoffeeScript: 2.42 ``` -------------------------------- ### Project Details Fact Check Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/gitlab Example of fact checks for project details, ensuring merge restrictions and non-archived status. ```yaml soundcheck: checks: - id: only_allows_merge_if_all_discussions_are_resolved rule: factRef: gitlab:default/project_details path: $.only_allow_merge_if_all_discussions_are_resolved operator: equal value: true - id: not_archived rule: factRef: gitlab:default/project_details path: $.archived operator: equal value: false ``` -------------------------------- ### Configure Single SonarQube Instance Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example configuration for a single SonarQube instance in Backstage's app-config.yaml. Uses an environment variable for the token. ```yaml soundcheck: collectors: sonarqube: token: ${SONARQUBE_TOKEN} # Alternatively you may use username and password # username: admin # password: ${SONARQUBE_PASSWORD} # If you wish to override the api URL # baseUrl: https://sonarqube.com ``` -------------------------------- ### Example Catalog Fact Structure Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/catalog This is an example of a collected Catalog fact, detailing its structure including factRef, entityRef, data, and timestamp. ```yaml factRef: catalog:default/entity_descriptor entityRef: component:default/artist-web data: apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: artist-web description: The place to be, for great artists labels: example.com/custom: custom_label_value annotations: example.com/service-discovery: artistweb circleci.com/project-slug: github/example-org/artist-website tags: - java links: - url: https://admin.example-org.com title: Admin Dashboard icon: dashboard type: admin-dashboard spec: type: website lifecycle: production owner: artist-relations-team system: public-websites timestamp: 2023-02-20T13:50:35Z ``` -------------------------------- ### SonarQube Integration Demo Track Configuration Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example Soundcheck track configuration demonstrating the SonarQube integration, including various checks related to project existence, tags, issues, and violations. ```yaml soundcheck: tracks: - id: sonarqube-integration-demo name: SonarQube Integration Demo ownerEntityRef: group:default/owning_group description: Demonstration of Soundcheck SonarQube Fact Collector levels: - ordinal: 1 name: First level description: Checks leveraging SoundCheck's SonarQube integration checks: - id: requires_project_to_exist name: Project exists in sonarqube description: Project exists in sonarqube - id: project_has_production_tag name: Project has production tag description: Project has production tag - id: no_blocker_issues name: No blocker issues description: No blocker issues - id: no_violations name: No violations found description: No violations found ``` -------------------------------- ### CertificationChangeEvent Example Payload Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/events This is an example payload for a `CertificationChangeEvent`, emitted when a certification state changes. The `previousState` field will be `undefined` if there was no prior status. ```js eventPayload: { trackId: 'track-1', entityRef: 'component:default/test-1', state: 'passed', cumulativeState: 'passed', levelOrdinal: 1, timestampMs: 1743777961, previousState: 'failed', scope: 'default', }, topic: 'soundcheck-certification-change', ``` -------------------------------- ### Recommended GitHub Settings Checks Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks Examples of pre-built Soundcheck checks for GitHub repository settings, including branch protection rules and owner verification. ```yaml - id: 21ed0e9c-7fd9-4e59-89dd-33902b1e6bcb ownerEntityRef: group:default/backstage name: Branch Is Not Locked description: This check ensures that the default branch is not locked. rule: all: - factRef: github:default/branch_protections path: $.lock_branch.enabled operator: equal value: false filter: - kind: - component passedMessage: Default branch is not locked. failedMessage: The default branch is locked, but shouldn't be. ``` ```yaml - id: ca05baf0-ff68-4ebd-9559-58a94de84ac3 ownerEntityRef: group:default/backstage name: Pull Request Requires Review description: Pull requests should require at least one reviewer. rule: all: - factRef: github:default/branch_protections path: $.required_pull_request_reviews.required_approving_review_count operator: greaterThan value: 0 filter: - kind: - component passedMessage: Passed because pull requests require at least one reviewer. failedMessage: Pull requests do not require any reviewers. ``` ```yaml - id: 013091a6-2bc8-46dc-8d34-58207c5b7370 ownerEntityRef: group:default/backstage name: Force Pushes Disabled description: >- Check to ensure that the repository does not allow force pushes to the main branch. rule: all: - factRef: github:default/branch_protections path: $.allow_force_pushes.enabled operator: equal value: false filter: - kind: - component passedMessage: Passed, force pushes are not allowed. failedMessage: Force pushes are not disabled, but should be. ``` ```yaml - id: b812cac0-8f80-4fa0-83cd-6a725c16ac2a ownerEntityRef: group:default/backstage name: Repository Has Owner description: Verifies that the repository has an owner set. rule: all: - factRef: github:default/repository_details path: $.owner.login operator: matches value: .+ filter: - kind: - component passedMessage: Repository has owner. failedMessage: Repository has no owner set. ``` ```yaml - id: 0d602bee-f46b-4ff9-8dad-198d82308fa4 ownerEntityRef: group:default/backstage name: Admins Must Follow Rules description: This check that ensures that even administrators cannot bypass branch rules. rule: all: - factRef: github:default/branch_protections path: $.enforce_admins.enabled operator: equal value: true filter: - kind: - component passedMessage: Admins must follow branch rules, passed. failedMessage: Admin rule enforcement is not enabled, but should be. ``` ```yaml - id: b113ad76-bb17-41af-980e-1f515f061004 ownerEntityRef: group:default/backstage name: Deletions Are Forbidden description: Check to ensure that the repository does not allow deletions. rule: all: - factRef: github:default/branch_protections path: $.allow_deletions.enabled operator: equal value: false filter: - kind: - component passedMessage: Repository does not allow deletions. failedMessage: Repository allows deletions but shouldn't. ``` -------------------------------- ### Example Check with JSONata Path Resolver Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/checks This example demonstrates how to configure a check to use the JSONata path resolver to extract a value from a fact. ```yaml id: annotation-defined ownerEntityRef: group:default/check-owner name: Annotation Defined description: Verifies that annotations are defined on the entity. pathResolver: jsonata rule: factRef: catalog:default/entity_descriptor path: $keys($.metadata.annotations) operator: contains value: 0 ``` -------------------------------- ### Kubernetes Deployment FieldsV1 Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/kubernetes This snippet shows an example of the 'FieldsV1' structure for a Kubernetes Deployment, detailing its metadata and spec fields. It's useful for understanding the granular data that can be collected about deployments. ```json { "fieldsV1": { "f:metadata": { "f:labels": { ".": {}, "f:app": {} } }, "f:spec": { "f:progressDeadlineSeconds": {}, "f:replicas": {}, "f:revisionHistoryLimit": {}, "f:selector": {}, "f:strategy": { "f:rollingUpdate": { ".": {}, "f:maxSurge": {}, "f:maxUnavailable": {} }, "f:type": {} }, "f:template": { "f:metadata": { "f:labels": { ".": {}, "f:app": {} } }, "f:spec": { "f:containers": { "k:\"{\"name\":\"echo-server\"}\" ": { ".": {}, "f:image": {}, "f:imagePullPolicy": {}, "f:name": {}, "f:resources": {}, "f:terminationMessagePath": {}, "f:terminationMessagePolicy": {} } }, "f:dnsPolicy": {}, "f:restartPolicy": {}, "f:schedulerName": {}, "f:securityContext": {}, "f:terminationGracePeriodSeconds": {} } } } } } ``` -------------------------------- ### Install Skill Exchange Plugin (Yarn) Source: https://backstage.spotify.com/docs/plugins/skill-exchange/setup-and-installation Adds the Skill Exchange plugin as a dependency to your Backstage application using Yarn. ```bash yarn workspace app add @spotify/backstage-plugin-skill-exchange ``` -------------------------------- ### SonarQube Project Tags Fact Response Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/sonarqube Example JSON response structure for the 'project-tags' fact collector, showing project tags. ```json { "tags": ["production", "test", "internal"] } ``` -------------------------------- ### Configure GitHub Integration in app-config.yaml Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/scm Provides an example of how to configure a GitHub integration in the main app-config.yaml file. Ensure you replace GITHUB_TOKEN with your actual token. ```yaml integrations: github: - host: github.com token: ${GITHUB_TOKEN} ``` -------------------------------- ### GitLab Branch Protections Fact Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/gitlab Example of the collected 'branch_protections' fact data from GitLab. This fact collects details about protected branches. ```yaml factRef: gitlab:default/branch_protections entityRef: component:default/queue-proxy timestamp: 2023-02-24T15:50+00Z data: id: 1 name: 'main' push_access_levels: - id: 1 access_level: 40 user_id: null group_id: null access_level_description: 'Maintainers' merge_access_levels: - id: 1 access_level: null user_id: null group_id: 1234 access_level_description: 'Example Merge Group' unprotect_access_levels: - id: 1 access_level: 40 user_id: null group_id: null access_level_description: 'Maintainers' allow_force_push: false code_owner_approval_required: false inherited: false ``` -------------------------------- ### CheckStatusChangeEvent Example Payload Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/events This is an example payload for a `CheckStatusChangeEvent`, emitted when a check's status changes. The `previousState` field will be `undefined` if there was no prior status. ```js eventPayload: { checkId: 'check-1', entityRef: 'component:default/test-1', state: 'passed', timestampMs: 1743777961, previousState: 'failed', scope: 'default' }, topic: 'soundcheck-check-status-change', ``` -------------------------------- ### Setup Soundcheck Backend Plugin Source: https://backstage.spotify.com/docs/plugins/soundcheck/setup-and-installation Integrate the Soundcheck backend plugin into your Backstage backend by adding it to the backend instance. ```ts import { createBackend } from '@backstage/backend-defaults'; const backend = createBackend(); backend.add(import('@backstage/plugin-app-backend/alpha')); backend.add(import('@spotify/backstage-plugin-soundcheck-backend')); // ... backend.start(); ``` -------------------------------- ### Example Fact Collector Configurations Source: https://backstage.spotify.com/docs/plugins/soundcheck/tutorials/custom-fact-collector Demonstrates various configuration options for fact collectors, including frequency, filtering, and exclusion. ```yaml # Every 5 minutes frequency: minutes: 5 ``` ```yaml # Daily at midnight frequency: cron: "0 0 * * *" ``` ```yaml # Filter to production components filter: kind: Component spec.lifecycle: production ``` ```yaml # Exclude experimental components exclude: metadata.tags: ['experimental'] ``` -------------------------------- ### Collected Policies Search Fact Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/newrelic An example of the data structure collected by the Policies Search Fact collector, referencing the NerdGraph query results. ```yaml factRef: 'newrelic:default/policies-search' entityRef: 'component:default/demo' timestamp: '2024-02-08T18:17:59.048Z' data: actor: account: alerts: policiesSearch: nextCursor: null policies: - accountId: 4338873 id: '5079003' incidentPreference: PER_POLICY name: Test policy - accountId: 4338873 id: '5081741' incidentPreference: PER_POLICY name: Service Levels default policy for account 4338873 totalCount: 2 id: 4338873 ``` -------------------------------- ### PagerDuty Service Fact Response Example Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/pagerduty Example JSON response from the PagerDuty Service Fact collector. This structure provides details about a PagerDuty service. ```json { "id": "PIJ90N7", "type": "service", "summary": "My Application Service", "self": "https://api.pagerduty.com/services/PIJ90N7", "html_url": "https://subdomain.pagerduty.com/service-directory/PIJ90N7", "name": "My Application Service", "auto_resolve_timeout": 14400, "acknowledgement_timeout": 600, "created_at": "2015-11-06T11:12:51-05:00", "status": "active", "alert_creation": "create_alerts_and_incidents", "integrations": [ { "id": "PQ12345", "type": "generic_email_inbound_integration_reference", "summary": "Email Integration", "self": "https://api.pagerduty.com/services/PIJ90N7/integrations/PQ12345", "html_url": "https://subdomain.pagerduty.com/services/PIJ90N7/integrations/PQ12345" } ], "escalation_policy": { "id": "PT20YPA", "type": "escalation_policy_reference", "summary": "Another Escalation Policy", "self": "https://api.pagerduty.com/escalation_policies/PT20YPA", "html_url": "https://subdomain.pagerduty.com/escalation_policies/PT20YPA" }, "teams": [ { "id": "PQ9K7I8", "type": "team_reference", "summary": "Engineering", "self": "https://api.pagerduty.com/teams/PQ9K7I8", "html_url": "https://subdomain.pagerduty.com/teams/PQ9K7I8" } ], "incident_urgency_rule": { "type": "use_support_hours", "during_support_hours": { "type": "constant", "urgency": "high" }, "outside_support_hours": { "type": "constant", "urgency": "low" } }, "support_hours": { "type": "fixed_time_per_day", "time_zone": "America/Lima", "start_time": "09:00:00", "end_time": "17:00:00", "days_of_week": [1, 2, 3, 4, 5] }, "scheduled_actions": [ { "type": "urgency_change", "at": { "type": "named_time", "name": "support_hours_start" }, "to_urgency": "high" } ], "auto_pause_notifications_parameters": { "enabled": true, "timeout": 300 } } ``` -------------------------------- ### Spotify-Recommended Cache Warmer Configuration Source: https://backstage.spotify.com/docs/plugins/soundcheck/performance Recommended settings for balancing update frequency and group warming for general use. ```yaml cacheWarmer: frequency: 3 # More frequent updates warmGroupsAboveSize: 50 # Warm more groups ``` -------------------------------- ### Add Skill Exchange Backend Packages Source: https://backstage.spotify.com/docs/plugins/skill-exchange/setup-and-installation Install the Skill Exchange backend packages as dependencies to your Backstage instance using yarn. ```bash yarn workspace backend add @spotify/backstage-plugin-skill-exchange-backend @spotify/backstage-plugin-search-backend-module-skill-exchange ``` -------------------------------- ### Example of Collected HTTP Fact Source: https://backstage.spotify.com/docs/plugins/soundcheck/core-concepts/fact-collectors/3p-integrations/http This is an example of the structure of an HTTP fact collected by the fact collector. The 'data' field contains the HTTP response details. ```yaml factRef: http:default/grafana_dashboard entityRef: component:default/queue-proxy scope: default timestamp: 2025-01-09T15:50+00Z data: id: null uid: null title: Production Overview tags: - production timezone: browser schemaVersion: 16 refresh: 25s ```