diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js index a58d524b9ed..9887bda973c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/container.js @@ -17,6 +17,7 @@ export default { 'section', { attrs: { + class: 'mr-section-container mr-widget-workflow', role: 'region', 'aria-label': __('Merge request reports'), }, diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index a3f70b551bf..62ad22c637b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -563,6 +563,7 @@ export default { :mr="mr" :service="service" /> +
- - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118844) in GitLab 12.8. -When configuring Review Apps for a project, you add a new job to the `.gitlab-ci.yml` file, +When configuring review apps for a project, you add a new job to the `.gitlab-ci.yml` file, as mentioned above. To facilitate this, and if you are using Kubernetes, you can select -**Enable Review Apps** and GitLab prompts you with a template code block that +**Enable review app** and GitLab prompts you with a template code block that you can copy and paste into `.gitlab-ci.yml` as a starting point. Prerequisite: - You need at least the Developer role for the project. -To use the Review Apps template: +To use the review apps template: -1. On the top bar, select **Main menu > Projects** and find the project you want to create a Review App job for. +1. On the top bar, select **Main menu > Projects** and find the project you want to create a review app job for. 1. On the left sidebar, select **Deployments > Environments**. -1. Select **Enable Review Apps**. +1. Select **Enable review app**. 1. Copy the provided code snippet and paste it into your `.gitlab-ci.yml` file: - ![Enable Review Apps modal](img/enable_review_app_v12_8.png) + ![enable review apps modal](img/enable_review_app_v12_8.png) You can edit this template as needed. -## Review Apps auto-stop +## Review apps auto-stop -See how to [configure Review Apps environments to expire and auto-stop](../environments/index.md#stop-an-environment-after-a-certain-time-period) +See how to [configure review apps environments to expire and auto-stop](../environments/index.md#stop-an-environment-after-a-certain-time-period) after a given period of time. -## Review Apps examples +## Review apps examples -The following are example projects that demonstrate Review App configuration: +The following are example projects that demonstrate review app configuration: | Project | Configuration file | |-------------------------------------------------------------------------|--------------------| @@ -104,17 +104,17 @@ The following are example projects that demonstrate Review App configuration: | [`https://about.gitlab.com/`](https://gitlab.com/gitlab-com/www-gitlab-com/) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/6ffcdc3cb9af2abed490cbe5b7417df3e83cd76c/.gitlab-ci.yml#L332) | | [GitLab Insights](https://gitlab.com/gitlab-org/gitlab-insights/) | [`.gitlab-ci.yml`](https://gitlab.com/gitlab-org/gitlab-insights/-/blob/9e63f44ac2a5a4defc965d0d61d411a768e20546/.gitlab-ci.yml#L234) | -Other examples of Review Apps: +Other examples of review apps: - [Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw). -- [Review Apps for Android](https://about.gitlab.com/blog/2020/05/06/how-to-create-review-apps-for-android-with-gitlab-fastlane-and-appetize-dot-io/). +- [Review apps for Android](https://about.gitlab.com/blog/2020/05/06/how-to-create-review-apps-for-android-with-gitlab-fastlane-and-appetize-dot-io/). ## Route Maps Route Maps allows you to go directly from source files to public pages on the [environment](../environments/index.md) defined for -Review Apps. +review apps. Once set up, the review app link in the merge request widget can take you directly to the pages changed, making it easier @@ -209,7 +209,7 @@ With Visual Reviews, members of any team (Product, Design, Quality, and so on) c ### Using Visual Reviews After Visual Reviews has been [configured](#configure-review-apps-for-visual-reviews) for the -Review App, the Visual Reviews feedback form is overlaid on the right side of every page. +review app, the Visual Reviews feedback form is overlaid on the right side of every page. ![Visual review feedback form](img/toolbar_feedback_form_v13_5.png) @@ -227,9 +227,9 @@ To use the feedback form to make a comment in the merge request: To see Visual reviews in action, see the [Visual Reviews Walk through](https://youtu.be/1_tvWTlPfM4). -### Configure Review Apps for Visual Reviews +### Configure review apps for Visual Reviews -The feedback form is served through a script you add to pages in your Review App. +The feedback form is served through a script you add to pages in your review app. It should be added to the `` of your application and consists of some project and merge request specific values. Here's how it looks for a project with code hosted in a project on GitLab.com: diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md index 21981d858f3..3e32f0b020a 100644 --- a/doc/development/documentation/styleguide/word_list.md +++ b/doc/development/documentation/styleguide/word_list.md @@ -936,6 +936,10 @@ Instead of: - Select **Create user** or **Save changes** if you created a new user or edited an existing one respectively. +## review app + +Use lowercase for **review app**. + ## roles Do not use **roles** and [**permissions**](#permissions) interchangeably. Each user is assigned a role. Each role includes a set of permissions. diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md index d3e8642cfa0..5edf18725c0 100644 --- a/doc/development/testing_guide/review_apps.md +++ b/doc/development/testing_guide/review_apps.md @@ -4,9 +4,9 @@ group: unassigned info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Review Apps +# Using review apps in the development of GitLab -Review Apps are deployed using the `start-review-app-pipeline` job which triggers a child pipeline containing a series of jobs to perform the various tasks needed to deploy a Review App. +Review apps are deployed using the `start-review-app-pipeline` job which triggers a child pipeline containing a series of jobs to perform the various tasks needed to deploy a review app. ![start-review-app-pipeline job](img/review-app-parent-pipeline.png) @@ -21,7 +21,7 @@ For any of the following scenarios, the `start-review-app-pipeline` job would be - for scheduled pipelines - the MR has the `pipeline:run-review-app` label set -## QA runs on Review Apps +## QA runs on review apps On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage (which comes after the `review` stage), the `review-qa-smoke` and `review-qa-reliable` jobs are automatically started. The `review-qa-smoke` runs @@ -34,7 +34,7 @@ You can also manually start the `review-qa-all`: it runs the full QA suite. After the end-to-end test runs have finished, [Allure reports](https://github.com/allure-framework/allure2) are generated and published by the `allure-report-qa-smoke`, `allure-report-qa-reliable`, and `allure-report-qa-all` jobs. A comment with links to the reports are added to the merge request. -Errors can be found in the `gitlab-review-apps` Sentry project and [filterable by Review App URL](https://sentry.gitlab.net/gitlab/gitlab-review-apps/?query=url%3A%22https%3A%2F%2Fgitlab-review-require-ve-u92nn2.gitlab-review.app%2F%22) or [commit SHA](https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/6095b501da7/all-events/). +Errors can be found in the `gitlab-review-apps` Sentry project and [filterable by review app URL](https://sentry.gitlab.net/gitlab/gitlab-review-apps/?query=url%3A%22https%3A%2F%2Fgitlab-review-require-ve-u92nn2.gitlab-review.app%2F%22) or [commit SHA](https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/6095b501da7/all-events/). ### Bypass failed review app deployment to merge a broken `master` fix @@ -47,7 +47,7 @@ On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in browser performance testing using a [Sitespeed.io Container](../../ci/testing/browser_performance_testing.md). -## Sample Data for Review Apps +## Sample Data for review apps Upon deployment of a review app, project data is created from the [`sample-gitlab-project`](https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project) template project. This aims to provide projects with prepopulated resources to facilitate manual and exploratory testing. @@ -55,16 +55,16 @@ The sample projects will be created in the `root` user namespace and can be acce ## How to -### Redeploy Review App from a clean slate +### Redeploy review app from a clean slate -To reset Review App and redeploy from a clean slate, do the following: +To reset review app and redeploy from a clean slate, do the following: 1. Run `review-stop` job. 1. Re-deploy by running or retrying `review-deploy` job. -Doing this will remove all existing data from a previously deployed Review App. +Doing this will remove all existing data from a previously deployed review app. -### Get access to the GCP Review Apps cluster +### Get access to the GCP review apps cluster You need to [open an access request (internal link)](https://gitlab.com/gitlab-com/access-requests/-/issues/new) for the `gcp-review-apps-dev` GCP group and role. @@ -74,7 +74,7 @@ This grants you the following permissions for: - [Retrieving pod logs](#dig-into-a-pods-logs). Granted by [Viewer (`roles/viewer`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles). - [Running a Rails console](#run-a-rails-console). Granted by [Kubernetes Engine Developer (`roles/container.pods.exec`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles). -### Log into my Review App +### Log into my review app For GitLab Team Members only. If you want to sign in to the review app, review the GitLab handbook information for the [shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams). @@ -82,23 +82,23 @@ the GitLab handbook information for the [shared 1Password account](https://about - The default username is `root`. - The password can be found in the 1Password login item named `GitLab EE Review App`. -### Enable a feature flag for my Review App +### Enable a feature flag for my review app -1. Open your Review App and sign in as documented above. +1. Open your review app and sign in as documented above. 1. Create a personal access token. 1. Enable the feature flag using the [Feature flag API](../../api/features.md). -### Find my Review App slug +### Find my review app slug 1. Open the `review-deploy` job. 1. Look for `** Deploying review-*`. 1. For instance for `** Deploying review-1234-abc-defg... **`, - your Review App slug would be `review-1234-abc-defg` in this case. + your review app slug would be `review-1234-abc-defg` in this case. ### Run a Rails console 1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) and the `container.pods.exec` permission first. -1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps). For example, `review-qa-raise-e-12chm0`. +1. [Filter Workloads by your review app slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps). For example, `review-qa-raise-e-12chm0`. 1. Find and open the `toolbox` Deployment. For example, `review-qa-raise-e-12chm0-toolbox`. 1. Select the Pod in the "Managed pods" section. For example, `review-qa-raise-e-12chm0-toolbox-d5455cc8-2lsvz`. 1. Select the `KUBECTL` dropdown list, then `Exec` -> `toolbox`. @@ -111,7 +111,7 @@ the GitLab handbook information for the [shared 1Password account](https://about ### Dig into a Pod's logs 1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) and the `container.pods.getLogs` permission first. -1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps). For example, `review-qa-raise-e-12chm0`. +1. [Filter Workloads by your review app slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps). For example, `review-qa-raise-e-12chm0`. 1. Find and open the `migrations` Deployment. For example, `review-qa-raise-e-12chm0-migrations.1`. 1. Select the Pod in the "Managed pods" section. For example, `review-qa-raise-e-12chm0-migrations.1-nqwtx`. 1. Select `Container logs`. @@ -149,11 +149,11 @@ subgraph "2. gitlab `review-prepare` stage" end subgraph "3. gitlab `review` stage" - C["review-deploy

Helm deploys the Review App using the Cloud
Native images built by the CNG-mirror pipeline.

Cloud Native images are deployed to the `review-apps`
Kubernetes (GKE) cluster, in the GCP `gitlab-review-apps` project."] + C["review-deploy

Helm deploys the review app using the Cloud
Native images built by the CNG-mirror pipeline.

Cloud Native images are deployed to the `review-apps`
Kubernetes (GKE) cluster, in the GCP `gitlab-review-apps` project."] end subgraph "4. gitlab `qa` stage" - E[review-qa-smoke, review-qa-reliable

gitlab-qa runs the smoke and reliable suites against the Review App.] + E[review-qa-smoke, review-qa-reliable

gitlab-qa runs the smoke and reliable suites against the review app.] end subgraph "CNG-mirror pipeline" @@ -180,10 +180,10 @@ subgraph "CNG-mirror pipeline" - We use the [`CNG-mirror`](https://gitlab.com/gitlab-org/build/CNG-mirror) project so that the `CNG`, (Cloud Native GitLab), project's registry is not overloaded with a lot of transient Docker images. 1. Once `review-build-cng` is done, the [`review-deploy`](https://gitlab.com/gitlab-org/gitlab/-/jobs/467724810) job - deploys the Review App using [the official GitLab Helm chart](https://gitlab.com/gitlab-org/charts/gitlab/) to + deploys the review app using [the official GitLab Helm chart](https://gitlab.com/gitlab-org/charts/gitlab/) to the [`review-apps`](https://console.cloud.google.com/kubernetes/clusters/details/us-central1-b/review-apps?project=gitlab-review-apps) Kubernetes cluster on GCP. - - The actual scripts used to deploy the Review App can be found at + - The actual scripts used to deploy the review app can be found at [`scripts/review_apps/review-apps.sh`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/review_apps/review-apps.sh). - These scripts are basically [our official Auto DevOps scripts](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml) where the @@ -192,11 +192,11 @@ subgraph "CNG-mirror pipeline" - Since we're using [the official GitLab Helm chart](https://gitlab.com/gitlab-org/charts/gitlab/), this means you get a dedicated environment for your branch that's very close to what it would look in production. - - Each review app is deployed to its own Kubernetes namespace. The namespace is based on the Review App slug that is + - Each review app is deployed to its own Kubernetes namespace. The namespace is based on the review app slug that is unique to each branch. 1. Once the [`review-deploy`](https://gitlab.com/gitlab-org/gitlab/-/jobs/467724810) job succeeds, you should be able to - use your Review App thanks to the direct link to it from the MR widget. To log - into the Review App, see "Log into my Review App?" below. + use your review app thanks to the direct link to it from the MR widget. To log + into the review app, see "Log into my review app?" below. **Additional notes:** @@ -213,23 +213,23 @@ subgraph "CNG-mirror pipeline" `#quality` channel and/or create a ~Quality ~"type::bug" issue with a link to your merge request. - The manual `review-stop` can be used to - stop a Review App manually, and is also started by GitLab once a merge + stop a review app manually, and is also started by GitLab once a merge request's branch is deleted after being merged. - The Kubernetes cluster is connected to the `gitlab` projects using the [GitLab Kubernetes integration](../../user/infrastructure/clusters/index.md). This basically - allows to have a link to the Review App directly from the merge request widget. + allows to have a link to the review app directly from the merge request widget. -### Auto-stopping of Review Apps +### Auto-stopping of review apps -Review Apps are automatically stopped 2 days after the last deployment thanks to +Review apps are automatically stopped 2 days after the last deployment thanks to the [Environment auto-stop](../../ci/environments/index.md#stop-an-environment-after-a-certain-time-period) feature. -If you need your Review App to stay up for a longer time, you can +If you need your review app to stay up for a longer time, you can [pin its environment](../../ci/environments/index.md#override-a-deployments-scheduled-stop-time) or retry the `review-deploy` job to update the "latest deployed at" time. The `review-cleanup` job that automatically runs in scheduled -pipelines stops stale Review Apps after 5 days, +pipelines stops stale review apps after 5 days, deletes their environment after 6 days, and cleans up any dangling Helm releases and Kubernetes resources after 7 days. @@ -246,13 +246,13 @@ The Helm version used is defined in the [`registry.gitlab.com/gitlab-org/gitlab-build-images:gitlab-helm3.5-kubectl1.17` image](https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/Dockerfile.gitlab-helm3.5-kubectl1.17#L6) used by the `review-deploy` and `review-stop` jobs. -## Diagnosing unhealthy Review App releases +## Diagnosing unhealthy review app releases -If [Review App Stability](https://app.periscopedata.com/app/gitlab/496118/Engineering-Productivity-Sandbox?widget=6690556&udv=785399) +If [review app stability](https://app.periscopedata.com/app/gitlab/496118/Engineering-Productivity-Sandbox?widget=6690556&udv=785399) dips this may be a signal that the `review-apps` cluster is unhealthy. -Leading indicators may be health check failures leading to restarts or majority failure for Review App deployments. +Leading indicators may be health check failures leading to restarts or majority failure for review app deployments. -The [Review Apps Overview dashboard](https://console.cloud.google.com/monitoring/classic/dashboards/6798952013815386466?project=gitlab-review-apps&timeDomain=1d) +The [review apps Overview dashboard](https://console.cloud.google.com/monitoring/classic/dashboards/6798952013815386466?project=gitlab-review-apps&timeDomain=1d) aids in identifying load spikes on the cluster, and if nodes are problematic or the entire cluster is trending towards unhealthy. See the [review apps page of the Engineering Productivity Runbook](https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/blob/main/runbook/review-apps.md) for troubleshooting review app releases. @@ -273,7 +273,7 @@ find a way to limit it to only us.** ## Other resources -- [Review Apps integration for CE/EE (presentation)](https://docs.google.com/presentation/d/1QPLr6FO4LduROU8pQIPkX1yfGvD13GEJIBOenqoKxR8/edit?usp=sharing) +- [Review apps integration for CE/EE (presentation)](https://docs.google.com/presentation/d/1QPLr6FO4LduROU8pQIPkX1yfGvD13GEJIBOenqoKxR8/edit?usp=sharing) - [Stability issues](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/212) ### Helpful command line tools diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md index de9275b8599..ef2d3a76990 100644 --- a/doc/user/analytics/ci_cd_analytics.md +++ b/doc/user/analytics/ci_cd_analytics.md @@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # CI/CD analytics **(FREE)** -Use the CI/CD analytics page to view pipeline success rates and duration, and the history of [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) over time. +Use the CI/CD analytics page to view pipeline success rates and duration, and the history of DORA metrics over time. ## Pipeline success and duration charts @@ -35,7 +35,7 @@ To view CI/CD analytics: 1. On the top bar, select **Main menu > Projects** and find your project. 1. On the left sidebar, select **Analytics > CI/CD Analytics**. -## View deployment frequency chart **(ULTIMATE)** +## View DORA deployment frequency chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/275991) in GitLab 13.8. @@ -44,7 +44,7 @@ frequency to the `production` environment. The environment must be part of the [production deployment tier](../../ci/environments/index.md#deployment-tier-of-environments) for its deployment information to appear on the graphs. - Deployment frequency is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery. + Deployment frequency is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery. The deployment frequency chart is available for groups and projects. @@ -56,7 +56,7 @@ To view the deployment frequency chart: ![Deployment frequency](img/deployment_frequency_charts_v13_12.png) -## View lead time for changes chart **(ULTIMATE)** +## View DORA lead time for changes chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250329) in GitLab 13.11. @@ -68,7 +68,7 @@ merge requests to be deployed to a production environment. This chart is availab - For time periods in which no merge requests were deployed, the charts render a red, dashed line. - Lead time for changes is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery. + Lead time for changes is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery. To view the lead time for changes chart: @@ -78,13 +78,13 @@ To view the lead time for changes chart: ![Lead time](img/lead_time_chart_v13_11.png) -## View time to restore service chart **(ULTIMATE)** +## View DORA time to restore service chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356959) in GitLab 15.1 The time to restore service chart shows information about the median time an incident was open in a production environment. This chart is available for groups and projects. -Time to restore service is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery. +Time to restore service is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery. To view the time to restore service chart: @@ -94,13 +94,13 @@ To view the time to restore service chart: ![Lead time](img/time_to_restore_service_charts_v15_1.png) -## View change failure rate chart **(ULTIMATE)** +## View DORA change failure rate chart **(ULTIMATE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357072) in GitLab 15.2 The change failure rate chart shows information about the percentage of deployments that cause an incident in a production environment. This chart is available for groups and projects. -Change failure rate is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery. +Change failure rate is one of the four DORA metrics that DevOps teams use for measuring excellence in software delivery. To view the change failure rate chart: diff --git a/doc/user/analytics/dora_metrics.md b/doc/user/analytics/dora_metrics.md new file mode 100644 index 00000000000..605985cd779 --- /dev/null +++ b/doc/user/analytics/dora_metrics.md @@ -0,0 +1,105 @@ +--- +stage: Plan +group: Optimize +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# DevOps Research and Assessment (DORA) metrics **(ULTIMATE)** + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/275991) in GitLab 13.7. +> - [Added support](https://gitlab.com/gitlab-org/gitlab/-/issues/291746) for lead time for changes in GitLab 13.10. + +The [DevOps Research and Assessment (DORA)](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance) +team has identified four metrics that measure DevOps performance. +Using these metrics helps improve DevOps efficiency and communicate performance to business stakeholders, which can accelerate business results. + +DORA includes four key metrics, divided into two core areas of DevOps: + +- [Deployment Frequency](#deployment-frequency) and [Lead Time for Change](#lead-time-for-changes) measure team velocity. +- [Change Failure Rate](#change-failure-rate) and [Time to Restore Service](#time-to-restore-service) measure stability. + +For software leaders, tracking velocity alongside quality metrics ensures they're not sacrificing quality for speed. + +## DORA Metrics dashboard in Value Stream Analytics + +The four DORA metrics are available out-of-the-box in the [Value Stream Analytics (VSA) overview dashboard](../group/value_stream_analytics/index.md#view-dora-metrics-and-key-metrics-for-a-group). +This helps you visualize the engineering work in the context of end-to-end value delivery. + +The One DevOps Platform [Value Stream Management](https://gitlab.com/gitlab-org/gitlab/-/value_stream_analytics) provides end-to-end visibility to the entire software delivery lifecycle. +This enables teams and managers to understand all aspects of productivity, quality, and delivery, without the ["toolchain tax"](https://about.gitlab.com/solutions/value-stream-management/). + +## Deployment frequency + +Deployment frequency is the frequency of successful deployments to production (hourly, daily, weekly, monthly, or yearly). +This measures how often you deliver value to end users. A higher deployment frequency means you can +get feedback sooner and iterate faster to deliver improvements and features. GitLab measures this as the number of +deployments to a production environment in the given time period. + +Deployment frequency displays in several charts: + +- [Group-level value stream analytics](../group/value_stream_analytics/index.md) +- [Project-level value stream analytics](value_stream_analytics.md) +- [CI/CD analytics](ci_cd_analytics.md) + +To retrieve metrics for deployment frequency, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. + +## Lead time for changes + +Lead time for changes measures the time to deliver a feature once it has been developed, +as described in [Measuring DevOps Performance](https://devops.com/measuring-devops-performance/). + +Lead time for changes displays in several charts: + +- [Group-level value stream analytics](../group/value_stream_analytics/index.md) +- [Project-level value stream analytics](value_stream_analytics.md) +- [CI/CD analytics](ci_cd_analytics.md) + +To retrieve metrics for lead time for changes, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. + +## Time to restore service + +Time to restore service measures how long it takes an organization to recover from a failure in production. +GitLab measures this as the average time required to close the incidents +in the given time period. This assumes: + +- All incidents are related to a production environment. +- Incidents and deployments have a strictly one-to-one relationship. An incident is related to only +one production deployment, and any production deployment is related to no more than one incident). + +Time to restore service displays in several charts: + +- [Group-level value stream analytics](../group/value_stream_analytics/index.md) +- [Project-level value stream analytics](value_stream_analytics.md) +- [CI/CD analytics](ci_cd_analytics.md) + +To retrieve metrics for time to restore service, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. + +## Change failure rate + +Change failure rate measures the percentage of deployments that cause a failure in production. GitLab measures this as the number +of incidents divided by the number of deployments to a +production environment in the given time period. This assumes: + +- All incidents are related to a production environment. +- Incidents and deployments have a strictly one-to-one relationship. An incident is related to only +one production deployment, and any production deployment is related to no +more than one incident. + +To retrieve metrics for change failure rate, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. + +### Insights: Custom DORA reporting + +Custom charts to visualize DORA data with Insights YAML-based reports. + +With this new visualization, software leaders can track metrics improvements, understand patterns in their metrics trends, and compare performance between groups and projects. + +### Supported DORA metrics in GitLab + +| Metric | Level | API | UI chart | Comments | +|---------------------------|-------------------|-----------------------------------------------------|------------------------|----------| +| `deployment_frequency` | Project | [GitLab 13.7 and later](../../api/dora/metrics.md) | GitLab 14.8 and later | The previous API endpoint was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/323713) in 13.10. | +| `deployment_frequency` | Group | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 13.12 and later | | +| `lead_time_for_changes` | Project | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 13.11 and later | Unit in seconds. Aggregation method is median. | +| `lead_time_for_changes` | Group | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 14.0 and later | Unit in seconds. Aggregation method is median. | +| `time_to_restore_service` | Project and group | [GitLab 14.9 and later](../../api/dora/metrics.md) | GitLab 15.1 and later | Unit in days. Aggregation method is median. | +| `change_failure_rate` | Project and group | [GitLab 14.10 and later](../../api/dora/metrics.md) | GitLab 15.2 and later | Percentage of deployments. | diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md index 56adbff9f59..01fe466755c 100644 --- a/doc/user/analytics/index.md +++ b/doc/user/analytics/index.md @@ -34,7 +34,7 @@ GitLab provides several analytics features at the group level. Some of these fea You can use GitLab to review analytics at the project level. Some of these features require you to use a higher tier than GitLab Free. - [Application Security](../application_security/security_dashboard/index.md) -- [CI/CD](ci_cd_analytics.md) +- [CI/CD & DORA](ci_cd_analytics.md) - [Code Review](code_review_analytics.md) - [Insights](../project/insights/index.md) - [Issue](../group/issues_analytics/index.md) @@ -51,93 +51,6 @@ The following analytics features are available for users to create personalized Be sure to review the documentation page for this feature for GitLab tier requirements. -## DevOps Research and Assessment (DORA) key metrics **(ULTIMATE)** - -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/275991) in GitLab 13.7. -> - [Added support](https://gitlab.com/gitlab-org/gitlab/-/issues/291746) for lead time for changes in GitLab 13.10. - -The [DevOps Research and Assessment (DORA)](https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance) -team developed several key metrics that you can use as performance indicators for software development -teams. - -### Deployment frequency - -Deployment frequency is the frequency of successful deployments to production (hourly, daily, weekly, monthly, or yearly). -This measures how often you deliver value to end users. A higher deployment frequency means you can -get feedback sooner and iterate faster to deliver improvements and features. GitLab measures this as the number of -deployments to a production environment in the given time period. - -Deployment frequency displays in several charts: - -- [Group-level value stream analytics](../group/value_stream_analytics/index.md) -- [Project-level value stream analytics](value_stream_analytics.md) -- [CI/CD analytics](ci_cd_analytics.md) - -To retrieve metrics for deployment frequency, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. - -### Lead time for changes - -DORA Lead time for changes measures the time to successfully deliver a feature into production. -This metric reflects the efficiency of CI/CD pipelines. - -In GitLab, Lead time for changes calculates the median time it takes for a merge request to get merged into production. -We measure "FROM code committed TO code successfully running in production" without adding the "coding_time" to the calculation. - -Over time, the lead time for changes should decrease, while your team's performance should increase. - -Lead time for changes displays in several charts: - -- [Group-level value stream analytics](../group/value_stream_analytics/index.md) -- [Project-level value stream analytics](value_stream_analytics.md) -- [CI/CD analytics](ci_cd_analytics.md) - -To retrieve metrics for lead time for changes, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. - -- The definition of lead time for change can vary widely, which often creates confusion within the industry. -- "Lead time for changes" is not the same as "Lead time". In the value stream, "Lead time" measures the time it takes for work on issue to move from the moment it's requested (Issue created) to the time it's fulfilled and delivered (Issue closed). - -### Time to restore service - -Time to restore service measures how long it takes an organization to recover from a failure in production. -GitLab measures this as the average time required to close the incidents -in the given time period. This assumes: - -- All incidents are related to a production environment. -- Incidents and deployments have a strictly one-to-one relationship. An incident is related to only -one production deployment, and any production deployment is related to no more than one incident). - -Time to restore service displays in several charts: - -- [Group-level value stream analytics](../group/value_stream_analytics/index.md) -- [Project-level value stream analytics](value_stream_analytics.md) -- [CI/CD analytics](ci_cd_analytics.md) - -To retrieve metrics for time to restore service, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. - -### Change failure rate - -Change failure rate measures the percentage of deployments that cause a failure in production. GitLab measures this as the number -of incidents divided by the number of deployments to a -production environment in the given time period. This assumes: - -- All incidents are related to a production environment. -- Incidents and deployments have a strictly one-to-one relationship. An incident is related to only -one production deployment, and any production deployment is related to no -more than one incident. - -To retrieve metrics for change failure rate, use the [GraphQL](../../api/graphql/reference/index.md) or the [REST](../../api/dora/metrics.md) APIs. - -### Supported DORA metrics in GitLab - -| Metric | Level | API | UI chart | Comments | -|---------------------------|-------------------|-----------------------------------------------------|------------------------|----------| -| `deployment_frequency` | Project | [GitLab 13.7 and later](../../api/dora/metrics.md) | GitLab 14.8 and later | The previous API endpoint was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/323713) in 13.10. | -| `deployment_frequency` | Group | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 13.12 and later | | -| `lead_time_for_changes` | Project | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 13.11 and later | Unit in seconds. Aggregation method is median. | -| `lead_time_for_changes` | Group | [GitLab 13.10 and later](../../api/dora/metrics.md) | GitLab 14.0 and later | Unit in seconds. Aggregation method is median. | -| `time_to_restore_service` | Project and group | [GitLab 14.9 and later](../../api/dora/metrics.md) | GitLab 15.1 and later | Unit in days. Aggregation method is median. | -| `change_failure_rate` | Project and group | [GitLab 14.10 and later](../../api/dora/metrics.md) | GitLab 15.2 and later | Percentage of deployments. | - ## Definitions We use the following terms to describe GitLab analytics: diff --git a/lib/api/api.rb b/lib/api/api.rb index 08269c81743..6f63d8998be 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -169,6 +169,7 @@ module API # Mount endpoints to include in the OpenAPI V2 documentation here namespace do + # Keep in alphabetical order mount ::API::AccessRequests mount ::API::Appearance mount ::API::BulkImports @@ -182,14 +183,16 @@ module API mount ::API::FeatureFlagsUserLists mount ::API::Features mount ::API::FreezePeriods - mount ::API::Metadata mount ::API::MergeRequestDiffs - mount ::API::UserCounts + mount ::API::Metadata mount ::API::ProjectRepositoryStorageMoves mount ::API::Release::Links + mount ::API::ResourceAccessTokens mount ::API::SnippetRepositoryStorageMoves mount ::API::Statistics + mount ::API::Suggestions mount ::API::Tags + mount ::API::UserCounts add_open_api_documentation! end @@ -299,7 +302,6 @@ module API mount ::API::Releases mount ::API::RemoteMirrors mount ::API::Repositories - mount ::API::ResourceAccessTokens mount ::API::ResourceLabelEvents mount ::API::ResourceMilestoneEvents mount ::API::ResourceStateEvents @@ -311,7 +313,6 @@ module API mount ::API::Snippets mount ::API::Submodules mount ::API::Subscriptions - mount ::API::Suggestions mount ::API::SystemHooks mount ::API::Tags mount ::API::Templates diff --git a/lib/api/entities/personal_access_token.rb b/lib/api/entities/personal_access_token.rb index 55764daef9d..305412d3dcc 100644 --- a/lib/api/entities/personal_access_token.rb +++ b/lib/api/entities/personal_access_token.rb @@ -3,9 +3,16 @@ module API module Entities class PersonalAccessToken < Grape::Entity - expose :id, :name, :revoked, :created_at, :scopes, :user_id, :last_used_at - expose :active?, as: :active - expose :expires_at do |personal_access_token| + expose :id, documentation: { type: 'string', example: 2 } + expose :name, documentation: { type: 'string', example: 'John Doe' } + expose :revoked, documentation: { type: 'boolean' } + expose :created_at, documentation: { type: 'dateTime' } + expose :scopes, documentation: { type: 'array', example: ['api'] } + expose :user_id, documentation: { type: 'string', example: 3 } + expose :last_used_at, documentation: { type: 'dateTime', example: '2020-08-31T15:53:00.073Z' } + expose :active?, as: :active, documentation: { type: 'boolean' } + expose :expires_at, documentation: + { type: 'dateTime', example: '2020-08-31T15:53:00.073Z' } do |personal_access_token| personal_access_token.expires_at ? personal_access_token.expires_at.strftime("%Y-%m-%d") : nil end end diff --git a/lib/api/entities/resource_access_token.rb b/lib/api/entities/resource_access_token.rb index 569fd16f488..e4f140d3fc0 100644 --- a/lib/api/entities/resource_access_token.rb +++ b/lib/api/entities/resource_access_token.rb @@ -3,7 +3,12 @@ module API module Entities class ResourceAccessToken < Entities::PersonalAccessToken - expose :access_level do |token, options| + expose :access_level, + documentation: { type: 'integer', + example: 40, + description: 'Access level. Valid values are 10 (Guest), 20 (Reporter), 30 (Developer) \ + , 40 (Maintainer), and 50 (Owner). Defaults to 40.', + values: [10, 20, 30, 40, 50] } do |token, options| options[:resource].member(token.user).access_level end end diff --git a/lib/api/resource_access_tokens.rb b/lib/api/resource_access_tokens.rb index 1735e63c566..754dfadb5fc 100644 --- a/lib/api/resource_access_tokens.rb +++ b/lib/api/resource_access_tokens.rb @@ -14,9 +14,12 @@ module API resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do desc 'Get list of all access tokens for the specified resource' do detail 'This feature was introduced in GitLab 13.9.' + is_array true + tags ["#{source_type}_access_tokens"] + success Entities::ResourceAccessToken end params do - requires :id, type: String, desc: "The #{source_type} ID" + requires :id, types: [String, Integer], desc: "ID or URL-encoded path of the #{source_type}" end get ":id/access_tokens" do resource = find_source(source_type, params[:id]) @@ -31,9 +34,11 @@ module API desc 'Get an access token for the specified resource by ID' do detail 'This feature was introduced in GitLab 14.10.' + tags ["#{source_type}_access_tokens"] + success Entities::ResourceAccessToken end params do - requires :id, type: String, desc: "The #{source_type} ID" + requires :id, types: [String, Integer], desc: "ID or URL-encoded path of the #{source_type}" requires :token_id, type: String, desc: "The ID of the token" end get ":id/access_tokens/:token_id" do @@ -53,6 +58,12 @@ module API desc 'Revoke a resource access token' do detail 'This feature was introduced in GitLab 13.9.' + tags ["#{source_type}_access_tokens"] + success code: 204 + failure [ + { code: 400, message: 'Bad Request' }, + { code: 404, message: 'Not found' } + ] end params do requires :id, type: String, desc: "The #{source_type} ID" @@ -77,13 +88,21 @@ module API desc 'Create a resource access token' do detail 'This feature was introduced in GitLab 13.9.' + tags ["#{source_type}_access_tokens"] + success Entities::ResourceAccessTokenWithToken end params do - requires :id, type: String, desc: "The #{source_type} ID" - requires :name, type: String, desc: "Resource access token name" - requires :scopes, type: Array[String], values: ::Gitlab::Auth.resource_bot_scopes.map(&:to_s), desc: "The permissions of the token" - optional :access_level, type: Integer, values: ALLOWED_RESOURCE_ACCESS_LEVELS.values, default: Gitlab::Access::MAINTAINER, desc: "The access level of the token in the #{source_type}" - optional :expires_at, type: Date, desc: "The expiration date of the token" + requires :id, type: String, desc: "The #{source_type} ID", documentation: { example: 2 } + requires :name, type: String, desc: "Resource access token name", documentation: { example: 'test' } + requires :scopes, type: Array[String], values: ::Gitlab::Auth.resource_bot_scopes.map(&:to_s), + desc: "The permissions of the token", + documentation: { example: %w[api read_repository] } + optional :access_level, type: Integer, + values: ALLOWED_RESOURCE_ACCESS_LEVELS.values, + default: Gitlab::Access::MAINTAINER, + desc: "The access level of the token in the #{source_type}", + documentation: { example: 40 } + optional :expires_at, type: Date, desc: "The expiration date of the token", documentation: { example: '"2021-01-31' } end post ':id/access_tokens' do resource = find_source(source_type, params[:id]) diff --git a/lib/api/suggestions.rb b/lib/api/suggestions.rb index 0697169b49a..6260983087f 100644 --- a/lib/api/suggestions.rb +++ b/lib/api/suggestions.rb @@ -9,9 +9,10 @@ module API resource :suggestions do desc 'Apply suggestion patch in the Merge Request it was created' do success Entities::Suggestion + tags %w[suggestions] end params do - requires :id, type: String, desc: 'The suggestion ID' + requires :id, type: Integer, desc: 'The ID of the suggestion' optional :commit_message, type: String, desc: "A custom commit message to use instead of the default generated message or the project's default message" end put ':id/apply', urgency: :low do @@ -26,9 +27,10 @@ module API desc 'Apply multiple suggestion patches in the Merge Request where they were created' do success Entities::Suggestion + tags %w[suggestions] end params do - requires :ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: "An array of suggestion ID's" + requires :ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: "An array of the suggestion IDs" optional :commit_message, type: String, desc: "A custom commit message to use instead of the default generated message or the project's default message" end put 'batch_apply', urgency: :low do diff --git a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb index 1abd75a7439..c59a4f468df 100644 --- a/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb +++ b/qa/qa/specs/features/api/1_manage/import/import_github_repo_spec.rb @@ -1,227 +1,194 @@ # frozen_string_literal: true module QA - # Spec uses real github.com, which means outage of github.com can actually block deployment - # Keep spec in reliable bucket but don't run in blocking pipelines - # # https://github.com/gitlab-qa-github/import-test <- project under test - RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do - describe 'Project import', issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/353583' do - let!(:api_client) { Runtime::API::Client.as_admin } - let!(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } } - let!(:user) do - Resource::User.fabricate_via_api! do |resource| - resource.api_client = api_client - resource.hard_delete_on_api_removal = true + # + RSpec.describe 'Manage', product_group: :import do + describe 'GitHub import', :reliable do + include_context 'with github import' + + context 'when imported via api' do + it 'imports project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347670' do + imported_project.reload! # import the project + + expect { imported_project.project_import_status[:import_status] }.to eventually_eq('finished') + .within(max_duration: 240, sleep_interval: 1) + + aggregate_failures do + verify_status_data + verify_repository_import + verify_protected_branches_import + verify_commits_import + verify_labels_import + verify_issues_import + verify_milestones_import + verify_wikis_import + verify_merge_requests_import + verify_release_import + end end - end - let!(:user_api_client) { Runtime::API::Client.new(user: user) } - - let(:imported_project) do - Resource::ProjectImportedFromGithub.fabricate_via_api! do |project| - project.name = 'imported-project' - project.group = group - project.github_personal_access_token = Runtime::Env.github_access_token - project.github_repository_path = 'gitlab-qa-github/import-test' - project.api_client = user_api_client - project.issue_events_import = true - project.full_notes_import = true + def verify_status_data + stats = imported_project.project_import_status.dig(:stats, :imported) + expect(stats).to include( + issue: 1, + label: 9, + milestone: 1, + note: 3, + pull_request: 1, + pull_request_review: 1, + diff_note: 1, + release: 1 + ) end - end - before do - group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) - end - - after do - user.remove_via_api! - end - - it 'imports Github repo via api', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347670' do - imported_project.reload! # import the project - - expect { imported_project.project_import_status[:import_status] }.to eventually_eq('finished') - .within(max_duration: 240, sleep_interval: 1) - - aggregate_failures do - verify_status_data - verify_repository_import - verify_protected_branches_import - verify_commits_import - verify_labels_import - verify_issues_import - verify_milestones_import - verify_wikis_import - verify_merge_requests_import - verify_release_import + def verify_repository_import + expect(imported_project.reload!.description).to eq('Project for github import test') + expect(imported_project.api_response[:import_error]).to be_nil end - end - def verify_status_data - stats = imported_project.project_import_status.dig(:stats, :imported) - expect(stats).to include( - issue: 1, - label: 9, - milestone: 1, - note: 3, - pull_request: 1, - pull_request_review: 1, - diff_note: 1, - release: 1 - ) - end - - def verify_repository_import - expect(imported_project.reload!.description).to eq('Project for github import test') - expect(imported_project.api_response[:import_error]).to be_nil - end - - def verify_protected_branches_import - branches = imported_project.protected_branches.map do |branch| - branch.slice(:name, :allow_force_push, :code_owner_approval_required) + def verify_protected_branches_import + branches = imported_project.protected_branches.map do |branch| + branch.slice(:name, :allow_force_push, :code_owner_approval_required) + end + expect(branches.first).to include( + { + name: 'main' + # TODO: Add validation once https://gitlab.com/groups/gitlab-org/-/epics/8585 is closed + # At the moment both options are always set to false regardless of state in github + # allow_force_push: true, + # code_owner_approval_required: true + } + ) end - expect(branches.first).to include( - { - name: 'main' - # TODO: Add validation once https://gitlab.com/groups/gitlab-org/-/epics/8585 is closed - # At the moment both options are always set to false regardless of state in github - # allow_force_push: true, - # code_owner_approval_required: true - } - ) - # GitHub branch protection rule "Require signed commits" is mapped to the - # "Reject unsigned commits" push rule - expect(imported_project.push_rules[:reject_unsigned_commits]).to be_truthy - end + def verify_commits_import + expect(imported_project.commits.length).to eq(2) + end - def verify_commits_import - expect(imported_project.commits.length).to eq(2) - end + def verify_labels_import + labels = imported_project.labels.map { |label| label.slice(:name, :color) } - def verify_labels_import - labels = imported_project.labels.map { |label| label.slice(:name, :color) } + expect(labels).to include( + { name: 'bug', color: '#d73a4a' }, + { name: 'documentation', color: '#0075ca' }, + { name: 'duplicate', color: '#cfd3d7' }, + { name: 'enhancement', color: '#a2eeef' }, + { name: 'good first issue', color: '#7057ff' }, + { name: 'help wanted', color: '#008672' }, + { name: 'invalid', color: '#e4e669' }, + { name: 'question', color: '#d876e3' }, + { name: 'wontfix', color: '#ffffff' } + ) + end - expect(labels).to include( - { name: 'bug', color: '#d73a4a' }, - { name: 'documentation', color: '#0075ca' }, - { name: 'duplicate', color: '#cfd3d7' }, - { name: 'enhancement', color: '#a2eeef' }, - { name: 'good first issue', color: '#7057ff' }, - { name: 'help wanted', color: '#008672' }, - { name: 'invalid', color: '#e4e669' }, - { name: 'question', color: '#d876e3' }, - { name: 'wontfix', color: '#ffffff' } - ) - end + def verify_milestones_import + milestones = imported_project.milestones - def verify_milestones_import - milestones = imported_project.milestones + expect(milestones.length).to eq(1) + expect(milestones.first).to include(title: '0.0.1', description: nil, state: 'active') + end - expect(milestones.length).to eq(1) - expect(milestones.first).to include(title: '0.0.1', description: nil, state: 'active') - end + def verify_wikis_import + wikis = imported_project.wikis - def verify_wikis_import - wikis = imported_project.wikis + expect(wikis.length).to eq(1) + expect(wikis.first).to include(title: 'Home', format: 'markdown') + end - expect(wikis.length).to eq(1) - expect(wikis.first).to include(title: 'Home', format: 'markdown') - end + def verify_issues_import + issues = imported_project.issues + issue = Resource::Issue.init do |resource| + resource.project = imported_project + resource.iid = issues.first[:iid] + resource.api_client = user_api_client + end.reload! + comments, events = fetch_events_and_comments(issue) - def verify_issues_import - issues = imported_project.issues - issue = Resource::Issue.init do |resource| - resource.project = imported_project - resource.iid = issues.first[:iid] - resource.api_client = user_api_client - end.reload! - comments, events = fetch_events_and_comments(issue) + expect(issues.length).to eq(1) + expect(issue.api_resource).to include( + title: 'Test issue', + description: "*Created by: gitlab-qa-github*\n\nTest issue description", + labels: ['good first issue', 'help wanted', 'question'] + ) + expect(comments).to match_array( + [ + "*Created by: gitlab-qa-github*\n\nSome test comment", + "*Created by: gitlab-qa-github*\n\nAnother test comment" + ] + ) + expect(events).to match_array( + [ + { name: "add_label", label: "question" }, + { name: "add_label", label: "good first issue" }, + { name: "add_label", label: "help wanted" }, + { name: "add_milestone", label: "0.0.1" }, + { name: "closed" }, + { name: "reopened" } + ] + ) + end - expect(issues.length).to eq(1) - expect(issue.api_resource).to include( - title: 'Test issue', - description: "*Created by: gitlab-qa-github*\n\nTest issue description", - labels: ['good first issue', 'help wanted', 'question'] - ) - expect(comments).to match_array( - [ - "*Created by: gitlab-qa-github*\n\nSome test comment", - "*Created by: gitlab-qa-github*\n\nAnother test comment" + def verify_merge_requests_import + merge_requests = imported_project.merge_requests + merge_request = Resource::MergeRequest.init do |mr| + mr.project = imported_project + mr.iid = merge_requests.first[:iid] + mr.api_client = user_api_client + end.reload! + comments, events = fetch_events_and_comments(merge_request) + + expect(merge_requests.length).to eq(1) + expect(merge_request.api_resource).to include( + title: 'Test pull request', + state: 'opened', + target_branch: 'main', + source_branch: 'gitlab-qa-github-patch-1', + labels: %w[documentation], + description: "*Created by: gitlab-qa-github*\n\nTest pull request body" + ) + expect(comments).to match_array( + [ + "*Created by: gitlab-qa-github*\n\n**Review:** Commented\n\nGood but needs some improvement", + "*Created by: gitlab-qa-github*\n\n```suggestion:-0+0\nProject for GitHub import test to GitLab\r\n```", + "*Created by: gitlab-qa-github*\n\nSome test PR comment" + ] + ) + expect(events).to match_array( + [ + { name: "add_label", label: "documentation" }, + { name: "add_milestone", label: "0.0.1" } + ] + ) + end + + def verify_release_import + releases = imported_project.releases + + expect(releases.length).to eq(1) + expect(releases.first).to include( + tag_name: "0.0.1", + name: "0.0.1", + description: "Initial release", + created_at: "2022-03-07T07:59:22.000Z", + released_at: "2022-03-07T08:02:09.000Z" + ) + end + + # Fetch events and comments from issue or mr + # + # @param [QA::Resource::Issuable] issuable + # @return [Array] + def fetch_events_and_comments(issuable) + comments = issuable.comments.map { |comment| comment[:body] } + events = [ + *issuable.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } }, + *issuable.state_events.map { |e| { name: e[:state] } }, + *issuable.milestone_events.map { |e| { name: "#{e[:action]}_milestone", label: e.dig(:milestone, :title) } } ] - ) - expect(events).to match_array( - [ - { name: "add_label", label: "question" }, - { name: "add_label", label: "good first issue" }, - { name: "add_label", label: "help wanted" }, - { name: "add_milestone", label: "0.0.1" }, - { name: "closed" }, - { name: "reopened" } - ] - ) - end - def verify_merge_requests_import - merge_requests = imported_project.merge_requests - merge_request = Resource::MergeRequest.init do |mr| - mr.project = imported_project - mr.iid = merge_requests.first[:iid] - mr.api_client = user_api_client - end.reload! - comments, events = fetch_events_and_comments(merge_request) - - expect(merge_requests.length).to eq(1) - expect(merge_request.api_resource).to include( - title: 'Test pull request', - state: 'opened', - target_branch: 'main', - source_branch: 'gitlab-qa-github-patch-1', - labels: %w[documentation], - description: "*Created by: gitlab-qa-github*\n\nTest pull request body" - ) - expect(comments).to match_array( - [ - "*Created by: gitlab-qa-github*\n\n**Review:** Commented\n\nGood but needs some improvement", - "*Created by: gitlab-qa-github*\n\n```suggestion:-0+0\nProject for GitHub import test to GitLab\r\n```", - "*Created by: gitlab-qa-github*\n\nSome test PR comment" - ] - ) - expect(events).to match_array( - [ - { name: "add_label", label: "documentation" }, - { name: "add_milestone", label: "0.0.1" } - ] - ) - end - - def verify_release_import - releases = imported_project.releases - - expect(releases.length).to eq(1) - expect(releases.first).to include( - tag_name: "0.0.1", - name: "0.0.1", - description: "Initial release", - created_at: "2022-03-07T07:59:22.000Z", - released_at: "2022-03-07T08:02:09.000Z" - ) - end - - # Fetch events and comments from issue or mr - # - # @param [QA::Resource::Issuable] issuable - # @return [Array] - def fetch_events_and_comments(issuable) - comments = issuable.comments.map { |comment| comment[:body] } - events = [ - *issuable.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } }, - *issuable.state_events.map { |e| { name: e[:state] } }, - *issuable.milestone_events.map { |e| { name: "#{e[:action]}_milestone", label: e.dig(:milestone, :title) } } - ] - - [comments, events] + [comments, events] + end end end end diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb index 4f0f54c1a15..15563e3aa2a 100644 --- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb +++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb @@ -4,88 +4,90 @@ module QA # Spec uses real github.com, which means outage of github can actually block deployment # Keep spec in reliable bucket but don't run in blocking pipelines RSpec.describe 'Manage', :github, :reliable, :skip_live_env, :requires_admin, product_group: :import do - describe 'Project import' do - let(:github_repo) { 'gitlab-qa-github/import-test' } - let(:api_client) { Runtime::API::Client.as_admin } - let(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } } - let(:user) do - Resource::User.fabricate_via_api! do |resource| - resource.api_client = api_client - resource.hard_delete_on_api_removal = true - end - end - - let(:imported_project) do - Resource::ProjectImportedFromGithub.init do |project| - project.import = true - project.group = group - project.github_personal_access_token = Runtime::Env.github_access_token - project.github_repository_path = github_repo - project.api_client = api_client - end - end - - let(:imported_issue) do - Resource::Issue.init do |resource| - resource.project = imported_project - resource.iid = imported_project.issues.first[:iid] - resource.api_client = api_client - end.reload! - end - - let(:imported_issue_events) do - imported_issue.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } } - end - - before do - group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) - - Flow::Login.sign_in(as: user) - Page::Main::Menu.perform(&:go_to_create_project) - Page::Project::New.perform do |project_page| - project_page.click_import_project - project_page.click_github_link - end - end - - after do - user.remove_via_api! - end - - it 'imports a GitHub repo', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do - Page::Project::Import::Github.perform do |import_page| - import_page.add_personal_access_token(Runtime::Env.github_access_token) - - import_page.select_advanced_option(:single_endpoint_issue_events_import) - import_page.select_advanced_option(:single_endpoint_notes_import) - import_page.select_advanced_option(:attachments_import) - - import_page.import!(github_repo, group.full_path, imported_project.name) - - aggregate_failures do - expect(import_page).to have_imported_project(github_repo, wait: 240) - # validate button is present instead of navigating to avoid dealing with multiple tabs - # which makes the test more complicated - expect(import_page).to have_go_to_project_button(github_repo) + describe 'GitHub import' do + context 'when imported via UI' do + let(:github_repo) { 'gitlab-qa-github/import-test' } + let(:api_client) { Runtime::API::Client.as_admin } + let(:group) { Resource::Group.fabricate_via_api! { |resource| resource.api_client = api_client } } + let(:user) do + Resource::User.fabricate_via_api! do |resource| + resource.api_client = api_client + resource.hard_delete_on_api_removal = true end end - imported_project.reload!.visit! - Page::Project::Show.perform do |project| - aggregate_failures do - expect(project).to have_content(imported_project.name) - expect(project).to have_content('Project for github import test') + let(:imported_project) do + Resource::ProjectImportedFromGithub.init do |project| + project.import = true + project.group = group + project.github_personal_access_token = Runtime::Env.github_access_token + project.github_repository_path = github_repo + project.api_client = api_client end end - # Validate :single_endpoint_issue_events_import option was triggered correctly and imported the events - expect(imported_issue_events).to match_array( - [ - { name: "add_label", label: "question" }, - { name: "add_label", label: "good first issue" }, - { name: "add_label", label: "help wanted" } - ] - ) + let(:imported_issue) do + Resource::Issue.init do |resource| + resource.project = imported_project + resource.iid = imported_project.issues.first[:iid] + resource.api_client = api_client + end.reload! + end + + let(:imported_issue_events) do + imported_issue.label_events.map { |e| { name: "#{e[:action]}_label", label: e.dig(:label, :name) } } + end + + before do + group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) + + Flow::Login.sign_in(as: user) + Page::Main::Menu.perform(&:go_to_create_project) + Page::Project::New.perform do |project_page| + project_page.click_import_project + project_page.click_github_link + end + end + + after do + user.remove_via_api! + end + + it 'imports a project', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347877' do + Page::Project::Import::Github.perform do |import_page| + import_page.add_personal_access_token(Runtime::Env.github_access_token) + + import_page.select_advanced_option(:single_endpoint_issue_events_import) + import_page.select_advanced_option(:single_endpoint_notes_import) + import_page.select_advanced_option(:attachments_import) + + import_page.import!(github_repo, group.full_path, imported_project.name) + + aggregate_failures do + expect(import_page).to have_imported_project(github_repo, wait: 240) + # validate button is present instead of navigating to avoid dealing with multiple tabs + # which makes the test more complicated + expect(import_page).to have_go_to_project_button(github_repo) + end + end + + imported_project.reload!.visit! + Page::Project::Show.perform do |project| + aggregate_failures do + expect(project).to have_content(imported_project.name) + expect(project).to have_content('Project for github import test') + end + end + + # Validate :single_endpoint_issue_events_import option was triggered correctly and imported the events + expect(imported_issue_events).to match_array( + [ + { name: "add_label", label: "question" }, + { name: "add_label", label: "good first issue" }, + { name: "add_label", label: "help wanted" } + ] + ) + end end end end diff --git a/qa/qa/specs/features/shared_contexts/github_import_shared_context.rb b/qa/qa/specs/features/shared_contexts/github_import_shared_context.rb new file mode 100644 index 00000000000..c32f5d8bf4b --- /dev/null +++ b/qa/qa/specs/features/shared_contexts/github_import_shared_context.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module QA + RSpec.shared_context "with github import", :github, :skip_live_env, :requires_admin do + let!(:api_client) { Runtime::API::Client.as_admin } + + let!(:group) do + Resource::Group.fabricate_via_api! do |resource| + resource.api_client = api_client + resource.path = "destination-group-for-import-#{SecureRandom.hex(4)}" + end + end + + let!(:user) do + Resource::User.fabricate_via_api! do |resource| + resource.api_client = api_client + resource.hard_delete_on_api_removal = true + end + end + + let!(:user_api_client) { Runtime::API::Client.new(user: user) } + + let(:imported_project) do + Resource::ProjectImportedFromGithub.fabricate_via_api! do |project| + project.name = 'imported-project' + project.group = group + project.github_personal_access_token = Runtime::Env.github_access_token + project.github_repository_path = 'gitlab-qa-github/import-test' + project.api_client = user_api_client + project.issue_events_import = true + project.full_notes_import = true + end + end + + before do + group.add_member(user, Resource::Members::AccessLevel::MAINTAINER) + end + + after do + user.remove_via_api! + end + end +end diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb index 97901042883..97ac7d41311 100644 --- a/qa/qa/specs/spec_helper.rb +++ b/qa/qa/specs/spec_helper.rb @@ -14,9 +14,6 @@ QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes) # Enable zero monkey patching mode before loading any other RSpec code. RSpec.configure(&:disable_monkey_patching!) -Dir[::File.join(__dir__, "features/shared_examples/*.rb")].sort.each { |f| require f } -Dir[::File.join(__dir__, "features/shared_contexts/*.rb")].sort.each { |f| require f } - # For JH additionally process when `jh/` exists require_relative('../../../jh/qa/qa/specs/spec_helper') if GitlabEdition.jh? @@ -149,3 +146,6 @@ RSpec.configure do |config| end end end + +Dir[::File.join(__dir__, "features/shared_examples/*.rb")].sort.each { |f| require f } +Dir[::File.join(__dir__, "features/shared_contexts/*.rb")].sort.each { |f| require f } diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb index 2393a268693..dfc5d169af6 100644 --- a/spec/requests/api/suggestions_spec.rb +++ b/spec/requests/api/suggestions_spec.rb @@ -91,7 +91,7 @@ RSpec.describe API::Suggestions do end context 'when suggestion is not found' do - let(:url) { "/suggestions/foo-123/apply" } + let(:url) { "/suggestions/9999/apply" } it 'renders a not found error and returns json content' do project.add_maintainer(user) @@ -103,6 +103,19 @@ RSpec.describe API::Suggestions do end end + context 'when suggestion ID is not valid' do + let(:url) { "/suggestions/foo-123/apply" } + + it 'renders a not found error and returns json content' do + project.add_maintainer(user) + + put api(url, user) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response).to eq({ 'error' => 'id is invalid' }) + end + end + context 'when unauthorized' do it 'renders a forbidden error and returns json content' do project.add_reporter(user)