From 0cb0f3c12597ba38f08d4cf9e38e3668d28ea544 Mon Sep 17 00:00:00 2001 From: Thong Kuah Date: Thu, 27 Sep 2018 21:36:55 +1200 Subject: [PATCH 01/38] Process $DB_INITIALIZE and $DB_MIGRATE variables if $DB_INITIALIZE is present, deploy an initial release where only $DB_INITIALIZE is run in a special job (and deployments are not rendered/loaded). This is then followed by second release with deployments as usual. if $DB_MIGRATE, set this value which will trigger a pre-upgrade helm hook. --- .../48004-db-initialize-migrate.yml | 5 ++ .../ci/templates/Auto-DevOps.gitlab-ci.yml | 73 ++++++++++++++----- 2 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 changelogs/unreleased/48004-db-initialize-migrate.yml diff --git a/changelogs/unreleased/48004-db-initialize-migrate.yml b/changelogs/unreleased/48004-db-initialize-migrate.yml new file mode 100644 index 00000000000..0d691babeca --- /dev/null +++ b/changelogs/unreleased/48004-db-initialize-migrate.yml @@ -0,0 +1,5 @@ +--- +title: Support db migration and initialization for Auto DevOps +merge_request: 21955 +author: +type: added diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index e3a2534e97a..727405c7acf 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -587,26 +587,59 @@ rollout 100%: secret_name='' fi - helm upgrade --install \ - --wait \ - --set service.enabled="$service_enabled" \ - --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ - --set image.repository="$CI_APPLICATION_REPOSITORY" \ - --set image.tag="$CI_APPLICATION_TAG" \ - --set image.pullPolicy=IfNotPresent \ - --set image.secrets[0].name="$secret_name" \ - --set application.track="$track" \ - --set application.database_url="$DATABASE_URL" \ - --set service.url="$CI_ENVIRONMENT_URL" \ - --set replicaCount="$replicas" \ - --set postgresql.enabled="$postgres_enabled" \ - --set postgresql.nameOverride="postgres" \ - --set postgresql.postgresUser="$POSTGRES_USER" \ - --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ - --set postgresql.postgresDatabase="$POSTGRES_DB" \ - --namespace="$KUBE_NAMESPACE" \ - "$name" \ - chart/ + if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then + helm upgrade --install \ + --wait \ + --set service.enabled="$service_enabled" \ + --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ + --set image.repository="$CI_APPLICATION_REPOSITORY" \ + --set image.tag="$CI_APPLICATION_TAG" \ + --set image.pullPolicy=IfNotPresent \ + --set image.secrets[0].name="$secret_name" \ + --set application.track="$track" \ + --set application.database_url="$DATABASE_URL" \ + --set service.url="$CI_ENVIRONMENT_URL" \ + --set replicaCount="$replicas" \ + --set postgresql.enabled="$postgres_enabled" \ + --set postgresql.nameOverride="postgres" \ + --set postgresql.postgresUser="$POSTGRES_USER" \ + --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ + --set postgresql.postgresDatabase="$POSTGRES_DB" \ + --set application.initializeCommand="$DB_INITIALIZE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + + helm upgrade --reuse-values \ + --wait \ + --set application.initializeCommand="" \ + --set application.migrateCommand="$DB_MIGRATE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + else + helm upgrade --install \ + --wait \ + --set service.enabled="$service_enabled" \ + --set releaseOverride="$CI_ENVIRONMENT_SLUG" \ + --set image.repository="$CI_APPLICATION_REPOSITORY" \ + --set image.tag="$CI_APPLICATION_TAG" \ + --set image.pullPolicy=IfNotPresent \ + --set image.secrets[0].name="$secret_name" \ + --set application.track="$track" \ + --set application.database_url="$DATABASE_URL" \ + --set service.url="$CI_ENVIRONMENT_URL" \ + --set replicaCount="$replicas" \ + --set postgresql.enabled="$postgres_enabled" \ + --set postgresql.nameOverride="postgres" \ + --set postgresql.postgresUser="$POSTGRES_USER" \ + --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ + --set postgresql.postgresDatabase="$POSTGRES_DB" \ + --set application.migrateCommand="$DB_MIGRATE" \ + --namespace="$KUBE_NAMESPACE" \ + "$name" \ + chart/ + fi kubectl rollout status -n "$KUBE_NAMESPACE" -w "deployment/$name" } From 0400b4b39147a1aeedd09af3cf794f5845391737 Mon Sep 17 00:00:00 2001 From: Evan Read Date: Mon, 10 Sep 2018 13:56:04 +1000 Subject: [PATCH 02/38] Add more introductory information to Review Apps page Also includes other refactoring. --- .../img/continuous-delivery-review-apps.svg | 48 ++++++ .../img/review_apps_preview_in_mr.png | Bin 11664 -> 29800 bytes doc/ci/review_apps/index.md | 161 ++++++++---------- 3 files changed, 119 insertions(+), 90 deletions(-) create mode 100644 doc/ci/review_apps/img/continuous-delivery-review-apps.svg diff --git a/doc/ci/review_apps/img/continuous-delivery-review-apps.svg b/doc/ci/review_apps/img/continuous-delivery-review-apps.svg new file mode 100644 index 00000000000..90ac763a01e --- /dev/null +++ b/doc/ci/review_apps/img/continuous-delivery-review-apps.svg @@ -0,0 +1,48 @@ + + + + review-apps-CD-outlined + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/ci/review_apps/img/review_apps_preview_in_mr.png b/doc/ci/review_apps/img/review_apps_preview_in_mr.png index 7d0923f198fa7b26d17e3f939149122283166edd..3e6506a6a3a1193d8ebabe63d930ec1e4b54761b 100644 GIT binary patch literal 29800 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 X3F*sDHVljEe}^h7sLR*LT7>@>R*)_> literal 11664 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 - [Introduced][ce-21971] in GitLab 8.12. Further additions were made in GitLab -> 8.13 and 8.14. -> - Inspired by [Heroku's Review Apps][heroku-apps] which itself was inspired by -> [Fourchette]. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/21971) in GitLab 8.12. Further additions were made in GitLab 8.13 and 8.14. +> - Inspired by [Heroku's Review Apps](https://devcenter.heroku.com/articles/github-integration-review-apps), which itself was inspired by [Fourchette](https://github.com/rainforestapp/fourchette). -The basis of Review Apps is the [dynamic environments] which allow you to create -a new environment (dynamically) for each one of your branches. - -A Review App can then be visible as a link when you visit the [merge request] -relevant to the branch. That way, you are able to see live all changes introduced -by the merge request changes. Reviewing anything, from performance to interface -changes, becomes much easier with a live environment and as such, Review Apps -can make a huge impact on your development flow. - -They mostly make sense to be used with web applications, but you can use them -any way you'd like. +For a video introduction to Review Apps, see [8.14 Webcast: Review Apps & Time Tracking Beta (EE) - GitLab Release](https://www.youtube.com/watch?v=CteZol_7pxo). ## Overview -Simply put, a Review App is a mapping of a branch with an environment as there -is a 1:1 relation between them. +Review Apps are a collaboration tool that takes the hard work out of providing an environment to showcase product changes. -Here's an example of what it looks like when viewing a merge request with a -dynamically set environment. +Review Apps: + +- Provide an automatic live preview of changes made in a feature branch by spinning up a dynamic environment for your merge requests. +- Allow designers and product manages to see your changes without needing to check out your branch and run your changes in a sandbox environment. +- Are fully integrated with the [GitLab DevOps LifeCycle](../../README.md#complete-devops-with-gitlab). +- Allow you to deploy your changes wherever you want. + +![Review Apps Workflow](img/continuous-delivery-review-apps.svg) + +Reviewing anything, from performance to interface changes, becomes much easier with a live environment and so Review Apps can make a large impact on your development flow. + +## What are Review Apps? + +A Review App is a mapping of a branch with an [environment](../environments.md). The following is an example of a merge request with an environment set dynamically. ![Review App in merge request](img/review_apps_preview_in_mr.png) -In the image above you can see that the `add-new-line` branch was successfully -built and deployed under a dynamic environment and can be previewed with an -also dynamically URL. +In this example, you can see a branch was: -The details of the Review Apps implementation depend widely on your real -technology stack and on your deployment process. The simplest case is to -deploy a simple static HTML website, but it will not be that straightforward -when your app is using a database for example. To make a branch be deployed -on a temporary instance and booting up this instance with all required software -and services automatically on the fly is not a trivial task. However, it is -doable, especially if you use Docker, or at least a configuration management -tool like Chef, Puppet, Ansible or Salt. +- Successfully built. +- Deployed under a dynamic environment that can be reached by clicking on the **View app** button. -## Prerequisites +## How do Review Apps work? -To get a better understanding of Review Apps, you must first learn how -environments and deployments work. The following docs will help you grasp that -knowledge: +The basis of Review Apps in GitLab is [dynamic environments](../environments.md#dynamic-environments), which allow you to dynamically create a new environment for each branch. -1. First, learn about [environments][] and their role in the development workflow. -1. Then make a small stop to learn about [CI variables][variables] and how they - can be used in your CI jobs. -1. Next, explore the [`environment` syntax][yaml-env] as defined in `.gitlab-ci.yml`. - This will be your primary reference when you are finally comfortable with - how environments work. -1. Additionally, find out about [manual actions][] and how you can use them to - deploy to critical environments like production with the push of a button. -1. And as a last step, follow the [example tutorials](#examples) which will - guide you step by step to set up the infrastructure and make use of - Review Apps. +Access to the Review App is made available as a link on the [merge request](../../user/project/merge_requests.md) relevant to the branch. Review Apps enable you to review all changes proposed by the merge request in live environment. -## Configuration +## Use cases -The configuration of Review apps depends on your technology stack and your -infrastructure. Read the [dynamic environments] documentation to understand -how to define and create them. +Some supported use cases include the: -## Creating and destroying Review Apps +- Simple case of deploying a simple static HTML website. +- More complicated case of an application that uses a database. Deploying a branch on a temporary instance and booting up this instance with all required software and services automatically on the fly is not a trivial task. However, it is possible, especially if you use Docker or a configuration management tool like Chef, Puppet, Ansible, or Salt. -The creation and destruction of a Review App is defined in `.gitlab-ci.yml` -at a job level under the `environment` keyword. +Review Apps usually make sense with web applications, but you can use them any way you'd like. -Check the [environments] documentation how to do so. +## Implementing Review Apps -## A simple workflow +Implementing Review Apps depends on your: -The process of adding Review Apps in your workflow would look like: +- Technology stack. +- Deployment process. + +### Prerequisite Knowledge + +To get a better understanding of Review Apps, review documentation on how environments and deployments work. Before you implement your own Review Apps: + +1. Learn about [environments](../environments.md) and their role in the development workflow. +1. Learn about [CI variables](../variables/README.md) and how they can be used in your CI jobs. +1. Explore the [`environment` syntax](../yaml/README.md#environment) as defined in `.gitlab-ci.yml`. This will become a primary reference. +1. Additionally, find out about [manual actions](../environments.md#manual-actions) and how you can use them to deploy to critical environments like production with the push of a button. +1. Follow the [example tutorials](#examples). These will guide you through setting up infrastructure and using Review Apps. + +### Configuring dynamic environments + +Configuring Review Apps dynamic environments depends on your technology stack and infrastructure. + +For more information, see [dynamic environments](../environments.md#dynamic-environments) documentation to understand how to define and create them. + +### Creating and destroying Review Apps + +Creating and destroying Review Apps is defined in `.gitlab-ci.yml` at a job level under the `environment` keyword. + +For more information, see [Introduction to environments and deployments](../environments.md). + +### Adding Review Apps to your workflow + +The process of adding Review Apps in your workflow is as follows: 1. Set up the infrastructure to host and deploy the Review Apps. -1. [Install][install-runner] and [configure][conf-runner] a Runner that does - the deployment. -1. Set up a job in `.gitlab-ci.yml` that uses the predefined - [predefined CI environment variable][variables] `${CI_COMMIT_REF_NAME}` to - create dynamic environments and restrict it to run only on branches. -1. Optionally set a job that [manually stops][manual-env] the Review Apps. +1. [Install](https://docs.gitlab.com/runner/install/) and [configure](https://docs.gitlab.com/runner/commands/) a Runner to do deployment. +1. Set up a job in `.gitlab-ci.yml` that uses the predefined [predefined CI environment variable](../variables/README.md) `${CI_COMMIT_REF_NAME}` to create dynamic environments and restrict it to run only on branches. +1. Optionally, set a job that [manually stops](../environments.md#stopping-an-environment) the Review Apps. -From there on, you would follow the branched Git flow: +After adding Review Apps to your workflow, you follow the branched Git flow. That is: -1. Push a branch and let the Runner deploy the Review App based on the `script` - definition of the dynamic environment job. -1. Wait for the Runner to build and/or deploy your web app. -1. Click on the link that's present in the MR related to the branch and see the - changes live. +1. Push a branch and let the Runner deploy the Review App based on the `script` definition of the dynamic environment job. +1. Wait for the Runner to build and deploy your web application. +1. Click on the link that provided in the merge request related to the branch to see the changes live. ## Limitations @@ -97,27 +96,9 @@ Check the [environments limitations](../environments.md#limitations). ## Examples -A list of examples used with Review Apps can be found below: +The following are example projects that use Review Apps with: -- [Use with NGINX][app-nginx] - Use NGINX and the shell executor of GitLab Runner - to deploy a simple HTML website. +- [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx). +- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift). -And below is a soon to be added examples list: - -- Use with Amazon S3 -- Use on Heroku with dpl -- Use with OpenShift/kubernetes - -[app-nginx]: https://gitlab.com/gitlab-examples/review-apps-nginx -[ce-21971]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21971 -[dynamic environments]: ../environments.md#dynamic-environments -[environments]: ../environments.md -[fourchette]: https://github.com/rainforestapp/fourchette -[heroku-apps]: https://devcenter.heroku.com/articles/github-integration-review-apps -[manual actions]: ../environments.md#manual-actions -[merge request]: ../../user/project/merge_requests.md -[variables]: ../variables/README.md -[yaml-env]: ../yaml/README.md#environment -[install-runner]: https://docs.gitlab.com/runner/install/ -[conf-runner]: https://docs.gitlab.com/runner/commands/ -[manual-env]: ../environments.md#stopping-an-environment +See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example. From 76a1a7a29d3b680b9be98051b57d6e0a2a9c8d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarka=20Ko=C5=A1anov=C3=A1?= Date: Mon, 1 Oct 2018 13:45:15 +0200 Subject: [PATCH 03/38] Extract status count key to a method --- app/finders/issuable_finder.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 251a559878a..fcb6ffaa993 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -128,7 +128,7 @@ class IssuableFinder labels_count = 1 if use_cte_for_search? finder.execute.reorder(nil).group(:state).count.each do |key, value| - counts[Array(key).last.to_sym] += value / labels_count + counts[count_key(key)] += value / labels_count end counts[:all] = counts.values.sum @@ -297,6 +297,10 @@ class IssuableFinder klass.all end + def count_key(value) + Array(value).last.to_sym + end + # rubocop: disable CodeReuse/ActiveRecord def by_scope(items) return items.none if current_user_related? && !current_user From 9b2e17ac71ee446da0f34dada41401803af816c7 Mon Sep 17 00:00:00 2001 From: Tiago Botelho Date: Mon, 1 Oct 2018 12:15:31 +0100 Subject: [PATCH 04/38] Adds WebIDE commits to UsagePing Implements UsageCounters model to track feature usage counters and makes easy to extend for future counters --- app/models/usage_counters.rb | 42 +++++++++++++++++++ ...5016-add-web-ide-commits-to-usage-ping.yml | 5 +++ .../20180929102611_create_usage_counters.rb | 13 ++++++ db/schema.rb | 8 +++- lib/api/commits.rb | 3 ++ lib/gitlab/usage_data.rb | 5 +++ spec/factories/usage_counters.rb | 6 +++ spec/lib/gitlab/import_export/all_models.yml | 3 ++ spec/lib/gitlab/usage_data_spec.rb | 1 + spec/models/usage_counters_spec.rb | 31 ++++++++++++++ spec/requests/api/commits_spec.rb | 6 +++ .../usage_counters_increment_service_spec.rb | 27 ++++++++++++ 12 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 app/models/usage_counters.rb create mode 100644 changelogs/unreleased/45016-add-web-ide-commits-to-usage-ping.yml create mode 100644 db/migrate/20180929102611_create_usage_counters.rb create mode 100644 spec/factories/usage_counters.rb create mode 100644 spec/models/usage_counters_spec.rb create mode 100644 spec/services/projects/usage_counters_increment_service_spec.rb diff --git a/app/models/usage_counters.rb b/app/models/usage_counters.rb new file mode 100644 index 00000000000..90f9c2a976e --- /dev/null +++ b/app/models/usage_counters.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +class UsageCounters < ActiveRecord::Base + RECORD_LIMIT = 1.freeze + BY = 1.freeze + BLACKLIST_ATTRIBUTES = %w(id created_at updated_at).freeze + + validate :ensure_only_one, on: :create + + default_value_for :web_ide_commits, 0 + + # This method supports concurrency so that several + # requests are able to increment the counter without + # us having inconsistent data + def increment_counters(attrs) + # We want to be able to use the service to increment + # both a single and multiple counters + attrs = Array(attrs) + + attrs_with_by = + attrs.each_with_object({}) do |attr, hsh| + hsh[attr] = BY + end + + self.class.update_counters(id, attrs_with_by) + end + + # Every attribute in this table except the blacklisted + # attributes is a counter + def totals + attributes.except(*BLACKLIST_ATTRIBUTES).symbolize_keys + end + + private + + # We only want one UsageCounters per instance + def ensure_only_one + return unless UsageCounters.count >= RECORD_LIMIT + + errors.add(:base, 'There can only be one usage counters record per instance') + end +end diff --git a/changelogs/unreleased/45016-add-web-ide-commits-to-usage-ping.yml b/changelogs/unreleased/45016-add-web-ide-commits-to-usage-ping.yml new file mode 100644 index 00000000000..a7f24742588 --- /dev/null +++ b/changelogs/unreleased/45016-add-web-ide-commits-to-usage-ping.yml @@ -0,0 +1,5 @@ +--- +title: Adds Web IDE commits to usage ping +merge_request: 22007 +author: +type: added diff --git a/db/migrate/20180929102611_create_usage_counters.rb b/db/migrate/20180929102611_create_usage_counters.rb new file mode 100644 index 00000000000..f17486bb513 --- /dev/null +++ b/db/migrate/20180929102611_create_usage_counters.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateUsageCounters < ActiveRecord::Migration + DOWNTIME = false + + def change + create_table :usage_counters do |t| + t.integer :web_ide_commits + + t.timestamps_with_timezone null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f92d8005dfb..fb57f2452d7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180917172041) do +ActiveRecord::Schema.define(version: 20180929102611) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -2080,6 +2080,12 @@ ActiveRecord::Schema.define(version: 20180917172041) do add_index "uploads", ["model_id", "model_type"], name: "index_uploads_on_model_id_and_model_type", using: :btree add_index "uploads", ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree + create_table "usage_counters", force: :cascade do |t| + t.integer "web_ide_commits" + t.datetime_with_timezone "created_at", null: false + t.datetime_with_timezone "updated_at", null: false + end + create_table "user_agent_details", force: :cascade do |t| t.string "user_agent", null: false t.string "ip_address", null: false diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 5aeffc8fb99..e16dd29d138 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -108,6 +108,9 @@ module API if result[:status] == :success commit_detail = user_project.repository.commit(result[:result]) + + UsageCounters.first_or_create.increment_counters(:web_ide_commits) if find_user_from_warden + present commit_detail, with: Entities::CommitDetail else render_api_error!(result[:message], 400) diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index f7d8ee571cd..afab36e89dd 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -10,6 +10,7 @@ module Gitlab .merge(features_usage_data) .merge(components_usage_data) .merge(cycle_analytics_usage_data) + .merge(usage_counters) end def to_json(force_refresh: false) @@ -106,6 +107,10 @@ module Gitlab } end + def usage_counters + UsageCounters.first_or_create.totals + end + def components_usage_data { gitlab_pages: { enabled: Gitlab.config.pages.enabled, version: Gitlab::Pages::VERSION }, diff --git a/spec/factories/usage_counters.rb b/spec/factories/usage_counters.rb new file mode 100644 index 00000000000..23277fd741e --- /dev/null +++ b/spec/factories/usage_counters.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :usage_counters, class: 'UsageCounters' do + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index ec2bdbe22e1..b2a0afcb827 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -330,3 +330,6 @@ resource_label_events: - merge_request - epic - label +usage_counters: +- project +- web_ide_commits diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 1ec1fe10744..d669c42ab4a 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -46,6 +46,7 @@ describe Gitlab::UsageData do git database avg_cycle_analytics + web_ide_commits )) end diff --git a/spec/models/usage_counters_spec.rb b/spec/models/usage_counters_spec.rb new file mode 100644 index 00000000000..0adbfe3cef7 --- /dev/null +++ b/spec/models/usage_counters_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe UsageCounters do + let!(:usage_counters) { create(:usage_counters) } + + describe 'maximum number of records' do + it 'allows for one single record to be created' do + expect do + described_class.create! + end.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: There can only be one usage counters record per instance') + end + end + + describe '#totals' do + subject { usage_counters.totals } + + it 'returns counters' do + is_expected.to include(web_ide_commits: 0) + end + end + + describe '#increment_counters' do + it 'increments specified counters by 1' do + expect do + usage_counters.increment_counters(:web_ide_commits) + end.to change { usage_counters.reload.web_ide_commits }.from(0).to(1) + end + end +end diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index f3fb88474a4..aebc24dd9a2 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -278,6 +278,12 @@ describe API::Commits do } end + it 'does not increment the usage counters using access token authentication' do + post api(url, user), valid_c_params + + expect_any_instance_of(::UsageCounters).not_to receive(:increment_counters) + end + it 'a new file in project repo' do post api(url, user), valid_c_params diff --git a/spec/services/projects/usage_counters_increment_service_spec.rb b/spec/services/projects/usage_counters_increment_service_spec.rb new file mode 100644 index 00000000000..d82a7337665 --- /dev/null +++ b/spec/services/projects/usage_counters_increment_service_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Projects::UsageCountersIncrementService do + let(:project) { create(:usage_counters) } + + subject(:service) { described_class.new(project) } + + context '#execute' do + context 'when single attribute is passed' do + it 'increments attribute' do + expect do + service.execute(:web_ide_commits) + end.to change { project.usage_counters.reload.web_ide_commits }.from(0).to(1) + end + end + + context 'when array is passed' do + it 'increments specified attributes' do + expect do + service.execute(%i(web_ide_commits)) + end.to change { project.usage_counters.reload.web_ide_commits }.from(0).to(1) + end + end + end +end From 6a63546fbcf196f2968e9c8f5db42ce5863ddd9b Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Tue, 2 Oct 2018 13:09:43 +1000 Subject: [PATCH 05/38] Speed up karma runs for development Using DefinePlugin means the block wrapped in GENERATE_COVERAGE_REPORT gets eliminated. Previously it was always included (with the entire app!), even if using -f option --- config/karma.config.js | 7 ++++++- spec/javascripts/test_bundle.js | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/config/karma.config.js b/config/karma.config.js index 74dc5c13c70..cad3c0e9193 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -6,6 +6,7 @@ const argumentsParser = require('commander'); const webpackConfig = require('./webpack.config.js'); const ROOT_PATH = path.resolve(__dirname, '..'); +const GENERATE_COVERAGE_REPORT = process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage'; function fatalError(message) { console.error(chalk.red(`\nError: ${message}\n`)); @@ -22,6 +23,10 @@ webpackConfig.optimization.splitChunks = false; // use quicker sourcemap option webpackConfig.devtool = 'cheap-inline-source-map'; +webpackConfig.plugins.push( + new webpack.DefinePlugin({ GENERATE_COVERAGE_REPORT }), +); + const specFilters = argumentsParser .option( '-f, --filter-spec [filter]', @@ -118,7 +123,7 @@ module.exports = function(config) { }; } - if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') { + if (GENERATE_COVERAGE_REPORT) { karmaConfig.reporters.push('coverage-istanbul'); karmaConfig.coverageIstanbulReporter = { reports: ['html', 'text-summary'], diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 96c0844f83c..ffabd661c09 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -155,8 +155,8 @@ describe('test errors', () => { // if we're generating coverage reports, make sure to include all files so // that we can catch files with 0% coverage // see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15 -if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report +if (GENERATE_COVERAGE_REPORT) { const troubleMakers = [ './blob_edit/blob_bundle.js', './boards/components/modal/empty_state.vue', From 47a87fbcd80e416a45b87499f5b6f9052990c1aa Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Tue, 2 Oct 2018 19:01:41 +1000 Subject: [PATCH 06/38] Fix lint and comments --- spec/javascripts/test_bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index ffabd661c09..d0fba401115 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -155,8 +155,8 @@ describe('test errors', () => { // if we're generating coverage reports, make sure to include all files so // that we can catch files with 0% coverage // see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15 +if (GENERATE_COVERAGE_REPORT) { // eslint-disable-line no-undef // exempt these files from the coverage report -if (GENERATE_COVERAGE_REPORT) { const troubleMakers = [ './blob_edit/blob_bundle.js', './boards/components/modal/empty_state.vue', From 681b7d7b49313f4bf380bace7042e7972d32b4a6 Mon Sep 17 00:00:00 2001 From: Thong Kuah Date: Tue, 2 Oct 2018 22:06:01 +1300 Subject: [PATCH 07/38] Document how to use DB_INITIALIZE and DB_MIGRATE * Limitation of DB_INITIALIZE (run once) * helm hooks * Examples --- doc/topics/autodevops/index.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 681dc8ff20d..646f716c0d0 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -440,6 +440,28 @@ no longer be valid as soon as the deployment job finishes. This means that Kubernetes can run the application, but in case it should be restarted or executed somewhere else, it cannot be accessed again. +> [Introduced][ce-21955] in GitLab 11.4 + +Database initialization and migrations can be configured to run within +the application pod by setting the project variables `DB_INITIALIZE` and +`DB_MIGRATE` respectively. + +If present, `DB_INITIALIZE` will be run as a shell command within an application pod as a helm +post-install hook. Note that this means that if any deploy succeeds, +`DB_INITIALIZE` will not be processed thereafter. + +If present, `DB_MIGRATE` will be run as a shell command within an application pod as +a helm pre-upgrade hook. + +For example, in a rails application : + +* `DB_INITIALIZE` can be set to `cd /app && RAILS_ENV=production + bin/setup` +* `DB_MIGRATE` can be set to `cd /app && RAILS_ENV=production bin/update` + +Note that `/app` is the location of the application as [configured by +Herokuish](https://github.com/gliderlabs/herokuish#paths) + > [Introduced][ce-19507] in GitLab 11.0. For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token) @@ -581,6 +603,8 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac | `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` | | `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.| | `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).| +| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's database. Run inside the application pod. | +| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's database. Run inside the application pod. | | `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | | `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). | | `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. | @@ -834,4 +858,5 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/ [postgresql]: https://www.postgresql.org/ [Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml [ee]: https://about.gitlab.com/pricing/ +[ce-21955]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21955 [ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507 From 23ba3b02b1988205043253aee9ba91ed802f01c5 Mon Sep 17 00:00:00 2001 From: Dylan Griffith Date: Tue, 2 Oct 2018 15:40:56 +0300 Subject: [PATCH 08/38] Minor doc update for auto devops db migrations --- doc/topics/autodevops/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 646f716c0d0..f67d35dac9e 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -459,7 +459,9 @@ For example, in a rails application : bin/setup` * `DB_MIGRATE` can be set to `cd /app && RAILS_ENV=production bin/update` -Note that `/app` is the location of the application as [configured by +NOTE: **Note:** +The `/app` path is the directory of your project inside the docker image +as [configured by Herokuish](https://github.com/gliderlabs/herokuish#paths) > [Introduced][ce-19507] in GitLab 11.0. From 5919c8fb90dd3cd17ef2b62e881718e1ce517ac9 Mon Sep 17 00:00:00 2001 From: Mark Lapierre Date: Tue, 2 Oct 2018 15:26:22 -0400 Subject: [PATCH 09/38] Revisit the login page when attempt to retry login When registration fails to successfully sign in, it looks like the UI has a sign-out option, but it doesn't. So instead of trying to use the UI, revisit the login page directly. --- qa/qa/factory/resource/fork.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/qa/factory/resource/fork.rb b/qa/qa/factory/resource/fork.rb index 92050eaba2a..1fa47e92983 100644 --- a/qa/qa/factory/resource/fork.rb +++ b/qa/qa/factory/resource/fork.rb @@ -37,7 +37,7 @@ module QA puts "Not signed in. Attempting to sign in again." Capybara::Screenshot.screenshot_and_save_page - Page::Menu::Main.act { sign_out } + Runtime::Browser.visit(:gitlab, Page::Main::Login) Page::Main::Login.perform do |login| login.sign_in_using_credentials(user) From a99bf447a24957cf11b89d4f04a2b84613367ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Rodr=C3=ADguez?= Date: Tue, 2 Oct 2018 00:21:46 -0300 Subject: [PATCH 10/38] Remove Gitlab::Git::Repository#rugged and Gollum code Cleanup code, and refactor tests that still use Rugged. After this, there should be no Rugged code that access the instance's repositories on non-test environments. There is still some rugged code for other tasks like the repository import task, but since it doesn't access any repository storage path it can stay. --- .rubocop_todo.yml | 1 - Gemfile | 5 +- Gemfile.lock | 5 +- Gemfile.rails5.lock | 5 +- app/models/wiki_page.rb | 7 +- config/initializers/8_metrics.rb | 12 - config/initializers/gollum.rb | 28 --- doc/development/diffs.md | 3 - doc/development/gitaly.md | 57 +---- doc/development/instrumentation.md | 4 +- lib/gitlab/git/commit.rb | 12 +- lib/gitlab/git/ref.rb | 9 - lib/gitlab/git/repository.rb | 39 ---- lib/gitlab/git/wiki.rb | 29 +-- lib/gitlab/git/wiki_file.rb | 17 +- lib/gitlab/git/wiki_page.rb | 28 +-- lib/gitlab/git/wiki_page_version.rb | 5 - lib/gitlab/gitaly_client/wiki_service.rb | 2 +- scripts/lint-rugged | 14 +- spec/features/markdown/markdown_spec.rb | 6 +- .../wiki/user_views_wiki_page_spec.rb | 13 +- ...ze_merge_request_diffs_and_commits_spec.rb | 8 +- spec/lib/gitlab/conflict/file_spec.rb | 4 +- spec/lib/gitlab/git/blob_spec.rb | 17 +- spec/lib/gitlab/git/branch_spec.rb | 8 +- spec/lib/gitlab/git/commit_spec.rb | 31 +-- spec/lib/gitlab/git/diff_spec.rb | 69 ++---- spec/lib/gitlab/git/repository_spec.rb | 214 +++++------------- spec/lib/gitlab/git_access_spec.rb | 27 ++- .../gitlab/gitaly_client/wiki_service_spec.rb | 4 + spec/models/namespace_spec.rb | 5 +- spec/models/project_spec.rb | 5 +- spec/models/remote_mirror_spec.rb | 6 +- spec/models/repository_spec.rb | 24 +- spec/models/wiki_page_spec.rb | 2 +- spec/services/git_tag_push_service_spec.rb | 5 +- .../merge_requests/squash_service_spec.rb | 6 +- .../projects/after_import_service_spec.rb | 4 +- spec/services/projects/create_service_spec.rb | 6 +- .../migrate_repository_service_spec.rb | 6 +- .../projects/transfer_service_spec.rb | 6 +- .../helpers/cycle_analytics_helpers.rb | 13 +- spec/support/helpers/git_helpers.rb | 6 + .../git_garbage_collect_worker_spec.rb | 6 +- .../repository_remove_remote_worker_spec.rb | 5 +- vendor/licenses.csv | 1 - 46 files changed, 223 insertions(+), 566 deletions(-) delete mode 100644 config/initializers/gollum.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d8c4e965190..d4f7615c80e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -445,7 +445,6 @@ Style/Dir: # Cop supports --auto-correct. Style/EachWithObject: Exclude: - - 'config/initializers/gollum.rb' - 'lib/expand_variables.rb' - 'lib/gitlab/ci/ansi2html.rb' - 'lib/gitlab/ee_compat_check.rb' diff --git a/Gemfile b/Gemfile index ecf5e6392c0..52de588deb3 100644 --- a/Gemfile +++ b/Gemfile @@ -80,11 +80,9 @@ gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap' gem 'net-ldap' # Git Wiki -# Required manually in config/initializers/gollum.rb to control load order +# Only used to compute wiki page slugs gem 'gitlab-gollum-lib', '~> 4.2', require: false -gem 'gitlab-gollum-rugged_adapter', '~> 0.4.4', require: false - # Language detection gem 'github-linguist', '~> 5.3.3', require: 'linguist' @@ -134,6 +132,7 @@ gem 'seed-fu', '~> 2.3.7' gem 'html-pipeline', '~> 2.8' gem 'deckar01-task_list', '2.0.0' gem 'gitlab-markup', '~> 1.6.4' +gem 'github-markup', '~> 1.7.0', require: 'github/markup' gem 'redcarpet', '~> 3.4' gem 'commonmarker', '~> 0.17' gem 'RedCloth', '~> 4.3.2' diff --git a/Gemfile.lock b/Gemfile.lock index 910e07f4a66..75e8d855d4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -295,9 +295,6 @@ GEM rouge (~> 3.1) sanitize (~> 4.6.4) stringex (~> 2.6) - gitlab-gollum-rugged_adapter (0.4.4.1) - mime-types (>= 1.15) - rugged (~> 0.25) gitlab-grit (2.8.2) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) @@ -1030,9 +1027,9 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-linguist (~> 5.3.3) + github-markup (~> 1.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) - gitlab-gollum-rugged_adapter (~> 0.4.4) gitlab-markup (~> 1.6.4) gitlab-styles (~> 2.4) gitlab_omniauth-ldap (~> 2.0.4) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 5959dc0e114..78cae4dc774 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -298,9 +298,6 @@ GEM rouge (~> 3.1) sanitize (~> 4.6.4) stringex (~> 2.6) - gitlab-gollum-rugged_adapter (0.4.4.1) - mime-types (>= 1.15) - rugged (~> 0.25) gitlab-grit (2.8.2) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) @@ -1039,9 +1036,9 @@ DEPENDENCIES gettext_i18n_rails_js (~> 1.3) gitaly-proto (~> 0.118.1) github-linguist (~> 5.3.3) + github-markup (~> 1.7.0) gitlab-flowdock-git-hook (~> 1.0.1) gitlab-gollum-lib (~> 4.2) - gitlab-gollum-rugged_adapter (~> 0.4.4) gitlab-markup (~> 1.6.4) gitlab-styles (~> 2.4) gitlab_omniauth-ldap (~> 2.0.4) diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 102907a8bd3..42fd213d03b 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -58,7 +58,7 @@ class WikiPage attr_reader :page # The attributes Hash used for storing and validating - # new Page values before writing to the Gollum repository. + # new Page values before writing to the raw repository. attr_accessor :attributes def hook_attrs @@ -111,10 +111,7 @@ class WikiPage # The processed/formatted content of this page. def formatted_content - # Assuming @page exists, nil formatted_data means we didn't load it - # before hand (i.e. page was fetched by Gitaly), so we fetch it separately. - # If the page was fetched by Gollum, formatted_data would've been a String. - @attributes[:formatted_content] ||= @page&.formatted_data || @wiki.page_formatted_data(@page) + @attributes[:formatted_content] ||= @wiki.page_formatted_data(@page) end # The markup format for the page. diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index 4d8d35bf6cf..eccf82ab8dc 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -3,7 +3,6 @@ # that we can stub it for testing, as it is only called when metrics are # enabled. # -# rubocop:disable Metrics/AbcSize def instrument_classes(instrumentation) instrumentation.instrument_instance_methods(Gitlab::Shell) @@ -48,16 +47,6 @@ def instrument_classes(instrumentation) instrumentation.instrument_methods(Premailer::Adapter::Nokogiri) instrumentation.instrument_instance_methods(Premailer::Adapter::Nokogiri) - [ - :Blame, :Branch, :BranchCollection, :Blob, :Commit, :Diff, :Repository, - :Tag, :TagCollection, :Tree - ].each do |name| - const = Rugged.const_get(name) - - instrumentation.instrument_methods(const) - instrumentation.instrument_instance_methods(const) - end - instrumentation.instrument_methods(Banzai::Renderer) instrumentation.instrument_methods(Banzai::Querying) @@ -101,7 +90,6 @@ def instrument_classes(instrumentation) # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/30224#note_32306159 instrumentation.instrument_instance_method(MergeRequestDiff, :load_commits) end -# rubocop:enable Metrics/AbcSize # With prometheus enabled by default this breaks all specs # that stubs methods using `any_instance_of` for the models reloaded here. diff --git a/config/initializers/gollum.rb b/config/initializers/gollum.rb deleted file mode 100644 index ea9cc151a57..00000000000 --- a/config/initializers/gollum.rb +++ /dev/null @@ -1,28 +0,0 @@ -# WARNING changes in this file must be manually propagated to gitaly-ruby. -# -# https://gitlab.com/gitlab-org/gitaly/blob/master/ruby/lib/gitlab/gollum.rb - -module Gollum - GIT_ADAPTER = "rugged".freeze -end -require "gollum-lib" - -module Gollum - class Page - def text_data(encoding = nil) - data = if raw_data.respond_to?(:encoding) - raw_data.force_encoding(encoding || Encoding::UTF_8) - else - raw_data - end - - Gitlab::EncodingHelper.encode!(data) - end - end -end - -Rails.application.configure do - config.after_initialize do - Gollum::Page.per_page = Kaminari.config.default_per_page - end -end diff --git a/doc/development/diffs.md b/doc/development/diffs.md index 5e8e8cc7541..c8ced445027 100644 --- a/doc/development/diffs.md +++ b/doc/development/diffs.md @@ -2,13 +2,10 @@ Currently we rely on different sources to present diffs, these include: -- Rugged gem - Gitaly service - Database (through `merge_request_diff_files`) - Redis (cached highlighted diffs) -We're constantly moving Rugged calls to Gitaly and the progress can be followed through [Gitaly repo](https://gitlab.com/gitlab-org/gitaly). - ## Architecture overview ### Merge request diffs diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md index f4784c19359..32beafad307 100644 --- a/doc/development/gitaly.md +++ b/doc/development/gitaly.md @@ -1,11 +1,7 @@ # GitLab Developers Guide to Working with Gitaly [Gitaly](https://gitlab.com/gitlab-org/gitaly) is a high-level Git RPC service used by GitLab CE/EE, -Workhorse and GitLab-Shell. All Rugged operations in GitLab CE/EE are currently being phased out to -be replaced by Gitaly API calls. - -Visit the [Gitaly Migration Board](https://gitlab.com/gitlab-org/gitaly/boards/331341) for current -status of the migration. +Workhorse and GitLab-Shell. ## Developing new Git features @@ -52,57 +48,6 @@ comfortable writing Go code. There is documentation for this approach in [the Gitaly repo](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/ruby_endpoint.md). -## Modifying existing Git features - -If you modify existing Git features in `lib/gitlab/git` you need to make -sure the changes also work in Gitaly. Because we are still in the -migration process there are a number of subtle pitfalls. Features that -have been migrated have dual implementations (Gitaly and local). The -Gitaly implementation may or may not use a vendored (and therefore -possibly outdated) copy of the local implementation in `lib/gitlab/git`. - -To avoid unexpected problems and conflicts, all changes to -`lib/gitlab/git` need to be approved by a member of the Gitaly team. - -For the time being, while the Gitaly migration is still in progress, -there should be no Enterprise Edition-only Git code in -`lib/gitlab/git`. Also no mixins. - -## Feature Flags - -Gitaly makes heavy use of [feature flags](feature_flags.md). - -Each Rugged-to-Gitaly migration goes through a [series of phases](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/MIGRATION_PROCESS.md): - -* **Opt-In**: by default the Rugged implementation is used. - * Production instances can choose to enable the Gitaly endpoint by enabling the feature flag. - * For testing purposes, you may wish to enable all feature flags by default. This can be done by exporting the following - environment variable: `GITALY_FEATURE_DEFAULT_ON=1`. - * On developer instances (ie, when `Rails.env.development?` is true), the Gitaly endpoint - is enabled by default, but can be _disabled_ using feature flags. -* **Opt-Out**: by default, the Gitaly endpoint is used, but the feature can be explicitly disabled using the feature flag. -* **Mandatory**: The migration is complete and cannot be disabled. The old codepath is removed. - -### Enabling and Disabling Feature - -In the Rails console, type: - -```ruby -Feature.enable(:gitaly_feature_name) -Feature.disable(:gitaly_feature_name) -``` - -Where `gitaly_feature_name` is the name of the Gitaly feature. This can be determined by finding the appropriate -`gitaly_migrate` code block, for example: - -```ruby -gitaly_migrate(:tag_names) do -... -end -``` - -Since Gitaly features are always prefixed with `gitaly_`, the name of the feature flag in this case would be `gitaly_tag_names`. - ## Gitaly-Related Test Failures If your test-suite is failing with Gitaly issues, as a first step, try running: diff --git a/doc/development/instrumentation.md b/doc/development/instrumentation.md index a14c0752366..7761f65d78a 100644 --- a/doc/development/instrumentation.md +++ b/doc/development/instrumentation.md @@ -69,7 +69,7 @@ The easiest way to check if a method has been instrumented is to check its source location. For example: ```ruby -method = Rugged::TagCollection.instance_method(:[]) +method = Banzai::Renderer.method(:render) method.source_location ``` @@ -82,7 +82,7 @@ method (along with its source location), this is easier than running the above Ruby code. In case of the above snippet you'd run the following: ``` -$ Rugged::TagCollection#[] +$ Banzai::Renderer.render ``` This will print out something along the lines of: diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb index 5b264868af0..74cdabfed9d 100644 --- a/lib/gitlab/git/commit.rb +++ b/lib/gitlab/git/commit.rb @@ -53,9 +53,6 @@ module Gitlab # Already a commit? return commit_id if commit_id.is_a?(Gitlab::Git::Commit) - # A rugged reference? - commit_id = Gitlab::Git::Ref.dereference_object(commit_id) - # Some weird thing? return nil unless commit_id.is_a?(String) @@ -127,8 +124,6 @@ module Gitlab # :topo, or any combination of them (in an array). Commit ordering types # are documented here: # http://www.rubydoc.info/github/libgit2/rugged/Rugged#SORT_NONE-constant) - # - # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/326 def find_all(repo, options = {}) repo.wrapped_gitaly_errors do Gitlab::GitalyClient::CommitService.new(repo).find_all_commits(options) @@ -328,7 +323,6 @@ module Gitlab entry = @repository.gitaly_commit_client.tree_entry(id, path, 1) return unless entry - # To be compatible with the rugged format entry = entry.to_h entry.delete(:data) entry[:name] = File.basename(path) @@ -346,8 +340,8 @@ module Gitlab subject: message_split[0] ? message_split[0].chomp.b : "", body: raw_commit.message.b, parent_ids: raw_commit.parent_ids, - author: gitaly_commit_author_from_rugged(raw_commit.author), - committer: gitaly_commit_author_from_rugged(raw_commit.committer) + author: gitaly_commit_author_from_raw(raw_commit.author), + committer: gitaly_commit_author_from_raw(raw_commit.committer) ) end @@ -381,7 +375,7 @@ module Gitlab SERIALIZE_KEYS end - def gitaly_commit_author_from_rugged(author_or_committer) + def gitaly_commit_author_from_raw(author_or_committer) Gitaly::CommitAuthor.new( name: author_or_committer[:name].b, email: author_or_committer[:email].b, diff --git a/lib/gitlab/git/ref.rb b/lib/gitlab/git/ref.rb index fa71a4e7ea7..31a280155bd 100644 --- a/lib/gitlab/git/ref.rb +++ b/lib/gitlab/git/ref.rb @@ -1,5 +1,3 @@ -# Gitaly note: JV: probably no RPC's here (just one interaction with Rugged). - module Gitlab module Git class Ref @@ -26,13 +24,6 @@ module Gitlab str.gsub(%r{\Arefs/heads/}, '') end - # Gitaly: this method will probably be migrated indirectly via its call sites. - def self.dereference_object(object) - object = object.target while object.is_a?(Rugged::Tag::Annotation) - - object - end - def initialize(repository, name, target, dereferenced_target) @name = Gitlab::Git.ref_name(name) @dereferenced_target = dereferenced_target diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 45d42c7078f..7732049b69b 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -9,14 +9,6 @@ module Gitlab include Gitlab::EncodingHelper include Gitlab::Utils::StrongMemoize - ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[ - GIT_OBJECT_DIRECTORY - GIT_ALTERNATE_OBJECT_DIRECTORIES - ].freeze - ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES = %w[ - GIT_OBJECT_DIRECTORY_RELATIVE - GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE - ].freeze SEARCH_CONTEXT_LINES = 3 REV_LIST_COMMIT_LIMIT = 2_000 # In https://gitlab.com/gitlab-org/gitaly/merge_requests/698 @@ -104,15 +96,6 @@ module Gitlab raise Gitlab::Git::CommandError.new(e.message) end - # This method will be removed when Gitaly reaches v1.1. - def rugged - circuit_breaker.perform do - Rugged::Repository.new(path, alternates: alternate_object_directories) - end - rescue Rugged::RepositoryError, Rugged::OSError - raise NoRepository.new('no repository for such path') - end - def circuit_breaker @circuit_breaker ||= Gitlab::Git::Storage::CircuitBreaker.for_storage(storage) end @@ -638,20 +621,6 @@ module Gitlab end end - AUTOCRLF_VALUES = { - "true" => true, - "false" => false, - "input" => :input - }.freeze - - def autocrlf - AUTOCRLF_VALUES[rugged.config['core.autocrlf']] - end - - def autocrlf=(value) - rugged.config['core.autocrlf'] = AUTOCRLF_VALUES.invert[value] - end - # Returns result like "git ls-files" , recursive and full file path # # Ex. @@ -1024,14 +993,6 @@ module Gitlab found_module && found_module['url'] end - def alternate_object_directories - relative_object_directories.map { |d| File.join(path, d) } - end - - def relative_object_directories - Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact - end - # Returns true if the given ref name exists # # Ref names must start with `refs/`. diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index d2dc4f2e688..072019dfb0a 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -1,9 +1,16 @@ +# We only need Gollum::Page so let's not load all of gollum-lib. +require 'gollum-lib/pagination' +require 'gollum-lib/wiki' +require 'gollum-lib/page' + module Gitlab module Git class Wiki DuplicatePageError = Class.new(StandardError) OperationError = Class.new(StandardError) + DEFAULT_PAGINATION = Kaminari.config.default_per_page + CommitDetails = Struct.new(:user_id, :username, :name, :email, :message) do def to_h { user_id: user_id, username: username, name: name, email: email, message: message } @@ -74,7 +81,7 @@ module Gitlab # Gitaly uses gollum-lib to get the versions. Gollum defaults to 20 # per page, but also fetches 20 if `limit` or `per_page` < 20. # Slicing returns an array with the expected number of items. - slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page + slice_bound = options[:limit] || options[:per_page] || DEFAULT_PAGINATION versions[0..slice_bound] end @@ -104,26 +111,6 @@ module Gitlab private - def new_page(gollum_page) - Gitlab::Git::WikiPage.new(gollum_page, new_version(gollum_page, gollum_page.version.id)) - end - - def new_version(gollum_page, commit_id) - Gitlab::Git::WikiPageVersion.new(version(commit_id), gollum_page&.format) - end - - def version(commit_id) - commit_find_proc = -> { Gitlab::Git::Commit.find(@repository, commit_id) } - - Gitlab::SafeRequestStore.fetch([:wiki_version_commit, commit_id]) { commit_find_proc.call } - end - - def assert_type!(object, klass) - unless object.is_a?(klass) - raise ArgumentError, "expected a #{klass}, got #{object.inspect}" - end - end - def gitaly_wiki_client @gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository) end diff --git a/lib/gitlab/git/wiki_file.rb b/lib/gitlab/git/wiki_file.rb index 84335aca4bc..64313bb04e8 100644 --- a/lib/gitlab/git/wiki_file.rb +++ b/lib/gitlab/git/wiki_file.rb @@ -3,17 +3,12 @@ module Gitlab class WikiFile attr_reader :mime_type, :raw_data, :name, :path - # This class is meant to be serializable so that it can be constructed - # by Gitaly and sent over the network to GitLab. - # - # Because Gollum::File is not serializable we must get all the data from - # 'gollum_file' during initialization, and NOT store it in an instance - # variable. - def initialize(gollum_file) - @mime_type = gollum_file.mime_type - @raw_data = gollum_file.raw_data - @name = gollum_file.name - @path = gollum_file.path + # This class wraps Gitlab::GitalyClient::WikiFile + def initialize(gitaly_file) + @mime_type = gitaly_file.mime_type + @raw_data = gitaly_file.raw_data + @name = gitaly_file.name + @path = gitaly_file.path end end end diff --git a/lib/gitlab/git/wiki_page.rb b/lib/gitlab/git/wiki_page.rb index 669ae11a423..c4087c9ebdc 100644 --- a/lib/gitlab/git/wiki_page.rb +++ b/lib/gitlab/git/wiki_page.rb @@ -3,25 +3,15 @@ module Gitlab class WikiPage attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :text_data, :historical, :formatted_data - # This class is meant to be serializable so that it can be constructed - # by Gitaly and sent over the network to GitLab. - # - # Because Gollum::Page is not serializable we must get all the data from - # 'gollum_page' during initialization, and NOT store it in an instance - # variable. - # - # Note that 'version' is a WikiPageVersion instance which it itself - # serializable. That means it's OK to store 'version' in an instance - # variable. - def initialize(gollum_page, version) - @url_path = gollum_page.url_path - @title = gollum_page.title - @format = gollum_page.format - @path = gollum_page.path - @raw_data = gollum_page.raw_data - @name = gollum_page.name - @historical = gollum_page.historical? - @formatted_data = gollum_page.formatted_data if gollum_page.is_a?(Gollum::Page) + # This class abstracts away Gitlab::GitalyClient::WikiPage + def initialize(gitaly_page, version) + @url_path = gitaly_page.url_path + @title = gitaly_page.title + @format = gitaly_page.format + @path = gitaly_page.path + @raw_data = gitaly_page.raw_data + @name = gitaly_page.name + @historical = gitaly_page.historical? @version = version end diff --git a/lib/gitlab/git/wiki_page_version.rb b/lib/gitlab/git/wiki_page_version.rb index 55f1afedcab..d5e7e70fd31 100644 --- a/lib/gitlab/git/wiki_page_version.rb +++ b/lib/gitlab/git/wiki_page_version.rb @@ -3,11 +3,6 @@ module Gitlab class WikiPageVersion attr_reader :commit, :format - # This class is meant to be serializable so that it can be constructed - # by Gitaly and sent over the network to GitLab. - # - # Both 'commit' (a Gitlab::Git::Commit) and 'format' (a string) are - # serializable. def initialize(commit, format) @commit = commit @format = format diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb index 75be7d1f5a0..7c2c228ad01 100644 --- a/lib/gitlab/gitaly_client/wiki_service.rb +++ b/lib/gitlab/gitaly_client/wiki_service.rb @@ -110,7 +110,7 @@ module Gitlab repository: @gitaly_repo, page_path: encode_binary(page_path), page: options[:page] || 1, - per_page: options[:per_page] || Gollum::Page.per_page + per_page: options[:per_page] || Gitlab::Git::Wiki::DEFAULT_PAGINATION ) stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_page_versions, request, timeout: GitalyClient.medium_timeout) diff --git a/scripts/lint-rugged b/scripts/lint-rugged index d0c2c544c47..22e3e1f1505 100755 --- a/scripts/lint-rugged +++ b/scripts/lint-rugged @@ -1,21 +1,9 @@ #!/usr/bin/env ruby ALLOWED = [ - # Can be fixed once Rugged is no longer used in production. Doesn't make Rugged calls. - 'config/initializers/8_metrics.rb', - - # Can be deleted once wiki's are fully (mandatory) migrated - 'config/initializers/gollum.rb', - - # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/953 + # Needed to handle repositories that are not in any storage 'lib/gitlab/bare_repository_import/repository.rb', - # Needs to be migrated, https://gitlab.com/gitlab-org/gitaly/issues/954 - 'lib/tasks/gitlab/cleanup.rake', - - # The only place where Rugged code is still allowed in production - 'lib/gitlab/git/', - # Needed to avoid using the git binary to validate a branch name 'lib/gitlab/git_ref_validator.rb' ].freeze diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index cac8a5068ec..3b37ede8579 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -264,9 +264,9 @@ describe 'GitLab Markdown', :aggregate_failures do @project_wiki = @feat.project_wiki @project_wiki_page = @feat.project_wiki_page - file = Gollum::File.new(@project_wiki.wiki) - expect(file).to receive(:path).and_return('images/example.jpg') - expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file) + path = 'images/example.jpg' + gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) + expect(@project_wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) allow(@project_wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki, page_slug: @project_wiki_page.slug }) diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index 747406efc8b..9a4ce426e69 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -83,12 +83,13 @@ describe 'User views a wiki page' do end it 'shows a file stored in a page' do - gollum_file_double = double('Gollum::File', - mime_type: 'image/jpeg', - name: 'images/image.jpg', - path: 'images/image.jpg', - raw_data: '') - wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double) + raw_file = Gitlab::GitalyClient::WikiFile.new( + mime_type: 'image/jpeg', + name: 'images/image.jpg', + path: 'images/image.jpg', + raw_data: '' + ) + wiki_file = Gitlab::Git::WikiFile.new(raw_file) allow(wiki_file).to receive(:mime_type).and_return('image/jpeg') allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file) diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index 0735ebd6dcb..5dce3fcbcb6 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :migration, schema: 20171114162227 do + include GitHelpers + let(:merge_request_diffs) { table(:merge_request_diffs) } let(:merge_requests) { table(:merge_requests) } @@ -9,11 +11,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m let(:merge_request) { merge_requests.create!(iid: 1, target_project_id: project.id, source_project_id: project.id, target_branch: 'feature', source_branch: 'master').becomes(MergeRequest) } let(:merge_request_diff) { MergeRequest.find(merge_request.id).create_merge_request_diff } let(:updated_merge_request_diff) { MergeRequestDiff.find(merge_request_diff.id) } - let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end - end + let(:rugged) { rugged_repo(project.repository) } before do allow_any_instance_of(MergeRequestDiff) diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb index 9095ffbfd52..1bd077ddbdf 100644 --- a/spec/lib/gitlab/conflict/file_spec.rb +++ b/spec/lib/gitlab/conflict/file_spec.rb @@ -1,9 +1,11 @@ require 'spec_helper' describe Gitlab::Conflict::File do + include GitHelpers + let(:project) { create(:project, :repository) } let(:repository) { project.repository } - let(:rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } } + let(:rugged) { rugged_repo(repository) } let(:their_commit) { rugged.branches['conflict-start'].target } let(:our_commit) { rugged.branches['conflict-resolvable'].target } let(:merge_request) { create(:merge_request, source_branch: 'conflict-resolvable', target_branch: 'conflict-start', source_project: project) } diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb index ea49502ae2e..b243f0dacae 100644 --- a/spec/lib/gitlab/git/blob_spec.rb +++ b/spec/lib/gitlab/git/blob_spec.rb @@ -4,6 +4,9 @@ require "spec_helper" describe Gitlab::Git::Blob, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:rugged) do + Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH)) + end describe 'initialize' do let(:blob) { Gitlab::Git::Blob.new(name: 'test') } @@ -139,9 +142,7 @@ describe Gitlab::Git::Blob, :seed_helper do it 'limits the size of a large file' do blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1 buffer = Array.new(blob_size, 0) - rugged_blob = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Rugged::Blob.from_buffer(repository.rugged, buffer.join('')) - end + rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join('')) blob = Gitlab::Git::Blob.raw(repository, rugged_blob) expect(blob.size).to eq(blob_size) @@ -156,9 +157,7 @@ describe Gitlab::Git::Blob, :seed_helper do context 'when sha references a tree' do it 'returns nil' do - tree = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.rev_parse('master^{tree}') - end + tree = rugged.rev_parse('master^{tree}') blob = Gitlab::Git::Blob.raw(repository, tree.oid) @@ -262,11 +261,7 @@ describe Gitlab::Git::Blob, :seed_helper do end describe '.batch_lfs_pointers' do - let(:tree_object) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.rev_parse('master^{tree}') - end - end + let(:tree_object) { rugged.rev_parse('master^{tree}') } let(:non_lfs_blob) do Gitlab::Git::Blob.find( diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb index 79ccbb79966..0df282d0ae3 100644 --- a/spec/lib/gitlab/git/branch_spec.rb +++ b/spec/lib/gitlab/git/branch_spec.rb @@ -3,9 +3,7 @@ require "spec_helper" describe Gitlab::Git::Branch, :seed_helper do let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end + Rugged::Repository.new(File.join(TestEnv.repos_path, repository.relative_path)) end subject { repository.branches } @@ -74,9 +72,7 @@ describe Gitlab::Git::Branch, :seed_helper do Gitlab::Git.committer_hash(email: user.email, name: user.name) end let(:params) do - parents = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - [repository.rugged.head.target] - end + parents = [rugged.head.target] tree = parents.first.tree { diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb index 2718a3c5e49..9ef27081f98 100644 --- a/spec/lib/gitlab/git/commit_spec.rb +++ b/spec/lib/gitlab/git/commit_spec.rb @@ -1,19 +1,17 @@ require "spec_helper" describe Gitlab::Git::Commit, :seed_helper do + include GitHelpers + let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } - let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } - let(:rugged_commit) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.lookup(SeedRepo::Commit::ID) - end + let(:rugged_repo) do + Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH)) end + let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) } + let(:rugged_commit) { rugged_repo.lookup(SeedRepo::Commit::ID) } + describe "Commit info" do before do - repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - end - @committer = { email: 'mike@smith.com', name: "Mike Smith", @@ -26,12 +24,12 @@ describe Gitlab::Git::Commit, :seed_helper do time: Time.now } - @parents = [repo.head.target] + @parents = [rugged_repo.head.target] @gitlab_parents = @parents.map { |c| described_class.find(repository, c.oid) } @tree = @parents.first.tree sha = Rugged::Commit.create( - repo, + rugged_repo, author: @author, committer: @committer, tree: @tree, @@ -40,7 +38,7 @@ describe Gitlab::Git::Commit, :seed_helper do update_ref: "HEAD" ) - @raw_commit = repo.lookup(sha) + @raw_commit = rugged_repo.lookup(sha) @commit = described_class.find(repository, sha) end @@ -61,10 +59,7 @@ describe Gitlab::Git::Commit, :seed_helper do after do # Erase the new commit so other tests get the original repo - repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - end - repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) + rugged_repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end end @@ -120,9 +115,7 @@ describe Gitlab::Git::Commit, :seed_helper do describe '.find' do it "should return first head commit if without params" do expect(described_class.last(repository).id).to eq( - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.head.target.oid - end + rugged_repo.head.target.oid ) end diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb index 27d803e0117..8a4415506c4 100644 --- a/spec/lib/gitlab/git/diff_spec.rb +++ b/spec/lib/gitlab/git/diff_spec.rb @@ -64,55 +64,19 @@ EOT end end - context 'using a Rugged::Patch' do - context 'with a small diff' do - let(:diff) { described_class.new(gitaly_diff) } - - it 'initializes the diff' do - expect(diff.to_hash).to eq(@raw_diff_hash) - end - - it 'does not prune the diff' do - expect(diff).not_to be_too_large - end - end - - context 'using a diff that is too large' do - it 'prunes the diff' do - gitaly_diff.too_large = true - diff = described_class.new(gitaly_diff) - - expect(diff.diff).to be_empty - expect(diff).to be_too_large - end - end - - context 'using a collapsable diff that is too large' do - it 'prunes the diff as a large diff instead of as a collapsed diff' do - gitaly_diff.too_large = true - diff = described_class.new(gitaly_diff, expanded: false) - - expect(diff.diff).to be_empty - expect(diff).to be_too_large - expect(diff).not_to be_collapsed - end - end - end - context 'using a GitalyClient::Diff' do - let(:diff) do - described_class.new( - Gitlab::GitalyClient::Diff.new( - to_path: ".gitmodules", - from_path: ".gitmodules", - old_mode: 0100644, - new_mode: 0100644, - from_id: '357406f3075a57708d0163752905cc1576fceacc', - to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0', - patch: raw_patch - ) + let(:gitaly_diff) do + Gitlab::GitalyClient::Diff.new( + to_path: ".gitmodules", + from_path: ".gitmodules", + old_mode: 0100644, + new_mode: 0100644, + from_id: '357406f3075a57708d0163752905cc1576fceacc', + to_id: '8e5177d718c561d36efde08bad36b43687ee6bf0', + patch: raw_patch ) end + let(:diff) { described_class.new(gitaly_diff) } context 'with a small diff' do let(:raw_patch) { @raw_diff_hash[:diff] } @@ -135,6 +99,19 @@ EOT end end + context 'using a collapsable diff that is too large' do + let(:raw_patch) { 'a' * 204800 } + + it 'prunes the diff as a large diff instead of as a collapsed diff' do + gitaly_diff.too_large = true + diff = described_class.new(gitaly_diff, expanded: false) + + expect(diff.diff).to be_empty + expect(diff).to be_too_large + expect(diff).not_to be_collapsed + end + end + context 'when the patch passed is not UTF-8-encoded' do let(:raw_patch) { @raw_diff_hash[:diff].encode(Encoding::ASCII_8BIT) } diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index d02536a2fb4..51eb997a325 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -19,7 +19,10 @@ describe Gitlab::Git::Repository, :seed_helper do end end + let(:mutable_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } + let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) } + let(:repository_rugged) { Rugged::Repository.new(repository_path) } let(:storage_path) { TestEnv.repos_path } let(:user) { build(:user) } @@ -71,7 +74,6 @@ describe Gitlab::Git::Repository, :seed_helper do describe "Respond to" do subject { repository } - it { is_expected.to respond_to(:rugged) } it { is_expected.to respond_to(:root_ref) } it { is_expected.to respond_to(:tags) } end @@ -91,57 +93,6 @@ describe Gitlab::Git::Repository, :seed_helper do end end - describe "#rugged" do - describe 'when storage is broken', :broken_storage do - it 'raises a storage exception when storage is not available' do - broken_repo = described_class.new('broken', 'a/path.git', '') - - expect { broken_repo.rugged }.to raise_error(Gitlab::Git::Storage::Inaccessible) - end - end - - it 'raises a no repository exception when there is no repo' do - broken_repo = described_class.new('default', 'a/path.git', '') - - expect do - Gitlab::GitalyClient::StorageSettings.allow_disk_access { broken_repo.rugged } - end.to raise_error(Gitlab::Git::Repository::NoRepository) - end - - describe 'alternates keyword argument' do - context 'with no Git env stored' do - before do - allow(Gitlab::Git::HookEnv).to receive(:all).and_return({}) - end - - it "is passed an empty array" do - expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: []) - - repository_rugged - end - end - - context 'with absolute and relative Git object dir envvars stored' do - before do - allow(Gitlab::Git::HookEnv).to receive(:all).and_return({ - 'GIT_OBJECT_DIRECTORY_RELATIVE' => './objects/foo', - 'GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE' => ['./objects/bar', './objects/baz'], - 'GIT_OBJECT_DIRECTORY' => 'ignored', - 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => %w[ignored ignored], - 'GIT_OTHER' => 'another_env' - }) - end - - it "is passed the relative object dir envvars after being converted to absolute ones" do - alternates = %w[foo bar baz].map { |d| File.join(repository_path, './objects', d) } - expect(Rugged::Repository).to receive(:new).with(repository_path, alternates: alternates) - - repository_rugged - end - end - end - end - describe '#branch_names' do subject { repository.branch_names } @@ -284,7 +235,6 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#submodule_url_for' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } let(:ref) { 'master' } def submodule_url(path) @@ -336,7 +286,7 @@ describe Gitlab::Git::Repository, :seed_helper do it { expect(repository.has_local_branches?).to eq(true) } context 'mutable' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -369,7 +319,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe "#delete_branch" do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -393,7 +343,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe "#create_branch" do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:repository) { mutable_repository } after do ensure_seeds @@ -418,39 +368,33 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#delete_refs' do - let(:repo) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - - def repo_rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repo.rugged - end - end + let(:repository) { mutable_repository } after do ensure_seeds end it 'deletes the ref' do - repo.delete_refs('refs/heads/feature') + repository.delete_refs('refs/heads/feature') - expect(repo_rugged.references['refs/heads/feature']).to be_nil + expect(repository_rugged.references['refs/heads/feature']).to be_nil end it 'deletes all refs' do refs = %w[refs/heads/wip refs/tags/v1.1.0] - repo.delete_refs(*refs) + repository.delete_refs(*refs) refs.each do |ref| - expect(repo_rugged.references[ref]).to be_nil + expect(repository_rugged.references[ref]).to be_nil end end it 'does not fail when deleting an empty list of refs' do - expect { repo.delete_refs(*[]) }.not_to raise_error + expect { repository.delete_refs(*[]) }.not_to raise_error end it 'raises an error if it failed' do - expect { repo.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError) + expect { repository.delete_refs('refs\heads\fix') }.to raise_error(Gitlab::Git::Repository::GitError) end end @@ -528,9 +472,7 @@ describe Gitlab::Git::Repository, :seed_helper do end def new_repository_path - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - new_repository.path - end + File.join(TestEnv.repos_path, new_repository.relative_path) end end @@ -577,18 +519,16 @@ describe Gitlab::Git::Repository, :seed_helper do Gitlab::Git::Commit.find(repository, @rename_commit_id) end - before(:context) do + before do # Add new commits so that there's a renamed file in the commit history - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - @commit_with_old_name_id = new_commit_edit_old_file(repo).oid - @rename_commit_id = new_commit_move_file(repo).oid - @commit_with_new_name_id = new_commit_edit_new_file(repo).oid + @commit_with_old_name_id = new_commit_edit_old_file(repository_rugged).oid + @rename_commit_id = new_commit_move_file(repository_rugged).oid + @commit_with_new_name_id = new_commit_edit_new_file(repository_rugged).oid end - after(:context) do + after do # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID) + repository_rugged.references.update("refs/heads/master", SeedRepo::LastCommit::ID) end context "where 'follow' == true" do @@ -1010,12 +950,10 @@ describe Gitlab::Git::Repository, :seed_helper do subject { repository.branches } context 'with local and remote branches' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do - create_remote_branch(repository, 'joe', 'remote_branch', 'master') + create_remote_branch('joe', 'remote_branch', 'master') repository.create_branch('local_branch', 'master') end @@ -1038,12 +976,10 @@ describe Gitlab::Git::Repository, :seed_helper do end context 'with local and remote branches' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do - create_remote_branch(repository, 'joe', 'remote_branch', 'master') + create_remote_branch('joe', 'remote_branch', 'master') repository.create_branch('local_branch', 'master') end @@ -1303,24 +1239,24 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#local_branches' do - before(:all) do - @repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') + let(:repository) { mutable_repository } + + before do + create_remote_branch('joe', 'remote_branch', 'master') + repository.create_branch('local_branch', 'master') end - after(:all) do + after do ensure_seeds end it 'returns the local branches' do - create_remote_branch(@repo, 'joe', 'remote_branch', 'master') - @repo.create_branch('local_branch', 'master') - - expect(@repo.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) - expect(@repo.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) + expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false) + expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true) end it 'returns a Branch with UTF-8 fields' do - branches = @repo.local_branches.to_a + branches = repository.local_branches.to_a expect(branches.size).to be > 0 branches.each do |branch| expect(branch.name).to be_utf8 @@ -1331,11 +1267,11 @@ describe Gitlab::Git::Repository, :seed_helper do it 'gets the branches from GitalyClient' do expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:local_branches) .and_return([]) - @repo.local_branches + repository.local_branches end it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :local_branches do - subject { @repo.local_branches } + subject { repository.local_branches } end end @@ -1391,8 +1327,7 @@ describe Gitlab::Git::Repository, :seed_helper do describe '#fetch_source_branch!' do let(:local_ref) { 'refs/merge-requests/1/head' } - let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') } - let(:source_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } + let(:source_repository) { mutable_repository } after do ensure_seeds @@ -1401,7 +1336,8 @@ describe Gitlab::Git::Repository, :seed_helper do context 'when the branch exists' do context 'when the commit does not exist locally' do let(:source_branch) { 'new-branch-for-fetch-source-branch' } - let(:source_rugged) { Gitlab::GitalyClient::StorageSettings.allow_disk_access { source_repository.rugged } } + let(:source_path) { File.join(TestEnv.repos_path, source_repository.relative_path) } + let(:source_rugged) { Rugged::Repository.new(source_path) } let(:new_oid) { new_commit_edit_old_file(source_rugged).oid } before do @@ -1513,8 +1449,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#set_config' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - let(:rugged) { repository_rugged } + let(:repository) { mutable_repository } let(:entries) do { 'test.foo1' => 'bla bla', @@ -1526,19 +1461,18 @@ describe Gitlab::Git::Repository, :seed_helper do it 'can set config settings' do expect(repository.set_config(entries)).to be_nil - expect(rugged.config['test.foo1']).to eq('bla bla') - expect(rugged.config['test.foo2']).to eq('1234') - expect(rugged.config['test.foo3']).to eq('true') + expect(repository_rugged.config['test.foo1']).to eq('bla bla') + expect(repository_rugged.config['test.foo2']).to eq('1234') + expect(repository_rugged.config['test.foo3']).to eq('true') end after do - entries.keys.each { |k| rugged.config.delete(k) } + entries.keys.each { |k| repository_rugged.config.delete(k) } end end describe '#delete_config' do - let(:repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') } - let(:rugged) { repository_rugged } + let(:repository) { mutable_repository } let(:entries) do { 'test.foo1' => 'bla bla', @@ -1549,21 +1483,19 @@ describe Gitlab::Git::Repository, :seed_helper do it 'can delete config settings' do entries.each do |key, value| - rugged.config[key] = value + repository_rugged.config[key] = value end expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil - config_keys = rugged.config.each_key.to_a + config_keys = repository_rugged.config.each_key.to_a expect(config_keys).not_to include('test.foo1') expect(config_keys).not_to include('test.foo2') end end describe '#merge' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' } let(:target_branch) { 'test-merge-target-branch' } @@ -1602,9 +1534,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#ff_merge' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' } let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' } let(:target_branch) { 'test-ff-target-branch' } @@ -1667,9 +1597,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe '#delete_all_refs_except' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end + let(:repository) { mutable_repository } before do repository.write_ref("refs/delete/a", "0b4bc9a49b562e85de7cc9e834518ea6828729b9") @@ -1693,12 +1621,7 @@ describe Gitlab::Git::Repository, :seed_helper do end describe 'remotes' do - let(:repository) do - Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '') - end - let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } - end + let(:repository) { mutable_repository } let(:remote_name) { 'my-remote' } let(:url) { 'http://my-repo.git' } @@ -1711,26 +1634,26 @@ describe Gitlab::Git::Repository, :seed_helper do it 'added the remote' do begin - rugged.remotes.delete(remote_name) + repository_rugged.remotes.delete(remote_name) rescue Rugged::ConfigError end repository.add_remote(remote_name, url, mirror_refmap: mirror_refmap) - expect(rugged.remotes[remote_name]).not_to be_nil - expect(rugged.config["remote.#{remote_name}.mirror"]).to eq('true') - expect(rugged.config["remote.#{remote_name}.prune"]).to eq('true') - expect(rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap) + expect(repository_rugged.remotes[remote_name]).not_to be_nil + expect(repository_rugged.config["remote.#{remote_name}.mirror"]).to eq('true') + expect(repository_rugged.config["remote.#{remote_name}.prune"]).to eq('true') + expect(repository_rugged.config["remote.#{remote_name}.fetch"]).to eq(mirror_refmap) end end describe '#remove_remote' do it 'removes the remote' do - rugged.remotes.create(remote_name, url) + repository_rugged.remotes.create(remote_name, url) repository.remove_remote(remote_name) - expect(rugged.remotes[remote_name]).to be_nil + expect(repository_rugged.remotes[remote_name]).to be_nil end end end @@ -1901,13 +1824,11 @@ describe Gitlab::Git::Repository, :seed_helper do end context 'when the diff contains a rename' do - let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged } - let(:end_sha) { new_commit_move_file(repo).oid } + let(:end_sha) { new_commit_move_file(repository_rugged).oid } after do # Erase our commits so other tests get the original repo - repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged - repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID) + repository_rugged.references.update('refs/heads/master', SeedRepo::LastCommit::ID) end it 'does not include the renamed file in the sparse checkout' do @@ -1954,10 +1875,9 @@ describe Gitlab::Git::Repository, :seed_helper do end end - def create_remote_branch(repository, remote_name, branch_name, source_branch_name) + def create_remote_branch(remote_name, branch_name, source_branch_name) source_branch = repository.branches.find { |branch| branch.name == source_branch_name } - rugged = repository_rugged - rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) + repository_rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", source_branch.dereferenced_target.sha) end # Build the options hash that's passed to Rugged::Commit#create @@ -2035,16 +1955,4 @@ describe Gitlab::Git::Repository, :seed_helper do line.split("\t").last end end - - def repository_rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end - end - - def repository_path - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.path - end - end end diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb index dbd64c4bec0..e7da5565c26 100644 --- a/spec/lib/gitlab/git_access_spec.rb +++ b/spec/lib/gitlab/git_access_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Gitlab::GitAccess do include TermsHelper + include GitHelpers let(:user) { create(:user) } @@ -736,21 +737,19 @@ describe Gitlab::GitAccess do def merge_into_protected_branch @protected_branch_merge_commit ||= begin - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.add_branch(user, unprotected_branch, 'feature') - rugged = project.repository.rugged - target_branch = rugged.rev_parse('feature') - source_branch = project.repository.create_file( - user, - 'filename', - 'This is the file content', - message: 'This is a good commit message', - branch_name: unprotected_branch) - author = { email: "email@example.com", time: Time.now, name: "Example Git User" } + project.repository.add_branch(user, unprotected_branch, 'feature') + rugged = rugged_repo(project.repository) + target_branch = rugged.rev_parse('feature') + source_branch = project.repository.create_file( + user, + 'filename', + 'This is the file content', + message: 'This is a good commit message', + branch_name: unprotected_branch) + author = { email: "email@example.com", time: Time.now, name: "Example Git User" } - merge_index = rugged.merge_commits(target_branch, source_branch) - Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) - end + merge_index = rugged.merge_commits(target_branch, source_branch) + Rugged::Commit.create(rugged, author: author, committer: author, message: "commit message", parents: [target_branch, source_branch], tree: merge_index.write_tree(rugged)) end end diff --git a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb index 5f67fe6b952..d82c9c28da0 100644 --- a/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb +++ b/spec/lib/gitlab/gitaly_client/wiki_service_spec.rb @@ -39,6 +39,10 @@ describe Gitlab::GitalyClient::WikiService do expect(wiki_page.title).to eq('My Page') expect(wiki_page.raw_data).to eq('ab') expect(wiki_page_version.format).to eq('markdown') + + expect(wiki_page.title).to be_utf8 + expect(wiki_page.path).to be_utf8 + expect(wiki_page.name).to be_utf8 end end diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 3649990670b..8481c14c78d 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Namespace do include ProjectForksHelper + include GitHelpers let!(:namespace) { create(:namespace) } let(:gitlab_shell) { Gitlab::Shell.new } @@ -339,9 +340,7 @@ describe Namespace do end def project_rugged(project) - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged_repo(project.repository) end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index afc9ea1917e..42f6bfbd5bd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe Project do include ProjectForksHelper + include GitHelpers describe 'associations' do it { is_expected.to belong_to(:group) } @@ -3996,8 +3997,6 @@ describe Project do end def rugged_config - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config - end + rugged_repo(project.repository).config end end diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index 269d5deca20..3d316fb3c5b 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe RemoteMirror do + include GitHelpers + describe 'URL validation' do context 'with a valid URL' do it 'should be valid' do @@ -74,9 +76,7 @@ describe RemoteMirror do mirror.update_attribute(:url, 'http://foo:baz@test.com') - config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repo.raw_repository.rugged.config - end + config = rugged_repo(repo).config expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com') end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 784d17e271e..77e549d9528 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe Repository do include RepoHelpers + include GitHelpers + TestBlob = Struct.new(:path) let(:project) { create(:project, :repository) } @@ -137,9 +139,7 @@ describe Repository do options = { message: 'test tag message\n', tagger: { name: 'John Smith', email: 'john@gmail.com' } } - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) - end + rugged_repo(repository).tags.create(annotated_tag_name, 'a48e4fc218069f68ef2e769dd8dfea3991362175', options) double_first = double(committed_date: Time.now - 1.second) double_last = double(committed_date: Time.now) @@ -151,9 +151,7 @@ describe Repository do it { is_expected.to eq(['v1.1.0', 'v1.0.0', annotated_tag_name]) } after do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged.tags.delete(annotated_tag_name) - end + rugged_repo(repository).tags.delete(annotated_tag_name) end end end @@ -1678,10 +1676,7 @@ describe Repository do it 'returns the number of branches' do expect(repository.branch_count).to be_an(Integer) - # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.raw_repository.rugged.branches.count - end + rugged_count = rugged_repo(repository).branches.count expect(repository.branch_count).to eq(rugged_count) end @@ -1691,10 +1686,7 @@ describe Repository do it 'returns the number of tags' do expect(repository.tag_count).to be_an(Integer) - # NOTE: Until rugged goes away, make sure rugged and gitaly are in sync - rugged_count = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.raw_repository.rugged.tags.count - end + rugged_count = rugged_repo(repository).tags.count expect(repository.tag_count).to eq(rugged_count) end @@ -2184,9 +2176,7 @@ describe Repository do end def create_remote_branch(remote_name, branch_name, target) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - repository.rugged - end + rugged = rugged_repo(repository) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 63850939be1..b4732ec0cd5 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -77,7 +77,7 @@ describe WikiPage do end describe "#initialize" do - context "when initialized with an existing gollum page" do + context "when initialized with an existing page" do before do create_page("test page", "test content") @page = wiki.wiki.page(title: "test page") diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index 92159e1e372..2699f6e7bcd 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -2,6 +2,7 @@ require 'spec_helper' describe GitTagPushService do include RepoHelpers + include GitHelpers let(:user) { create(:user) } let(:project) { create(:project, :repository) } @@ -118,9 +119,7 @@ describe GitTagPushService do before do # Create the lightweight tag - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.raw_repository.rugged.tags.create(tag_name, newrev) - end + rugged_repo(project.repository).tags.create(tag_name, newrev) # Clear tag list cache project.repository.expire_tags_cache diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb index 8ab09412f55..53bce15735c 100644 --- a/spec/services/merge_requests/squash_service_spec.rb +++ b/spec/services/merge_requests/squash_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe MergeRequests::SquashService do + include GitHelpers + let(:service) { described_class.new(project, user, {}) } let(:user) { project.owner } let(:project) { create(:project, :repository) } @@ -63,9 +65,7 @@ describe MergeRequests::SquashService do end it 'has the same diff as the merge request, but a different SHA' do - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha) squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha) diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb index cd52bc88f4c..4dd6c6dab86 100644 --- a/spec/services/projects/after_import_service_spec.rb +++ b/spec/services/projects/after_import_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::AfterImportService do + include GitHelpers + subject { described_class.new(project) } let(:project) { create(:project, :repository) } @@ -53,7 +55,7 @@ describe Projects::AfterImportService do end def rugged - Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged } + rugged_repo(repository) end end end diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb index bb3f1501f0e..a80c8a7fe51 100644 --- a/spec/services/projects/create_service_spec.rb +++ b/spec/services/projects/create_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::CreateService, '#execute' do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:user) { create :user } let(:opts) do @@ -295,9 +297,7 @@ describe Projects::CreateService, '#execute' do it 'writes project full path to .git/config' do project = create_project(user, opts) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) expect(rugged.config['gitlab.fullpath']).to eq project.full_path end diff --git a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb index 5f67c325223..0e82194e9ee 100644 --- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb +++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::HashedStorage::MigrateRepositoryService do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) } let(:legacy_storage) { Storage::LegacyProject.new(project) } @@ -38,9 +40,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do it 'writes project full path to .git/config' do service.execute - rugged_config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config['gitlab.fullpath'] - end + rugged_config = rugged_repo(project.repository).config['gitlab.fullpath'] expect(rugged_config).to eq project.full_path end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 92c5ac7354a..1411723fb9e 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe Projects::TransferService do + include GitHelpers + let(:gitlab_shell) { Gitlab::Shell.new } let(:user) { create(:user) } let(:group) { create(:group) } @@ -291,8 +293,6 @@ describe Projects::TransferService do end def rugged_config - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged.config - end + rugged_repo(project.repository).config end end diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index e0fceae88de..83035788a56 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -1,4 +1,6 @@ module CycleAnalyticsHelpers + include GitHelpers + def create_commit_referencing_issue(issue, branch_name: generate(:branch)) project.repository.add_branch(user, branch_name, 'master') create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) @@ -9,7 +11,7 @@ module CycleAnalyticsHelpers oldrev = repository.commit(branch_name)&.sha || Gitlab::Git::BLANK_SHA if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files) - mock_gitaly_multi_action_dates(repository.raw, commit_time) + mock_gitaly_multi_action_dates(repository, commit_time) end commit_shas = Array.new(count) do |index| @@ -118,18 +120,15 @@ module CycleAnalyticsHelpers protected: false) end - def mock_gitaly_multi_action_dates(raw_repository, commit_time) - allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args| + def mock_gitaly_multi_action_dates(repository, commit_time) + allow(repository.raw).to receive(:multi_action).and_wrap_original do |m, *args| new_date = commit_time || Time.now branch_update = m.call(*args) if branch_update.newrev _, opts = args - commit = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - rugged = raw_repository.rugged - rugged.rev_parse(branch_update.newrev) - end + commit = rugged_repo(repository).rev_parse(branch_update.newrev) branch_update.newrev = commit.amend( update_ref: "#{Gitlab::Git::BRANCH_REF_PREFIX}#{opts[:branch_name]}", diff --git a/spec/support/helpers/git_helpers.rb b/spec/support/helpers/git_helpers.rb index fc92bc38561..99a7c39852e 100644 --- a/spec/support/helpers/git_helpers.rb +++ b/spec/support/helpers/git_helpers.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true module GitHelpers + def rugged_repo(repository) + path = File.join(TestEnv.repos_path, repository.disk_path + '.git') + + Rugged::Repository.new(path) + end + def project_hook_exists?(project) Gitlab::GitalyClient::StorageSettings.allow_disk_access do project_path = project.repository.raw_repository.path diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb index 30e67e67e0e..a159f24f876 100644 --- a/spec/workers/git_garbage_collect_worker_spec.rb +++ b/spec/workers/git_garbage_collect_worker_spec.rb @@ -3,6 +3,8 @@ require 'fileutils' require 'spec_helper' describe GitGarbageCollectWorker do + include GitHelpers + let(:project) { create(:project, :repository) } let(:shell) { Gitlab::Shell.new } let!(:lease_uuid) { SecureRandom.uuid } @@ -197,9 +199,7 @@ describe GitGarbageCollectWorker do # Create a new commit on a random new branch def create_objects(project) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) old_commit = rugged.branches.first.target new_commit_sha = Rugged::Commit.create( rugged, diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb index a653f6f926c..6ddb653d142 100644 --- a/spec/workers/repository_remove_remote_worker_spec.rb +++ b/spec/workers/repository_remove_remote_worker_spec.rb @@ -2,6 +2,7 @@ require 'rails_helper' describe RepositoryRemoveRemoteWorker do include ExclusiveLeaseHelpers + include GitHelpers describe '#perform' do let!(:project) { create(:project, :repository) } @@ -50,9 +51,7 @@ describe RepositoryRemoveRemoteWorker do end def create_remote_branch(remote_name, branch_name, target) - rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + rugged = rugged_repo(project.repository) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end diff --git a/vendor/licenses.csv b/vendor/licenses.csv index b36aabb9b3d..6adcb28f736 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -438,7 +438,6 @@ github-linguist,5.3.3,MIT github-markup,1.7.0,MIT gitlab-flowdock-git-hook,1.0.1,MIT gitlab-gollum-lib,4.2.7.5,MIT -gitlab-gollum-rugged_adapter,0.4.4.1,MIT gitlab-grit,2.8.2,MIT gitlab-markup,1.6.4,MIT gitlab_omniauth-ldap,2.0.4,MIT From 7848e42ac2eb6d47b48bbaab818fb33372450486 Mon Sep 17 00:00:00 2001 From: Mike Lewis Date: Tue, 2 Oct 2018 19:45:21 +0000 Subject: [PATCH 11/38] Port jira doc update to ce --- doc/user/project/integrations/jira.md | 236 +++++++++++++------------- 1 file changed, 119 insertions(+), 117 deletions(-) diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md index ba8b79b911b..30f49fefc41 100644 --- a/doc/user/project/integrations/jira.md +++ b/doc/user/project/integrations/jira.md @@ -1,123 +1,129 @@ -# GitLab JIRA integration +# GitLab Jira integration -GitLab can be configured to interact with [JIRA], a project management platform. +GitLab Issues are a powerful tool for discussing ideas and planning and tracking work. +However, many organizations have been using Jira for these purposes and have +extensive data and business processes built into it. -Once your GitLab project is connected to JIRA, you can reference and close the -issues in JIRA directly from GitLab. +While you can always migrate content and process from Jira to GitLab Issues, +you can also opt to continue using Jira and use it together with GitLab through +our integration. -For a use case, check out this article of [How and why to integrate GitLab with -JIRA](https://www.programmableweb.com/news/how-and-why-to-integrate-gitlab-jira/how-to/2017/04/25). +Once you integrate your GitLab project with your Jira instance, you can automatically +detect and cross-reference activity between the GitLab project and any of your projects +in Jira. This includes the ability to close or transition Jira issues when the work +is completed in GitLab. + +Here's how the integration responds when you take the following actions in GitLab: + +- **Mention a Jira issue ID** in a commit message or MR (merge request). + - GitLab hyperlinks to the Jira issue. + - The Jira issue adds an issue link to the commit/MR in GitLab. + - The Jira issue adds a comment reflecting the comment made in GitLab, the comment author, and a link to the commit/MR in GitLab. +- **Mention that a commit or MR 'closes', 'resolves', or 'fixes' a Jira issue ID**. When the commit is made on master or the change is merged to master: + - GitLab's merge request page displays a note that it "Closed" the Jira issue, with a link to the issue. (Note: Before the merge, an MR will display that it "Closes" the Jira issue.) + - The Jira issue shows the activity and the Jira issue is closed, or otherwise transitioned. + +You can also use [Jira's Smart Commits](https://confluence.atlassian.com/fisheye/using-smart-commits-298976812.html) +directly from GitLab, as covered in the article +[How and why to integrate GitLab with Jira](https://www.programmableweb.com/news/how-and-why-to-integrate-gitlab-Jira/how-to/2017/04/25). ## Configuration -Each GitLab project can be configured to connect to a different JIRA instance. That -means one GitLab project maps to _all_ JIRA projects in that JIRA instance once -the configuration is set up. Therefore, you don't have to explicitly associate -one GitLab project to any JIRA project. Once the configuration is set up, any JIRA -projects in the JIRA instance are already mapped to the GitLab project. +Each GitLab project can be configured to connect to an entire Jira instance. That +means one GitLab project can interact with _all_ Jira projects in that instance, once +configured. Therefore, you will not have to explicitly associate +a GitLab project with any single Jira project. -If you have one JIRA instance you can pre-fill the settings page with a default -template, see the [Services Templates][services-templates] docs. +If you have one Jira instance, you can pre-fill the settings page with a default +template. See the [Services Templates][services-templates] docs. -Configuration happens via user name and password. Connecting to a JIRA server +Configuration happens via user name and password. Connecting to a Jira server via CAS is not possible. -In order to enable the JIRA service in GitLab, you need to first configure the -project in JIRA and then enter the correct values in GitLab. +In order to enable the Jira service in GitLab, you need to first configure the +project in Jira and then enter the correct values in GitLab. -### Configuring JIRA +### Configuring Jira -We need to create a user in JIRA which will have access to all projects that -need to integrate with GitLab. Login to your JIRA instance as admin and under -Administration go to User Management and create a new user. +We need to create a user in Jira which will have access to all projects that +need to integrate with GitLab. Login to your Jira instance as admin and under +*Administration*, go to *User Management* and create a new user. -As an example, we'll create a user named `gitlab` and add it to `JIRA-developers` +As an example, we'll create a user named `gitlab` and add it to the `Jira-developers` group. -**It is important that the user `GitLab` has write-access to projects in JIRA** +**It is important that the user `gitlab` has 'write' access to projects in Jira** We have split this stage in steps so it is easier to follow. ---- - -1. Login to your JIRA instance as an administrator and under **Administration** +1. Log in to your Jira instance as an administrator and under **Administration** go to **User Management** to create a new user. - ![JIRA user management link](img/jira_user_management_link.png) - - --- + ![Jira user management link](img/jira_user_management_link.png) 1. The next step is to create a new user (e.g., `gitlab`) who has write access - to projects in JIRA. Enter the user's name and a _valid_ e-mail address - since JIRA sends a verification e-mail to set up the password. - _**Note:** JIRA creates the username automatically by using the e-mail - prefix. You can change it later if you want._ + to projects in Jira. Enter the user's name and a _valid_ e-mail address + since Jira sends a verification e-mail to set up the password. + _**Note:** Jira creates the username automatically by using the e-mail + prefix. You can change it later, if needed. Our integration does not support SSO (such as SAML). You will need to create + an HTTP basic authentication password. You can do this by visiting the user + profile, looking up the username, and setting a password._ - ![JIRA create new user](img/jira_create_new_user.png) - - --- + ![Jira create new user](img/jira_create_new_user.png) 1. Now, let's create a `gitlab-developers` group which will have write access - to projects in JIRA. Go to the **Groups** tab and select **Create group**. + to projects in Jira. Go to the **Groups** tab and select **Create group**. - ![JIRA create new user](img/jira_create_new_group.png) + ![Jira create new user](img/jira_create_new_group.png) - --- + Give it an optional description and click **Create group**. - Give it an optional description and hit **Create group**. + ![Jira create new group](img/jira_create_new_group_name.png) - ![jira create new group](img/jira_create_new_group_name.png) +1. To give the newly-created group 'write' access, go to + **Application access ➔ View configuration** and add the `gitlab-developers` + group to Jira Core. - --- - -1. Give the newly-created group write access by going to - **Application access ➔ View configuration** and adding the `gitlab-developers` - group to JIRA Core. - - ![JIRA group access](img/jira_group_access.png) - - --- + ![Jira group access](img/jira_group_access.png) 1. Add the `gitlab` user to the `gitlab-developers` group by going to **Users ➔ GitLab user ➔ Add group** and selecting the `gitlab-developers` - group from the dropdown menu. Notice that the group says _Access_ which is - what we aim for. + group from the dropdown menu. Notice that the group says _Access_, which is + intended as part of this process. - ![JIRA add user to group](img/jira_add_user_to_group.png) + ![Jira add user to group](img/jira_add_user_to_group.png) - --- - -The JIRA configuration is over. Write down the new JIRA username and its +The Jira configuration is complete. Write down the new Jira username and its password as they will be needed when configuring GitLab in the next section. ### Configuring GitLab > **Notes:** -> - The currently supported JIRA versions are `v6.x` and `v7.x.`. GitLab 7.8 or +> - The currently supported Jira versions are `v6.x` and `v7.x.`. GitLab 7.8 or > higher is required. -> - GitLab 8.14 introduced a new way to integrate with JIRA which greatly simplified +> - GitLab 8.14 introduced a new way to integrate with Jira which greatly simplified > the configuration options you have to enter. If you are using an older version, > [follow this documentation][jira-repo-old-docs]. > - In order to support Oracle's Access Manager, GitLab will send additional cookies > to enable Basic Auth. The cookie being added to each request is `OBBasicAuth` with > a value of `fromDialog`. -To enable JIRA integration in a project, navigate to the +To enable Jira integration in a project, navigate to the [Integrations page](project_services.md#accessing-the-project-services), click -the **JIRA** service, and fill in the required details on the page as described +the **Jira** service, and fill in the required details on the page as described in the table below. | Field | Description | | ----- | ----------- | -| `Web URL` | The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., `https://jira.example.com`. | -| `JIRA API URL` | The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. | -| `Username` | The user name created in [configuring JIRA step](#configuring-jira). Using the email address will cause `401 unauthorized`. | -| `Password` |The password of the user created in [configuring JIRA step](#configuring-jira). | -| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** | +| `Web URL` | The base URL to the Jira instance web interface which is being linked to this GitLab project. E.g., `https://Jira.example.com`. | +| `Jira API URL` | The base URL to the Jira instance API. Web URL value will be used if not set. E.g., `https://jira-api.example.com`. | +| `Username` | The user name created in [configuring Jira step](#configuring-jira). Using the email address will cause `401 unauthorized`. | +| `Password` |The password of the user created in [configuring Jira step](#configuring-jira). | +| `Transition ID` | This is the ID of a transition that moves issues to the desired state. It is possible to insert transition ids separated by `,` or `;` which means the issue will be moved to each state after another using the given order. **Closing Jira issues via commits or Merge Requests won't work if you don't set the ID correctly.** | -### Getting a transition ID +### Obtaining a transition ID -In the most recent JIRA UI, you can no longer see transition IDs in the workflow +In the most recent Jira user interface, you can no longer see transition IDs in the workflow administration UI. You can get the ID you need in either of the following ways: 1. By using the API, with a request like `https://yourcompany.atlassian.net/rest/api/2/issue/ISSUE-123/transitions` @@ -129,25 +135,23 @@ Note that the transition ID may vary between workflows (e.g., bug vs. story), even if the status you are changing to is the same. After saving the configuration, your GitLab project will be able to interact -with all JIRA projects in your JIRA instance and you'll see the JIRA link on the GitLab project pages that takes you to the appropriate JIRA project. +with all Jira projects in your Jira instance and you'll see the Jira link on the GitLab project pages that takes you to the appropriate Jira project. -![JIRA service page](img/jira_service_page.png) +![Jira service page](img/jira_service_page.png) ---- +## Jira issues -## JIRA issues - -By now you should have [configured JIRA](#configuring-jira) and enabled the -[JIRA service in GitLab](#configuring-gitlab). If everything is set up correctly -you should be able to reference and close JIRA issues by just mentioning their +By now you should have [configured Jira](#configuring-jira) and enabled the +[Jira service in GitLab](#configuring-gitlab). If everything is set up correctly +you should be able to reference and close Jira issues by just mentioning their ID in GitLab commits and merge requests. -### Referencing JIRA Issues +### Referencing Jira Issues -When GitLab project has JIRA issue tracker configured and enabled, mentioning -JIRA issue in GitLab will automatically add a comment in JIRA issue with the +When GitLab project has Jira issue tracker configured and enabled, mentioning +Jira issue in GitLab will automatically add a comment in Jira issue with the link back to GitLab. This means that in comments in merge requests and commits -referencing an issue, e.g., `PROJECT-7`, will add a comment in JIRA issue in the +referencing an issue, e.g., `PROJECT-7`, will add a comment in Jira issue in the format: ``` @@ -156,21 +160,19 @@ ENTITY_TITLE ``` * `USER` A user that mentioned the issue. This is the link to the user profile in GitLab. -* `LINK_TO_THE_COMMENT` Link to the origin of mention with a name of the entity where JIRA issue was mentioned. +* `LINK_TO_THE_COMMENT` Link to the origin of mention with a name of the entity where Jira issue was mentioned. * `RESOURCE_NAME` Kind of resource which referenced the issue. Can be a commit or merge request. * `PROJECT_NAME` GitLab project name. * `ENTITY_TITLE` Merge request title or commit message first line. -![example of mentioning or closing the JIRA issue](img/jira_issue_reference.png) +![example of mentioning or closing the Jira issue](img/jira_issue_reference.png) ---- +### Closing Jira Issues -### Closing JIRA Issues - -JIRA issues can be closed directly from GitLab by using trigger words in +Jira issues can be closed directly from GitLab by using trigger words in commits and merge requests. When a commit which contains the trigger word -followed by the JIRA issue ID in the commit message is pushed, GitLab will -add a comment in the mentioned JIRA issue and immediately close it (provided +followed by the Jira issue ID in the commit message is pushed, GitLab will +add a comment in the mentioned Jira issue and immediately close it (provided the transition ID was set up correctly). There are currently three trigger words, and you can use either one to achieve @@ -180,66 +182,66 @@ the same goal: - `Closes PROJECT-1` - `Fixes PROJECT-1` -where `PROJECT-1` is the issue ID of the JIRA project. +where `PROJECT-1` is the issue ID of the Jira project. > **Notes:** > - Only commits and merges into the project's default branch (usually **master**) will > close an issue in Jira. You can change your projects default branch under > [project settings](img/jira_project_settings.png). -> - The JIRA issue will not be transitioned if it has a resolution. +> - The Jira issue will not be transitioned if it has a resolution. -### JIRA issue closing example +### Jira issue closing example Let's consider the following example: -1. For the project named `PROJECT` in JIRA, we implemented a new feature +1. For the project named `PROJECT` in Jira, we implemented a new feature and created a merge request in GitLab. -1. This feature was requested in JIRA issue `PROJECT-7` and the merge request +1. This feature was requested in Jira issue `PROJECT-7` and the merge request in GitLab contains the improvement 1. In the merge request description we use the issue closing trigger `Closes PROJECT-7`. -1. Once the merge request is merged, the JIRA issue will be automatically closed +1. Once the merge request is merged, the Jira issue will be automatically closed with a comment and an associated link to the commit that resolved the issue. ---- - -In the following screenshot you can see what the link references to the JIRA +In the following screenshot you can see what the link references to the Jira issue look like. -![A Git commit that causes the JIRA issue to be closed](img/jira_merge_request_close.png) +![A Git commit that causes the Jira issue to be closed](img/jira_merge_request_close.png) ---- - -Once this merge request is merged, the JIRA issue will be automatically closed +Once this merge request is merged, the Jira issue will be automatically closed with a link to the commit that resolved the issue. -![The GitLab integration closes JIRA issue](img/jira_service_close_issue.png) +![The GitLab integration closes Jira issue](img/jira_service_close_issue.png) ---- - -![The GitLab integration creates a comment and a link on JIRA issue.](img/jira_service_close_comment.png) +![The GitLab integration creates a comment and a link on Jira issue.](img/jira_service_close_comment.png) ## Troubleshooting -If things don't work as expected that's usually because you have configured -incorrectly the JIRA-GitLab integration. +If these features do not work as expected, it is likely due to a problem with the way the integration settings were configured. -### GitLab is unable to comment on a ticket +### GitLab is unable to comment on a Jira issue -Make sure that the user you set up for GitLab to communicate with JIRA has the -correct access permission to post comments on a ticket and to also transition -the ticket, if you'd like GitLab to also take care of closing them. -JIRA issue references and update comments will not work if the GitLab issue tracker is disabled. +Make sure that the Jira user you set up for the integration has the +correct access permission to post comments on a Jira issue and also to transition +the issue, if you'd like GitLab to also be able to do so. +Jira issue references and update comments will not work if the GitLab issue tracker is disabled. -### GitLab is unable to close a ticket +### GitLab is unable to close a Jira issue -Make sure the `Transition ID` you set within the JIRA settings matches the one -your project needs to close a ticket. +Make sure the `Transition ID` you set within the Jira settings matches the one +your project needs to close an issue. -Make sure that the JIRA issue is not already marked as resolved, in other words that -the JIRA issue resolution field is not set. (It should not be struck through in -JIRA lists.) +Make sure that the Jira issue is not already marked as resolved; that is, +the Jira issue resolution field is not set. (It should not be struck through in +Jira lists.) + +### CAPTCHA + +CAPTCHA may be triggered after several consecutive failed login attempts +which may lead to a `401 unauthorized` error when testing your Jira integration. +If CAPTCHA has been triggered, you will not be able to use Jira's REST API to +authenticate with the Jira site. You will need to log in to your Jira instance +and complete the CAPTCHA. [services-templates]: services_templates.md [jira-repo-old-docs]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-13-stable/doc/project_services/jira.md -[jira]: https://www.atlassian.com/software/jira From 6553348aff043870bd5449cd22a3dc5a4e869de3 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 2 Oct 2018 01:09:58 -0500 Subject: [PATCH 12/38] Upgrade to babel 7 --- .babelrc | 26 +- package.json | 15 +- yarn.lock | 1420 ++++++++++++++++++++++++-------------------------- 3 files changed, 713 insertions(+), 748 deletions(-) diff --git a/.babelrc b/.babelrc index 50d85f58d69..72a81d08dbc 100644 --- a/.babelrc +++ b/.babelrc @@ -1,24 +1,40 @@ { - "presets": [["latest", { "es2015": { "modules": false } }], "stage-2"], + "presets": [ + [ + "@babel/preset-env", + { + "modules": false, + "targets": { + "ie": "11" + } + } + ] + ], + "plugins": [ + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-syntax-import-meta", + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-json-strings" + ], "env": { "karma": { - "plugins": ["rewire"] + "plugins": ["babel-plugin-rewire"] }, "coverage": { "plugins": [ [ - "istanbul", + "babel-plugin-istanbul", { "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"] } ], [ - "transform-define", + "babel-plugin-transform-define", { "process.env.BABEL_ENV": "coverage" } ], - "rewire" + "babel-plugin-rewire" ] } } diff --git a/package.json b/package.json index 714bdb285fe..96dd414ec30 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,18 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { + "@babel/core": "^7.1.2", + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-json-strings": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.0.0", + "@babel/plugin-syntax-import-meta": "^7.0.0", + "@babel/preset-env": "^7.1.0", "@gitlab-org/gitlab-svgs": "^1.29.0", "@gitlab-org/gitlab-ui": "^1.7.1", "autosize": "^4.0.0", "axios": "^0.17.1", - "babel-core": "^6.26.3", - "babel-loader": "^7.1.5", + "babel-loader": "^8.0.4", "babel-plugin-transform-define": "^1.3.0", - "babel-preset-latest": "^6.24.1", - "babel-preset-stage-2": "^6.24.1", "blackst0ne-mermaid": "^7.1.0-fixed", "bootstrap": "4.1.1", "brace-expansion": "^1.1.8", @@ -111,7 +114,7 @@ "devDependencies": { "axios-mock-adapter": "^1.15.0", "babel-eslint": "^9.0.0", - "babel-plugin-istanbul": "^5.0.1", + "babel-plugin-istanbul": "^5.1.0", "babel-plugin-rewire": "^1.2.0", "babel-template": "^6.26.0", "babel-types": "^6.26.0", @@ -135,7 +138,7 @@ "jasmine-jquery": "^2.1.1", "karma": "^3.0.0", "karma-chrome-launcher": "^2.2.0", - "karma-coverage-istanbul-reporter": "^1.4.2", + "karma-coverage-istanbul-reporter": "^2.0.4", "karma-jasmine": "^1.1.2", "karma-junit-reporter": "^1.2.0", "karma-mocha-reporter": "^2.2.5", diff --git a/yarn.lock b/yarn.lock index 33849642705..8bc242f208a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,27 +2,30 @@ # yarn lockfile v1 -"@babel/code-frame@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.51.tgz#bd71d9b192af978df915829d39d4094456439a0c" - dependencies: - "@babel/highlight" "7.0.0-beta.51" - "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" dependencies: "@babel/highlight" "^7.0.0" -"@babel/generator@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.51.tgz#6c7575ffde761d07485e04baedc0392c6d9e30f6" +"@babel/core@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.1.2.tgz#f8d2a9ceb6832887329a7b60f9d035791400ba4e" dependencies: - "@babel/types" "7.0.0-beta.51" - jsesc "^2.5.1" - lodash "^4.17.5" + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.1.2" + "@babel/helpers" "^7.1.2" + "@babel/parser" "^7.1.2" + "@babel/template" "^7.1.2" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.1.2" + convert-source-map "^1.1.0" + debug "^3.1.0" + json5 "^0.5.0" + lodash "^4.17.10" + resolve "^1.3.2" + semver "^5.4.1" source-map "^0.5.0" - trim-right "^1.0.1" "@babel/generator@^7.0.0": version "7.0.0" @@ -34,13 +37,51 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/helper-function-name@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.51.tgz#21b4874a227cf99ecafcc30a90302da5a2640561" +"@babel/generator@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.2.tgz#fde75c072575ce7abbd97322e8fef5bae67e4630" dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.51" - "@babel/template" "7.0.0-beta.51" - "@babel/types" "7.0.0-beta.51" + "@babel/types" "^7.1.2" + jsesc "^2.5.1" + lodash "^4.17.10" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/helper-annotate-as-pure@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz#6b69628dfe4087798e0c4ed98e3d4a6b2fbd2f5f" + dependencies: + "@babel/helper-explode-assignable-expression" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-call-delegate@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz#6a957f105f37755e8645343d3038a22e1449cc4a" + dependencies: + "@babel/helper-hoist-variables" "^7.0.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-define-map@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz#3b74caec329b3c80c116290887c0dd9ae468c20c" + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/types" "^7.0.0" + lodash "^4.17.10" + +"@babel/helper-explode-assignable-expression@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz#537fa13f6f1674df745b0c00ec8fe4e99681c8f6" + dependencies: + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" "@babel/helper-function-name@^7.1.0": version "7.1.0" @@ -50,23 +91,82 @@ "@babel/template" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/helper-get-function-arity@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.51.tgz#3281b2d045af95c172ce91b20825d85ea4676411" - dependencies: - "@babel/types" "7.0.0-beta.51" - "@babel/helper-get-function-arity@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" dependencies: "@babel/types" "^7.0.0" -"@babel/helper-split-export-declaration@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.51.tgz#8a6c3f66c4d265352fc077484f9f6e80a51ab978" +"@babel/helper-hoist-variables@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz#46adc4c5e758645ae7a45deb92bab0918c23bb88" dependencies: - "@babel/types" "7.0.0-beta.51" + "@babel/types" "^7.0.0" + +"@babel/helper-member-expression-to-functions@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz#8cd14b0a0df7ff00f009e7d7a436945f47c7a16f" + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-imports@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-module-transforms@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.1.0.tgz#470d4f9676d9fad50b324cdcce5fbabbc3da5787" + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + lodash "^4.17.10" + +"@babel/helper-optimise-call-expression@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz#a2920c5702b073c15de51106200aa8cad20497d5" + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-plugin-utils@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" + +"@babel/helper-regex@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0.tgz#2c1718923b57f9bbe64705ffe5640ac64d9bdb27" + dependencies: + lodash "^4.17.10" + +"@babel/helper-remap-async-to-generator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz#361d80821b6f38da75bd3f0785ece20a88c5fe7f" + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-wrap-function" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-replace-supers@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.1.0.tgz#5fc31de522ec0ef0899dc9b3e7cf6a5dd655f362" + dependencies: + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-simple-access@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz#65eeb954c8c245beaa4e859da6188f39d71e585c" + dependencies: + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" "@babel/helper-split-export-declaration@^7.0.0": version "7.0.0" @@ -74,13 +174,22 @@ dependencies: "@babel/types" "^7.0.0" -"@babel/highlight@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.51.tgz#e8844ae25a1595ccfd42b89623b4376ca06d225d" +"@babel/helper-wrap-function@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz#8cf54e9190706067f016af8f75cb3df829cc8c66" dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^3.0.0" + "@babel/helper-function-name" "^7.1.0" + "@babel/template" "^7.1.0" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helpers@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.1.2.tgz#ab752e8c35ef7d39987df4e8586c63b8846234b5" + dependencies: + "@babel/template" "^7.1.2" + "@babel/traverse" "^7.1.0" + "@babel/types" "^7.1.2" "@babel/highlight@^7.0.0": version "7.0.0" @@ -90,22 +199,345 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-beta.51.tgz#27cec2df409df60af58270ed8f6aa55409ea86f6" - "@babel/parser@^7.0.0", "@babel/parser@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.0.tgz#a7cd42cb3c12aec52e24375189a47b39759b783e" -"@babel/template@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.51.tgz#9602a40aebcf357ae9677e2532ef5fc810f5fbff" +"@babel/parser@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.2.tgz#85c5c47af6d244fab77bce6b9bd830e38c978409" + +"@babel/plugin-proposal-async-generator-functions@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz#41c1a702e10081456e23a7b74d891922dd1bb6ce" dependencies: - "@babel/code-frame" "7.0.0-beta.51" - "@babel/parser" "7.0.0-beta.51" - "@babel/types" "7.0.0-beta.51" - lodash "^4.17.5" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + "@babel/plugin-syntax-async-generators" "^7.0.0" + +"@babel/plugin-proposal-class-properties@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.1.0.tgz#9af01856b1241db60ec8838d84691aa0bd1e8df4" + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-member-expression-to-functions" "^7.0.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.1.0" + "@babel/plugin-syntax-class-properties" "^7.0.0" + +"@babel/plugin-proposal-json-strings@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz#3b4d7b5cf51e1f2e70f52351d28d44fc2970d01e" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.0.0" + +"@babel/plugin-proposal-object-rest-spread@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz#9a17b547f64d0676b6c9cecd4edf74a82ab85e7e" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz#b610d928fe551ff7117d42c8bb410eec312a6425" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz#498b39cd72536cd7c4b26177d030226eba08cd33" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + regexpu-core "^4.2.0" + +"@babel/plugin-syntax-async-generators@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz#bf0891dcdbf59558359d0c626fdc9490e20bc13c" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-class-properties@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz#e051af5d300cbfbcec4a7476e37a803489881634" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-dynamic-import@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.0.0.tgz#6dfb7d8b6c3be14ce952962f658f3b7eb54c33ee" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-import-meta@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.0.0.tgz#ca946b73216c29c39a55ef2d739097fee8a85d69" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-json-strings@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz#0d259a68090e15b383ce3710e01d5b23f3770cbd" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-object-rest-spread@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz#37d8fbcaf216bd658ea1aebbeb8b75e88ebc549b" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz#886f72008b3a8b185977f7cb70713b45e51ee475" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-arrow-functions@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz#a6c14875848c68a3b4b3163a486535ef25c7e749" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-async-to-generator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz#109e036496c51dd65857e16acab3bafdf3c57811" + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-remap-async-to-generator" "^7.1.0" + +"@babel/plugin-transform-block-scoped-functions@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz#482b3f75103927e37288b3b67b65f848e2aa0d07" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-block-scoping@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0.tgz#1745075edffd7cdaf69fab2fb6f9694424b7e9bc" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + lodash "^4.17.10" + +"@babel/plugin-transform-classes@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz#ab3f8a564361800cbc8ab1ca6f21108038432249" + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-define-map" "^7.1.0" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-optimise-call-expression" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.0.0" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz#2fbb8900cd3e8258f2a2ede909b90e7556185e31" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-destructuring@^7.0.0": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.2.tgz#5fa77d473f5a0a3f5266ad7ce2e8c995a164d60a" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-dotall-regex@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz#73a24da69bc3c370251f43a3d048198546115e58" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + regexpu-core "^4.1.3" + +"@babel/plugin-transform-duplicate-keys@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz#a0601e580991e7cace080e4cf919cfd58da74e86" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-exponentiation-operator@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz#9c34c2ee7fd77e02779cfa37e403a2e1003ccc73" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-for-of@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz#f2ba4eadb83bd17dc3c7e9b30f4707365e1c3e39" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz#29c5550d5c46208e7f730516d41eeddd4affadbb" + dependencies: + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-literals@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz#2aec1d29cdd24c407359c930cdd89e914ee8ff86" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-amd@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz#f9e0a7072c12e296079b5a59f408ff5b97bf86a8" + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-commonjs@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz#0a9d86451cbbfb29bd15186306897c67f6f9a05c" + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-simple-access" "^7.1.0" + +"@babel/plugin-transform-modules-systemjs@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0.tgz#8873d876d4fee23209decc4d1feab8f198cf2df4" + dependencies: + "@babel/helper-hoist-variables" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-modules-umd@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz#a29a7d85d6f28c3561c33964442257cc6a21f2a8" + dependencies: + "@babel/helper-module-transforms" "^7.1.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-new-target@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz#ae8fbd89517fa7892d20e6564e641e8770c3aa4a" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-object-super@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz#b1ae194a054b826d8d4ba7ca91486d4ada0f91bb" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-replace-supers" "^7.1.0" + +"@babel/plugin-transform-parameters@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz#44f492f9d618c9124026e62301c296bf606a7aed" + dependencies: + "@babel/helper-call-delegate" "^7.1.0" + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-regenerator@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0.tgz#5b41686b4ed40bef874d7ed6a84bdd849c13e0c1" + dependencies: + regenerator-transform "^0.13.3" + +"@babel/plugin-transform-shorthand-properties@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz#85f8af592dcc07647541a0350e8c95c7bf419d15" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-spread@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz#93583ce48dd8c85e53f3a46056c856e4af30b49b" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-sticky-regex@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz#30a9d64ac2ab46eec087b8530535becd90e73366" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + +"@babel/plugin-transform-template-literals@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz#084f1952efe5b153ddae69eb8945f882c7a97c65" + dependencies: + "@babel/helper-annotate-as-pure" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-typeof-symbol@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz#4dcf1e52e943e5267b7313bff347fdbe0f81cec9" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +"@babel/plugin-transform-unicode-regex@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz#c6780e5b1863a76fe792d90eded9fcd5b51d68fc" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/helper-regex" "^7.0.0" + regexpu-core "^4.1.3" + +"@babel/preset-env@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.0.tgz#e67ea5b0441cfeab1d6f41e9b5c79798800e8d11" + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-async-generator-functions" "^7.1.0" + "@babel/plugin-proposal-json-strings" "^7.0.0" + "@babel/plugin-proposal-object-rest-spread" "^7.0.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.0.0" + "@babel/plugin-syntax-async-generators" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + "@babel/plugin-transform-arrow-functions" "^7.0.0" + "@babel/plugin-transform-async-to-generator" "^7.1.0" + "@babel/plugin-transform-block-scoped-functions" "^7.0.0" + "@babel/plugin-transform-block-scoping" "^7.0.0" + "@babel/plugin-transform-classes" "^7.1.0" + "@babel/plugin-transform-computed-properties" "^7.0.0" + "@babel/plugin-transform-destructuring" "^7.0.0" + "@babel/plugin-transform-dotall-regex" "^7.0.0" + "@babel/plugin-transform-duplicate-keys" "^7.0.0" + "@babel/plugin-transform-exponentiation-operator" "^7.1.0" + "@babel/plugin-transform-for-of" "^7.0.0" + "@babel/plugin-transform-function-name" "^7.1.0" + "@babel/plugin-transform-literals" "^7.0.0" + "@babel/plugin-transform-modules-amd" "^7.1.0" + "@babel/plugin-transform-modules-commonjs" "^7.1.0" + "@babel/plugin-transform-modules-systemjs" "^7.0.0" + "@babel/plugin-transform-modules-umd" "^7.1.0" + "@babel/plugin-transform-new-target" "^7.0.0" + "@babel/plugin-transform-object-super" "^7.1.0" + "@babel/plugin-transform-parameters" "^7.1.0" + "@babel/plugin-transform-regenerator" "^7.0.0" + "@babel/plugin-transform-shorthand-properties" "^7.0.0" + "@babel/plugin-transform-spread" "^7.0.0" + "@babel/plugin-transform-sticky-regex" "^7.0.0" + "@babel/plugin-transform-template-literals" "^7.0.0" + "@babel/plugin-transform-typeof-symbol" "^7.0.0" + "@babel/plugin-transform-unicode-regex" "^7.0.0" + browserslist "^4.1.0" + invariant "^2.2.2" + js-levenshtein "^1.1.3" + semver "^5.3.0" + +"@babel/template@^7.0.0", "@babel/template@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644" + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.1.2" + "@babel/types" "^7.1.2" "@babel/template@^7.1.0": version "7.1.0" @@ -115,22 +547,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@babel/traverse@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.51.tgz#981daf2cec347a6231d3aa1d9e1803b03aaaa4a8" - dependencies: - "@babel/code-frame" "7.0.0-beta.51" - "@babel/generator" "7.0.0-beta.51" - "@babel/helper-function-name" "7.0.0-beta.51" - "@babel/helper-split-export-declaration" "7.0.0-beta.51" - "@babel/parser" "7.0.0-beta.51" - "@babel/types" "7.0.0-beta.51" - debug "^3.1.0" - globals "^11.1.0" - invariant "^2.2.0" - lodash "^4.17.5" - -"@babel/traverse@^7.0.0": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.0.tgz#503ec6669387efd182c3888c4eec07bcc45d91b2" dependencies: @@ -144,14 +561,6 @@ globals "^11.1.0" lodash "^4.17.10" -"@babel/types@7.0.0-beta.51": - version "7.0.0-beta.51" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.51.tgz#d802b7b543b5836c778aa691797abf00f3d97ea9" - dependencies: - esutils "^2.0.2" - lodash "^4.17.5" - to-fast-properties "^2.0.0" - "@babel/types@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118" @@ -160,6 +569,14 @@ lodash "^4.17.10" to-fast-properties "^2.0.0" +"@babel/types@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.2.tgz#183e7952cf6691628afdc2e2b90d03240bac80c0" + dependencies: + esutils "^2.0.2" + lodash "^4.17.10" + to-fast-properties "^2.0.0" + "@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.29.0": version "1.29.0" resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70" @@ -454,11 +871,11 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -append-transform@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" +append-transform@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" dependencies: - default-require-extensions "^1.0.0" + default-require-extensions "^2.0.0" aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" @@ -565,7 +982,7 @@ async@1.x, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" -async@^2.0.0, async@^2.1.4, async@^2.5.0: +async@^2.0.0, async@^2.5.0, async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" dependencies: @@ -600,30 +1017,6 @@ babel-code-frame@^6.26.0: esutils "^2.0.2" js-tokens "^3.0.2" -babel-core@^6.26.0, babel-core@^6.26.3: - version "6.26.3" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.1" - debug "^2.6.9" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.8" - slash "^1.0.0" - source-map "^0.5.7" - babel-eslint@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-9.0.0.tgz#7d9445f81ed9f60aff38115f838970df9f2b6220" @@ -635,144 +1028,14 @@ babel-eslint@^9.0.0: eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" -babel-generator@^6.18.0, babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helper-bindify-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" - dependencies: - babel-helper-explode-assignable-expression "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-call-delegate@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-define-map@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-explode-assignable-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-explode-class@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" - dependencies: - babel-helper-bindify-decorators "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" - dependencies: - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-get-function-arity@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-hoist-variables@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-optimise-call-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-regex@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" - dependencies: - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-remap-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-replace-supers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" - dependencies: - babel-helper-optimise-call-expression "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-loader@^7.1.5: - version "7.1.5" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68" +babel-loader@^8.0.4: + version "8.0.4" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.4.tgz#7bbf20cbe4560629e2e41534147692d3fecbdce6" dependencies: find-cache-dir "^1.0.0" loader-utils "^1.0.2" mkdirp "^0.5.1" + util.promisify "^1.0.0" babel-messages@^6.23.0: version "6.23.0" @@ -780,91 +1043,18 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-check-es2015-constants@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-istanbul@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.0.1.tgz#2ce7bf211f0d9480ff7fd294bd05e2fa555e31ea" +babel-plugin-istanbul@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.0.tgz#6892f529eff65a3e2d33d87dc5888ffa2ecd4a30" dependencies: find-up "^3.0.0" - istanbul-lib-instrument "^2.2.0" + istanbul-lib-instrument "^3.0.0" test-exclude "^5.0.0" babel-plugin-rewire@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89" -babel-plugin-syntax-async-functions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" - -babel-plugin-syntax-async-generators@^6.5.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" - -babel-plugin-syntax-class-properties@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" - -babel-plugin-syntax-decorators@^6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" - -babel-plugin-syntax-dynamic-import@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" - -babel-plugin-syntax-exponentiation-operator@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" - -babel-plugin-syntax-object-rest-spread@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" - -babel-plugin-syntax-trailing-function-commas@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" - -babel-plugin-transform-async-generator-functions@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-generators "^6.5.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-class-properties@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" - dependencies: - babel-helper-function-name "^6.24.1" - babel-plugin-syntax-class-properties "^6.8.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" - dependencies: - babel-helper-explode-class "^6.24.1" - babel-plugin-syntax-decorators "^6.13.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-types "^6.24.1" - babel-plugin-transform-define@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.0.tgz#94c5f9459c810c738cc7c50cbd44a31829d6f319" @@ -872,202 +1062,6 @@ babel-plugin-transform-define@^1.3.0: lodash "4.17.4" traverse "0.6.6" -babel-plugin-transform-es2015-arrow-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoping@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" - dependencies: - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-plugin-transform-es2015-classes@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" - dependencies: - babel-helper-define-map "^6.24.1" - babel-helper-function-name "^6.24.1" - babel-helper-optimise-call-expression "^6.24.1" - babel-helper-replace-supers "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-computed-properties@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-destructuring@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-duplicate-keys@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-for-of@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-modules-amd@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" - dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-commonjs@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" - dependencies: - babel-plugin-transform-strict-mode "^6.24.1" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-types "^6.26.0" - -babel-plugin-transform-es2015-modules-systemjs@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-umd@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" - dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-object-super@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" - dependencies: - babel-helper-replace-supers "^6.24.1" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-parameters@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" - dependencies: - babel-helper-call-delegate "^6.24.1" - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-shorthand-properties@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-spread@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-sticky-regex@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-template-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-typeof-symbol@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-unicode-regex@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - regexpu-core "^2.0.0" - -babel-plugin-transform-exponentiation-operator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" - dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" - babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-object-rest-spread@^6.22.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" - dependencies: - babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-regenerator@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" - dependencies: - regenerator-transform "^0.10.0" - -babel-plugin-transform-strict-mode@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - babel-polyfill@6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" @@ -1076,95 +1070,14 @@ babel-polyfill@6.23.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-preset-es2015@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.24.1" - babel-plugin-transform-es2015-classes "^6.24.1" - babel-plugin-transform-es2015-computed-properties "^6.24.1" - babel-plugin-transform-es2015-destructuring "^6.22.0" - babel-plugin-transform-es2015-duplicate-keys "^6.24.1" - babel-plugin-transform-es2015-for-of "^6.22.0" - babel-plugin-transform-es2015-function-name "^6.24.1" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-plugin-transform-es2015-modules-systemjs "^6.24.1" - babel-plugin-transform-es2015-modules-umd "^6.24.1" - babel-plugin-transform-es2015-object-super "^6.24.1" - babel-plugin-transform-es2015-parameters "^6.24.1" - babel-plugin-transform-es2015-shorthand-properties "^6.24.1" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.24.1" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.22.0" - babel-plugin-transform-es2015-unicode-regex "^6.24.1" - babel-plugin-transform-regenerator "^6.24.1" - -babel-preset-es2016@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz#f900bf93e2ebc0d276df9b8ab59724ebfd959f8b" - dependencies: - babel-plugin-transform-exponentiation-operator "^6.24.1" - -babel-preset-es2017@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-es2017/-/babel-preset-es2017-6.24.1.tgz#597beadfb9f7f208bcfd8a12e9b2b29b8b2f14d1" - dependencies: - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.24.1" - -babel-preset-latest@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-latest/-/babel-preset-latest-6.24.1.tgz#677de069154a7485c2d25c577c02f624b85b85e8" - dependencies: - babel-preset-es2015 "^6.24.1" - babel-preset-es2016 "^6.24.1" - babel-preset-es2017 "^6.24.1" - -babel-preset-stage-2@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" - dependencies: - babel-plugin-syntax-dynamic-import "^6.18.0" - babel-plugin-transform-class-properties "^6.24.1" - babel-plugin-transform-decorators "^6.24.1" - babel-preset-stage-3 "^6.24.1" - -babel-preset-stage-3@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" - dependencies: - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-generator-functions "^6.24.1" - babel-plugin-transform-async-to-generator "^6.24.1" - babel-plugin-transform-exponentiation-operator "^6.24.1" - babel-plugin-transform-object-rest-spread "^6.22.0" - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" -babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: +babel-template@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" dependencies: @@ -1174,7 +1087,7 @@ babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0: babylon "^6.18.0" lodash "^4.17.4" -babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0: +babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" dependencies: @@ -1188,7 +1101,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.24.1, babel-traverse@^6.26.0: invariant "^2.2.2" lodash "^4.17.4" -babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: +babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" dependencies: @@ -1426,6 +1339,14 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" +browserslist@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.1.1.tgz#328eb4ff1215b12df6589e9ab82f8adaa4fc8cd6" + dependencies: + caniuse-lite "^1.0.30000884" + electron-to-chromium "^1.3.62" + node-releases "^1.0.0-alpha.11" + buffer-from@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531" @@ -1552,6 +1473,10 @@ camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" +caniuse-lite@^1.0.30000884: + version "1.0.30000888" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000888.tgz#22edb50d91dd70612b5898e3b36f460600c6492f" + capture-stack-trace@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" @@ -1750,6 +1675,10 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +compare-versions@^3.2.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26" + component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" @@ -1860,9 +1789,11 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" +convert-source-map@^1.1.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + dependencies: + safe-buffer "~5.1.1" cookie-signature@1.0.6: version "1.0.6" @@ -1887,7 +1818,7 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" -core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0: +core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" @@ -2336,11 +2267,11 @@ default-gateway@^2.6.0: execa "^0.10.0" ip-regex "^2.1.0" -default-require-extensions@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" +default-require-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" dependencies: - strip-bom "^2.0.0" + strip-bom "^3.0.0" define-properties@^1.1.2: version "1.1.3" @@ -2417,12 +2348,6 @@ destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - dependencies: - repeating "^2.0.0" - detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -2561,6 +2486,10 @@ ejs@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" +electron-to-chromium@^1.3.62: + version "1.3.73" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.73.tgz#aa67787067d58cc3920089368b3b8d6fe0fc12f6" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2670,7 +2599,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.6.1: +es-abstract@^1.5.1, es-abstract@^1.6.1: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" dependencies: @@ -3132,7 +3061,7 @@ file-loader@^2.0.0: loader-utils "^1.0.2" schema-utils "^1.0.0" -fileset@^2.0.2: +fileset@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" dependencies: @@ -3491,7 +3420,7 @@ handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" -handlebars@^4.0.1, handlebars@^4.0.3: +handlebars@^4.0.1, handlebars@^4.0.11: version "4.0.12" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" dependencies: @@ -3612,13 +3541,6 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - hoopy@^0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -3848,7 +3770,7 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@^2.2.0, invariant@^2.2.2: +invariant@^2.2.2: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" dependencies: @@ -3952,12 +3874,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -4077,10 +3993,6 @@ is-symbol@^1.0.1: dependencies: has-symbols "^1.0.0" -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4115,84 +4027,68 @@ isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" -istanbul-api@^1.3.1: - version "1.3.7" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa" +istanbul-api@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.0.6.tgz#cd7b33ee678f6c01531d05f5e567ebbcd25f8ecc" dependencies: - async "^2.1.4" - fileset "^2.0.2" - istanbul-lib-coverage "^1.2.1" - istanbul-lib-hook "^1.2.2" - istanbul-lib-instrument "^1.10.2" - istanbul-lib-report "^1.1.5" - istanbul-lib-source-maps "^1.2.6" - istanbul-reports "^1.5.1" - js-yaml "^3.7.0" - mkdirp "^0.5.1" + async "^2.6.1" + compare-versions "^3.2.1" + fileset "^2.0.3" + istanbul-lib-coverage "^2.0.1" + istanbul-lib-hook "^2.0.1" + istanbul-lib-instrument "^3.0.0" + istanbul-lib-report "^2.0.2" + istanbul-lib-source-maps "^2.0.1" + istanbul-reports "^2.0.1" + js-yaml "^3.12.0" + make-dir "^1.3.0" once "^1.4.0" -istanbul-lib-coverage@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0" - istanbul-lib-coverage@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#2aee0e073ad8c5f6a0b00e0dfbf52b4667472eda" -istanbul-lib-hook@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86" +istanbul-lib-hook@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.1.tgz#918a57b75a0f951d552a08487ca1fa5336433d72" dependencies: - append-transform "^0.4.0" + append-transform "^1.0.0" -istanbul-lib-instrument@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca" +istanbul-lib-instrument@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz#b5f066b2a161f75788be17a9d556f40a0cf2afc9" dependencies: - babel-generator "^6.18.0" - babel-template "^6.16.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" - babylon "^6.18.0" - istanbul-lib-coverage "^1.2.1" - semver "^5.3.0" - -istanbul-lib-instrument@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-2.3.2.tgz#b287cbae2b5f65f3567b05e2e29b275eaf92d25e" - dependencies: - "@babel/generator" "7.0.0-beta.51" - "@babel/parser" "7.0.0-beta.51" - "@babel/template" "7.0.0-beta.51" - "@babel/traverse" "7.0.0-beta.51" - "@babel/types" "7.0.0-beta.51" + "@babel/generator" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/template" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" istanbul-lib-coverage "^2.0.1" semver "^5.5.0" -istanbul-lib-report@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c" +istanbul-lib-report@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.2.tgz#430a2598519113e1da7af274ba861bd42dd97535" dependencies: - istanbul-lib-coverage "^1.2.1" - mkdirp "^0.5.1" - path-parse "^1.0.5" - supports-color "^3.1.2" + istanbul-lib-coverage "^2.0.1" + make-dir "^1.3.0" + supports-color "^5.4.0" -istanbul-lib-source-maps@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f" +istanbul-lib-source-maps@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-2.0.1.tgz#ce8b45131d8293fdeaa732f4faf1852d13d0a97e" dependencies: debug "^3.1.0" - istanbul-lib-coverage "^1.2.1" - mkdirp "^0.5.1" - rimraf "^2.6.1" - source-map "^0.5.3" + istanbul-lib-coverage "^2.0.1" + make-dir "^1.3.0" + rimraf "^2.6.2" + source-map "^0.6.1" -istanbul-reports@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a" +istanbul-reports@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.0.1.tgz#fb8d6ea850701a3984350b977a969e9a556116a7" dependencies: - handlebars "^4.0.3" + handlebars "^4.0.11" istanbul@^0.4.5: version "0.4.5" @@ -4264,25 +4160,25 @@ js-cookie@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526" -js-tokens@^3.0.0, js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" +js-levenshtein@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.4.tgz#3a56e3cbf589ca0081eb22cd9ba0b1290a16d26e" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" -js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.7.0: +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@3.x, js-yaml@^3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" dependencies: argparse "^1.0.7" esprima "^4.0.0" -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" - jsesc@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" @@ -4311,7 +4207,7 @@ json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" -json5@^0.5.0, json5@^0.5.1: +json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -4336,11 +4232,11 @@ karma-chrome-launcher@^2.2.0: fs-access "^1.0.0" which "^1.2.1" -karma-coverage-istanbul-reporter@^1.4.2: - version "1.4.3" - resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.3.tgz#3b5dff4664fa5b8d5196b9889e3f61c1fa2b80d9" +karma-coverage-istanbul-reporter@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.4.tgz#402ae4ed6eadb9d9dafbd408ffda17897c0d003a" dependencies: - istanbul-api "^1.3.1" + istanbul-api "^2.0.5" minimatch "^3.0.4" karma-jasmine@^1.1.2: @@ -4631,6 +4527,12 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-dir@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + mamacro@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" @@ -4989,6 +4891,12 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" +node-releases@^1.0.0-alpha.11: + version "1.0.0-alpha.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.12.tgz#32e461b879ea76ac674e511d9832cf29da345268" + dependencies: + semver "^5.3.0" + nodemon@^1.18.4: version "1.18.4" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.4.tgz#873f65fdb53220eb166180cf106b1354ac5d714d" @@ -5124,6 +5032,13 @@ object.entries@^1.0.4: function-bind "^1.1.0" has "^1.0.1" +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -5224,7 +5139,7 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -5379,7 +5294,7 @@ path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -5566,7 +5481,7 @@ prismjs@^1.6.0: optionalDependencies: clipboard "^1.5.5" -private@^0.1.6, private@^0.1.8: +private@^0.1.6: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -5800,10 +5715,20 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +regenerate-unicode-properties@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" + dependencies: + regenerate "^1.4.0" + regenerate@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + regenerator-runtime@^0.10.0: version "0.10.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" @@ -5812,12 +5737,10 @@ regenerator-runtime@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" -regenerator-transform@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" +regenerator-transform@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb" dependencies: - babel-runtime "^6.18.0" - babel-types "^6.19.0" private "^0.1.6" regex-not@^1.0.0, regex-not@^1.0.2: @@ -5839,13 +5762,16 @@ regexpu-core@^1.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -regexpu-core@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" +regexpu-core@^4.1.3, regexpu-core@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d" dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" + regenerate "^1.4.0" + regenerate-unicode-properties "^7.0.0" + regjsgen "^0.4.0" + regjsparser "^0.3.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.0.2" registry-auth-token@^3.0.1: version "3.3.2" @@ -5864,12 +5790,22 @@ regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" +regjsgen@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561" + regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" dependencies: jsesc "~0.5.0" +regjsparser@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96" + dependencies: + jsesc "~0.5.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -5886,12 +5822,6 @@ repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -5933,6 +5863,12 @@ resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +resolve@^1.3.2: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0: version "1.7.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3" @@ -6072,7 +6008,7 @@ semver-diff@^2.0.0: dependencies: semver "^5.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.5.1: +"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1: version "5.5.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" @@ -6191,10 +6127,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - slice-ansi@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" @@ -6320,12 +6252,6 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - dependencies: - source-map "^0.5.6" - source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -6334,7 +6260,7 @@ source-map@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: +source-map@^0.5.0, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -6527,12 +6453,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -6556,7 +6476,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.2: +supports-color@^3.1.0: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -6805,6 +6725,25 @@ underscore@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4" +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz#9f1dc76926d6ccf452310564fd834ace059663d4" + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz#5a533f31b4317ea76f17d807fa0d116546111dd0" + union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -6947,6 +6886,13 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3, util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" From 63bc9a60c5e43c38733c9b9dc6ae0669c9cc4515 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 2 Oct 2018 14:24:25 -0500 Subject: [PATCH 13/38] Add license approval for caniuse-lite and node-releases These are needed for babel-preset-env to function. These are both curated lists of compatability tables which are used during the build process to determine which babel transformations to perform. --- config/dependency_decisions.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 664035831a5..62760ffee3a 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -585,3 +585,17 @@ and are therefore exempt. :versions: [] :when: 2018-08-30 12:06:35.668181000 Z +- - :approve + - caniuse-lite + - :who: Mike Greiling + :why: CC-BY-4.0 license. Tool only used during build process, code is not present + in compiled/distributed product so attribution not needed. + :versions: [] + :when: 2018-10-02 19:23:11.221660000 Z +- - :approve + - node-releases + - :who: Mike Greiling + :why: CC-BY-4.0 license. Tool only used during build process, code is not present + in compiled/distributed product so attribution not needed. + :versions: [] + :when: 2018-10-02 19:23:54.840151000 Z From a9df7d866dc726ded2cb0a295a4af68a010201e2 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 2 Oct 2018 15:12:04 -0500 Subject: [PATCH 14/38] Remove babel-plugin-transform-define dependency --- .babelrc | 6 ------ config/karma.config.js | 11 +++++++---- package.json | 1 - spec/javascripts/test_bundle.js | 2 +- yarn.lock | 15 --------------- 5 files changed, 8 insertions(+), 27 deletions(-) diff --git a/.babelrc b/.babelrc index 72a81d08dbc..83172a47753 100644 --- a/.babelrc +++ b/.babelrc @@ -28,12 +28,6 @@ "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"] } ], - [ - "babel-plugin-transform-define", - { - "process.env.BABEL_ENV": "coverage" - } - ], "babel-plugin-rewire" ] } diff --git a/config/karma.config.js b/config/karma.config.js index cad3c0e9193..7960bbf69ad 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -6,7 +6,6 @@ const argumentsParser = require('commander'); const webpackConfig = require('./webpack.config.js'); const ROOT_PATH = path.resolve(__dirname, '..'); -const GENERATE_COVERAGE_REPORT = process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage'; function fatalError(message) { console.error(chalk.red(`\nError: ${message}\n`)); @@ -24,7 +23,11 @@ webpackConfig.optimization.splitChunks = false; webpackConfig.devtool = 'cheap-inline-source-map'; webpackConfig.plugins.push( - new webpack.DefinePlugin({ GENERATE_COVERAGE_REPORT }), + new webpack.DefinePlugin({ + 'process.env': { + BABEL_ENV: JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null), + }, + }) ); const specFilters = argumentsParser @@ -89,7 +92,7 @@ module.exports = function(config) { basePath: ROOT_PATH, browsers: ['ChromeHeadlessCustom'], client: { - color: !process.env.CI + color: !process.env.CI, }, customLaunchers: { ChromeHeadlessCustom: { @@ -123,7 +126,7 @@ module.exports = function(config) { }; } - if (GENERATE_COVERAGE_REPORT) { + if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') { karmaConfig.reporters.push('coverage-istanbul'); karmaConfig.coverageIstanbulReporter = { reports: ['html', 'text-summary'], diff --git a/package.json b/package.json index 96dd414ec30..fe2f56e5da6 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "autosize": "^4.0.0", "axios": "^0.17.1", "babel-loader": "^8.0.4", - "babel-plugin-transform-define": "^1.3.0", "blackst0ne-mermaid": "^7.1.0-fixed", "bootstrap": "4.1.1", "brace-expansion": "^1.1.8", diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index d0fba401115..96c0844f83c 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -155,7 +155,7 @@ describe('test errors', () => { // if we're generating coverage reports, make sure to include all files so // that we can catch files with 0% coverage // see: https://github.com/deepsweet/istanbul-instrumenter-loader/issues/15 -if (GENERATE_COVERAGE_REPORT) { // eslint-disable-line no-undef +if (process.env.BABEL_ENV === 'coverage') { // exempt these files from the coverage report const troubleMakers = [ './blob_edit/blob_bundle.js', diff --git a/yarn.lock b/yarn.lock index 8bc242f208a..023a5f082fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1055,13 +1055,6 @@ babel-plugin-rewire@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89" -babel-plugin-transform-define@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.0.tgz#94c5f9459c810c738cc7c50cbd44a31829d6f319" - dependencies: - lodash "4.17.4" - traverse "0.6.6" - babel-polyfill@6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" @@ -4461,10 +4454,6 @@ lodash.upperfirst@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" -lodash@4.17.4: - version "4.17.4" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" - lodash@^4.11.1, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -6643,10 +6632,6 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -traverse@0.6.6: - version "0.6.6" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" - trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" From 5f53269f0e4cb819c59ecd173496d6202feb7f7a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 2 Oct 2018 16:11:53 -0500 Subject: [PATCH 15/38] Update babelrc with comments --- .babelrc | 35 ----------------------------------- .babelrc.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 35 deletions(-) delete mode 100644 .babelrc create mode 100644 .babelrc.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 83172a47753..00000000000 --- a/.babelrc +++ /dev/null @@ -1,35 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "modules": false, - "targets": { - "ie": "11" - } - } - ] - ], - "plugins": [ - "@babel/plugin-syntax-dynamic-import", - "@babel/plugin-syntax-import-meta", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-json-strings" - ], - "env": { - "karma": { - "plugins": ["babel-plugin-rewire"] - }, - "coverage": { - "plugins": [ - [ - "babel-plugin-istanbul", - { - "exclude": ["spec/javascripts/**/*", "app/assets/javascripts/locale/**/app.js"] - } - ], - "babel-plugin-rewire" - ] - } - } -} diff --git a/.babelrc.js b/.babelrc.js new file mode 100644 index 00000000000..27caf378b99 --- /dev/null +++ b/.babelrc.js @@ -0,0 +1,38 @@ +const BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || null; + +const presets = [ + [ + '@babel/preset-env', + { + modules: false, + targets: { + ie: '11', + }, + }, + ], +]; + +// include stage 3 proposals +const plugins = [ + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-syntax-import-meta', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-proposal-json-strings', +]; + +// add code coverage tooling if necessary +if (BABEL_ENV === 'coverage') { + plugins.push([ + 'babel-plugin-istanbul', + { + exclude: ['spec/javascripts/**/*', 'app/assets/javascripts/locale/**/app.js'], + }, + ]); +} + +// add rewire support when running tests +if (BABEL_ENV === 'karma' || BABEL_ENV === 'coverage') { + plugins.push('babel-plugin-rewire'); +} + +module.exports = { presets, plugins }; From 7ab73173b6e3345535da363743fba6ceef21265a Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 2 Oct 2018 16:24:44 -0500 Subject: [PATCH 16/38] Make DefinePlugin definitions more specific --- config/karma.config.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/config/karma.config.js b/config/karma.config.js index 7960bbf69ad..e1d7c30b1c2 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -22,11 +22,10 @@ webpackConfig.optimization.splitChunks = false; // use quicker sourcemap option webpackConfig.devtool = 'cheap-inline-source-map'; +// set BABEL_ENV to indicate when we're running code coverage webpackConfig.plugins.push( new webpack.DefinePlugin({ - 'process.env': { - BABEL_ENV: JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null), - }, + 'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null), }) ); From 65e18817a439b6f14f32ad8518ea2e99f84da200 Mon Sep 17 00:00:00 2001 From: Jose Vargas Date: Wed, 26 Sep 2018 15:44:48 -0500 Subject: [PATCH 17/38] Add gl-button from gitlab-ui --- .../boards/components/board_new_issue.vue | 16 ++++++++++------ .../components/environment_monitoring.vue | 9 ++++++--- package.json | 2 +- spec/javascripts/boards/board_new_issue_spec.js | 1 - yarn.lock | 5 ++--- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index f248f53fa51..f7ce5128964 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -1,5 +1,6 @@ diff --git a/package.json b/package.json index 714bdb285fe..3321115a84b 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@gitlab-org/gitlab-svgs": "^1.29.0", - "@gitlab-org/gitlab-ui": "^1.7.1", + "@gitlab-org/gitlab-ui": "https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.3", diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js index ee37821ad08..1245e3e099a 100644 --- a/spec/javascripts/boards/board_new_issue_spec.js +++ b/spec/javascripts/boards/board_new_issue_spec.js @@ -69,7 +69,6 @@ describe('Issue boards new issue form', () => { vm.$el.querySelector('.btn-success').click(); expect(vm.submit.calls.count()).toBe(1); - expect(vm.$refs['submit-button']).toBe(vm.$el.querySelector('.btn-success')); }) .then(done) .catch(done.fail); diff --git a/yarn.lock b/yarn.lock index 20b587d1fc5..4cfdfa51092 100644 --- a/yarn.lock +++ b/yarn.lock @@ -185,10 +185,9 @@ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70" integrity sha512-sCl6nP3ph36+8P3nrw9VanAR648rgOUEBlEoLPHkhKm79xB1dUkXGBtI0uaSJVgbJx40M1/Ts8HSdMv+PF3EIg== -"@gitlab-org/gitlab-ui@^1.7.1": +"@gitlab-org/gitlab-ui@https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz": version "1.7.1" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.7.1.tgz#e9cce86cb7e34311405e705c1de674276b453f17" - integrity sha512-X12W39lFnWmfmYcHBokrauKvp6VLW9u0rFdgBXWlnrRL2hBShnyeBsBXKLMGUGnofMtgYv3iYO/rFW9IB79lFg== + resolved "https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz#e17416d3b829a03ee8d9eef8b58b39dc8c9fb867" dependencies: "@gitlab-org/gitlab-svgs" "^1.23.0" bootstrap-vue "^2.0.0-rc.11" From bc94598490b181820c1b3275f5b4ebc874263c17 Mon Sep 17 00:00:00 2001 From: Jose Vargas Date: Tue, 2 Oct 2018 16:43:06 -0500 Subject: [PATCH 18/38] Update GitLab-ui version in package.json --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3321115a84b..004260ec9f9 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@gitlab-org/gitlab-svgs": "^1.29.0", - "@gitlab-org/gitlab-ui": "https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz", + "@gitlab-org/gitlab-ui": "^1.8.0", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.3", diff --git a/yarn.lock b/yarn.lock index 4cfdfa51092..059eaa5fc39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -185,9 +185,10 @@ resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.29.0.tgz#03b65b513f9099bbda6ecf94d673a2952f8c6c70" integrity sha512-sCl6nP3ph36+8P3nrw9VanAR648rgOUEBlEoLPHkhKm79xB1dUkXGBtI0uaSJVgbJx40M1/Ts8HSdMv+PF3EIg== -"@gitlab-org/gitlab-ui@https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz": - version "1.7.1" - resolved "https://gitlab.com/gitlab-org/gitlab-ui/-/jobs/101969901/artifacts/raw/gitlab-ui.jivl-add-text-button-component.tgz#e17416d3b829a03ee8d9eef8b58b39dc8c9fb867" +"@gitlab-org/gitlab-ui@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.8.0.tgz#dee33d78f68c91644273dbd51734b796108263ee" + integrity sha512-Owm8bkP4vEihiLD3pmMw1r+UWr3WYGaGUtj0JcwaAg3d05ZneozFEZjazIOWeYTcFsk+ZvNmSk1UA+ARIauhgQ== dependencies: "@gitlab-org/gitlab-svgs" "^1.23.0" bootstrap-vue "^2.0.0-rc.11" From 2645862f687eb32718bef3a033a5d5a1845510a6 Mon Sep 17 00:00:00 2001 From: James Lopez Date: Wed, 3 Oct 2018 06:49:25 +0000 Subject: [PATCH 19/38] Add encrypted sensitive word to export spec --- spec/features/projects/import_export/export_file_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb index a2b96514d64..f76f9ba7577 100644 --- a/spec/features/projects/import_export/export_file_spec.rb +++ b/spec/features/projects/import_export/export_file_spec.rb @@ -12,7 +12,7 @@ describe 'Import/Export - project export integration test', :js do let(:export_path) { "#{Dir.tmpdir}/import_file_spec" } let(:config_hash) { YAML.load_file(Gitlab::ImportExport.config_file).deep_stringify_keys } - let(:sensitive_words) { %w[pass secret token key] } + let(:sensitive_words) { %w[pass secret token key encrypted] } let(:safe_list) do { token: [ProjectHook, Ci::Trigger, CommitStatus], From 1ee06e30890a54e5112133967ea750429875c40b Mon Sep 17 00:00:00 2001 From: sliaquat Date: Wed, 3 Oct 2018 13:12:25 +0500 Subject: [PATCH 20/38] Add element classes used in ee branch qa-257-group-saml-sso --- app/views/layouts/nav/sidebar/_group.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 43170587797..4aa22138498 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -10,7 +10,7 @@ = group_icon(@group, class: "avatar s40 avatar-tile") .sidebar-context-title = @group.name - %ul.sidebar-top-level-items + %ul.sidebar-top-level-items.qa-group-sidebar - if group_sidebar_link?(:overview) = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do = link_to group_path(@group) do @@ -109,9 +109,9 @@ = link_to edit_group_path(@group) do .nav-icon-container = sprite_icon('settings') - %span.nav-item-name.qa-settings-item + %span.nav-item-name.qa-group-settings-item = _('Settings') - %ul.sidebar-sub-level-items + %ul.sidebar-sub-level-items.qa-group-sidebar-submenu = nav_link(path: %w[groups#projects groups#edit badges#index ci_cd#show], html_options: { class: "fly-out-top-item" } ) do = link_to edit_group_path(@group) do %strong.fly-out-top-item-name From 38f3d59fd0b2dd4eef5c94512ea6216a0e5d56b5 Mon Sep 17 00:00:00 2001 From: Chantal Rollison Date: Wed, 3 Oct 2018 08:15:00 +0000 Subject: [PATCH 21/38] #13650 added wip search functionality and tests --- .../filtered_search/dropdown_hint.js | 6 +- .../filtered_search/dropdown_utils.js | 4 +- .../filtered_search_dropdown_manager.js | 17 +++++- .../filtered_search_manager.js | 40 +++++++++---- .../filtered_search_token_keys.js | 27 +++++++++ .../filtered_search_visual_tokens.js | 54 +++++++++++++---- .../pages/groups/merge_requests/index.js | 2 + .../projects/merge_requests/index/index.js | 3 + app/finders/merge_requests_finder.rb | 27 ++++++++- app/models/merge_request.rb | 2 +- .../shared/issuable/_search_bar.html.haml | 28 +++++---- changelogs/unreleased/ccr-wip_filter.yml | 5 ++ doc/api/merge_requests.md | 1 + .../img/filter_wip_merge_requests.png | Bin 0 -> 17346 bytes .../work_in_progress_merge_requests.md | 11 +++- lib/api/merge_requests.rb | 2 +- .../filtered_search/dropdown_hint_spec.rb | 18 ++++++ spec/finders/merge_requests_finder_spec.rb | 56 +++++++++++++++--- .../filtered_search/dropdown_utils_spec.js | 5 +- .../filtered_search_visual_tokens_spec.js | 8 ++- spec/models/merge_request_spec.rb | 2 +- spec/requests/api/merge_requests_spec.rb | 38 ++++++++++++ 22 files changed, 300 insertions(+), 56 deletions(-) create mode 100644 changelogs/unreleased/ccr-wip_filter.yml create mode 100644 doc/user/project/merge_requests/img/filter_wip_merge_requests.png diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js index 8aecf9725e6..c568f4e4ebf 100644 --- a/app/assets/javascripts/filtered_search/dropdown_hint.js +++ b/app/assets/javascripts/filtered_search/dropdown_hint.js @@ -51,7 +51,11 @@ export default class DropdownHint extends FilteredSearchDropdown { FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' ')); } - FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container); + const key = token.replace(':', ''); + const { uppercaseTokenName } = this.tokenKeys.searchByKey(key); + FilteredSearchDropdownManager.addWordToInput(key, '', false, { + uppercaseTokenName, + }); } this.dismissDropdown(); this.dispatchInputEvent(); diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js index 27fff488603..6da6ca10008 100644 --- a/app/assets/javascripts/filtered_search/dropdown_utils.js +++ b/app/assets/javascripts/filtered_search/dropdown_utils.js @@ -143,7 +143,9 @@ export default class DropdownUtils { const dataValue = selected.getAttribute('data-value'); if (dataValue) { - FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true); + FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true, { + capitalizeTokenValue: selected.hasAttribute('data-capitalize'), + }); } // Return boolean based on whether it was set diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js index 207616b9de2..cd3d532c958 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js @@ -91,6 +91,11 @@ export default class FilteredSearchDropdownManager { gl: DropdownEmoji, element: this.container.querySelector('#js-dropdown-my-reaction'), }, + wip: { + reference: null, + gl: DropdownNonUser, + element: this.container.querySelector('#js-dropdown-wip'), + }, status: { reference: null, gl: NullDropdown, @@ -136,10 +141,16 @@ export default class FilteredSearchDropdownManager { return endpoint; } - static addWordToInput(tokenName, tokenValue = '', clicked = false) { + static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) { + const { + uppercaseTokenName = false, + capitalizeTokenValue = false, + } = options; const input = FilteredSearchContainer.container.querySelector('.filtered-search'); - - FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue, { + uppercaseTokenName, + capitalizeTokenValue, + }); input.value = ''; if (clicked) { diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js index d25f6f95b22..54533ebb70d 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_manager.js +++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js @@ -405,7 +405,10 @@ export default class FilteredSearchManager { if (isLastVisualTokenValid) { tokens.forEach((t) => { input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, ''); - FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`); + FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`, { + uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(t.key), + capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(t.key), + }); }); const fragments = searchToken.split(':'); @@ -421,7 +424,10 @@ export default class FilteredSearchManager { FilteredSearchVisualTokens.addSearchVisualToken(searchTerms); } - FilteredSearchVisualTokens.addFilterVisualToken(tokenKey); + FilteredSearchVisualTokens.addFilterVisualToken(tokenKey, null, { + uppercaseTokenName: this.filteredSearchTokenKeys.shouldUppercaseTokenName(tokenKey), + capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(tokenKey), + }); input.value = input.value.replace(`${tokenKey}:`, ''); } } else { @@ -429,7 +435,10 @@ export default class FilteredSearchManager { const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g; if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') { - FilteredSearchVisualTokens.addFilterVisualToken(searchToken); + const tokenKey = FilteredSearchVisualTokens.getLastTokenPartial(); + FilteredSearchVisualTokens.addFilterVisualToken(searchToken, null, { + capitalizeTokenValue: this.filteredSearchTokenKeys.shouldCapitalizeTokenValue(tokenKey), + }); // Trim the last space as seen in the if statement above input.value = input.value.replace(searchToken, '').trim(); @@ -480,7 +489,7 @@ export default class FilteredSearchManager { FilteredSearchVisualTokens.addFilterVisualToken( condition.tokenKey, condition.value, - canEdit, + { canEdit }, ); } else { // Sanitize value since URL converts spaces into + @@ -506,10 +515,15 @@ export default class FilteredSearchManager { hasFilteredSearch = true; const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue); + const { uppercaseTokenName, capitalizeTokenValue } = match; FilteredSearchVisualTokens.addFilterVisualToken( sanitizedKey, `${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`, - canEdit, + { + canEdit, + uppercaseTokenName, + capitalizeTokenValue, + }, ); } else if (!match && keyParam === 'assignee_id') { const id = parseInt(value, 10); @@ -517,7 +531,7 @@ export default class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'assignee'; const canEdit = this.canEdit && this.canEdit(tokenName); - FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit }); } } else if (!match && keyParam === 'author_id') { const id = parseInt(value, 10); @@ -525,7 +539,7 @@ export default class FilteredSearchManager { hasFilteredSearch = true; const tokenName = 'author'; const canEdit = this.canEdit && this.canEdit(tokenName); - FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit); + FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, { canEdit }); } } else if (!match && keyParam === 'search') { hasFilteredSearch = true; @@ -561,15 +575,17 @@ export default class FilteredSearchManager { this.saveCurrentSearchQuery(); - const { tokens, searchToken } - = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys()); + const tokenKeys = this.filteredSearchTokenKeys.getKeys(); + const { tokens, searchToken } = this.tokenizer.processTokens(searchQuery, tokenKeys); const currentState = state || getParameterByName('state') || 'opened'; paths.push(`state=${currentState}`); tokens.forEach((token) => { const condition = this.filteredSearchTokenKeys .searchByConditionKeyValue(token.key, token.value.toLowerCase()); - const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {}; + const tokenConfig = this.filteredSearchTokenKeys.searchByKey(token.key) || {}; + const { param } = tokenConfig; + // Replace hyphen with underscore to use as request parameter // e.g. 'my-reaction' => 'my_reaction' const underscoredKey = token.key.replace('-', '_'); @@ -581,6 +597,10 @@ export default class FilteredSearchManager { } else { let tokenValue = token.value; + if (tokenConfig.lowercaseValueOnSubmit) { + tokenValue = tokenValue.toLowerCase(); + } + if ((tokenValue[0] === '\'' && tokenValue[tokenValue.length - 1] === '\'') || (tokenValue[0] === '"' && tokenValue[tokenValue.length - 1] === '"')) { tokenValue = tokenValue.slice(1, tokenValue.length - 1); diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js index 5d131b396a0..a09ad3e4758 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js +++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js @@ -23,6 +23,16 @@ export default class FilteredSearchTokenKeys { return this.conditions; } + shouldUppercaseTokenName(tokenKey) { + const token = this.searchByKey(tokenKey.toLowerCase()); + return token && token.uppercaseTokenName; + } + + shouldCapitalizeTokenValue(tokenKey) { + const token = this.searchByKey(tokenKey.toLowerCase()); + return token && token.capitalizeTokenValue; + } + searchByKey(key) { return this.tokenKeys.find(tokenKey => tokenKey.key === key) || null; } @@ -55,4 +65,21 @@ export default class FilteredSearchTokenKeys { return this.conditions .find(condition => condition.tokenKey === key && condition.value === value) || null; } + + addExtraTokensForMergeRequests() { + const wipToken = { + key: 'wip', + type: 'string', + param: '', + symbol: '', + icon: 'admin', + tag: 'Yes or No', + lowercaseValueOnSubmit: true, + uppercaseTokenName: true, + capitalizeTokenValue: true, + }; + + this.tokenKeys.push(wipToken); + this.tokenKeysWithAlternative.push(wipToken); + } } diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js index 56fe1ab4e90..0854c1822fb 100644 --- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js +++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js @@ -55,12 +55,18 @@ export default class FilteredSearchVisualTokens { } } - static createVisualTokenElementHTML(canEdit = true) { + static createVisualTokenElementHTML(options = {}) { + const { + canEdit = true, + uppercaseTokenName = false, + capitalizeTokenValue = false, + } = options; + return `
-
+
-
+
@@ -182,16 +188,26 @@ export default class FilteredSearchVisualTokens { } } - static addVisualTokenElement(name, value, isSearchTerm, canEdit) { + static addVisualTokenElement(name, value, options = {}) { + const { + isSearchTerm = false, + canEdit, + uppercaseTokenName, + capitalizeTokenValue, + } = options; const li = document.createElement('li'); li.classList.add('js-visual-token'); li.classList.add(isSearchTerm ? 'filtered-search-term' : 'filtered-search-token'); if (value) { - li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML(canEdit); + li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({ + canEdit, + uppercaseTokenName, + capitalizeTokenValue, + }); FilteredSearchVisualTokens.renderVisualTokenValue(li, name, value); } else { - li.innerHTML = '
'; + li.innerHTML = `
`; } li.querySelector('.name').innerText = name; @@ -212,20 +228,32 @@ export default class FilteredSearchVisualTokens { } } - static addFilterVisualToken(tokenName, tokenValue, canEdit) { + static addFilterVisualToken(tokenName, tokenValue, { + canEdit, + uppercaseTokenName = false, + capitalizeTokenValue = false, + } = {}) { const { lastVisualToken, isLastVisualTokenValid } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); const { addVisualTokenElement } = FilteredSearchVisualTokens; if (isLastVisualTokenValid) { - addVisualTokenElement(tokenName, tokenValue, false, canEdit); + addVisualTokenElement(tokenName, tokenValue, { + canEdit, + uppercaseTokenName, + capitalizeTokenValue, + }); } else { const previousTokenName = lastVisualToken.querySelector('.name').innerText; const tokensContainer = FilteredSearchContainer.container.querySelector('.tokens-container'); tokensContainer.removeChild(lastVisualToken); const value = tokenValue || tokenName; - addVisualTokenElement(previousTokenName, value, false, canEdit); + addVisualTokenElement(previousTokenName, value, { + canEdit, + uppercaseTokenName, + capitalizeTokenValue, + }); } } @@ -235,7 +263,9 @@ export default class FilteredSearchVisualTokens { if (lastVisualToken && lastVisualToken.classList.contains('filtered-search-term')) { lastVisualToken.querySelector('.name').innerText += ` ${searchTerm}`; } else { - FilteredSearchVisualTokens.addVisualTokenElement(searchTerm, null, true); + FilteredSearchVisualTokens.addVisualTokenElement(searchTerm, null, { + isSearchTerm: true, + }); } } @@ -306,7 +336,9 @@ export default class FilteredSearchVisualTokens { let value; if (token.classList.contains('filtered-search-token')) { - FilteredSearchVisualTokens.addFilterVisualToken(nameElement.innerText); + FilteredSearchVisualTokens.addFilterVisualToken(nameElement.innerText, null, { + uppercaseTokenName: nameElement.classList.contains('text-uppercase'), + }); const valueContainerElement = token.querySelector('.value-container'); value = valueContainerElement.dataset.originalValue; diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js index b798a254459..339ce67438a 100644 --- a/app/assets/javascripts/pages/groups/merge_requests/index.js +++ b/app/assets/javascripts/pages/groups/merge_requests/index.js @@ -4,6 +4,8 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered import { FILTERED_SEARCH } from '~/pages/constants'; document.addEventListener('DOMContentLoaded', () => { + IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests(); + initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, isGroupDecendent: true, diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js index 3647048a872..ec39db12e74 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -7,10 +7,13 @@ import { FILTERED_SEARCH } from '~/pages/constants'; import { ISSUABLE_INDEX } from '~/pages/projects/constants'; document.addEventListener('DOMContentLoaded', () => { + IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests(); + initFilteredSearch({ page: FILTERED_SEARCH.MERGE_REQUESTS, filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, }); + new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new new UsersSelect(); // eslint-disable-line no-new diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index b698a3c7b09..50c051c3aa1 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -27,13 +27,17 @@ # updated_before: datetime # class MergeRequestsFinder < IssuableFinder + def self.scalar_params + @scalar_params ||= super + [:wip] + end + def klass MergeRequest end def filter_items(_items) items = by_source_branch(super) - + items = by_wip(items) by_target_branch(items) end @@ -61,5 +65,24 @@ class MergeRequestsFinder < IssuableFinder items.where(target_branch: target_branch) end - # rubocop: enable CodeReuse/ActiveRecord + + def item_project_ids(items) + items&.reorder(nil)&.select(:target_project_id) + end + + def by_wip(items) + if params[:wip] == 'yes' + items.where(wip_match(items.arel_table)) + elsif params[:wip] == 'no' + items.where.not(wip_match(items.arel_table)) + else + items + end + end + + def wip_match(table) + table[:title].matches('WIP:%') + .or(table[:title].matches('WIP %')) + .or(table[:title].matches('[WIP]%')) + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0481a4a3d28..6559f94a696 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -261,7 +261,7 @@ class MergeRequest < ActiveRecord::Base end end - WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze + WIP_REGEX = /\A*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze def self.work_in_progress?(title) !!(title =~ WIP_REGEX) diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 659e03fd67d..c4d177361e7 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -33,13 +33,13 @@ #js-dropdown-hint.filtered-search-input-dropdown-menu.dropdown-menu.hint-dropdown %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { action: 'submit' } } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } = sprite_icon('search') %span Press Enter or click to search %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } -# Encapsulate static class name `{{icon}}` inside #{} to bypass -# haml lint's ClassAttributeWithStaticValue %svg @@ -60,7 +60,7 @@ #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'none' } } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } No Assignee %li.divider.droplab-item-ignore - if current_user @@ -73,38 +73,46 @@ #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'none' } } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } No Milestone %li.filter-dropdown-item{ data: { value: 'upcoming' } } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } Upcoming %li.filter-dropdown-item{ 'data-value' => 'started' } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } Started %li.divider.droplab-item-ignore %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item - %button.btn.btn-link.js-data-value + %button.btn.btn-link.js-data-value{ type: 'button' } {{title}} #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'none' } } - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } No Label %li.divider.droplab-item-ignore %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } %span.dropdown-label-box{ style: 'background: {{color}}' } %span.label-title.js-data-value {{title}} #js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %li.filter-dropdown-item - %button.btn.btn-link + %button.btn.btn-link{ type: 'button' } %gl-emoji %span.js-data-value.prepend-left-10 {{name}} + #js-dropdown-wip.filtered-search-input-dropdown-menu.dropdown-menu + %ul.filter-dropdown{ data: { dropdown: true } } + %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } } + %button.btn.btn-link{ type: 'button' } + = _('Yes') + %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } } + %button.btn.btn-link{ type: 'button' } + = _('No') = render_if_exists 'shared/issuable/filter_weight', type: type diff --git a/changelogs/unreleased/ccr-wip_filter.yml b/changelogs/unreleased/ccr-wip_filter.yml new file mode 100644 index 00000000000..07d85ec02ae --- /dev/null +++ b/changelogs/unreleased/ccr-wip_filter.yml @@ -0,0 +1,5 @@ +--- +title: Added search functionality for Work In Progress (WIP) merge requests +merge_request: 18119 +author: Chantal Rollison +type: added diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 4c099581f07..b37e7698ab4 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -47,6 +47,7 @@ Parameters: | `source_branch` | string | no | Return merge requests with the given source branch | | `target_branch` | string | no | Return merge requests with the given target branch | | `search` | string | no | Search merge requests against their `title` and `description` | +| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* WIP merge requests, `no` to return *non* WIP merge requests | ```json [ diff --git a/doc/user/project/merge_requests/img/filter_wip_merge_requests.png b/doc/user/project/merge_requests/img/filter_wip_merge_requests.png new file mode 100644 index 0000000000000000000000000000000000000000..40913718385f0da03aeed5b4f4ea094fd4527b8f GIT binary patch literal 17346 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 NOh`(wf?vn){{k9P!5shq literal 0 HcmV?d00001 diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md index f01da06fa6e..66ac7740157 100644 --- a/doc/user/project/merge_requests/work_in_progress_merge_requests.md +++ b/doc/user/project/merge_requests/work_in_progress_merge_requests.md @@ -7,7 +7,7 @@ have been marked a **Work In Progress**. ![Blocked Accept Button](img/wip_blocked_accept_button.png) To mark a merge request a Work In Progress, simply start its title with `[WIP]` -or `WIP:`. As an alternative, you're also able to do it by sending a commit +or `WIP:`. As an alternative, you're also able to do it by sending a commit with its title starting with `wip` or `WIP` to the merge request's source branch. ![Mark as WIP](img/wip_mark_as_wip.png) @@ -15,4 +15,11 @@ with its title starting with `wip` or `WIP` to the merge request's source branch To allow a Work In Progress merge request to be accepted again when it's ready, simply remove the `WIP` prefix. -![Unark as WIP](img/wip_unmark_as_wip.png) +![Unmark as WIP](img/wip_unmark_as_wip.png) + +## Filtering merge requests with WIP Status + +To filter merge requests with the `WIP` status, you can type `wip` +and select the value for your filter from the merge request search input. + +![Filter WIP MRs](img/filter_wip_merge_requests.png) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 764905ca00f..440d94ae186 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -33,7 +33,6 @@ module API # rubocop: disable CodeReuse/ActiveRecord def find_merge_requests(args = {}) args = declared_params.merge(args) - args[:milestone_title] = args.delete(:milestone) args[:label_name] = args.delete(:labels) args[:scope] = args[:scope].underscore if args[:scope] @@ -97,6 +96,7 @@ module API optional :source_branch, type: String, desc: 'Return merge requests with the given source branch' optional :target_branch, type: String, desc: 'Return merge requests with the given target branch' optional :search, type: String, desc: 'Search merge requests for text present in the title or description' + optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title' use :pagination end end diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb index b99c5a7f4e3..0e296ab2109 100644 --- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb @@ -15,6 +15,7 @@ describe 'Dropdown hint', :js do before do project.add_maintainer(user) create(:issue, project: project) + create(:merge_request, source_project: project, target_project: project) end context 'when user not logged in' do @@ -224,4 +225,21 @@ describe 'Dropdown hint', :js do end end end + + context 'merge request page' do + before do + sign_in(user) + visit project_merge_requests_path(project) + filtered_search.click + end + + it 'shows the WIP menu item and opens the WIP options dropdown' do + click_hint('wip') + + expect(page).to have_css(js_dropdown_hint, visible: false) + expect(page).to have_css('#js-dropdown-wip', visible: true) + expect_tokens([{ name: 'wip' }]) + expect_filtered_search_input_empty + end + end end diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 35d0eeda8f6..33d01697c75 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -16,12 +16,18 @@ describe MergeRequestsFinder do p end let(:project4) { create(:project, :public, group: subgroup) } + let(:project5) { create(:project, :public, group: subgroup) } + let(:project6) { create(:project, :public, group: subgroup) } let!(:merge_request1) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project1) } let!(:merge_request2) { create(:merge_request, :conflict, author: user, source_project: project2, target_project: project1, state: 'closed') } - let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked') } - let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3) } - let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4) } + let!(:merge_request3) { create(:merge_request, :simple, author: user, source_project: project2, target_project: project2, state: 'locked', title: 'thing WIP thing') } + let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3, title: 'WIP thing') } + let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4, title: '[WIP]') } + let!(:merge_request6) { create(:merge_request, :simple, author: user, source_project: project5, target_project: project5, title: 'WIP: thing') } + let!(:merge_request7) { create(:merge_request, :simple, author: user, source_project: project6, target_project: project6, title: 'wip thing') } + let!(:merge_request8) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project1, title: '[wip] thing') } + let!(:merge_request9) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2, title: 'wip: thing') } before do project1.add_maintainer(user) @@ -29,19 +35,21 @@ describe MergeRequestsFinder do project3.add_developer(user) project2.add_developer(user2) project4.add_developer(user) + project5.add_developer(user) + project6.add_developer(user) end describe "#execute" do it 'filters by scope' do params = { scope: 'authored', state: 'opened' } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(3) + expect(merge_requests.size).to eq(7) end it 'filters by project' do params = { project_id: project1.id, scope: 'authored', state: 'opened' } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(1) + expect(merge_requests.size).to eq(2) end it 'filters by group' do @@ -49,7 +57,7 @@ describe MergeRequestsFinder do merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(2) + expect(merge_requests.size).to eq(3) end it 'filters by group including subgroups', :nested_groups do @@ -57,13 +65,13 @@ describe MergeRequestsFinder do merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(3) + expect(merge_requests.size).to eq(6) end it 'filters by non_archived' do params = { non_archived: true } merge_requests = described_class.new(user, params).execute - expect(merge_requests.size).to eq(4) + expect(merge_requests.size).to eq(8) end it 'filters by iid' do @@ -98,6 +106,36 @@ describe MergeRequestsFinder do expect(merge_requests).to contain_exactly(merge_request3) end + it 'filters by wip' do + params = { wip: 'yes' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9) + end + + it 'filters by not wip' do + params = { wip: 'no' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3) + end + + it 'returns all items if no valid wip param exists' do + params = { wip: '' } + + merge_requests = described_class.new(user, params).execute + + expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9) + end + + it 'adds wip to scalar params' do + scalar_params = described_class.scalar_params + + expect(scalar_params).to include(:wip, :assignee_id) + end + context 'filtering by group milestone' do let!(:group) { create(:group, :public) } let(:group_milestone) { create(:milestone, group: group) } @@ -207,7 +245,7 @@ describe MergeRequestsFinder do it 'returns the number of rows for the default state' do finder = described_class.new(user) - expect(finder.row_count).to eq(3) + expect(finder.row_count).to eq(7) end it 'returns the number of rows for a given state' do diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js index 8792e99d461..68bbbf838da 100644 --- a/spec/javascripts/filtered_search/dropdown_utils_spec.js +++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js @@ -288,13 +288,13 @@ describe('Dropdown Utils', () => { describe('setDataValueIfSelected', () => { beforeEach(() => { - spyOn(FilteredSearchDropdownManager, 'addWordToInput') - .and.callFake(() => {}); + spyOn(FilteredSearchDropdownManager, 'addWordToInput').and.callFake(() => {}); }); it('calls addWordToInput when dataValue exists', () => { const selected = { getAttribute: () => 'value', + hasAttribute: () => false, }; DropdownUtils.setDataValueIfSelected(null, selected); @@ -304,6 +304,7 @@ describe('Dropdown Utils', () => { it('returns true when dataValue exists', () => { const selected = { getAttribute: () => 'value', + hasAttribute: () => false, }; const result = DropdownUtils.setDataValueIfSelected(null, selected); diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js index 756a654765b..53a6d1d62b0 100644 --- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js +++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js @@ -240,13 +240,17 @@ describe('Filtered Search Visual Tokens', () => { beforeEach(() => { setFixtures(`
- ${subject.createVisualTokenElementHTML()} + ${subject.createVisualTokenElementHTML('custom-token')}
`); tokenElement = document.querySelector('.test-area').firstElementChild; }); + it('should add class name to token element', () => { + expect(document.querySelector('.test-area .custom-token')).toBeDefined(); + }); + it('contains name div', () => { expect(tokenElement.querySelector('.name')).toEqual(jasmine.anything()); }); @@ -280,7 +284,7 @@ describe('Filtered Search Visual Tokens', () => { describe('addVisualTokenElement', () => { it('renders search visual tokens', () => { - subject.addVisualTokenElement('search term', null, true); + subject.addVisualTokenElement('search term', null, { isSearchTerm: true }); const token = tokensContainer.querySelector('.js-visual-token'); expect(token.classList.contains('filtered-search-term')).toEqual(true); diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 48f4e53b93e..666d7e69f89 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -746,7 +746,7 @@ describe MergeRequest do end describe "#wipless_title" do - ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', ' [WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| + ['WIP ', 'WIP:', 'WIP: ', '[WIP]', '[WIP] ', '[WIP] WIP [WIP] WIP: WIP '].each do |wip_prefix| it "removes the '#{wip_prefix}' prefix" do wipless_title = subject.title subject.title = "#{wip_prefix}#{subject.title}" diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index e987eee6e91..07d19e3ad29 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -81,6 +81,35 @@ describe API::MergeRequests do let(:user2) { create(:user) } it 'returns an array of all merge requests except unauthorized ones' do + get api('/merge_requests', user), scope: :all + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id) + end + + it "returns an array of no merge_requests when wip=yes" do + get api("/merge_requests", user), wip: 'yes' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + + it "returns an array of no merge_requests when wip=no" do + get api("/merge_requests", user), wip: 'no' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |mr| mr['id'] }) + .to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request_locked.id, merge_request2.id) + end + + it 'does not return unauthorized merge requests' do private_project = create(:project, :private) merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch') @@ -244,6 +273,15 @@ describe API::MergeRequests do expect(response).to have_gitlab_http_status(404) end + it "returns an array of no merge_requests when wip=yes" do + get api("/projects/#{project.id}/merge_requests", user), wip: 'yes' + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.length).to eq(0) + end + it 'returns merge_request by "iids" array' do get api(endpoint_path, user), iids: [merge_request.iid, merge_request_closed.iid] From 8c1568d9766c674054f90af0816dbeebcdb8e2ce Mon Sep 17 00:00:00 2001 From: George Tsiolis Date: Wed, 3 Oct 2018 08:31:03 +0000 Subject: [PATCH 22/38] Add copy to clipboard button for application id and secret --- app/assets/stylesheets/pages/commits.scss | 1 + app/views/admin/applications/show.html.haml | 21 ++++++++++++------- .../doorkeeper/applications/show.html.haml | 19 +++++++++++------ ...rd-button-to-application-id-and-secret.yml | 5 +++++ locale/gitlab.pot | 13 +++++++----- .../admin/admin_manage_applications_spec.rb | 4 ++-- .../user_manages_applications_spec.rb | 4 ++-- 7 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/add-clipboard-button-to-application-id-and-secret.yml diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 10764e0f3df..628a4ca38da 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -223,6 +223,7 @@ } } +.clipboard-group, .commit-sha-group { display: inline-flex; diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml index 593a6d816e3..e69143abe45 100644 --- a/app/views/admin/applications/show.html.haml +++ b/app/views/admin/applications/show.html.haml @@ -1,4 +1,5 @@ - page_title @application.name, "Applications" + %h3.page-title Application: #{@application.name} @@ -6,23 +7,29 @@ %table.table %tr %td - Application Id + = _('Application ID') %td - %code#application_id= @application.uid + .clipboard-group + .input-group + %input.label.label-monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true } + .input-group-append + = clipboard_button(target: '#application_id', title: _("Copy ID to clipboard"), class: "btn btn btn-default") %tr %td - Secret: + = _('Secret') %td - %code#secret= @application.secret - + .clipboard-group + .input-group + %input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true } + .input-group-append + = clipboard_button(target: '#application_id', title: _("Copy secret to clipboard"), class: "btn btn btn-default") %tr %td - Callback url + = _('Callback URL') %td - @application.redirect_uri.split.each do |uri| %div %span.monospace= uri - %tr %td Trusted diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml index bb76ac6d5f6..776bbc36ec2 100644 --- a/app/views/doorkeeper/applications/show.html.haml +++ b/app/views/doorkeeper/applications/show.html.haml @@ -10,18 +10,25 @@ %table.table %tr %td - = _('Application Id') + = _('Application ID') %td - %code#application_id= @application.uid + .clipboard-group + .input-group + %input.label.label-monospace{ id: "application_id", type: "text", autocomplete: 'off', value: @application.uid, readonly: true } + .input-group-append + = clipboard_button(target: '#application_id', title: _("Copy ID to clipboard"), class: "btn btn btn-default") %tr %td - = _('Secret:') + = _('Secret') %td - %code#secret= @application.secret - + .clipboard-group + .input-group + %input.label.label-monospace{ id: "secret", type: "text", autocomplete: 'off', value: @application.secret, readonly: true } + .input-group-append + = clipboard_button(target: '#application_id', title: _("Copy secret to clipboard"), class: "btn btn btn-default") %tr %td - = _('Callback url') + = _('Callback URL') %td - @application.redirect_uri.split.each do |uri| %div diff --git a/changelogs/unreleased/add-clipboard-button-to-application-id-and-secret.yml b/changelogs/unreleased/add-clipboard-button-to-application-id-and-secret.yml new file mode 100644 index 00000000000..7c707cfe5a0 --- /dev/null +++ b/changelogs/unreleased/add-clipboard-button-to-application-id-and-secret.yml @@ -0,0 +1,5 @@ +--- +title: Add copy to clipboard button for application id and secret +merge_request: 21978 +author: George Tsiolis +type: other diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cc11577b624..646397b7757 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -598,7 +598,7 @@ msgstr "" msgid "Application" msgstr "" -msgid "Application Id" +msgid "Application ID" msgstr "" msgid "Application: %{name}" @@ -1101,9 +1101,6 @@ msgstr "" msgid "Callback URL" msgstr "" -msgid "Callback url" -msgstr "" - msgid "Can't find HEAD commit for this branch" msgstr "" @@ -1930,6 +1927,9 @@ msgstr "" msgid "Copy HTTPS clone URL" msgstr "" +msgid "Copy ID to clipboard" +msgstr "" + msgid "Copy SSH clone URL" msgstr "" @@ -1951,6 +1951,9 @@ msgstr "" msgid "Copy reference to clipboard" msgstr "" +msgid "Copy secret to clipboard" +msgstr "" + msgid "Copy to clipboard" msgstr "" @@ -5318,7 +5321,7 @@ msgstr "" msgid "Seconds to wait for a storage access attempt" msgstr "" -msgid "Secret:" +msgid "Secret" msgstr "" msgid "Select" diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb index f979d2f6090..a4904272706 100644 --- a/spec/features/admin/admin_manage_applications_spec.rb +++ b/spec/features/admin/admin_manage_applications_spec.rb @@ -16,7 +16,7 @@ RSpec.describe 'admin manage applications' do check :doorkeeper_application_trusted click_on 'Submit' expect(page).to have_content('Application: test') - expect(page).to have_content('Application Id') + expect(page).to have_content('Application ID') expect(page).to have_content('Secret') expect(page).to have_content('Trusted Y') @@ -28,7 +28,7 @@ RSpec.describe 'admin manage applications' do click_on 'Submit' expect(page).to have_content('test_changed') - expect(page).to have_content('Application Id') + expect(page).to have_content('Application ID') expect(page).to have_content('Secret') expect(page).to have_content('Trusted N') diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb index 387584fef62..34aaab240cc 100644 --- a/spec/features/profiles/user_manages_applications_spec.rb +++ b/spec/features/profiles/user_manages_applications_spec.rb @@ -16,7 +16,7 @@ describe 'User manages applications' do click_on 'Save application' expect(page).to have_content 'Application: test' - expect(page).to have_content 'Application Id' + expect(page).to have_content 'Application ID' expect(page).to have_content 'Secret' click_on 'Edit' @@ -26,7 +26,7 @@ describe 'User manages applications' do click_on 'Save application' expect(page).to have_content 'test_changed' - expect(page).to have_content 'Application Id' + expect(page).to have_content 'Application ID' expect(page).to have_content 'Secret' visit applications_profile_path From 65d7bf20e3b962f7f0db3dbc1dc331ec076a6823 Mon Sep 17 00:00:00 2001 From: Johann Hubert Sonntagbauer Date: Wed, 3 Oct 2018 08:34:13 +0000 Subject: [PATCH 23/38] Fix props name casing in time time tracker vue component --- .../time_tracking/sidebar_time_tracking.vue | 8 ++-- .../components/time_tracking/time_tracker.vue | 46 +++++++------------ .../sidebar/mount_milestone_sidebar.js | 10 ++-- .../time_tracking/time_tracker_spec.js | 33 +++++++------ 4 files changed, 44 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue index 2e1d6e9643a..8660b0546cf 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.vue @@ -51,10 +51,10 @@ export default { diff --git a/app/assets/javascripts/diffs/components/changed_files.vue b/app/assets/javascripts/diffs/components/changed_files.vue deleted file mode 100644 index 97751db1254..00000000000 --- a/app/assets/javascripts/diffs/components/changed_files.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - diff --git a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue b/app/assets/javascripts/diffs/components/changed_files_dropdown.vue deleted file mode 100644 index 0ec6b8b7f21..00000000000 --- a/app/assets/javascripts/diffs/components/changed_files_dropdown.vue +++ /dev/null @@ -1,126 +0,0 @@ - - - diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index 1c9ad8e77f1..9bbf62c0eb6 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -1,9 +1,18 @@ diff --git a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue index 96cccb49378..c3acc352d5e 100644 --- a/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue +++ b/app/assets/javascripts/diffs/components/compare_versions_dropdown.vue @@ -108,7 +108,7 @@ export default { + + diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue index bcbe374a90c..4e04e50c52a 100644 --- a/app/assets/javascripts/diffs/components/diff_file.vue +++ b/app/assets/javascripts/diffs/components/diff_file.vue @@ -1,5 +1,5 @@ + + + + diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue new file mode 100644 index 00000000000..cfe4273742f --- /dev/null +++ b/app/assets/javascripts/diffs/components/tree_list.vue @@ -0,0 +1,101 @@ + + + diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js index 2795dddfc48..6a50d2c1426 100644 --- a/app/assets/javascripts/diffs/constants.js +++ b/app/assets/javascripts/diffs/constants.js @@ -29,3 +29,5 @@ export const LENGTH_OF_AVATAR_TOOLTIP = 17; export const LINES_TO_BE_RENDERED_DIRECTLY = 100; export const MAX_LINES_TO_BE_RENDERED = 2000; + +export const MR_TREE_SHOW_KEY = 'mr_tree_show'; diff --git a/app/assets/javascripts/diffs/mixins/changed_files.js b/app/assets/javascripts/diffs/mixins/changed_files.js deleted file mode 100644 index da1339f0ffa..00000000000 --- a/app/assets/javascripts/diffs/mixins/changed_files.js +++ /dev/null @@ -1,38 +0,0 @@ -export default { - props: { - diffFiles: { - type: Array, - required: true, - }, - }, - methods: { - fileChangedIcon(diffFile) { - if (diffFile.deletedFile) { - return 'file-deletion'; - } else if (diffFile.newFile) { - return 'file-addition'; - } - return 'file-modified'; - }, - fileChangedClass(diffFile) { - if (diffFile.deletedFile) { - return 'cred'; - } else if (diffFile.newFile) { - return 'cgreen'; - } - - return ''; - }, - truncatedDiffPath(path) { - const maxLength = 60; - - if (path.length > maxLength) { - const start = path.length - maxLength; - const end = start + maxLength; - return `...${path.slice(start, end)}`; - } - - return path; - }, - }, -}; diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 98d8d5943f9..1e0b27b538d 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -12,6 +12,7 @@ import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, + MR_TREE_SHOW_KEY, } from '../constants'; export const setBaseConfig = ({ commit }, options) => { @@ -195,5 +196,23 @@ export const saveDiffDiscussion = ({ dispatch }, { note, formData }) => { .catch(() => createFlash(s__('MergeRequests|Saving the comment failed'))); }; +export const toggleTreeOpen = ({ commit }, path) => { + commit(types.TOGGLE_FOLDER_OPEN, path); +}; + +export const scrollToFile = ({ state, commit }, path) => { + const { fileHash } = state.treeEntries[path]; + document.location.hash = fileHash; + + commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash); + + setTimeout(() => commit(types.UPDATE_CURRENT_DIFF_FILE_ID, ''), 1000); +}; + +export const toggleShowTreeList = ({ commit, state }) => { + commit(types.TOGGLE_SHOW_TREE_LIST); + localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList); +}; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/diffs/store/getters.js b/app/assets/javascripts/diffs/store/getters.js index 968ba3c5e13..d4c205882ff 100644 --- a/app/assets/javascripts/diffs/store/getters.js +++ b/app/assets/javascripts/diffs/store/getters.js @@ -110,5 +110,9 @@ export const shouldRenderInlineCommentRow = state => line => { export const getDiffFileByHash = state => fileHash => state.diffFiles.find(file => file.fileHash === fileHash); +export const allBlobs = state => Object.values(state.treeEntries).filter(f => f.type === 'blob'); + +export const diffFilesLength = state => state.diffFiles.length; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index eb596b251c1..ae8930c8968 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -1,10 +1,11 @@ import Cookies from 'js-cookie'; import { getParameterValues } from '~/lib/utils/url_utility'; -import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME } from '../../constants'; +import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, MR_TREE_SHOW_KEY } from '../../constants'; const viewTypeFromQueryString = getParameterValues('view')[0]; const viewTypeFromCookie = Cookies.get(DIFF_VIEW_COOKIE_NAME); const defaultViewType = INLINE_DIFF_VIEW_TYPE; +const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY); export default () => ({ isLoading: true, @@ -17,4 +18,8 @@ export default () => ({ mergeRequestDiff: null, diffLineCommentForms: {}, diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType, + tree: [], + treeEntries: {}, + showTreeList: storedTreeShow === null ? true : storedTreeShow === 'true', + currentDiffFileId: '', }); diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js index f61efbe6e1e..6474ee628e2 100644 --- a/app/assets/javascripts/diffs/store/mutation_types.js +++ b/app/assets/javascripts/diffs/store/mutation_types.js @@ -11,3 +11,6 @@ export const EXPAND_ALL_FILES = 'EXPAND_ALL_FILES'; export const RENDER_FILE = 'RENDER_FILE'; export const SET_LINE_DISCUSSIONS_FOR_FILE = 'SET_LINE_DISCUSSIONS_FOR_FILE'; export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FILE'; +export const TOGGLE_FOLDER_OPEN = 'TOGGLE_FOLDER_OPEN'; +export const TOGGLE_SHOW_TREE_LIST = 'TOGGLE_SHOW_TREE_LIST'; +export const UPDATE_CURRENT_DIFF_FILE_ID = 'UPDATE_CURRENT_DIFF_FILE_ID'; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 59a2c09e54f..0b4485ecdb5 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { sortTree } from '~/ide/stores/utils'; import { findDiffFile, addLineReferences, @@ -7,6 +8,7 @@ import { addContextLines, prepareDiffData, isDiscussionApplicableToLine, + generateTreeList, } from './utils'; import * as types from './mutation_types'; @@ -23,9 +25,12 @@ export default { [types.SET_DIFF_DATA](state, data) { const diffData = convertObjectPropsToCamelCase(data, { deep: true }); prepareDiffData(diffData); + const { tree, treeEntries } = generateTreeList(diffData.diffFiles); Object.assign(state, { ...diffData, + tree: sortTree(tree), + treeEntries, }); }, @@ -163,4 +168,13 @@ export default { } } }, + [types.TOGGLE_FOLDER_OPEN](state, path) { + state.treeEntries[path].opened = !state.treeEntries[path].opened; + }, + [types.TOGGLE_SHOW_TREE_LIST](state) { + state.showTreeList = !state.showTreeList; + }, + [types.UPDATE_CURRENT_DIFF_FILE_ID](state, fileId) { + state.currentDiffFileId = fileId; + }, }; diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js index 631e3de311e..4ae588042e4 100644 --- a/app/assets/javascripts/diffs/store/utils.js +++ b/app/assets/javascripts/diffs/store/utils.js @@ -267,3 +267,49 @@ export function isDiscussionApplicableToLine({ discussion, diffPosition, latestD return latestDiff && discussion.active && lineCode === discussion.line_code; } + +export const generateTreeList = files => + files.reduce( + (acc, file) => { + const { fileHash, addedLines, removedLines, newFile, deletedFile, newPath } = file; + const split = newPath.split('/'); + + split.forEach((name, i) => { + const parent = acc.treeEntries[split.slice(0, i).join('/')]; + const path = `${parent ? `${parent.path}/` : ''}${name}`; + + if (!acc.treeEntries[path]) { + const type = path === newPath ? 'blob' : 'tree'; + acc.treeEntries[path] = { + key: path, + path, + name, + type, + tree: [], + }; + + const entry = acc.treeEntries[path]; + + if (type === 'blob') { + Object.assign(entry, { + changed: true, + tempFile: newFile, + deleted: deletedFile, + fileHash, + addedLines, + removedLines, + }); + } else { + Object.assign(entry, { + opened: true, + }); + } + + (parent ? parent.tree : acc.tree).push(entry); + } + }); + + return acc; + }, + { treeEntries: {}, tree: [] }, + ); diff --git a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue index 3aca38399fb..b0e60edcbe5 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/editor_header.vue @@ -3,7 +3,7 @@ import $ from 'jquery'; import { mapActions } from 'vuex'; import { __ } from '~/locale'; import FileIcon from '~/vue_shared/components/file_icon.vue'; -import ChangedFileIcon from '../changed_file_icon.vue'; +import ChangedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; export default { components: { diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue index a612739d641..72ce37be63a 100644 --- a/app/assets/javascripts/ide/components/file_finder/item.vue +++ b/app/assets/javascripts/ide/components/file_finder/item.vue @@ -1,7 +1,7 @@